/*++

Copyright (C) Microsoft Corporation, 1995 - 1999
All rights reserved.

Module Name:

    docprop.cxx

Abstract:

    Job Properties

Author:

    Steve Kiraly (SteveKi)  10/19/95

Revision History:
    
    Lazar Ivanov (LazarI) - added DocumentPropertiesWrap (Nov-03-2000)

--*/
#include "precomp.hxx"
#pragma hdrstop

#include "time.hxx"
#include "docdata.hxx"
#include "propmgr.hxx"
#include "docprop.hxx"


/*++

Routine Name:

    vDocPropSelections

Routine Description:

    Displays Document property sheets for multiple selections.

Arguments:

    TSelection - pointer to a list of document selections.

Return Value:

    Nothing.

--*/

VOID
vDocumentPropSelections(
    IN HWND         hWnd,
    IN LPCTSTR      pszPrinterName,
    IN TSelection  *pSelection
    )
{

    //
    // Get the selection information.  We are in a loop to
    // handle the selection of multiple jobs.
    //
    for( UINT i = 0; i < pSelection->_cSelected; ++i ){
        //
        // Display the document property pages.
        //
        vDocumentPropPages(
            hWnd,
            pszPrinterName,
            pSelection->_pid[i],
            SW_SHOWNORMAL,
            0 );
    }

}

/*++

Routine Description:

    This function opens the property sheet of specified document.

    We can't guarentee that this propset will perform all lengthy
    operations in a worker thread (we synchronously call things like
    ConfigurePort).  Therefore, we spawn off a separate thread to
    handle document properties.

Arguments:

    hWnd        - Specifies the parent window (optional).
    pszPrinter  - Specifies the printer name
    nCmdShow    - Initial show state
    lParam      - May spcify a sheet specifc index to directly open.

Return Value:

--*/
VOID
vDocumentPropPages(
    IN HWND     hWnd,
    IN LPCTSTR  pszPrinterName,
    IN IDENT    JobId,
    IN INT      iCmdShow,
    IN LPARAM   lParam
    )

{
    HANDLE hThread;

    //
    // Create the document specific data
    //
    TDocumentData* pDocumentData = new TDocumentData( pszPrinterName,
                                                      JobId,
                                                      iCmdShow,
                                                      lParam );
    //
    // If errors were encountered creating document data.
    //
    if( !VALID_PTR( pDocumentData )){
        goto Fail;
    }

    //
    // Create the thread which handles the UI.  vPrinterPropPages adopts
    // pPrinterData, therefore only on thread creation failure do we
    // releae the document data back to the heap.
    //
    DWORD dwIgnore;
    hThread = TSafeThread::Create( NULL,
                                   0,
                                   (LPTHREAD_START_ROUTINE)iDocumentPropPagesProc,
                                   pDocumentData,
                                   0,
                                   &dwIgnore );

    //
    // Check thread creation.
    //
    if( !hThread ){

        //
        // Display error message, and release document data.
        //
        vShowResourceError( hWnd );
        delete pDocumentData;

    } else {

        CloseHandle( hThread );
    }

    return;

Fail:

    //
    // Display the error message.
    //
    iMessage( hWnd,
              IDS_ERR_DOC_JOB_PROPERTY_TITLE,
              IDS_ERR_DOC_JOB_PROPERTY_JOB_NA,
              MB_OK|MB_ICONSTOP,
              kMsgGetLastError,
              NULL );

    delete pDocumentData;
}

/*++

Routine Name:

    iDocumentPropPagesProc

Routine Description:

    This is the routine called by the create thread call to display the
    document property sheets.

Arguments:

    pDocumentData - Pointer to the document data needed for all property sheets.

Return Value:

    TRUE - if the property sheets were displayed.
    FALSE - error creating and displaying property sheets.

--*/

INT
iDocumentPropPagesProc(
    IN TDocumentData *pDocumentData ADOPT
    )
{
    DBGMSG( DBG_TRACE, ( "iDocumentPropPagesProc\n") );

    BOOL bStatus;
    bStatus = pDocumentData->bRegisterWindow( PRINTER_PIDL_TYPE_JOBID |
                                                  pDocumentData->JobId( ));
    if( bStatus ){

        //
        // Check if the window is already present.  If it is, then
        // exit immediately.
        //
        if( !pDocumentData->hwnd( )){
            delete pDocumentData;
            return 0;
        }

        bStatus = pDocumentData->bLoad();
    }

    if( !bStatus ){

        iMessage( pDocumentData->hwnd(),
                  IDS_ERR_DOC_JOB_PROPERTY_TITLE,
                  IDS_ERR_DOC_JOB_PROPERTY_JOB_NA,
                  MB_OK|MB_ICONSTOP|MB_SETFOREGROUND,
                  kMsgGetLastError,
                  NULL );

        delete pDocumentData;
        return 0;
    }

    //
    // Create the ducument property sheet windows.
    //
    TDocumentWindows DocumentWindows( pDocumentData );

    //
    // Were the document windows create
    //
    if( !VALID_OBJ( DocumentWindows ) ){
        vShowResourceError( pDocumentData->hwnd() );
        bStatus = FALSE;
    }

    //
    // Display the property pages.
    //
    if( bStatus ){
        if( !DocumentWindows.bDisplayPages( pDocumentData->hwnd() ) ){
            vShowResourceError( pDocumentData->hwnd() );
            bStatus = FALSE;
        }
    }

    //
    // Ensure we release the document data.
    // We have adopted pPrinterData, so we must free it.
    //
    delete pDocumentData;
    return bStatus;

}

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

    Document Prop Base Class

********************************************************************/
/*++

Routine Name:

    TDocumentProp

Routine Description:

    Initialized the document property sheet base class

Arguments:

    pDocumentData - Pointer to the document data needed for all property sheets.

Return Value:

    None.

--*/
TDocumentProp::
TDocumentProp(
    TDocumentData* pDocumentData
    ) : _pDocumentData( pDocumentData ),
        _bApplyData( FALSE )
{
}

/*++

Routine Name:

    ~TDocumentProp

Routine Description:

    Base class desctuctor.

Arguments:

    None.

Return Value:

    Nothing.

--*/
TDocumentProp::
~TDocumentProp(
    )
{
}

/*++

Routine Name:

    bHandleMessage

Routine Description:

    Base class message handler.  This routine is called by
    derived classes who do not want to handle the message.


Arguments:

    uMsg    - Windows message
    wParam  - Word parameter
    lParam  - Long parameter


Return Value:

    TRUE if message was handled, FALSE if message not handled.

--*/
BOOL
TDocumentProp::
bHandleMessage(
    IN UINT uMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    BOOL bStatus = TRUE;

    switch( uMsg ){

    //
    // Set the values on the UI.
    //
    case WM_INITDIALOG:
        bStatus = bSetUI();
        break;

    //
    // Handle help and context help.
    //
    case WM_HELP:
    case WM_CONTEXTMENU:
        bStatus = PrintUIHelp( uMsg, _hDlg, wParam, lParam );
        break;

    case WM_NOTIFY:
        switch( ((LPNMHDR)lParam)->code )
        {
            //
            // User switched to the next page.
            //
            case PSN_KILLACTIVE:
                bStatus = bReadUI();
                vSetDlgMsgResult( !bStatus ? TRUE : FALSE );
                break;

            //
            // User chiose the apply button.
            //
            case PSN_APPLY:
                _bApplyData = TRUE;
                bStatus = ( _bApplyData ) ? bSaveUI() : FALSE;
                if( !bStatus )
                {
                    //
                    // Switch to page with the error.
                    //
                    PropSheet_SetCurSelByID( GetParent( _hDlg ), DLG_DOC_JOB_GENERAL );
                }
                vSetDlgMsgResult( !bStatus ? PSNRET_INVALID_NOCHANGEPAGE : PSNRET_NOERROR );
                break;

            //
            // Indicate the use canceled the dialog.
            //
            case PSN_QUERYCANCEL:
                _bApplyData = FALSE;
                break;

            default:
                bStatus = FALSE;
                break;
        }
        break;

    default:
        bStatus = FALSE;
        break;
    }

    if( !bStatus )
    {
        //
        // Allow the derived classes to handle the message.
        //
        bStatus = _bHandleMessage( uMsg, wParam, lParam );
    }

    //
    // If the message was handled check if the 
    // apply button should be enabled.
    //
    if( bStatus )
    {
        if( _pDocumentData->bCheckForChange() )
        {
            PropSheet_Changed( GetParent( _hDlg ), _hDlg );
        }
        else
        {
            PropSheet_UnChanged( GetParent( _hDlg), _hDlg );
        }
    }

    return bStatus;
}


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

    General Document Property Sheet.

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

/*++

Routine Name:

    TDocumentGeneral

Routine Description:

    Document property sheet derived class.

Arguments:

    None.

Return Value:

    Nothing.

--*/
TDocumentGeneral::
TDocumentGeneral(
    IN TDocumentData* pDocumentData
    ) : TDocumentProp( pDocumentData ),
        _bSetUIDone( FALSE )
{
}

/*++

Routine Name:

    ~TDocumentGeneral

Routine Description:

    Document derived class destructor.

Arguments:

    None.

Return Value:

    Nothing.

--*/

TDocumentGeneral::
~TDocumentGeneral(
    )
{
}

/*++

Routine Name:

    bValid

Routine Description:

    Document property sheet derived class valid object indicator.

Arguments:

    None.

Return Value:

    Returns the status of the base class.

--*/
BOOL
TDocumentGeneral::
bValid(
    VOID
    )
{
    return TDocumentProp::bValid();
}

/*++

Routine Name:

    bSetStartAndUntilTime

Routine Description:

    Initialized the start and until time.

Arguments:

    None.

Return Value:

    TRUE time controls initialized, FALSE error occured.

--*/
BOOL
TDocumentGeneral::
bSetStartAndUntilTime(
    VOID
    )
{
    TString strFormatString;
    TStatusB bStatus;

    //
    // Get the time format string without seconds.
    //
    bStatus DBGCHK = bGetTimeFormatString( strFormatString );

    //
    // If we have retrived a valid time format string then use it, 
    // else use the default format string implemented by common control.
    //
    if( bStatus )
    {
        DateTime_SetFormat(GetDlgItem( _hDlg, IDC_DOC_JOB_START_TIME ), static_cast<LPCTSTR>( strFormatString ) );
        DateTime_SetFormat(GetDlgItem( _hDlg, IDC_DOC_JOB_UNTIL_TIME ), static_cast<LPCTSTR>( strFormatString ) );
    }

    //
    // If the printer is always available.
    //
    BOOL bAlways = ( _pDocumentData->pJobInfo()->StartTime == _pDocumentData->pJobInfo()->UntilTime );

    //
    // Set the start time.
    //
    SYSTEMTIME  StartTime           = { 0 };
    DWORD       dwLocalStartTime    = 0;

    GetLocalTime( &StartTime );

    dwLocalStartTime = ( !bAlways ) ? SystemTimeToLocalTime( _pDocumentData->pJobInfo()->StartTime ) : 0;

    StartTime.wHour     = static_cast<WORD>( dwLocalStartTime / 60 );
    StartTime.wMinute   = static_cast<WORD>( dwLocalStartTime % 60 );

    DateTime_SetSystemtime(GetDlgItem( _hDlg, IDC_DOC_JOB_START_TIME ), GDT_VALID, &StartTime );

    //
    // Set the until time.
    //
    SYSTEMTIME  UntilTime           = { 0 };
    DWORD       dwLocalUntilTime    = 0;

    GetLocalTime( &UntilTime );

    dwLocalUntilTime = ( !bAlways ) ? SystemTimeToLocalTime( _pDocumentData->pJobInfo()->UntilTime ) : 0;

    UntilTime.wHour     = static_cast<WORD>( dwLocalUntilTime / 60 );
    UntilTime.wMinute   = static_cast<WORD>( dwLocalUntilTime % 60 );

    DateTime_SetSystemtime(GetDlgItem( _hDlg, IDC_DOC_JOB_UNTIL_TIME ), GDT_VALID, &UntilTime );

    return TRUE;
}

/*++

Routine Name:

    bSetUI

Routine Description:

    Loads the property sheet dialog with the document data
    information.

Arguments:

    None.

Return Value:

    TRUE if data loaded successfully, FALSE if error occurred.

--*/
BOOL
TDocumentGeneral::
bSetUI(
    VOID
    )

{
    //
    // Get the flag if always availble.
    //
    BOOL bAlways = ( _pDocumentData->pJobInfo()->StartTime ==
                     _pDocumentData->pJobInfo()->UntilTime );

    //
    // Initialize the stat and until time controls.
    //
    if( !bSetStartAndUntilTime() )
    {
        DBGMSG( DBG_TRACE, ( "TDocumentGeneral::bSetStartAndUntilTime failed %d\n", GetLastError( )));
    }

    //
    // Read the job size format string.
    //
    TString strFormat;
    if( strFormat.bLoadString( ghInst, IDS_JOB_SIZE ) ){

        //
        // Set the size in byes of the job.
        //
        bSetEditTextFormat( _hDlg,
                            IDC_DOC_JOB_SIZE,
                            strFormat,
                            _pDocumentData->pJobInfo()->Size );
    }

    //
    // Set the Number of pages in the job.
    //
    bSetEditTextFormat( _hDlg,
                        IDC_DOC_JOB_PAGES,
                        TEXT( "%d" ),
                        _pDocumentData->pJobInfo()->TotalPages );

    //
    // Set the document text.
    //
    bSetEditText( _hDlg, IDC_DOC_JOB_TITLE,      _pDocumentData->pJobInfo()->pDocument );
    bSetEditText( _hDlg, IDC_DOC_JOB_DATATYPE,   _pDocumentData->pJobInfo()->pDatatype );
    bSetEditText( _hDlg, IDC_DOC_JOB_PROCCESSOR, _pDocumentData->pJobInfo()->pPrintProcessor );
    bSetEditText( _hDlg, IDC_DOC_JOB_OWNER,      _pDocumentData->pJobInfo()->pUserName );
    bSetEditText( _hDlg, IDC_DOC_JOB_NOTIFY,     _pDocumentData->pJobInfo()->pNotifyName );

    //
    // Set the Priority indicator.
    //
    bSetEditTextFormat( _hDlg,
                        IDC_DOC_JOB_PRIORITY,
                        TEXT( "%d" ),
                        _pDocumentData->pJobInfo()->Priority );

    SendDlgItemMessage( _hDlg,
                        IDC_DOC_JOB_PRIORITY_CONTROL,
                        TBM_SETRANGE,
                        FALSE,
                        MAKELONG( TDocumentData::kPriorityLowerBound, TDocumentData::kPriorityUpperBound ));

    SendDlgItemMessage( _hDlg,
                        IDC_DOC_JOB_PRIORITY_CONTROL,
                        TBM_SETPOS,
                        TRUE,
                        _pDocumentData->pJobInfo()->Priority );

    //
    // Format the submitted time field.
    //
    TStatusB bStatus = FALSE;
    TCHAR szBuff[kStrMax] = {0};
    SYSTEMTIME LocalTime;

    //
    // Convert to local time.
    //
    bStatus DBGCHK = SystemTimeToTzSpecificLocalTime(
                        NULL,
                        &_pDocumentData->pJobInfo()->Submitted,
                        &LocalTime );
    if( !bStatus ){
        DBGMSG( DBG_MIN, ( "SysTimeToTzSpecLocalTime failed %d\n", GetLastError( )));
    }

    if( bStatus ){

        //
        // Convert using local format information.
        //
        bStatus DBGCHK = GetTimeFormat( LOCALE_USER_DEFAULT,
                            0,
                            &LocalTime,
                            NULL,
                            szBuff,
                            COUNTOF( szBuff ));
        if( !bStatus ){
            DBGMSG( DBG_MIN, ( "No Time %d, ", GetLastError( )));
        }
    }

    if( bStatus ){

        //
        // Tack on space between time and date.
        //
        lstrcat( szBuff, TEXT("  ") );

        //
        // Get data format.
        //
        bStatus DBGCHK = GetDateFormat( LOCALE_USER_DEFAULT,
                            0,
                            &LocalTime,
                            NULL,
                            szBuff + lstrlen( szBuff ),
                            COUNTOF( szBuff ) - lstrlen( szBuff ) );

        if( !bStatus ){
            DBGMSG( DBG_MIN, ( "No Date %d\n", GetLastError( )));
        }
    }

    //
    // Set the submitted  field
    //
    bStatus DBGCHK = bSetEditText( _hDlg, IDC_DOC_JOB_AT, szBuff );

    //
    // Set schedule radio buttons.
    //
    CheckRadioButton( _hDlg, IDC_DOC_JOB_START, IDC_DOC_JOB_ALWAYS,
        bAlways ? IDC_DOC_JOB_ALWAYS : IDC_DOC_JOB_START );

    vEnableAvailable( !bAlways );

    //
    // Disable all the controls if not an administrator.
    //
    if( !_pDocumentData->bAdministrator( )){

        //
        // Disable the time controls.
        //
        vEnableAvailable( FALSE );

        //
        // Disable things if not administrator.
        //
        static UINT auAvailable[] = {
            IDC_DOC_JOB_NOTIFY,
            IDC_DOC_JOB_PRIORITY_CONTROL,
            IDC_DOC_JOB_ALWAYS,
            IDC_DOC_JOB_START,
        };

        COUNT i;
        for( i = 0; i < COUNTOF( auAvailable ); ++i ){
            vEnableCtl( _hDlg, auAvailable[i], FALSE );
        }
    }

    _bSetUIDone = TRUE;

    return TRUE;
}

/*++

Routine Name:

    bReadUI

Routine Description:

    Stores the property information to the print server.

Arguments:

    Nothing data is contained with in the class.

Return Value:

    TRUE if data is stores successfully, FALSE if error occurred.

--*/
BOOL
TDocumentGeneral::
bReadUI(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TDocumentGeneral::bReadUI\n") );

    //
    // Attempt to validate any UI changeable data.
    // Currently not much can be validated, since all the
    // controls have set constraints.
    //

    //
    // Extract the UI and save it into the Document Data.
    //
    _pDocumentData->pJobInfo()->Priority = (DWORD)SendDlgItemMessage( _hDlg,
                        IDC_DOC_JOB_PRIORITY_CONTROL,
                        TBM_GETPOS,
                        0,
                        0 );
    //
    // Get the notify name.
    //
    bGetEditText( _hDlg, IDC_DOC_JOB_NOTIFY, _pDocumentData->strNotifyName() );
    _pDocumentData->pJobInfo()->pNotifyName = (LPTSTR)(LPCTSTR)_pDocumentData->strNotifyName();

    //
    // If the Job always is set then indicate
    // not time restriction in the start time and until time.
    //
    if( bGetCheck( _hDlg, IDC_DOC_JOB_ALWAYS ) ){

        _pDocumentData->pJobInfo()->StartTime = 0;
        _pDocumentData->pJobInfo()->UntilTime = 0;

    } else {

        //
        // Get the Start time.
        //
        SYSTEMTIME StartTime;
        DateTime_GetSystemtime( GetDlgItem( _hDlg, IDC_DOC_JOB_START_TIME ), &StartTime );
        _pDocumentData->pJobInfo()->StartTime = LocalTimeToSystemTime( StartTime.wHour * 60 + StartTime.wMinute );

        //
        // Get the Until time.
        //
        SYSTEMTIME UntilTime;
        DateTime_GetSystemtime( GetDlgItem( _hDlg, IDC_DOC_JOB_UNTIL_TIME ), &UntilTime );
        _pDocumentData->pJobInfo()->UntilTime = LocalTimeToSystemTime( UntilTime.wHour * 60 + UntilTime.wMinute );

        //
        // If the printer start and until time are the same this is 
        // exactly the same as always available.
        //
        if( _pDocumentData->pJobInfo()->StartTime == _pDocumentData->pJobInfo()->UntilTime )
        {
            _pDocumentData->pJobInfo()->StartTime = 0;
            _pDocumentData->pJobInfo()->UntilTime = 0;
        }
    }

    //
    // Set the job position to unspecified it may 
    // have changed while this dialog was up.
    //
    _pDocumentData->pJobInfo()->Position =  JOB_POSITION_UNSPECIFIED;

    return TRUE;
}

/*++

Routine Name:

    bSaveUI

Routine Description:

    Saves the UI data to some API call or print server.

Arguments:

    Nothing data is contained with in the class.

Return Value:

    TRUE if data is stores successfully, FALSE if error occurred.

--*/
BOOL
TDocumentGeneral::
bSaveUI(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TDocumentGeneral::bSaveUI\n") );

    //
    // Clear the error saving flag.
    // 
    _pDocumentData->bErrorSaving() = TRUE;

    //
    // Save the document data.
    //
    if( !_pDocumentData->bStore() ){

        DWORD dwLastError = GetLastError ();

        if( dwLastError == ERROR_INVALID_TIME ){
            _pDocumentData->iErrorMsgId() = IDS_ERR_DOC_JOB_PROPERTY_TIME;
        } else {
            _pDocumentData->iErrorMsgId() = IDS_ERR_DOC_JOB_PROPERTY_MODIFY;
        }
        _pDocumentData->bErrorSaving() = FALSE;
    }

    //
    // If there was an error saving the document data.
    //
    if( !_pDocumentData->bErrorSaving() ){

        //
        // Display the error message.
        //
        iMessage( _hDlg,
                  IDS_ERR_DOC_JOB_PROPERTY_TITLE,
                  _pDocumentData->iErrorMsgId(),
                  MB_OK|MB_ICONSTOP|MB_SETFOREGROUND,
                  kMsgNone,
                  NULL );

        return FALSE;
    }

    return TRUE;
}

/*++

Routine Name:

    vEanbleAvailablity

Routine Description:

    Enables the time availabilty of the job

Arguments:

    TRUE enable the availablity, FALSE disable time availablity.

Return Value:

    Nothing.

--*/
VOID
TDocumentGeneral::
vEnableAvailable(
    IN BOOL bEnable
    )
{
    vEnableCtl( _hDlg, IDC_DOC_JOB_START_TIME, bEnable );
    vEnableCtl( _hDlg, IDC_DOC_JOB_UNTIL_TIME, bEnable );
}

/*++

Routine Name:

    bReadUI

Routine Description:

    Stores the property information to the print server.

Arguments:

    Nothing data is contained with in the class.

Return Value:

    TRUE if data is stores successfully, FALSE if error occurred.

--*/
VOID
TDocumentGeneral::
vSetActive(
    VOID
    )
{
    DBGMSG( DBG_TRACE, ( "TDocumentGeneral::vSetActive\n") );
    (VOID)bSetStartAndUntilTime();
}

/*++

Routine Name:

    bHandleMessage

Routine Description:

    Document property sheet message handler.  This handler only
    handles events it wants and the base class handle will do the
    standard message handling.

Arguments:

    uMsg    - Windows message
    wParam  - Word parameter
    lParam  - Long parameter


Return Value:

    TRUE if message was handled, FALSE if message not handled.

--*/

BOOL
TDocumentGeneral::
_bHandleMessage(
    IN UINT uMsg,
    IN WPARAM wParam,
    IN LPARAM lParam
    )
{
    BOOL bStatus = TRUE;

    switch( uMsg )
    {
    case WM_HSCROLL:

        //
        // Check for slider notification.
        //
        if( GET_WM_HSCROLL_HWND( wParam, lParam ) == GetDlgItem(_hDlg, IDC_DOC_JOB_PRIORITY_CONTROL ) ){
            bSetEditTextFormat( _hDlg,
                               IDC_DOC_JOB_PRIORITY,
                               TEXT("%d"),
                               SendDlgItemMessage( _hDlg,
                                                   IDC_DOC_JOB_PRIORITY_CONTROL,
                                                   TBM_GETPOS,
                                                   0,
                                                   0 ) );
        }
        break;

    case WM_COMMAND:
        switch( GET_WM_COMMAND_ID( wParam, lParam ))
        {
        case IDC_DOC_JOB_ALWAYS:
            vEnableAvailable( FALSE );
            PropSheet_Changed( GetParent( _hDlg ), _hDlg );
            break;

        case IDC_DOC_JOB_START:
            vEnableAvailable( TRUE );
            PropSheet_Changed( GetParent( _hDlg ), _hDlg );
            break;

        case IDC_DOC_JOB_NOTIFY:
            bStatus = GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE;
            break;

        default:
            bStatus = FALSE;
            break;
        }
        break;

    case WM_WININICHANGE:
        vSetActive();
        break;

    case WM_NOTIFY:
        {
            LPNMHDR pnmh = (LPNMHDR)lParam;

            switch( wParam )
            {
            case 0:
                {
                    switch( pnmh->code )
                    {
                    case PSN_SETACTIVE:
                        vSetActive();
                        break;

                    default:
                        bStatus = FALSE;
                        break;
                    }
                }
                break;
                
            case IDC_DOC_JOB_START_TIME:
            case IDC_DOC_JOB_UNTIL_TIME:
                {
                    switch( pnmh->code )
                    {
                    case DTN_DATETIMECHANGE:
                        break;

                    default:
                        bStatus = FALSE;
                        break;
                    }
                }
                break;

            default:
                bStatus = FALSE;
                break;
            }
        }
        break;
    
    default:
        bStatus = FALSE;
        break;
    }

    //
    // If message handled look for change.
    //
    if( bStatus && _bSetUIDone )
    {
        (VOID)bReadUI();
    }

    return bStatus;
}

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

    Document property windows.

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

/*++

Routine Description:

    Document property windows.

Arguments:

    pDocumentData - Document data to display.

Return Value:

    TRUE - Success, FALSE - failure.

--*/

TDocumentWindows::
TDocumentWindows(
    TDocumentData* pDocumentData
    ) : _pDocumentData( pDocumentData ),
        _General( pDocumentData )
{
    DBGMSG( DBG_TRACE, ( "TDocumentWindows ctor\n") );
}

TDocumentWindows::
~TDocumentWindows(
    )
{
    DBGMSG( DBG_TRACE, ( "TDocumentWindows dtor\n") );
}

/*++

Routine Name:

    bBuildPages

Routine Description:

    Builds the document property windows.

Arguments:

    None - class specific.

Return Value:

    TRUE pages built ok, FALSE failure building pages.

--*/

BOOL
TDocumentWindows::
bBuildPages(
    IN PPROPSHEETUI_INFO pCPSUIInfo
    )
{
    TStatusB bStatus;
    DBGMSG( DBG_TRACE, ( "TDocumentWindows bBuildPages\n") );

    //
    // Set the default activation context to be V6 prior calling into
    // compstui to create the pages. This will force V6 context unless
    // the callbacks which create the compstui pages specify otherwise
    // on a per page basis.
    //
    bStatus DBGCHK = (BOOL)pCPSUIInfo->pfnComPropSheet(
            pCPSUIInfo->hComPropSheet, 
            CPSFUNC_SET_FUSION_CONTEXT, 
            reinterpret_cast<LPARAM>(g_hActCtx),
            static_cast<LPARAM>(0));

    if (bStatus)
    {
        PROPSHEETPAGE  psp;
        ZeroMemory( &psp, sizeof( psp ) );

        psp.dwSize          = sizeof( psp );
        psp.dwFlags         = PSP_DEFAULT;
        psp.hInstance       = ghInst;
        psp.pfnDlgProc      = MGenericProp::SetupDlgProc;
        psp.pszTemplate     = MAKEINTRESOURCE( DLG_DOC_JOB_GENERAL );
        psp.lParam          = (LPARAM)(MGenericProp*)&_General;

        if( pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
                                    CPSFUNC_ADD_PROPSHEETPAGE,
                                    (LPARAM)&psp,
                                    NULL ) <= 0 ) 
        {
            DBGMSG( DBG_TRACE, ( "CPSFUNC_ADD_PROPSHEETPAGE failed.\n") );
            bStatus DBGCHK = FALSE;
        }

        if (bStatus)
        {
            //
            // If the dev mode is null don't display the
            // device property sheets.
            //
            if( _pDocumentData->pJobInfo()->pDevMode )
            {
                ZeroMemory( &_dph, sizeof( _dph ) );

                _dph.cbSize         = sizeof( _dph );
                _dph.hPrinter       = _pDocumentData->hPrinter();
                _dph.pszPrinterName = (LPTSTR)(LPCTSTR)_pDocumentData->strPrinterName();
                _dph.pdmOut         = _pDocumentData->pJobInfo()->pDevMode;
                _dph.pdmIn          = _pDocumentData->pJobInfo()->pDevMode;
                _dph.fMode          = DM_IN_BUFFER | DM_PROMPT | DM_NOPERMISSION;

                if( pCPSUIInfo->pfnComPropSheet( pCPSUIInfo->hComPropSheet,
                                                 CPSFUNC_ADD_PFNPROPSHEETUI,
                                                 (LPARAM)DocumentPropertySheets,
                                                 (LPARAM)&_dph ) <= 0 )
                {
                    DBGMSG( DBG_TRACE, ( "CPSFUNC_ADD_PFNPROPSHEETUI failed.\n") );
                    bStatus DBGCHK = FALSE;
                }
            }
        }
    }

    return bStatus;
}


BOOL
TDocumentWindows::
bSetHeader(
    IN PPROPSHEETUI_INFO pCPSUIInfo,
    IN PPROPSHEETUI_INFO_HEADER pPSUIInfoHdr
    )
{
    DBGMSG( DBG_TRACE, ( "TDocumentWindows bSetHeader\n") );

    UNREFERENCED_PARAMETER( pCPSUIInfo );

    pPSUIInfoHdr->pTitle     = _pDocumentData->pJobInfo()->pDocument;
    pPSUIInfoHdr->Flags      = PSUIHDRF_PROPTITLE;
    pPSUIInfoHdr->hWndParent = _pDocumentData->hwnd();
    pPSUIInfoHdr->hInst      = ghInst;
    pPSUIInfoHdr->IconID     = IDI_DOCUMENT;

    return TRUE;
}

BOOL
TDocumentWindows::
bValid(
    VOID
    )
{
    return _General.bValid();
}

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

    Document properties UI (driver UI)

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

TDocumentProperties::
TDocumentProperties(
    IN  HWND hwnd,
    IN  HANDLE hPrinter,
    IN  LPCTSTR pszPrinter,
    IN  PDEVMODE pDevModeIn,
    OUT PDEVMODE pDevModeOut,
    IN  DWORD dwHideBits,
    IN  DWORD dwFlags
    ):
        _hwnd(hwnd),
        _hPrinter(hPrinter),
        _pszPrinter(pszPrinter),
        _pDevModeIn(pDevModeIn),
        _pDevModeOut(pDevModeOut),
        _dwHideBits(dwHideBits),
        _dwFlags(dwFlags),
        _lResult(-1)
{
    // nothing
}

LONG
TDocumentProperties::
lGetResult(
    VOID
    ) const
{
    return _lResult;
}

BOOL
TDocumentProperties::
bBuildPages(
    IN PPROPSHEETUI_INFO pCPSUIInfo
    )
{
    BOOL bRet = FALSE;

    if( bValid() )
    {
        //
        // Set the default activation context to be V6 prior calling into
        // compstui to create the pages. This will force V6 context unless
        // the callbacks which create the compstui pages specify otherwise
        // on a per page basis.
        //
        bRet = (BOOL)pCPSUIInfo->pfnComPropSheet(
                pCPSUIInfo->hComPropSheet, 
                CPSFUNC_SET_FUSION_CONTEXT, 
                reinterpret_cast<LPARAM>(g_hActCtx),
                static_cast<LPARAM>(0));

        if( bRet )
        {
            ZeroMemory(&_dph, sizeof(_dph));

            _dph.cbSize         = sizeof(_dph);
            _dph.hPrinter       = _hPrinter;
            _dph.pszPrinterName = const_cast<LPTSTR>(_pszPrinter);
            _dph.pdmIn          = _pDevModeIn;
            _dph.pdmOut         = _pDevModeOut;
            _dph.fMode          = _dwFlags;

            if( _dwHideBits )
            {
                _lResult = pCPSUIInfo->pfnComPropSheet(pCPSUIInfo->hComPropSheet, 
                    CPSFUNC_SET_DMPUB_HIDEBITS, static_cast<LPARAM>(_dwHideBits), 0);
            }
            else
            {
                //
                // If no bit is hided, The result value should be valid.
                //
                _lResult = 1;
            }

            if( _lResult > 0 )
            {
                _lResult = pCPSUIInfo->pfnComPropSheet(pCPSUIInfo->hComPropSheet, 
                    CPSFUNC_ADD_PFNPROPSHEETUI, reinterpret_cast<LPARAM>(DocumentPropertySheets), 
                    reinterpret_cast<LPARAM>(&_dph));
            }
        }
    }

    return (_lResult > 0);
}

BOOL
TDocumentProperties::
bSetHeader(
    IN PPROPSHEETUI_INFO pCPSUIInfo, 
    IN PPROPSHEETUI_INFO_HEADER pPSUInfoHeader
    )
{
    // construct the title & setup the header
    UNREFERENCED_PARAMETER(pCPSUIInfo);

    if( 0 == _strTitle.uLen() )
    {
        _strTitle.bLoadString(ghInst, IDS_PRINTER_PREFERENCES);
    }

    pPSUInfoHeader->pTitle     = const_cast<LPTSTR>(static_cast<LPCTSTR>(_strTitle));
    pPSUInfoHeader->Flags      = PSUIHDRF_EXACT_PTITLE | PSUIHDRF_NOAPPLYNOW;
    pPSUInfoHeader->hWndParent = _hwnd;
    pPSUInfoHeader->hInst      = ghInst;
    pPSUInfoHeader->IconID     = IDI_PRINTER;

    return TRUE;
}

// table to remap the devmode field selections to DMPUB_* ids.
static DWORD arrDmFlagMap[] = 
{ 
    DM_ORIENTATION,        MAKE_DMPUB_HIDEBIT( DMPUB_ORIENTATION ),
    DM_PAPERSIZE  ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_PAPERLENGTH,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_PAPERWIDTH ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_SCALE      ,        MAKE_DMPUB_HIDEBIT( DMPUB_SCALE ),
    DM_COPIES     ,        MAKE_DMPUB_HIDEBIT( DMPUB_COPIES_COLLATE ),
    DM_DEFAULTSOURCE,      MAKE_DMPUB_HIDEBIT( DMPUB_DEFSOURCE ),
    DM_PRINTQUALITY,       MAKE_DMPUB_HIDEBIT( DMPUB_PRINTQUALITY ),
    DM_COLOR      ,        MAKE_DMPUB_HIDEBIT( DMPUB_COLOR ),
    DM_DUPLEX     ,        MAKE_DMPUB_HIDEBIT( DMPUB_DUPLEX ),
    DM_YRESOLUTION,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_TTOPTION   ,        MAKE_DMPUB_HIDEBIT( DMPUB_TTOPTION ),
    DM_COLLATE    ,        MAKE_DMPUB_HIDEBIT( DMPUB_COPIES_COLLATE ),
    DM_FORMNAME   ,        MAKE_DMPUB_HIDEBIT( DMPUB_FORMNAME ),
    DM_LOGPIXELS  ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_BITSPERPEL ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_PELSWIDTH  ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_PELSHEIGHT ,        MAKE_DMPUB_HIDEBIT( 0 ),
    DM_DISPLAYFLAGS,       MAKE_DMPUB_HIDEBIT( 0 ),
    DM_DISPLAYFREQUENCY,   MAKE_DMPUB_HIDEBIT( 0 ),
    DM_ICMMETHOD  ,        MAKE_DMPUB_HIDEBIT( DMPUB_ICMMETHOD ),
    DM_ICMINTENT  ,        MAKE_DMPUB_HIDEBIT( DMPUB_ICMINTENT ),
    DM_MEDIATYPE  ,        MAKE_DMPUB_HIDEBIT( DMPUB_MEDIATYPE ),
    DM_DITHERTYPE ,        MAKE_DMPUB_HIDEBIT( DMPUB_DITHERTYPE ),
    DM_PANNINGWIDTH,       MAKE_DMPUB_HIDEBIT( 0 ),
    DM_PANNINGHEIGHT,      MAKE_DMPUB_HIDEBIT( 0 ) 
};

static DWORD 
RemapExclusionFlags(DWORD fExclusionFlags)
{
    // walk through to collect the exclusion bits from the table above.
    DWORD dwFlags = 0;
    for( UINT i = 0; i < ARRAYSIZE(arrDmFlagMap); i += 2 )
    {
        if( fExclusionFlags & arrDmFlagMap[i] )
        {
            dwFlags |= arrDmFlagMap[i+1];
        }
    }
    return dwFlags;
}

LONG 
DocumentPropertiesWrapNative(
    HWND hwnd,                  // handle to parent window 
    HANDLE hPrinter,            // handle to printer object
    LPTSTR pDeviceName,         // device name
    PDEVMODE pDevModeOutput,    // modified device mode
    PDEVMODE pDevModeInput,     // original device mode
    DWORD fMode,                // mode options
    DWORD fExclusionFlags       // exclusion flags
    )
{
    // lResult <= 0 means error, for more information see
    // the description of DocumentProperties in SDK.
    LONG lResult = (-1);

    if( hPrinter && pDeviceName && pDevModeOutput && pDevModeInput )
    {
        if (fMode & DM_PROMPT)
        {
            // invoke compstui to show up the driver UI
            TDocumentProperties props(hwnd, hPrinter, pDeviceName, pDevModeInput, pDevModeOutput, 
                RemapExclusionFlags(fExclusionFlags), fMode);

            if( props.bDisplayPages(hwnd, &lResult) )
            {
                // convert the result to IDOK/IDCANCEL
                lResult = (lResult == CPSUI_OK) ? IDOK : IDCANCEL;
            }
            else
            {
                // error occured.
                lResult = (-1);
            }
        }
        else
        {
            // this is no UI call - just invoke DocumentProperties.
            lResult = DocumentProperties(hwnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
        }
    }

    return lResult;
}

LONG 
DocumentPropertiesWrapWOW64(
    HWND hwnd,                  // handle to parent window 
    HANDLE hPrinter,            // handle to printer object
    LPTSTR pDeviceName,         // device name
    PDEVMODE pDevModeOutput,    // modified device mode
    PDEVMODE pDevModeInput,     // original device mode
    DWORD fMode,                // mode options
    DWORD fExclusionFlags       // exclusion flags
    )
{
    CDllLoader dll(TEXT("winspool.drv"));
    ptr_PrintUIDocumentPropertiesWrap pfnPrintUIDocumentPropertiesWrap =
        (ptr_PrintUIDocumentPropertiesWrap )dll.GetProcAddress(ord_PrintUIDocumentPropertiesWrap);

    LONG lResult = (-1);
    if( pfnPrintUIDocumentPropertiesWrap )
    {
        lResult = pfnPrintUIDocumentPropertiesWrap(hwnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode, fExclusionFlags);
    }
    return lResult;
}

LONG 
DocumentPropertiesWrap(
    HWND hwnd,                  // handle to parent window 
    HANDLE hPrinter,            // handle to printer object
    LPTSTR pDeviceName,         // device name
    PDEVMODE pDevModeOutput,    // modified device mode
    PDEVMODE pDevModeInput,     // original device mode
    DWORD fMode,                // mode options
    DWORD fExclusionFlags       // exclusion flags
    )
{
    // if running in WOW64 environment we need to call winspool.drv to remote the 
    // call into the surrogate 64bit process where the driver UI can be displayed.
    LONG lResult = IsRunningWOW64() ?
        DocumentPropertiesWrapWOW64(hwnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode, fExclusionFlags) :
        DocumentPropertiesWrapNative(hwnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode, fExclusionFlags);
    
    return lResult;
}