/*	XGFont.cpp
 *
 *		The font management code
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or any
 *  later version.
 *  
 *  This library is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XError.h>
#include <XDraw.h>
#include <string.h>

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

XGPDynArray<XGFont> XGFont::gFontArray;


/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

// @G"Construction/Destruction"

/*	@F"XGFont::XGFont" @I
 *
 *		Create a font, marked as attached.
 */

XGFont::XGFont()
{
	/*
	 *	Create a font
	 */
	
	fAttach = 1;
	fFontResID = -1;
	
	fFontFlags = 0;
	fFontSize = 9;
	fFontName[0] = 0;
	
#if OPT_MACOS == 1
	fFontID = -1;
#endif

#if OPT_WINOS == 1
	fFontHandle = NULL;
#endif

#if OPT_XWIN == 1
	fFontData = NULL;
#endif
	
	/*
	 *	Add it to the list of fonts I'm managing
	 */
	
	gFontArray += this;
}

/*	@F"XGFont::~XGFont" @I
 *
 *		Delete a font. Normally you should use the 'Detach' method
 *	instead of directly destroying this object; otherwise, the font
 *	cache can become confused.
 */

XGFont::~XGFont()
{
	long i,len;
	
	/*
	 *	Barf if I'm locked or attached
	 */
	
	Assert(fAttach <= 0);
	
	/*
	 *	Remove me from the font array
	 */
	
	len = gFontArray.Length();
	for (i = 0; i < len; i++) {
		if (gFontArray[i] == this) {
			gFontArray.Delete(i);
		}
	}
	
	/*
	 *	(Windows) Delete the font handle
	 */
	
#if OPT_WINOS == 1
	if (fFontHandle) ::DeleteObject(fFontHandle);
	fFontHandle = NULL;
#endif

#if OPT_XWIN == 1
	if (fFontData) ::XFreeFont(_GDisplay,fFontData);
	fFontData = NULL;
#endif
}

/************************************************************************/
/*																		*/
/*	Attach/Detach semantics												*/
/*																		*/
/************************************************************************/

// @G"Attach/Detach Semantics"

/*	@F"XGFont::Detach" @I
 *
 *		Detach; this detaches this font, and when this hits zero, delete
 */

void XGFont::Detach(void)
{
	fAttach--;
	if (fAttach <= 0) delete this;
}

/************************************************************************/
/*																		*/
/*	Attach/Detach semantics												*/
/*																		*/
/************************************************************************/

// @G"Font Specification"

/*	@F"XGFont::LoadFont" @I
 *
 *		Load the font from a resource
 */

XGFont *XGFont::LoadFont(short resID)
{
	XGFont *f;
	XGSFontResource *fptr;
	long i,len;
	
	/*
	 *	Search for the font and return it if it's already loaded.
	 */
	
#if OPT_MACOS || OPT_XWIN			// printer kludge workaround
	len = gFontArray.Length();		// skip for Windows because the same
	for (i = 0; i < len; i++) {		// font handle works differently from
		f = gFontArray[i];			// printer to printer... ###
		if (f->fFontResID == resID) {
			f->Attach();
			return f;
		}
	}
#endif
	
	/*
	 *	Create the font if it ain't loaded
	 */

	f = new XGFont;
	if (resID == 0) {
		/*
		 *	Create default system font for dialogs
		 */
		
#if OPT_MACOS == 1
		f->SetFontName("");
		f->SetFontSize(0);
		f->SetFontFlags(0);
#endif

#if OPT_WINOS == 1
		f->SetFontName("Arial");
		f->SetFontSize(9);
		f->SetFontFlags(KXGFontBold);
#endif

#if OPT_XWIN == 1
		f->SetFontName("");
		f->SetFontSize(10);
		f->SetFontFlags(KXGFontBold);
#endif
	} else if (resID == 1) {
		/*
		 *	Create default application font
		 */
		
#if OPT_MACOS == 1
		f->SetFontName("Geneva");
		f->SetFontSize(9);
		f->SetFontFlags(0);
#endif

#if OPT_WINOS == 1
		f->SetFontName("Arial");
		f->SetFontSize(8);
		f->SetFontFlags(0);
#endif

#if OPT_XWIN == 1
		f->SetFontName("");
		f->SetFontSize(10);
		f->SetFontFlags(0);
#endif
	} else if (resID == 2) {
		/*
		 *	Create default mono-spaced
		 */
		
#if OPT_MACOS == 1
		f->SetFontName("Monaco");
		f->SetFontSize(9);
		f->SetFontFlags(0);
#endif

#if OPT_WINOS == 1
		f->SetFontName("Courier New");
		f->SetFontSize(8);
		f->SetFontFlags(0);
#endif

#if OPT_XWIN == 1
		f->SetFontName("Courier");
		f->SetFontSize(10);
		f->SetFontFlags(0);
#endif
	} else {
		/*
		 *	Load font from resource
		 */
	
#if OPT_MACOS == 1
		Handle h;
		short flags;
		
		h = GetResource('MFnt',resID);
		if (h == NULL) {
			f->Detach();
			return NULL;
		}
		fptr = (XGSFontResource *)(*h);
		f->SetFontName(fptr->name);
		f->SetFontSize(fptr->size);
		
		flags = 0;
		if (fptr->fBold)      flags |= KXGFontBold;
		if (fptr->fItalic)    flags |= KXGFontItalic;
		if (fptr->fUnderline) flags |= KXGFontUnderline;
		if (fptr->fFixed)     flags |= KXGFontFixFont;
		if (fptr->fSerif)     flags |= KXGFontSerifFont;
		f->SetFontFlags(flags);
		
		ReleaseResource(h);
#endif

#if OPT_WINOS == 1
		/*
		 *	### Things to do...
		 *
		 *	This should load the font from the resource specified. Right
		 *	now, this isn't defined. I need to go in and plug this back in.
		 */
		
		return NULL;
#endif

#if OPT_XWIN == 1
		/*
		 *	### Things to do...
		 *
		 *	This should load the font from the resource specified. Right
		 *	now, this isn't defined. I need to go in and plug this back in.
		 */
		
		return NULL;
#endif

	}
	
	/*
	 *	Now I have a new font, mark it as such
	 */
	
	f->fFontResID = resID;
	return f;
}

/*	@F"XGFont::GetFontVariant" @I
 *
 *		This allows me to get a font which is exactly like the font I have
 *	on hand, but with the 'underline', 'italic', or 'bold' attributes
 *	different. This also sets the 'attach' flag
 */

XGFont *XGFont::GetFontVariant(XGFont *f, short newflags)
{
	char name[64],buffer[64];
	short flags;
	short size;
	long i,len;
	XGFont *ptr;
	
	/*
	 *	Initialize
	 */
	
	newflags &= 0x00FF;				// strip font type flags
	
	/*
	 *	Get font attributes from the old font
	 */
	
	f->GetFontName(name);
	flags = (f->GetFontFlags() & 0xFF00) | newflags;
	size = f->GetFontSize();
	
	/*
	 *	Search for the specified font
	 */
	
	len = gFontArray.Length();
	for (i = 0; i < len; i++) {
		ptr = gFontArray[i];
		if (flags != ptr->GetFontFlags()) continue;
		if (size != ptr->GetFontSize()) continue;
		
		ptr->GetFontName(buffer);
		if (!strcmp(buffer,name)) {
			/*
			 *	Found it!
			 */
			
			ptr->Attach();
			return ptr;
		}
	}

	/*
	 *	If the font is not found, create it
	 */
	
	ptr = new XGFont;
	ptr->SetFontName(name);
	ptr->SetFontSize(size);
	ptr->SetFontFlags(flags);

	return ptr;
}


/************************************************************************/
/*																		*/
/*	Font Attributes														*/
/*																		*/
/************************************************************************/

/*	@F"XGFont::GetFontName" @I
 *
 *		Get the name of this font
 */

void XGFont::GetFontName(char *f) const
{
	strcpy(f,fFontName);
}

/*	@F"XGFont::SetFontFlags" @I
 *
 *		Set the font flags. This <<only>> works if there is only one
 *	attached object; the assumption is that this is not in use or
 *	shared with someone else
 */

void XGFont::SetFontFlags(short f)
{
	if (fAttach != 1) return;
	
#if OPT_WINOS == 1
	if (fFontHandle) ::DeleteObject(fFontHandle);
	fFontHandle = NULL;
#endif

#if OPT_XWIN == 1
	if (fFontData) ::XFreeFont(_GDisplay,fFontData);
	fFontData = NULL;
#endif

	fFontFlags = f;
	fFontResID = -1;
}

/*	@F"XGFont::SetFontName" @I
 *
 *		Set the font name. This <<only>> works if there is only one
 *	attached object; the assumption is that this is not in use or
 *	shared with someone else
 */

void XGFont::SetFontName(const char *f)
{
	if (fAttach != 1) return;
	
#if OPT_WINOS == 1
	if (fFontHandle) ::DeleteObject(fFontHandle);
	fFontHandle = NULL;
#endif

#if OPT_XWIN == 1
	if (fFontData) ::XFreeFont(_GDisplay,fFontData);
	fFontData = NULL;
#endif

	strcpy(fFontName,f);
	fFontResID = -1;

#if OPT_MACOS == 1
	fFontID = -1;
#endif
}

/*	@F"XGFont::SetFontSize" @I
 *
 *		Set the font size. This <<only>> works if there is only one
 *	attached object; the assumption is that this is not in use or
 *	shared with someone else
 */

void XGFont::SetFontSize(short f)
{
	if (fAttach != 1) return;
	
#if OPT_WINOS == 1
	if (fFontHandle) ::DeleteObject(fFontHandle);
	fFontHandle = NULL;
#endif

#if OPT_XWIN == 1
	if (fFontData) ::XFreeFont(_GDisplay,fFontData);
	fFontData = NULL;
#endif

	fFontSize = f;
	fFontResID = -1;
}

/************************************************************************/
/*																		*/
/*	Font Hooks															*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*	XGFont::SetFont
 *
 *		Set the font according to the attributes specified.
 */

void XGFont::SetFont(void)
{
	short flags;
	unsigned char buffer[64];
	
	if (fFontID == -1) {
		/*
		 *	Get the font ID
		 */
	
		if (fFontName[0] == 0) fFontID = 0;			/* System font kludge */
		else {
			strcpy((char *)buffer,fFontName);
			c2pstr((char *)buffer);
			
			/*
			 *	Get the font
			 */
			
			::GetFNum(buffer,&fFontID);
			if (fFontID == 0) {
				/*
				 *	Okay, the font ID doesn't exist. Pick either monaco,
				 *	geneva, courier or times
				 */
				
				if (fFontFlags & KXGFontFixFont) {
					/*
					 *	Fixed fonts are Courier or Monaco.
					 */
					
					if (fFontFlags & KXGFontSerifFont) {
						::GetFNum("\pCourier",&fFontID);
						if (fFontID == 0) fFontID = 4;	// go to Monaco
					} else {
						fFontID = 4;
					}
				} else {
					/*
					 *	Variable with fonts are Geneva or Times
					 */
					
					if (fFontFlags & KXGFontSerifFont) {
						::GetFNum("\pTimes",&fFontID);
						if (fFontID == 0) fFontID = 1;
					} else {
						fFontID = 1;
					}
				}
			}
		}
	}
	
	/*
	 *	Now set the parameters
	 */
	
	::TextFont(fFontID);
	::TextSize(fFontSize);
	flags = 0;
	if (fFontFlags & KXGFontBold) flags |= 1;
	if (fFontFlags & KXGFontItalic) flags |= 2;
	if (fFontFlags & KXGFontUnderline) flags |= 4;
	::TextFace(flags);
}

#endif // OPT_MACOS


#if OPT_WINOS == 1

/*	XGFont::GetFont
 *
 *		Get the font handle associated with this font
 */

HFONT XGFont::GetFont(HDC dc)
{
	LOGFONT lf;
	bool f;
	
	/*
	 *	DC is used for information only. If NULL, this gets
	 *	an information device context
	 */
	
	if (dc) {
		f = true;
	} else {
		f = false;
		dc = ::CreateDC("DISPLAY",NULL,NULL,NULL);
	}
	
	/*
	 *	Is the font already built? If so, return it
	 */
	
	if (fFontHandle) return fFontHandle;
	
	/*
	 *	Run through the font list
	 */
	
	lf.lfHeight = -MulDiv(fFontSize,GetDeviceCaps(dc,LOGPIXELSY),72);
	lf.lfWidth = 0;
	lf.lfEscapement = 0;
	lf.lfOrientation = 0;
	lf.lfWeight = (fFontFlags & KXGFontBold) ? FW_BOLD : FW_NORMAL;
	lf.lfItalic = (fFontFlags & KXGFontItalic) ? true : false;
	lf.lfUnderline = (fFontFlags & KXGFontUnderline) ? true : false;
	lf.lfStrikeOut = 0;
	lf.lfCharSet = ANSI_CHARSET;			// what the heck.
	lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
	lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	lf.lfQuality = DEFAULT_QUALITY;
	
	if (fFontFlags & KXGFontFixFont) {
		lf.lfPitchAndFamily = FIXED_PITCH;
	} else {
		lf.lfPitchAndFamily = VARIABLE_PITCH;
	}
	if (fFontFlags & KXGFontSerifFont) {
		lf.lfPitchAndFamily |= FF_ROMAN;
	} else {
		lf.lfPitchAndFamily |= FF_SWISS;
	}
	
	strcpy(lf.lfFaceName,fFontName);
	
	/*
	 *	Release stuff
	 */
	
	if (!f) {
		::DeleteDC(dc);
	}
	
	/*
	 *	Now generate the font
	 */
	
	fFontHandle = ::CreateFontIndirect(&lf);
	return fFontHandle;
}

#endif // OPT_WINOS

#if OPT_XWIN == 1

/*	XGFont::GetFont
 *
 *		Get the font reference associated with this font specification
 */

Font XGFont::GetFont()
{
	char buffer[256];
	char *deffont;
	
	if (fFontData) return fFontData->fid;

	/*
	 *	Construct the font name
	 */
	
	if (fFontName[0]) {
		/*
		 *	First, try to get the font by name.
		 */
		
		sprintf(buffer,"-*-%s-%s-%s-normal--*-%d*",
					   fFontName,
					   (fFontFlags & KXGFontBold) ? "bold" : "medium",
					   (fFontFlags & KXGFontItalic) ? "i" : "r",
					   fFontSize * 10);
		fFontData = XLoadQueryFont(_GDisplay,buffer);
		if (fFontData) return fFontData->fid;
		
		if (fFontFlags & KXGFontItalic) {
			/* Try oblique for italic */
			sprintf(buffer,"-*-%s-%s-o-normal--*-%d*",
						   fFontName,
						   (fFontFlags & KXGFontBold) ? "bold" : "medium",
						   fFontSize * 10);
			fFontData = XLoadQueryFont(_GDisplay,buffer);
			if (fFontData) return fFontData->fid;
		}
	}
	
	/*
	 *	Now try the font by it's fixed/unfixed width attribute by
	 *	picking names I think may be there
	 */
	
	if (fFontFlags & KXGFontFixFont) {
		deffont = "courier";
	} else if (fFontFlags & KXGFontSerifFont) {
		deffont = "times";
	} else {
		deffont = "helvetica";
	}
	
	sprintf(buffer,"-*-%s-%s-%s-normal--*-%d*",
				   deffont,
				   (fFontFlags & KXGFontBold) ? "bold" : "medium",
				   (fFontFlags & KXGFontItalic) ? "i" : "r",
				   fFontSize * 10);
	fFontData = XLoadQueryFont(_GDisplay,buffer);
	if (fFontData) return fFontData->fid;
	if (fFontFlags & KXGFontItalic) {
		/* Try oblique for italic */
		sprintf(buffer,"-*-%s-%s-o-normal--*-%d*",
					   deffont,
					   (fFontFlags & KXGFontBold) ? "bold" : "medium",
					   fFontSize * 10);
		fFontData = XLoadQueryFont(_GDisplay,buffer);
		if (fFontData) return fFontData->fid;
	}
	
	/*
	 *	Now try without any font names
	 */
	
	sprintf(buffer,"*%s-%s-normal--*-%d-*-*-%s*",
				   (fFontFlags & KXGFontBold) ? "bold" : "medium",
				   (fFontFlags & KXGFontItalic) ? "i" : "r",
				   fFontSize * 10,
				   (fFontFlags & KXGFontFixFont) ? "m" : "p");
	fFontData = XLoadQueryFont(_GDisplay,buffer);
	if (fFontData) return fFontData->fid;
	if (fFontFlags & KXGFontItalic) {
		/* Try oblique for italic */
		sprintf(buffer,"*%s-o-normal--*-%d-*-*-%s*",
					   deffont,
					   (fFontFlags & KXGFontBold) ? "bold" : "medium",
					   fFontSize * 10,
					   (fFontFlags & KXGFontFixFont) ? "m" : "p");
		fFontData = XLoadQueryFont(_GDisplay,buffer);
		if (fFontData) return fFontData->fid;
	}
	
	/*
	 *	If we get here, panic. Use a default font
	 */
	
	fFontData = XLoadQueryFont(_GDisplay,"*-normal-r-*-100*");
	if (fFontData) return fFontData->fid;
	return NULL;
}

/*	XGFont::GetFontStruct
 *
 *		Get the font structure for this thing
 */

XFontStruct *XGFont::GetFontStruct()
{
	GetFont();			// force loading structure
	return fFontData;
}

#endif // OPT_XWIN
