//
// File: dlgcset.cpp
//
// This file contains the code that implements CNativeFont class.
//
// history:
//     7-21-97 created; 
// 
#include "ctlspriv.h"
#include "ccontrol.h"

#define THISCLASS CNativeFont
#define SUPERCLASS CControl

typedef enum 
{
    FAS_NOTINITIALIZED = 0,
    FAS_DISABLED,
    FAS_ENABLED,
} FASTATUS;

class CNativeFont : public CControl
{
public:
    //Function Memebers
    virtual LRESULT v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    static LRESULT NativeFontWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
protected:
    
    CNativeFont();
    
    //Function Members    

    virtual void v_OnPaint(HDC hdc) ;
    virtual LRESULT v_OnCreate();
    virtual void v_OnSize(int x, int y)  {};

    virtual LRESULT v_OnCommand(WPARAM wParam, LPARAM lParam);
    virtual LRESULT v_OnNotify(WPARAM wParam, LPARAM lParam);
    virtual DWORD v_OnStyleChanged(WPARAM wParam, LPARAM lParam) { return 0; };    
    
    HRESULT _GetNativeDialogFont(HWND hDlg);
    static HRESULT _GetFontAssocStatus(FASTATUS  *uiAssoced);
    static BOOL _SetFontEnumProc(HWND hwnd, LPARAM lparam);
    static LRESULT _SubclassDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uIdSubclass, ULONG_PTR dwRefData);

    HFONT   m_hfontOrg;
    HFONT   m_hfontNative;
    HFONT   m_hfontDelete;
    typedef struct {
                HFONT hfontSet;
                DWORD dwStyle;
            } NFENUMCHILDDATA;
    static FASTATUS _s_uiFontAssocStatus;
};

// static variable initialization
FASTATUS CNativeFont::_s_uiFontAssocStatus = FAS_NOTINITIALIZED;

// reg keys
static const TCHAR s_szRegFASettings[] = TEXT("System\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset");

CNativeFont::CNativeFont(void)
{
    m_hfontOrg = NULL;
    m_hfontNative = NULL;
    m_hfontDelete = NULL;
}

LRESULT THISCLASS::NativeFontWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CNativeFont *pn = (CNativeFont *)GetWindowLongPtr(hwnd, 0);
    if (uMsg == WM_CREATE) {
        ASSERT(!pn);
        pn = new CNativeFont();
        if (!pn)
            return 0L;
    } 

    if (pn) {
        return pn->v_WndProc(hwnd, uMsg, wParam, lParam);
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void THISCLASS::v_OnPaint(HDC hdc)
{
    return;
}

LRESULT THISCLASS::v_OnCommand(WPARAM wParam, LPARAM lParam)
{
    // forward to parent (do we really need this?)
    return SendMessage(ci.hwndParent, WM_COMMAND, wParam, lParam);
}

LRESULT THISCLASS::v_OnNotify(WPARAM wParam, LPARAM lParam)
{
    // forward to parent
    LPNMHDR lpNmhdr = (LPNMHDR)lParam;
    
    return SendNotifyEx(ci.hwndParent, (HWND) -1,
                         lpNmhdr->code, lpNmhdr, ci.bUnicode);
}

LRESULT THISCLASS::v_OnCreate()
{
    return TRUE;
}

BOOL THISCLASS::_SetFontEnumProc(HWND hwnd, LPARAM lparam)
{
     NFENUMCHILDDATA *  pdt = (NFENUMCHILDDATA *)lparam; 
     BOOL bMatch = FALSE;
     
     if (pdt && pdt->hfontSet)
     {
         if (pdt->dwStyle & NFS_ALL)
         {
             bMatch = TRUE;
         }
         else
         {
             TCHAR szClass[32];
             
             GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
             
             if (pdt->dwStyle & NFS_EDIT)
             {
                 bMatch |= (lstrcmpi(TEXT("Edit"), szClass) == 0);
                 bMatch |= (lstrcmpi(TEXT("RichEdit20A"), szClass) == 0);
                 bMatch |= (lstrcmpi(TEXT("RichEdit20W"), szClass) == 0);
             }
             
             if (pdt->dwStyle & NFS_STATIC)
                 bMatch |= (lstrcmpi(TEXT("Static"), szClass) == 0);
             
             if (pdt->dwStyle & NFS_BUTTON)
                 bMatch |= (lstrcmpi(TEXT("Button"), szClass) == 0);

             if (pdt->dwStyle & NFS_LISTCOMBO)
             {
                 bMatch |= (lstrcmpi(TEXT("ListBox"), szClass) == 0);
                 bMatch |= (lstrcmpi(TEXT("ComboBox"), szClass) == 0);
                 bMatch |= (lstrcmpi(TEXT("ComboBoxEx32"), szClass) == 0);
                 bMatch |= (lstrcmpi(WC_LISTVIEW, szClass) == 0);
             }
         }

         if (bMatch) 
             SendMessage(hwnd, WM_SETFONT, (WPARAM)pdt->hfontSet, MAKELPARAM(FALSE, 0));

         return TRUE;
     }
     else
         return FALSE;
}

LRESULT THISCLASS::_SubclassDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam, WPARAM uIdSubclass, ULONG_PTR dwRefData)
{
    LRESULT lret = 0;
    CNativeFont * pnf = (CNativeFont *)dwRefData;
    
    if (pnf)
    {
    
        switch (uMsg)
        {
            case WM_INITDIALOG:
                // we enumerate its children so they get font 
                // in native charset selected if necessary
                // 
                if (S_OK == pnf->_GetNativeDialogFont(hdlg))
                {
                    // S_OK means we have different charset from 
                    // the default of the platform on which we're 
                    // running.
                    NFENUMCHILDDATA dt;
                    dt.hfontSet = pnf->m_hfontNative;
                    dt.dwStyle = pnf->ci.style;
                    EnumChildWindows(hdlg, pnf->_SetFontEnumProc, (LPARAM)&dt);
                }
                // we no longer need subclass procedure.
                // assumes no one has subclassed this dialog by now
                break;

            case WM_DESTROY:
                // if we've created a font, we have to clean it up.
                if (pnf->m_hfontDelete)
                {
                    NFENUMCHILDDATA dt;
                
                    dt.hfontSet = pnf->m_hfontOrg;
                    dt.dwStyle = pnf->ci.style;
                    // just in case anyone is still alive
                    EnumChildWindows(hdlg, pnf->_SetFontEnumProc, (LPARAM)&dt);
                    DeleteObject(pnf->m_hfontDelete);
                    pnf->m_hfontDelete = NULL;
                }
                RemoveWindowSubclass(hdlg, pnf->_SubclassDlgProc, 0);

                break;
        }
    
        lret = DefSubclassProc(hdlg, uMsg, wParam, lParam);
    }
 
    return lret;
}

LRESULT THISCLASS::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HWND hdlg;
    
    switch (uMsg)
    {
        case WM_CREATE:
        // subclass the parent dialog just to get notified for WM_INITDIALOG
            hdlg = GetParent(hwnd);
            if (hdlg)
            {
                // if we had an error just do nothing, we have to succeed in creating
                // window anyway otherwise dialog fails.
                SetWindowSubclass(hdlg, _SubclassDlgProc, 0, (ULONG_PTR)this);
            }
            break;
    }
    return SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
}

// _GetNativeDialogFont
//
// Retreive font handle in platform native character set
//
// returns S_OK if the given dialogbox requires setting font
//              in native charset
//         S_FALSE if the given dialogbox already has native
//              charset.
//         E_FAIL if anyother error occurs
//
HRESULT THISCLASS::_GetNativeDialogFont(HWND hDlg)
{
    HRESULT hres = E_FAIL;
    
    if(!m_hfontNative)
    {
        HFONT hfontNative, hfont = GetWindowFont(hDlg);
        LOGFONT lf, lfNative;
        FASTATUS uiFAStat = FAS_NOTINITIALIZED;
        GetObject(hfont, sizeof(LOGFONT), &lf);

        SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lfNative, 0);
        
        // there are two cases we don't want to create/set font
        // for the platform native character set.
        // 1) we already have matching character set
        // 2) the platform has 'font assoc' enabled or 'font link'
        //    and our client wants to use it instead of
        //    setting the right character set. (NFS_USEFONTASSOC)
        //    this solution sometimes provides better
        //    appearance (thought it is broken in its 
        //    font metrics) because it would use 
        //    'western font' as is.
        if (ci.style & NFS_USEFONTASSOC)
        {
            _GetFontAssocStatus(&uiFAStat);
        }

        if ( uiFAStat == FAS_ENABLED
           || lfNative.lfCharSet == lf.lfCharSet)
        {
                
            m_hfontOrg = m_hfontNative = hfont;
        }
        else
        {
            // we have non-native charset for the platform
            // Save away the original font first.
            m_hfontOrg = hfont;
            
            // Use the height of original dialog font
            lfNative.lfHeight = lf.lfHeight;
            if (!(hfontNative=CreateFontIndirect(&lfNative)))
            {
                hfontNative = hfont;
            }

            // save it away so we can delete it later
            if (hfontNative != hfont)
                m_hfontDelete = hfont;
        
            // set this variable to avoid calling createfont twice
            // if we get called again.
            m_hfontNative = hfontNative;
        }
    }

    return hres = (m_hfontNative == m_hfontOrg ? S_FALSE : S_OK);
}

//
// _GetFontAssocStatus
//
// synopsis: check to see if the platform has "Font Association"
//           enabled or 'Font Link' capability
//
HRESULT THISCLASS::_GetFontAssocStatus(FASTATUS  *puiAssoced)
{
    HRESULT hr = S_OK;
    ASSERT(puiAssoced);
    
    // I assume the setting won't change without rebooting
    // the system
    //
    if (FAS_NOTINITIALIZED == _s_uiFontAssocStatus)
    {
        // NT5 has fontlink functionality
        _s_uiFontAssocStatus = FAS_ENABLED;
    }
    *puiAssoced = _s_uiFontAssocStatus;

    return hr;
}

extern "C" {
    
BOOL InitNativeFontCtl(HINSTANCE hinst)
{
    WNDCLASS wc;

    wc.lpfnWndProc     = THISCLASS::NativeFontWndProc;
    wc.hCursor         = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon           = NULL;
    wc.lpszMenuName    = NULL;
    wc.hInstance       = hinst;
    wc.lpszClassName   = WC_NATIVEFONTCTL;
    wc.hbrBackground   = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
    wc.style           = CS_GLOBALCLASS;
    wc.cbWndExtra      = sizeof(LPVOID);
    wc.cbClsExtra      = 0;

    if (!RegisterClass(&wc) && !GetClassInfo(hinst, WC_NATIVEFONTCTL, &wc))
        return FALSE;

    return TRUE;
}

};