/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    twkeng.c

Abstract:

    UI engine for the kerntwk utility. Provides common
    registry/UI handling code to make it simple to add
    new property pages and items.

Author:

    John Vert (jvert) 10-Mar-1995

Revision History:

--*/
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include <windows.h>
#include <commctrl.h>
#include "dialogs.h"
#include "twkeng.h"

//
// Local type definitions
//

typedef enum _CONTROL_TYPE {
    Edit,
    Button,
    Unknown
} CONTROL_TYPE;



//
// Globals to this module
//

PTWEAK_PAGE CurrentPage=NULL;

//
// Prototypes for local functions
//
CONTROL_TYPE
GetControlType(
    HWND hDlg,
    ULONG ControlId
    );

VOID
InitializeKnobs(
    HWND hDlg
    );

PKNOB
FindKnobById(
    HWND hPage,
    ULONG DialogId
    );

BOOL
ProcessCommand(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    );

BOOL
ApplyChanges(
    HWND hDlg
    );

INT_PTR
APIENTRY
RebootDlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    );

BOOL
ApplyChanges(
    HWND hDlg
    )
{
    PTWEAK_PAGE Page;
    PKNOB Current;
    int i;

    Page = (PTWEAK_PAGE)GetWindowLongPtr(hDlg, DWLP_USER);

    //
    // Iterate through the knobs and set their values into the controls
    //
    i = 0;
    Current = Page->Knobs[0];
    while (Current != NULL) {
        HWND hControl;
        TCHAR ClassName[100];
        BOOL Translated;

        //
        // Determine whether the control is an edit
        // control or a check box
        //
        switch (GetControlType(hDlg, Current->DialogId)) {
            case Button:

                Current->NewValue = IsDlgButtonChecked(hDlg, Current->DialogId);
                Current->Flags &= ~KNOB_NO_NEW_VALUE;
                break;

            case Edit:
                Current->NewValue = GetDlgItemInt(hDlg,
                                                  Current->DialogId,
                                                  &Translated,
                                                  FALSE);
                if (!Translated) {
                    Current->Flags |= KNOB_NO_NEW_VALUE;
                } else {
                    Current->Flags &= ~KNOB_NO_NEW_VALUE;
                }
                break;
        }
        Current = Page->Knobs[++i];
    }

    //
    // If this page has a dynamic callback, allow it to try and apply
    // its own knobs.  If there is no dynamic callback, or the callback's
    // initialization fails, get the defaults from the registry. If the
    // appropriate value does not exist, set the knob to be empty.
    //
    if ((Page->DynamicChange == NULL) ||
        (Page->DynamicChange(FALSE,hDlg) == FALSE)) {
        //
        // Attempt to update registry from the knobs
        //
        i = 0;
        Current = Page->Knobs[i];
        while (Current != NULL) {
            LONG Result;
            HKEY Key;
            DWORD Size;
            DWORD Value;
            DWORD Disposition;

            if (Current->KeyPath != NULL) {
                Result = RegCreateKeyEx(Current->RegistryRoot,
                                        Current->KeyPath,
                                        0,
                                        NULL,
                                        0,
                                        MAXIMUM_ALLOWED,
                                        NULL,
                                        &Key,
                                        &Disposition);
                if (Result == ERROR_SUCCESS) {
                    if (Current->Flags & KNOB_NO_NEW_VALUE) {
                        //
                        // Try and delete the value
                        //
                        Result = RegDeleteValue(Key, Current->ValueName);
                        RegCloseKey(Key);
                        if (Result == ERROR_SUCCESS) {
                            Current->Flags |= KNOB_NO_CURRENT_VALUE;
                        }

                    } else {
                        //
                        // Set the current value
                        //
                        Result = RegSetValueEx(Key,
                                               Current->ValueName,
                                               0,
                                               REG_DWORD,
                                               (LPBYTE)&Current->NewValue,
                                               sizeof(Current->NewValue));
                        RegCloseKey(Key);
                        if (Result == ERROR_SUCCESS) {
                            Current->CurrentValue = Current->NewValue;
                            Current->Flags &= ~KNOB_NO_CURRENT_VALUE;
                        }
                    }
                }
            } else {
                Current->CurrentValue = Current->NewValue;
                Current->Flags = 0;
            }

            Current = Page->Knobs[++i];
        }
        SendMessage(GetParent(hDlg),
                    PSM_REBOOTSYSTEM,
                    0,
                    0);
    }
    return(TRUE);
}

BOOL
ProcessCommand(
    HWND hDlg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    PKNOB Knob;
    ULONG DialogId;
    BOOL Translated;

    DialogId = LOWORD(wParam);
    Knob = FindKnobById(hDlg, DialogId);
    switch (GetControlType(hDlg, DialogId)) {
        case Edit:
            if (HIWORD(wParam) == EN_CHANGE) {
                if (Knob != NULL) {
                    Knob->NewValue = GetDlgItemInt(hDlg, DialogId, &Translated, FALSE);
                    if ((Knob->NewValue != Knob->CurrentValue) ||
                        (Translated && (Knob->Flags & KNOB_NO_CURRENT_VALUE)) ||
                        (!Translated && ((Knob->Flags & KNOB_NO_CURRENT_VALUE) == 0))) {
                        SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
                    }
                    if (Translated) {
                        Knob->Flags &= ~KNOB_NO_NEW_VALUE;
                    }
                } else {
                    SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);

                }
                return(TRUE);
            }
            break;

        case Button:
            if (HIWORD(wParam) == BN_CLICKED) {
                if (Knob != NULL) {
                    Knob->NewValue = IsDlgButtonChecked(hDlg, DialogId);
                    if (Knob->NewValue != Knob->CurrentValue) {
                        SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
                    }
                } else {
                    SendMessage(GetParent(hDlg), PSM_CHANGED, (WPARAM)hDlg, 0);
                }
                return(TRUE);
            }
            break;
    }
    return(FALSE);
}


CONTROL_TYPE
GetControlType(
    HWND hDlg,
    ULONG ControlId
    )
{
    HWND hControl;
    TCHAR ClassName[100];

    hControl = GetDlgItem(hDlg, ControlId);
    if (hControl != NULL) {
        GetClassName(hControl, ClassName, 100);
        if (_stricmp(ClassName, "BUTTON")==0) {
            return(Button);
        } else if (_stricmp(ClassName, "EDIT") == 0) {
            return(Edit);
        }
    }

    return(Unknown);

}

VOID
InitializeKnobs(
    HWND hDlg
    )

{
    PTWEAK_PAGE Page;
    PKNOB Current;
    int i;

    Page = (PTWEAK_PAGE)GetWindowLongPtr(hDlg, DWLP_USER);
    //
    // If this page has a dynamic callback, allow it to try and initialize
    // its own knobs.  If there is no dynamic callback, or the callback's
    // initialization fails, get the defaults from the registry. If the
    // appropriate value does not exist, set the knob to be empty.
    //
    if ((Page->DynamicChange == NULL) ||
        (Page->DynamicChange(TRUE,hDlg) == FALSE)) {
        //
        // Attempt to initialize knobs from the registry.
        //
        i = 0;
        Current = Page->Knobs[0];
        while (Current != NULL) {
            LONG Result;
            HKEY Key;
            DWORD Size;
            DWORD Value;

            Result = RegOpenKeyEx(Current->RegistryRoot,
                                  Current->KeyPath,
                                  0,
                                  KEY_QUERY_VALUE,
                                  &Key);
            if (Result == ERROR_SUCCESS) {
                //
                // Query out the value we are interested in
                //
                Size = 4;
                Result = RegQueryValueEx(Key,
                                         Current->ValueName,
                                         0,
                                         NULL,
                                         (LPBYTE)&Value,
                                         &Size);
                RegCloseKey(Key);
                if (Result == ERROR_SUCCESS) {
                    Current->Flags = 0;
                    Current->CurrentValue = Value;
                }
            }

            if (Result != ERROR_SUCCESS) {
                Current->Flags |= KNOB_NO_CURRENT_VALUE;
                Current->Flags |= KNOB_NO_NEW_VALUE;
            }
            Current = Page->Knobs[++i];
        }
    }

    //
    // Iterate through the knobs and set their values into the controls
    //
    i = 0;
    Current = Page->Knobs[0];
    while (Current != NULL) {
        HWND hControl;
        TCHAR ClassName[100];

        //
        // Determine whether the control is an edit
        // control or a check box
        //
        if ((Current->Flags & KNOB_NO_CURRENT_VALUE) == 0) {
            switch (GetControlType(hDlg, Current->DialogId)) {
                case Button:
                    CheckDlgButton(hDlg,
                                   Current->DialogId,
                                   Current->CurrentValue);
                    break;

                case Edit:
                    SetDlgItemInt(hDlg,
                                  Current->DialogId,
                                  Current->CurrentValue,
                                  FALSE);
                    break;
            }
        }
        Current = Page->Knobs[++i];
    }
}

PKNOB
FindKnobById(
    HWND  hPage,
    ULONG DialogId
    )
{
    PTWEAK_PAGE Page;
    PKNOB Current;
    int i;

    Page = (PTWEAK_PAGE)GetWindowLongPtr(hPage, DWLP_USER);
    i=0;
    Current = Page->Knobs[0];
    while (Current != NULL) {
        if (Current->DialogId == DialogId) {
            break;
        }
        Current = Page->Knobs[++i];
    }
    return(Current);
}

INT_PTR
APIENTRY
PageDlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    LPNMHDR Notify;
    PTWEAK_PAGE TweakPage;

    TweakPage = (PTWEAK_PAGE)GetWindowLongPtr(hDlg, DWLP_USER);
    switch (message) {
        case WM_INITDIALOG:

            //
            // This page is being created.
            //
            TweakPage = (PTWEAK_PAGE)((LPPROPSHEETPAGE)lParam)->lParam;

            //
            // Stash a pointer to our page
            //
            SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR)TweakPage);

            //
            // Initialize controls.
            //

            InitializeKnobs(hDlg);
            return(TRUE);

        case WM_COMMAND:

            return(ProcessCommand(hDlg, wParam, lParam));

        case WM_NOTIFY:
            Notify = (LPNMHDR)lParam;
            switch (Notify->code) {
                case PSN_SETACTIVE:
                    CurrentPage = TweakPage;
                    break;
                case PSN_APPLY:
                    //
                    // User has chosen to apply the changes.
                    //
                    if (ApplyChanges(hDlg)) {
                        SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
                        return(TRUE);
                    }
            }
    }
    return FALSE;
}

int
TweakSheet(
    DWORD PageCount,
    PTWEAK_PAGE TweakPages[]
    )

/*++

Routine Description:

    Assembles the appropriate structures for a property sheet
    and creates the sheet.

Arguments:

    PageCount - Supplies the number of pages.

    TweakPages - Supplies the pages.

Return Value:

    Return value from PropertySheet()

--*/

{
    PROPSHEETHEADER psh;
    PROPSHEETPAGE *Page;
    DWORD i;
    INT_PTR Status;

    Page = LocalAlloc(LMEM_FIXED, PageCount * sizeof(PROPSHEETPAGE));
    if (Page==NULL) {
        return(ERROR_NOT_ENOUGH_MEMORY);
    }

    //
    // Initialize pages.
    //
    for (i=0; i<PageCount; i++) {
        Page[i].dwSize = sizeof(PROPSHEETPAGE);
        Page[i].dwFlags = PSP_USEICONID;
        Page[i].hInstance = GetModuleHandle(NULL);
        Page[i].pszIcon = MAKEINTRESOURCE(IDI_KERNTWEAK);
        Page[i].pszTemplate = TweakPages[i]->DlgTemplate;
        Page[i].pfnDlgProc = PageDlgProc;
        Page[i].pszTitle = NULL;
        Page[i].lParam = (LPARAM)TweakPages[i];
    }

    //
    // Initialize header.
    //
    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_USEICONID | PSH_PROPSHEETPAGE;
    psh.hwndParent = NULL;
    psh.hInstance = GetModuleHandle(NULL);
    psh.pszIcon = MAKEINTRESOURCE(IDI_KERNTWEAK);
    psh.pszCaption = TEXT("Windows NT Kernel Tweaker");
    psh.nPages = PageCount;
    psh.ppsp = (LPCPROPSHEETPAGE)Page;

    CurrentPage = TweakPages[0];

    Status = PropertySheet(&psh);
    if ((Status == ID_PSREBOOTSYSTEM) ||
        (Status == ID_PSRESTARTWINDOWS)) {
        BOOLEAN Enabled;

        Status = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(DLG_REBOOT), NULL, RebootDlgProc);
        RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE,
                           TRUE,
                           FALSE,
                           &Enabled);
        if (Status == ID_FAST_REBOOT) {
            ExitWindowsEx(EWX_FORCE | EWX_REBOOT, 0);
        } else if (Status == ID_SLOW_REBOOT) {
            ExitWindowsEx(EWX_REBOOT, 0);
        }
    }

    return((int)Status);
}

INT_PTR
APIENTRY
RebootDlgProc(
    HWND hDlg,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
    )
{
    switch (message) {
        case WM_COMMAND:
            if (HIWORD(wParam) == BN_CLICKED) {
                switch (LOWORD(wParam)) {
                    case ID_FAST_REBOOT:
                        EndDialog(hDlg, ID_FAST_REBOOT);
                        return(1);
                    case ID_SLOW_REBOOT:
                        EndDialog(hDlg, ID_SLOW_REBOOT);
                        return(1);
                    case ID_NO_REBOOT:
                        EndDialog(hDlg, ID_NO_REBOOT);
                        return(1);
                }
            }
    }
    return(0);
}