/* Copyright (c) 1995, Microsoft Corporation, all rights reserved
**
** popupdlg.c
** UI helper library
** Popup dialog routines
** Listed alphabetically
**
** 08/25/95 Steve Cobb
*/


#include <windows.h>  // Win32 root
#include <lmerr.h>    // LAN Manager errors
#include <lmcons.h>   // LAN Manager constants
#include <stdarg.h>   // To stop va_list argument warning only
#include <ras.h>      // RAS API definitions
#include <debug.h>    // Trace/assert library
#include <nouiutil.h> // No-HWND utilities
#include <uiutil.h>   // Our public header


/*----------------------------------------------------------------------------
** Error popup
**----------------------------------------------------------------------------
*/

int
ErrorDlgUtil(
    IN     HWND       hwndOwner,
    IN     DWORD      dwOperation,
    IN     DWORD      dwError,
    IN OUT ERRORARGS* pargs,
    IN     HINSTANCE  hInstance,
    IN     DWORD      dwTitle,
    IN     DWORD      dwFormat )

    /* Pops up a modal error dialog centered on 'hwndOwner'.  'DwOperation' is
    ** the string resource ID of the string describing the operation underway
    ** when the error occurred.  'DwError' is the code of the system or RAS
    ** error that occurred.  'Pargs' is a extended formatting arguments or
    ** NULL if none.  'hInstance' is the application/module handle where
    ** string resources are located.  'DwTitle' is the string ID of the dialog
    ** title.  'DwFormat' is the string ID of the error format title.
    **
    ** Returns MessageBox-style code.
    */
{
    TCHAR* pszUnformatted;
    TCHAR* pszOp;
    TCHAR  szErrorNum[ 50 ];
    TCHAR* pszError;
    TCHAR* pszResult;
    TCHAR* pszNotFound;
    int    nResult;

    TRACE("ErrorDlgUtil");

    /* A placeholder for missing strings components.
    */
    pszNotFound = TEXT("");

    /* Build the error number string.
    */
    if (dwError > 0x7FFFFFFF)
        wsprintf( szErrorNum, TEXT("0x%X"), dwError );
    else
        wsprintf( szErrorNum, TEXT("%u"), dwError );

    /* Build the error text string.
    */
    if (!GetErrorText( dwError, &pszError ))
        pszError = pszNotFound;

    /* Build the operation string.
    */
    pszUnformatted = PszFromId( hInstance, dwOperation );
    pszOp = pszNotFound;

    if (pszUnformatted)
    {
        FormatMessage(
            FORMAT_MESSAGE_FROM_STRING +
                FORMAT_MESSAGE_ALLOCATE_BUFFER +
                FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pszUnformatted, 0, 0, (LPTSTR )&pszOp, 1,
            (va_list* )((pargs) ? pargs->apszOpArgs : NULL) );

        Free( pszUnformatted );
    }

    /* Call MsgDlgUtil with the standard arguments plus any auxillary format
    ** arguments.
    */
    pszUnformatted = PszFromId( hInstance, dwFormat );
    pszResult = pszNotFound;

    if (pszUnformatted)
    {
        MSGARGS msgargs;

        ZeroMemory( &msgargs, sizeof(msgargs) );
        msgargs.dwFlags = MB_ICONEXCLAMATION + MB_OK + MB_SETFOREGROUND;
        msgargs.pszString = pszUnformatted;
        msgargs.apszArgs[ 0 ] = pszOp;
        msgargs.apszArgs[ 1 ] = szErrorNum;
        msgargs.apszArgs[ 2 ] = pszError;

        if (pargs)
        {
            msgargs.fStringOutput = pargs->fStringOutput;

            CopyMemory( &msgargs.apszArgs[ 3 ], pargs->apszAuxFmtArgs,
                3 * sizeof(TCHAR) );
        }

        nResult =
            MsgDlgUtil(
                hwndOwner, 0, &msgargs, hInstance, dwTitle );

        Free( pszUnformatted );

        if (pargs && pargs->fStringOutput)
            pargs->pszOutput = msgargs.pszOutput;
    }

    if (pszOp != pszNotFound)
        LocalFree( pszOp );
    if (pszError != pszNotFound)
        LocalFree( pszError );

    return nResult;
}


BOOL
GetErrorText(
    DWORD   dwError,
    TCHAR** ppszError )

    /* Fill caller's '*ppszError' with the address of a LocalAlloc'ed heap
    ** block containing the error text associated with error 'dwError'.  It is
    ** caller's responsibility to LocalFree the returned string.
    **
    ** Returns true if successful, false otherwise.
    */
{
#define MAXRASERRORLEN 256

    TCHAR  szBuf[ MAXRASERRORLEN + 1 ];
    DWORD  dwFlags;
    HANDLE hmodule;
    DWORD  cch;

    /* Don't panic if the RAS API address is not loaded.  Caller may be trying
    ** and get an error up during LoadRas.
    */
    if (g_hRasapi32Dll
        && g_pRasGetErrorString
        && g_pRasGetErrorString(
               (UINT )dwError, (LPTSTR )szBuf, MAXRASERRORLEN ) == 0)
    {
        /* It's a RAS error.
        */
        *ppszError = LocalAlloc( LPTR, (lstrlen( szBuf ) + 1) * sizeof(TCHAR) );
        if (!*ppszError)
            return FALSE;

        lstrcpy( *ppszError, szBuf );
        return TRUE;
    }

    /* The rest adapted from BLT's LoadSystem routine.
    */
    dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER + FORMAT_MESSAGE_IGNORE_INSERTS;

    if (dwError >= MIN_LANMAN_MESSAGE_ID && dwError <= MAX_LANMAN_MESSAGE_ID)
    {
        /* It's a net error.
        */
        dwFlags += FORMAT_MESSAGE_FROM_HMODULE;
        hmodule = GetModuleHandle( TEXT("NETMSG.DLL") );
    }
    else
    {
        /* It must be a system error.
        */
        dwFlags += FORMAT_MESSAGE_FROM_SYSTEM;
        hmodule = NULL;
    }

    cch = FormatMessage(
        dwFlags, hmodule, dwError, 0, (LPTSTR )ppszError, 1, NULL );
    return (cch > 0);
}


/*----------------------------------------------------------------------------
** Message popup
**----------------------------------------------------------------------------
*/

int
MsgDlgUtil(
    IN     HWND      hwndOwner,
    IN     DWORD     dwMsg,
    IN OUT MSGARGS*  pargs,
    IN     HINSTANCE hInstance,
    IN     DWORD     dwTitle )

    /* Pops up a message dialog centered on 'hwndOwner'.  'DwMsg' is the
    ** string resource ID of the message text.  'Pargs' is a extended
    ** formatting arguments or NULL if none.  'hInstance' is the
    ** application/module handle where string resources are located.
    ** 'DwTitle' is the string ID of the dialog title.
    **
    ** Returns MessageBox-style code.
    */
{
    TCHAR* pszUnformatted;
    TCHAR* pszResult;
    TCHAR* pszNotFound;
    int    nResult;

    TRACE("MsgDlgUtil");

    /* A placeholder for missing strings components.
    */
    pszNotFound = TEXT("");

    /* Build the message string.
    */
    pszResult = pszNotFound;

    if (pargs && pargs->pszString)
    {
        FormatMessage(
            FORMAT_MESSAGE_FROM_STRING +
                FORMAT_MESSAGE_ALLOCATE_BUFFER +
                FORMAT_MESSAGE_ARGUMENT_ARRAY,
            pargs->pszString, 0, 0, (LPTSTR )&pszResult, 1,
            (va_list* )pargs->apszArgs );
    }
    else
    {
        pszUnformatted = PszFromId( hInstance, dwMsg );

        if (pszUnformatted)
        {
            FormatMessage(
                FORMAT_MESSAGE_FROM_STRING +
                    FORMAT_MESSAGE_ALLOCATE_BUFFER +
                    FORMAT_MESSAGE_ARGUMENT_ARRAY,
                pszUnformatted, 0, 0, (LPTSTR )&pszResult, 1,
                (va_list* )((pargs) ? pargs->apszArgs : NULL) );

            Free( pszUnformatted );
        }
    }

    if (!pargs || !pargs->fStringOutput)
    {
        TCHAR* pszTitle;
        DWORD  dwFlags;
        HHOOK  hhook;

        if (pargs && pargs->dwFlags != 0)
            dwFlags = pargs->dwFlags;
        else
            dwFlags = MB_ICONINFORMATION + MB_OK + MB_SETFOREGROUND;

        pszTitle = PszFromId( hInstance, dwTitle );

        if (hwndOwner)
        {
            /* Install hook that will get the message box centered on the
            ** owner window.
            */
            hhook = SetWindowsHookEx( WH_CALLWNDPROC,
                CenterDlgOnOwnerCallWndProc,
                hInstance, GetCurrentThreadId() );
        }
        else
            hhook = NULL;

        nResult = MessageBox( hwndOwner, pszResult, pszTitle, dwFlags );

        if (hhook)
            UnhookWindowsHookEx( hhook );

        Free0( pszTitle );
        if (pszResult != pszNotFound)
            LocalFree( pszResult );
    }
    else
    {
        /* Caller wants the string without doing the popup.
        */
        pargs->pszOutput = (pszResult != pszNotFound) ? pszResult : NULL;
        nResult = IDOK;
    }

    return nResult;
}


LRESULT CALLBACK
CenterDlgOnOwnerCallWndProc(
    int    code,
    WPARAM wparam,
    LPARAM lparam )

    /* Standard Win32 CallWndProc hook callback that looks for the next dialog
    ** started and centers it on it's owner window.
    */
{
    /* Arrive here when any window procedure associated with our thread is
    ** called.
    */
    if (!wparam)
    {
        CWPSTRUCT* p = (CWPSTRUCT* )lparam;

        /* The message is from outside our process.  Look for the MessageBox
        ** dialog initialization message and take that opportunity to center
        ** the dialog on it's owner's window.
        */
        if (p->message == WM_INITDIALOG)
            CenterWindow( p->hwnd, GetParent( p->hwnd ) );
    }

    return 0;
}
