/*++

Copyright (c) 1990-1995,  Microsoft Corporation  All rights reserved.

Module Name:

    dlgs.c

Abstract:

    This module contains the common functions for the Win32 common dialogs.

Revision History:

--*/



//
//  Include Files.
//

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "windows.h"
#include <port1632.h>
#include "privcomd.h"




//
//  Global Variables.
//

extern BOOL bInitializing;

extern DWORD g_tlsiExtError;
extern CRITICAL_SECTION g_csExtError;




//
//  Function Prototypes.
//

LONG
RgbInvertRgb(
    LONG rgbOld);





////////////////////////////////////////////////////////////////////////////
//
//  HourGlass
//
//  Turn hourglass on or off.
//
//  bOn - specifies ON or OFF
//
////////////////////////////////////////////////////////////////////////////

VOID HourGlass(
    BOOL bOn)
{
    //
    //  Change cursor to hourglass.
    //
    if (!bInitializing)
    {
        if (!bMouse)
        {
            ShowCursor(bCursorLock = bOn);
        }
        SetCursor(LoadCursor(NULL, bOn ? IDC_WAIT : IDC_ARROW));
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  StoreExtendedError
//
//  Stores an extended error code for the next call to
//  CommDlgExtendedError.
//
////////////////////////////////////////////////////////////////////////////

void StoreExtendedError(
    DWORD dwError)
{
    EnterCriticalSection(&g_csExtError);

    TlsSetValue(g_tlsiExtError, (LPVOID)dwError);

    LeaveCriticalSection(&g_csExtError);
}


////////////////////////////////////////////////////////////////////////////
//
//  GetStoredExtendedError
//
//  Retieves the stored extended error code.
//
////////////////////////////////////////////////////////////////////////////

DWORD GetStoredExtendedError(void)
{
    DWORD dwError;

    EnterCriticalSection(&g_csExtError);

    dwError = (DWORD)TlsGetValue(g_tlsiExtError);

    LeaveCriticalSection(&g_csExtError);

    return (dwError);
}


////////////////////////////////////////////////////////////////////////////
//
//  CommDlgExtendedError
//
//  Provides additional information about dialog failure.
//  This should be called immediately after failure.
//
//  Returns:   LO word - error code
//             HI word - error specific info
//
////////////////////////////////////////////////////////////////////////////

DWORD WINAPI CommDlgExtendedError()
{
    return (GetStoredExtendedError());
}


////////////////////////////////////////////////////////////////////////////
//
//  LoadAlterBitmap
//
//  Loads a bitmap given its name and gives all the pixels that are
//  a certain color a new color.
//
//  Returns:   NULL - failed
//             handle to the bitmap loaded - success
//
////////////////////////////////////////////////////////////////////////////

HBITMAP WINAPI LoadAlterBitmap(
    int id,
    DWORD rgbReplace,
    DWORD rgbInstead)
{
    LPBITMAPINFOHEADER qbihInfo;
    HDC hdcScreen;
    BOOL fFound;
    HANDLE hresLoad;
    HANDLE hres;
    LPLONG qlng;
    DWORD *qlngReplace;       // points to bits that are replaced
    LPBYTE qbBits;
    HANDLE hbmp;
    LPBITMAPINFOHEADER lpBitmapInfo;
    UINT cbBitmapSize;

    hresLoad = FindResource(g_hinst, MAKEINTRESOURCE(id), RT_BITMAP);
    if (hresLoad == HNULL)
    {
        return (HNULL);
    }
    hres = LoadResource(g_hinst, hresLoad);
    if (hres == HNULL)
    {
        return (HNULL);
    }

    //
    //  Lock the bitmap data and make a copy of it for the mask and the
    //  bitmap.
    //
    cbBitmapSize = SizeofResource(g_hinst, hresLoad);
    lpBitmapInfo = (LPBITMAPINFOHEADER)LockResource(hres);

    qbihInfo = (LPBITMAPINFOHEADER)LocalAlloc(LPTR, cbBitmapSize);

    if ((lpBitmapInfo == NULL) || (qbihInfo == NULL))
    {
        return (NULL);
    }

    memcpy((TCHAR *)qbihInfo, (TCHAR *)lpBitmapInfo, cbBitmapSize);

    //
    //  Get a pointer into the color table of the bitmaps, cache the
    //  number of bits per pixel.
    //
    rgbReplace = RgbInvertRgb(rgbReplace);
    rgbInstead = RgbInvertRgb(rgbInstead);

    qlng = (LPLONG)((LPSTR)(qbihInfo) + qbihInfo->biSize);

    fFound = FALSE;
    while (!fFound)
    {
        if (*qlng == (LONG)rgbReplace)
        {
            fFound = TRUE;
            qlngReplace = (DWORD *)qlng;
            *qlng = (LONG)rgbInstead;
        }
        qlng++;
    }

    //
    //  First skip over the header structure.
    //
    qbBits = (LPBYTE)(qbihInfo + 1);

    //
    //  Skip the color table entries, if any.
    //
    qbBits += (1 << (qbihInfo->biBitCount)) * sizeof(RGBQUAD);

    //
    //  Create a color bitmap compatible with the display device.
    //
    hdcScreen = GetDC(HNULL);
    if (hdcScreen != HNULL)
    {
        hbmp = CreateDIBitmap( hdcScreen,
                               qbihInfo,
                               (LONG)CBM_INIT,
                               qbBits,
                               (LPBITMAPINFO)qbihInfo,
                               DIB_RGB_COLORS );
        ReleaseDC(HNULL, hdcScreen);
    }

    //
    //  Reset color bits to original value.
    //
    *qlngReplace = (LONG)rgbReplace;

    LocalFree(qbihInfo);
    return (hbmp);
}


////////////////////////////////////////////////////////////////////////////
//
//  RgbInvertRgb
//
//  Reverses the byte order of the RGB value (for file format).
//
//  Returns the new color value (RGB to BGR).
//
////////////////////////////////////////////////////////////////////////////

LONG RgbInvertRgb(
    LONG rgbOld)
{
    LONG lRet;
    BYTE R, G, B;

    R = GetRValue(rgbOld);
    G = GetGValue(rgbOld);
    B = GetBValue(rgbOld);

    lRet = (LONG)RGB(B, G, R);

    return (lRet);
}


////////////////////////////////////////////////////////////////////////////
//
//  HbmpLoadBmp
//
//  Loads in a bitmap.
//
//  Returns:   Bitmap handle - success
//             HNULL         - failure
//
////////////////////////////////////////////////////////////////////////////

#if 0
HBITMAP HbmpLoadBmp(
    WORD bmp)
{
    HBITMAP hbmp;
    CHAR szBitmap[cbResNameMax];

    hbmp = HNULL;
    if (LoadString(g_hinst, bmp, (LPTSTR)szBitmap, cbResNameMax - 1))
    {
        hbmp = LoadBitmap(g_hinst, (LPCTSTR)szBitmap);
    }
    return (hbmp);
}
#endif


////////////////////////////////////////////////////////////////////////////
//
//  AddNetButton
//
//  Attempts to add a network button to the open, save, or print dialogs.
//
//  hDlg           - dialog to add button to
//  hInstance      - instance handle for dlg
//  dyBottomMargin - DUs to bottom edge
//
////////////////////////////////////////////////////////////////////////////

#define xDUsToPels(DUs, lDlgBaseUnits) \
   (int)(((DUs) * (int)LOWORD((lDlgBaseUnits))) / 4)

#define yDUsToPels(DUs, lDlgBaseUnits) \
   (int)(((DUs) * (int)HIWORD((lDlgBaseUnits))) / 8)

VOID AddNetButton(
    HWND hDlg,
    HANDLE hInstance,
    int dyBottomMargin,
    BOOL bAddAccel,
    BOOL bTryLowerRight,
    BOOL bTryLowerLeft)
{
    LONG lDlgBaseUnits;
    RECT rcDlg, rcCtrl, rcTmp;
    LONG xButton, yButton;
    LONG dxButton, dyButton;
    LONG dxDlgFrame, dyDlgFrame;
    LONG yDlgHeight, xDlgWidth;
    HWND hwndButton, hCtrl, hLastCtrl, hTmp, hSave;
    HFONT hFont;
    POINT ptTopLeft, ptTopRight, ptCenter, ptBtmLeft, ptBtmRight;
    TCHAR szNetwork[MAX_PATH];

    //
    //  Make sure a network button (psh14) doesn't already exist in
    //  the dialog.
    //
    if (GetDlgItem(hDlg, psh14))
    {
        return;
    }

    //
    //  Get dialog coordinate info.
    //
    lDlgBaseUnits = GetDialogBaseUnits();

    dxDlgFrame = GetSystemMetrics(SM_CXDLGFRAME);
    dyDlgFrame = GetSystemMetrics(SM_CYDLGFRAME);

    GetWindowRect(hDlg, &rcDlg);

    rcDlg.left += dxDlgFrame;
    rcDlg.right -= dxDlgFrame;
    rcDlg.top += dyDlgFrame + GetSystemMetrics(SM_CYCAPTION);
    rcDlg.bottom -= dyDlgFrame;

    //
    //  Get the OK button.
    //
    if (!(hCtrl = GetDlgItem(hDlg, IDOK)))
    {
        return;
    }

    GetWindowRect(hCtrl, &rcCtrl);

    ptTopLeft.x = rcCtrl.left;
    ptTopLeft.y = rcCtrl.top;

    //
    //  Make sure the OK button isn't outside the dialog.
    //
    if (!PtInRect(&rcDlg, ptTopLeft))
    {
        //
        //  Try the CANCEL button.
        //
        if (!(hCtrl = GetDlgItem(hDlg, IDCANCEL)))
        {
           //
           //  Both OK and CANCEL do not exist, so return.
           //
           return;
        }

        //
        //  The check for the Cancel button outside the dialog is handled
        //  below.
        //
        GetWindowRect(hCtrl, &rcCtrl);
    }
    hSave = hCtrl;

#ifdef UNICODE
    //
    //  Get the full hDlg value if coming from WOW.
    //
    if (HIWORD(hDlg) == 0)
    {
        HWND hNewDlg;

        if (hDlg == (HWND)LOWORD(hNewDlg = GetParent(hCtrl)))
        {
            hDlg = hNewDlg;
        }
    }
#endif

    //
    //  Save the coordinate info of the button.
    //
    dxButton = rcCtrl.right - rcCtrl.left;
    dyButton = rcCtrl.bottom - rcCtrl.top;

    xButton = rcCtrl.left;
    yButton = rcCtrl.bottom + yDUsToPels(4, lDlgBaseUnits);

    yDlgHeight = rcDlg.bottom - yDUsToPels(dyBottomMargin, lDlgBaseUnits);

    //
    //  Try to insert the network button in the lower right corner
    //  of dialog box.
    //
    if (bTryLowerRight && (hTmp = GetDlgItem(hDlg, cmb2)))
    {
        //
        //  See if the network button can be inserted in the
        //  lower right corner of the dialog box.
        //
        hLastCtrl = hCtrl;
        GetWindowRect(hTmp, &rcTmp);
        yButton = rcTmp.top;

        //
        //  Set the coordinates of the new button.
        //
        ptTopLeft.x = ptBtmLeft.x = xButton;
        ptTopLeft.y = ptTopRight.y = yButton;
        ptTopRight.x = ptBtmRight.x = xButton + dxButton;
        ptBtmLeft.y = ptBtmRight.y = yButton + dyButton;
        ptCenter.x = xButton + dxButton / 2;
        ptCenter.y = yButton + dyButton / 2;
        ScreenToClient(hDlg, (LPPOINT)&ptTopLeft);
        ScreenToClient(hDlg, (LPPOINT)&ptBtmLeft);
        ScreenToClient(hDlg, (LPPOINT)&ptTopRight);
        ScreenToClient(hDlg, (LPPOINT)&ptBtmRight);
        ScreenToClient(hDlg, (LPPOINT)&ptCenter);

        //
        //  See if the new button is over any other buttons.
        //
        if (((yButton + dyButton) < yDlgHeight) &&
            ((ChildWindowFromPoint(hDlg, ptTopLeft)  == hDlg) &&
             (ChildWindowFromPoint(hDlg, ptTopRight) == hDlg) &&
             (ChildWindowFromPoint(hDlg, ptCenter)   == hDlg) &&
             (ChildWindowFromPoint(hDlg, ptBtmLeft)  == hDlg) &&
             (ChildWindowFromPoint(hDlg, ptBtmRight) == hDlg)))
        {
            goto FoundPlace;
        }

        //
        //  Reset yButton.
        //
        yButton = rcCtrl.bottom + yDUsToPels(4, lDlgBaseUnits);
    }

    //
    //  Try to insert the network button vertically below the other
    //  control buttons.
    //
    while (hCtrl != NULL)
    {
        //
        //  Move vertically down and see if there is space.
        //
        hLastCtrl = hCtrl;
        GetWindowRect(hCtrl, &rcCtrl);
        yButton = rcCtrl.bottom + yDUsToPels(4, lDlgBaseUnits);

        //
        //  Make sure there is still room in the dialog.
        //
        if ((yButton + dyButton) > yDlgHeight)
        {
            //
            //  No space.
            //
            break;
        }

        //
        //  Set the coordinates of the new button.
        //
        ptTopLeft.x = ptBtmLeft.x = xButton;
        ptTopLeft.y = ptTopRight.y = yButton;
        ptTopRight.x = ptBtmRight.x = xButton + dxButton;
        ptBtmLeft.y = ptBtmRight.y = yButton + dyButton;
        ptCenter.x = xButton + dxButton / 2;
        ptCenter.y = yButton + dyButton / 2;
        ScreenToClient(hDlg, (LPPOINT)&ptTopLeft);
        ScreenToClient(hDlg, (LPPOINT)&ptBtmLeft);
        ScreenToClient(hDlg, (LPPOINT)&ptTopRight);
        ScreenToClient(hDlg, (LPPOINT)&ptBtmRight);
        ScreenToClient(hDlg, (LPPOINT)&ptCenter);

        //
        //  See if the new button is over any other buttons.
        //
        if (((hCtrl = ChildWindowFromPoint(hDlg, ptTopLeft))  == hDlg) &&
            ((hCtrl = ChildWindowFromPoint(hDlg, ptTopRight)) == hDlg) &&
            ((hCtrl = ChildWindowFromPoint(hDlg, ptCenter))   == hDlg) &&
            ((hCtrl = ChildWindowFromPoint(hDlg, ptBtmLeft))  == hDlg) &&
            ((hCtrl = ChildWindowFromPoint(hDlg, ptBtmRight)) == hDlg))
        {
            goto FoundPlace;
        }
    }

    //
    //  Try to insert the network button in the lower left corner of
    //  the dialog box.
    //
    if (bTryLowerLeft)
    {
        //
        //  Get the width of the dialog to make sure the button doesn't
        //  go off the side of the dialog.
        //
        xDlgWidth = rcDlg.right - xDUsToPels(FILE_RIGHT_MARGIN, lDlgBaseUnits);

        //
        //  Use the OK or CANCEL button saved earlier to get the size of
        //  the buttons.
        //
        hCtrl = hSave;

        //
        //  Start at the far left of the dialog.
        //
        //  NOTE: We know that hCtrl is not NULL at this point because
        //        otherwise we would have returned earlier.
        //
        //        The print dialogs have a left margin of 8.
        //
        GetWindowRect(hCtrl, &rcCtrl);
        xButton = rcDlg.left + xDUsToPels(FILE_LEFT_MARGIN + 3, lDlgBaseUnits);
        yButton = rcCtrl.top;

        while (1)
        {
            hLastCtrl = hCtrl;

            //
            //  Make sure there is still room in the dialog.
            //
            if ((xButton + dxButton) > xDlgWidth)
            {
                //
                //  No space.
                //
                break;
            }

            //
            //  Set the coordinates of the new button.
            //
            ptTopLeft.x = ptBtmLeft.x = xButton;
            ptTopLeft.y = ptTopRight.y = yButton;
            ptTopRight.x = ptBtmRight.x = xButton + dxButton;
            ptBtmLeft.y = ptBtmRight.y = yButton + dyButton;
            ptCenter.x = xButton + dxButton / 2;
            ptCenter.y = yButton + dyButton / 2;
            ScreenToClient(hDlg, (LPPOINT)&ptTopLeft);
            ScreenToClient(hDlg, (LPPOINT)&ptBtmLeft);
            ScreenToClient(hDlg, (LPPOINT)&ptTopRight);
            ScreenToClient(hDlg, (LPPOINT)&ptBtmRight);
            ScreenToClient(hDlg, (LPPOINT)&ptCenter);

            //
            //  See if the new button is over any other buttons.
            //
            if ( ( ((hCtrl = ChildWindowFromPoint(hDlg, ptTopLeft))  == hDlg) &&
                   ((hCtrl = ChildWindowFromPoint(hDlg, ptTopRight)) == hDlg) &&
                   ((hCtrl = ChildWindowFromPoint(hDlg, ptCenter))   == hDlg) &&
                   ((hCtrl = ChildWindowFromPoint(hDlg, ptBtmLeft))  == hDlg) &&
                   ((hCtrl = ChildWindowFromPoint(hDlg, ptBtmRight)) == hDlg) ) )
            {
                //
                //  If the last control is the OK button and there is a
                //  CANCEL button, then the last control should be the
                //  CANCEL button.
                //
                if ((hLastCtrl == GetDlgItem(hDlg, IDOK)) &&
                    (hCtrl = GetDlgItem(hDlg, IDCANCEL)))
                {
                    POINT ptTopLeftTmp;

                    GetWindowRect(hCtrl, &rcCtrl);
                    ptTopLeftTmp.x = rcCtrl.left;
                    ptTopLeftTmp.y = rcCtrl.top;

                    //
                    //  Make sure the CANCEL button isn't outside the dialog
                    //  and then set the last control to be the CANCEL button.
                    //
                    if (PtInRect(&rcDlg, ptTopLeftTmp))
                    {
                        hLastCtrl = hCtrl;
                    }
                }

                goto FoundPlace;
            }

            //
            //  Make sure we encountered another control and that we
            //  didn't go off the end of the dialog.
            //
            if (!hCtrl)
            {
                break;
            }

            //
            //  Move over to the right and see if there is space.
            //
            GetWindowRect(hCtrl, &rcCtrl);
            xButton = rcCtrl.right + xDUsToPels(4, lDlgBaseUnits);
        }
    }

    return;

FoundPlace:

    xButton = ptTopLeft.x;
    yButton = ptTopLeft.y;

    if (LoadString( g_hinst,
                    (bAddAccel ? iszNetworkButtonTextAccel : iszNetworkButtonText),
                    (LPTSTR)szNetwork,
                    MAX_PATH ))
    {
        hwndButton = CreateWindow( TEXT("button"),
                                   szNetwork,
                                   WS_VISIBLE | WS_CHILD | WS_GROUP |
                                       WS_TABSTOP | BS_PUSHBUTTON,
                                   xButton,
                                   yButton,
                                   dxButton,
                                   dyButton,
                                   hDlg,
                                   NULL,
                                   hInstance,
                                   NULL );

        if (hwndButton != NULL)
        {
            SetWindowLong(hwndButton, GWL_ID, psh14);
            SetWindowPos( hwndButton,
                          hLastCtrl,
                          0, 0, 0, 0,
                          SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
            hFont = (HFONT)SendDlgItemMessage(hDlg, IDOK, WM_GETFONT, 0, 0L);
            SendMessage(hwndButton, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(TRUE,0));
        }
    }
}


////////////////////////////////////////////////////////////////////////////
//
//  IsNetworkInstalled
//
////////////////////////////////////////////////////////////////////////////

BOOL IsNetworkInstalled()
{
    BOOL bNetwork = FALSE;
    DWORD dwErr;
    HKEY hKey;
    DWORD dwcbBuffer = 0;

    dwErr = RegOpenKey(
             HKEY_LOCAL_MACHINE,
             TEXT("System\\CurrentControlSet\\Control\\NetworkProvider\\Order"),
             &hKey );

    if (!dwErr)
    {
        dwErr = RegQueryValueEx( hKey,
                                 TEXT("ProviderOrder"),
                                 NULL,
                                 NULL,
                                 NULL,
                                 &dwcbBuffer );

        if (ERROR_SUCCESS == dwErr && dwcbBuffer > 1)
        {
            bNetwork = TRUE;
        }

        RegCloseKey(hKey);
    }

    return (bNetwork);
}


#ifdef WX86

////////////////////////////////////////////////////////////////////////////
//
//  Wx86GetX86Callback
//
//  Creates a RISC-callable alias for a x86 hook function pointer.
//
//  lpfnHook - x86 address of hook
//
//  Returns a function pointer which can be called from RISC.
//
////////////////////////////////////////////////////////////////////////////

PVOID Wx86GetX86Callback(
    PVOID lpfnHook)
{
    if (!lpfnHook)
    {
        return (NULL);
    }

    if (!pfnAllocCallBx86)
    {
        HMODULE hMod;

        if (!Wx86CurrentTib())
        {
            //
            //  Wx86 is not running in this thread.  Assume a RISC app has
            //  passed a bad flag value and that lpfnHook is already a RISC
            //  function pointer.
            //
            return (lpfnHook);
        }

        hMod = GetModuleHandle(TEXT("wx86.dll"));
        if (hMod == NULL)
        {
            //
            //  Wx86 is running, but wx86.dll is not loaded!  This should
            //  never happen, but if it does, assume lpfnHook is already a
            //  RISC pointer.
            //
            return (lpfnHook);
        }
        pfnAllocCallBx86 = (PALLOCCALLBX86)GetProcAddress( hMod,
                                                           "AllocCallBx86" );
        if (!pfnAllocCallBx86)
        {
            //
            //  Something has gone terribly wrong!
            //
            return (lpfnHook);
        }
    }

    //
    //  Call into Wx86.dll to create a RISC-to-x86 callback which takes
    //  4 parameters and has no logging.
    //
    return (*pfnAllocCallBx86)(lpfnHook, 4, NULL, NULL);
}

#endif