/* * MISCWIN.C vi:ts=4 * * Copyright (c) Eddy Carroll, September 1994. * * This module contains miscellaneous functions associated with * the SnoopDos GUI, but nevertheless not directly tied to any * particular window. * */ #include "system.h" #include "snoopdos.h" #include "gui.h" #if 0 #define DB(s) KPrintF(s) #else #define DB(s) #endif /* * Four dummy requesters for use when disabling window input */ struct FileRequester *SnoopDosFR; APTR AG_Context; struct NewAmigaGuide AG_NewGuide; char *PendingAGuideMsg; extern struct TextAttr TopazFontAttr; extern struct TextAttr SystemFontAttr; extern struct TextAttr WindowFontAttr; extern struct TextAttr BufferFontAttr; /* * Now our busy pointer for V37 users (borrowed from ToolMaker) */ UWORD __chip WaitPointer[36] = { 0x0000, 0x0000, 0x0400, 0x07C0, 0x0000, 0x07C0, 0x0100, 0x0380, 0x0000, 0x07E0, 0x07C0, 0x1FF8, 0x1FF0, 0x3FEC, 0x3FF8, 0x7FDE, 0x3FF8, 0x7FBE, 0x7FFC, 0xFF7F, 0x7EFC, 0xFFFF, 0x7FFC, 0xFFFF, 0x3FF8, 0x7FFE, 0x3FF8, 0x7FFE, 0x1FF0, 0x3FFC, 0x07C0, 0x1FF8, 0x0000, 0x07E0, 0x0000, 0x0000 }; ULONG __chip FileButtonData[] = { 0x0FF00000, 0x0C180000, 0x0C140000, 0x0C120000, 0x0C1F0000, 0x0C030000, 0x0C030000, 0x0C030000, 0x0C030000, 0x0FFF0000, }; ULONG __chip FontButtonData[] = { 0x0FE00000, 0x09200000, 0x01000000, 0x017F0000, 0x01490000, 0x01080000, 0x01080000, 0x00080000, 0x00080000, 0x00080000, }; /* * This structure holds all the information we need to know about * one of our custom images */ struct CustomBitMap { struct Image image; struct Image altimage; struct BitMap bitmap; struct RastPort rport; }; struct CustomImage { struct CustomBitMap image[2]; int size; }; /***************************************************************************** * * Start of functions! * *****************************************************************************/ /* * DisableWindow(window, requester) * * Disables the specified window by displaying a dummy requester in it * and turning on the wait pointer. It is assumed that the window * exists and has not been disabled already. */ void DisableWindow(struct Window *win, struct Requester *req) { InitRequester(req); if (Request(req, win)) { /* * Use the new V39 system busy pointer if possible, else * drop back to our own busy pointer */ if (IntuitionBase->LibNode.lib_Version >= 39) SetWindowPointer(win, WA_BusyPointer, TRUE, WA_PointerDelay, TRUE, TAG_DONE); else SetPointer(win, WaitPointer, 16, 16, -6, 0); } } /* * EnableWindow() * * Enables the specified window by removing the dummy requester * placed inside it earlier. You must have called DisableWindow() * with the same parameters first. */ void EnableWindow(struct Window *win, struct Requester *req) { EndRequest(req, win); ClearPointer(win); } /* * DisableAllWindows() * * Disables all windows by opening a dummy requester in them and * setting the window pointer to busy. Calls to this function * nest, so be sure to call EnableAllWindows the correct number * of times. This is intended for use when displaying modal * requesters (e.g. ASL, error messages, etc.) */ void DisableAllWindows(void) { DisableNestCount++; if (DisableNestCount == 1) { if (MainWindow) { /* * If we are disabling the main window, we won't be able * to respond to IDCMP_SIZEVERIFY messages, so instead, we * just stop requesting them. This lets the user resize the * window when we have a file/font requester up, even though * it won't redraw until the requester is finished. */ ModifyIDCMP(MainWindow,MainWindow->IDCMPFlags & ~IDCMP_SIZEVERIFY); DisableWindow(MainWindow, &MainRequester); } if (SetWindow) DisableWindow(SetWindow, &SetRequester); if (FuncWindow) DisableWindow(FuncWindow, &FuncRequester); if (FormWindow) DisableWindow(FormWindow, &FormRequester); } } /* * EnableAllWindows() * * Re-enables all windows disabled by calling DisableAllWindows() */ void EnableAllWindows(void) { DisableNestCount--; if (DisableNestCount == 0) { if (SetWindow) EnableWindow(SetWindow, &SetRequester); if (FuncWindow) EnableWindow(FuncWindow, &FuncRequester); if (FormWindow) EnableWindow(FormWindow, &FormRequester); if (MainWindow) { /* * Because the user might have resized the main window * while we were disabled, causing us to miss the resize * message (which happens when IDCMP_SIZEVERIFY is active) * we check to see if the size has changed and if it has, * cause a fake RESIZE message to be sent to ourself. */ EnableWindow(MainWindow, &MainRequester); ModifyIDCMP(MainWindow, MainWindow->IDCMPFlags | IDCMP_SIZEVERIFY); if (MainWindow->Width != CurWindowWidth || MainWindow->Height != CurWindowHeight) { SizeWindow(MainWindow, 0, 0); } } } } /* * RecordWindowSizes() * * Records the current size and position of all open windows on the * display so that they can be re-opened in the same position next time. * Usually called before a window is closed, but may also be called * when (for example) settings are being saved. */ void RecordWindowSizes(void) { if (MainWindow) { CurSettings.MainWinLeft = MainWindow->LeftEdge; CurSettings.MainWinTop = MainWindow->TopEdge; CurSettings.MainWinWidth = MainWindow->Width; CurSettings.MainWinHeight = MainWindow->Height; } if (FormWindow) { CurSettings.FormWinLeft = FormWindow->LeftEdge; CurSettings.FormWinTop = FormWindow->TopEdge; } if (FuncWindow) { CurSettings.FuncWinLeft = FuncWindow->LeftEdge; CurSettings.FuncWinTop = FuncWindow->TopEdge; } if (SetWindow) { CurSettings.SetupWinLeft = SetWindow->LeftEdge; CurSettings.SetupWinTop = SetWindow->TopEdge; } } /* * ShowError(errormsg) * * Displays the specified error message in a requester (on the same * screen as the main SnoopDos window if possible, else on the * default window), waits for an OKAY click, and returns. */ void ShowError(char *errormsg, ...) { static struct EasyStruct es = { sizeof(struct EasyStruct), 0, "SnoopDos", NULL, NULL }; int pausestate = Paused; Paused = 0; es.es_TextFormat = errormsg; es.es_GadgetFormat = MSG(MSG_ERROR_OKAY); DisableAllWindows(); /* * If MainWindow is NULL, then the requester will appear on * the default public screen */ EasyRequestArgs(MainWindow, &es, 0, (&errormsg)+1); EnableAllWindows(); Paused = pausestate; } /* * GetResponse(prompt, message, params, ...) * * Displays an easy requester with the specified prompts, waits for the * user to click on an option, then returns the selected option. * * Note that options are numbered 1,2,3....,0 with the leftmost gadget * returning 1 and the rightmost gadget returning 0. */ int GetResponse(char *prompts, char *reqmsg, ...) { static struct EasyStruct es = { sizeof(struct EasyStruct), 0, "SnoopDos", NULL, NULL }; int pausestate = Paused; int choice; Paused = 0; es.es_TextFormat = reqmsg; es.es_GadgetFormat = prompts; DisableAllWindows(); choice = EasyRequestArgs(MainWindow, &es, 0, (&reqmsg)+1); EnableAllWindows(); Paused = pausestate; return (choice); } /* * MaxTextLen(font, textarray) * * Returns the length (in pixels) of the longest string in the given * zero-terminated array of message IDs, using the given font. */ int MaxTextLen(struct TextFont *font, int *ids) { struct RastPort rp; int maxlen = 0; InitRastPort(&rp); SetFont(&rp, font); while (*ids) { char *msg = MSG(*ids++); int len = TextLength(&rp, msg, strlen(msg)); if (len > maxlen) maxlen = len; } return (maxlen); } /* * GetTextLen(font, msg) * * Returns the length (in pixels) of the given message indicated by the * message ID, when rendered in the given font. */ int GetTextLen(struct TextFont *font, char *msg) { struct RastPort rp; InitRastPort(&rp); SetFont(&rp, font); return (TextLength(&rp, msg, strlen(msg))); } /* * AddKeyShortcut(shortcut, gadid, msgid) * * Checks the string corresponding to msgid for an underlined * character. If one is found, then a corresponding entry is * made in the shortcut array with that gadget ID. * * For alphabetic shortcuts, we make entries for both the lower * and upper case versions of the key (since we might have * strings like "_Cancel" and "U_se" and we want both kinds * to be treated identically). */ void AddKeyShortcut(UBYTE *shortcut, int gadid, int msgid) { UBYTE *p = MSG(msgid); while (*p) { if (*p++ == '_') { UBYTE ch = *p; if (ch >= 'a' && ch <= 'z') ch &= 0x5F; shortcut[ch] = gadid; if (ch >= 'A' && ch <= 'Z') shortcut[ch + 32] = gadid; return; } } } /* * ShowGadget(window, gadget, type) * * Activates or deactives the described gadget, by showing it in * a GADGET_DOWN or GADGET_UP rendered state. This is mainly used * to give the user direct feedback as to what's happening when * they hit a keyboard shortcut. * * Automatically ignores disabled gadgets. */ void ShowGadget(struct Window *win, struct Gadget *gad, int type) { if ((gad->Flags & GFLG_DISABLED) == 0) { int gadpos = RemoveGadget(win, gad); if (type == GADGET_DOWN) gad->Flags |= GFLG_SELECTED; else gad->Flags &= ~GFLG_SELECTED; AddGadget(win, gad, gadpos); RefreshGList(gad, win, NULL, 1); } } /* * MyOpenFont() * * Tries to open the specified font. If we can't open diskfont.library, * then falls back to the ROM OpenFont() in graphics.library. We do * this so that we can still run, even when there is no diskfont.library * around. * * Returns a pointer to the opened font, or NULL for failure. */ struct TextFont *MyOpenFont(struct TextAttr *textattr) { if (DiskfontBase) return OpenDiskFont(textattr); else return OpenFont(textattr); } /* * CheckForASL() * * Tries to load asl.library and displays an error requester if it * can't be located. Returns TRUE if successful, or FALSE if it * couldn't be loaded. All functions using asl.library functions * must call this first. * * We do it like this since it's useful to be able to run SnoopDos * even when asl.library isn't around. */ int CheckForASL(void) { if (!DiskfontBase) { DiskfontBase = OpenLibrary("diskfont.library", 0); if (!DiskfontBase) { ShowError(MSG(MSG_ERROR_NO_DISKFONT)); return (FALSE); } } if (!AslBase) { AslBase = OpenLibrary("asl.library", 0); if (!AslBase) { ShowError(MSG(MSG_ERROR_NO_ASL)); return (FALSE); } } return (TRUE); } /* * SelectFont(win, fonttype) * * Requests a font from the user of the appropriate type (either * FONTSEL_WINDOW or FONTSEL_BUFFER). Returns TRUE if a font was * successfully chosen, else FALSE. win is the window this * requester is associated with (used to choose the correct * opening screen). * * If successful, the font requester (WindowFR or BufferFR) will * hold details of the selected font. */ int SelectFont(struct Window *win, int fonttype) { struct FontRequester **fontreq; struct TextAttr *fontattr; struct TextAttr *reqfont; struct TextFont *tempfont; int fixedwidth; char *title; int retval; int pausestate = Paused; if (!CheckForASL()) return (FALSE); /* * Now a little check to make sure we can actually open * the current window font. If we can't, fall back to the * system font. (This should never arise, but if it does, * the user is in the unfortunate position of not being * able to change to a font that IS recognised, since the * font requester can't be opened!) */ reqfont = &WindowFontAttr; tempfont = MyOpenFont(reqfont); if (tempfont) CloseFont(tempfont); else reqfont = &SystemFontAttr; /* * Next, initialise according to the font type being selected */ if (fonttype == FONTSEL_BUFFER) { fontreq = &BufferFR; fontattr = &BufferFontAttr; title = MSG(MSG_BFONT_TITLE); fixedwidth = TRUE; } else { fontreq = &WindowFR; fontattr = &WindowFontAttr; title = MSG(MSG_GFONT_TITLE); fixedwidth = FALSE; } if (*fontreq == NULL) { *fontreq = (struct FontRequester *) AllocAslRequestTags(ASL_FontRequest, ASLFO_PrivateIDCMP, TRUE, ASLFO_TitleText, title, ASLFO_PositiveText, MSG(MSG_ERROR_OKAY), ASLFO_NegativeText, MSG(MSG_ERROR_CANCEL), ASLFO_FixedWidthOnly, fixedwidth, ASLFO_InitialHeight, SnoopScreen->Height*3/4, TAG_DONE); if (!*fontreq) { ShowError(MSG(MSG_ERROR_OPENFONT)); return (FALSE); } } Paused = 0; DisableAllWindows(); retval = AslRequestTags(*fontreq, ASLFO_TextAttr, reqfont, ASLFO_Window, win, ASLFO_InitialName, fontattr->ta_Name, ASLFO_InitialSize, fontattr->ta_YSize, TAG_DONE); EnableAllWindows(); Paused = pausestate; if (retval && fonttype == FONTSEL_BUFFER) { /* * Now do an additional check to ensure we really got a * non-proportional font, since the ASL requester (as of V39) * can sometimes return a proportional font even when the * FixedWidthOnly tag is set. If we got a proportional font, * ignore the selection. */ struct TextFont *font = MyOpenFont(&BufferFR->fo_Attr); if (!font || (font->tf_Flags & FPF_PROPORTIONAL)) { ShowError(MSG(MSG_ERROR_FONT_PROPORTIONAL)); retval = FALSE; } if (font) CloseFont(font); } return (retval); } /* * SelectFile(char *newname, char *defname, win, type) * * Requests a file from the user of the appropriate type: * * FILESEL_LOADCONFIG - Loading a configuration * FILESEL_SAVECONFIG - Saving a configuration * FILESEL_DEFLOGNAME - Default log filename * FILESEL_NEWLOGNAME - Current log filename * * Returns TRUE for success, FALSE for failure. * * newname is the buffer to store the full pathname of the selected file. * defname is the default path/filename to use in the requester. * * defname and newname may both point to the same string -- newname is * only updated if a file is actually selected. */ int SelectFile(char *newname, char *defname, struct Window *win, int type) { char pathname[100]; char filename[50]; struct TextAttr *reqfont; struct TextFont *tempfont; int titleid; int retval; int pausestate = Paused; int savemode; int n; if (!CheckForASL()) return (FALSE); switch (type) { case FILESEL_LOADCONFIG: titleid = MSG_ASL_LOADCONFIG; break; case FILESEL_SAVECONFIG: titleid = MSG_ASL_SAVECONFIG; break; case FILESEL_DEFLOGNAME: titleid = MSG_ASL_DEFLOGNAME; break; case FILESEL_NEWLOGNAME: titleid = MSG_ASL_NEWLOGNAME; break; case FILESEL_SAVEWINDOW: titleid = MSG_ASL_SAVEWINDOW; break; case FILESEL_SAVEBUFFER: titleid = MSG_ASL_SAVEBUFFER; break; } savemode = (type != FILESEL_LOADCONFIG); /* * Now a little check to make sure we can actually open * the current window font. If we can't, fall back to the * system font. */ reqfont = &WindowFontAttr; tempfont = MyOpenFont(reqfont); if (tempfont) CloseFont(tempfont); else reqfont = &SystemFontAttr; strcpy(filename, FilePart(defname)); n = PathPart(defname) - defname; if (n) strncpy(pathname, defname, n); pathname[n] = '\0'; if (SnoopDosFR == NULL) { SnoopDosFR = (struct FileRequester *) AllocAslRequestTags(ASL_FileRequest, ASLFR_PrivateIDCMP, TRUE, ASLFR_PositiveText, MSG(MSG_ERROR_OKAY), ASLFR_NegativeText, MSG(MSG_ERROR_CANCEL), ASLFR_RejectIcons, TRUE, ASLFR_InitialHeight, SnoopScreen->Height*3/4, TAG_DONE); if (!SnoopDosFR) { ShowError(MSG(MSG_ERROR_OPENFILEREQ)); return (FALSE); } } /* * It's important that we disable windows while the * file requester is displayed, otherwise users might * become confused */ Paused = 0; DisableAllWindows(); retval = AslRequestTags(SnoopDosFR, ASLFR_TitleText, MSG(titleid), ASLFR_Window, win, ASLFR_TextAttr, reqfont, ASLFR_InitialFile, filename, ASLFR_InitialDrawer, pathname, ASLFR_DoSaveMode, savemode, TAG_DONE); EnableAllWindows(); Paused = pausestate; /* * Now build our return filename. If no output filename was selected * but a directory was given, then we only allow a successful if * the "directory" is actually a device -- if it's a real directory * we return failure (since you can't save or load a directory). * * We even return failure if a device is specified as the directory * and we're trying to load a file, since loading from a printer * makes no sense. (Mind you, loading a config file from a CON: file * might be an interesting way to provide a built-in command line * interpreter!) */ if (retval) { if (*SnoopDosFR->fr_File || (savemode && !IsFileSystem(SnoopDosFR->fr_Drawer))) { strcpy(newname, SnoopDosFR->fr_Drawer); AddPart(newname, SnoopDosFR->fr_File, 255); } else retval = FALSE; } return (retval); } /* * InitFonts() * * Initialises our default fonts (system, topaz) and * updates the current font settings to whichever font is * appropriate, if they have not already been set to * a particular font. */ void InitFonts(void) { /* * We have four fonts in total; the current gadget and buffer fonts, * the system font, and finally topaz 8 point. If we fail to open * a window using either of the first two, we will try again using * the third and finally using the fourth before giving up. * * We default to having the gadget font be the same as the screen * font, and having the buffer font the same as the system font. */ strcpy(SystemFontName, GfxBase->DefaultFont->tf_Message.mn_Node.ln_Name); SystemFontAttr.ta_YSize = GfxBase->DefaultFont->tf_YSize; BufferFontAttr.ta_YSize = BufferFontSize; WindowFontAttr.ta_YSize = WindowFontSize; if (BufferFontSize == 0) { strcpy(BufferFontName, SystemFontName); BufferFontAttr.ta_YSize = SystemFontAttr.ta_YSize; BufferFontSize = BufferFontAttr.ta_YSize; } if (SnoopScreen && WindowFontSize == 0) { strcpy(WindowFontName, SnoopScreen->Font->ta_Name); WindowFontAttr.ta_YSize = SnoopScreen->Font->ta_YSize; WindowFontSize = WindowFontAttr.ta_YSize; } } /* * SetupScreen() * * Initialises the font and other variables which depend on the * user's chosen screen. This is called prior to opening any * windows on a screen. * * If we can't open on the user's desired screen (maybe it doesn't * exist) then we fall back to Workbench. If we can't even open on * Workbench, then we're really in trouble and should probably quit. * * We maintain the screen info until CleanupScreen() is called (usually * because SnoopDos is going into a hidden state). * * Returns TRUE for success, false for failure. SnoopScreen can also * be checked at any time to see if we have a valid screen or not. * */ int SetupScreen(void) { struct ColorMap *cm; struct Screen *scr; char *scrname = NULL; /* Default is default public screen */ struct List *psl; struct PubScreenNode *psn; /* * First, let's locate the screen we want, according to the * user's preference. If we can't open the preferred screen, * we try again with the default public screen. If we can't * open the default public screen, we try Workbench. If we * can't open THAT, then we're really screwed. */ if (SnoopScreen) CleanupScreen(); switch (CurSettings.Setup.ScreenType) { case SCREEN_FRONT: /* * We want to open on the front public screen if possible. * The only way to find out if the current screen is a * public screen is to read IntuitionBase->FirstScreen and * then try and find a match for it on the public screen list. * If we do, then the public screen structure will give the * public name of the screen, which we can then use to * lock it. */ psl = (void *)LockPubScreenList(); if (psl) { scr = IntuitionBase->FirstScreen; FORLIST(psl, psn) { if (psn->psn_Screen == scr) { scrname = psn->psn_Node.ln_Name; break; } } UnlockPubScreenList(); } break; case SCREEN_NAMED: scrname = CurSettings.Setup.ScreenName; break; } scr = LockPubScreen(scrname); if (!scr && scrname) scr = LockPubScreen(NULL); if (!scr) { scr = LockPubScreen("Workbench"); if (!scr) return (FALSE); } BorderLeft = scr->WBorLeft; BorderRight = scr->WBorRight; BorderTop = scr->WBorTop; BorderBottom = scr->WBorBottom; ScreenWidth = scr->Width; ScreenHeight = scr->Height; TitlebarHeight = BorderTop + scr->Font->ta_YSize + 1; /* * Now calculate aspect ratio of screen, so we can scale the * horizontal scroll bar accordingly. */ SquareAspect = 1; /* Assume square aspect ratio */ GadgetHeight = HI_GADGET_HEIGHT; GadgetSpacing = HI_GADGET_SPACING; cm = scr->ViewPort.ColorMap; if (cm) { struct TagItem ti[] = { VTAG_VIEWPORTEXTRA_GET, NULL, VTAG_END_CM, NULL }; if (VideoControl(cm, ti) == NULL) { struct ViewPortExtra *vpe = (struct ViewPortExtra *)ti[0].ti_Data; struct Rectangle *rect = &vpe->DisplayClip; if ((rect->MaxX - rect->MinX)/2 > (rect->MaxY - rect->MinY)) { SquareAspect = 0; GadgetHeight = LO_GADGET_HEIGHT; GadgetSpacing = LO_GADGET_SPACING; } } } SnoopScreen = scr; ScreenDI = GetScreenDrawInfo(SnoopScreen); /* * Get the sizing image used by windows. We need this info * so that we can align our scroll arrows and scroll bars * correctly. */ ScreenResolution = (SnoopScreen->Flags & SCREENHIRES) ? SYSISIZE_MEDRES : SYSISIZE_LOWRES; SizeImage = (struct Image *)NewObject(NULL, "sysiclass", SYSIA_DrawInfo, ScreenDI, SYSIA_Which, SIZEIMAGE, SYSIA_Size, ScreenResolution, TAG_END); if (!SizeImage) { CleanupScreen(); return (NULL); } InitFonts(); return (TRUE); } /* * CleanupScreen() * * Frees resources associated with the screen. Called when * SnoopDos is about to go into HIDE mode. */ void CleanupScreen(void) { if (SizeImage) { DisposeObject(SizeImage); SizeImage = NULL; } if (SnoopScreen) { if (ScreenDI) { FreeScreenDrawInfo(SnoopScreen, ScreenDI); ScreenDI = NULL; } UnlockPubScreen(NULL, SnoopScreen); SnoopScreen = NULL; } } /* * CleanupWindows() * * Frees all resources allocated in windows module */ void CleanupWindows(void) { CleanupMainWindow(); CleanupSubWindows(); CleanupScreen(); if (WindowFR) FreeAslRequest(WindowFR), WindowFR = NULL; if (BufferFR) FreeAslRequest(BufferFR), BufferFR = NULL; if (SnoopDosFR) FreeAslRequest(SnoopDosFR), SnoopDosFR = NULL; } /* * ShowAGuide(cmdstring) * * Attempts to open AmigaGuide and display help on the specified * topic. (The cmd string should be an actual AmigaGuide command * in the form "LINK keyword".) * * After calling this function, you should call CleanupHelp() * before exiting the program, to ensure that the AmigaGuide * system is safely closed down. * * If SnoopDos is running on a separate screen to the one currently * in use by AmigaGuide, AmigaGuide will be shut down and re-opened * on the current screen. * * Returns true for success, false for failure. However, it puts * up its own error messages, so you can pretty much ignore the * return value. (Calling CleanupAGuide() is always safe.) * * Note: cmdstring should be a permanent string, not a temporary * (at least until the next time this function is called!) */ int ShowAGuide(char *cmdstring) { if (!AmigaGuideBase) { AmigaGuideBase = OpenLibrary("amigaguide.library", 34); if (!AmigaGuideBase) { ShowError(MSG(MSG_ERROR_NO_AMIGAGUIDE)); return (FALSE); } } if (!CheckForScreen()) return (FALSE); if (AG_Context && AG_NewGuide.nag_Screen != SnoopScreen) { CloseAmigaGuide(AG_Context); AG_Context = NULL; AmigaGuideMask = 0; } if (!AG_Context) { /* * AmigaGuide wasn't already running so open it up on * our current screen * * The docs say that we must ensure the screen pointer * AmigaGuide gets remains valid for the duration of * AmigaGuide use. What happens if someone closes the * main SnoopDos window causing us to remove our lock on * the public screen? * * Well, since earlier, we ensured that we always force * AmigaGuide to open on our current screen, and since a * public screen can't close as long as AmigaGuide has some * windows on it, the only potential for a problem would be * if AmigaGuide tried to open a new window on its screen * on its own -- and it will never do that since we always * control it via this function. */ static char copycmd[80]; AG_NewGuide.nag_Lock = NULL; AG_NewGuide.nag_Name = MSG(MSG_HELPFILE_NAME); AG_NewGuide.nag_Screen = SnoopScreen; AG_NewGuide.nag_ClientPort = HELP_AREXX_PORT; AG_NewGuide.nag_BaseName = HELP_BASENAME; AG_NewGuide.nag_Flags = HTF_CACHE_NODE; AG_Context = OpenAmigaGuideAsync(&AG_NewGuide, TAG_DONE); if (!AG_Context) { ShowError(MSG(MSG_ERROR_CREATE_AMIGAGUIDE)); return (FALSE); } AmigaGuideMask = AmigaGuideSignal(AG_Context); /* * Now since AmigaGuide won't initialise immediately, it's * no use sending this help request to it -- we need to wait * until it tells us that it's ready for action. So, we * remember the command that was to be sent, and when we * get the okay from AmigaGuide, we can send it (see * HandleAGuideMsgs() below.) * * Also, since sometimes the string that we are sent is only * temporary (e.g. from an ARexx message) we need to make a * copy of it. */ strcpy(copycmd, cmdstring); PendingAGuideMsg = copycmd; return (TRUE); } /* * Usual case: just send the command directly to AmigaGuide */ SendAmigaGuideCmd(AG_Context, cmdstring, NULL); return (TRUE); } /* * HandleAGuideMsgs() * * Handles any AmigaGuide messages (as indicate by a signal * arriving that matches AmigaGuideMask) */ void HandleAGuideMsgs(void) { struct AmigaGuideMsg *agm; int unloadhelp = 0; if (!AmigaGuideBase || !AG_Context) return; while ((agm = GetAmigaGuideMsg(AG_Context)) != NULL) { switch (agm->agm_Type) { case ActiveToolID: /* * The first time we try and display something, * AmigaGuide has to load first. We get this * message when it's finished loading, to let * us know we can now display the help. */ if (PendingAGuideMsg) { SendAmigaGuideCmd(AG_Context, PendingAGuideMsg, NULL); PendingAGuideMsg = NULL; } break; case ToolCmdReplyID: case ToolStatusID: case ShutdownMsgID: if (agm->agm_Pri_Ret){ if (agm->agm_Sec_Ret == HTERR_CANT_OPEN_DATABASE) { ShowError(MSG(MSG_ERROR_AGUIDE_CANT_OPEN), MSG(MSG_HELPFILE_NAME)); unloadhelp = 1; } else { ShowError(MSG(MSG_ERROR_AGUIDE_GENERIC), GetAmigaGuideString(agm->agm_Sec_Ret)); } PendingAGuideMsg = NULL; /* Cancel any pending cmd */ } break; } ReplyAmigaGuideMsg(agm); } if (unloadhelp) CleanupAGuide(); } /* * CleanupAGuide() * * Frees all resources allocated by ShowAGuide(). Should only be * called at the end of the program. */ void CleanupAGuide(void) { if (AG_Context) CloseAmigaGuide(AG_Context), AG_Context = NULL; if (AmigaGuideBase) CloseLibrary(AmigaGuideBase), AmigaGuideBase = NULL; AmigaGuideMask = 0; } /* * CreateCustomImage(imagetype, height) * * Creates an image of the specified type (IMAGE_FILE or IMAGE_FONT) * and returns a pointer to an image structure with details about * the image. The return value + 1 points to the same image, but * selected. * * height is the height of the image -- the width is fixed at 16 pixels. * * N.b. ScreenScreen should be valid when this function is called. You * should call FreeCustomImage(imageptr) to free up any memory allocated * when you're done. */ struct Image *CreateCustomImage(int imagetype, int height) { #define NUM_ELS(x) (sizeof(x) / sizeof(x[0])) ULONG *imagedata = FileButtonData; int imbytesperrow = 4; /* Must be even */ int imwidth = 20; int imheight = NUM_ELS(FileButtonData); int depth = SnoopScreen->RastPort.BitMap->Depth; struct CustomImage *ci; struct CustomBitMap *cbm; UBYTE *bitplane; int size; if (imagetype == IMAGE_FONT) { imagedata = FontButtonData; imheight = NUM_ELS(FontButtonData); if (height <= 12) /* Strip last line if gadget is tiny */ imheight--; } /* * For the gadget, we need to store two full bitmaps, one for * the selected image and one for the unselected image. We * allocate a single structure in CHIP ram that holds all the * details we need. */ size = sizeof(struct CustomImage) + imbytesperrow * height * depth * 2; ci = AllocMem(size, MEMF_CHIP | MEMF_CLEAR); if (!ci) return (NULL); ci->size = size; /* * Now initialise our two images -- the steps are almost identical * for each, except that one is filled and one is not. */ bitplane = (UBYTE *)(ci+1); for (cbm = ci->image; cbm < &ci->image[2]; cbm++) { struct RastPort *rport = &cbm->rport; struct BitMap *bitmap = &cbm->bitmap; int fillpen; int textpen; int shinepen; int shadowpen; int planemask; int i; InitRastPort(rport); InitBitMap(bitmap, depth, imwidth, height); /* * Now initialise the plane pointers accordingly */ for (i = 0; i < depth; i++) { bitmap->Planes[i] = bitplane; bitplane += (imbytesperrow * height); } rport->BitMap = bitmap; /* * Now we're ready to render our chosen image. * First let's draw the box and box highlight. * If we're drawing the selected state, we * invert the highlight colours to make the box * look indented. */ shinepen = ScreenDI->dri_Pens[SHINEPEN]; shadowpen = ScreenDI->dri_Pens[SHADOWPEN]; if (cbm != ci->image) { int swappen = shinepen; shinepen = shadowpen; shadowpen = swappen; } SetDrMd(rport, JAM1); SetAPen(rport, shinepen); Move(rport, 0, height-1); Draw(rport, 0, 0); Draw(rport, imwidth-2, 0); Move(rport, 1, height-2); Draw(rport, 1, 1); SetAPen(rport, shadowpen); Move(rport, imwidth-1, 0); Draw(rport, imwidth-1, height-1); Draw(rport, 1, height-1); Move(rport, imwidth-2, 1); Draw(rport, imwidth-2, height-2); /* * Now, if we're on the second round, fill in the box * in the select colour */ fillpen = 0; textpen = 1; if (cbm != ci->image) { fillpen = ScreenDI->dri_Pens[FILLPEN]; textpen = ScreenDI->dri_Pens[FILLTEXTPEN]; SetAPen(rport, fillpen); RectFill(rport, 2, 1, imwidth-3, height-2); } /* * Now the tricky bit -- we need to copy our image data * into the middle of the box icon we've just created. * Luckily, the data is already word-aligned for us so * we just need to worry about vertically centering it. * We use a little XOR trick to figure out which planes * need to be updated, and also to do the updating. */ planemask = fillpen ^ textpen; for (i = 0; i < depth; i++) { if (planemask & (1 << i)) { ULONG *src = imagedata; ULONG *dest = ((ULONG *)(cbm->bitmap.Planes[i])) + (height - imheight) / 2; int j; for (j = 0; j < imheight; j++) *dest++ ^= *src++; } } /* * All done, now initialise the image accordingly */ cbm->image.Width = imwidth; cbm->image.Height = height; cbm->image.Depth = depth; cbm->image.ImageData = (UWORD *)(cbm->bitmap.Planes[0]); cbm->image.PlanePick = (1 << depth) - 1; } /* * To ensure that our two image structures our consecutive, * we copy the second bitmap's image back into the first * image's bitmap's alternate image */ ci->image[0].altimage = ci->image[1].image; return ((struct Image *)ci); } /* * FreeCustomImage(image) * * Release a custom image allocated earlier by CreateCustomImage() */ void FreeCustomImage(struct Image *image) { if (image) FreeMem(image, ((struct CustomImage *)image)->size); } /* * ConvertIMsgToChar() * * Takes an IntuiMessage of IDCMP_RAWKEY and returns the single * character representation it corresponds to. If this isn't * possible (e.g. a HELP key or cursor key) then returns 0 * instead. */ int ConvertIMsgToChar(struct IntuiMessage *imsg) { static struct InputEvent ev; /* Ensure initalised to 0 */ char buffer[9]; int len; if (imsg->Class != IDCMP_RAWKEY) return (0); ev.ie_Class = IECLASS_RAWKEY; ev.ie_Code = imsg->Code; ev.ie_Qualifier = imsg->Qualifier; ev.ie_EventAddress = *(APTR *)imsg->IAddress; len = MapRawKey(&ev, buffer, 8, NULL); if (len != 1) return (0); return (buffer[0]); }