home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 10 / amigaformatcd10.iso / -look_here_1st!- / handy_tools / snoopdos / snoopdos_source / miscwin.c < prev    next >
C/C++ Source or Header  |  1994-09-17  |  33KB  |  1,186 lines

  1. /*
  2.  *        MISCWIN.C                                            vi:ts=4
  3.  *
  4.  *      Copyright (c) Eddy Carroll, September 1994.
  5.  *
  6.  *        This module contains miscellaneous functions associated with
  7.  *        the SnoopDos GUI, but nevertheless not directly tied to any
  8.  *        particular window.
  9.  *        
  10.  */        
  11.  
  12. #include "system.h"
  13. #include "snoopdos.h"
  14. #include "gui.h"
  15.  
  16. #if 0
  17. #define DB(s)    KPrintF(s)
  18. #else
  19. #define DB(s)
  20. #endif
  21.  
  22. /*
  23.  *        Four dummy requesters for use when disabling window input
  24.  */
  25. struct FileRequester    *SnoopDosFR;
  26.  
  27. APTR                    AG_Context;
  28. struct NewAmigaGuide    AG_NewGuide;
  29. char                    *PendingAGuideMsg;
  30.  
  31. extern struct TextAttr    TopazFontAttr;
  32. extern struct TextAttr    SystemFontAttr;
  33. extern struct TextAttr    WindowFontAttr;
  34. extern struct TextAttr    BufferFontAttr;
  35.  
  36. /*
  37.  *        Now our busy pointer for V37 users (borrowed from ToolMaker)
  38.  */
  39. UWORD __chip WaitPointer[36] = {
  40.     0x0000, 0x0000, 0x0400, 0x07C0, 0x0000, 0x07C0, 0x0100, 0x0380,
  41.     0x0000, 0x07E0, 0x07C0, 0x1FF8, 0x1FF0, 0x3FEC, 0x3FF8, 0x7FDE,
  42.     0x3FF8, 0x7FBE, 0x7FFC, 0xFF7F, 0x7EFC, 0xFFFF, 0x7FFC, 0xFFFF,
  43.     0x3FF8, 0x7FFE, 0x3FF8, 0x7FFE, 0x1FF0, 0x3FFC, 0x07C0, 0x1FF8,
  44.     0x0000, 0x07E0, 0x0000, 0x0000
  45. };
  46.  
  47. ULONG __chip FileButtonData[] = {
  48.     0x0FF00000, 0x0C180000, 0x0C140000, 0x0C120000, 0x0C1F0000,
  49.     0x0C030000, 0x0C030000, 0x0C030000, 0x0C030000, 0x0FFF0000,
  50. };
  51.  
  52. ULONG __chip FontButtonData[] = {
  53.     0x0FE00000, 0x09200000, 0x01000000, 0x017F0000, 0x01490000,
  54.     0x01080000, 0x01080000, 0x00080000, 0x00080000, 0x00080000,
  55. };
  56.  
  57. /*
  58.  *        This structure holds all the information we need to know about
  59.  *        one of our custom images
  60.  */
  61. struct CustomBitMap {
  62.     struct Image    image;
  63.     struct Image    altimage;
  64.     struct BitMap    bitmap;
  65.     struct RastPort    rport;
  66. };
  67.  
  68. struct CustomImage {
  69.     struct CustomBitMap    image[2];
  70.     int                    size;
  71. };
  72.     
  73. /*****************************************************************************
  74.  *
  75.  *        Start of functions!
  76.  *
  77.  *****************************************************************************/
  78.  
  79. /*
  80.  *        DisableWindow(window, requester)
  81.  *
  82.  *        Disables the specified window by displaying a dummy requester in it
  83.  *        and turning on the wait pointer. It is assumed that the window
  84.  *        exists and has not been disabled already.
  85.  */
  86. void DisableWindow(struct Window *win, struct Requester *req)
  87. {
  88.     InitRequester(req);
  89.     if (Request(req, win)) {
  90.         /*
  91.          *        Use the new V39 system busy pointer if possible, else
  92.          *        drop back to our own busy pointer
  93.          */
  94.         if (IntuitionBase->LibNode.lib_Version >= 39)
  95.             SetWindowPointer(win,
  96.                              WA_BusyPointer,    TRUE,
  97.                              WA_PointerDelay,    TRUE,
  98.                              TAG_DONE);
  99.         else
  100.             SetPointer(win, WaitPointer, 16, 16, -6, 0);
  101.     }
  102. }
  103.  
  104. /*
  105.  *        EnableWindow()
  106.  *
  107.  *        Enables the specified window by removing the dummy requester
  108.  *        placed inside it earlier. You must have called DisableWindow()
  109.  *        with the same parameters first.
  110.  */
  111. void EnableWindow(struct Window *win, struct Requester *req)
  112. {
  113.     EndRequest(req, win);
  114.     ClearPointer(win);
  115. }
  116.  
  117. /*
  118.  *        DisableAllWindows()
  119.  *
  120.  *        Disables all windows by opening a dummy requester in them and
  121.  *        setting the window pointer to busy. Calls to this function
  122.  *        nest, so be sure to call EnableAllWindows the correct number
  123.  *        of times. This is intended for use when displaying modal
  124.  *        requesters (e.g. ASL, error messages, etc.)
  125.  */
  126. void DisableAllWindows(void)
  127. {
  128.     DisableNestCount++;
  129.     if (DisableNestCount == 1) {
  130.         if (MainWindow) {
  131.             /*
  132.              *        If we are disabling the main window, we won't be able
  133.              *        to respond to IDCMP_SIZEVERIFY messages, so instead, we
  134.              *        just stop requesting them. This lets the user resize the
  135.              *        window when we have a file/font requester up, even though
  136.              *        it won't redraw until the requester is finished.
  137.              */
  138.             ModifyIDCMP(MainWindow,MainWindow->IDCMPFlags & ~IDCMP_SIZEVERIFY);
  139.             DisableWindow(MainWindow, &MainRequester);
  140.         }
  141.         if (SetWindow)        DisableWindow(SetWindow,    &SetRequester);
  142.         if (FuncWindow)        DisableWindow(FuncWindow,    &FuncRequester);
  143.         if (FormWindow)        DisableWindow(FormWindow,    &FormRequester);
  144.     }
  145. }
  146.  
  147. /*
  148.  *        EnableAllWindows()
  149.  *
  150.  *        Re-enables all windows disabled by calling DisableAllWindows()
  151.  */
  152. void EnableAllWindows(void)
  153. {
  154.     DisableNestCount--;
  155.     if (DisableNestCount == 0) {
  156.         if (SetWindow)        EnableWindow(SetWindow,        &SetRequester);
  157.         if (FuncWindow)        EnableWindow(FuncWindow,    &FuncRequester);
  158.         if (FormWindow)        EnableWindow(FormWindow,    &FormRequester);
  159.         if (MainWindow) {
  160.             /*
  161.              *        Because the user might have resized the main window
  162.              *        while we were disabled, causing us to miss the resize
  163.              *        message (which happens when IDCMP_SIZEVERIFY is active)
  164.              *        we check to see if the size has changed and if it has,
  165.              *        cause a fake RESIZE message to be sent to ourself.
  166.              */
  167.             EnableWindow(MainWindow, &MainRequester);
  168.             ModifyIDCMP(MainWindow, MainWindow->IDCMPFlags | IDCMP_SIZEVERIFY);
  169.             if (MainWindow->Width  != CurWindowWidth ||
  170.                             MainWindow->Height != CurWindowHeight) {
  171.                 SizeWindow(MainWindow, 0, 0);
  172.             }
  173.         }
  174.     }
  175. }
  176.  
  177. /*
  178.  *        RecordWindowSizes()
  179.  *
  180.  *        Records the current size and position of all open windows on the
  181.  *        display so that they can be re-opened in the same position next time.
  182.  *        Usually called before a window is closed, but may also be called
  183.  *        when (for example) settings are being saved.
  184.  */
  185. void RecordWindowSizes(void)
  186. {
  187.     if (MainWindow) {
  188.         CurSettings.MainWinLeft   = MainWindow->LeftEdge;
  189.         CurSettings.MainWinTop    = MainWindow->TopEdge;
  190.         CurSettings.MainWinWidth  = MainWindow->Width;
  191.         CurSettings.MainWinHeight = MainWindow->Height;
  192.     }
  193.     if (FormWindow) {
  194.         CurSettings.FormWinLeft   = FormWindow->LeftEdge;
  195.         CurSettings.FormWinTop    = FormWindow->TopEdge;
  196.     }
  197.     if (FuncWindow) {
  198.         CurSettings.FuncWinLeft   = FuncWindow->LeftEdge;
  199.         CurSettings.FuncWinTop    = FuncWindow->TopEdge;
  200.     }
  201.     if (SetWindow) {
  202.         CurSettings.SetupWinLeft  = SetWindow->LeftEdge;
  203.         CurSettings.SetupWinTop   = SetWindow->TopEdge;
  204.     }
  205. }
  206.  
  207. /*
  208.  *        ShowError(errormsg)
  209.  *
  210.  *        Displays the specified error message in a requester (on the same
  211.  *        screen as the main SnoopDos window if possible, else on the
  212.  *        default window), waits for an OKAY click, and returns.
  213.  */
  214. void ShowError(char *errormsg, ...)
  215. {
  216.     static struct EasyStruct es = {
  217.         sizeof(struct EasyStruct),
  218.         0,
  219.         "SnoopDos",
  220.         NULL,
  221.         NULL
  222.     };
  223.     int pausestate = Paused;
  224.  
  225.     Paused = 0;
  226.  
  227.     es.es_TextFormat    = errormsg;
  228.     es.es_GadgetFormat    = MSG(MSG_ERROR_OKAY);
  229.     DisableAllWindows();
  230.     /*
  231.      *        If MainWindow is NULL, then the requester will appear on
  232.      *        the default public screen
  233.      */
  234.     EasyRequestArgs(MainWindow, &es, 0, (&errormsg)+1);
  235.     EnableAllWindows();
  236.     Paused = pausestate;
  237. }
  238.  
  239. /*
  240.  *        GetResponse(prompt, message, params, ...)
  241.  *
  242.  *        Displays an easy requester with the specified prompts, waits for the
  243.  *        user to click on an option, then returns the selected option.
  244.  *
  245.  *        Note that options are numbered 1,2,3....,0 with the leftmost gadget
  246.  *        returning 1 and the rightmost gadget returning 0.
  247.  */
  248. int GetResponse(char *prompts, char *reqmsg, ...)
  249. {
  250.     static struct EasyStruct es = {
  251.         sizeof(struct EasyStruct),
  252.         0,
  253.         "SnoopDos",
  254.         NULL,
  255.         NULL
  256.     };
  257.     int pausestate = Paused;
  258.     int choice;
  259.  
  260.     Paused = 0;
  261.  
  262.     es.es_TextFormat    = reqmsg;
  263.     es.es_GadgetFormat    = prompts;
  264.     DisableAllWindows();
  265.     choice = EasyRequestArgs(MainWindow, &es, 0, (&reqmsg)+1);
  266.     EnableAllWindows();
  267.     Paused = pausestate;
  268.     return (choice);
  269. }
  270.  
  271. /*
  272.  *        MaxTextLen(font, textarray)
  273.  *
  274.  *        Returns the length (in pixels) of the longest string in the given
  275.  *        zero-terminated array of message IDs, using the given font.
  276.  */
  277. int MaxTextLen(struct TextFont *font, int *ids)
  278. {
  279.     struct RastPort rp;
  280.     int maxlen = 0;
  281.  
  282.     InitRastPort(&rp);
  283.     SetFont(&rp, font);
  284.  
  285.     while (*ids) {
  286.         char *msg = MSG(*ids++);
  287.         int  len  = TextLength(&rp, msg, strlen(msg));
  288.  
  289.         if (len > maxlen)
  290.             maxlen = len;
  291.     }
  292.     return (maxlen);
  293. }
  294.  
  295. /*
  296.  *        GetTextLen(font, msg)
  297.  *
  298.  *        Returns the length (in pixels) of the given message indicated by the
  299.  *        message ID, when rendered in the given font.
  300.  */
  301. int GetTextLen(struct TextFont *font, char *msg)
  302. {
  303.     struct RastPort rp;
  304.  
  305.     InitRastPort(&rp);
  306.     SetFont(&rp, font);
  307.     return (TextLength(&rp, msg, strlen(msg)));
  308. }
  309.  
  310. /*
  311.  *        AddKeyShortcut(shortcut, gadid, msgid)
  312.  *
  313.  *        Checks the string corresponding to msgid for an underlined
  314.  *        character. If one is found, then a corresponding entry is
  315.  *        made in the shortcut array with that gadget ID.
  316.  *
  317.  *        For alphabetic shortcuts, we make entries for both the lower
  318.  *        and upper case versions of the key (since we might have
  319.  *        strings like "_Cancel" and "U_se" and we want both kinds
  320.  *        to be treated identically).
  321.  */
  322. void AddKeyShortcut(UBYTE *shortcut, int gadid, int msgid)
  323. {
  324.     UBYTE *p = MSG(msgid);
  325.  
  326.     while (*p) {
  327.         if (*p++ == '_') {
  328.             UBYTE ch = *p;
  329.  
  330.             if (ch >= 'a' && ch <= 'z')
  331.                 ch &= 0x5F;
  332.  
  333.             shortcut[ch] = gadid;
  334.             if (ch >= 'A' && ch <= 'Z')
  335.                 shortcut[ch + 32] = gadid;
  336.             return;
  337.         }
  338.     }
  339. }
  340.  
  341. /*
  342.  *        ShowGadget(window, gadget, type)
  343.  *
  344.  *        Activates or deactives the described gadget, by showing it in
  345.  *        a GADGET_DOWN or GADGET_UP rendered state. This is mainly used
  346.  *        to give the user direct feedback as to what's happening when
  347.  *        they hit a keyboard shortcut.
  348.  *
  349.  *        Automatically ignores disabled gadgets.
  350.  */
  351. void ShowGadget(struct Window *win, struct Gadget *gad, int type)
  352. {
  353.     if ((gad->Flags & GFLG_DISABLED) == 0) {
  354.         int gadpos = RemoveGadget(win, gad);
  355.  
  356.         if (type == GADGET_DOWN)
  357.             gad->Flags |= GFLG_SELECTED;
  358.         else
  359.             gad->Flags &= ~GFLG_SELECTED;
  360.         AddGadget(win, gad, gadpos);
  361.         RefreshGList(gad, win, NULL, 1);
  362.     }
  363. }
  364.  
  365. /*
  366.  *        MyOpenFont()
  367.  *
  368.  *        Tries to open the specified font. If we can't open diskfont.library,
  369.  *        then falls back to the ROM OpenFont() in graphics.library. We do
  370.  *        this so that we can still run, even when there is no diskfont.library
  371.  *        around.
  372.  *
  373.  *        Returns a pointer to the opened font, or NULL for failure.
  374.  */
  375. struct TextFont *MyOpenFont(struct TextAttr *textattr)
  376. {
  377.     if (DiskfontBase)
  378.         return OpenDiskFont(textattr);
  379.     else
  380.         return OpenFont(textattr);
  381. }
  382.  
  383. /*
  384.  *        CheckForASL()
  385.  *
  386.  *        Tries to load asl.library and displays an error requester if it
  387.  *        can't be located. Returns TRUE if successful, or FALSE if it
  388.  *        couldn't be loaded. All functions using asl.library functions
  389.  *        must call this first.
  390.  *
  391.  *        We do it like this since it's useful to be able to run SnoopDos
  392.  *        even when asl.library isn't around.
  393.  */
  394. int CheckForASL(void)
  395. {
  396.     if (!DiskfontBase) {
  397.         DiskfontBase = OpenLibrary("diskfont.library", 0);
  398.         if (!DiskfontBase) {
  399.             ShowError(MSG(MSG_ERROR_NO_DISKFONT));
  400.             return (FALSE);
  401.         }
  402.     }
  403.     if (!AslBase) {
  404.         AslBase = OpenLibrary("asl.library", 0);
  405.         if (!AslBase) {
  406.             ShowError(MSG(MSG_ERROR_NO_ASL));
  407.             return (FALSE);
  408.         }
  409.     }
  410.     return (TRUE);
  411. }
  412.  
  413. /*
  414.  *        SelectFont(win, fonttype)
  415.  *
  416.  *        Requests a font from the user of the appropriate type (either
  417.  *        FONTSEL_WINDOW or FONTSEL_BUFFER). Returns TRUE if a font was
  418.  *        successfully chosen, else FALSE. win is the window this
  419.  *        requester is associated with (used to choose the correct
  420.  *        opening screen).
  421.  *
  422.  *        If successful, the font requester (WindowFR or BufferFR) will
  423.  *        hold details of the selected font.
  424.  */
  425. int SelectFont(struct Window *win, int fonttype)
  426. {
  427.     struct FontRequester **fontreq;
  428.     struct TextAttr *fontattr;
  429.     struct TextAttr *reqfont;
  430.     struct TextFont *tempfont;
  431.     int fixedwidth;
  432.     char *title;
  433.     int retval;
  434.     int pausestate = Paused;
  435.  
  436.     if (!CheckForASL())
  437.         return (FALSE);
  438.  
  439.     /*
  440.      *        Now a little check to make sure we can actually open
  441.      *        the current window font. If we can't, fall back to the
  442.      *        system font. (This should never arise, but if it does,
  443.      *        the user is in the unfortunate position of not being
  444.      *        able to change to a font that IS recognised, since the
  445.      *        font requester can't be opened!)
  446.      */
  447.     reqfont = &WindowFontAttr;
  448.     tempfont = MyOpenFont(reqfont);
  449.     if (tempfont)
  450.         CloseFont(tempfont);
  451.     else
  452.         reqfont = &SystemFontAttr;
  453.         
  454.     /*
  455.      *        Next, initialise according to the font type being selected
  456.      */
  457.     if (fonttype == FONTSEL_BUFFER) {
  458.         fontreq     = &BufferFR;
  459.         fontattr    = &BufferFontAttr;
  460.         title       = MSG(MSG_BFONT_TITLE);
  461.         fixedwidth    = TRUE;
  462.     } else {
  463.         fontreq     = &WindowFR;
  464.         fontattr    = &WindowFontAttr;
  465.         title       = MSG(MSG_GFONT_TITLE);
  466.         fixedwidth    = FALSE;
  467.     }
  468.  
  469.     if (*fontreq == NULL) {
  470.         *fontreq = (struct FontRequester *)
  471.                     AllocAslRequestTags(ASL_FontRequest,
  472.                             ASLFO_PrivateIDCMP,        TRUE,
  473.                             ASLFO_TitleText,        title,
  474.                             ASLFO_PositiveText,        MSG(MSG_ERROR_OKAY),
  475.                             ASLFO_NegativeText,        MSG(MSG_ERROR_CANCEL),
  476.                             ASLFO_FixedWidthOnly,    fixedwidth,
  477.                             ASLFO_InitialHeight,    SnoopScreen->Height*3/4,
  478.                             TAG_DONE);
  479.         if (!*fontreq) {
  480.             ShowError(MSG(MSG_ERROR_OPENFONT));
  481.             return (FALSE);
  482.         }
  483.     }
  484.     Paused = 0;
  485.     DisableAllWindows();
  486.     retval = AslRequestTags(*fontreq,
  487.                             ASLFO_TextAttr,            reqfont,
  488.                             ASLFO_Window,            win,
  489.                             ASLFO_InitialName,        fontattr->ta_Name,
  490.                             ASLFO_InitialSize,        fontattr->ta_YSize,
  491.                             TAG_DONE);
  492.     EnableAllWindows();
  493.     Paused = pausestate;
  494.  
  495.     if (retval && fonttype == FONTSEL_BUFFER) {
  496.         /*
  497.           *        Now do an additional check to ensure we really got a
  498.          *        non-proportional font, since the ASL requester (as of V39)
  499.          *        can sometimes return a proportional font even when the
  500.          *        FixedWidthOnly tag is set. If we got a proportional font,
  501.          *        ignore the selection.
  502.          */
  503.         struct TextFont *font = MyOpenFont(&BufferFR->fo_Attr);
  504.  
  505.         if (!font || (font->tf_Flags & FPF_PROPORTIONAL)) {
  506.             ShowError(MSG(MSG_ERROR_FONT_PROPORTIONAL));
  507.             retval = FALSE;
  508.         }
  509.         if (font)
  510.             CloseFont(font);
  511.     }
  512.     return (retval);
  513. }
  514.  
  515. /*
  516.  *        SelectFile(char *newname, char *defname, win, type)
  517.  *
  518.  *        Requests a file from the user of the appropriate type:
  519.  *
  520.  *            FILESEL_LOADCONFIG        - Loading a configuration
  521.  *            FILESEL_SAVECONFIG        - Saving a configuration
  522.  *            FILESEL_DEFLOGNAME        - Default log filename
  523.  *            FILESEL_NEWLOGNAME        - Current log filename
  524.  *
  525.  *        Returns TRUE for success, FALSE for failure.
  526.  *
  527.  *        newname is the buffer to store the full pathname of the selected file.
  528.  *        defname is the default path/filename to use in the requester.
  529.  *
  530.  *        defname and newname may both point to the same string -- newname is
  531.  *        only updated if a file is actually selected.
  532.  */
  533. int SelectFile(char *newname, char *defname, struct Window *win, int type)
  534. {
  535.     char pathname[100];
  536.     char filename[50];
  537.     struct TextAttr *reqfont;
  538.     struct TextFont *tempfont;
  539.     int titleid;
  540.     int retval;
  541.     int pausestate = Paused;
  542.     int savemode;
  543.     int n;
  544.  
  545.     if (!CheckForASL())
  546.         return (FALSE);
  547.  
  548.     switch (type) {
  549.         case FILESEL_LOADCONFIG: titleid = MSG_ASL_LOADCONFIG;    break;
  550.         case FILESEL_SAVECONFIG: titleid = MSG_ASL_SAVECONFIG;    break;
  551.         case FILESEL_DEFLOGNAME: titleid = MSG_ASL_DEFLOGNAME;    break;
  552.         case FILESEL_NEWLOGNAME: titleid = MSG_ASL_NEWLOGNAME;    break;
  553.         case FILESEL_SAVEWINDOW: titleid = MSG_ASL_SAVEWINDOW;    break;
  554.         case FILESEL_SAVEBUFFER: titleid = MSG_ASL_SAVEBUFFER;    break;
  555.     }
  556.     savemode = (type != FILESEL_LOADCONFIG);
  557.  
  558.     /*
  559.      *        Now a little check to make sure we can actually open
  560.      *        the current window font. If we can't, fall back to the
  561.      *        system font.
  562.      */
  563.     reqfont  = &WindowFontAttr;
  564.     tempfont = MyOpenFont(reqfont);
  565.     if (tempfont)
  566.         CloseFont(tempfont);
  567.     else
  568.         reqfont = &SystemFontAttr;
  569.         
  570.     strcpy(filename, FilePart(defname));
  571.     n = PathPart(defname) - defname;
  572.     if (n)
  573.         strncpy(pathname, defname, n);
  574.     pathname[n] = '\0';
  575.  
  576.     if (SnoopDosFR == NULL) {
  577.         SnoopDosFR = (struct FileRequester *)
  578.                       AllocAslRequestTags(ASL_FileRequest,
  579.                             ASLFR_PrivateIDCMP,        TRUE,
  580.                             ASLFR_PositiveText,        MSG(MSG_ERROR_OKAY),
  581.                             ASLFR_NegativeText,        MSG(MSG_ERROR_CANCEL),
  582.                             ASLFR_RejectIcons,        TRUE,
  583.                             ASLFR_InitialHeight,    SnoopScreen->Height*3/4,
  584.                             TAG_DONE);
  585.         if (!SnoopDosFR) {
  586.             ShowError(MSG(MSG_ERROR_OPENFILEREQ));
  587.             return (FALSE);
  588.         }
  589.     }
  590.     /*
  591.      *        It's important that we disable windows while the
  592.      *        file requester is displayed, otherwise users might
  593.      *        become confused
  594.      */
  595.     Paused = 0;
  596.     DisableAllWindows();
  597.     retval = AslRequestTags(SnoopDosFR,
  598.                             ASLFR_TitleText,        MSG(titleid),
  599.                             ASLFR_Window,            win,
  600.                             ASLFR_TextAttr,            reqfont,
  601.                             ASLFR_InitialFile,        filename,
  602.                             ASLFR_InitialDrawer,    pathname,
  603.                             ASLFR_DoSaveMode,        savemode,
  604.                             TAG_DONE);
  605.     EnableAllWindows();
  606.     Paused = pausestate;
  607.     /*
  608.      *        Now build our return filename. If no output filename was selected
  609.      *        but a directory was given, then we only allow a successful if
  610.      *        the "directory" is actually a device -- if it's a real directory
  611.      *        we return failure (since you can't save or load a directory).
  612.      *
  613.      *        We even return failure if a device is specified as the directory
  614.      *        and we're trying to load a file, since loading from a printer
  615.      *        makes no sense. (Mind you, loading a config file from a CON: file
  616.      *        might be an interesting way to provide a built-in command line
  617.      *        interpreter!)
  618.      */
  619.     if (retval) {
  620.         if (*SnoopDosFR->fr_File ||
  621.             (savemode && !IsFileSystem(SnoopDosFR->fr_Drawer)))
  622.         {
  623.             strcpy(newname, SnoopDosFR->fr_Drawer);
  624.             AddPart(newname, SnoopDosFR->fr_File, 255);
  625.         } else
  626.             retval = FALSE;
  627.     }
  628.     return (retval);
  629. }
  630.  
  631. /*
  632.  *        InitFonts()
  633.  *
  634.  *        Initialises our default fonts (system, topaz) and
  635.  *        updates the current font settings to whichever font is
  636.  *        appropriate, if they have not already been set to
  637.  *        a particular font.
  638.  */
  639. void InitFonts(void)
  640. {
  641.     /*
  642.      *        We have four fonts in total; the current gadget and buffer fonts,
  643.      *        the system font, and finally topaz 8 point. If we fail to open
  644.      *        a window using either of the first two, we will try again using
  645.      *        the third and finally using the fourth before giving up.
  646.      *
  647.      *        We default to having the gadget font be the same as the screen
  648.      *        font, and having the buffer font the same as the system font.
  649.      */
  650.     strcpy(SystemFontName, GfxBase->DefaultFont->tf_Message.mn_Node.ln_Name);
  651.     SystemFontAttr.ta_YSize = GfxBase->DefaultFont->tf_YSize;
  652.     BufferFontAttr.ta_YSize = BufferFontSize;
  653.     WindowFontAttr.ta_YSize = WindowFontSize;
  654.  
  655.     if (BufferFontSize == 0) {
  656.         strcpy(BufferFontName, SystemFontName);
  657.         BufferFontAttr.ta_YSize = SystemFontAttr.ta_YSize;
  658.         BufferFontSize = BufferFontAttr.ta_YSize;
  659.     }
  660.  
  661.     if (SnoopScreen && WindowFontSize == 0) {
  662.         strcpy(WindowFontName, SnoopScreen->Font->ta_Name);
  663.         WindowFontAttr.ta_YSize = SnoopScreen->Font->ta_YSize;
  664.         WindowFontSize = WindowFontAttr.ta_YSize;
  665.     }
  666. }
  667.  
  668. /*
  669.  *        SetupScreen()
  670.  *
  671.  *        Initialises the font and other variables which depend on the
  672.  *        user's chosen screen. This is called prior to opening any
  673.  *        windows on a screen.
  674.  *
  675.  *        If we can't open on the user's desired screen (maybe it doesn't
  676.  *        exist) then we fall back to Workbench. If we can't even open on
  677.  *        Workbench, then we're really in trouble and should probably quit.
  678.  *
  679.  *        We maintain the screen info until CleanupScreen() is called (usually
  680.  *        because SnoopDos is going into a hidden state).
  681.  *
  682.  *        Returns TRUE for success, false for failure. SnoopScreen can also
  683.  *        be checked at any time to see if we have a valid screen or not.
  684.  *
  685.  */
  686. int SetupScreen(void)
  687. {
  688.     struct ColorMap *cm;
  689.     struct Screen *scr;
  690.     char *scrname = NULL;            /* Default is default public screen */
  691.     struct List *psl;
  692.     struct PubScreenNode *psn;
  693.  
  694.     /*
  695.      *        First, let's locate the screen we want, according to the
  696.      *        user's preference. If we can't open the preferred screen,
  697.      *        we try again with the default public screen. If we can't
  698.      *        open the default public screen, we try Workbench. If we
  699.      *        can't open THAT, then we're really screwed.
  700.      */
  701.     if (SnoopScreen)
  702.         CleanupScreen();
  703.  
  704.     switch (CurSettings.Setup.ScreenType) {
  705.         
  706.         case SCREEN_FRONT:
  707.             /*
  708.              *        We want to open on the front public screen if possible.
  709.              *        The only way to find out if the current screen is a
  710.              *        public screen is to read IntuitionBase->FirstScreen and
  711.              *        then try and find a match for it on the public screen list.
  712.              *        If we do, then the public screen structure will give the
  713.              *        public name of the screen, which we can then use to
  714.              *        lock it.
  715.              */
  716.             psl = (void *)LockPubScreenList();
  717.             if (psl) {
  718.                 scr = IntuitionBase->FirstScreen;
  719.                 FORLIST(psl, psn) {
  720.                     if (psn->psn_Screen == scr) {
  721.                         scrname = psn->psn_Node.ln_Name;
  722.                         break;
  723.                     }
  724.                 }
  725.                 UnlockPubScreenList();
  726.             }
  727.             break;
  728.  
  729.         case SCREEN_NAMED:
  730.             scrname = CurSettings.Setup.ScreenName;
  731.             break;
  732.     }
  733.     scr = LockPubScreen(scrname);
  734.     if (!scr && scrname)
  735.         scr = LockPubScreen(NULL);
  736.  
  737.     if (!scr) {
  738.         scr = LockPubScreen("Workbench");
  739.         if (!scr)
  740.             return (FALSE);
  741.     }
  742.     BorderLeft        = scr->WBorLeft;
  743.     BorderRight        = scr->WBorRight;
  744.     BorderTop       = scr->WBorTop;
  745.     BorderBottom    = scr->WBorBottom;
  746.     ScreenWidth        = scr->Width;
  747.     ScreenHeight    = scr->Height;
  748.     TitlebarHeight    = BorderTop + scr->Font->ta_YSize + 1;
  749.  
  750.     /*
  751.      *        Now calculate aspect ratio of screen, so we can scale the
  752.      *        horizontal scroll bar accordingly.
  753.      */
  754.     SquareAspect  = 1;    /* Assume square aspect ratio */
  755.     GadgetHeight  = HI_GADGET_HEIGHT;
  756.     GadgetSpacing = HI_GADGET_SPACING;
  757.     cm = scr->ViewPort.ColorMap;
  758.     if (cm) {
  759.         struct TagItem ti[] = {
  760.             VTAG_VIEWPORTEXTRA_GET,    NULL,
  761.             VTAG_END_CM,            NULL
  762.         };
  763.         if (VideoControl(cm, ti) == NULL) {
  764.             struct ViewPortExtra *vpe = (struct ViewPortExtra *)ti[0].ti_Data;
  765.             struct Rectangle *rect = &vpe->DisplayClip;
  766.  
  767.             if ((rect->MaxX - rect->MinX)/2 > (rect->MaxY - rect->MinY)) {
  768.                 SquareAspect  = 0;
  769.                 GadgetHeight  = LO_GADGET_HEIGHT;
  770.                 GadgetSpacing = LO_GADGET_SPACING;
  771.             }
  772.         }
  773.     }
  774.     SnoopScreen = scr;
  775.     ScreenDI    = GetScreenDrawInfo(SnoopScreen);
  776.     /*
  777.      *        Get the sizing image used by windows. We need this info
  778.      *        so that we can align our scroll arrows and scroll bars
  779.      *        correctly.
  780.      */
  781.     ScreenResolution = (SnoopScreen->Flags & SCREENHIRES) ? SYSISIZE_MEDRES :
  782.                                                             SYSISIZE_LOWRES;
  783.     SizeImage = (struct Image *)NewObject(NULL, "sysiclass",
  784.                                           SYSIA_DrawInfo,    ScreenDI,
  785.                                           SYSIA_Which,        SIZEIMAGE,
  786.                                           SYSIA_Size,        ScreenResolution,
  787.                                           TAG_END);
  788.     if (!SizeImage) {
  789.         CleanupScreen();
  790.         return (NULL);
  791.     }
  792.     InitFonts();
  793.     return (TRUE);
  794. }
  795.  
  796. /*
  797.  *        CleanupScreen()
  798.  *
  799.  *        Frees resources associated with the screen. Called when
  800.  *        SnoopDos is about to go into HIDE mode.
  801.  */
  802. void CleanupScreen(void)
  803. {
  804.     if (SizeImage) {
  805.         DisposeObject(SizeImage);
  806.         SizeImage = NULL;
  807.     }
  808.     if (SnoopScreen) {
  809.         if (ScreenDI) {
  810.             FreeScreenDrawInfo(SnoopScreen, ScreenDI);
  811.             ScreenDI = NULL;
  812.         }
  813.         UnlockPubScreen(NULL, SnoopScreen);
  814.         SnoopScreen = NULL;
  815.     }
  816. }
  817.  
  818. /*
  819.  *        CleanupWindows()
  820.  *
  821.  *        Frees all resources allocated in windows module
  822.  */
  823. void CleanupWindows(void)
  824. {
  825.     CleanupMainWindow();
  826.     CleanupSubWindows();
  827.     CleanupScreen();
  828.     if (WindowFR)        FreeAslRequest(WindowFR),    WindowFR    = NULL;
  829.     if (BufferFR)        FreeAslRequest(BufferFR),    BufferFR    = NULL;
  830.     if (SnoopDosFR)        FreeAslRequest(SnoopDosFR),    SnoopDosFR    = NULL;
  831. }
  832.  
  833. /*
  834.  *        ShowAGuide(cmdstring)
  835.  *
  836.  *        Attempts to open AmigaGuide and display help on the specified
  837.  *        topic. (The cmd string should be an actual AmigaGuide command
  838.  *        in the form "LINK keyword".)
  839.  *
  840.  *        After calling this function, you should call CleanupHelp()
  841.  *        before exiting the program, to ensure that the AmigaGuide
  842.  *        system is safely closed down.
  843.  *
  844.  *        If SnoopDos is running on a separate screen to the one currently
  845.  *        in use by AmigaGuide, AmigaGuide will be shut down and re-opened
  846.  *        on the current screen.
  847.  *
  848.  *        Returns true for success, false for failure. However, it puts
  849.  *        up its own error messages, so you can pretty much ignore the
  850.  *        return value. (Calling CleanupAGuide() is always safe.)
  851.  *
  852.  *        Note: cmdstring should be a permanent string, not a temporary
  853.  *        (at least until the next time this function is called!)
  854.  */
  855. int ShowAGuide(char *cmdstring)
  856. {
  857.     if (!AmigaGuideBase) {
  858.         AmigaGuideBase = OpenLibrary("amigaguide.library", 34);
  859.         if (!AmigaGuideBase) {
  860.             ShowError(MSG(MSG_ERROR_NO_AMIGAGUIDE));
  861.             return (FALSE);
  862.         }
  863.     }
  864.     if (!CheckForScreen())
  865.         return (FALSE);
  866.     
  867.     if (AG_Context && AG_NewGuide.nag_Screen != SnoopScreen) {
  868.         CloseAmigaGuide(AG_Context);
  869.         AG_Context      = NULL;
  870.         AmigaGuideMask = 0;
  871.     }
  872.     if (!AG_Context) {
  873.         /*
  874.          *        AmigaGuide wasn't already running so open it up on
  875.          *        our current screen
  876.          *
  877.          *        The docs say that we must ensure the screen pointer
  878.          *        AmigaGuide gets remains valid for the duration of
  879.          *        AmigaGuide use. What happens if someone closes the
  880.          *        main SnoopDos window causing us to remove our lock on
  881.          *        the public screen?
  882.          *
  883.          *        Well, since earlier, we ensured that we always force
  884.          *        AmigaGuide to open on our current screen, and since a
  885.          *        public screen can't close as long as AmigaGuide has some
  886.          *        windows on it, the only potential for a problem would be
  887.          *        if AmigaGuide tried to open a new window on its screen
  888.          *        on its own -- and it will never do that since we always
  889.          *        control it via this function.
  890.          */
  891.         static char copycmd[80];
  892.  
  893.         AG_NewGuide.nag_Lock        = NULL;
  894.         AG_NewGuide.nag_Name        = MSG(MSG_HELPFILE_NAME);
  895.         AG_NewGuide.nag_Screen        = SnoopScreen;
  896.         AG_NewGuide.nag_ClientPort    = HELP_AREXX_PORT;
  897.         AG_NewGuide.nag_BaseName    = HELP_BASENAME;
  898.         AG_NewGuide.nag_Flags        = HTF_CACHE_NODE;
  899.  
  900.         AG_Context = OpenAmigaGuideAsync(&AG_NewGuide, TAG_DONE);
  901.         if (!AG_Context) {
  902.             ShowError(MSG(MSG_ERROR_CREATE_AMIGAGUIDE));
  903.             return (FALSE);
  904.         }
  905.         AmigaGuideMask = AmigaGuideSignal(AG_Context);
  906.         /*
  907.          *        Now since AmigaGuide won't initialise immediately, it's
  908.          *        no use sending this help request to it -- we need to wait
  909.          *        until it tells us that it's ready for action. So, we
  910.          *        remember the command that was to be sent, and when we
  911.          *        get the okay from AmigaGuide, we can send it (see
  912.          *        HandleAGuideMsgs() below.)
  913.          *
  914.          *        Also, since sometimes the string that we are sent is only
  915.          *        temporary (e.g. from an ARexx message) we need to make a
  916.          *        copy of it.
  917.          */        
  918.         strcpy(copycmd, cmdstring);
  919.         PendingAGuideMsg = copycmd;
  920.         return (TRUE);
  921.     }
  922.  
  923.     /*
  924.      *        Usual case: just send the command directly to AmigaGuide
  925.      */
  926.     SendAmigaGuideCmd(AG_Context, cmdstring, NULL);
  927.     return (TRUE);
  928. }
  929.  
  930. /*
  931.  *        HandleAGuideMsgs()
  932.  *
  933.  *        Handles any AmigaGuide messages (as indicate by a signal
  934.  *        arriving that matches AmigaGuideMask)
  935.  */
  936. void HandleAGuideMsgs(void)
  937. {
  938.     struct AmigaGuideMsg *agm;
  939.     int unloadhelp = 0;
  940.  
  941.     if (!AmigaGuideBase || !AG_Context)
  942.         return;
  943.  
  944.     while ((agm = GetAmigaGuideMsg(AG_Context)) != NULL) {
  945.         switch (agm->agm_Type) {
  946.  
  947.             case ActiveToolID:
  948.                 /*
  949.                  *        The first time we try and display something,
  950.                  *        AmigaGuide has to load first. We get this
  951.                  *        message when it's finished loading, to let
  952.                  *        us know we can now display the help.
  953.                  */
  954.                 if (PendingAGuideMsg) {
  955.                     SendAmigaGuideCmd(AG_Context, PendingAGuideMsg, NULL);
  956.                     PendingAGuideMsg = NULL;
  957.                 }
  958.                 break;
  959.  
  960.             case ToolCmdReplyID:
  961.             case ToolStatusID:
  962.             case ShutdownMsgID:
  963.                 if (agm->agm_Pri_Ret){
  964.                     if (agm->agm_Sec_Ret == HTERR_CANT_OPEN_DATABASE) {
  965.                         ShowError(MSG(MSG_ERROR_AGUIDE_CANT_OPEN),
  966.                                   MSG(MSG_HELPFILE_NAME));
  967.                         unloadhelp = 1;
  968.                     } else {
  969.                         ShowError(MSG(MSG_ERROR_AGUIDE_GENERIC),
  970.                                   GetAmigaGuideString(agm->agm_Sec_Ret));
  971.                     }
  972.                     PendingAGuideMsg = NULL;    /* Cancel any pending cmd */
  973.                 }
  974.                 break;
  975.         }
  976.         ReplyAmigaGuideMsg(agm);
  977.     }
  978.     if (unloadhelp)
  979.         CleanupAGuide();
  980. }
  981.  
  982. /*
  983.  *        CleanupAGuide()
  984.  *
  985.  *        Frees all resources allocated by ShowAGuide(). Should only be
  986.  *        called at the end of the program.
  987.  */
  988. void CleanupAGuide(void)
  989. {
  990.     if (AG_Context)        CloseAmigaGuide(AG_Context),    AG_Context = NULL;
  991.     if (AmigaGuideBase)    CloseLibrary(AmigaGuideBase),    AmigaGuideBase = NULL;
  992.     AmigaGuideMask = 0;
  993. }
  994.  
  995. /*
  996.  *        CreateCustomImage(imagetype, height)
  997.  *
  998.  *        Creates an image of the specified type (IMAGE_FILE or IMAGE_FONT)
  999.  *        and returns a pointer to an image structure with details about
  1000.  *        the image. The return value + 1 points to the same image, but
  1001.  *        selected.
  1002.  *
  1003.  *        height is the height of the image -- the width is fixed at 16 pixels.
  1004.  *
  1005.  *        N.b. ScreenScreen should be valid when this function is called. You
  1006.  *        should call FreeCustomImage(imageptr) to free up any memory allocated
  1007.  *        when you're done.
  1008.  */
  1009. struct Image *CreateCustomImage(int imagetype, int height)
  1010. {
  1011. #define NUM_ELS(x)    (sizeof(x) / sizeof(x[0]))
  1012.  
  1013.     ULONG *imagedata  = FileButtonData;
  1014.     int imbytesperrow = 4;    /* Must be even */
  1015.     int imwidth          = 20;
  1016.     int imheight      = NUM_ELS(FileButtonData);
  1017.     int depth          = SnoopScreen->RastPort.BitMap->Depth;
  1018.  
  1019.     struct CustomImage  *ci;
  1020.     struct CustomBitMap *cbm;
  1021.     UBYTE *bitplane;
  1022.     int size;
  1023.  
  1024.     if (imagetype == IMAGE_FONT) {
  1025.         imagedata = FontButtonData;
  1026.         imheight  = NUM_ELS(FontButtonData);
  1027.         if (height <= 12)    /* Strip last line if gadget is tiny */
  1028.             imheight--;
  1029.     }
  1030.  
  1031.     /*
  1032.      *        For the gadget, we need to store two full bitmaps, one for
  1033.      *        the selected image and one for the unselected image. We
  1034.      *        allocate a single structure in CHIP ram that holds all the
  1035.      *        details we need.
  1036.      */
  1037.     size = sizeof(struct CustomImage) + imbytesperrow * height * depth * 2;
  1038.     ci   = AllocMem(size, MEMF_CHIP | MEMF_CLEAR);
  1039.     if (!ci)
  1040.         return (NULL);
  1041.     
  1042.     ci->size = size;
  1043.  
  1044.     /*
  1045.      *        Now initialise our two images -- the steps are almost identical
  1046.      *        for each, except that one is filled and one is not.
  1047.      */
  1048.     bitplane = (UBYTE *)(ci+1);
  1049.     for (cbm = ci->image; cbm < &ci->image[2]; cbm++) {
  1050.         struct RastPort *rport  = &cbm->rport;
  1051.         struct BitMap   *bitmap = &cbm->bitmap;
  1052.         int fillpen;
  1053.         int textpen;
  1054.         int shinepen;
  1055.         int shadowpen;
  1056.         int planemask;
  1057.         int i;
  1058.  
  1059.         InitRastPort(rport);
  1060.         InitBitMap(bitmap, depth, imwidth, height);
  1061.  
  1062.         /*
  1063.          *        Now initialise the plane pointers accordingly
  1064.          */
  1065.         for (i = 0; i < depth; i++) {
  1066.             bitmap->Planes[i] = bitplane;
  1067.             bitplane += (imbytesperrow * height);
  1068.         }
  1069.         rport->BitMap = bitmap;
  1070.  
  1071.         /*
  1072.          *        Now we're ready to render our chosen image.
  1073.          *        First let's draw the box and box highlight.
  1074.          *        If we're drawing the selected state, we
  1075.          *        invert the highlight colours to make the box
  1076.          *        look indented.
  1077.          */
  1078.         shinepen    = ScreenDI->dri_Pens[SHINEPEN];
  1079.         shadowpen    = ScreenDI->dri_Pens[SHADOWPEN];
  1080.         if (cbm != ci->image) {
  1081.             int swappen = shinepen;
  1082.             shinepen      = shadowpen;
  1083.             shadowpen     = swappen;
  1084.         }
  1085.         SetDrMd(rport, JAM1);
  1086.         SetAPen(rport, shinepen);
  1087.         Move(rport, 0, height-1);
  1088.         Draw(rport, 0, 0);
  1089.         Draw(rport, imwidth-2, 0);
  1090.         Move(rport, 1, height-2);
  1091.         Draw(rport, 1, 1);
  1092.         SetAPen(rport, shadowpen);
  1093.         Move(rport, imwidth-1, 0);
  1094.         Draw(rport, imwidth-1, height-1);
  1095.         Draw(rport, 1, height-1);
  1096.         Move(rport, imwidth-2, 1);
  1097.         Draw(rport, imwidth-2, height-2);
  1098.  
  1099.         /*
  1100.          *        Now, if we're on the second round, fill in the box
  1101.          *        in the select colour
  1102.          */
  1103.         fillpen = 0;
  1104.         textpen = 1;
  1105.         if (cbm != ci->image) {
  1106.             fillpen = ScreenDI->dri_Pens[FILLPEN];
  1107.             textpen = ScreenDI->dri_Pens[FILLTEXTPEN];
  1108.             SetAPen(rport, fillpen);
  1109.             RectFill(rport, 2, 1, imwidth-3, height-2);
  1110.         }
  1111.         /*
  1112.          *        Now the tricky bit -- we need to copy our image data
  1113.          *        into the middle of the box icon we've just created.
  1114.          *        Luckily, the data is already word-aligned for us so
  1115.          *        we just need to worry about vertically centering it.
  1116.          *        We use a little XOR trick to figure out which planes
  1117.          *        need to be updated, and also to do the updating.
  1118.          */
  1119.         planemask = fillpen ^ textpen;
  1120.         for (i = 0; i < depth; i++) {
  1121.             if (planemask & (1 << i)) {
  1122.                 ULONG *src  = imagedata;
  1123.                 ULONG *dest = ((ULONG *)(cbm->bitmap.Planes[i])) +
  1124.                               (height - imheight) / 2;
  1125.                 int j;
  1126.  
  1127.                 for (j = 0; j < imheight; j++)
  1128.                     *dest++ ^= *src++;
  1129.             }
  1130.         }
  1131.         /*
  1132.          *        All done, now initialise the image accordingly
  1133.          */
  1134.         cbm->image.Width     = imwidth;
  1135.         cbm->image.Height    = height;
  1136.         cbm->image.Depth     = depth;
  1137.         cbm->image.ImageData = (UWORD *)(cbm->bitmap.Planes[0]);
  1138.         cbm->image.PlanePick = (1 << depth) - 1;
  1139.     }
  1140.     /*
  1141.      *        To ensure that our two image structures our consecutive,
  1142.      *        we copy the second bitmap's image back into the first
  1143.      *        image's bitmap's alternate image
  1144.      */
  1145.     ci->image[0].altimage = ci->image[1].image;
  1146.     return ((struct Image *)ci);
  1147. }
  1148.  
  1149. /*
  1150.  *        FreeCustomImage(image)
  1151.  *
  1152.  *        Release a custom image allocated earlier by CreateCustomImage()
  1153.  */
  1154. void FreeCustomImage(struct Image *image)
  1155. {
  1156.     if (image)
  1157.         FreeMem(image, ((struct CustomImage *)image)->size);
  1158. }
  1159.  
  1160. /*
  1161.  *        ConvertIMsgToChar()
  1162.  *
  1163.  *        Takes an IntuiMessage of IDCMP_RAWKEY and returns the single
  1164.  *        character representation it corresponds to. If this isn't
  1165.  *        possible (e.g. a HELP key or cursor key) then returns 0
  1166.  *        instead.
  1167.  */
  1168. int ConvertIMsgToChar(struct IntuiMessage *imsg)
  1169. {
  1170.     static struct InputEvent ev;        /* Ensure initalised to 0 */
  1171.     char buffer[9];
  1172.     int len;
  1173.  
  1174.     if (imsg->Class != IDCMP_RAWKEY)
  1175.         return (0);
  1176.  
  1177.     ev.ie_Class          = IECLASS_RAWKEY;
  1178.     ev.ie_Code           = imsg->Code;
  1179.     ev.ie_Qualifier     = imsg->Qualifier;
  1180.     ev.ie_EventAddress    = *(APTR *)imsg->IAddress;
  1181.     len = MapRawKey(&ev, buffer, 8, NULL);
  1182.     if (len != 1)
  1183.         return (0);
  1184.     return (buffer[0]);
  1185. }
  1186.