/*	XGPrinter.cpp
 *
 *		Handle the XGPrinter declaration
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/


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

#include <stdlib.h>
#include <string.h>

/************************************************************************/
/*																		*/
/*	Global Objects														*/
/*																		*/
/************************************************************************/

XGPrinter GPrinter;							// Global printer object

#if OPT_MACOS == 1

/*$	_abs
 *
 *		Absolute value. I define my own local version so I don't have
 *	to rely on the C libraries in that way...
 */

static int _abs(int x)
{
	return (x < 0) ? -x : x;
}

#endif

#if OPT_WINOS == 1

extern "C" {
	extern HWND _GMainWindow;
	extern HINSTANCE _GInstance;
}

#endif

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

/*$	XGPrinter::XGPrinter
 *
 *		Allocate memory and fill in reasonable defaults for the THPrint
 *	handle
 */

XGPrinter::XGPrinter()
{
#if OPT_MACOS == 1
	int err;
	
	/*
	 *	Allocate a print handle and initialize it, if there is a printer
	 *	port available.
	 */
	
	fHPrint = (THPrint)::NewHandle(sizeof(TPrint));
	if (fHPrint == NULL) throw XGPostError(KXGOutMemory);

	::PrOpen();								// Assure printer driver is okay
	if (0 == (err = ::PrError())) {
		fPrinter = true;
		::PrintDefault(fHPrint);			// Create a valid one of these
		::PrClose();
	} else {
		fPrinter = false;
	}
	
	fPort = NULL;
	fWindow = NULL;
#endif

#if OPT_WINOS == 1
	fPrintDC = NULL;
	
	::memset(&fPrintData,0,sizeof(PRINTDLG));
	fPrintData.lStructSize = sizeof(PRINTDLG);
	
	fPrintData.Flags |= PD_RETURNDEFAULT;
	::PrintDlg(&fPrintData);
#endif
}

/*$	XGPrinter::~XGPrinter
 *
 *		Release memory
 */

XGPrinter::~XGPrinter()
{
#if OPT_MACOS == 1
	if (fHPrint) ::DisposeHandle((Handle)fHPrint);
#endif
}

/************************************************************************/
/*																		*/
/*	XGPrinter Parameters												*/
/*																		*/
/************************************************************************/

/*$	XGPrinter::PageSize
 *
 *		Get the size of this page. This returns the boundaries of the
 *	page, which may not be the boundaries of the drawable area on the
 *	page.
 */

XRect XGPrinter::PageSize()
{
	XRect r;

#if OPT_MACOS == 1	
	if (fPrinter && fHPrint) return (**fHPrint).rPaper;
	else {
		r.left = -36;
		r.top = -36;
		r.right = 612;
		r.bottom = 792;				// 8.5 x 11
		return r;
	}
#endif

#if OPT_WINOS == 1
	if (fPrintDC) {
		r.left = 0;
		r.top = 0;
		r.right = ::GetDeviceCaps(fPrintDC,HORZRES);
		r.bottom = ::GetDeviceCaps(fPrintDC,VERTRES);
	} else {
		r.left = 0;
		r.top = 0;
		r.right = 648;
		r.bottom = 828;				// 8.5 x 11
	}
	
	return r;
#endif
}

/*$	XGPrinter::PrintSize
 *
 *		Get the drawable area of the page.
 */

XRect XGPrinter::PrintSize()
{
#if OPT_MACOS == 1
	XRect r;
	
	if (fPrinter && fHPrint) return (**fHPrint).prInfo.rPage;
	else {
		r.left = 0;
		r.top = 0;
		r.right = 576;
		r.bottom = 756;				// 8 x 10.5
		return r;
	}
#endif

#if OPT_WINOS == 1
	return PageSize();				//###TODO: Fix this 
#endif
}

/*$	XGPrinter::Resolution
 *
 *		Get the horizontal, vertical resolution of this page.
 */

XPoint XGPrinter::Resolution()
{
	XPoint pt;

#if OPT_MACOS == 1	
	if (fPrinter && fHPrint) {
		pt.h = (**fHPrint).prInfo.iHRes;
		pt.v = (**fHPrint).prInfo.iVRes;
	} else {
		pt.h = 72;
		pt.v = 72;
	}
#endif

#if OPT_WINOS == 1
	if (fPrintDC) {
		pt.h = ::GetDeviceCaps(fPrintDC,LOGPIXELSX);
		pt.v = ::GetDeviceCaps(fPrintDC,LOGPIXELSY);
	} else {
		pt.h = 72;
		pt.v = 72;
	}
#endif

	return pt;
}

/************************************************************************/
/*																		*/
/*	Set Resolutions														*/
/*																		*/
/************************************************************************/

/*$	XGPrinter::SetResolution
 *
 *		Set various resolutions for this thing. This sets the resolution
 *	of the printing context depending on the parameters provided. This
 *	only makes the best attempt to set the parameters provided. You should
 *	still call the Resolution() method to find out the actual drawing
 *	resolution.
 */

void XGPrinter::SetResolution(short flags)
{
#if OPT_MACOS == 1
	int i;
	int j;
	long max;
	long measure;
	
	TGetRslBlk getRes;
	TSetRslBlk setRes;
	
	if (!fPrinter || !fHPrint) return;
	
	getRes.iOpCode = getRslDataOp;
	getRes.iError = 0;
	getRes.lReserved = 0;
	::PrGeneral((Ptr)&getRes);
	if (getRes.iError) return;					// Not implemented
	
	setRes.iOpCode = setRslOp;
	setRes.iError = 0;
	setRes.lReserved = 0;
	setRes.hPrint = fHPrint;
	
	max = 0;									// Make compiler happy

	if (flags == KTPDefault) {
		setRes.iXRsl = 0;
		setRes.iYRsl = 0;
	} else {
		if (getRes.xRslRg.iMin == 0) {
			/*
			 *  Descrete resolution printer
			 */
		
			j = -1;
			for (i = 0; i < getRes.iRslRecCnt; i++) {
				if (flags & KTPSquareResolution) {
					if (getRes.rgRslRec[i].iXRsl != getRes.rgRslRec[i].iYRsl) continue;
				}
				if (flags & KTPMaxResolution) {
					measure = getRes.rgRslRec[i].iXRsl * getRes.rgRslRec[i].iYRsl;
					if ((j == -1) || (max < measure)) {
						j = i;
						max = measure;
					}
				} else {
					measure = _abs(72 - getRes.rgRslRec[i].iXRsl) + _abs(72 - getRes.rgRslRec[i].iYRsl);
					if ((j == -1) || (max > measure)) {
						j = i;
						max = measure;
					}
				}
			}
			
			if (j == -1) {
				setRes.iXRsl = 0;
				setRes.iYRsl = 0;
			} else {
				setRes.iXRsl = getRes.rgRslRec[j].iXRsl;
				setRes.iYRsl = getRes.rgRslRec[j].iYRsl;
			}
		} else {
			if (flags & KTPMaxResolution) {
				if (flags & KTPSquareResolution) {
					j = getRes.xRslRg.iMax;
					if (j > getRes.yRslRg.iMax) j = getRes.yRslRg.iMax;
					setRes.iXRsl = j;
					setRes.iYRsl = j;
				} else {
					setRes.iXRsl = getRes.xRslRg.iMax;
					setRes.iYRsl = getRes.yRslRg.iMax;
				}
			} else {
				setRes.iXRsl = 72;
				setRes.iYRsl = 72;
			}
		}
	}
	
	::PrGeneral((Ptr)&setRes);
#endif

#if OPT_WINOS == 1
	#ifdef __MWERKS__
		#pragma unused(flags)
	#endif
#endif
}

/************************************************************************/
/*																		*/
/*	Dialog Stuff														*/
/*																		*/
/************************************************************************/

/*$	XGPrinter::PageSetup
 *
 *		Run the page setup dialog box. Return -1 if there was a problem.
 *	This is done in responce to the "Page Setup..." menu item.
 */

bool XGPrinter::PageSetup()
{
#if OPT_MACOS == 1
	int res;
	int err;
	
	if (!fHPrint) return false;
	
	::PrOpen();
	if (0 != (err = ::PrError())) {
		throw XGPostError(KXGPrintChooser);
	}
	if (!fPrinter) ::PrintDefault(fHPrint);
	else ::PrValidate(fHPrint);
	fPrinter = true;
	
	res = ::PrStlDialog(fHPrint);
	::PrClose();
	return res ? true : false;
#endif

#if OPT_WINOS == 1
	fPrintData.lStructSize = sizeof(fPrintData);
	fPrintData.hwndOwner = _GMainWindow;
	fPrintData.Flags = PD_PRINTSETUP;
	
	return ::PrintDlg(&fPrintData) ? true : false;
#endif
}

/*$	XGPrinter::PrintJob
 *
 *		Run the print job dialog box. This returns the first page and
 *	the last page to print, or 'false' if the cancel button was pressed
 *	in the Print Job dialog box.
 *
 *		This is the first thing you would call in response to a "Print..."
 *	menu command.
 */

bool XGPrinter::PrintJob(short *first, short *last)
{
#if OPT_MACOS == 1
	int res;
	int err;
	
	if (!fHPrint) return false;
	
	::PrOpen();
	if (0 != (err = ::PrError())) {
		throw XGPostError(KXGPrintChooser);
	}
	if (!fPrinter) ::PrintDefault(fHPrint);
	else ::PrValidate(fHPrint);
	fPrinter = true;
	
	res = ::PrJobDialog(fHPrint);
	::PrClose();
	if (res) {
		*first = (**fHPrint).prJob.iFstPage;
		*last = (**fHPrint).prJob.iLstPage;
		(**fHPrint).prJob.iFstPage = 1;
		(**fHPrint).prJob.iLstPage = 9999;
		return true;
	} else {
		return false;
	}
#endif

#if OPT_WINOS == 1
	fPrintData.lStructSize = sizeof(fPrintData);
	fPrintData.hwndOwner = _GMainWindow;
	fPrintData.Flags = PD_RETURNDC | PD_NOSELECTION;
	fPrintData.nMinPage = 1;
	fPrintData.nMaxPage = 0xFFFF;
	fPrintData.nFromPage = 1;
	fPrintData.nToPage = 1;
	
	if (!::PrintDlg(&fPrintData)) return false;			// Error or cancel
	
	if (fPrintData.Flags & PD_PAGENUMS) {
		*first = fPrintData.nFromPage;
		*last = fPrintData.nToPage;
	} else {
		*first = 1;
		*last = 9999;
	}
	
	fPrintDC = fPrintData.hDC;
	
	return true;
#endif
}

/************************************************************************/
/*																		*/
/*	Page Printing Control Operations									*/
/*																		*/
/************************************************************************/

/*$	XGPrinter::StartJob
 *
 *		Start this printing job. This opens a window with a cancel message,
 *	and starts the printing process. If there is a problem printing,
 *	this would return 'false'.
 */

bool XGPrinter::StartJob(char *doc)
{
#if OPT_MACOS == 1
	#pragma unused(doc)
	
	int err;
	if (!fHPrint) return false;

	::PrOpen();
	if (0 != (err = ::PrError())) {
		throw XGPostError(KXGPrintChooser);
	}
	if (!fPrinter) ::PrintDefault(fHPrint);
	else ::PrValidate(fHPrint);
	fPrinter = true;
	
	if (NULL == (fPort = ::PrOpenDoc(fHPrint,NULL,NULL))) {
		throw XGPostError(KXGPrintError);
	}
	OpenWindow();
	return true;
#endif

#if OPT_WINOS == 1
	DOCINFO info;
	int ret;
	static char name[256];
	
	::memset(&info,0,sizeof(info));
	info.cbSize = sizeof(info);
	info.lpszDocName = (doc) ? doc : "Document";
	if (fPrintData.Flags & PD_PRINTTOFILE) {
		/*
		 *	We're printing to a file. This is in the category of
		 *	things I hate about Windows 95/Windows NT.
		 *
		 *	It turns out that with WinNT4 (and perhaps earlier),
		 *	simply passing "FILE:" to StartDoc() no longer pops
		 *	up a standard output file. So I have to run the damned
		 *	dialog myself.
		 *
		 *	Of course this is not document *anywhere* except in
		 *	the source kit for MFC v4.2 (in ViewPrint.cpp) and as
		 *	a footnote to StartDoc()...
		 */
		

		OPENFILENAME ofn;
		char buffer[64];
		char *c;
		
		memset(&ofn,0,sizeof(ofn));
		ofn.lStructSize = sizeof(ofn);
		ofn.hwndOwner = _GMainWindow;
		ofn.hInstance = _GInstance;
		
		strcpy(buffer,"Printer Files (*.prn)|*.PRN|All Files (*.*)|*.*|");
		for (c = buffer; *c; c++) if (*c == '|') *c = '\0';
		ofn.lpstrFilter = buffer;
		ofn.lpstrTitle = "Print To File";
		
		strcpy(name,"Output.prn");		
		ofn.lpstrFile = name;
		ofn.nMaxFile = 256;
		ofn.Flags = OFN_OVERWRITEPROMPT | OFN_LONGNAMES | OFN_HIDEREADONLY;

		if (!GetSaveFileName(&ofn)) return false;
		info.lpszOutput = name;
	} else {
		info.lpszOutput = NULL;
	}

	ret = ::StartDoc(fPrintDC,&info);
	if (SP_ERROR == ret) return false;
	return true;
#endif
}

/*$	XGPrinter::EndJob
 *
 *		End the printing job. If this job needs to be spooled, spool it.
 */

void XGPrinter::EndJob()
{
#if OPT_MACOS == 1
	TPrStatus status;
	OSErr err;
	
	::PrCloseDoc(fPort);
	if ((**fHPrint).prJob.bJDocLoop == bSpoolLoop) {
		::PrPicFile(fHPrint,NULL,NULL,NULL,&status);
	}
	if (fWindow) {
		::CloseWindow(fWindow);
		fWindow = NULL;
	}
	::PrClose();
	err = ::PrError();
	if (err && (err != iPrAbort)) {
		throw XGPostError(KXGPrintError);
	}
#endif

#if OPT_WINOS == 1
	int i;
	
	i = ::EndDoc(fPrintDC);
	::DeleteDC(fPrintDC);
	if (i < 0) {
		throw XGPostError(KXGPrintError);
	}
#endif
}

/*$	XGPrinter::StartPage
 *
 *		Start printing in a page. This initializes printing this page.
 *	To draw into this page, you would then create an XGDraw object.
 *	If there was a problem opening this page, for example, if the
 *	print job was canceled, this returns false.
 */

bool XGPrinter::StartPage()
{
#if OPT_MACOS == 1
	::PrOpenPage(fPort,NULL);
	return PrError() ? false : true;
#endif

#if OPT_WINOS == 1
	if (::StartPage(fPrintDC) <= 0) return false;
	return true;
#endif
}

/*$	XGPrinter::EndPage
 *
 *		End printing in a page. This puts the page into a print queue.
 */

void XGPrinter::EndPage()
{
#if OPT_MACOS == 1
	::PrClosePage(fPort);
#endif

#if OPT_WINOS == 1
	::EndPage(fPrintDC);
#endif
}

/*$	XGPrinter::GetCancelState
 *
 *		Get the current state of the cancel button. If the user
 *	hit the cancel button, this returns true.
 *
 *		TODO: Fix this on Windows.
 */

bool XGPrinter::GetCancelState()
{
#if OPT_MACOS == 1
	return ::PrError() ? true : false;
#endif

#if OPT_WINOS == 1
	return false;							// ###
#endif
}

/************************************************************************/
/*																		*/
/*	Status Window														*/
/*																		*/
/************************************************************************/

#if OPT_MACOS == 1

/*	XGPrinter::OpenWindow
 *
 *		Open a little status window in the display
 */

void XGPrinter::OpenWindow(void)
{
	Rect r;
	WindowPtr w;
	unsigned char line1[] = "\pPrinting is now in progress";
	unsigned char line2[] = "\pTo cancel, press \021+'.'";
	FontInfo fontInfo;
	int y;
	int dy;
	
	::GetWMgrPort(&w);
	r.left = (w->portRect.right + w->portRect.left - 250) / 2;
	r.right = r.left + 250;
	r.top = (w->portRect.bottom - w->portRect.top) / 3 + w->portRect.top;
	r.bottom = r.top + 50;
	
	fWindow = ::NewWindow(NULL,&r,(unsigned char *)"\p",1,dBoxProc,(WindowPtr)-1L,0,0L);
	if (fWindow != NULL) {
		::SetPort(fWindow);
		::TextFont(0);
		::TextSize(12);
		
		::GetFontInfo(&fontInfo);
		dy = fontInfo.ascent + fontInfo.descent + fontInfo.leading;
		y = (r.bottom - r.top) / 2 - fontInfo.descent;
		
		::MoveTo((r.right - r.left - ::StringWidth((unsigned char *)line1))/2,y);
		::DrawString((unsigned char *)line1);
		::MoveTo((r.right - r.left - ::StringWidth((unsigned char *)line2))/2,y+dy);
		::DrawString((unsigned char *)line2);
	}
}

#endif
