home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 19
/
AACD19.BIN
/
AACD
/
System
/
SnoopDos
/
SnoopDos_Src
/
SnoopDos_Source
/
subwin.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-02-04
|
185KB
|
3,853 lines
/*
* 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(void);
/*****************************************************************************
*
* 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 ("<Unknown Function>");
}
/*
* 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));
}