/****************************************************************************
** $Id: qt/qprinter_mac.cpp   3.3.1   edited Feb 16 16:16 $
**
** Implementation of QPrinter class for mac
**
** Created : 001019
**
** Copyright (C) 1992-2003 Trolltech AS.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses for Macintosh may use this file in accordance with the Qt Commercial
** License Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "qprinter.h"

#ifndef QT_NO_PRINTER

#include "qapplication.h"
#include "qapplication_p.h"
#include "qdir.h"
#include "qfile.h"
#include "qfileinfo.h"
#include "qmacstyle_mac.h"
#include "qpaintdevicemetrics.h"
#include "qprintdialog.h"
#include "qprinter_p.h"
#include "qpsprinter_p.h"
#include "qstyle.h"
#include "qt_mac.h"

#include <stdlib.h>


/*****************************************************************************
  External functions
 *****************************************************************************/
CFStringRef qstring2cfstring(const QString &); //qglobal.cpp

/*****************************************************************************
  QPrinter member functions
 *****************************************************************************/

// QPrinter states

#define PST_IDLE        0
#define PST_ACTIVE      1
#define PST_ERROR       2
#define PST_ABORTED     3

static inline void qt_get_margins(PMPageFormat pformat, uint *top, uint *left,
                                  uint *bottom, uint *right)
{
    PMRect paperr, pager;
    if (PMGetAdjustedPaperRect(pformat, &paperr) != noErr) {
        qWarning("Qt: QPrinter: Unexpected condition reached %s:%d", __FILE__, __LINE__);
        return;
    }
    if (PMGetAdjustedPageRect(pformat, &pager) != noErr) {
	qWarning("Qt: QPrinter: Unexpected condition reached %s:%d", __FILE__, __LINE__);
	return;
    }
    if(top)
	*top = (uint)(pager.top - paperr.top);
    if(left)
	*left = (uint)(pager.left - paperr.left);
    if(bottom)
	*bottom = (uint)(paperr.bottom - pager.bottom);
    if(right)
	*right = (uint)(paperr.right - pager.right);
}

QPrinter::QPrinter(PrinterMode m) : QPaintDevice(QInternal::Printer | QInternal::ExternalDevice)
{
    d = new QPrinterPrivate;
    if(PMCreateSession(&psession) != noErr)
	psession = NULL;

    switch(m) {
    case Compatible:
	devFlags |= QInternal::CompatibilityMode;
	// fall through
    case PrinterResolution:
    case HighResolution: {
	bool found = FALSE;
	PMPrinter printer = 0;
        if(psession && PMSessionGetCurrentPrinter(psession, &printer) == noErr) {
	    PMResolution pres;
	    UInt32 count = 0, maxRes = 0;
	    if (PMPrinterGetPrinterResolutionCount( printer, &count ) == noErr && count)
		for( ; count > 0; --count )
		    if (PMPrinterGetIndexedPrinterResolution( printer, count, &pres ) == noErr) {
			found = TRUE;
			maxRes = QMAX( (uint)pres.vRes, maxRes );
			res = maxRes;
		    }
	}
	if(!found)
	    res = 600; //just to have something
	break;
    }
    case ScreenResolution: {
	short vr, hr;
	ScreenRes(&hr, &vr);
	res = vr;
	break; }
    }

    //other
    orient = Portrait;
    page_size = A4;
    page_order = FirstPageFirst;
    paper_source = OnlyOne;
    color_mode = GrayScale;
    ncopies = 1;
    from_pg = to_pg = min_pg = max_pg = 0;
    state = PST_IDLE;
    output_file = FALSE;
    to_edge     = FALSE;

    //mac specific
    pformat = kPMNoPageFormat;
    psettings = kPMNoPrintSettings;
    prepare(&pformat);
    prepare(&psettings);
    interpret(&pformat);
    interpret(&psettings);
    setPrintRange( AllPages );

    d->printerOptions = 0;
    setOptionEnabled( PrintToFile, TRUE );
    setOptionEnabled( PrintPageRange, TRUE );
    setPrintRange( AllPages );
}

QPrinter::~QPrinter()
{
#if 0
    if(pformat != kPMNoPageFormat)
        PMDisposePageFormat(pformat);
    if(psettings != kPMNoPrintSettings)
        PMDisposePrintSettings(psettings);
#endif
}


bool QPrinter::newPage()
{
    if(state != PST_ACTIVE)
	return FALSE;
    if(PMSessionEndPage(psession) != noErr)  {//end the last page
        state = PST_ERROR;
        return FALSE;
    }

    if(PMSessionBeginPage(psession, pformat, 0) != noErr)  { //start a new one
        state = PST_ERROR;
        return FALSE;
    }
    if(PMSessionGetGraphicsContext(psession, kPMGraphicsContextQuickdraw, &hd) != noErr) {
	state = PST_ERROR;
	return FALSE;
    }

    if (fullPage()) {
	uint top, left, bottom, right;
	qt_get_margins(pformat, &top, &left, &bottom, &right);
	QMacSavedPortInfo mp(this);
	SetOrigin(top, left);
    } else {
	QMacSavedPortInfo mp(this);
	SetOrigin(0, 0);
    }
    return TRUE;
}


bool QPrinter::abort()
{
    if(state != PST_ACTIVE)
	return FALSE;
    if(PMSessionEndPage(psession) == noErr && 
       PMSessionEndDocument(psession) == noErr) {
	hd = NULL;
        state = PST_ABORTED;
    }
    return aborted();
}

bool QPrinter::aborted() const
{
    return (state == PST_ABORTED);
}

bool
QPrinter::prepare(PMPrintSettings *s)
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;
    if(*s == kPMNoPrintSettings) {
        if(PMCreatePrintSettings(s) != noErr)
            return FALSE;
        if(PMSessionDefaultPrintSettings(psession, *s) != noErr)
            return FALSE;
    } else {
        if(PMSessionValidatePrintSettings(psession, *s, kPMDontWantBoolean) != noErr )
            return FALSE;
    }
    PMSetPageRange(*s, minPage()+1, maxPage()+1);
    PMSetFirstPage(*s, fromPage(), TRUE);
    PMSetLastPage(*s, toPage(), TRUE);
    PMSetColorMode(*s, colorMode() == GrayScale ? kPMGray : kPMColor);
    PMSetCopies(*s, numCopies(), TRUE);
    if(outputToFile()) {
	CFStringRef cfstring = qstring2cfstring(outputFileName());
	CFURLRef outFile = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cfstring, 
							 kCFURLPOSIXPathStyle, false);
	PMSessionSetDestination(psession, *s, kPMDestinationFile, kPMDocumentFormatPDF, outFile);
	CFRelease(cfstring);
    }
    return TRUE;
}

bool
QPrinter::prepare(PMPageFormat *f)
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;
    if(*f == kPMNoPageFormat) {
        if(PMCreatePageFormat(f) != noErr)
            return FALSE;
        if(PMSessionDefaultPageFormat(psession, *f) != noErr)
            return FALSE;
    } else {
        if(PMSessionValidatePageFormat(psession, *f,kPMDontWantBoolean) != noErr)
            return FALSE;
    }
    PMSetOrientation(*f, orientation() == Portrait ? kPMPortrait : kPMLandscape, TRUE);
    PMResolution pres;
    pres.hRes = res;
    pres.vRes = res;
    PMSetResolution(*f, &pres);
    return TRUE;
}


void QPrinter::setPrinterName(const QString &name)
{
    if (state != 0) {
#if defined(QT_CHECK_STATE)
        qWarning("Qt: QPrinter::setPrinterName: Cannot do this during printing");
#endif
        return;
    }
    printer_name = name;
}

bool QPrinter::setup(QWidget *)
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;
    if(::qt_cast<QMacStyle *>(&qApp->style())) {
	return (printSetup() && pageSetup());
    } else if(QPrintDialog::getPrinterSetup(this)) {
        if(!prepare(&pformat) || !prepare(&psettings))
            return FALSE;
        return TRUE;
    }
    return FALSE;
}

void QPrinter::interpret(PMPrintSettings *s)
{
    //get values
    UInt32 from, to;
    if(PMGetFirstPage(*s, &from) == noErr && PMGetLastPage(*s, &to) == noErr) {
	if(to == INT_MAX)  //this means all pages!
	    from = to = 0;
	setFromTo(from, to);
    }

    UInt32 copies;
    if(PMGetCopies(*s, &copies) == noErr)
	setNumCopies(copies);

    UInt32 max, min;
    if(PMGetPageRange(*s, &min, &max) == noErr)
	setMinMax(min-1, max-1);

    PMColorMode cm;
    if(PMGetColorMode(*s, &cm) == noErr)
	setColorMode(cm == kPMGray ? GrayScale : Color);
}

void QPrinter::interpret(PMPageFormat *f)
{
    //get values
    PMOrientation o;
    if(PMGetOrientation(*f, &o) == noErr)
	setOrientation(o == kPMPortrait ? Portrait : Landscape);

    //Finally we update the scale so the resolution is effected by it
    PMSessionValidatePageFormat(psession, *f, kPMDontWantBoolean);
}

// shows the native mac print setup dialog
/*!
    \internal
*/
bool QPrinter::printSetup()
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;
    if(::qt_cast<QMacStyle *>(&qApp->style())) {
        Boolean ret;
	QMacBlockingFunction block;
        //setup
        if(!prepare(&psettings))
            return FALSE;
        if(!outputToFile() && PMSessionPrintDialog(psession, psettings, pformat, &ret) != noErr || !ret )
            return FALSE;
	interpret(&psettings);
	return TRUE;
    }
    return FALSE;
}

// shows the native mac page setup dialog
/*!
    \internal
*/
bool QPrinter::pageSetup()
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;
    if(::qt_cast<QMacStyle *>(&qApp->style())) {
        Boolean ret;
	QMacBlockingFunction block;
        //page format
        if(!prepare(&pformat))
            return FALSE;
        if(PMSessionPageSetupDialog(psession, pformat, &ret) != noErr || !ret)
            return FALSE;
	interpret(&pformat);
        return TRUE;
    }
    return FALSE;
}


bool QPrinter::cmd(int c, QPainter *, QPDevCmdParam *)
{
    if(!psession && PMCreateSession(&psession) != noErr)
        return FALSE;

    if (c ==  PdcBegin) {                     // begin; start printing
        if(state != PST_IDLE) {
            qDebug("Qt: internal: printer: two PdcBegin(s).");
            return FALSE;
        }

	//just to be sure they've been setup
	prepare(&pformat);
	prepare(&psettings);

        //validate the settings
        if(PMSessionValidatePrintSettings(psession, psettings, kPMDontWantBoolean) != noErr)
            return FALSE;
        if(PMSessionValidatePageFormat(psession, pformat, kPMDontWantBoolean) != noErr)
            return FALSE;

        if(PMSessionBeginDocument(psession, psettings, pformat) != noErr) //begin the document
            return FALSE;


        if(PMSessionBeginPage(psession, pformat, 0) != noErr) //begin the page
            return FALSE;
        if(PMSessionGetGraphicsContext(psession, kPMGraphicsContextQuickdraw,
					&hd) != noErr) //get the gworld
            return FALSE;
        state = PST_ACTIVE;
        if (fullPage()) {
            uint top, left, bottom, right;
            qt_get_margins(pformat, &top, &left, &bottom, &right);
	    QMacSavedPortInfo mp(this);
	    SetOrigin(top, left);
	} else {
	    QMacSavedPortInfo mp(this);
	    SetOrigin(0, 0);
	}
    } else if(c == PdcEnd) {
        if(hd && state != PST_IDLE) {
            PMSessionEndPage(psession);
            PMSessionEndDocument(psession);
            hd = NULL;
        }
        state  = PST_IDLE;
    } else {                                    // all other commands...
        if(state == PST_ABORTED || ((state == PST_ACTIVE || state == PST_ERROR ) && PMSessionError(psession) != noErr))
            return FALSE;
    }
    return TRUE;
}

static inline int qt_get_PDMWidth(PMPageFormat pformat, bool fullPage)
{
    int val;
    if(fullPage) {
	PMRect r;
	if(PMGetAdjustedPaperRect(pformat, &r) == noErr)
	    val = (int)(r.right - r.left);
    } else {
	PMRect r;
	if(PMGetAdjustedPageRect(pformat, &r) == noErr)
	    val = (int)(r.right - r.left);
    }
    return val;
}

static inline int qt_get_PDMHeight(PMPageFormat pformat, bool fullPage)
{
    int val;
    if(fullPage) {
	PMRect r;
	if(PMGetAdjustedPaperRect(pformat, &r) == noErr)
	    val = (int)(r.bottom - r.top);
    } else {
	PMRect r;
	if(PMGetAdjustedPageRect(pformat, &r) == noErr)
	    val = (int)(r.bottom - r.top);
    }
    return val;
}

int QPrinter::metric(int m) const
{
    int val = 1;
    switch(m) {
    case QPaintDeviceMetrics::PdmWidth:
    {
	    val = qt_get_PDMWidth(pformat, fullPage());
	if (state == PST_ACTIVE || orientation() == Portrait)
	    val = qt_get_PDMWidth(pformat, fullPage());
	else
	    val = qt_get_PDMHeight(pformat, fullPage());
        break;
    }
    case QPaintDeviceMetrics::PdmHeight:
    {
	    val = qt_get_PDMHeight(pformat, fullPage());
	if (state == PST_ACTIVE || orientation() == Portrait)
	    val = qt_get_PDMHeight(pformat, fullPage());
	else
	    val = qt_get_PDMWidth(pformat, fullPage());
        break;
    }
    // We don't have to worry about the printer state here as metric() does that for us.
    case QPaintDeviceMetrics::PdmWidthMM:
        val = metric(QPaintDeviceMetrics::PdmWidth);
        val = (val * 254 + 5*res) / (10*res);
        break;
    case QPaintDeviceMetrics::PdmHeightMM:
        val = metric(QPaintDeviceMetrics::PdmHeight);
        val = (val * 254 + 5*res) / (10*res);
        break;
    case QPaintDeviceMetrics::PdmPhysicalDpiX:
    case QPaintDeviceMetrics::PdmPhysicalDpiY: {
	PMPrinter printer;
	if(PMSessionGetCurrentPrinter(psession, &printer) == noErr) {
	    PMResolution resolution;
	    PMPrinterGetPrinterResolution(printer, kPMCurrentValue, &resolution);
	    val = (int)resolution.vRes;
	    break;
	}
	//otherwise fall through
    }
    case QPaintDeviceMetrics::PdmDpiY:
    case QPaintDeviceMetrics::PdmDpiX:
	val = res;
	break;
    case QPaintDeviceMetrics::PdmNumColors:
        val = (1 << metric(QPaintDeviceMetrics::PdmDepth));
        break;
    case QPaintDeviceMetrics::PdmDepth:
        val = 24;
        break;
    default:
        val = 0;
#if defined(QT_CHECK_RANGE)
        qWarning("Qt: QPixmap::metric: Invalid metric command");
#endif
    }
    return val;
}


QSize QPrinter::margins() const
{
    uint t, l, r, b;
    // If the printer is not active, we need to flip the values for the landscape case.
    margins(&t, &l, &r, &b);

    if (state == PST_ACTIVE || orientation() == Portrait)
	return QSize(t, l);
    else
	return QSize(r, t);
}

void QPrinter::setMargins(uint, uint, uint, uint)
{
}

void QPrinter::margins(uint *top, uint *left, uint *bottom, uint *right) const
{
    // If the printer is not active, we need to flip the values for the landscape case.
    if (state == PST_ACTIVE || orientation() == Portrait)
	qt_get_margins(pformat, top, left, bottom, right);
    else
	qt_get_margins(pformat, right, top, left, bottom);
}


#endif
