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 >
Wrap
C/C++ Source or Header
|
1994-09-17
|
33KB
|
1,186 lines
/*
* 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]);
}