/* * SUBWIN.C vi:ts=4 * * Copyright (c) Eddy Carroll, September 1994. * * This module handles all the windows other than the main window. * That is, the functions window, settings window and format * window. */ #include "system.h" #include "snoopdos.h" #include "gui.h" #if 0 #define DB(s) KPrintF(s) #else #define DB(s) #endif /* * These are used by all the requesters to decide what options to do * when leaving a requester (make current settings permanent or * restore the old settings) */ #define SEL_NONE 0 /* User didn't select an option yet */ #define SEL_USE 1 /* User selected USE in a requester */ #define SEL_CANCEL 2 /* User selected CANCEL in a requester */ /* * These control which items are displayed in the System and DOS * columns in the function window. */ #define COL_SELECTED 0 /* Column shows selected items */ #define COL_ALL 1 /* Column shows all items */ #define COL_NONE 2 /* Column shows no items */ #define SETBUTTON_FILE 0 /* Little image of a file folder */ #define SETBUTTON_FONT 1 /* Little image of a font folder */ #define SETBUTTON_MAX 2 /* Anything above this is a string ID */ /* * These two are imported from SNOOPDOS.C */ extern char SnoopDosTitle[]; /* * Variables for the format editor */ int FBoxA_Left; /* X pos of left box in format window */ int FBoxA_Top; /* Y pos of left box in format window */ int FBoxA_Width; /* Width of left box in format window */ int FBoxA_Height; /* Height of left box in format window */ int FBoxA_NumChars; /* No. of characters across left box */ int FBoxA_CurLines; /* No. of lines currently in left box */ int FBoxB_Left; /* X pos of right box in format window */ int FBoxB_Top; /* Y pos of right box in format window */ int FBoxB_Width; /* Hidth of right box in format window */ int FBoxB_Height; /* Weight of right box in format window */ int FBoxB_NumChars; /* No. of characters across right box */ int FBoxB_CurLines; /* No. of lines currently in right box */ int FBoxSpacing; /* Distance between box text baselines */ int FBoxSelected; /* Line number of right box selection */ /* (or FBOX_NOSELECT / FBOX_SELECTLEFT) */ int FBoxDeltaX; /* Offset from mouse to corner of drag */ int FBoxDeltaY; /* Offset from mouse to corner of drag */ int FBoxDragSelect; /* Currently selected drag drop area */ int FormatCurLMB; /* Current state of left mouse button */ int FormatMovingField; /* If true, currently dragging field */ int FormatDragBox; /* Box we're dragging from */ int FormatDragLine; /* Line we're dragging from */ char FBoxA_FormString[20]; /* sprintf format string for left box */ char FBoxB_FormString[20]; /* sprintf format string for right box */ char FormatSaveFormat[MAX_FORM_LEN]; /* Save area for format ed */ /* * These next two arrays are indices into the FieldTypes[] array * of format initialisers. */ typedef struct { int type; /* Type of this entry */ int width; /* Width of this entry */ } EditEvent; extern FieldInit FieldTypes[]; /* Pulled in from BUFFER.C */ EditEvent CurrentFields[EF_COUNT+1]; /* Fields in current format */ EditEvent AvailableFields[EF_COUNT+1]; /* Fields still available */ Settings SavedFuncSets; /* Saved function settings */ Settings SavedSetupSets; /* Saved setup settings */ struct Gadget *SetGadList; /* Settings window gadgets */ struct Gadget *FuncGadList; /* Function window gadgets */ struct Gadget *FormGadList; /* Format window gadgets */ struct Image *FileButtonImage; /* Image of a file button */ struct Image *FontButtonImage; /* Image of a font button */ struct TextFont *FormBufFont; /* Format window listview */ struct TextFont *FormGadFont; /* Format window gadgets */ /* * We don't usually free the Function window gadgets when we close * the window (since it takes a long time to generate them). However, * if we change window font, then we need a way to be able to change * them. This variable shows you how. */ int FuncSystemCol; /* Mode of system column (all/none/sel) */ int FuncDOSCol; /* Mode of DOS column (all/none/select) */ UBYTE SavedCols[GID_NUMFUNCSETS]; /* Currently selected settings */ UBYTE TempCols[GID_NUMFUNCSETS]; /* Currently selected settings */ UBYTE SetKeyboard[KB_SHORTCUT_SIZE]; /* For keyboard equivs */ UBYTE FuncKeyboard[KB_SHORTCUT_SIZE]; /* For keyboard equivs */ UBYTE FormKeyboard[KB_SHORTCUT_SIZE]; /* For keyboard equivs */ /* * Now our font structures. * * We do everything in terms of FontAttr structures, and whenever a * window needs a font, it OpenFont's it itself. This has several * advantages: it allows each window to dynamically select the * best-fitting font, and it also ensures no problems if we have a * window open with one font and the user than changes the font * to something else -- the first window will still have a valid * lock on the old font. */ extern struct TextAttr TopazFontAttr; extern struct TextAttr SystemFontAttr; extern struct TextAttr WindowFontAttr; extern struct TextAttr BufferFontAttr; extern struct { struct TextAttr *gadgetfa; struct TextAttr *bufferfa; } MainWindowFontList[]; /* Imported from MAINWIN.C */ struct TextAttr *SubWindowFontList[] = { &WindowFontAttr, &SystemFontAttr, &TopazFontAttr, NULL }; /* * Next, all our gadget structures -- these are used to build the * font-sensitive gadget windows at runtime. */ struct FuncGadgets { ULONG gadgid; /* Gadget ID */ UWORD stringid; /* Name of checkmark gadget */ UBYTE col; /* Column (0, 1 or 2) */ UBYTE row; /* Row (0 -- 13) */ }; struct FuncGadgets FuncGadgs[] = { /* * General controls */ GID_ONLYFAILS, MSG_SHOWFAILS_GAD, 0, 0, GID_SHOWCLINUM, MSG_SHOWCLI_GAD, 0, 1, GID_SHOWPATHS, MSG_SHOWFULLPATHS_GAD, 0, 2, GID_USEDEVNAMES, MSG_USEDEVNAMES_GAD, 0, 3, GID_MONPACKETS, MSG_MONPACKETS_GAD, 0, 4, GID_MONALLPACKETS, MSG_MONALLPACKETS_GAD, 0, 5, GID_MONROMCALLS, MSG_MONROMCALLS_GAD, 0, 6, GID_IGNOREWB, MSG_IGNOREWB_GAD, 0, 7, /* * System functions */ GID_FINDPORT, MSG_FINDPORT_GAD, 1, 0, GID_FINDRESIDENT, MSG_FINDRESIDENT_GAD, 1, 1, GID_FINDSEMAPHORE, MSG_FINDSEMAPHORE_GAD, 1, 2, GID_FINDTASK, MSG_FINDTASK_GAD, 1, 3, GID_LOCKSCREEN, MSG_LOCKSCREEN_GAD, 1, 4, GID_OPENDEVICE, MSG_OPENDEVICE_GAD, 1, 5, GID_OPENFONT, MSG_OPENFONT_GAD, 1, 6, GID_OPENLIBRARY, MSG_OPENLIBRARY_GAD, 1, 7, GID_OPENRESOURCE, MSG_OPENRESOURCE_GAD, 1, 8, GID_READTOOLTYPES, MSG_READTOOLTYPES_GAD, 1, 9, GID_SENDREXX, MSG_SENDREXX_GAD, 1, 10, /* * AmigaDOS Functions */ GID_CHANGEDIR, MSG_CHANGEDIR_GAD, 2, 0, GID_DELETE, MSG_DELETE_GAD, 2, 1, GID_EXECUTE, MSG_EXECUTE_GAD, 2, 2, GID_GETVAR, MSG_GETVAR_GAD, 2, 3, GID_LOADSEG, MSG_LOADSEG_GAD, 2, 4, GID_LOCKFILE, MSG_LOCKFILE_GAD, 2, 5, GID_MAKEDIR, MSG_MAKEDIR_GAD, 2, 6, GID_MAKELINK, MSG_MAKELINK_GAD, 2, 7, GID_OPENFILE, MSG_OPENFILE_GAD, 2, 8, GID_RENAME, MSG_RENAME_GAD, 2, 9, GID_RUNCOMMAND, MSG_RUNCOMMAND_GAD, 2, 10, GID_SETVAR, MSG_SETVAR_GAD, 2, 11, GID_SYSTEM, MSG_SYSTEM_GAD, 2, 12, 0, 0, 0, 0 }; /* * This structure defines the gadgets in the settings window. There * are three basic types of gadgets supported: cycle, string and * display. For cycle gadgets, data1 is the index of the current * selection, data2 is a 0-terminated array of message IDs that * will hold the possible ListView choices, and data3 is a pointer * to an array of string pointers of the same size (including * terminating NULL) that will be filled in by the code with actual * pointers to those strings. * * For string and display gadgets, data2 is a pointer to the string * to be displayed inside the gadget and data1/data3 are unused. * However, if promptid is specified, then data1 is the message * number of the prompt to be used in the button instead of defaulting * to a question mark (the width is automatically adjusted). */ struct SetGadgets { UWORD gadgid; /* Gadget ID */ UWORD stringid; /* ID of label name */ ULONG gtype; /* Gadget type (cycle, string, or display) */ UWORD promptid; /* If non-zero, ID of optional "?" button */ ULONG data1; /* See above */ void *data2; /* See above */ void *data3; /* See above */ UBYTE col; /* Start col (0 or 1) */ UBYTE cwidth; /* Column width (0, 1 or 2) */ UBYTE row; /* Row (0 to 6) */ }; int SetCylHide[] = { MSG_CYL_HIDE_GAD, MSG_CYL_ICONIFY_GAD, MSG_CYL_TOOLSMENU_GAD, MSG_CYL_NONE_GAD, 0 }; int SetCylOpenOn[] = { MSG_CYL_DEFSCREEN_GAD, MSG_CYL_FRONTSCREEN_GAD, MSG_CYL_NAMEDSCREEN_GAD, 0 }; int SetCylLogMode[] = { MSG_CYL_PROMPT_USER_GAD, MSG_CYL_APPEND_GAD, MSG_CYL_OVERWRITE_GAD, MSG_CYL_SERIALPORT_GAD, 0 }; int SetCylFileIO[] = { MSG_CYL_AUTOMATIC_GAD, MSG_CYL_IMMEDIATE_GAD, MSG_CYL_BUFFERED_GAD, 0 }; char *SetCylHideText[HIDE_MAX+1]; char *SetCylOpenOnText[SCREEN_MAX+1]; char *SetCylLogModeText[LOGMODE_MAX+1]; char *SetCylFileIOText[FILE_MAX+1]; /* * If you re-arrange the order of these gadget definitions, * make sure you re-order the enum's that follow below as well. */ struct SetGadgets SetGadgs[] = { GID_BUFFORMAT, MSG_BUFFORMAT_GAD, STRING_KIND, GID_FORMATEDIT, MSG_FORMATEDIT_GAD, NULL, 0, 0, 2, 5, GID_LOGFORMAT, MSG_LOGFORMAT_GAD, STRING_KIND, GID_FORMATCOPY, MSG_FORMATCOPYF_GAD, NULL, 0, 0, 2, 6, GID_HIDEMETHOD, MSG_HIDEMETHOD_GAD, CYCLE_KIND, 0, 0, SetCylHide, SetCylHideText, 0, 0, 0, GID_OPENON, MSG_OPENON_GAD, CYCLE_KIND, 0, 0, SetCylOpenOn, SetCylOpenOnText, 0, 0, 1, GID_LOGMODE, MSG_LOGMODE_GAD, CYCLE_KIND, 0, 0, SetCylLogMode, SetCylLogModeText, 0, 0, 2, GID_FILEIO, MSG_FILEIO_GAD, CYCLE_KIND, 0, 0, SetCylFileIO, SetCylFileIOText, 0, 0, 3, GID_HOTKEY, MSG_HOTKEY_GAD, STRING_KIND, 0, 0, NULL, 0, 1, 1, 0, GID_SCREENNAME, MSG_SCREENNAME_GAD, STRING_KIND, 0, 0, NULL, 0, 1, 1, 1, GID_LOGFILE, MSG_LOGFILE_GAD, STRING_KIND, GID_FILEPROMPT, SETBUTTON_FILE, NULL, 0, 1, 1, 2, GID_WINDOWFONT, MSG_WINDOWFONT_GAD, TEXT_KIND, GID_WFONTPROMPT, SETBUTTON_FONT, NULL, 0, 1, 1, 3, GID_BUFFERFONT, MSG_BUFFERFONT_GAD, TEXT_KIND, GID_BFONTPROMPT, SETBUTTON_FONT, NULL, 0, 1, 1, 4, 0 }; /* * The following gadgets should match the _order_ that the above * gadgets are laid out in, since they are used as indices into the * above array. */ typedef enum { SG_BufferFormat, SG_LogfileFormat, SG_HideMethod, SG_ScreenType, SG_LogMode, SG_FileIOType, SG_HotKey, SG_ScreenName, SG_LogFile, SG_WindowFont, SG_BufferFont, } SetGadgetEnums; /* * Arrays of gadget names for calculating minimum width */ /* * Miscellaneous gadgets in functions window */ int MiscColNames[] = { MSG_SHOWFAILS_GAD, MSG_SHOWCLI_GAD, MSG_SHOWFULLPATHS_GAD, MSG_USEDEVNAMES_GAD, MSG_MONPACKETS_GAD, MSG_MONALLPACKETS_GAD, MSG_IGNOREWB_GAD, 0 }; /* * System gadgets in functions window */ int SysColNames[] = { MSG_FINDPORT_GAD, MSG_FINDRESIDENT_GAD, MSG_FINDSEMAPHORE_GAD, MSG_FINDTASK_GAD, MSG_LOCKSCREEN_GAD, MSG_OPENDEVICE_GAD, MSG_OPENFONT_GAD, MSG_OPENLIBRARY_GAD, MSG_OPENRESOURCE_GAD, MSG_READTOOLTYPES_GAD, MSG_SENDREXX_GAD, 0 }; /* * AmigaDOS gadgets in functions window */ int DOSColNames[] = { MSG_CHANGEDIR_GAD, MSG_DELETE_GAD, MSG_EXECUTE_GAD, MSG_GETVAR_GAD, MSG_LOADSEG_GAD, MSG_LOCKFILE_GAD, MSG_MAKEDIR_GAD, MSG_MAKELINK_GAD, MSG_OPENFILE_GAD, MSG_RENAME_GAD, MSG_RUNCOMMAND_GAD, MSG_SETVAR_GAD, MSG_SYSTEM_GAD, 0 }; /* * Aditional gadgets in function window */ int FuncCycleText[] = { MSG_ALL_GAD, MSG_NONE_GAD, MSG_SELECTED_GAD, 0 }; int FuncCycleName[] = { MSG_SELSYSTEM_GAD, MSG_SELDOS_GAD, 0 }; int UseCancelUndoText[] = { MSG_USE_GAD, MSG_CANCEL_GAD, MSG_UNDO_GAD, 0 }; /* * Left column gadgets names in settings window */ int SettingsLeftNames[] = { MSG_HIDEMETHOD_GAD, MSG_OPENON_GAD, MSG_LOGMODE_GAD, MSG_FILEIO_GAD, MSG_BUFFORMAT_GAD, MSG_LOGFORMAT_GAD, 0 }; /* * Right column gadget names in settings window */ int SettingsRightNames[] = { MSG_HOTKEY_GAD, MSG_SCREENNAME_GAD, MSG_LOGFILE_GAD, MSG_WINDOWFONT_GAD, MSG_BUFFERFONT_GAD, 0 }; /* * Two mini buttons attached to format gadgets in settings window * Note that we have two types of possibility: one for fixed point * fonts and one for proportional fonts. This is so that we can * arrange for the two strings "Edit..." and "Copy" to always * look properly aligned, regardless of font type. If we didn't do * this, then with fixed point fonts, "Copy" looks mis-aligned * with respect to "Edit..." because they are different lengths. */ int SetMiniButtonsP[] = { MSG_FORMATCOPYP_GAD, MSG_FORMATEDIT_GAD, 0 }; int SetMiniButtonsF[] = { MSG_FORMATCOPYF_GAD, MSG_FORMATEDIT_GAD, 0 }; /* * Left column gadget contents in settings */ int SettingsLeftContents[] = { MSG_CYL_HIDE_GAD, MSG_CYL_ICONIFY_GAD, MSG_CYL_TOOLSMENU_GAD, MSG_CYL_DEFSCREEN_GAD, MSG_CYL_FRONTSCREEN_GAD, MSG_CYL_NAMEDSCREEN_GAD, MSG_CYL_PROMPT_USER_GAD, MSG_CYL_APPEND_GAD, MSG_CYL_OVERWRITE_GAD, MSG_CYL_AUTOMATIC_GAD, MSG_CYL_IMMEDIATE_GAD, MSG_CYL_BUFFERED_GAD, 0 }; /* * Mega struct that collects all the data needed to create useful BOBs. */ typedef struct MyGel { struct GelsInfo gelinfo; /* GelInfo for entire structure */ WORD nextline[8]; /* Nextline data */ WORD *lastcolor[8]; /* Last colour data */ struct RastPort *mainrast; /* Rastport bob is displayed in */ struct collTable colltable; /* Collision table */ struct VSprite vshead; /* Head sprite anchor */ struct VSprite vstail; /* Tail sprite anchor */ struct VSprite bobvsprite; /* Vsprite used for bob */ struct Bob bob; /* Data for a single bob */ struct RastPort rastport; /* Rastport for our BOB */ struct BitMap bitmap; /* Bitmap for our BOB */ ULONG planesize; /* Size of one plane in bob */ UBYTE *planedata; /* Pointer to first plane */ UBYTE *chipdata; /* Pointer to all CHIP RAM data */ ULONG chipsize; /* Total size of allocated CHIP */ } MyGel; MyGel *TextBob; /* Info about GEL/BOBs */ /* * Some prototypes private to this file */ MyGel *CreateBob(struct RastPort *rport, int width, int height, int transp); void FreeBob(MyGel *mygel); void UpdateBob(int x, int y); int PickupBob(int hit, int x, int y); void DropBob(); /***************************************************************************** * * Start of functions! * *****************************************************************************/ /* * CleanupSubWindows() * * Frees any resources associated with this module */ void CleanupSubWindows(void) { PurgeFuncGadgets = 1; CloseFunctionWindow(); CloseSettingsWindow(); CloseFormatWindow(); } /* * GetFuncName(gadgetid) * * Returns a pointer to a string describing the function name which * matches the gadget ID passed in. This is used by PATCHES.C when * it is unpatching the patched functions at exit time to identify * which function can't be unpatched in the event of a problem. */ char *GetFuncName(int gadgetid) { struct FuncGadgets *fg = FuncGadgs; if (gadgetid == GID_SENDREXX) /* Special-case this multi-function one */ return MSG(MSG_PACKET_NAME); for (fg = &FuncGadgs[0]; fg->gadgid; fg++) { if (fg->gadgid == gadgetid) return MSG(fg->stringid); } return (""); } /* * CreateFunctionGadgets(fontattr, getsize, &width, &height) * * Creates all the gadgets for the function window. If getsize * is true, then the width and height values are filled in with * the dimensions of the window needed to hold the gadgets. In * this case, NULL is returned if the window would be too big to * fit on the current screen, or non-NULL if width and height * were successfully initialised. * * If getsize is zero, then the actual gadgets are created and * a pointer to the gadget list is returned, or NULL if an * error occurred (typically, out of memory). */ struct Gadget *CreateFunctionGadgets(struct TextAttr *fontattr, int getsize, int *pwidth, int *pheight) { static char *cyltext[4]; struct TextFont *font; struct FuncGadgets *fg; struct NewGadget ng; struct Gadget *gadlist; struct Gadget *gad; UWORD spacing[3]; UWORD yoffset[3]; UWORD colpos[3]; int heightadjust = 0; int width; int height; int gwidth; int gheight; int fonty; int colspace = 30; int topline; int w1, w2; if (!FuncVI) { FuncVI = GetVisualInfoA(SnoopScreen, NULL); if (!FuncVI) return (NULL); } font = MyOpenFont(fontattr); if (!font) return (NULL); fonty = font->tf_YSize; gheight = fonty + 3; gwidth = gheight + 15; spacing[0] = fonty + 4; spacing[1] = fonty + 4; spacing[2] = fonty + 4; yoffset[1] = TitlebarHeight + fonty/2; yoffset[2] = yoffset[1]; yoffset[0] = yoffset[1] + 10 * spacing[1] - 7 * spacing[0]; colpos[0] = BorderLeft + FUNC_MARGIN; colpos[1] = colpos[0] + MaxTextLen(font, MiscColNames) + gwidth + colspace; w1 = MaxTextLen(font, SysColNames) + gwidth; w2 = GetTextLen(font, MSG(MSG_SYSFUNCS_GAD)); colpos[2] = colpos[1] + MAX(w1, w2) + colspace; w1 = MaxTextLen(font, DOSColNames) + gwidth; w2 = GetTextLen(font, MSG(MSG_DOSFUNCS_GAD)); width = colpos[2] + MAX(w1, w2) + FUNC_MARGIN + BorderRight + 8; height = yoffset[2] + spacing[2] * 15 + BorderBottom; topline = yoffset[2]; if (ScreenHeight < 256 && height > ScreenHeight) { /* * For medium and low resolution screens, shave another * few pixels from the height. Our aim is to be able to * open on a 640 x 200 screen with a 15 point screen font. */ heightadjust = -2; height += heightadjust * 2; } if ((height + spacing[2] * 2) <= ScreenHeight) { int disp = (spacing[2] * 3) / 2; yoffset[0] += disp; yoffset[1] += disp; yoffset[2] += disp; height += disp; } if (width > ScreenWidth || height > ScreenHeight) { CloseFont(font); return (NULL); } if (getsize) { CloseFont(font); *pwidth = width; *pheight = height; return (struct Gadget *)(-1); } /* * Now create our new gadgets */ ng.ng_Height = gheight; ng.ng_Width = gwidth; ng.ng_TextAttr = fontattr; ng.ng_VisualInfo = FuncVI; ng.ng_Flags = PLACETEXT_RIGHT; gadlist = NULL; gad = CreateContext(&gadlist); if (!gad) goto fgad_failed; for (fg = &FuncGadgs[0]; fg->gadgid; fg++) { int col = fg->col; ng.ng_LeftEdge = colpos[col]; ng.ng_TopEdge = fg->row * spacing[col] + yoffset[col]; ng.ng_GadgetText = MSG(fg->stringid); ng.ng_GadgetID = fg->gadgid; gad = CreateGadget(CHECKBOX_KIND, gad, &ng, GT_Underscore, '_', GTCB_Scaled, TRUE, TAG_DONE); if (!gad) goto fgad_failed; Gadget[fg->gadgid] = gad; AddKeyShortcut(FuncKeyboard, fg->gadgid, fg->stringid); } /* * Now create the remaining window gadgets */ ng.ng_LeftEdge = colpos[0] + GetTextLen(font, MSG(MSG_MATCHNAME_GAD)) + 10; ng.ng_TopEdge = yoffset[1] + (23 * spacing[1]) / 2; ng.ng_GadgetText = MSG(MSG_MATCHNAME_GAD); ng.ng_GadgetID = GID_MATCHNAME; ng.ng_Width = colpos[2] - ng.ng_LeftEdge - colspace + 8; ng.ng_Height = gheight + 3; ng.ng_Flags = PLACETEXT_LEFT; gad = CreateGadget(STRING_KIND, gad, &ng, GT_Underscore, '_', GTST_MaxChars, MAX_STR_LEN, GTST_String, CurSettings.Func.Pattern, TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_MATCHNAME] = gad; AddKeyShortcut(FuncKeyboard, GID_MATCHNAME, MSG_MATCHNAME_GAD); ng.ng_GadgetText = MSG(MSG_SELSYSTEM_GAD); ng.ng_GadgetID = GID_SELSYSTEM; ng.ng_Height = gheight + 3; ng.ng_Width = MaxTextLen(font, FuncCycleText) + 40; ng.ng_LeftEdge = colpos[0] + MaxTextLen(font, FuncCycleName) + 10; ng.ng_TopEdge = yoffset[1]; cyltext[0] = MSG(MSG_SELECTED_GAD); cyltext[1] = MSG(MSG_ALL_GAD); cyltext[2] = MSG(MSG_NONE_GAD); cyltext[3] = NULL; gad = CreateGadget(CYCLE_KIND, gad, &ng, GT_Underscore, '_', GTCY_Labels, cyltext, GTCY_Active, COL_SELECTED, TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_SELSYSTEM] = gad; AddKeyShortcut(FuncKeyboard, GID_SELSYSTEM, MSG_SELSYSTEM_GAD); ng.ng_GadgetText = MSG(MSG_SELDOS_GAD); ng.ng_GadgetID = GID_SELDOS; ng.ng_TopEdge = (yoffset[0] + yoffset[1]) / 2; gad = CreateGadget(CYCLE_KIND, gad, &ng, GT_Underscore, '_', GTCY_Labels, cyltext, GTCY_Active, COL_SELECTED, TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_SELDOS] = gad; AddKeyShortcut(FuncKeyboard, GID_SELDOS, MSG_SELDOS_GAD); ng.ng_Width = MaxTextLen(font, UseCancelUndoText) + 32; ng.ng_Height = fonty + GadgetHeight; ng.ng_GadgetText = MSG(MSG_USE_GAD); ng.ng_GadgetID = GID_FUNCUSE; ng.ng_LeftEdge = colpos[0]; ng.ng_TopEdge = yoffset[2] + heightadjust + (27 * spacing[2]) / 2; ng.ng_Flags = PLACETEXT_IN; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_FUNCUSE] = gad; AddKeyShortcut(FuncKeyboard, GID_FUNCUSE, MSG_USE_GAD); ng.ng_LeftEdge = width - BorderRight - FUNC_MARGIN - ng.ng_Width; ng.ng_GadgetText = MSG(MSG_CANCEL_GAD); ng.ng_GadgetID = GID_FUNCCANCEL; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_FUNCCANCEL] = gad; AddKeyShortcut(FuncKeyboard, GID_FUNCCANCEL, MSG_CANCEL_GAD); ng.ng_LeftEdge = (ng.ng_LeftEdge + BorderLeft + FUNC_MARGIN) / 2; ng.ng_GadgetText = MSG(MSG_UNDO_GAD); ng.ng_GadgetID = GID_FUNCUNDO; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fgad_failed; Gadget[GID_FUNCUNDO] = gad; AddKeyShortcut(FuncKeyboard, GID_FUNCUNDO, MSG_UNDO_GAD); if (topline != yoffset[2]) { /* * If there's room at the top of the window, create two text * gadgets with the column headings for the System and DOS cols. */ ng.ng_TopEdge = topline; ng.ng_LeftEdge = colpos[1]; ng.ng_GadgetText = ""; ng.ng_GadgetID = 0; gad = CreateGadget(TEXT_KIND, gad, &ng, GTTX_Text, MSG(MSG_SYSFUNCS_GAD), TAG_DONE); if (!gad) goto fgad_failed; ng.ng_LeftEdge = colpos[2]; gad = CreateGadget(TEXT_KIND, gad, &ng, GTTX_Text, MSG(MSG_DOSFUNCS_GAD), TAG_DONE); if (!gad) goto fgad_failed; } PurgeFuncGadgets = 0; /* No need to purge now */ CloseFont(font); return (gadlist); fgad_failed: if (gadlist) FreeGadgets(gadlist); CloseFont(font); return (NULL); } /* * OpenFunctionWindow() * * Opens the functions window with all the functions gadgets. * Returns TRUE for success, FALSE for failure. * The window will contain all necessary gadgets. * * As with the Settings window, we try a variety of fonts until we * find one that fits onto the screen. */ int OpenFunctionWindow(void) { static struct TextAttr funcfontattr = { "empty-storage-for-func-fonts...", 0, FS_NORMAL, FPB_DISKFONT}; static int width; /* Maintained from call to call */ static int height; int left = CurSettings.FuncWinLeft; int top = CurSettings.FuncWinTop; struct TextAttr *fontattr = NULL; struct Window *win; int i; CheckSegTracker(); if (FuncWindow) { WindowToFront(FuncWindow); ActivateWindow(FuncWindow); return (TRUE); } if (!CheckForScreen()) return (FALSE); SavedFuncSets = CurSettings; /* Save so we can restore later */ if (!FuncGadList) { /* * Find out what dimensions our new window should be; to do * this, we calculate the size with a variety of fonts until * we find one that works. */ int i; for (i = 0; fontattr = SubWindowFontList[i]; i++) { if (CreateFunctionGadgets(fontattr, TRUE, &width, &height)) break; } if (!fontattr) { ShowError(MSG(MSG_ERROR_OPENFUNC)); return (FALSE); } /* * We need to make a copy of the fontattr now so that if * the user changes the font, all our gadgets don't suddenly * inherit the new font (at least until the window has been * closed and re-opened!) */ strcpy(funcfontattr.ta_Name, fontattr->ta_Name); funcfontattr.ta_YSize = fontattr->ta_YSize; fontattr = &funcfontattr; } if (left == -1) left = (ScreenWidth - width) / 2; if (top == -1) top = (ScreenHeight - height) / 2; win = OpenWindowTags(NULL, WA_Title, MSG(MSG_FUNCTION_TITLE), WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_NEWSIZE | IDCMP_INACTIVEWINDOW | IDCMP_RAWKEY | BUTTONIDCMP, WA_Left, left, WA_Top, top, WA_Width, width, WA_Height, height, WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_ACTIVATE | WFLG_RMBTRAP | WFLG_NEWLOOKMENUS, RefreshTag, TRUE, WA_NoCareRefresh, NoCareRefreshBool, WA_PubScreen, SnoopScreen, TAG_DONE); if (!win) { ShowError(MSG(MSG_ERROR_OPENFUNC)); return (FALSE); } if (!FuncGadList) { FuncGadList = CreateFunctionGadgets(fontattr, 0, 0, 0); if (!FuncGadList) { CloseWindow(win); ShowError(MSG(MSG_ERROR_OPENFUNC)); return (FALSE); } } /* * Now update function gadgets to reflect current settings * for AmigaDOS functions. Under V39+, we can do this before * adding them to the window. Under V37, we have to do it * after adding them to the window. */ if (GadToolsBase->lib_Version < 39) { AddGList(win, FuncGadList, -1, -1, NULL); RefreshGList(FuncGadList, win, NULL, -1); GT_RefreshWindow(win, NULL); for (i = FIRST_BOOL_GADGET; i <= LAST_BOOL_GADGET; i++) { if (Gadget[i]) { GT_SetGadgetAttrs(Gadget[i], win, NULL, GTCB_Checked, CurSettings.Func.Opts[i], TAG_DONE); } } } else { /* GadToolsBase->lib_Version >= 39 */ for (i = FIRST_BOOL_GADGET; i <= LAST_BOOL_GADGET; i++) { if (Gadget[i]) { GT_SetGadgetAttrs(Gadget[i], NULL, NULL, GTCB_Checked, CurSettings.Func.Opts[i], TAG_DONE); } } AddGList(win, FuncGadList, -1, -1, NULL); RefreshGList(FuncGadList, win, NULL, -1); GT_RefreshWindow(win, NULL); } /* * All done, so initialise some globals and return */ FuncWindow = win; FuncWindowPort = win->UserPort; FuncWindowMask = 1 << FuncWindowPort->mp_SigBit; FuncSystemCol = COL_SELECTED; FuncDOSCol = COL_SELECTED; if (DisableNestCount) DisableWindow(FuncWindow, &FuncRequester); return (TRUE); } /* * CreateSettingsGadgets(fontattr, getsize, &pwidth, &pheight) * * Creates the gadgets list for the settings window using the specified * window font. If the window would be to big for the current screen * using this font, or if there is a problem creating the gadgets, * returns NULL. * * The returned gadget list should be passed to FreeGadgets() when * it is no longer required. * * If getsize is true, then pwidth and pheight are filled in with the * dimensions of the window size needed to hold the gadgets, but * no actual gadget creation is carried out. In this case, NULL is * returned for a failure (couldn't open font, or window size would * exceed current screen size) and non-NULL for success. */ struct Gadget *CreateSettingsGadgets(struct TextAttr *fontattr, int getsize, int *pwidth, int *pheight) { struct SetGadgets *sg; struct NewGadget ng; struct NewGadget ngbut; struct TextFont *font; struct Gadget *gadlist; struct Gadget *gad; UWORD spacing; UWORD yoffset; UWORD colpos[2]; UWORD colwidth[3]; int miniwidth; int width; int height; int gheight; int fonty; int fontx; if (!SetVI) { SetVI = GetVisualInfoA(SnoopScreen, NULL); if (!SetVI) return (NULL); } font = MyOpenFont(fontattr); if (!font) return (NULL); fonty = font->tf_YSize; fontx = font->tf_XSize; gheight = fonty + GadgetHeight; spacing = fonty + GadgetSpacing; yoffset = TitlebarHeight + fonty/2; colpos[0] = BorderLeft + SET_MARGIN + MaxTextLen(font, SettingsLeftNames) + 7; colwidth[0] = MaxTextLen(font, SettingsLeftContents) + 40; colpos[1] = colpos[0] + colwidth[0] + SET_MARGIN*2 + MaxTextLen(font, SettingsRightNames); colwidth[1] = fontx * 24; /* Room for about 24 chars in string gadgets */ colwidth[2] = colwidth[1] + colpos[1] - colpos[0]; /* Dual width */ /* * See SetMiniButtons[] definitions for explanation of this bit */ if (font->tf_Flags & FPF_PROPORTIONAL) { miniwidth = MaxTextLen(font, SetMiniButtonsP) + 20; SetGadgs[1].data1 = MSG_FORMATCOPYP_GAD; } else { miniwidth = MaxTextLen(font, SetMiniButtonsF) + 20; SetGadgs[1].data1 = MSG_FORMATCOPYF_GAD; } width = colpos[1] + colwidth[1] + SET_MARGIN + BorderRight; height = yoffset + spacing * 8 + BorderBottom; if (width > ScreenWidth || height >ScreenHeight) { CloseFont(font); return (NULL); } if (getsize) { *pwidth = width; *pheight = height; CloseFont(font); return (struct Gadget *)(-1); } /* * Okay, looks like our size is okay so now prime our gadget * table with the current settings values to use when the * gadget is created. * */ #define DEF_CYCLE(x) SetGadgs[SG_##x].data1 #define DEF_STRING(x) SetGadgs[SG_##x].data2 DEF_CYCLE(HideMethod) = CurSettings.Setup.HideMethod; DEF_CYCLE(ScreenType) = CurSettings.Setup.ScreenType; DEF_CYCLE(LogMode) = CurSettings.Setup.LogMode; DEF_CYCLE(FileIOType) = CurSettings.Setup.FileIOType; DEF_STRING(HotKey) = CurSettings.Setup.HotKey; DEF_STRING(ScreenName) = CurSettings.Setup.ScreenName; DEF_STRING(LogFile) = CurSettings.Setup.LogFile; DEF_STRING(WindowFont) = GetFontDesc(WindowFontDesc, WindowFontName, WindowFontSize); DEF_STRING(BufferFont) = GetFontDesc(BufferFontDesc, BufferFontName, BufferFontSize); DEF_STRING(BufferFormat) = BufFormat; DEF_STRING(LogfileFormat) = LogFormat; /* * Next, onto the gadget creation itself */ ng.ng_Height = gheight; ng.ng_TextAttr = fontattr; ng.ng_VisualInfo = SetVI; ng.ng_Flags = PLACETEXT_LEFT; ngbut.ng_Height = gheight; ngbut.ng_TextAttr = fontattr; ngbut.ng_VisualInfo = SetVI; ngbut.ng_Flags = PLACETEXT_IN; FileButtonImage = CreateCustomImage(IMAGE_FILE, gheight+2-2*SquareAspect); FontButtonImage = CreateCustomImage(IMAGE_FONT, gheight); if (!FileButtonImage || !FontButtonImage) { CloseFont(font); return (NULL); } gadlist = NULL; gad = CreateContext(&gadlist); if (!gad) { CloseFont(font); return (NULL); } for (sg = &SetGadgs[0]; sg->gadgid; sg++) { ng.ng_LeftEdge = colpos[sg->col]; ng.ng_Width = colwidth[sg->cwidth]; ng.ng_TopEdge = yoffset + sg->row * spacing; ng.ng_GadgetText = MSG(sg->stringid); ng.ng_GadgetID = sg->gadgid; ng.ng_Height = gheight; if (sg->promptid) { /* * Create optional mini gadget to right of main gadget. * If data1 == 0 or data1 == 1 then use a file or font * BOOPSI gadget image, else use a Gadtools text button * containing the label corresponding to MSG(data1) else * use string ID indicated by data1 */ ngbut.ng_GadgetID = sg->promptid; ngbut.ng_TopEdge = ng.ng_TopEdge; ngbut.ng_Height = gheight; if (sg->gtype == STRING_KIND && !SquareAspect) { ngbut.ng_Height += 2; ngbut.ng_TopEdge -= 1; } if (sg->data1 > SETBUTTON_MAX) { /* * Creating a text gadget */ ngbut.ng_LeftEdge = ng.ng_LeftEdge + ng.ng_Width - miniwidth; ngbut.ng_Width = miniwidth; ngbut.ng_GadgetText = MSG(sg->data1); gad = CreateGadget(BUTTON_KIND, gad, &ngbut, GT_Underscore, '_', TAG_DONE); if (!gad) goto sgad_failed; AddKeyShortcut(SetKeyboard, sg->promptid, sg->data1); } else { /* * Creating an image gadget */ struct Image *img = FileButtonImage; if (sg->data1 == SETBUTTON_FONT) img = FontButtonImage; ngbut.ng_Width = img->Width; ngbut.ng_GadgetText = NULL; ngbut.ng_LeftEdge = ng.ng_LeftEdge + ng.ng_Width -img->Width; gad = CreateGadget(GENERIC_KIND, gad, &ngbut, TAG_DONE); if (!gad) goto sgad_failed; /* * Now fill in the details of our gadget */ gad->Flags |= GFLG_GADGIMAGE | GFLG_GADGHIMAGE; gad->Activation |= GACT_IMMEDIATE | GACT_RELVERIFY; gad->GadgetType |= GTYP_BOOLGADGET; gad->GadgetRender = img; gad->SelectRender = img + 1; } Gadget[sg->promptid] = gad; ng.ng_Width -= ngbut.ng_Width + 2; } switch (sg->gtype) { case STRING_KIND: if (!SquareAspect) { ng.ng_Height += 2; ng.ng_TopEdge -= 1; } gad = CreateGadget(STRING_KIND, gad, &ng, GT_Underscore, '_', GTST_String, sg->data2, GTST_MaxChars, MAX_STR_LEN, TAG_DONE); break; case TEXT_KIND: ng.ng_Width-= 2; ng.ng_LeftEdge++; gad = CreateGadget(TEXT_KIND, gad, &ng, GT_Underscore, '_', GTTX_Text, sg->data2, GTTX_Border, TRUE, TAG_DONE); break; case CYCLE_KIND: { int *msgid = sg->data2; char **msg = sg->data3; int i; for (i = 0; msgid[i]; i++) msg[i] = MSG(msgid[i]); msg[i] = NULL; gad = CreateGadget(CYCLE_KIND, gad, &ng, GT_Underscore, '_', GTCY_Active, sg->data1, GTCY_Labels, msg, TAG_DONE); break; } } if (!gad) goto sgad_failed; Gadget[sg->gadgid] = gad; AddKeyShortcut(SetKeyboard, sg->gadgid, sg->stringid); } ng.ng_GadgetText = MSG(MSG_BUFFERSIZE_GAD); ng.ng_GadgetID = GID_BUFFERSIZE; ng.ng_TopEdge = yoffset + spacing * 4 + (1 - SquareAspect); ng.ng_Height = gheight + (2 - 2 * SquareAspect); ng.ng_Width = GetTextLen(font, "10000") + 14; ng.ng_LeftEdge = colpos[0] + colwidth[0] - ng.ng_Width; gad = CreateGadget(INTEGER_KIND, gad, &ng, GT_Underscore, '_', GTIN_Number, CurSettings.Setup.BufferSize, TAG_DONE); if (!gad) goto sgad_failed; Gadget[GID_BUFFERSIZE] = gad; AddKeyShortcut(SetKeyboard, GID_BUFFERSIZE, MSG_BUFFERSIZE_GAD); ng.ng_Width = MaxTextLen(font, UseCancelUndoText) + 32; ng.ng_Height = fonty + GadgetHeight; ng.ng_GadgetText = MSG(MSG_USE_GAD); ng.ng_GadgetID = GID_SETUSE; ng.ng_LeftEdge = BorderLeft + SET_MARGIN; ng.ng_TopEdge = yoffset + 7 * spacing; ng.ng_Flags = PLACETEXT_IN; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto sgad_failed; Gadget[GID_SETUSE] = gad; AddKeyShortcut(SetKeyboard, GID_SETUSE, MSG_USE_GAD); ng.ng_LeftEdge = width - BorderRight - SET_MARGIN - ng.ng_Width; ng.ng_GadgetText = MSG(MSG_CANCEL_GAD); ng.ng_GadgetID = GID_SETCANCEL; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto sgad_failed; Gadget[GID_SETCANCEL] = gad; AddKeyShortcut(SetKeyboard, GID_SETCANCEL, MSG_CANCEL_GAD); ng.ng_LeftEdge = (ng.ng_LeftEdge + BorderLeft + SET_MARGIN) / 2; ng.ng_GadgetText = MSG(MSG_UNDO_GAD); ng.ng_GadgetID = GID_SETUNDO; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto sgad_failed; Gadget[GID_SETUNDO] = gad; AddKeyShortcut(SetKeyboard, GID_SETUNDO, MSG_UNDO_GAD); CloseFont(font); return (gadlist); sgad_failed: if (gadlist) FreeGadgets(gadlist); CloseFont(font); return (FALSE); } /* * OpenSettingsWindow() * * Opens the settings window with all the settings gadgets. * The window will contain all necessary gadgets. Note that * since gadgets can take some time to create on 68000 systems, * we cache the gadget list so that on subsequent re-opens of * the window, we don't need to recreate them all. * * Returns TRUE for success, false for failure. */ int OpenSettingsWindow(void) { static struct TextAttr setfontattr = { "empty-storage-for-setup-fonts..", 0, FS_NORMAL, FPB_DISKFONT}; static int width; /* Maintained from call to call */ static int height; int left = CurSettings.SetupWinLeft; int top = CurSettings.SetupWinTop; struct TextAttr *fontattr = NULL; struct Window *win; CheckSegTracker(); if (SetWindow) { WindowToFront(SetWindow); ActivateWindow(SetWindow); return (TRUE); } if (!CheckForScreen()) return (FALSE); SavedSetupSets = CurSettings; /* Save so we can restore later */ if (!SetGadList) { /* * Find out what dimensions our new window should be; to do * this, we calculate the size with a variety of fonts until * we find one that works. */ int i; for (i = 0; fontattr = SubWindowFontList[i]; i++) { if (CreateSettingsGadgets(fontattr, TRUE, &width, &height)) break; } if (!fontattr) { ShowError(MSG(MSG_ERROR_OPENSET)); return (FALSE); } /* * We need to make a copy of the fontattr now so that if * the user changes the font, all our gadgets don't suddenly * inherit the new font (at least until the window has been * closed and re-opened!) */ strcpy(setfontattr.ta_Name, fontattr->ta_Name); setfontattr.ta_YSize = fontattr->ta_YSize; fontattr = &setfontattr; } if (left == -1) left = (ScreenWidth - width) / 2; if (top == -1) top = (ScreenHeight - height) / 2; win = OpenWindowTags(NULL, WA_Title, MSG(MSG_SETTINGS_TITLE), WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_NEWSIZE | IDCMP_RAWKEY | IDCMP_INACTIVEWINDOW | BUTTONIDCMP, WA_Left, left, WA_Top, top, WA_Width, width, WA_Height, height, WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_ACTIVATE | WFLG_RMBTRAP | WFLG_NEWLOOKMENUS, RefreshTag, TRUE, WA_NoCareRefresh, NoCareRefreshBool, WA_PubScreen, SnoopScreen, TAG_DONE); if (!win) { ShowError(MSG(MSG_ERROR_OPENSET)); return (FALSE); } if (!SetGadList) { SetGadList = CreateSettingsGadgets(fontattr, 0, 0, 0); if (!SetGadList) { CloseWindow(win); ShowError(MSG(MSG_ERROR_OPENSET)); return (FALSE); } } AddGList(win, SetGadList, -1, -1, NULL); RefreshGList(SetGadList, win, NULL, -1); GT_RefreshWindow(win, NULL); SetWindow = win; SetWindowPort = win->UserPort; SetWindowMask = 1 << SetWindowPort->mp_SigBit; if (DisableNestCount) DisableWindow(SetWindow, &SetRequester); return (TRUE); } /* * CloseFunctionWindow(void) * * Closes the functions window. Note that we do NOT free the gadget * list in this case; since it takes so long to build, we leave it * alone for the benefit of future opens. */ void CloseFunctionWindow(void) { if (FuncWindow) { RecordWindowSizes(); CloseWindow(FuncWindow); FuncWindow = NULL; FuncWindowMask = 0; } if (PurgeFuncGadgets && FuncGadList) { FreeGadgets(FuncGadList); FuncGadList = NULL; } if (PurgeFuncGadgets && FuncVI) { FreeVisualInfo(FuncVI); FuncVI = NULL; } PurgeFuncGadgets = 0; } /* * CloseSettingsWindow(void) * * Closes the settings window, and free gadgets associated with window. */ void CloseSettingsWindow(void) { if (SetWindow) { RecordWindowSizes(); CloseWindow(SetWindow); SetWindow = NULL; SetWindowMask = 0; } if (SetGadList) { FreeGadgets(SetGadList); SetGadList = NULL; } if (FileButtonImage) { FreeCustomImage(FileButtonImage); FileButtonImage = NULL; } if (FontButtonImage) { FreeCustomImage(FontButtonImage); FontButtonImage = NULL; } if (SetVI) { FreeVisualInfo(SetVI); SetVI = NULL; } } /* * HandleSettingsMsgs() * * Processes all outstanding messages associated with the Settings * gadget window. To be called whenever we get a settings event. */ #define STRVAL(gid) (((struct StringInfo *)(Gadget[gid]->SpecialInfo))->Buffer) void HandleSettingsMsgs(void) { static int activegad; /* Non-zero if gadget is depressed */ static int activekey; /* Keycode shortcut of activegad */ struct IntuiMessage *imsg; int doneset = SEL_NONE; char *str; int val; if (!SetWindow) return; while ((imsg = GT_GetIMsg(SetWindowPort)) != NULL) { struct Gadget *gad; int gadid; int shift = (imsg->Qualifier & IE_SHIFT); int newval; switch (imsg->Class) { case IDCMP_CLOSEWINDOW: doneset = SEL_USE; break; case IDCMP_REFRESHWINDOW: GT_BeginRefresh(SetWindow); GT_EndRefresh(SetWindow, TRUE); break; case IDCMP_MOUSEMOVE: case IDCMP_GADGETDOWN: case IDCMP_GADGETUP: gad = (struct Gadget *)imsg->IAddress; gadid = gad->GadgetID; newval = imsg->Code; handle_set_gads: switch (gadid) { case GID_FILEPROMPT: { char *defname = ""; /* * Choose a default name for the logfile */ if (IsFileSystem(DefaultLogName)) defname = DefaultLogName; if (SelectFile(DefaultLogName, defname, SetWindow, FILESEL_DEFLOGNAME)) { /* * Got a new default filename, so update * the logfile gadget accordingly */ GT_SetGadgetAttrs(Gadget[GID_LOGFILE], SetWindow, NULL, GTST_String, DefaultLogName, TAG_DONE); } break; } case GID_WFONTPROMPT: /* * Choose a new gadget (window) font */ if (SelectFont(SetWindow, FONTSEL_WINDOW)) { strcpy(WindowFontName, WindowFR->fo_Attr.ta_Name); WindowFontSize = WindowFR->fo_Attr.ta_YSize; WindowFontAttr.ta_YSize = WindowFontSize; GT_SetGadgetAttrs( Gadget[GID_WINDOWFONT], SetWindow, NULL, GTTX_Text, GetFontDesc(WindowFontDesc, WindowFontName, WindowFontSize), TAG_DONE); ReOpenMainWindow(); WindowToFront(SetWindow); /* * Since we allow the function gadgets * to hang around even when the function * window closes, we need to free the * gadgets when we change fonts to force * them to be regenerated in the new * font. */ if (!FuncWindow) { FreeGadgets(FuncGadList); FuncGadList = NULL; } else { PurgeFuncGadgets = TRUE; } } break; case GID_BFONTPROMPT: /* * Choose a new buffer font */ if (SelectFont(SetWindow, FONTSEL_BUFFER)) { strcpy(BufferFontName, BufferFR->fo_Attr.ta_Name); BufferFontSize = BufferFR->fo_Attr.ta_YSize; BufferFontAttr.ta_YSize = BufferFontSize; GT_SetGadgetAttrs( Gadget[GID_BUFFERFONT], SetWindow, NULL, GTTX_Text, GetFontDesc(BufferFontDesc, BufferFontName, BufferFontSize), TAG_DONE); ReOpenMainWindow(); WindowToFront(SetWindow); } break; case GID_FORMATEDIT: OpenFormatWindow(); break; case GID_FORMATCOPY: if (shift) *LogFormat = '\0'; else strcpy(LogFormat, BufFormat); GT_SetGadgetAttrs(Gadget[GID_LOGFORMAT], SetWindow, NULL, GTST_String, LogFormat, TAG_DONE); break; case GID_FILEIO: CurSettings.Setup.FileIOType = newval; break; case GID_OPENON: CurSettings.Setup.ScreenType = newval; break; case GID_LOGMODE: CurSettings.Setup.LogMode = newval; SetLogGadget(newval, LG_REFRESH); break; case GID_HIDEMETHOD: if (!CxBase) newval = 0; if (newval != CurSettings.Setup.HideMethod) { int oldval = CurSettings.Setup.HideMethod; CurSettings.Setup.HideMethod = newval; if (newval == HIDE_NONE) CleanupHotKey(); else if (oldval == HIDE_NONE) { /* * Switching out of hide state so * re-activate hotkey */ InstallHotKey(CurSettings.Setup.HotKey); } SetMainHideState(newval); } break; case GID_HOTKEY: { char *msg = CurSettings.Setup.HotKey; strcpy(msg, STRVAL(GID_HOTKEY)); InstallHotKey(msg); SetMainHideState(CurSettings.Setup.HideMethod); break; } case GID_SCREENNAME: strcpy(CurSettings.Setup.ScreenName, STRVAL(GID_SCREENNAME)); break; case GID_LOGFILE: strcpy(CurSettings.Setup.LogFile, STRVAL(GID_LOGFILE)); break; case GID_LOGFORMAT: strcpy(CurSettings.Setup.LogfileFormat, STRVAL(GID_LOGFORMAT)); break; case GID_BUFFORMAT: str = STRVAL(GID_BUFFORMAT); if (strcmp(BufFormat, str) != 0) { strcpy(BufFormat, str); InstallNewFormat(NEW_STRING); } break; case GID_BUFFERSIZE: /* * We don't do anything here at all ... instead, * the update is handled when the user clicks * on USE. */ break; case GID_SETUSE: doneset = SEL_USE; break; case GID_SETCANCEL: doneset = SEL_CANCEL; break; case GID_SETUNDO: /* * Major bummer -- we need to manually update * ALL the string gadgets, since if the user * modified one of them and didn't press RETURN * we won't have picked it up and so we won't * know to undo it :-( */ InstallSettings(&SavedSetupSets, SET_SETUP); #define UndoSet SavedSetupSets.Setup GT_SetGadgetAttrs(Gadget[GID_HOTKEY], SetWindow, NULL, GTST_String, UndoSet.HotKey, TAG_DONE); GT_SetGadgetAttrs(Gadget[GID_SCREENNAME], SetWindow, NULL, GTST_String, UndoSet.ScreenName, TAG_DONE); GT_SetGadgetAttrs(Gadget[GID_LOGFILE], SetWindow, NULL, GTST_String, UndoSet.LogFile, TAG_DONE); GT_SetGadgetAttrs(Gadget[GID_BUFFORMAT], SetWindow, NULL, GTST_String, UndoSet.BufferFormat, TAG_DONE); GT_SetGadgetAttrs(Gadget[GID_LOGFORMAT], SetWindow, NULL, GTST_String, UndoSet.LogfileFormat, TAG_DONE); GT_SetGadgetAttrs(Gadget[GID_BUFFERSIZE], SetWindow, NULL, GTIN_Number, UndoSet.BufferSize, TAG_DONE); break; } break; case IDCMP_INACTIVEWINDOW: if (activegad) { ShowGadget(SetWindow, Gadget[activegad], GADGET_UP); activegad = 0; } break; case IDCMP_RAWKEY: { int upstroke = imsg->Code & 0x80; int keypress = ConvertIMsgToChar(imsg); gadid = SetKeyboard[keypress]; if (activegad) { /* * We're releasing a gadget that was pressed * earlier, so handle it now */ int samekey = (imsg->Code & 0x7f) == activekey; if (samekey && !upstroke) /* Ignore repeated keys */ break; ShowGadget(SetWindow, Gadget[activegad], GADGET_UP); if (samekey) { /* * Just released the key that was originally * pressed for this gadget, so now go and * handle the gadget action. */ gadid = activegad; gad = Gadget[gadid]; activegad = 0; goto handle_set_gads; } /* * If the above check didn't kick in, it means * we got a downpress of a different key, so * disable the gadget for this key and continue * to press the other key. */ activegad = 0; } if (imsg->Code == HELPKEY) { ShowAGuide(MSG(MSG_LINK_SETUPWIN)); break; } /* * Handle keyboard equivalent keypresses for gadgets */ if (gadid) { int numopts; gad = Gadget[gadid]; switch (gadid) { case GID_HIDEMETHOD: newval = CurSettings.Setup.HideMethod; numopts = HIDE_MAX; goto handle_set_cycle; case GID_OPENON: newval = CurSettings.Setup.ScreenType; numopts = SCREEN_MAX; goto handle_set_cycle; case GID_LOGMODE: newval = CurSettings.Setup.LogMode; numopts = LOGMODE_MAX; goto handle_set_cycle; case GID_FILEIO: newval = CurSettings.Setup.FileIOType; numopts = FILE_MAX; handle_set_cycle: newval++; if (shift) newval -= 2; if (newval < 0) newval += numopts; if (newval >= numopts) newval -= numopts; GT_SetGadgetAttrs(gad, SetWindow, NULL, GTCY_Active, newval, TAG_DONE); goto handle_set_gads; case GID_BUFFERSIZE: case GID_BUFFORMAT: case GID_LOGFORMAT: case GID_HOTKEY: case GID_SCREENNAME: case GID_LOGFILE: if (gadid != GID_LOGFILE || !shift) { ActivateGadget(gad, SetWindow, NULL); break; } /* * User pressed GID_LOGFILE with SHIFT * pressed, so change gadget type to * GID_FILEPROMPT and fall through to * button code. */ gadid = GID_FILEPROMPT; /* Fall through */ case GID_SETUSE: case GID_SETUNDO: case GID_SETCANCEL: case GID_FORMATCOPY: case GID_FORMATEDIT: case GID_WINDOWFONT: case GID_BUFFERFONT: case GID_WFONTPROMPT: case GID_BFONTPROMPT: if (gadid == GID_WINDOWFONT) gadid = GID_WFONTPROMPT; if (gadid == GID_BUFFERFONT) gadid = GID_BFONTPROMPT; gad = Gadget[gadid]; ShowGadget(SetWindow, gad, GADGET_DOWN); activegad = gadid; activekey = imsg->Code; break; } } break; } } GT_ReplyIMsg(imsg); } /* * Handled all messages. Now check for possible USE/CANCEL */ if (doneset == SEL_USE) { /* * Use the current settings. To be safe, we update all * our string gadgets in case they were modified but * we didn't catch the modification (i.e. user didn't press * Return). */ str = STRVAL(GID_BUFFORMAT); if (strcmp(BufFormat, str) != 0) { strcpy(BufFormat, str); InstallNewFormat(NEW_STRING); } strcpy(CurSettings.Setup.LogFile, STRVAL(GID_LOGFILE)); strcpy(CurSettings.Setup.ScreenName, STRVAL(GID_SCREENNAME)); strcpy(CurSettings.Setup.HotKey, STRVAL(GID_HOTKEY)); strcpy(CurSettings.Setup.LogfileFormat, STRVAL(GID_LOGFORMAT)); strcpy(CurSettings.Setup.LogfileFormat, STRVAL(GID_LOGFORMAT)); val = atoi(STRVAL(GID_BUFFERSIZE)); CloseSettingsWindow(); /* * Now check if we've got a new buffer size -- * if so, install it. */ if (val < 1) val = 1; if (val != SavedSetupSets.Setup.BufferSize) { CurSettings.Setup.BufferSize = val; ClearWindowBuffer(); if (!SetTotalBufferSize(val * 1024, SETBUF_FORCENEW)) { ShowError(MSG(MSG_ERROR_NOBUFMEM), val); Cleanup(20); } } } else if (doneset == SEL_CANCEL) { /* * Cancel the current settings, and restore those that * were in effect before we started this. We close * the settings window first in case the main window * needs to close and re-open (change of font) since * this looks cleaner. * * Note that even though we saved the entire set of settings, * we only restore the SETUP settings, not all of them. */ CloseSettingsWindow(); InstallSettings(&SavedSetupSets, SET_SETUP); } } /* * ShowFuncOpts(options, firstid, lastid) * * Updates the function window gadgets to reflect the new values * in the specified options array. * * The new options are compared with the existing options to see * if they've changed, so you should make sure to call this function * _before_ copying the new options onto the current settings. * * Only gadgets from the first to the last ID given are affected. */ void ShowFuncOpts(UBYTE newopts[], int firstid, int lastid) { int i; for (i = firstid; i <= lastid; i++) { if (Gadget[i] != NULL && newopts[i] != CurSettings.Func.Opts[i]) { GT_SetGadgetAttrs(Gadget[i], FuncWindow, NULL, GTCB_Checked, newopts[i], TAG_DONE); } } } /* * ResetFuncToSelected() * * Changes the two cycle gadgets in the function window back to the * SELECTED state, and updates the variables accordingly. Note * that doesn't change the actual boolean settings themselves; in * effect, it makes the current settings permanent by stopping you * switching back to the SELECTED state to restore an older set * by clicking on the cycle gadget yourself. */ void ResetFuncToSelected(void) { if (FuncSystemCol != COL_SELECTED) { GT_SetGadgetAttrs(Gadget[GID_SELSYSTEM], FuncWindow, NULL, GTCY_Active, COL_SELECTED, TAG_DONE); } if (FuncDOSCol != COL_SELECTED) { GT_SetGadgetAttrs(Gadget[GID_SELDOS], FuncWindow, NULL, GTCY_Active, COL_SELECTED, TAG_DONE); } FuncSystemCol = COL_SELECTED; FuncDOSCol = COL_SELECTED; } /* * HandleFuncMsgs() * * Processes all outstanding messages associated with the Functions * gadget window. To be called whenever we get a functions event. * * The only slightly complicated bit here concerns the two cycle * gadgets which can be used to select all or none of either of * the DOS and System columns, or to restore a previous setting * installed before. */ void HandleFuncMsgs(void) { static int activegad; /* Non-zero if gadget is depressed */ static int activekey; /* Keycode shortcut of activegad */ struct IntuiMessage *imsg; int donefunc = SEL_NONE; if (!FuncWindow) return; while ((imsg = GT_GetIMsg(FuncWindowPort)) != NULL) { struct Gadget *gad; int shift = (imsg->Qualifier & IE_SHIFT); LONG gadid; int newval; switch (imsg->Class) { case IDCMP_REFRESHWINDOW: GT_BeginRefresh(FuncWindow); GT_EndRefresh(FuncWindow, TRUE); break; case IDCMP_CLOSEWINDOW: donefunc = SEL_USE; break; case IDCMP_MOUSEMOVE: case IDCMP_GADGETDOWN: case IDCMP_GADGETUP: gad = (struct Gadget *)imsg->IAddress; gadid = gad->GadgetID; newval = imsg->Code; handle_func_gads: switch (gadid) { case GID_MATCHNAME: strcpy(MatchName, STRVAL(GID_MATCHNAME)); SetPattern(MatchName, IgnoreWBShell); break; case GID_SELSYSTEM: case GID_SELDOS: /* * Changing system column cycle gadget. * Update to show ALL, NONE or SELECTED * system gadgets. */ { int first = FIRST_SYS_GADGET; int last = LAST_SYS_GADGET; int numgads = NUM_SYS_GADGETS; int *pcurval = &FuncSystemCol; if (gadid == GID_SELDOS) { /* * Override above defaults with new * defaults for DOS cycle gadget */ first = FIRST_DOS_GADGET; last = LAST_DOS_GADGET; numgads = NUM_DOS_GADGETS; pcurval = &FuncDOSCol; } if (newval == *pcurval) break; /* Ignore no-op events */ if (newval == COL_SELECTED) { /* * Restore previously saved system col values */ ShowFuncOpts(SavedCols, first, last); memcpy(&CurSettings.Func.Opts[first], &SavedCols[first], numgads); } else { /* * Select either ALL or NONE of the settings * in the system column. */ int fillval = (newval == COL_ALL); memset(&TempCols[first], fillval, numgads); ShowFuncOpts(TempCols, first, last); if (*pcurval == COL_SELECTED) { /* * If we are moving out of the * selected state, then save the * existing settings for later. */ memcpy(&SavedCols[first], &CurSettings.Func.Opts[first], numgads); } memset(&CurSettings.Func.Opts[first], fillval, numgads); } LoadFuncSettings(&CurSettings.Func); *pcurval = newval; break; } case GID_FUNCUSE: /* * Retain existing settings, and exit */ donefunc = SEL_USE; break; case GID_FUNCCANCEL: /* * Restore original settings and exit */ donefunc = SEL_CANCEL; break; case GID_FUNCUNDO: /* * Restore original settings but don't exit */ InstallSettings(&SavedFuncSets, SET_FUNC); break; default: if (gadid >= FIRST_BOOL_GADGET && gadid <= LAST_BOOL_GADGET) { /* * Getting a new boolean gadget so update * CurSettings to reflect this. */ CurSettings.Func.Opts[gadid] = (gad->Flags & GFLG_SELECTED) != 0; LoadFuncSettings(&CurSettings.Func); /* * Now check to see if we need to put * either column back into the selected * state again. */ if (FuncSystemCol != COL_SELECTED && gadid >= FIRST_SYS_GADGET && gadid <= LAST_SYS_GADGET) { FuncSystemCol = COL_SELECTED; GT_SetGadgetAttrs(Gadget[GID_SELSYSTEM], FuncWindow, NULL, GTCY_Active, COL_SELECTED, TAG_DONE); } if (FuncDOSCol != COL_SELECTED && gadid >= FIRST_DOS_GADGET && gadid <= LAST_DOS_GADGET) { FuncDOSCol = COL_SELECTED; GT_SetGadgetAttrs(Gadget[GID_SELDOS], FuncWindow, NULL, GTCY_Active, COL_SELECTED, TAG_DONE); } } break; } break; case IDCMP_INACTIVEWINDOW: if (activegad) { ShowGadget(FuncWindow, Gadget[activegad], GADGET_UP); activegad = 0; } break; case IDCMP_RAWKEY: { int upstroke = imsg->Code & 0x80; int keypress = ConvertIMsgToChar(imsg); gadid = FuncKeyboard[keypress]; if (activegad) { /* * We're releasing a gadget that was pressed * earlier, so handle it now */ int samekey = (imsg->Code & 0x7f) == activekey; if (samekey && !upstroke) /* Ignore repeated keys */ break; ShowGadget(FuncWindow, Gadget[activegad], GADGET_UP); if (samekey) { /* * Just released the key that was originally * pressed for this gadget, so now go and * handle the gadget action. */ gadid = activegad; gad = Gadget[gadid]; activegad = 0; goto handle_func_gads; } /* * If the above check didn't kick in, it means * we got a downpress of a different key, so * disable the gadget for this key and continue * to press the other key. */ activegad = 0; } if (imsg->Code == HELPKEY) { ShowAGuide(MSG(MSG_LINK_FUNCWIN)); break; } /* * Handle keyboard equivalents for gadgets */ gadid = FuncKeyboard[keypress]; if (gadid) { gad = Gadget[gadid]; if (gadid >= FIRST_BOOL_GADGET && gadid <= LAST_BOOL_GADGET) { /* * It's a boolean gadget so just invert its * current setting */ newval = !CurSettings.Func.Opts[gadid]; GT_SetGadgetAttrs(Gadget[gadid], FuncWindow, NULL, GTCB_Checked, newval, TAG_DONE); goto handle_func_gads; } else if (gadid == GID_SELSYSTEM || gadid == GID_SELDOS) { /* * One of the two cycle gadgets. Cycle forward * if the unshifted key was pressed or back * if the shifted version was pressed */ newval = FuncSystemCol; if (gadid == GID_SELDOS) newval = FuncDOSCol; newval++; if (shift) newval -= 2; if (newval < 0) newval = 2; if (newval > 2) newval = 0; GT_SetGadgetAttrs(gad, FuncWindow, NULL, GTCY_Active, newval, TAG_DONE); goto handle_func_gads; } else if (gadid == GID_MATCHNAME) { /* * The string gadget, so simply activate it */ ActivateGadget(gad, FuncWindow, NULL); } else { /* * It must be a button gadget so show it * selected first before processing it */ ShowGadget(FuncWindow, gad, GADGET_DOWN); activegad = gadid; activekey = imsg->Code; } } break; } } GT_ReplyIMsg(imsg); } if (donefunc != SEL_NONE) { /* * Since we cache the function gadgets between window opens, * we need to ensure the two cycle gadgets are reset to * SELECTED mode ready for the next time we open the window. * (Ideally, we would remember the cycle state and use the * same state next time, but that's too much work!) */ ResetFuncToSelected(); } if (donefunc == SEL_USE) { strcpy(MatchName, STRVAL(GID_MATCHNAME)); SetPattern(MatchName, IgnoreWBShell); CloseFunctionWindow(); } else if (donefunc == SEL_CANCEL) { CloseFunctionWindow(); /* * Note that though we saved all the settings, we only * re-install the original function settings, not all * of them. */ InstallSettings(&SavedFuncSets, SET_FUNC); } } /* * CreateFormatGadgets(gadgetfa, bufferfa, getsize, &width, &height) * * Creates all the gadgets for the format window. If getsize * is true, then the width and height values are filled in with * the dimensions of the window needed to hold the gadgets. In * this case, NULL is returned if the window would be too big to * fit on the current screen, or non-NULL if width and height * were successfully initialised. * * gadgetfa gives the gadget font to use, bufferfa gives the buffer * font (for the format listings). * * If getsize is zero, then the actual gadgets are created and * a pointer to the gadget list is returned, or NULL if an * error occurred (typically, out of memory). */ struct Gadget *CreateFormatGadgets(struct TextAttr *gadgetfa, struct TextAttr *bufferfa, int getsize, int *pwidth, int *pheight) { struct TextFont *gfont; struct TextFont *bfont; struct NewGadget ng; struct Gadget *gadlist; struct Gadget *gad; FieldInit *fi; int width; int height; int gwidth; int gheight; int gfonty; int bfontx; int bfonty; int topline; int maxtitlelen; int buttonwidth; int buttonspace; int headingspace; int outmargin; if (!FormVI) { FormVI = GetVisualInfoA(SnoopScreen, NULL); if (!FormVI) return (NULL); } gfont = MyOpenFont(gadgetfa); if (!gfont) return (NULL); bfont = MyOpenFont(bufferfa); if (!bfont) { CloseFont(gfont); return (NULL); } if (bfont->tf_Flags & FPF_PROPORTIONAL) { CloseFont(bfont); CloseFont(gfont); return (NULL); } bfontx = bfont->tf_XSize; bfonty = bfont->tf_YSize; gfonty = gfont->tf_YSize; gheight = gfonty + 3; gwidth = gheight + 15; topline = TitlebarHeight + gfonty/2; /* * Now calculate the width of the window boxes which will contain * the format sequences being displayed. The defaults box must be * wide enough to hold "titlemsg.. %x" whereas the current format * box must be wide enough to hold "titlemsg.. %20x". * * Since the message titles may be localised, we calculate the * longest string and work from that. */ maxtitlelen = 0; for (fi = FieldTypes; fi->type != EF_END; fi++) { int len = strlen(MSG(fi->titlemsgid)); if (len > maxtitlelen) maxtitlelen = len; } /* * The next two vars give the number of characters that will fit * across each box. These is used when producing output. * * For the left box, this is the length of the longest heading title, * plus 2 for spacing after the heading name, another 2 for the * format ID (%x) and a final 2 for the spacing on either side. * * The right box is similar, but uses an additional 2 characters to * display the field width of the selected item. */ FBoxA_NumChars = maxtitlelen + 6; FBoxB_NumChars = FBoxA_NumChars + 2; FBoxA_Width = FBoxA_NumChars * bfontx + 4; FBoxB_Width = FBoxB_NumChars * bfontx + 4; /* * Create format strings for sprintf formatting later on. These * look like this: * * FBoxA_FormString: " %-??s %%%lc " (2 parameters) * FBoxB_FormString: " %-??s %%%02ld%lc " (3 parameters) * * and when strings are output using these templates, they look like: * * FBoxA_FormString: " PaddedTitleName.... %x " * FBoxB_FormString: " PaddedTitleName.... %12x " */ mysprintf(FBoxA_FormString, " %%-%lds %%%%%%lc ", maxtitlelen); mysprintf(FBoxB_FormString, " %%-%lds %%%%%%02ld%%lc ", maxtitlelen); /* * Make sure we never have overlapping button gadgets (this might * happen if we had a big gadget font and small buffer font). */ buttonwidth = MaxTextLen(gfont, UseCancelUndoText) + 32; buttonspace = buttonwidth * 3 + FORM_MARGIN * 4 + BorderLeft + BorderRight; width = FBoxA_Width + FBoxB_Width + FORM_MARGIN * 4 + BorderLeft + BorderRight; outmargin = FORM_MARGIN; if (buttonspace > width) width = buttonspace; headingspace = GetTextLen(gfont, MSG(MSG_AVAILABLE_GAD)) + GetTextLen(gfont, MSG(MSG_CURFORMAT_GAD)) + BorderLeft + BorderRight + FORM_MARGIN*3; if (headingspace > width) width = headingspace; /* * Now calculate the dimensions of our two windows boxes. The left * box displays the available format headings, and the right box * displays the currently selected format headings. */ FBoxSpacing = bfonty + 2; FBoxA_Top = topline + gfonty + 4; FBoxA_Height = EF_COUNT * FBoxSpacing + 4; FBoxA_Left = BorderLeft + outmargin; FBoxB_Top = FBoxA_Top; FBoxB_Height = FBoxA_Height; FBoxB_Left = width - BorderRight - outmargin - FBoxB_Width; height = FBoxA_Top + FBoxA_Height + BorderBottom + (gfonty + GadgetHeight) * 3; if (width > ScreenWidth || height > ScreenHeight) { CloseFont(gfont); CloseFont(bfont); return (NULL); } if (getsize) { CloseFont(gfont); CloseFont(bfont); *pwidth = width; *pheight = height; return (struct Gadget *)(-1); } /* * Now create our window gadgets. We have a total of 4 -- the * Use, Undo, Cancel buttons, and the field width slider. */ gadlist = NULL; gad = CreateContext(&gadlist); if (!gad) goto fogad_failed; ng.ng_TextAttr = gadgetfa; ng.ng_VisualInfo = FormVI; ng.ng_Flags = PLACETEXT_IN; ng.ng_TopEdge = height - BorderBottom - (gfonty+GadgetHeight) * 3/2; ng.ng_Height = gfonty + GadgetHeight; ng.ng_Width = buttonwidth; ng.ng_LeftEdge = FORM_MARGIN + BorderLeft; ng.ng_GadgetText = MSG(MSG_USE_GAD); ng.ng_GadgetID = GID_FORMUSE; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fogad_failed; Gadget[GID_FORMUSE] = gad; AddKeyShortcut(FormKeyboard, GID_FORMUSE, MSG_USE_GAD); ng.ng_LeftEdge = width - BorderRight - FORM_MARGIN - ng.ng_Width; ng.ng_GadgetText = MSG(MSG_CANCEL_GAD); ng.ng_GadgetID = GID_FORMCANCEL; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fogad_failed; Gadget[GID_FORMCANCEL] = gad; AddKeyShortcut(FormKeyboard, GID_FORMCANCEL, MSG_CANCEL_GAD); ng.ng_LeftEdge = (ng.ng_LeftEdge + BorderLeft + FORM_MARGIN) / 2; ng.ng_GadgetText = MSG(MSG_UNDO_GAD); ng.ng_GadgetID = GID_FORMUNDO; gad = CreateGadget(BUTTON_KIND, gad, &ng, GT_Underscore, '_', TAG_DONE); if (!gad) goto fogad_failed; Gadget[GID_FORMUNDO] = gad; AddKeyShortcut(FormKeyboard, GID_FORMUNDO, MSG_UNDO_GAD); /* * Create the slider gadget. We need to leave room on the left for * the title, and on the right for the current value. */ ng.ng_LeftEdge = FORM_MARGIN + BorderLeft + 8 + GetTextLen(gfont, MSG(MSG_FIELDWIDTH_GAD)); ng.ng_Width = width - FORM_MARGIN - ng.ng_LeftEdge - BorderLeft - BorderRight - GetTextLen(gfont, "99 "); ng.ng_Height = gfonty + 3; ng.ng_TopEdge = (ng.ng_TopEdge + FBoxA_Top + FBoxA_Height - ng.ng_Height) / 2; ng.ng_GadgetText = MSG(MSG_FIELDWIDTH_GAD); ng.ng_GadgetID = GID_FORMWIDTH; ng.ng_Flags = PLACETEXT_LEFT; gad = CreateGadget(SLIDER_KIND, gad, &ng, GTSL_LevelFormat, "%2ld ", GTSL_LevelPlace, PLACETEXT_RIGHT, GTSL_Level, 1, GTSL_Min, 1, GTSL_Max, MAX_FIELD_LEN, GTSL_MaxLevelLen, 2, GT_Underscore, '_', GA_Disabled, TRUE, GA_RelVerify, TRUE, TAG_DONE); if (!gad) goto fogad_failed; Gadget[GID_FORMWIDTH] = gad; AddKeyShortcut(FormKeyboard, GID_FORMWIDTH, MSG_FIELDWIDTH_GAD); FormGadFont = gfont; FormBufFont = bfont; return (gadlist); fogad_failed: if (gadlist) FreeGadgets(gadlist); CloseFont(gfont); CloseFont(bfont); return (NULL); } /* * ShowFormatLine(line, select) * * Outputs the numbered line of text in the right-hand box. If the * number given is too high, then a blank line is output instead. * The text will be of the form "HeadingName.... %02x" according * to the current contents of the line. * * If select is FORMAT_SELECTED, the line will be highlighted, else * it will be output normally. */ void ShowFormatLine(int line, int select) { int bfontbaseline = FormBufFont->tf_Baseline; int boxwidth = FormBufFont->tf_XSize * FBoxB_NumChars; struct RastPort *rport = FormWindow->RPort; char viewbuf[60]; int fillpen = 0; int textpen = 1; int textrow; if (select == FORMAT_SELECTED) { fillpen = ScreenDI->dri_Pens[FILLPEN]; textpen = ScreenDI->dri_Pens[FILLTEXTPEN]; } SetFont(rport, FormBufFont); SetAPen(rport, textpen); SetBPen(rport, fillpen); SetDrMd(rport, JAM2); if (line < 0 || line >= EF_COUNT) return; if (line < FBoxB_CurLines) { FieldInit *fi = &FieldTypes[CurrentFields[line].type]; mysprintf(viewbuf, FBoxB_FormString, MSG(fi->titlemsgid), CurrentFields[line].width, fi->idchar); } else { memset(viewbuf, ' ', FBoxB_NumChars); } textrow = FBoxB_Top + line*FBoxSpacing + 3; Move(rport, FBoxB_Left + 2, textrow + bfontbaseline); Text(rport, viewbuf, FBoxB_NumChars); /* * Now draw highlight on top and bottom of selected line * (or erase, as the case may be). This fills out the * selection area and makes it look much better. */ SetAPen(rport, fillpen); Move(rport, FBoxB_Left + 2, textrow - 1); Draw(rport, FBoxB_Left + 1 + boxwidth, textrow - 1); Move(rport, FBoxB_Left + 2, textrow + FormBufFont->tf_YSize); Draw(rport, FBoxB_Left + 1 + boxwidth, textrow + FormBufFont->tf_YSize); } /* * ShowDragSelect(pos, select) * * Highlights the specified region to indicate where the currently * dragged field will end up if it is released here. * * If pos is FBOX_NOSELECT, no drop area is active and no output is * produced. If pos is FBOX_SELECTLEFT, then the left box is active * and it is given an inverse highlight around the interior of the * box giving a bezel effect. * * If pos is in the range 0 ... EF_COUNT, then it corresponds to an * insertion point between two lines in the right-most box. This is * show by a two-pixel high highlight line drawn between those two * lines. * * If select is FORMAT_SELECTED, the feature is drawn; if select is * FORMAT_UNSELECTED, the feature is instead erased. Note that after * calling this function, it may be necessary to redisplay any * current highlight bar in the right box (see ShowFormatLine) since * there is a one-pixel overlap between the highlight bar and the * rendering done here. */ void ShowDragSelect(int pos, int select) { struct RastPort *rport = FormWindow->RPort; int black = 1; int white = 2; if (pos == FBOX_NOSELECT) return; if (select == FORMAT_UNSELECTED) { black = 0; white = 0; } SetDrMd(rport, JAM2); if (pos == FBOX_SELECTLEFT) { /* * Draw bezel inside left box */ int boxleft = FBoxA_Left + 2; int boxtop = FBoxA_Top + 1; int boxright = FBoxA_Left + FBoxA_Width - 3; int boxbot = FBoxA_Top + FBoxA_Height - 2; SetAPen(rport, black); Move(rport, boxleft, boxbot); Draw(rport, boxleft, boxtop); Move(rport, boxleft+1, boxbot-1); Draw(rport, boxleft+1, boxtop); Draw(rport, boxright-1, boxtop); SetAPen(rport, white); Move(rport, boxright, boxtop); Draw(rport, boxright, boxbot); Move(rport, boxright-1, boxtop); Draw(rport, boxright-1, boxbot); Draw(rport, boxleft-1, boxbot); } else if (pos >= 0 && pos <= EF_COUNT) { /* * Draw thin selection line in right box */ int line = FBoxB_Top + 1 + pos * FBoxSpacing; SetAPen(rport, white); Move(rport, FBoxB_Left+2, line); Draw(rport, FBoxB_Left+FBoxB_Width-3, line); SetAPen(rport, black); Move(rport, FBoxB_Left+2, line+1); Draw(rport, FBoxB_Left+FBoxB_Width-3, line+1); } } /* * RedrawFormatWindow() * * Redraws all the parts of the format window that are generated by * hand: this includes the two bevelled boxes containing the available * format fields, the headings for those boxes, and the current contens * of those boxes, including highlighting where appropriate. */ void RedrawFormatWindow(void) { char viewbuf[60]; struct RastPort *rport = FormWindow->RPort; int gfonty = FormGadFont->tf_YSize; int gfontbaseline = FormGadFont->tf_Baseline; int bfontbaseline = FormBufFont->tf_Baseline; int line; int headpos; EditEvent *ee; DrawBevelBox(rport, FBoxA_Left, FBoxA_Top, FBoxA_Width, FBoxA_Height, GT_VisualInfo, FormVI, TAG_DONE); DrawBevelBox(rport, FBoxB_Left, FBoxB_Top, FBoxB_Width, FBoxB_Height, GT_VisualInfo, FormVI, TAG_DONE); SetAPen(rport, 1); SetBPen(rport, 0); SetDrMd(rport, JAM2); SetFont(rport, FormGadFont); /* * When outputting the box headings, we adjust the centering * to ensure that headings wider than the boxes don't get * pushed into the window borders. */ #define CENTRE(left,width,msgid) \ ((width - GetTextLen(FormGadFont, MSG(msgid)))/2 + left) headpos = CENTRE(FBoxA_Left, FBoxA_Width, MSG_AVAILABLE_GAD); if (headpos < (BorderLeft + 4)) headpos = FBoxA_Left; /* was: BorderLeft + 4; */ Move(rport, headpos, FBoxA_Top - gfonty + gfontbaseline - 4); Text(rport, MSG(MSG_AVAILABLE_GAD), strlen(MSG(MSG_AVAILABLE_GAD))); headpos = CENTRE(FBoxB_Left, FBoxB_Width, MSG_CURFORMAT_GAD); if (headpos < (FBoxB_Left - 4)) headpos = FBoxB_Left + FBoxB_Width - GetTextLen(FormGadFont, MSG(MSG_CURFORMAT_GAD)); Move(rport, headpos, FBoxB_Top - gfonty + gfontbaseline - 4); Text(rport, MSG(MSG_CURFORMAT_GAD), strlen(MSG(MSG_CURFORMAT_GAD))); /* * Now redraw the text inside the left box (available headings) */ SetFont(rport, FormBufFont); ee = &AvailableFields[0]; for (line = 0; line < (FBoxB_Height - 4); line += FBoxSpacing) { if (ee->type != END_EDITLIST) { FieldInit *fi = &FieldTypes[ee->type]; mysprintf(viewbuf, FBoxA_FormString, MSG(fi->titlemsgid), fi->idchar); ee++; } else { memset(viewbuf, ' ', FBoxA_NumChars); } Move(rport, FBoxA_Left + 2, FBoxA_Top + line + bfontbaseline + 3); Text(rport, viewbuf, FBoxA_NumChars); } /* * Redraw the text inside the right box (currently selected headings) */ ee = &CurrentFields[0]; for (line = 0; line < EF_COUNT; line++) { if (line == FBoxSelected) ShowFormatLine(line, FORMAT_SELECTED); else ShowFormatLine(line, FORMAT_UNSELECTED); } } /* * ConvertListToFormat(list, format) * * Converts the passed in EditList to a format list which can be * used for updating the main SnoopDos display. Returns the * total width (in characters) occupied by the format. */ int ConvertListToFormat(EditEvent *ee, EventFormat *evformat) { int width = 0; while (ee->type != END_EDITLIST) { width += ee->width + 1; /* Include space between cols */ evformat->type = FieldTypes[ee->type].type; evformat->width = ee->width; evformat->titlemsgid = FieldTypes[ee->type].titlemsgid; evformat++; ee++; } evformat->type = EF_END; if (width) width--; /* Don't count extra space at end */ return (width); } /* * CreateFormatLists(format) * * Parses the passed-in format string and creates the two lists * used by the format editor window to allow the user to re-arrange * the format. * * Should be called before RedrawFormatWindow() is called for the * first time. * * Also initialises some format globals related to the list boxes. */ void CreateFormatLists(EventFormat *evformat) { EditEvent *ee; FieldInit *fi; EventFormat *ef; /* * Now build the lists for the two boxes. The right box contains * all items in the format string (and in the same order that they * appear in the format string). The left box contains all the * items that are NOT in the format string, in the presorted * order that they appear in the FieldTypes[] initialiser array. * * Note that the two edit lists contain indexes directly into * the FieldTypes array, rather than specific event types. * Thus, we store the offset into the array when we find a match * rather than fi->type. */ ee = &CurrentFields[0]; for (ef = evformat; ef->type != EF_END; ef++) { for (fi = FieldTypes; fi->type != ef->type && fi->type != EF_END; fi++) ; ee->type = fi - FieldTypes; ee->width = ef->width; ee++; } ee->type = END_EDITLIST; FBoxB_CurLines = ee - &CurrentFields[0]; ee = &AvailableFields[0]; for (fi = FieldTypes; fi->type != EF_END; fi++) { for (ef = BufferEFormat; ef->type != fi->type && ef->type != EF_END; ef++) ; if (ef->type != fi->type) { /* Not in current format so add it here */ ee->type = fi - &FieldTypes[0]; ee++; } } ee->type = END_EDITLIST; FBoxA_CurLines = ee - &AvailableFields[0]; FBoxSelected = FBOX_NOSELECT; FBoxDragSelect = FBOX_NOSELECT; FormatCurLMB = 0; /* Left mouse button not selected at start */ FormatMovingField = 0; /* * Ensure slider gadget is disabled (it might have been left * enabled from an earlier time, which can be confusing). */ if (FormWindow) { GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GA_Disabled, TRUE, TAG_DONE); } } /* * OpenFormatWindow() * * Opens the format editor window with all the format gadgets. * Returns TRUE for success, FALSE for failure. * The window will contain all necessary gadgets. * * As with the Settings window, we try a variety of fonts until we * find one that fits onto the screen. */ int OpenFormatWindow(void) { static struct TextAttr formgfontattr = { "empty-storage-for-format1-fonts", 0, FS_NORMAL, FPB_DISKFONT}; static struct TextAttr formbfontattr = { "empty-storage-for-format2-fonts", 0, FS_NORMAL, FPB_DISKFONT}; static int width; /* Maintained from call to call */ static int height; int left = CurSettings.FormWinLeft; int top = CurSettings.FormWinTop; struct TextAttr *gadgetfa; struct TextAttr *bufferfa; struct Window *win; int i; CheckSegTracker(); if (FormWindow) { WindowToFront(FormWindow); ActivateWindow(FormWindow); return (TRUE); } if (!CheckForScreen()) return (FALSE); strcpy(FormatSaveFormat, BufFormat); /* Save for possible Undo */ /* * Now try all combinations of gadget and buffer font until we * find a font combination that fits on our screen */ for (i = 0; gadgetfa = MainWindowFontList[i].gadgetfa; i++) { bufferfa = MainWindowFontList[i].bufferfa; if (CreateFormatGadgets(gadgetfa, bufferfa, TRUE, &width, &height)) break; } if (!gadgetfa) { ShowError(MSG(MSG_ERROR_OPENFORMAT)); return (FALSE); } /* * Now copy our font attributes so that our gadgets won't strangely * get changed behind the scenes if we update our fonts */ strcpy(formgfontattr.ta_Name, gadgetfa->ta_Name); formgfontattr.ta_YSize = gadgetfa->ta_YSize; gadgetfa = &formgfontattr; strcpy(formbfontattr.ta_Name, bufferfa->ta_Name); formbfontattr.ta_YSize = bufferfa->ta_YSize; bufferfa = &formbfontattr; if (left == -1) left = (ScreenWidth - width) / 2; if (top == -1) top = (ScreenHeight - height) / 2; win = OpenWindowTags(NULL, WA_Title, MSG(MSG_FORMAT_TITLE), WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_NEWSIZE | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_INACTIVEWINDOW | BUTTONIDCMP | SLIDERIDCMP, WA_Left, left, WA_Top, top, WA_Width, width, WA_Height, height, WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_ACTIVATE | WFLG_RMBTRAP | WFLG_NEWLOOKMENUS, RefreshTag, TRUE, WA_NoCareRefresh, NoCareRefreshBool, WA_PubScreen, SnoopScreen, TAG_DONE); if (!win) { ShowError(MSG(MSG_ERROR_OPENFORMAT)); return (FALSE); } if (!FormGadList) { FormGadList = CreateFormatGadgets(gadgetfa, bufferfa, 0, 0, 0); if (!FormGadList) { CloseWindow(win); ShowError(MSG(MSG_ERROR_OPENFORMAT)); return (FALSE); } } /* * Now try and create our BOBs for the window */ TextBob = CreateBob(win->RPort, FBoxB_Width + 8, FormBufFont->tf_YSize + 4, 1); if (!TextBob) { ShowError(MSG(MSG_ERROR_OPENFORMAT)); FreeGadgets(FormGadList); FormGadList = NULL; CloseWindow(win); return (FALSE); } AddBob(&TextBob->bob, win->RPort); /* * Now add gadgets to screen window, and then setup the current * format string for editing. */ AddGList(win, FormGadList, -1, -1, NULL); RefreshGList(FormGadList, win, NULL, -1); GT_RefreshWindow(win, NULL); /* * All done, so initialise some globals and return */ FormWindow = win; FormWindowPort = win->UserPort; FormWindowMask = 1 << FormWindowPort->mp_SigBit; CreateFormatLists(BufferEFormat); /* * Finally, draw our window to reflect this */ RedrawFormatWindow(); if (DisableNestCount) DisableWindow(FormWindow, &FormRequester); return (TRUE); } /* * CloseFormatWindow(void) * * Close the format window and free gadgets associated with the window. */ void CloseFormatWindow(void) { if (FormWindow) { if (TextBob) { FreeBob(TextBob); TextBob = NULL; } RecordWindowSizes(); CloseWindow(FormWindow); CloseFont(FormGadFont); CloseFont(FormBufFont); FormWindow = NULL; FormWindowMask = 0; FormGadFont = NULL; FormBufFont = NULL; } if (FormGadList) { FreeGadgets(FormGadList); FormGadList = NULL; } if (FormVI) { FreeVisualInfo(FormVI); FormVI = NULL; } } /* * FormatHitDetect(x, y) * * Does a bounds check for the specified X/Y co-ordinates against the * format editor window. A return value in the range 0..EF_COUNT-1 * indicates that line number in the right hand window was hit. * * A return of FBOX_SELECTLEFT indicates that the left box was selected * (not sure which line; doesn't really matter). * * A return of FBOX_NOSELECT indicates neither box was selected. */ int FormatHitDetect(int x, int y) { if (x >= FBoxA_Left && x <= (FBoxA_Left + FBoxA_Width - 1) && y >= FBoxA_Top && y <= (FBoxA_Top + FBoxA_Height - 1)) { return (FBOX_SELECTLEFT); } if (x >= FBoxB_Left && x <= (FBoxB_Left + FBoxB_Width - 1) && y >= FBoxB_Top && y <= (FBoxB_Top + FBoxB_Height - 1)) { if (y <= FBoxB_Top + FBoxSpacing + 2) return (0); else return (y - FBoxB_Top - 2) / FBoxSpacing; } return (FBOX_NOSELECT); } /* * InstallNewFormat(type) * * Installs the currently defined format into the main window for * the user to see immediately. * * If type is NEW_LISTVIEW, then the new format is assumed to be * from the format editor window's CurrentFields list. The * setup window, if open, will have its own format string updated * accordingly. * * If type is NEW_STRING, then the new format is assumed to be * stored in the BufFormat string. The format window, if open, * will have its own format listview updated accordingly. * * If the buffer string is completely invalid (i.e. evaluates to * an empty format) then it will be replaced with an empty string. */ void InstallNewFormat(int type) { if (type == NEW_LISTVIEW) { BufferWidth = ConvertListToFormat(CurrentFields, BufferEFormat); } else /* type == NEW_STRING */ { BufferWidth = ParseFormatString(BufFormat, BufferEFormat, MAX_FORM_LEN); if (BufferWidth == 0) { BufferWidth = ParseFormatString(DefaultSettings.Setup.BufferFormat, BufferEFormat, MAX_FORM_LEN); } if (FormWindow) { CreateFormatLists(BufferEFormat); RedrawFormatWindow(); } } BuildFormatString(BufferEFormat, BufFormat, MAX_FORM_LEN); if (SetWindow) { GT_SetGadgetAttrs(Gadget[GID_BUFFORMAT], SetWindow, NULL, GTST_String, BufFormat, TAG_DONE); } ClearMainRHS = 1; InitMainMargins(); UpdateMainHScroll(); RedrawMainWindow(); } /* * HandleFormatMsgs() * * Processes all outstanding messages associated with the Format * editor gadget window. To be called whenever we get a format event. */ void HandleFormatMsgs(void) { static int activegad; /* Non-zero if gadget is depressed */ static int activekey; /* Keycode shortcut of activegad */ struct IntuiMessage *imsg; int doneform = SEL_NONE; int hit; int lmb; int rmb; if (!FormWindow) return; while ((imsg = GT_GetIMsg(FormWindowPort)) != NULL) { struct Gadget *gad; int dxwidth = 0; /* Amount to adjust width by */ int newrow = FBoxSelected; /* Currently selected row */ int gadid; switch (imsg->Class) { case IDCMP_CLOSEWINDOW: doneform = SEL_USE; break; case IDCMP_REFRESHWINDOW: GT_BeginRefresh(FormWindow); RedrawFormatWindow(); GT_EndRefresh(FormWindow, TRUE); break; case IDCMP_MOUSEBUTTONS: lmb = imsg->Qualifier & IEQUALIFIER_LEFTBUTTON; rmb = imsg->Qualifier & IEQUALIFIER_RBUTTON; /* * If we're already dragging something, then the * right mouse button allows it to be cancelled. */ if (FormatMovingField && rmb) goto cancel_moving_field; if (lmb == FormatCurLMB) /* Ignore spurious events */ break; hit = FormatHitDetect(imsg->MouseX, imsg->MouseY); if (lmb) { /* * User is trying to select or pick-up a new * event. */ if (hit >= 0 && hit < FBoxB_CurLines && hit != FBoxSelected) { if (FBoxSelected >= 0 && FBoxSelected < FBoxB_CurLines) ShowFormatLine(FBoxSelected, FORMAT_UNSELECTED); FBoxSelected = hit; ShowFormatLine(hit, FORMAT_SELECTED); GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GA_Disabled, FALSE, GTSL_Level, CurrentFields[hit].width, TAG_DONE); } if (hit != FBOX_NOSELECT) { /* * Pick-up the icon under mouse */ if (PickupBob(hit, imsg->MouseX, imsg->MouseY)) { FormatMovingField = 1; ReportMouse(1, FormWindow); /* Enable mouse moves */ } } } else { /* * We might be finished doing a drag of a field * from one position to another -- if so, drop the * field in it's new place. */ if (FormatMovingField) { int curdragselect = FBoxDragSelect; FormatMovingField = 0; ReportMouse(0, FormWindow); /* Disable mouse moves */ DropBob(); if (curdragselect != FBOX_NOSELECT) InstallNewFormat(NEW_LISTVIEW); } } FormatCurLMB = lmb; break; case IDCMP_MOUSEMOVE: if (FormatMovingField) { UpdateBob(imsg->MouseX, imsg->MouseY); break; } /* * Fall through to handle MOUSEMOVE for slider gadget */ case IDCMP_GADGETDOWN: case IDCMP_GADGETUP: gad = (struct Gadget *)imsg->IAddress; gadid = gad->GadgetID; handle_form_gads: switch (gadid) { case GID_FORMUSE: doneform = SEL_USE; break; case GID_FORMCANCEL: doneform = SEL_CANCEL; break; case GID_FORMUNDO: strcpy(BufFormat, FormatSaveFormat); InstallNewFormat(NEW_STRING); break; case GID_FORMWIDTH: /* * The slider has been moved, so update the * currently selected item accordingly. */ if (FBoxSelected >= 0 && FBoxSelected < FBoxB_CurLines) { CurrentFields[FBoxSelected].width = imsg->Code; ShowFormatLine(FBoxSelected, FORMAT_SELECTED); if (imsg->Class == IDCMP_GADGETUP) InstallNewFormat(NEW_LISTVIEW); } break; } break; case IDCMP_INACTIVEWINDOW: /* * Cancel any currently pressed button */ if (activegad) { ShowGadget(FormWindow, Gadget[activegad], GADGET_UP); activegad = 0; } /* * If we're dragging something and the window * goes inactive, drop it. Otherwise, when the * window is reactivated, it will still be attached * to the mouse pointer! */ if (FormatMovingField) { FormatCurLMB = 0; cancel_moving_field: ReportMouse(0, FormWindow); UpdateBob(0, -9999); /* Make position invalid */ DropBob(); FormatMovingField = 0; if (imsg->Class == IDCMP_INACTIVEWINDOW) { /* * If we went inactive, then the act of * erasing the BOB has probably resulted * in a portion of the window frame being * restored in its active colours, even * though the window is now inactive -- so * we refresh it to get around this. */ RefreshWindowFrame(FormWindow); } } break; case IDCMP_RAWKEY: { int shift = imsg->Qualifier & IE_SHIFT; int altctrl = imsg->Qualifier & (IE_ALT | IE_CTRL); int upstroke = imsg->Code & 0x80; int keypress = ConvertIMsgToChar(imsg); /* * Handle keyboard shortcuts */ if (FormatMovingField && keypress == '\033') { /* * ESC cancels any drag currently in operation */ goto cancel_moving_field; } gadid = FormKeyboard[keypress]; if (activegad) { /* * We're releasing a gadget that was pressed * earlier, so handle it now */ int samekey = (imsg->Code & 0x7f) == activekey; if (samekey && !upstroke) /* Ignore repeated keys */ break; ShowGadget(FormWindow, Gadget[activegad], GADGET_UP); if (samekey) { /* * Just released the key that was originally * pressed for this gadget, so now go and * handle the gadget action. */ gadid = activegad; gad = Gadget[gadid]; activegad = 0; goto handle_form_gads; } /* * If the above check didn't kick in, it means * we got a downpress of a different key, so * disable the gadget for this key and continue * to press the other key. */ activegad = 0; } /* * Check for normal gadget keyboard shortcuts */ if (gadid) { gad = Gadget[gadid]; if (gadid == GID_FORMWIDTH) { /* * Got a slider adjustment request */ if (imsg->Qualifier & IE_SHIFT) dxwidth = -1; else dxwidth = 1; if (newrow == FBOX_NOSELECT) newrow = 0; /* If no row active, pick first row */ if (FBoxB_CurLines == 0) /* Ignore if box is empty */ break; goto format_move_slider; } /* * Must be a simple button press so highlight * the button and handle accordingly */ ShowGadget(FormWindow, gad, GADGET_DOWN); activegad = gadid; activekey = imsg->Code; break; } /* * Handle cursor movement. This lets us choose a * new item in the format box, or adjust the width * of an existing item. */ if (!FBoxB_CurLines) /* A format must exist */ break; if (newrow == FBOX_NOSELECT) newrow = -1; /* Ensure cursor down selects item 0 */ switch (imsg->Code) { case CURSORUP: if (newrow > 0 && !(shift | altctrl)) newrow--; else newrow = 0; break; case CURSORDOWN: if (shift | altctrl) newrow = FBoxB_CurLines - 1; else if (newrow < (FBoxB_CurLines-1)) newrow++; break; case CURSORLEFT: dxwidth = -1; if (shift) dxwidth = -5; if (altctrl) dxwidth = -MAX_FIELD_LEN; break; case CURSORRIGHT: dxwidth = 1; if (shift) dxwidth = 5; if (altctrl) dxwidth = MAX_FIELD_LEN; break; case HELPKEY: ShowAGuide(MSG(MSG_LINK_FORMATWIN)); goto done_keys; default: goto done_keys; /* Ignore all other keys */ } format_move_slider: /* * First check if the row has changed. (As well as * changing via cursor up/down, this can also happen * with left/right if there was no item selected to * begin with */ if (newrow < 0) newrow = 0; if (newrow != FBoxSelected && FBoxB_CurLines > 0) { /* * Unactivate old row, activate a new row */ if (FBoxSelected >= 0 && FBoxSelected < FBoxB_CurLines) ShowFormatLine(FBoxSelected, FORMAT_UNSELECTED); FBoxSelected = newrow; ShowFormatLine(newrow, FORMAT_SELECTED); GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GA_Disabled, FALSE, GTSL_Level, CurrentFields[newrow].width, TAG_DONE); } /* * Now check if the horizontal position of the * slider has changed. */ if (dxwidth) { int newwidth = CurrentFields[FBoxSelected].width + dxwidth; if (newwidth < 1) newwidth = 1; if (newwidth > MAX_FIELD_LEN) newwidth = MAX_FIELD_LEN; if (FBoxSelected != FBOX_NOSELECT) { if (newwidth != CurrentFields[FBoxSelected].width) { GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GTSL_Level, newwidth, TAG_DONE); CurrentFields[FBoxSelected].width = newwidth; ShowFormatLine(FBoxSelected, FORMAT_SELECTED); InstallNewFormat(NEW_LISTVIEW); } } } done_keys: break; } } GT_ReplyIMsg(imsg); } if (doneform != SEL_NONE) { CloseFormatWindow(); if (doneform == SEL_CANCEL) { strcpy(BufFormat, FormatSaveFormat); InstallNewFormat(NEW_STRING); } } } /* * ----------------------------------------------------------------------- * Now our BOB/GEL routines for the drag-and-drop format editor. * These should really be in their own module, but they're being * put here for now for convenience. * ----------------------------------------------------------------------- */ /* * InitGel(rastport, width, height, transp) * * This function does all the initialisation necessary to allow us * to use a single BOB in the given rastport. A new bitmap is allocated * of dimensions width x height into which data can be rendered. * * If successful, this function returns a pointer to a MyGel structure * which contains, among other things, the rastport and bob structure * you will use when calling system routines to manipulate the bob. * * Note that the bob must be removed from the Gel list before rendering * into it. * * If transp is true, then the BOB will be transparent on colour zero. * In this case, you must call InitMasks(mygel->vsprite) whenever * you make a change to the image, to recalculate the mask. */ MyGel *CreateBob(struct RastPort *rport, int width, int height, int transp) { struct GelsInfo *gInfo; struct Bob *bob; struct VSprite *vsprite; MyGel *mygel; ULONG linesize; ULONG planesize; ULONG imagesize; ULONG chipsize; UBYTE *chipdata; int wordwidth; int depth; int i; wordwidth = (width + 15) >> 4; depth = rport->BitMap->Depth; mygel = (MyGel *)AllocMem(sizeof(MyGel), MEMF_CLEAR); if (!mygel) return (NULL); mygel->mainrast = rport; gInfo = &mygel->gelinfo; gInfo->nextLine = mygel->nextline; gInfo->lastColor = mygel->lastcolor; gInfo->collHandler = &mygel->colltable; gInfo->sprRsrvd = 0x03; /* * Set left-most and top-most to 1 to better keep items * inside the display boundaries. */ gInfo->leftmost = 1; gInfo->topmost = 1; gInfo->rightmost = (rport->BitMap->BytesPerRow << 3) - 1; gInfo->bottommost = rport->BitMap->Rows - 1; rport->GelsInfo = gInfo; InitGels(&mygel->vshead, &mygel->vstail, gInfo); /* * Now allocate a new BOB to be linked into this structure. * We also allocate a bitmap associated with the bob, and * give it its own RastPort so that we can render into it. */ linesize = sizeof(WORD) * wordwidth; planesize = linesize * height; imagesize = planesize * depth; chipsize = imagesize*2 + linesize + planesize; bob = &mygel->bob; vsprite = &mygel->bobvsprite; if (!(chipdata = AllocMem(chipsize, MEMF_CHIP | MEMF_CLEAR))) { rport->GelsInfo = NULL; FreeMem(mygel, sizeof(MyGel)); return (NULL); } mygel->chipdata = (void *)(chipdata); mygel->chipsize = chipsize; mygel->planedata = (void *)(chipdata); bob->SaveBuffer = (void *)(chipdata + imagesize); vsprite->BorderLine = (void *)(chipdata + imagesize*2); vsprite->CollMask = (void *)(chipdata + imagesize*2 + linesize); vsprite->Y = -9999; vsprite->X = -9999; vsprite->Flags = SAVEBACK | (transp ? OVERLAY : 0); vsprite->Width = wordwidth; vsprite->Depth = depth; vsprite->Height = height; vsprite->MeMask = 0; vsprite->HitMask = 0; vsprite->ImageData = (void *)mygel->planedata; vsprite->SprColors = NULL; vsprite->PlanePick = -1; vsprite->PlaneOnOff = 0x00; InitMasks(vsprite); vsprite->VSBob = bob; bob->BobVSprite = vsprite; bob->ImageShadow = vsprite->CollMask; bob->Flags = 0; bob->Before = NULL; bob->After = NULL; bob->BobComp = NULL; bob->DBuffer = NULL; /* * Now setup the Rastport and Bitmap so we can render into the BOB */ InitRastPort(&mygel->rastport); InitBitMap(&mygel->bitmap, depth, width, height); mygel->rastport.BitMap = &mygel->bitmap; for (i = 0; i < depth; i++) mygel->bitmap.Planes[i] = mygel->planedata + i * planesize; return (mygel); } /* * FreeBob(mygel) * * Frees the bob allocated earlier, along with all other associated info * Also removes all GELs from the system rastport. */ void FreeBob(MyGel *mygel) { mygel->mainrast->GelsInfo = NULL; FreeMem(mygel->chipdata, mygel->chipsize); FreeMem(mygel, sizeof(MyGel)); } /* * PickupBob(hit, x, y) * * Picks up the line and copies it into our bob's bitmap, then adds * the bob to the screen at the current mouse location so the * user can drag it around. * * If the hit is correct, we can ignore Y since the field being * dragged is in the right box and we already know which line. * Otherwise, Y is a pixel co-ordinate that we use to calculate * the line of the box that's being chosen. * * If hit is FBOX_SELECTLEFT, then we need to calculate which line * we are dealing with. * * We also record the start line we use. * * Returns true for success, false if we didn't have a hit after all. */ int PickupBob(int hit, int x, int y) { struct RastPort *rport = FormWindow->RPort; struct RastPort *bobrport = &TextBob->rastport; char viewbuf[50]; int line; int box; int xpos; int ypos; int len; if (hit == FBOX_NOSELECT) { return (FALSE); } else if (hit == FBOX_SELECTLEFT) { box = FORMAT_LEFTBOX; line = (y - FBoxA_Top - 2); if (line < FBoxSpacing) line = 0; else line /= FBoxSpacing; if (line >= FBoxA_CurLines) return (FALSE); } else { /* Right box */ box = FORMAT_RIGHTBOX; line = hit; if (line >= FBoxB_CurLines) return (FALSE); } /* * Okay, got a definite hit so go ahead and start the drag */ FormatDragBox = box; FormatDragLine = line; SetRast(bobrport, 0); /* Erase rast bitmap */ SetAPen(bobrport, 2); /* White foreground */ SetBPen(bobrport, 1); /* Black background */ SetDrMd(bobrport, JAM2); /* Solid text */ SetFont(bobrport, FormBufFont); if (box == FORMAT_LEFTBOX) { FieldInit *fi = &FieldTypes[AvailableFields[line].type]; mysprintf(viewbuf, FBoxA_FormString, MSG(fi->titlemsgid), fi->idchar); len = FBoxA_NumChars; xpos = FBoxA_Left + 2; ypos = FBoxA_Top + 2 + line * FBoxSpacing; } else { FieldInit *fi = &FieldTypes[CurrentFields[line].type]; mysprintf(viewbuf, FBoxB_FormString, MSG(fi->titlemsgid), CurrentFields[line].width, fi->idchar); len = FBoxB_NumChars; xpos = FBoxB_Left + 2; ypos = FBoxB_Top + 2 + line * FBoxSpacing; } TextBob->bob.BobVSprite->X = xpos; TextBob->bob.BobVSprite->Y = ypos; FBoxDeltaX = xpos - x; FBoxDeltaY = ypos - y; Move(bobrport, 0, FormBufFont->tf_Baseline+1); Text(bobrport, viewbuf, len); /* * Now draw highlight above and below the text */ SetAPen(bobrport, 1); Move(bobrport, 0, 0); Draw(bobrport, len * FormBufFont->tf_XSize - 1, 0); Move(bobrport, 0, FormBufFont->tf_YSize + 1); Draw(bobrport, len * FormBufFont->tf_XSize - 1, FormBufFont->tf_YSize + 1); InitMasks(TextBob->bob.BobVSprite); SortGList(rport); DrawGList(rport, ViewPortAddress(FormWindow)); return (1); } /* * DropBob() * * Releases the current bob at the target, and updates the two * lists/displays accordingly. */ void DropBob(void) { EditEvent savedev; EditEvent *ee; int destpos; int seltype; destpos = FBoxDragSelect; /* Save destination drop selection */ UpdateBob(0, -9999); /* Kill any highlighted region */ /* * Now re-arrange the edit lists to reflect the new position of * the dragged field. There are three cases to consider: * * Left box -> Right box * Right box -> Right box * Right box -> Left box */ if (destpos == FBOX_NOSELECT) return; ShowFormatLine(FBoxSelected, FORMAT_UNSELECTED); if (FBoxSelected >= 0 && FBoxSelected < EF_COUNT) seltype = CurrentFields[FBoxSelected].type; else seltype = -1; /* * First, delete item from its current list */ if (FormatDragBox == FORMAT_LEFTBOX) { /* * Deleting from left box */ ee = &AvailableFields[FormatDragLine]; savedev.type = ee->type; savedev.width = FieldTypes[ee->type].defwidth; while (ee->type != END_EDITLIST) { ee[0] = ee[1]; ee++; } FBoxA_CurLines--; seltype = savedev.type; /* New selection is item we just dragged */ } else { /* * Deleting from right box. If the drop position is after * the point where we're deleting, we adjust it to reflect * the deleted line. */ if (destpos > FormatDragLine && destpos <= EF_COUNT) destpos--; ee = &CurrentFields[FormatDragLine]; savedev = *ee; while (ee->type != END_EDITLIST) { ee[0] = ee[1]; ee++; } FBoxB_CurLines--; } /* * Now add the item (savedev) to the new list in the appropriate * place */ if (destpos == FBOX_SELECTLEFT) { /* * Search the existing left box list looking for the best place * to insert the item -- we do this in strict alphabetical * order (based on the % identifer) */ int newid = FieldTypes[savedev.type].idchar; EditEvent tempev; for (ee = &AvailableFields[0]; ee->type != END_EDITLIST; ee++) if (FieldTypes[ee->type].idchar > newid) break; do { tempev = *ee; *ee = savedev; savedev = tempev; ee++; } while (ee[-1].type != END_EDITLIST); FBoxA_CurLines++; } else { /* * Adding line to the right box. Simply insert it at the * specified location. */ EditEvent tempev; ee = &CurrentFields[destpos]; do { tempev = *ee; *ee = savedev; savedev = tempev; ee++; } while (ee[-1].type != END_EDITLIST); FBoxB_CurLines++; } /* * Now search our right box list to see can we match the item * that was currently highlighted when we entered this routine. */ FBoxSelected = 0; for (ee = &CurrentFields[0]; ee->type != END_EDITLIST; ee++) { if (ee->type == seltype) break; FBoxSelected++; } if (ee->type == END_EDITLIST) { FBoxSelected = FBOX_NOSELECT; GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GA_Disabled, TRUE, TAG_DONE); } else { GT_SetGadgetAttrs(Gadget[GID_FORMWIDTH], FormWindow, NULL, GA_Disabled, FALSE, GTSL_Level, ee->width, TAG_DONE); } /* * Now, finally, update our window to show the new arrangement. */ RedrawFormatWindow(); } /* * UpdateBob(x, y) * * Updates the current position of the bob on the screen, and * draws any highlights etc. that are deemed necessary. */ void UpdateBob(int x, int y) { int bfonty = FormBufFont->tf_YSize; int pos; /* * Now see if we are overlapping any of the drop regions, and if * so, highlight the region appropriately. * * When dragging into the left box, we highlight the entire box * since the order of items is pre-determined and it doesn't * make any sense to drop a field into one particular place. * * When dragging into the right box, we highlight a line in * between two other lines, which shows where the item will * be inserted. * * For neatness, we don't show highlighting which would have * no effect on the current settings (i.e. moving a field to * the same position it's already in). */ pos = FBOX_NOSELECT; if (x >= FBoxA_Left && x <= (FBoxA_Left + FBoxA_Width - 1) && y >= FBoxA_Top && y <= (FBoxA_Top + FBoxA_Height - 1) && FormatDragBox != FORMAT_LEFTBOX) { /* * Got a hit in left-hand box */ pos = FBOX_SELECTLEFT; } else if (x >= FBoxB_Left && x <= (FBoxB_Left + FBoxB_Width - 1) && y >= (FBoxB_Top - 10) && y <= (FBoxB_Top + FBoxB_Height + 10)) { /* * Got a hit in the right-hand box, so now calculate which line */ pos = (y - FBoxB_Top - 2 + bfonty/2) / FBoxSpacing; if (pos < 0) pos = 0; if (pos > FBoxB_CurLines) pos = FBoxB_CurLines; if (FormatDragBox == FORMAT_RIGHTBOX && (pos == FormatDragLine || pos == (FormatDragLine+1))) { pos = FBOX_NOSELECT; /* Don't highlight line we dragged from */ } } if (pos != FBoxDragSelect) { /* * Currently selected item has changed, so erase current one, * then draw new one. We also refresh the currently selected * line, in case it got trashed by the highlighting. * * Note that we must first move the bob off-screen while we * render the changes, otherwise it gets confused and tends * to obscure the output. */ TextBob->bob.BobVSprite->X = 0; TextBob->bob.BobVSprite->Y = -9999; SortGList(FormWindow->RPort); DrawGList(FormWindow->RPort, ViewPortAddress(FormWindow)); ShowDragSelect(FBoxDragSelect, FORMAT_UNSELECTED); ShowFormatLine(FBoxSelected, FORMAT_SELECTED); ShowDragSelect(pos, FORMAT_SELECTED); FBoxDragSelect = pos; } TextBob->bob.BobVSprite->X = x + FBoxDeltaX; TextBob->bob.BobVSprite->Y = y + FBoxDeltaY; SortGList(FormWindow->RPort); DrawGList(FormWindow->RPort, ViewPortAddress(FormWindow)); }