/***********************/
/*		       */
/* Typeface v1.0       */
/* © 1995 David Kinder */
/*		       */
/***********************/

#include "Typeface.h"

extern struct IntuitionBase *IntuitionBase;
struct Library *AslBase, *BGUIBase, *DiskfontBase;
struct Screen *Screen;
struct Window *FontWnd, *OldWindowPtr;
struct DrawInfo *ScrDrawInfo;
struct List *CharWndList;
struct MsgPort *WndMsgPort;
struct Image *SizeImage, *LeftImage, *RightImage, *UpImage, *DownImage;
struct TextFont *NewFont;
struct FontRequester *FontReq;
struct ScreenModeRequester *ScrReq;
struct Character CharBuffer[257];
struct Character ClipChar;
struct IBox CharSize;
struct NameInfo ModeIDName;
Object *FontWndObj, *CharObj, *ScrollObj;
Class *CharClass, *EditClass, *SlideClass;
WORD SpaceTable[257], KernTable[257];
ULONG FirstChar, LastChar;
char WinTitle[256], FontPath[256], SavePath[256], FontName[256];
BOOL CustomScreen = TRUE, ChangeScreen = FALSE, NewCustom, UseTable;

struct Preferences Prefs =
  { 1,PREFS_CUSTOMSCREEN|PREFS_PIXELBORDER,0,2,11,20,20,0 };
struct NewMenu menus[] = { PROJECT_MENU,End };
char Version[] = "$VER:"NAME" 1.0 (29.5.95)";

struct Window *ParamWnd;
Object *ParamWndObj, *PropCycle, *HeightInt, *WidthInt, *BaseInt, *SmearInt;
Object *NormalCheck, *BoldCheck, *ItalicCheck, *UnderCheck;
Object *ExtCheck, *RevCheck, *AspectCycle, *BlankButton;
ULONG Proportional, Normal, Bold, Italic, ULine, Extended, Reversed;
ULONG Height, Width, Baseline, Smear, Aspect;

struct Window *PrefsWnd;
Object *PrefsWndObj;
Object *CustButton, *ChooseButton, *ModeInfo;
Object *VecInt, *PixelXInt, *PixelYInt, *PixelCheck;
Object *SaveCycle;
ULONG PrefScrModeID,PrefScrDepth;
extern ULONG PixelBorder;

struct Window *AssignWnd;
Object *AssignWndObj, *FontDirStr, *DirReqObj, *SaveReqObj;

struct Window *WidthWnd;
struct Character *WidthChar;
struct CharNode *WidthNode;
Object *WidthWndObj, *ChrWidthScroll, *ChrWidthInt, *LeftInd, *RightInd;
char WidthTitle[256];

struct Window *KernWnd;
struct Character *KernChar;
struct CharNode *KernNode;
Object *KernWndObj, *SpaceObj, *KernObj;
char KernTitle[256];

struct Window *SaveWnd;
Object *SaveWndObj, *SaveDirStr, *FontNameStr, *SizeInfo;
Object *FirstNum, *LastNum, *TableCheck;

main()
{
  Setup();
  MsgLoop();
  Quit();
}

wbmain()
{
  main();
}

void Setup(void)
{
struct Screen *defscr;
static struct TextAttr topaz8 = { "topaz.font",8,FS_NORMAL,FPF_ROMFONT };
BPTR lock;

  if (IntuitionBase->LibNode.lib_Version < 37) Quit();
  if ((AslBase = OpenLibrary("asl.library",37)) == NULL)
    Error("Cannot open asl.library v37+.");
  if ((BGUIBase = OpenLibrary("bgui.library",38)) == NULL)
  {
    if ((BGUIBase = OpenLibrary("PROGDIR:libs/bgui.library",38)) == NULL)
      Error("Cannot open bgui.library v38+.");
  }
  if ((DiskfontBase = OpenLibrary("diskfont.library",36)) == NULL)
    Error("Cannot open diskfont.library v36+.");
  if ((FontReq = AllocAslRequestTags(ASL_FontRequest,
    ASLFO_TitleText,"Open Font",
    ASLFO_MaxHeight,1000,TAG_DONE)) == NULL) Quit();
  if (AslBase->lib_Version > 37)
  {
    if ((ScrReq = AllocAslRequestTags(ASL_ScreenModeRequest,
      ASLSM_TitleText,"Change Screen Mode",
      ASLSM_DoDepth,TRUE,TAG_DONE)) == NULL) Quit();
  }
  if ((CharClass = InitCharGadgClass()) == NULL) Quit();
  if ((EditClass = InitEditGadgClass()) == NULL) Quit();
  if ((SlideClass = InitSlideGadgClass()) == NULL) Quit();
  if (GetNewList(&CharWndList) == NULL) Quit();
  if ((WndMsgPort = CreateMsgPort()) == NULL) Quit();

  if ((defscr = LockPubScreen(NULL)) == NULL) Quit();
  Prefs.ScrModeID = GetVPModeID(&defscr->ViewPort);
  UnlockPubScreen(NULL,defscr);
  LoadPrefs();
  SetupScreen();
  strcpy(FontPath,"FONTS:");
  if (lock = Lock("FONTS:",ACCESS_READ))
  {
    NameFromLock(lock,FontPath,256);
    UnLock(lock);
  }
  strcpy(SavePath,FontPath);
  LoadFont(&topaz8);
}

void SetupScreen(void)
{
UWORD pens[] = { ~0 };
struct IBox box = { 0,0,0,0 };

  if (CustomScreen)
  {
    if ((Screen = OpenScreenTags(NULL,
      SA_Depth,Prefs.ScrDepth,
      SA_DisplayID,Prefs.ScrModeID,
      SA_Pens,pens,
      SA_Title,NAME,
      SA_PubName,NAME,
      SA_AutoScroll,TRUE,
      SA_SysFont,TRUE,
      TAG_DONE)) == NULL) Quit();
  }
  else
  {
    if ((Screen = LockPubScreen(NULL)) == NULL) Quit();
  }
  if ((ScrDrawInfo = GetScreenDrawInfo(Screen)) == NULL) Quit();
  SizeImage = CreateSysImage(SIZEIMAGE,ScrDrawInfo);
  LeftImage = CreateSysImage(LEFTIMAGE,ScrDrawInfo);
  RightImage = CreateSysImage(RIGHTIMAGE,ScrDrawInfo);
  UpImage = CreateSysImage(UPIMAGE,ScrDrawInfo);
  DownImage = CreateSysImage(DOWNIMAGE,ScrDrawInfo);

  box.Top = Screen->BarHeight+1;
  FontWndObj = WindowObject,
    WINDOW_Screen,Screen,
    WINDOW_SharedPort,WndMsgPort,
    WINDOW_MenuStrip,menus,
    WINDOW_Bounds,&box,
    WINDOW_HelpFile,NAME".guide",
    WINDOW_SmartRefresh,TRUE,
    WINDOW_NoBufferRP,TRUE,
    WINDOW_SizeGadget,FALSE,
    WINDOW_MasterGroup,
      HGroupObject,
	HOffset(4),VOffset(2),Spacing(2),
	StartMember,
	  CharObj = ExternalObject,
	    EXT_MinWidth,GetCharGadgWidth()-2,
	    EXT_MinHeight,GetCharGadgHeight()-2,
	    EXT_Class,CharClass,
	    EXT_NoRebuild,TRUE,
	    GA_ID,ID_CHARS,
	  EndObject,
	EndMember,
	StartMember,
	  ScrollObj = PropObject,
	    PGA_Top,0,
	    PGA_Total,4,
	    PGA_Visible,1,
	    PGA_Arrows,TRUE,
	    GA_ID,ID_CHARSCROLL,
	  EndObject,
	EndMember,
      EndObject,
    EndObject;
  if (FontWndObj == NULL) Quit();
  if ((FontWnd = WindowOpen(FontWndObj)) == NULL)
    Error("Cannot open window.");
  OldWindowPtr = ((struct Process *)FindTask(NULL))->pr_WindowPtr;
  ((struct Process *)FindTask(NULL))->pr_WindowPtr = FontWnd;

  GetAttr(WINDOW_Bounds,FontWndObj,&CharSize);
  CharSize.Left += (CharSize.Width + Screen->RastPort.Font->tf_YSize);
  CharSize.Width = Screen->Width - CharSize.Left;
  CharSize.Height = Screen->Height - CharSize.Top;
}

void Quit(void)
{
  QuitScreen();
  RemoveList(&CharWndList,TRUE,NULL);
  ClearCurrentFont();
  if (ClipChar.chr_Data) FreeVec(ClipChar.chr_Data);
  if (NewFont) CloseFont(NewFont);
  if (WndMsgPort) DeleteMsgPort(WndMsgPort);
  if (SlideClass) FreeEditGadgClass(SlideClass);
  if (EditClass) FreeEditGadgClass(EditClass);
  if (CharClass) FreeCharGadgClass(CharClass);
  if (ScrReq) FreeAslRequest(ScrReq);
  if (FontReq) FreeAslRequest(FontReq);
  if (DiskfontBase) CloseLibrary(DiskfontBase);
  if (BGUIBase) CloseLibrary(BGUIBase);
  if (AslBase) CloseLibrary(AslBase);
  exit(0);
}

void QuitScreen(void)
{
  WakeWindows();
  RemoveList(&CharWndList,FALSE,&CloseCharWin);
  if (FontWnd)
    ((struct Process *)FindTask(NULL))->pr_WindowPtr = OldWindowPtr;
  ClrWindowClose(&FontWndObj,&FontWnd);
  ClrDisposeObject(&FontWndObj);
  CloseEditFontWnd(TRUE);
  ClosePrefsWnd(TRUE);
  CloseAssignWnd(TRUE);
  CloseWidthWnd(TRUE);
  CloseKernWnd(TRUE);
  CloseSaveWnd(TRUE);
  ClrDisposeObject(&DirReqObj);
  ClrDisposeObject(&SaveReqObj);
  ClrDisposeObject(&SizeImage);
  ClrDisposeObject(&LeftImage);
  ClrDisposeObject(&RightImage);
  ClrDisposeObject(&UpImage);
  ClrDisposeObject(&DownImage);
  if (ScrDrawInfo)
  {
    FreeScreenDrawInfo(Screen,ScrDrawInfo);
    ScrDrawInfo = NULL;
  }
  if (Screen)
  {
    if (CustomScreen)
      CloseScreen(Screen);
    else
      UnlockPubScreen(NULL,Screen);
    Screen = NULL;
  }
}

void MsgLoop(void)
{
ULONG signal,winsig,code,scroll_pos,pressed,old_height,left,first,last;
LONG temp,normal;
BOOL free_node,redraw = FALSE;
struct CharNode *node,*oldnode;
Object *charobj;
char *fontstr;
BPTR lock;

  GetAttr(WINDOW_SigMask,FontWndObj,&winsig);
  GetAttr(EXT_Object,CharObj,&charobj);
  while (TRUE)
  {
    signal = Wait(winsig|SIGBREAKF_CTRL_C);
    if (signal & SIGBREAKF_CTRL_C) Quit();
    if (signal & winsig)
    {
      while ((code = CheckObjMsg(FontWnd,FontWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	    Quit();
	    break;
	  case ID_CHARSCROLL:
	    GetAttr(PGA_TOP,ScrollObj,&scroll_pos);
	    SetGadgetAttrs(charobj,FontWnd,NULL,CG_Pos,scroll_pos,TAG_DONE);
	    break;
	  case ID_CHARS:
	    GetAttr(CG_Pressed,charobj,&pressed);
	    SetGadgetAttrs(charobj,FontWnd,NULL,CG_Pressed,~0,TAG_DONE);
	    if (pressed != ~0) CheckOpenCharWin(pressed);
	    break;
	  default:
	    SharedMsgs(code,NULL);
	    break;
	}
      }
      while ((code = CheckObjMsg(ParamWnd,ParamWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    CloseEditFontWnd(FALSE);
	    break;
	  case ID_BOLD:
	  case ID_ITALIC:
	  case ID_ULINE:
	  case ID_EXTEND:
	    GetAttr(GA_Selected,BoldCheck,&normal);
	    GetAttr(GA_Selected,ItalicCheck,&temp);
	    normal |= temp;
	    GetAttr(GA_Selected,UnderCheck,&temp);
	    normal |= temp;
	    GetAttr(GA_Selected,ExtCheck,&temp);
	    normal |= temp;
	    SetGadgetAttrs(NormalCheck,ParamWnd,NULL,
	      GA_Selected,normal == 0,TAG_DONE);
	    break;
	  case ID_NORMAL:
	    SetGadgetAttrs(BoldCheck,ParamWnd,NULL,
	      GA_Selected,FALSE,TAG_DONE);
	    SetGadgetAttrs(ItalicCheck,ParamWnd,NULL,
	      GA_Selected,FALSE,TAG_DONE);
	    SetGadgetAttrs(UnderCheck,ParamWnd,NULL,
	      GA_Selected,FALSE,TAG_DONE);
	    SetGadgetAttrs(ExtCheck,ParamWnd,NULL,
	      GA_Selected,FALSE,TAG_DONE);
	    break;
	  case ID_BLANK:
	    CheckOpenCharWin(256);
	    break;
	  case ID_ACCEPT:
	    GetAttr(GA_Selected,NormalCheck,&Normal);
	    GetAttr(GA_Selected,BoldCheck,&Bold);
	    GetAttr(GA_Selected,ItalicCheck,&Italic);
	    GetAttr(GA_Selected,UnderCheck,&ULine);
	    GetAttr(GA_Selected,ExtCheck,&Extended);
	    GetAttr(GA_Selected,RevCheck,&Reversed);
	    GetAttr(CYC_Active,AspectCycle,&Aspect);

	    GetAttr(STRINGA_LongVal,SmearInt,&temp);
	    PutPositive(ParamWnd,"bold smear",temp,0,&Smear);

	    GetAttr(STRINGA_LongVal,HeightInt,&temp);
	    old_height = Height;
	    PutPositive(ParamWnd,"font height",temp,1,&Height);
	    if (old_height != Height)
	    {
	      redraw = TRUE;
	      NewHeights();
	    }

	    GetAttr(STRINGA_LongVal,BaseInt,&temp);
	    if (temp >= (LONG)Height)
	    {
	      if (redraw)
		Baseline = Height-1;
	      else
		ShowReq("The font baseline must be\n"
		  "less than the font height.","Continue");
	    }
	    else PutPositive(ParamWnd,"font baseline",temp,0,&Baseline);

	    GetAttr(CYC_Active,PropCycle,&temp);
	    if (temp != Proportional)
	    {
	      if ((Proportional = temp) == FALSE)
	      {
		redraw = TRUE;
		NewWidths();
	      }
	    }

	    GetAttr(STRINGA_LongVal,WidthInt,&temp);
	    if (temp >= 0)
	    {
	      if (temp != Width)
	      {
		Width = temp;
		if (Proportional == FALSE)
		{
		  redraw = TRUE;
		  NewWidths();
		}
	      }
	    }
	    else ShowReq("The font width must be positive.","Continue");

	    CloseEditFontWnd(FALSE);
	    if (redraw == TRUE)
	    {
	      node = CharWndList->lh_Head;
	      while (node->chln_Node.ln_Succ)
	      {
		RedrawEdit(node);
		node = node->chln_Node.ln_Succ;
	      }
	      redraw = FALSE;
	    }
	    break;
	  default:
	    SharedMsgs(code,NULL);
	    break;
	}
      }
      while ((code = CheckObjMsg(PrefsWnd,PrefsWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    ClosePrefsWnd(FALSE);
	    break;
	  case ID_CUSTOM:
	    GetAttr(GA_Selected,CustButton,&temp);
	    SetGadgetAttrs(ChooseButton,PrefsWnd,NULL,
	      GA_Disabled,(temp == TRUE) ? FALSE : TRUE,TAG_DONE);
	    break;
	  case ID_CHOOSE:
	    SleepWindows();
	    if (AslBase->lib_Version > 37)
	    {
	      if (AslRequestTags(ScrReq,
		ASLSM_Window,FontWnd,
		ASLSM_InitialDisplayID,PrefScrModeID,
		ASLSM_InitialDisplayDepth,PrefScrDepth,TAG_DONE))
	      {
		PrefScrModeID = ScrReq->sm_DisplayID;
		PrefScrDepth = ScrReq->sm_DisplayDepth;
		GetDisplayInfoData(NULL,&ModeIDName,
		  sizeof(struct NameInfo),DTAG_NAME,PrefScrModeID);
		SetGadgetAttrs(ModeInfo,PrefsWnd,NULL,
		  INFO_TextFormat,ModeIDName.Name,TAG_DONE);
	      }
	    }
	    else ShowReq("asl.library v38 or higher required.","Cancel");
	    WakeWindows();
	    break;
	  case ID_ACCEPT:
	  case ID_USE:
	    NewCustom = CustomScreen;
	    GetAttr(GA_Selected,CustButton,&temp);
	    if (temp != CustomScreen)
	    {
	      ChangeScreen = TRUE;
	      NewCustom = (CustomScreen == TRUE) ? FALSE : TRUE;
	    }
	    if (temp == TRUE)
	    {
	      if ((PrefScrModeID != Prefs.ScrModeID) ||
		(PrefScrDepth != Prefs.ScrDepth))
	      {
		ChangeScreen = TRUE;
		NewCustom = TRUE;
	      }
	    }
	    Prefs.ScrModeID = PrefScrModeID;
	    Prefs.ScrDepth = PrefScrDepth;

	    GetAttr(STRINGA_LongVal,VecInt,&temp);
	    PutPositive(PrefsWnd,"tool bar height",temp,10,
	      &(Prefs.VecHeight));
	    GetAttr(STRINGA_LongVal,PixelXInt,&temp);
	    PutPositive(PrefsWnd,"pixel width",temp,1,&(Prefs.PixelX));
	    GetAttr(STRINGA_LongVal,PixelYInt,&temp);
	    PutPositive(PrefsWnd,"pixel height",temp,1,&(Prefs.PixelY));
	    GetAttr(GA_Selected,PixelCheck,&PixelBorder);

	    GetAttr(CYC_Active,SaveCycle,&(Prefs.SaveTo));
	    if (code == ID_ACCEPT)
	    {
	      if (Prefs.SaveTo == 0)
	      {
		SavePrefs("ENV:"NAME".prefs");
		SavePrefs("ENVARC:"NAME".prefs");
	      }
	      else SavePrefs("PROGDIR:"NAME".prefs");
	    }
	    ClosePrefsWnd(FALSE);
	    break;
	  default:
	    SharedMsgs(code,NULL);
	    break;
	}
      }
      while ((code = CheckObjMsg(AssignWnd,AssignWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    CloseAssignWnd(FALSE);
	    break;
	  case ID_ASSIGN:
	  case ID_ASSIGNADD:
	    GetAttr(STRINGA_TextVal,FontDirStr,&fontstr);
	    strcpy(FontPath,fontstr);
	    SleepWindows();
	    if ((lock = Lock(FontPath,ACCESS_READ)) != 0)
	    {
	      if (code == ID_ASSIGNADD)
		AssignAdd("FONTS",lock);
	      else
		AssignLock("FONTS",lock);
	      strcpy(SavePath,FontPath);
	      FlushAllFonts();
	    }
	    else ShowReq("Cannot find directory\n\"%s\"","Cancel",FontPath);
	    WakeWindows();
	    CloseAssignWnd(FALSE);
	    break;
	  case ID_ASSIGNPATH:
	    SleepWindows();
	    GetAttr(STRINGA_TextVal,FontDirStr,&fontstr);
	    SetAttrs(DirReqObj,ASLFR_InitialDrawer,fontstr,TAG_DONE);
	    if (DoRequest(DirReqObj) == FRQ_OK)
	    {
	      GetAttr(FRQ_Drawer,DirReqObj,&fontstr);
	      SetGadgetAttrs(FontDirStr,AssignWnd,NULL,
		STRINGA_TextVal,fontstr,TAG_DONE);
	    }
	    WakeWindows();
	    break;
	  default:
	    SharedMsgs(code,NULL);
	    break;
	}
      }
      while ((code = CheckObjMsg(WidthWnd,WidthWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    CloseWidthWnd(FALSE);
	    break;
	  case ID_ACCEPT:
	    GetAttr(STRINGA_LongVal,ChrWidthInt,&temp);
	    GetAttr(PGA_Top,ChrWidthScroll,&left);
	    if (temp >= 0)
	    {
	      if (temp > WidthChar->chr_Width)
		ChangeCharWidth(WidthChar,temp,left,0);
	      else
		ChangeCharWidth(WidthChar,temp,0,left);
	      RedrawEdit(WidthNode);
	    }
	    else ShowReq("The font width must be positive.","Continue");
	    CloseWidthWnd(FALSE);
	    break;
	  case ID_CHARWIDTH:
	    GetAttr(STRINGA_LongVal,ChrWidthInt,&temp);
	    if (temp >= 0)
	    {
	      SetGadgetAttrs(ChrWidthScroll,WidthWnd,NULL,
		PGA_Total,MAX(WidthChar->chr_Width,temp),
		PGA_Visible,MIN(WidthChar->chr_Width,temp),TAG_DONE);
	    }
	    else
	    {
	      ShowReq("The font width must be positive.","Continue");
	      SetGadgetAttrs(ChrWidthInt,WidthWnd,NULL,
		STRINGA_LongVal,WidthChar->chr_Width,TAG_DONE);
	    }
	    break;
	  default:
	    SharedMsgs(code,WidthNode);
	    break;
	}
      }
      while ((code = CheckObjMsg(KernWnd,KernWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    CloseKernWnd(FALSE);
	    break;
	  case ID_ACCEPT:
	    GetAttr(STRINGA_LongVal,SpaceObj,&temp);
	    SpaceTable[KernNode->chln_Number] = temp;
	    GetAttr(STRINGA_LongVal,KernObj,&temp);
	    KernTable[KernNode->chln_Number] = temp;
	    CloseKernWnd(FALSE);
	    break;
	  default:
	    SharedMsgs(code,KernNode);
	    break;
	}
      }
      while ((code = CheckObjMsg(SaveWnd,SaveWndObj)) != WMHI_NOMORE)
      {
	switch (code)
	{
	  case WMHI_CLOSEWINDOW:
	  case ID_CANCEL:
	    CloseSaveWnd(FALSE);
	    break;
	  case ID_ACCEPT:
	    GetAttr(STRINGA_LongVal,FirstNum,&first);
	    if ((first < 0) || (first > 255)) SaveShowReq("The first "
	      "character must be between 0 and 255.","Cancel");
	    else
	    {
	      GetAttr(STRINGA_LongVal,LastNum,&last);
	      if ((last < 0) || (last > 255)) SaveShowReq("The first "
		"character must be between 0 and 255.","Cancel");
	      else
	      {
		if (first > last) SaveShowReq("The first character "
		  "cannot be greater than the last.","Cancel");
		else
		{
		  FirstChar = first;
		  LastChar = last;
		  GetAttr(STRINGA_TextVal,SaveDirStr,&fontstr);
		  strcpy(SavePath,fontstr);
		  GetAttr(STRINGA_TextVal,FontNameStr,&fontstr);
		  strcpy(FontName,fontstr);
		  GetAttr(GA_Selected,TableCheck,&temp);
		  SaveFont(temp);
		  sprintf(WinTitle,"%s/%ld",FontName,Height);
		  SetAttrs(FontWndObj,WINDOW_Title,WinTitle,TAG_DONE);
		}
	      }
	    }
	    CloseSaveWnd(FALSE);
	    break;
	  case ID_GETSAVEDIR:
	    GetAttr(STRINGA_TextVal,SaveDirStr,&fontstr);
	    SetAttrs(SaveReqObj,
	      ASLFR_Window,SaveWnd,
	      ASLFR_InitialDrawer,fontstr,TAG_DONE);
	    WindowBusy(SaveWndObj);
	    if (DoRequest(SaveReqObj) == FRQ_OK)
	    {
	      GetAttr(FRQ_Drawer,SaveReqObj,&fontstr);
	      SetGadgetAttrs(SaveDirStr,SaveWnd,NULL,
		STRINGA_TextVal,fontstr,TAG_DONE);
	    }
	    WindowReady(SaveWndObj);
	    break;
	}
      }
      node = CharWndList->lh_Head;
      while (node->chln_Node.ln_Succ)
      {
	free_node = FALSE;
	while ((code =
	  CheckObjMsg(node->chln_Window,node->chln_Object)) != WMHI_NOMORE)
	{
	  switch (code)
	  {
	    case WMHI_CLOSEWINDOW:
	      CloseCharWin(node);
	      free_node = TRUE;
	      break;
	    default:
	      SharedMsgs(code,node);
	      break;
	  }
	}
	oldnode = node;
	node = node->chln_Node.ln_Succ;
	if (free_node)
	{
	  Remove(oldnode);
	  FreeVec(oldnode);
	}
      }
    }
    if (ChangeScreen)
    {
      QuitScreen();
      CustomScreen = NewCustom;
      SetupScreen();
      SetAttrs(FontWndObj,WINDOW_Title,WinTitle,TAG_DONE);
      GetAttr(WINDOW_SigMask,FontWndObj,&winsig);
      GetAttr(EXT_Object,CharObj,&charobj);
      ChangeScreen = FALSE;
    }
  }
}

ULONG CheckObjMsg(struct Window *wnd, Object *obj)
{
  return ((wnd != NULL) ? HandleEvent(obj) : WMHI_NOMORE);
}

void CheckOpenCharWin(ULONG pressed)
{
struct CharNode *node;
BOOL open = FALSE;

  node = CharWndList->lh_Head;
  while (node->chln_Node.ln_Succ)
  {
    if (pressed == node->chln_Number)
    {
      open = TRUE;
      WindowToFront(node->chln_Window);
      ActivateWindow(node->chln_Window);
    }
    node = node->chln_Node.ln_Succ;
  }
  if (open == FALSE) OpenCharWin(pressed,CharSize);
}

void SharedMsgs(ULONG code,struct CharNode *node)
{
  switch (code)
  {
    case ID_OPEN:
      SleepWindows();
      if (AslRequestTags(FontReq,ASLFO_Window,FontWnd,TAG_DONE))
      {
	LoadFont(&FontReq->fo_Attr);
	WakeWindows();
	if (ParamWnd)
	{
	  SetGadgetAttrs(PropCycle,ParamWnd,NULL,
	    CYC_Active,Proportional,TAG_DONE);
	  SetGadgetAttrs(HeightInt,ParamWnd,NULL,
	    STRINGA_LongVal,Height,TAG_DONE);
	  SetGadgetAttrs(WidthInt,ParamWnd,NULL,
	    STRINGA_LongVal,Width,TAG_DONE);
	  SetGadgetAttrs(BaseInt,ParamWnd,NULL,
	    STRINGA_LongVal,Baseline,TAG_DONE);
	  SetGadgetAttrs(SmearInt,ParamWnd,NULL,
	    STRINGA_LongVal,Smear,TAG_DONE);
	  SetGadgetAttrs(NormalCheck,ParamWnd,NULL,
	    GA_Selected,Normal,TAG_DONE);
	  SetGadgetAttrs(BoldCheck,ParamWnd,NULL,
	    GA_Selected,Bold,TAG_DONE);
	  SetGadgetAttrs(ItalicCheck,ParamWnd,NULL,
	    GA_Selected,Italic,TAG_DONE);
	  SetGadgetAttrs(UnderCheck,ParamWnd,NULL,
	    GA_Selected,ULine,TAG_DONE);
	  SetGadgetAttrs(ExtCheck,ParamWnd,NULL,
	    GA_Selected,Extended,TAG_DONE);
	  SetGadgetAttrs(RevCheck,ParamWnd,NULL,
	    GA_Selected,Reversed,TAG_DONE);
	  SetGadgetAttrs(AspectCycle,ParamWnd,NULL,
	    CYC_Active,Aspect,TAG_DONE);
	}
	node = CharWndList->lh_Head;
	while (node->chln_Node.ln_Succ)
	{
	  RedrawEdit(node);
	  CheckWidthKern(node->chln_Character);
	  node = node->chln_Node.ln_Succ;
	}
      }
      else WakeWindows();
      break;
    case ID_SAVE:
      OpenSaveWnd();
      break;
    case ID_CHANGEDIR:
      OpenAssignWnd();
      break;
    case ID_EDITFONT:
      OpenEditFontWnd();
      break;
    case ID_PREFS:
      OpenPrefsWnd();
      break;
    case ID_ABOUT:
      ShowReq(NAME" v1.0\n© 1995 David Kinder","Continue");
      break;
    case ID_QUIT:
      Quit();
      break;
    case ID_WIDTHL:
      if (node)
      {
	if (node->chln_Character->chr_Width > 0)
	{
	  ChangeCharWidth(node->chln_Character,
	    node->chln_Character->chr_Width-1,0,0);
	  RedrawEdit(node);
	}
      }
      break;
    case ID_WIDTHR:
      if (node)
      {
	ChangeCharWidth(node->chln_Character,
	  node->chln_Character->chr_Width+1,0,0);
	  RedrawEdit(node);
      }
      break;
    case ID_WIDTHC:
      if (node) OpenWidthWnd(node);
      break;
    case ID_KERNING:
      if (node) OpenKernWnd(node);
      break;
    case ID_MOVEL:
      if (node) MoveLeft(node);
      break;
    case ID_MOVER:
      if (node) MoveRight(node);
      break;
    case ID_MOVEU:
      if (node) MoveUp(node);
      break;
    case ID_MOVED:
      if (node) MoveDown(node);
      break;
    case ID_COPY:
      if (node) CopyChar(node);
      break;
    case ID_PASTE:
      if (node) PasteChar(node);
      break;
  }
}

void Error(char *message)
{
  ShowReq(message,"Cancel");
  Quit();
}

void ShowReq(char *text,char *gadgets,...)
{
va_list va;
struct EasyStruct req =
  { sizeof(struct EasyStruct),0,NAME,NULL,NULL };

  SleepWindows();
  req.es_TextFormat = text;
  req.es_GadgetFormat = gadgets;
  va_start(va,gadgets);
  EasyRequestArgs(FontWnd,&req,NULL,va);
  va_end(va);
  WakeWindows();
}

void ClrDisposeObject(Object **obj)
{
  if (*obj)
  {
    DisposeObject(*obj);
    *obj = NULL;
  }
}

void ClrWindowClose(Object **obj,struct Window **wnd)
{
  if (*wnd)
  {
    WindowClose(*obj);
    *wnd = NULL;
  }
}

void SleepWindows(void)
{
struct CharNode *node;

  if (FontWnd)
  {
    WindowBusy(FontWndObj);
    if (ParamWnd) WindowBusy(ParamWndObj);
    if (PrefsWnd) WindowBusy(PrefsWndObj);
    if (AssignWnd) WindowBusy(AssignWndObj);
    if (WidthWnd) WindowBusy(WidthWndObj);
    if (KernWnd) WindowBusy(KernWndObj);
    node = CharWndList->lh_Head;
    while (node->chln_Node.ln_Succ)
    {
      WindowBusy(node->chln_Object);
      node = node->chln_Node.ln_Succ;
    }
  }
}

void WakeWindows(void)
{
struct CharNode *node;

  if (FontWnd)
  {
    WindowReady(FontWndObj);
    if (ParamWnd) WindowReady(ParamWndObj);
    if (PrefsWnd) WindowReady(PrefsWndObj);
    if (AssignWnd) WindowReady(AssignWndObj);
    if (WidthWnd) WindowReady(WidthWndObj);
    if (KernWnd) WindowReady(KernWndObj);
    node = CharWndList->lh_Head;
    while (node->chln_Node.ln_Succ)
    {
      WindowReady(node->chln_Object);
      node = node->chln_Node.ln_Succ;
    }
  }
}

void LoadFont(struct TextAttr *font)
{
ULONG i;
char *endptr;

  strcpy(FontName,font->ta_Name);
  if (endptr = strstr(FontName,".font")) *endptr = 0;
  if ((NewFont = OpenFont(font)) == NULL)
  {
    if ((NewFont = OpenDiskFont(font)) == NULL)
    {
      ShowReq("Cannot open %s.","Continue",FontName,(ULONG)font->ta_YSize);
      return;
    }
  }
  ClearCurrentFont();
  Height = NewFont->tf_YSize;
  Width = NewFont->tf_XSize;
  Baseline = NewFont->tf_Baseline;
  Smear = NewFont->tf_BoldSmear;
  FirstChar = NewFont->tf_LoChar;
  LastChar = NewFont->tf_HiChar;
  Proportional = ((NewFont->tf_Flags & FPF_PROPORTIONAL) != 0);
  Bold = ((NewFont->tf_Style & FSF_BOLD) != 0);
  Italic = ((NewFont->tf_Style & FSF_ITALIC) != 0);
  ULine = ((NewFont->tf_Style & FSF_UNDERLINED) != 0);
  Extended = ((NewFont->tf_Style & FSF_EXTENDED) != 0);
  Normal = ((Bold | Italic | ULine | Extended) == 0);
  Reversed = ((NewFont->tf_Flags & FPF_REVPATH) != 0);
  Aspect = 0;
  if (NewFont->tf_Flags & FPF_TALLDOT) Aspect = 1;
  if (NewFont->tf_Flags & FPF_WIDEDOT) Aspect = 2;

  for (i = 0; i < 257; i++)
  {
    (CharBuffer+i)->chr_Width = NewFont->tf_XSize;
    (CharBuffer+i)->chr_Height = NewFont->tf_YSize;
    SpaceTable[i] = NewFont->tf_XSize;
    KernTable[i] = 0;
  }
  for (i = 0; i < LastChar-FirstChar+1; i++)
  {
    if (!UnpackChar(CharBuffer+FirstChar+i,NewFont,i)) Quit();
    KernTables(FirstChar+i,i);
  }
  UnpackChar(CharBuffer+256,NewFont,LastChar-FirstChar+1);
  KernTables(256,LastChar-FirstChar+1);
  sprintf(WinTitle,"%s/%ld",FontName,(ULONG)font->ta_YSize);
  SetAttrs(FontWndObj,WINDOW_Title,WinTitle,TAG_DONE);
  CloseFont(NewFont); NewFont = NULL;
}

BOOL UnpackChar(struct Character *chr,struct TextFont *font,ULONG i)
{
struct charDef *def;
ULONG j,k,mod,off,width,modj,widthj;
UBYTE *data;

  mod = font->tf_Modulo;
  data = (UBYTE *)font->tf_CharData;
  def = ((struct charDef *)(font->tf_CharLoc))+i;
  off = def->charOffset;
  chr->chr_Width = def->charBitWidth;
  if ((width = chr->chr_Width) > 0)
  {
    if ((chr->chr_Data = AllocVec(chr->chr_Width*chr->chr_Height,MEMF_ANY))
      == NULL) return (FALSE);
    for (j = 0; j < chr->chr_Height; j++)
    {
      modj = mod*j;
      widthj = width*j;
      for (k = 0; k < width; k++)
        *(chr->chr_Data+widthj+k) = GETBIT(data+modj,off+k);
    }
  }
  return (TRUE);
}

void KernTables(ULONG to, ULONG from)
{
  if (NewFont->tf_CharSpace)
    SpaceTable[to] = *((WORD *)(NewFont->tf_CharSpace)+from);
  if (NewFont->tf_CharKern)
    KernTable[to] = *((WORD *)(NewFont->tf_CharKern)+from);
  if ((NewFont->tf_CharSpace) || (NewFont->tf_CharKern))
    UseTable = TRUE;
  else
    UseTable = FALSE;
}

void ClearCurrentFont(void)
{
struct Character *chr;

  for (chr = CharBuffer; chr < CharBuffer+257; chr++)
  {
    if (chr->chr_Data) FreeVec(chr->chr_Data);
    chr->chr_Data = NULL;
  }
}

void OpenEditFontWnd(void)
{
static STRPTR prop_labels[] = { "Fixed Width","Proportional",NULL };
static STRPTR aspect_labels[] = { "Normal","Thin","Wide",NULL };
Object *accept, *cancel;

  if (ParamWnd)
  {
    WindowToFront(ParamWnd);
    ActivateWindow(ParamWnd);
  }
  else
  {
    if (ParamWndObj == NULL)
    {
      ParamWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_MenuStrip,menus,
	WINDOW_Title,"Edit Font Parameters",
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"params",
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),Spacing(4),
	    GROUP_BackFill,SHINE_RASTER,
	    StartMember,
	      VGroupObject,
		HOffset(8),VOffset(4),Spacing(2),
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  PropCycle =
		    KeyCycle("_Font Type",prop_labels,Proportional,ID_PROP),
		EndMember,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      VGroupObject,
		      Spacing(2),
			StartMember,
			  HeightInt = TabKeyInteger("_Height",Height,
			    3,ID_HEIGHT),
			EndMember,
			StartMember,
			  BaseInt = TabKeyInteger("_Baseline",Baseline,
			    3,ID_BASELINE),
			EndMember,
		      EndObject,
		    EndMember,
		    StartMember,
		      VGroupObject,
			Spacing(2),
			StartMember,
			  WidthInt = TabKeyInteger("_Width",Width,
			    3,ID_WIDTH),
			EndMember,
			StartMember,
			  SmearInt = TabKeyInteger("Bold _Smear",Smear,
			    3,ID_SMEAR),
			EndMember,
		      EndObject,
		    EndMember,
		  EndObject,
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      VGroupObject,
		HOffset(8),VOffset(4),Spacing(2),
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      VGroupObject,
			Spacing(2),
			StartMember,
			  NormalCheck =
			    KeyCheckBox("_Normal",Normal,ID_NORMAL),
			EndMember,
			StartMember,
			  ItalicCheck =
			    KeyCheckBox("_Italic",Italic,ID_ITALIC),
			EndMember,
		      EndObject,
		    EndMember,
		    StartMember,
		      VGroupObject,
			Spacing(2),
			StartMember,
			  BoldCheck = KeyCheckBox("B_old",Bold,ID_BOLD),
			EndMember,
			StartMember,
			  UnderCheck =
			    KeyCheckBox("_Underline",ULine,ID_ULINE),
			EndMember,
		      EndObject,
		    EndMember,
		    StartMember,
		      VGroupObject,
			Spacing(2),
			StartMember,
			  ExtCheck =
			    KeyCheckBox("_Extended",Extended,ID_EXTEND),
			EndMember,
			StartMember,
			  RevCheck =
			    KeyCheckBox("_Reversed",Reversed,ID_REVERSE),
			EndMember,
		      EndObject,
		    EndMember,
		  EndObject,
		EndMember,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      AspectCycle = KeyPopCycle("As_pect",
			aspect_labels,Aspect,ID_ASPECT),
		    EndMember,
		    StartMember,
		      BlankButton = KeyButton("B_lank",ID_BLANK),
		      Weight(20),
		    EndMember,
		  EndObject,
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		Spacing(8),
		EqualWidth,
		StartMember,
		  accept = KeyButton("_Accept",ID_ACCEPT),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (ParamWndObj == NULL) Quit();
      GadgetKey(ParamWndObj,accept,"a");
      GadgetKey(ParamWndObj,cancel,"c");
      GadgetKey(ParamWndObj,PropCycle,"f");
      GadgetKey(ParamWndObj,HeightInt,"h");
      GadgetKey(ParamWndObj,WidthInt,"w");
      GadgetKey(ParamWndObj,BaseInt,"b");
      GadgetKey(ParamWndObj,SmearInt,"s");
      GadgetKey(ParamWndObj,NormalCheck,"n");
      GadgetKey(ParamWndObj,BoldCheck,"o");
      GadgetKey(ParamWndObj,ItalicCheck,"i");
      GadgetKey(ParamWndObj,UnderCheck,"u");
      GadgetKey(ParamWndObj,ExtCheck,"e");
      GadgetKey(ParamWndObj,RevCheck,"r");
      GadgetKey(ParamWndObj,AspectCycle,"p");
      GadgetKey(ParamWndObj,BlankButton,"l");
      DoMethod(ParamWndObj,WM_TABCYCLE_ORDER,
	HeightInt,WidthInt,BaseInt,SmearInt,NULL);
    }

    SetAttrs(PropCycle,CYC_Active,Proportional,TAG_DONE);
    SetAttrs(HeightInt,STRINGA_LongVal,Height,TAG_DONE);
    SetAttrs(BaseInt,STRINGA_LongVal,Baseline,TAG_DONE);
    SetAttrs(WidthInt,STRINGA_LongVal,Width,TAG_DONE);
    SetAttrs(SmearInt,STRINGA_LongVal,Smear,TAG_DONE);
    SetAttrs(NormalCheck,GA_Selected,Normal,TAG_DONE);
    SetAttrs(ItalicCheck,GA_Selected,Italic,TAG_DONE);
    SetAttrs(BoldCheck,GA_Selected,Bold,TAG_DONE);
    SetAttrs(UnderCheck,GA_Selected,ULine,TAG_DONE);
    SetAttrs(ExtCheck,GA_Selected,Extended,TAG_DONE);
    SetAttrs(RevCheck,GA_Selected,Reversed,TAG_DONE);
    SetAttrs(AspectCycle,CYC_Active,Aspect,TAG_DONE);
    if ((ParamWnd = WindowOpen(ParamWndObj)) == NULL) Quit();
  }
}

void CloseEditFontWnd(BOOL obj)
{
  ClrWindowClose(&ParamWndObj,&ParamWnd);
  if (obj) ClrDisposeObject(&ParamWndObj);
}

void OpenPrefsWnd(void)
{
Object *save, *use, *cancel, *select, *page;
static UBYTE *pages[] = { "Screen","Edit","Misc",NULL };
static UBYTE *save_labels[] = { "ENV:","PROGDIR:",NULL };
static ULONG map[] = { MX_Active,PAGE_Active,TAG_END };

  if (PrefsWnd)
  {
    WindowToFront(PrefsWnd);
    ActivateWindow(PrefsWnd);
  }
  else
  {
    if (PrefsWndObj == NULL)
    {
      PrefsWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_MenuStrip,menus,
	WINDOW_Title,"Preferences",
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"prefs",
	WINDOW_ScaleWidth,15,
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),
	    GROUP_BackFill,SHINE_RASTER,
            StartMember,
	      select = MxObject,
		MX_TabsObject,TRUE,
		MX_Labels,pages,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	    StartMember,
	      page = PageObject,

		PageMember,
		  VGroupObject,
		    HOffset(8),VOffset(4),
		    ButtonFrame,
		    FRM_Recessed,TRUE,
		    VarSpace(50),
		    StartMember,
		      HGroupObject,
			VarSpace(50),
			StartMember,
			  CustButton = KeyCheckBox("Cus_tom Screen",
			    CustomScreen,ID_CUSTOM),
			EndMember,
			VarSpace(50),
		      EndObject,
		    EndMember,
		    StartMember,
		      HGroupObject,
			TOffset(6),
			VarSpace(50),
			StartMember,
			  InfoObject,
			    INFO_TextFormat,"Screen Type",
			    INFO_MinLines,1,
			    INFO_HorizOffset,0,
			    INFO_VertOffset,2,
			    INFO_FixTextWidth,TRUE,
			  EndObject,
			EndMember,
			VarSpace(50),
		      EndObject,
		    EndMember,
		    StartMember,
		      HGroupObject,
			FixMinHeight,
			EqualHeight,
			StartMember,
			  ModeInfo = InfoObject,
			    ButtonFrame,
			    FRM_Flags,FRF_RECESSED,
			    INFO_MinLines,1,
			    INFO_HorizOffset,6,
			    INFO_VertOffset,3,
			  EndObject,
			  Weight(70),
			EndMember,
			StartMember,
			  ChooseButton = ButtonObject,
			    Label("Choose"),
			    ButtonFrame,
			    GA_ID,ID_CHOOSE,
			  EndObject,
			  Weight(30),
			EndMember,
		      EndObject,
		    EndMember,
		    VarSpace(50),
		  EndObject,

		PageMember,
		  HGroupObject,
		    HOffset(8),VOffset(4),
		    ButtonFrame,
		    FRM_Recessed,TRUE,
		    VarSpace(5),
		    StartMember,
		      VGroupObject,
			Spacing(2),
			StartMember,
			  PixelXInt = TabKeyInteger("Pixel _Width",
			    Prefs.PixelX,3,ID_PIXELWIDTH),
			EndMember,
			StartMember,
			  PixelYInt = TabKeyInteger("Pixel _Height",
			    Prefs.PixelY,3,ID_PIXELHEIGHT),
			EndMember,
			StartMember,
			  VecInt = TabKeyInteger("_Tool Bar Height",
			    Prefs.VecHeight,3,ID_VECHEIGHT),
			EndMember,
			StartMember,
			  PixelCheck = KeyCheckBox("Pixel _Border",
			    PixelBorder,ID_PIXELBORDER),
			EndMember,
		      EndObject,
		    EndMember,
		    VarSpace(5),
		  EndObject,

		PageMember,
		  VGroupObject,
		    HOffset(8),VOffset(4),Spacing(2),
		    ButtonFrame,
		    FRM_Recessed,TRUE,
		    VarSpace(50),
		    StartMember,
		      SaveCycle = CycleObject,
			UScoreLabel("Sa_ve preferences file to",'_'),
			Place(PLACE_ABOVE),
			ButtonFrame,
			CYC_Labels,save_labels,
			CYC_Active,Prefs.SaveTo,
			CYC_Popup,TRUE,
			GA_ID,ID_SAVETO,
		      EndObject,
		    EndMember,
		    VarSpace(50),
		  EndObject,

	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		TOffset(4),Spacing(8),
		EqualWidth,
		StartMember,
		  save = KeyButton("_Save",ID_ACCEPT),
		EndMember,
		StartMember,
		  use = KeyButton("_Use",ID_USE),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (PrefsWndObj == NULL) Quit();
      GadgetKey(PrefsWndObj,save,"s");
      GadgetKey(PrefsWndObj,use,"u");
      GadgetKey(PrefsWndObj,cancel,"c");
      GadgetKey(PrefsWndObj,VecInt,"t");
      GadgetKey(PrefsWndObj,PixelXInt,"w");
      GadgetKey(PrefsWndObj,PixelYInt,"h");
      GadgetKey(PrefsWndObj,PixelCheck,"b");
      GadgetKey(PrefsWndObj,CustButton,"t");
      GadgetKey(PrefsWndObj,SaveCycle,"v");
      AddMap(select,page,map);
      DoMethod(PrefsWndObj,WM_TABCYCLE_ORDER,
	VecInt,PixelXInt,PixelYInt,NULL);
    }

    SetAttrs(CustButton,GA_Selected,CustomScreen,TAG_DONE);
    SetAttrs(ChooseButton,
      GA_Disabled,(CustomScreen == TRUE) ? FALSE : TRUE,TAG_DONE);
    PrefScrModeID = Prefs.ScrModeID;
    PrefScrDepth = Prefs.ScrDepth;
    GetDisplayInfoData(NULL,&ModeIDName,sizeof(struct NameInfo),
      DTAG_NAME,PrefScrModeID);
    SetAttrs(ModeInfo,INFO_TextFormat,ModeIDName.Name,TAG_DONE);

    SetAttrs(VecInt,STRINGA_LongVal,Prefs.VecHeight,TAG_DONE);
    SetAttrs(PixelXInt,STRINGA_LongVal,Prefs.PixelX,TAG_DONE);
    SetAttrs(PixelYInt,STRINGA_LongVal,Prefs.PixelY,TAG_DONE);
    SetAttrs(PixelCheck,GA_Selected,PixelBorder,TAG_DONE);

    SetAttrs(SaveCycle,CYC_Active,Prefs.SaveTo,TAG_DONE);
    if ((PrefsWnd = WindowOpen(PrefsWndObj)) == NULL) Quit();
  }
}

void ClosePrefsWnd(BOOL obj)
{
  ClrWindowClose(&PrefsWndObj,&PrefsWnd);
  if (obj) ClrDisposeObject(&PrefsWndObj);
}

void OpenAssignWnd(void)
{
Object *assign, *assignadd, *cancel;

  if (AssignWnd)
  {
    WindowToFront(AssignWnd);
    ActivateWindow(AssignWnd);
  }
  else
  {
    if (AssignWndObj == NULL)
    {
      AssignWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_MenuStrip,menus,
	WINDOW_Title,"Change Font Directory",
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"dir",
	WINDOW_ScaleWidth,26,
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),Spacing(4),
	    GROUP_BackFill,SHINE_RASTER,
	    StartMember,
	      HGroupObject,
		HOffset(8),VOffset(4),
		EqualHeight,
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  FontDirStr = StringObject,
		    RidgeFrame,
		    STRINGA_TextVal,FontPath,
		    STRINGA_MaxChars,255,
		    GA_ID,ID_ASSIGNSTR,
		  EndObject,
		EndMember,
		StartMember,
		  ButtonObject,
		    GetPath,
		    ButtonFrame,
		    GA_ID,ID_ASSIGNPATH,
		  EndObject,
		  FixMinWidth,
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		Spacing(8),
		StartMember,
		  assign = KeyButton("_Assign",ID_ASSIGN),
		EndMember,
        	StartMember,
		  assignadd = KeyButton("Assign A_dd",ID_ASSIGNADD),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (AssignWndObj == NULL) Quit();
      GadgetKey(AssignWndObj,assign,"a");
      GadgetKey(AssignWndObj,assignadd,"d");
      GadgetKey(AssignWndObj,cancel,"c");

      DirReqObj = FileReqObject,
	ASLFR_TitleText,"Change Font Directory",
	ASLFR_DrawersOnly,TRUE,
	ASLFR_Window,FontWnd,
      EndObject;
      if (DirReqObj == NULL) Quit();
    }

    SetAttrs(FontDirStr,STRINGA_TextVal,FontPath,TAG_DONE);
    if ((AssignWnd = WindowOpen(AssignWndObj)) == NULL) Quit();
  }
}

void CloseAssignWnd(BOOL obj)
{
  ClrWindowClose(&AssignWndObj,&AssignWnd);
  if (obj) ClrDisposeObject(&AssignWndObj);
}

void OpenWidthWnd(struct CharNode *node)
{
Object *accept, *cancel;
static struct NewMenu menus[] = { PROJECT_MENU,CHARACTER_MENU,End };
static ULONG lmap[] = { PGA_Top,INDIC_Level,TAG_END };
static ULONG rmap[] = { SCRL_Right,INDIC_Level,TAG_END };

  if (node->chln_Number < 256)
    sprintf(WidthTitle,"Change Width of %lu (from %d)",
      node->chln_Number,node->chln_Character->chr_Width);
  else
    sprintf(WidthTitle,"Change Width of blank (from %d)",
      node->chln_Character->chr_Width);
  WidthNode = node;
  WidthChar = node->chln_Character;

  if (WidthWnd)
  {
    SetGadgetAttrs(ChrWidthScroll,WidthWnd,NULL,
      PGA_Top,0,
      PGA_Total,1,
      PGA_Visible,1,TAG_DONE);
    SetGadgetAttrs(ChrWidthInt,WidthWnd,NULL,
      STRINGA_LongVal,WidthChar->chr_Width,TAG_DONE);
    SetAttrs(WidthWndObj,WINDOW_Title,WidthTitle,TAG_DONE);
    WindowToFront(WidthWnd);
    ActivateWindow(WidthWnd);
  }
  else
  {
    if (WidthWndObj == NULL)
    {
      WidthWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_MenuStrip,menus,
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"width",
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),Spacing(4),
	    GROUP_BackFill,SHINE_RASTER,
	    StartMember,
	      VGroupObject,
		HOffset(8),VOffset(4),Spacing(2),
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  ChrWidthInt = KeyInteger("New _Width",
		    WidthChar->chr_Width,3,ID_CHARWIDTH),
		EndMember,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      LeftInd = IndicatorObject,
			INDIC_Min,0,
			INDIC_Max,999,
			INDIC_FormatString,"Left Offset: %ld",
		      EndObject,
		    EndMember,
		    StartMember,
		      RightInd = IndicatorObject,
			INDIC_Min,0,
			INDIC_Max,999,
			INDIC_FormatString,"Right Offset: %ld",
		      EndObject,
		    EndMember,
		  EndObject,
		EndMember,
		StartMember,
		  ChrWidthScroll = NewObject(SlideClass,NULL,
		    PGA_Freedom,FREEHORIZ,
		    PGA_Arrows,FALSE,
		    GA_ID,ID_WIDTHPOS,
		  EndObject,
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		Spacing(8),
		StartMember,
		  accept = KeyButton("_Accept",ID_ACCEPT),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (WidthWndObj == NULL) Quit();
      GadgetKey(WidthWndObj,accept,"a");
      GadgetKey(WidthWndObj,cancel,"c");
      GadgetKey(WidthWndObj,ChrWidthInt,"w");
      AddMap(ChrWidthScroll,LeftInd,lmap);
      AddMap(ChrWidthScroll,RightInd,rmap);
    }

    SetAttrs(ChrWidthScroll,
      PGA_Top,0,
      PGA_Total,1,
      PGA_Visible,1,TAG_DONE);
    SetAttrs(ChrWidthInt,STRINGA_LongVal,WidthChar->chr_Width,TAG_DONE);
    SetAttrs(WidthWndObj,WINDOW_Title,WidthTitle,TAG_DONE);
    if ((WidthWnd = WindowOpen(WidthWndObj)) == NULL) Quit();
  }
}

void CloseWidthWnd(BOOL obj)
{
  ClrWindowClose(&WidthWndObj,&WidthWnd);
  if (obj) ClrDisposeObject(&WidthWndObj);
  WidthNode = NULL; WidthChar = NULL;
}

void OpenKernWnd(struct CharNode *node)
{
Object *accept, *cancel;
static struct NewMenu menus[] = { PROJECT_MENU,CHARACTER_MENU,End };

  if (node->chln_Number < 256)
    sprintf(KernTitle,"Kerning for %lu",node->chln_Number);
  else
    sprintf(KernTitle,"Kerning for blank");
  KernNode = node;
  KernChar = node->chln_Character;

  if (KernWnd)
  {
    SetAttrs(KernWndObj,WINDOW_Title,KernTitle,TAG_DONE);
    SetGadgetAttrs(SpaceObj,KernWnd,NULL,
      STRINGA_LongVal,SpaceTable[node->chln_Number],TAG_DONE);
    SetGadgetAttrs(KernObj,KernWnd,NULL,
      STRINGA_LongVal,KernTable[node->chln_Number],TAG_DONE);
    WindowToFront(KernWnd);
    ActivateWindow(KernWnd);
  }
  else
  {
    if (KernWndObj == NULL)
    {
      KernWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_MenuStrip,menus,
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"kern",
	WINDOW_ScaleWidth,11,
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),Spacing(4),
	    GROUP_BackFill,SHINE_RASTER,
	    StartMember,
	      VGroupObject,
		HOffset(8),VOffset(4),Spacing(2),
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  SpaceObj = TabKeyInteger("_Spacing",0,3,ID_SPACE),
		EndMember,
		StartMember,
		  KernObj = TabKeyInteger("_Kerning",0,3,ID_KERN),
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		Spacing(8),
		StartMember,
		  accept = KeyButton("_Accept",ID_ACCEPT),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (KernWndObj == NULL) Quit();
      GadgetKey(KernWndObj,accept,"a");
      GadgetKey(KernWndObj,cancel,"c");
      GadgetKey(KernWndObj,SpaceObj,"s");
      GadgetKey(KernWndObj,KernObj,"k");
      DoMethod(KernWndObj,WM_TABCYCLE_ORDER,SpaceObj,KernObj,NULL);
    }

    SetAttrs(KernWndObj,WINDOW_Title,KernTitle,TAG_DONE);
    SetAttrs(SpaceObj,
      STRINGA_LongVal,SpaceTable[node->chln_Number],TAG_DONE);
    SetAttrs(KernObj,STRINGA_LongVal,KernTable[node->chln_Number],TAG_DONE);
    if ((KernWnd = WindowOpen(KernWndObj)) == NULL) Quit();
  }
}

void CloseKernWnd(BOOL obj)
{
  ClrWindowClose(&KernWndObj,&KernWnd);
  if (obj) ClrDisposeObject(&KernWndObj);
  KernNode = NULL; KernChar = NULL;
}

void OpenSaveWnd(void)
{
Object *save, *cancel;
ULONG satan = 666;

  if (SaveWnd)
  {
    WindowToFront(SaveWnd);
    ActivateWindow(SaveWnd);
  }
  else
  {
    if (SaveWndObj == NULL)
    {
      SaveWndObj = WindowObject,
	WINDOW_Screen,Screen,
	WINDOW_SharedPort,WndMsgPort,
	WINDOW_Title,"Save Font",
	WINDOW_HelpFile,NAME".guide",
	WINDOW_HelpNode,"save",
	WINDOW_SmartRefresh,TRUE,
	WINDOW_SizeGadget,FALSE,
	WINDOW_RMBTrap,TRUE,
	WINDOW_MasterGroup,
	  VGroupObject,
	    HOffset(8),VOffset(4),Spacing(4),
	    GROUP_BackFill,SHINE_RASTER,
	    StartMember,
	      VGroupObject,
		HOffset(8),VOffset(4),Spacing(2),
		ButtonFrame,
		FRM_Recessed,TRUE,
		StartMember,
		  HGroupObject,
		    StartMember,
		      SaveDirStr = StringObject,
			RidgeFrame,
			UScoreLabel("Font _Directory",'_'),
			STRINGA_MaxChars,255,
			GA_TabCycle,TRUE,
			GA_ID,ID_SAVEDIR,
		      EndObject,
		    EndMember,
		    StartMember,
		      ButtonObject,
			GetPath,
			ButtonFrame,
			GA_ID,ID_GETSAVEDIR,
		      EndObject,
		      FixMinWidth,
		    EndMember,
		  EndObject,
		  NoAlign,
		EndMember,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      FontNameStr = StringObject,
			RidgeFrame,
			UScoreLabel("_Font",'_'),
			STRINGA_MaxChars,255,
			GA_TabCycle,TRUE,
			GA_ID,ID_FONTNAME,
		      EndObject,
		    EndMember,
		    StartMember,
		      SizeInfo = InfoObject,
			ButtonFrame,
			FRM_Flags,FRF_RECESSED,
			Label("Size"),
			INFO_TextFormat,"\33c%ld",
			INFO_Args,&satan,
			INFO_MinLines,1,
			INFO_HorizOffset,6,
			INFO_VertOffset,3,
			INFO_FixTextWidth,TRUE,
		      EndObject,
		      Weight(10),
		    EndMember,
		  EndObject,
		EndMember,
		StartMember,
		  HGroupObject,
		    Spacing(8),
		    StartMember,
		      FirstNum = TabKeyInteger("F_irst",0,3,ID_FIRST),
		    EndMember,
		    StartMember,
		      LastNum = TabKeyInteger("_Last",0,3,ID_LAST),
		    EndMember,
		    StartMember,
		      VGroupObject,
			VarSpace(60),
			StartMember,
			  TableCheck = KeyCheckBox("_Kern Info",0,
			    ID_KERNTABLE),
			EndMember,
			VarSpace(50),
		      EndObject,
		    EndMember,
		  EndObject,
		EndMember,
	      EndObject,
	    EndMember,
	    StartMember,
	      HGroupObject,
		Spacing(8),
		StartMember,
		  save = KeyButton("_Save",ID_ACCEPT),
		EndMember,
        	StartMember,
		  cancel = KeyButton("_Cancel",ID_CANCEL),
		EndMember,
	      EndObject,
	      FixMinHeight,
	    EndMember,
	  EndObject,
	EndObject;
      if (SaveWndObj == NULL) Quit();
      GadgetKey(SaveWndObj,save,"s");
      GadgetKey(SaveWndObj,cancel,"c");
      GadgetKey(SaveWndObj,SaveDirStr,"d");
      GadgetKey(SaveWndObj,FontNameStr,"f");
      GadgetKey(SaveWndObj,FirstNum,"i");
      GadgetKey(SaveWndObj,LastNum,"l");
      GadgetKey(SaveWndObj,TableCheck,"k");
      DoMethod(SaveWndObj,WM_TABCYCLE_ORDER,
	SaveDirStr,FontNameStr,FirstNum,LastNum,NULL);

      SaveReqObj = FileReqObject,
	ASLFR_TitleText,"Change Font Directory",
	ASLFR_DrawersOnly,TRUE,
      EndObject;
      if (SaveReqObj == NULL) Quit();
    }

    SleepWindows();
    SetAttrs(SaveDirStr,STRINGA_TextVal,SavePath,TAG_DONE);
    SetAttrs(FontNameStr,STRINGA_TextVal,FontName,TAG_DONE);
    SetAttrs(FirstNum,STRINGA_LongVal,FirstChar,TAG_DONE);
    SetAttrs(LastNum,STRINGA_LongVal,LastChar,TAG_DONE);
    SetAttrs(SizeInfo,INFO_Args,&Height,TAG_DONE);
    SetAttrs(TableCheck,
      GA_Disabled,Proportional == TRUE,
      GA_Selected,(Proportional == TRUE) || (UseTable == TRUE),TAG_DONE);
    if ((SaveWnd = WindowOpen(SaveWndObj)) == NULL) Quit();
  }
}

void CloseSaveWnd(BOOL obj)
{
  WakeWindows();
  ClrWindowClose(&SaveWndObj,&SaveWnd);
  if (obj) ClrDisposeObject(&SaveWndObj);
}

void PutPositive(struct Window *wnd, char *name, LONG value, LONG min,
  LONG *dest)
{
  if (value > min)
    *dest = value;
  else
    ShowReq("The %s must be greater than %ld.","Continue",name,min);
}

void NewHeights(void)
{
struct Character *chr;
ULONG i;
UBYTE *new_char;

  for (i = 0; i < 256; i++)
  {
    chr = CharBuffer+i;
    if (chr->chr_Data)
    {
      if ((new_char = AllocVec(chr->chr_Width*Height,MEMF_CLEAR)) == NULL)
	Quit();
      CopyMem(chr->chr_Data,new_char,
	MIN(chr->chr_Height,Height)*chr->chr_Width);
      FreeVec(chr->chr_Data);
      chr->chr_Data = new_char;
    }
    chr->chr_Height = Height;
  }
  sprintf(WinTitle,"%s/%ld",FontName,Height);
  SetAttrs(FontWndObj,WINDOW_Title,WinTitle,TAG_DONE);
}

void NewWidths(void)
{
struct Character *chr;
ULONG i;

  for (i = 0; i < 256; i++)
  {
    chr = CharBuffer+i;
    if (chr->chr_Data) ChangeCharWidth(chr,Width,0,0);
    chr->chr_Width = Width;
  }
}

void ChangeCharWidth(struct Character *chr, ULONG width, ULONG newoffset,
  ULONG oldoffset)
{
ULONG j;
UBYTE *data = NULL;

  if ((width > 0) && (chr->chr_Data))
  {
    if ((data = AllocVec(width*Height,MEMF_CLEAR)) == NULL) Quit();
    for (j = 0; j < Height; j++)
      CopyMem(chr->chr_Data+(j*chr->chr_Width)+oldoffset,
      data+(j*width)+newoffset,MIN(chr->chr_Width,width));
  }
  if (chr->chr_Data) FreeVec(chr->chr_Data);
  if (Reversed == FALSE)
    SpaceTable[chr-CharBuffer] += (width-chr->chr_Width);
  chr->chr_Data = data;
  chr->chr_Width = width;
  CheckWidthKern(chr);
}

void CheckWidthKern(struct Character *chr)
{
  if (chr == WidthChar)
  {
    if (WidthNode->chln_Number < 256)
      sprintf(WidthTitle,"Change Width of %lu (from %d)",
        WidthNode->chln_Number,WidthChar->chr_Width);
    else
      sprintf(WidthTitle,"Change Width of blank (from %d)",
	WidthChar->chr_Width);
    SetGadgetAttrs(ChrWidthScroll,WidthWnd,NULL,
      PGA_Top,0,
      PGA_Total,1,
      PGA_Visible,1,TAG_DONE);
    SetGadgetAttrs(ChrWidthInt,WidthWnd,NULL,
      STRINGA_LongVal,WidthChar->chr_Width,TAG_DONE);
    SetAttrs(WidthWndObj,WINDOW_Title,WidthTitle,TAG_DONE);
  }
  if (chr == KernChar)
    SetGadgetAttrs(SpaceObj,KernWnd,NULL,
      STRINGA_LongVal,SpaceTable[KernNode->chln_Number],TAG_DONE);
}

void SavePrefs(char *filename)
{
BPTR file;

  SleepWindows();
  Prefs.Flags = 0;
  if (NewCustom) Prefs.Flags |= PREFS_CUSTOMSCREEN;
  if (PixelBorder) Prefs.Flags |= PREFS_PIXELBORDER;
  if (file = Open(filename,MODE_NEWFILE))
  {
    Write(file,&Prefs,sizeof(struct Preferences));
    Close(file);
  }
  else ShowReq("Cannot save preferences to\n\"%s\"","Continue",filename);
  WakeWindows();
}

void LoadPrefs(void)
{
BPTR file;

  if ((file = Open("ENV:"NAME".prefs",MODE_OLDFILE)) == 0)
    file = Open("PROGDIR:"NAME".prefs",MODE_OLDFILE);
  if (file)
  {
    Read(file,&Prefs,sizeof(struct Preferences));
    Close(file);
    CustomScreen = (Prefs.Flags & PREFS_CUSTOMSCREEN) ? TRUE : FALSE;
    PixelBorder = (Prefs.Flags & PREFS_PIXELBORDER) ? TRUE : FALSE;
  }
}

void CopyChar(struct CharNode *node)
{
struct Character *chr;

  chr = node->chln_Character;
  if ((chr->chr_Width > 0) && (chr->chr_Data))
  {
    if (ClipChar.chr_Data) FreeVec(ClipChar.chr_Data);
    ClipChar.chr_Data = NULL;
    ClipChar.chr_Width = chr->chr_Width;
    ClipChar.chr_Height = chr->chr_Height;
    if ((ClipChar.chr_Data = AllocVec(chr->chr_Width*chr->chr_Height,
      MEMF_CLEAR)) == NULL) Quit();
    CopyMem(chr->chr_Data,ClipChar.chr_Data,chr->chr_Width*chr->chr_Height);
  }
}

void PasteChar(struct CharNode *node)
{
struct Character *chr;
ULONG i;

  chr = node->chln_Character;
  if (ClipChar.chr_Data)
  {
    if (chr->chr_Data) FreeVec(chr->chr_Data);
    chr->chr_Data = NULL;
    if (Proportional) chr->chr_Width = ClipChar.chr_Width;
    if (chr->chr_Width > 0)
    {
      if ((chr->chr_Data = AllocVec(chr->chr_Width*chr->chr_Height,
	MEMF_CLEAR)) == NULL) Quit();
      for (i = 0; i < MIN(chr->chr_Height,ClipChar.chr_Height); i++)
	CopyMem(ClipChar.chr_Data+(i*ClipChar.chr_Width),
	chr->chr_Data+(i*chr->chr_Width),
	MIN(chr->chr_Width,ClipChar.chr_Width));
    }
    RedrawEdit(node);
  }
}

void SaveFont(BOOL tables)
{
char fontpath[256], filename[256], datestr[LEN_DATSTRING];
ULONG filesize = 0, numchars, bitwidth = 0, i, offbit, hunkstart;
struct DateTime dt;
struct DiskFontHeader *dfh;
struct TextFont *tfh;
struct FontContentsHeader *fch;
ULONG *longptr, *relocstart;
UBYTE *buffer, *mptr, *fontdataptr;
struct charDef *fontlocptr;
WORD *fontkernptr;
BPTR fontfile, contfile, lock;

  strcpy(fontpath,SavePath);
  AddPart(fontpath,FontName,256);
  sprintf(filename,"%s/%ld",fontpath,Height);

  DateStamp(&(dt.dat_Stamp));
  dt.dat_Format = FORMAT_CDN;
  dt.dat_Flags = 0;
  dt.dat_StrDay = NULL;
  dt.dat_StrDate = datestr;
  dt.dat_StrTime = NULL;
  DateToStr(&dt);
  while ((mptr = strchr(datestr,'-')) != NULL) *mptr = '.';

  numchars = LastChar-FirstChar+2;
  filesize += 14*4;			/* Hunk structure */
  filesize += sizeof(struct DiskFontHeader);
  filesize += 4*4;			/* Relocatable addresses */
  for (i = FirstChar; i < LastChar+1; i++)
    bitwidth += (CharBuffer+i)->chr_Width;
  bitwidth += (CharBuffer+256)->chr_Width;
  if (bitwidth % 16 > 0) bitwidth = ((bitwidth/16)+1)*16;
  filesize += (bitwidth/8)*Height;	/* Character data */
  filesize += numchars*sizeof(struct charDef);
  if (tables)
  {
    filesize += numchars*4;		/* Space and kern tables */
    filesize += 2*4;			/* Table relocs */
  }
  if (filesize % 4 > 0) filesize = ((filesize/4)+1)*4;
  if ((buffer = AllocVec(filesize,MEMF_CLEAR)) != NULL)
  {
    longptr = buffer;
    *longptr++ = 0x000003F3;		/* Hunk structure */
    *longptr++ = 0x00000000;
    *longptr++ = 0x00000001;
    *longptr++ = 0x00000000;
    *longptr++ = 0x00000000;
    *longptr++ = (filesize - ((tables ? 19 : 17)*4))/4;
    *longptr++ = 0x000003E9;		/* Hunk size (twice) */
    *longptr++ = (filesize - ((tables ? 19 : 17)*4))/4;
    hunkstart = longptr;		/* All relocs relative to here */
    *longptr++ = 0x70FF4E75;		/* MOVEQ #-1,D0 RTS */
    relocstart = buffer+filesize-((9+(tables ? 2 : 0))*4);

    dfh = longptr;
    dfh->dfh_DF.ln_Type = NT_FONT;
    dfh->dfh_DF.ln_Name = (UBYTE *)((ULONG)dfh->dfh_Name-hunkstart);
    dfh->dfh_FileID = DFH_ID;
    sprintf(dfh->dfh_Name,"$VER: %s%d 39.0 (%s)",FontName,Height,datestr);

    fontdataptr = ((UBYTE *)dfh)+sizeof(struct DiskFontHeader);
    fontlocptr = fontdataptr+((bitwidth/8)*Height);
    if ((ULONG)fontlocptr % 2 > 0)
      fontlocptr = (struct charDef *)((((ULONG)fontlocptr/2)+1)*2);
    fontkernptr = (UBYTE *)fontlocptr + numchars*sizeof(struct charDef);

    tfh = &(dfh->dfh_TF);
    tfh->tf_Message.mn_Node.ln_Type = NT_FONT;
    tfh->tf_Message.mn_Node.ln_Name = dfh->dfh_DF.ln_Name;
    tfh->tf_YSize = Height;
    if (Normal == FALSE)
    {
      if (Bold) tfh->tf_Style |= FSF_BOLD;
      if (Italic) tfh->tf_Style |= FSF_ITALIC;
      if (ULine) tfh->tf_Style |= FSF_UNDERLINED;
      if (Extended) tfh->tf_Style |= FSF_EXTENDED;
    }
    else tfh->tf_Style = FS_NORMAL;
    tfh->tf_Flags = FPF_DESIGNED|FPF_DISKFONT;
    if (Proportional) tfh->tf_Flags |= FPF_PROPORTIONAL;
    if (Reversed) tfh->tf_Flags |= FPF_REVPATH;
    switch (Aspect)
    {
      case 1:
	tfh->tf_Flags |= FPF_TALLDOT;
	break;
      case 2:
	tfh->tf_Flags |= FPF_WIDEDOT;
	break;
    }
    tfh->tf_XSize = Width;
    tfh->tf_Baseline = Baseline;
    tfh->tf_BoldSmear = Smear;
    tfh->tf_LoChar = FirstChar;
    tfh->tf_HiChar = LastChar;
    tfh->tf_CharData = (UBYTE *)((ULONG)fontdataptr-hunkstart);
    tfh->tf_Modulo = bitwidth/8;
    tfh->tf_CharLoc = (UBYTE *)((ULONG)fontlocptr-hunkstart);
    if (tables)
    {
      tfh->tf_CharSpace = (UBYTE *)((ULONG)fontkernptr-hunkstart);
      tfh->tf_CharKern =
	(UBYTE *)((ULONG)fontkernptr+(numchars*2)-hunkstart);
    }
    for (i = FirstChar, offbit = 0; i < LastChar+1; i++)
    {
      WriteCharData(fontlocptr,fontdataptr,i-FirstChar,i,&offbit,bitwidth/8);
      if (tables)
      {
	*(fontkernptr+(i-FirstChar)) = SpaceTable[i];
	*(fontkernptr+(i-FirstChar+numchars)) = KernTable[i];
      }
    }
    WriteCharData(fontlocptr,fontdataptr,numchars-1,256,&offbit,bitwidth/8);
    if (tables)
    {
      *(fontkernptr+(numchars-1)) = SpaceTable[256];
      *(fontkernptr+(numchars-1+numchars)) = KernTable[256];
    }

    *relocstart++ = 0x000003EC;		/* Reloc structure */
    *relocstart++ = 4 + (tables ? 2 : 0);
    *relocstart++ = 0x00000000;
    if (tables)
    {
      *relocstart++ = (ULONG)(&(tfh->tf_CharKern))-hunkstart;
      *relocstart++ = (ULONG)(&(tfh->tf_CharSpace))-hunkstart;
    }
    *relocstart++ = (ULONG)(&(tfh->tf_CharLoc))-hunkstart;
    *relocstart++ = (ULONG)(&(tfh->tf_CharData))-hunkstart;
    *relocstart++ = (ULONG)(&(tfh->tf_Message.mn_Node.ln_Name))-hunkstart;
    *relocstart++ = (ULONG)(&(dfh->dfh_DF.ln_Name))-hunkstart;
    *relocstart++ = 0x00000000;
    *relocstart++ = 0x000003F2;

    WindowBusy(SaveWndObj);
    lock = Lock(fontpath,ACCESS_READ);
    WindowReady(SaveWndObj);
    if (lock == 0)
    {
      if (SaveShowReq("Directory \"%s\"\ndoes not exist.","Create|Cancel",
	fontpath))
      {
        WindowBusy(SaveWndObj);
        lock = CreateDir(fontpath);
        WindowReady(SaveWndObj);
	if (lock == 0)
	{
	  SaveShowReq("Cannot create directory\n\"%s\".","Cancel",fontpath);
	  if (buffer) FreeVec(buffer);
	  return;
	}
	else UnLock(lock);
      }
      else
      {
	if (buffer) FreeVec(buffer);
	return;
      }
    }
    else UnLock(lock);

    WindowBusy(SaveWndObj);
    lock = Lock(filename,ACCESS_READ);
    WindowReady(SaveWndObj);
    if (lock)
    {
      UnLock(lock);
      if (!SaveShowReq("File \"%s\" exists.","Overwrite|Cancel",filename))
      {
	if (buffer) FreeVec(buffer);
	return;
      }
    }

    WindowBusy(SaveWndObj);
    fontfile = Open(filename,MODE_NEWFILE);
    WindowReady(SaveWndObj);
    if (fontfile)
    {
      WindowBusy(SaveWndObj);
      Write(fontfile,buffer,filesize);
      Close(fontfile);

      lock = Lock(SavePath,ACCESS_READ);
      WindowReady(SaveWndObj);
      if (lock != 0)
      {
	sprintf(filename,"%s.font",fontpath);
	WindowBusy(SaveWndObj);
	fch = NewFontContents(lock,FilePart(filename));
	WindowReady(SaveWndObj);
	if (fch != NULL)
	{
	  WindowBusy(SaveWndObj);
	  contfile = Open(filename,MODE_NEWFILE);
	  WindowReady(SaveWndObj);
	  if (contfile != 0)
	  {
	    WindowBusy(SaveWndObj);
	    Write(contfile,fch,sizeof(struct FontContentsHeader)+
	      (fch->fch_NumEntries*sizeof(struct FontContents)));
	    Close(contfile);
	    WindowReady(SaveWndObj);
	  }
	  else SaveShowReq("Cannot write font header to \n\"%s\".","Cancel",
	    filename);
	  DisposeFontContents(fch);
	}
	else ShowReq("Cannot create font header.","Cancel");
	UnLock(lock);
      }
    }
    else SaveShowReq("Cannot write font data to \n\"%s\".","Cancel",
      filename);
  }
  else SaveShowReq("Not enough memory.","Cancel");
  if (buffer) FreeVec(buffer);
  FlushAllFonts();
}

ULONG SaveShowReq(char *text,char *gadgets,...)
{
va_list va;
struct EasyStruct req =
  { sizeof(struct EasyStruct),0,NAME,NULL,NULL };
ULONG retcode;

  if (SaveWnd) WindowBusy(SaveWndObj);
  req.es_TextFormat = text;
  req.es_GadgetFormat = gadgets;
  va_start(va,gadgets);
  retcode = EasyRequestArgs(SaveWnd,&req,NULL,va);
  va_end(va);
  if (SaveWnd) WindowReady(SaveWndObj);
  return (retcode);
}

void WriteCharData(struct charDef *cd, UBYTE *fontdataptr, ULONG dest,
  ULONG src, ULONG *off, ULONG mod)
{
struct Character *chr;
UBYTE *data;
ULONG i,j,width,widthi,modi,offset;

  chr = CharBuffer+src;
  data = chr->chr_Data;
  width = chr->chr_Width;
  offset = *off;
  for (i = 0; i < chr->chr_Height; i++)
  {
    modi = mod*i;
    widthi = width*i;
    for (j = 0; j < width; j++)
    {
      if (*(data+widthi+j) == 1)
	*(fontdataptr+modi+((offset+j)>>3)) |= 128>>((offset+j)&7);
    }
  }
  (cd+dest)->charOffset = offset;
  (cd+dest)->charBitWidth = width;
  *off += width;

}

void FlushAllFonts(void)
{
UBYTE *free;

  if ((free = AllocMem(~0,MEMF_ANY)) != NULL) FreeMem(free,~0);
}
