//*********************************************************************
//*                  Microsoft Windows                               **
//*            Copyright(c) Microsoft Corp., 1996                    **
//*********************************************************************

//
// ADVANCED.C - "Advanced" Property Sheet
//

//
//  This is a registry driven UI that walk through the part of the
//  registry tree and convert it into a tree view list.
//  Note that registry values can be localized, so internation versions
//  have to localize the INF file so that setup will set the registry
//  with the right text.
//
//  Here is how the registry key looks like,
//
//  HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\AdvancedOptions\
//      GroupKey1\
//          Type="group"
//          Text="text to display"
//          DefaultImage="somedll.dll,icon_index", (optional).
//
//          RadioKey1.1\
//              Type="radio"
//              Text="text to display"
//   (optional) HKeyRoot=any one of the pre-defined root HKEY in DWORD,
//                  ; default to HKEY_CURRENT_USER.
//   (optional) RegPath="path to the key where the setting value is stored."
//                  ; default to Software\Microsoft\Windows\CurrentVersion\Explorer\AdvancedOptions
//              ValueName="reg value name."
//              CheckedValue=...    ; value for this radio button,
//                  ; if the value is platform dependent, use one of the following two instead
//              CheckedValueNT=...  ; if the value is platform dependent
//              CheckedValueW95=... ; if the value is platform dependent
//              DefaultValue=...
//   (optional) SPIAction=uiAction param to SystemParametersInfo in DWORD
//   (optional) SPIParamON=uiParam param to SystemParametersInfo for this radio button in DWORD
//   (optional) Mask=...          ; mask for the bitfield 1's for the bits we want to be able to set/clear
//              
//          RadioKey1.2\
//              Type="radio"
//              Text="text to display"
//   (optional) HKeyRoot=any one of the pre-defined root HKEY in DWORD,
//                  ; default to HKEY_CURRENT_USER.
//   (optional) RegPath="path to the key where the setting value is stored."
//                  ; default to Software\Microsoft\Windows\CurrentVersion\Explorer\AdvancedOptions
//              ValueName="reg value name."
//              CheckedValue=...    ; value for this checkbox if it's checked
//                  ; if the value is platform dependent, use one of the following two instead
//              CheckedValueNT=...  ; if the value is platform dependent
//              CheckedValueW95=... ; if the value is platform dependent
//              UncheckedValue=...  ; value for this checkbox if it's unchecked
//              DefaultValue=...
//   (optional) SPIAction=uiAction param to SystemParametersInfo in DWORD
//   (optional) SPIParamON=uiParam param to SystemParametersInfo if checked in DWORD
//   (optional) SPIParamOFF=uiParam param to SystemParametersInfo if checked in DWORD
//   (optional) Mask=...          ; mask for the bitfield 1's for the bits we want to be able to set/clear
//
//          CheckBoxKey1.1\
//              ...
//          GroupKey1.1\
//              ...
//      GroupKey2\
//          ...
//          CheckBoxKey2.1\
//              ...
//          CheckBoxKey2.2\
//              ...
//
//
//  Notes:
//      1. All the settings are store in HKCU\...\Explorer\Settings key,
//          code can be added to support any random absolute reg path for
//          the settings.
//

#include "stdafx.h"
#include "utils.h"
#pragma hdrstop

void Cabinet_StateChanged(void);

//
// Private Calls and structures
//
//

typedef struct {
    HWND hDlg;              // handle of our dialog
    HWND hwndTree;          // handle to the treeview
    IRegTreeOptions *pTO;    // pointer to RegTreeOptions interface
    CFolderOptionsPsx *ppsx;    // to talk to our propsheet sibling
    BOOL    fDirty;          // Dirty bit to detect if anything changed.
} ADVANCEDPAGE, *LPADVANCEDPAGE;

#define ENABLEAPPLY(hDlg) SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0L)

//
// Initializes Advanced property sheet
//
// History:
void Install_AdvancedShellSettings(SHELLSTATE * pss);

BOOL AdvancedDlgInit(HWND hDlg, LPARAM lParam)
{
    HRESULT hr;
    LPADVANCEDPAGE  pAdv = (LPADVANCEDPAGE)LocalAlloc(LPTR, sizeof(*pAdv));
    if (!pAdv)
    {
        EndDialog(hDlg, 0);
        return FALSE;   // no memory?
    }

    // We expose some of SHGetSetSettings in the advanced settings section,
    // so we need to migrate these PER-USER settings from the SHELLSTATE
    // structure into the registry tree structure once per user.  Since we
    // don't have a per-user install section, do it here on demand.
    // We don't have to do it every time, but that's easier...
    Install_AdvancedShellSettings(NULL);
    
    // tell dialog where to get info
    SetWindowPtr(hDlg, DWLP_USER, pAdv);

    PROPSHEETPAGE *pps = (PROPSHEETPAGE *)lParam;

    pAdv->ppsx = (CFolderOptionsPsx *)pps->lParam;

    //Check if we have pointer to Shell Browser
    if (!pAdv->ppsx->HasBrowserService())
    {
        //Hey ! we dont have pointer to Shell Browser so we must have been
        //invoked through a global Folder Options . In this case it doesn't 
        //make sense to  say "You can make all your folders look the same Like Current Folder"
        //because we dont have a current folder so let just disable the button "Like Current Folder"
        EnableWindow(GetDlgItem(hDlg, IDC_ADVO_USECURRENTFOLDER), FALSE);
    }

    // save dialog handle
    pAdv->hDlg = hDlg;
    pAdv->hwndTree = GetDlgItem(pAdv->hDlg, IDC_ADVO_ADVANCEDTREE);
    DWORD dwServerType = CLSCTX_INPROC_SERVER;

    hr = CoCreateInstance(CLSID_CRegTreeOptions, NULL, dwServerType,
                          IID_IRegTreeOptions, (LPVOID *)&(pAdv->pTO));
    if (SUCCEEDED(hr))
    {
        // HACK - IRegTreeOptions is ANSI, so we temporarily turn off UNICODE
        #undef TEXT
        #define TEXT(s) s
        hr = pAdv->pTO->InitTree(pAdv->hwndTree, HKEY_LOCAL_MACHINE, REGSTR_EXPLORER_ADVANCED, NULL);
        #undef TEXT
        #define TEXT(s) __TEXT(s)
    }
    
    // find the first root and make sure that it is visible
    TreeView_EnsureVisible(pAdv->hwndTree, TreeView_GetRoot(pAdv->hwndTree));

    return SUCCEEDED(hr) ? TRUE : FALSE;
}

void Tree_OnNotify(LPADVANCEDPAGE pAdv)
{
    TV_HITTESTINFO ht;

    GetCursorPos(&ht.pt);                         // get where we were hit

    ScreenToClient(pAdv->hwndTree, &ht.pt);       // translate it to our window

    // retrieve the item hit
    // IRegTreeOptions might fail to cocreate under stress conditions and
    // low memory, in which case we would fault here if we dont check for
    // pTO. Bug # 211108 - ramkumar
    if (pAdv->pTO)
    {
        pAdv->pTO->ToggleItem(TreeView_HitTest(pAdv->hwndTree, &ht));
        ENABLEAPPLY(pAdv->hDlg);
        pAdv->fDirty = TRUE;
    }
} // Tree_OnNotify


#if 0
void AnimateToTray(HWND hwnd)
{
    HWND hwndTray = FindWindow(TEXT("Shell_TrayWnd"), NULL);
    if (hwndTray)
    {
        HWND hwndIcons = FindWindowEx(hwndTray, NULL, TEXT("TrayNotifyWnd"), NULL);
        if (hwndIcons)
        {
            RECT rcSource, rcDest;
            GetWindowRect(hwnd, &rcSource);
            GetWindowRect(hwndIcons, &rcDest);

            DrawAnimatedRects(hwnd, IDANI_CAPTION, &rcSource, &rcDest);
        }
    }
}
#endif

//
// AdvancedDlgOnNotify()
//
// Handles Advanced property sheets WM_NOTIFY messages
//
//
void AdvancedDlgOnNotify(LPADVANCEDPAGE pAdv, LPNMHDR psn)
{
    SetWindowLongPtr(pAdv->hDlg, DWLP_MSGRESULT, 0); // handled

    switch (psn->code)
    {
    case TVN_KEYDOWN:
    {
        TV_KEYDOWN *pnm = (TV_KEYDOWN*)psn;
        if (pnm->wVKey == VK_SPACE)
        {
            pAdv->pTO->ToggleItem((HTREEITEM)SendMessage(pAdv->hwndTree, TVM_GETNEXTITEM, TVGN_CARET, NULL));
            ENABLEAPPLY(pAdv->hDlg);
            pAdv->fDirty = TRUE;
            // Specify that we handled the key, so we don't beep
            SetWindowLongPtr(pAdv->hDlg, DWLP_MSGRESULT, TRUE);
        }
        break;
    }

    case NM_CLICK:
    case NM_DBLCLK:
        // is this click in our tree?
        if (psn->idFrom == IDC_ADVO_ADVANCEDTREE)
        {
            // yes...
            Tree_OnNotify(pAdv);
        }
        break;

    case PSN_APPLY:
        if (pAdv->fDirty) //We will save these only if something changed.
        {
            pAdv->pTO->WalkTree(WALK_TREE_SAVE);

            CABINETSTATE cs;
            ReadCabinetState(&cs, sizeof(cs));   // refresh the global CABINETSTATE
            WriteCabinetState(&cs);              // and make sure we commit to the bitfield.
        
            // We should only notify if needed.

            SHRefreshSettings();        // refresh the SHELLSTATE structure

            SHSettingsChanged(0, 0);    // invalidate the restrictions

            // Let everybody know about the new settings
            SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0);

            // Okay now tell all Cabinets to refresh
            Cabinet_RefreshAll(Cabinet_RefreshEnum, (LPARAM)0L);
        }
        break;
    }
}

const static DWORD aAdvOptsHelpIDs[] = {  // Context Help IDs
    IDC_ADVO_GROUPBOX,               IDH_COMM_GROUPBOX,
    IDC_ADVO_ADVANCEDTEXT,       IDH_GROUPBOX,
    IDC_ADVO_ADV_RESTORE_DEF,    IDH_RESTORE_DEFAULT,
    IDC_ADVO_RESETTOORIGINAL,    IDH_RESET_TO_ORIGINAL,
    IDC_ADVO_USECURRENTFOLDER,   IDH_USE_CURRENT_FOLDER,
    IDC_ADVO_IMAGEFOLDER,       -1,        //Suppress help for this item.
    IDC_ADVO_STATICTEXT,        -1,        //Suppress help for this item.
    0, 0
};


//
// AdvancedDlgProc
//
// History:
//
//
BOOL_PTR CALLBACK AdvancedOptionsDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPADVANCEDPAGE pAdv = (LPADVANCEDPAGE)GetWindowPtr(hDlg, DWLP_USER);

    if (uMsg == WM_INITDIALOG)
    {
        // Initialize Dialog controls
        return AdvancedDlgInit(hDlg, lParam);
    }

    if (pAdv)
    {
        switch (uMsg)
        {

        case WM_NOTIFY:
            AdvancedDlgOnNotify(pAdv, (LPNMHDR)lParam);
            return TRUE;
            break;

        case WM_COMMAND:
            switch (GET_WM_COMMAND_ID(wParam, lParam))
            {
            case IDC_ADVO_ADV_RESTORE_DEF:
                pAdv->pTO->WalkTree(WALK_TREE_RESTORE);
                ENABLEAPPLY(hDlg);
                pAdv->fDirty = TRUE;
                break;

            case IDC_ADVO_USECURRENTFOLDER:
                if (ShellMessageBox(HINST_THISDLL, hDlg,
                                    MAKEINTRESOURCE(IDS_LIKECURRENT_TEXT),
                                    MAKEINTRESOURCE(IDS_FOLDERVIEWS),
                                    MB_YESNO | MB_ICONINFORMATION) == IDYES)
                {
                    pAdv->ppsx->SetAsDefFolderSettings();
                    pAdv->ppsx->SetNeedRefresh(TRUE);
                    pAdv->fDirty = TRUE;
                }
                break;

            case IDC_ADVO_RESETTOORIGINAL:
                if (ShellMessageBox(HINST_THISDLL, hDlg,
                                    MAKEINTRESOURCE(IDS_RESETALL_TEXT),
                                    MAKEINTRESOURCE(IDS_FOLDERVIEWS),
                                    MB_YESNO | MB_ICONINFORMATION) == IDYES)
                {
                    pAdv->ppsx->ResetDefFolderSettings();
                    pAdv->ppsx->SetNeedRefresh(TRUE);
                    pAdv->fDirty = TRUE;
                }
            
                break;
            }
            break;

        case WM_HELP:                   // F1
        {
            HELPINFO *phi = (HELPINFO *)lParam;

            //if the help is for one of the command buttons then call winhelp 
            if (phi->iCtrlId != IDC_ADVO_ADVANCEDTREE)
            {
                WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, TEXT(SHELL_HLP),
                             HELP_WM_HELP, (DWORD_PTR)(LPSTR)aAdvOptsHelpIDs);
            }
            else 
            {
                //Help is for the tree item so we need to do some special processing
                
                HTREEITEM hItem;

                // Is this help invoked throught F1 key
                if (GetAsyncKeyState(VK_F1) < 0)                
                {
                    // Yes. WE need to give help for the currently selected item
                    hItem = TreeView_GetSelection(pAdv->hwndTree);
                }
                else 
                {
                    //No, We need to give help for the item at the cursor position 
                    TV_HITTESTINFO ht;
                    ht.pt = phi->MousePos;
                    ScreenToClient(pAdv->hwndTree, &ht.pt); // Translate it to our window
                    hItem = TreeView_HitTest(pAdv->hwndTree, &ht);
                }

                if (FAILED(pAdv->pTO->ShowHelp(hItem, HELP_WM_HELP)))
                {
                    WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, NULL,
                                HELP_WM_HELP, (DWORD_PTR)(LPSTR)aAdvOptsHelpIDs);
                }
            }
            break; 
        }

        case WM_CONTEXTMENU:        // right mouse click
        {
            HTREEITEM hti;

            if ((LPARAM)-1 == lParam)
            {
                hti = TreeView_GetSelection(pAdv->hwndTree);
            }
            else
            {
                TV_HITTESTINFO ht;

                ht.pt.x = GET_X_LPARAM(lParam);
                ht.pt.y = GET_Y_LPARAM(lParam);
                ScreenToClient(pAdv->hwndTree, &ht.pt);

                hti = TreeView_HitTest(pAdv->hwndTree, &ht);
            }

            // retrieve the item hit
            if (FAILED(pAdv->pTO->ShowHelp(hti, HELP_CONTEXTMENU)))
            {           
                WinHelp((HWND) wParam, TEXT(SHELL_HLP),
                            HELP_CONTEXTMENU, (ULONG_PTR)(LPSTR)aAdvOptsHelpIDs);
            }
            break; 
        }

        case WM_DESTROY:
            // free the tree
            if (pAdv->pTO)
            {
                pAdv->pTO->WalkTree(WALK_TREE_DELETE);
                ATOMICRELEASE(pAdv->pTO);
            }

            LocalFree(pAdv);
            // make sure we don't re-enter
            SetWindowPtr(hDlg, DWLP_USER, NULL);
            break; // WM_DESTORY
        }
    }
    return FALSE; // not handled
}
