/*++

Copyright (c) 2000-2001  Microsoft Corporation

Module Name:

    prcdlg.cpp

Abstract:

    Contains dialog-related functions.

--*/

#include "precomp.hxx"
#pragma hdrstop

#include <time.h>

// Remote client workspaces do not contain engine information
// since a remote client attaches to many engines and it's
// rare that you'd want the same piece of engine information
// applied to every connect.
#define SAVE_ENGINE_WORKSPACE() \
    (g_Workspace != NULL && !g_RemoteClient)
#define DIRTY_ENGINE_WORKSPACE(Flags) FALSE

// Find dialog, if open.
HWND g_FindDialog;
// Find text.
char g_FindText[256];
// Message code for FINDMSGSTRING.
UINT g_FindMsgString;
FINDREPLACE g_FindRep;
PCOMMONWIN_DATA g_FindLast;

char g_ComSettings[64];
char g_1394Settings[64];

TCHAR szOpenExeArgs[2 * _MAX_PATH];
TCHAR g_DlgString[2 * _MAX_PATH];
TCHAR g_DlgString2[2 * _MAX_PATH];

PCSTR g_ExecutionNames[] =
{
    "enabled", "disabled", "output", "ignore"
};
PCSTR g_ContinueNames[] =
{
    "handled", "not handled"
};

PSTR g_SymbolTypeNames[] =
{
    "None", "COFF", "CodeView", "PDB", "Export", "Deferred", "Sym",
    "DIA",
};

PTSTR __cdecl
BufferString(PTSTR Buffer, ULONG Size, ULONG StrId, ...)
{
    va_list Args;
    TCHAR FmtStr[SHORT_MSG];

    Buffer[0] = 0;
    Buffer[Size - 1] = 0;
    Dbg(LoadString(g_hInst, StrId, FmtStr, _tsizeof(FmtStr)));
    va_start(Args, StrId);
    _vsnprintf(Buffer, Size, FmtStr, Args);
    va_end(Args);
    return Buffer;
}

void
SendLockStatusMessage(HWND Win, UINT Msg, HRESULT Status)
{
    TCHAR Str[SHORT_MSG];

    if (FAILED(Status))
    {
        BufferString(Str, _tsizeof(Str), ERR_Unable_To_Retrieve_Info,
                     FormatStatusCode(Status), FormatStatus(Status));
    }
    else
    {
        BufferString(Str, _tsizeof(Str),
                     STR_Retrieving_Information);
    }
    SendMessage(Win, Msg, 0, (LPARAM)Str);
}

BpStateType
IsBpAtOffset(BpBufferData* DataIn, ULONG64 Offset, PULONG Id)
{
    BpBufferData* Data;
    
    if (DataIn == NULL)
    {
        if (g_BpBuffer->UiLockForRead() != S_OK)
        {
            return BP_NONE;
        }

        Data = (BpBufferData*)g_BpBuffer->GetDataBuffer();
    }
    else
    {
        Data = DataIn;
    }
    
    ULONG i;
    BpStateType BpState = BP_NONE;

    for (i = 0; i < g_BpCount; i++)
    {
        if (Data->Offset != DEBUG_INVALID_OFFSET &&
            Data->Offset == Offset)
        {
            if (Data->Flags & DEBUG_BREAKPOINT_ENABLED)
            {
                BpState = BP_ENABLED;
            }
            else
            {
                BpState = BP_DISABLED;
            }
            if (Id != NULL)
            {
                *Id = Data->Id;
            }
            break;
        }

        Data++;
    }

    if (DataIn == NULL)
    {
        UnlockStateBuffer(g_BpBuffer);
    }

    return BpState;
}

BOOL
GetBpBufferData(ULONG Index, BpBufferData* RetData)
{
    if (Index >= g_BpCount ||
        g_BpBuffer->UiLockForRead() != S_OK)
    {
        return FALSE;
    }

    BpBufferData* Data = (BpBufferData*)g_BpBuffer->GetDataBuffer();
    *RetData = Data[Index];

    UnlockStateBuffer(g_BpBuffer);
    return TRUE;
}

void
EnableBpButtons(BOOL UpdateThread)
{
    HWND Dlg = g_BpBuffer->m_Win;
    
    LRESULT Sel = SendDlgItemMessage(Dlg, ID_SETBREAK_BREAKPOINT,
                                     LB_GETCURSEL, 0, 0);
    BOOL Enable = Sel != LB_ERR;

    EnableWindow(GetDlgItem(Dlg, ID_SETBREAK_REMOVE), Enable);
    EnableWindow(GetDlgItem(Dlg, ID_SETBREAK_DISABLE), Enable);
    EnableWindow(GetDlgItem(Dlg, ID_SETBREAK_ENABLE), Enable);

    if (UpdateThread)
    {
        BpBufferData Data;
        char Text[16];

        Text[0] = 0;
        if (Sel >= 0 &&
            GetBpBufferData((ULONG)Sel, &Data) &&
            Data.Thread != DEBUG_ANY_ID)
        {
            _itoa(Data.Thread, Text, 10);
        }
        SetWindowText(GetDlgItem(Dlg, ID_SETBREAK_THREAD), Text);
    }
    
    EnableWindow(GetDlgItem(Dlg, ID_SETBREAK_THREAD),
                 GetWindowTextLength(GetDlgItem(Dlg,
                                                ID_SETBREAK_COMMAND)) > 0);
}

void
FillBpList(HWND List)
{
    HRESULT Status;
    ULONG Width;
    RECT ListRect;

    LRESULT ListSel = SendMessage(List, LB_GETCURSEL, 0, 0);
    SendMessage(List, WM_SETREDRAW, FALSE, 0);
    SendMessage(List, LB_RESETCONTENT, 0, 0);

    GetClientRect(List, &ListRect);
    Width = ListRect.right - ListRect.left;
    
    Status = g_BpBuffer->UiLockForRead();
    if (Status == S_OK)
    {
        PSTR Buf = (PSTR)g_BpBuffer->GetDataBuffer() + g_BpTextOffset;
        // Ignore trailing terminator.
        PSTR End = (PSTR)g_BpBuffer->GetDataBuffer() +
            g_BpBuffer->GetDataLen() - 1;

        while (Buf < End)
        {
            ULONG Len, BufWidth;

            Len = strlen(Buf) + 1;
            if (Len == 1)
            {
                // Break out on empty lines to avoid displaying
                // kernel internal breakpoint information and blank
                // line separating it from the normal breakpoints.
                break;
            }
            
            SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Buf);

            // Extra char in Len due to terminator ensures there's
            // a character of extra space to the right side.
            BufWidth = g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth * Len;
            if (BufWidth > Width)
            {
                Width = BufWidth;
            }
            
            Buf += Len;
        }

        UnlockStateBuffer(g_BpBuffer);
    }
    else
    {
        SendLockStatusMessage(List, LB_ADDSTRING, Status);
    }

    SendMessage(List, LB_SETHORIZONTALEXTENT, Width, 0);
    SendMessage(List, WM_SETREDRAW, TRUE, 0);
    if (ListSel != LB_ERR)
    {
        SendMessage(List, LB_SETCURSEL, ListSel, 0);
    }
    EnableBpButtons(TRUE);
}

ULONG
GetBpListId(HWND List)
{
    LRESULT ListSel = SendMessage(List, LB_GETCURSEL, 0, 0);
    if (ListSel == LB_ERR)
    {
        return DEBUG_ANY_ID;
    }

    LRESULT Len = SendMessage(List, LB_GETTEXTLEN, ListSel, 0);
    PSTR Buf = (PSTR)malloc(Len + 1);
    if (Buf == NULL)
    {
        return DEBUG_ANY_ID;
    }

    SendMessage(List, LB_GETTEXT, ListSel, (LPARAM)Buf);

    ULONG Id;
    if (sscanf(Buf, "%d", &Id) != 1)
    {
        return DEBUG_ANY_ID;
    }
    else
    {
        return Id;
    }
}

// Space for "Pending: ".
#define BP_PENDING_CHARS 9

void
AddBpCommandString(HWND List, PSTR Buf, ULONG Thread)
{
    BOOL HasThread = FALSE;
    BOOL IsBpCmd = FALSE;
    PCSTR Scan;

    //
    // If the string looks like a real command just pass
    // it on for execution.  Otherwise, assume it's
    // a code breakpoint address expression.
    //
    
    Scan = Buf + BP_PENDING_CHARS;
    
    if (*Scan == '~')
    {
        Scan++;
        HasThread = TRUE;
        
        // Skip optional thread indicator.
        if (*Scan == '.' || *Scan == '#')
        {
            Scan++;
        }
        else
        {
            while (*Scan >= '0' && *Scan <= '9')
            {
                Scan++;
            }
        }
    }
    
    if (*Scan == 'b' || *Scan == 'B')
    {
        Scan++;
        if (*Scan == 'a' || *Scan == 'A' ||
            *Scan == 'i' || *Scan == 'I' ||
            *Scan == 'p' || *Scan == 'P' ||
            *Scan == 'u' || *Scan == 'U' ||
            *Scan == 'w' || *Scan == 'W')
        {
            Scan++;

            // Skip optional bp id.
            while (*Scan >= '0' && *Scan <= '9')
            {
                Scan++;
            }
            
            if (*Scan == ' ')
            {
                IsBpCmd = TRUE;
            }
        }
        else if (*Scan == 'c' || *Scan == 'C' ||
                 *Scan == 'd' || *Scan == 'D' ||
                 *Scan == 'e' || *Scan == 'E')
        {
            Scan++;
            if (*Scan == ' ')
            {
                IsBpCmd = TRUE;
            }
        }
    }

    // Immediately add the breakpoint string to the listbox
    // to indicate that it's pending.
    memcpy(Buf, "Pending: ", BP_PENDING_CHARS);
    LRESULT Sel = SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Buf);
    if (Sel != LB_ERR && Sel != LB_ERRSPACE)
    {
        SendMessage(List, LB_SETCURSEL, Sel, 0);
    }

    Buf += BP_PENDING_CHARS;
    if (IsBpCmd)
    {
        if (HasThread || Thread == DEBUG_ANY_ID)
        {
            AddStringCommand(UIC_SILENT_EXECUTE, Buf);
        }
        else
        {
            PrintStringCommand(UIC_SILENT_EXECUTE, "~%d%s",
                               Thread, Buf);
        }
    }
    else if (Thread == DEBUG_ANY_ID)
    {
        PrintStringCommand(UIC_SILENT_EXECUTE, "bu %s", Buf);
    }
    else
    {
        PrintStringCommand(UIC_SILENT_EXECUTE, "~%dbu %s", Thread, Buf);
    }
}

INT_PTR CALLBACK
DlgProc_SetBreak(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    static UINT s_CmdLen;
    HWND List;
    HWND Ctrl;
    ULONG Id;
    
    List = GetDlgItem(Hwnd, ID_SETBREAK_BREAKPOINT);
    switch(Message)
    {
    case WM_INITDIALOG:
        s_CmdLen = 0;
        g_BpBuffer->m_Win = Hwnd;
        SendMessage(List, WM_SETFONT, (WPARAM)g_Fonts[FONT_FIXED].Font, FALSE);
        SendMessage(List, LB_SETCURSEL, -1, 0);
        FillBpList(List);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case ID_SETBREAK_COMMAND:
            if (HIWORD(Wpm) == EN_CHANGE)
            {
                EnableBpButtons(FALSE);

                UINT NewLen = GetWindowTextLength
                    (GetDlgItem(Hwnd, ID_SETBREAK_COMMAND));
                if (NewLen == 1 && s_CmdLen == 0)
                {
                    // If we're starting a new breakpoint command
                    // default the thread to nothing.
                    SetWindowText(GetDlgItem(Hwnd, ID_SETBREAK_THREAD), "");
                }
                s_CmdLen = NewLen;
            }
            break;
            
        case ID_SETBREAK_BREAKPOINT:
            SetWindowText(GetDlgItem(Hwnd, ID_SETBREAK_COMMAND), "");
            EnableBpButtons(TRUE);
            break;
            
        case ID_SETBREAK_REMOVE:
            Id = GetBpListId(List);
            if (Id != DEBUG_ANY_ID)
            {
                SetWindowText(GetDlgItem(Hwnd, ID_SETBREAK_COMMAND), "");
                PrintStringCommand(UIC_SILENT_EXECUTE, "bc %d", Id);
                DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_BREAKPOINTS);
            }
            break;

        case ID_SETBREAK_REMOVEALL:
            SetWindowText(GetDlgItem(Hwnd, ID_SETBREAK_COMMAND), "");
            AddStringCommand(UIC_SILENT_EXECUTE, "bc *");
            DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_BREAKPOINTS);
            break;
            
        case ID_SETBREAK_ENABLE:
        case ID_SETBREAK_DISABLE:
            Id = GetBpListId(List);
            if (Id != DEBUG_ANY_ID)
            {
                SetWindowText(GetDlgItem(Hwnd, ID_SETBREAK_COMMAND), "");
                PrintStringCommand(UIC_SILENT_EXECUTE, "b%c %d",
                                   LOWORD(Wpm) == ID_SETBREAK_ENABLE ?
                                   'e' : 'd', Id);
                DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_BREAKPOINTS);
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_BREAKPOINTS);
            break;
            
        case IDOK:
            char Buf[1024 + BP_PENDING_CHARS];
            UINT Thread;
            BOOL ThreadValid;

            Thread = GetDlgItemInt(Hwnd, ID_SETBREAK_THREAD,
                                   &ThreadValid, FALSE);
            if (!ThreadValid)
            {
                Thread = DEBUG_ANY_ID;
            }
            
            Ctrl = GetDlgItem(Hwnd, ID_SETBREAK_COMMAND);
            if (SendMessage(Ctrl, WM_GETTEXT, sizeof(Buf) - BP_PENDING_CHARS,
                            (LPARAM)(Buf + BP_PENDING_CHARS)) > 0)
            {
                AddBpCommandString(List, Buf, Thread);
                DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_BREAKPOINTS);
                SendMessage(Ctrl, WM_SETTEXT, 0, (LPARAM)"");
                // A command was executed so do not close the dialog.
                break;
            }
            
            // No command so fall through to close dialog.
            
        case IDCANCEL:
            g_BpBuffer->m_Win = NULL;
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    case LB_RESETCONTENT:
        FillBpList(List);
        break;
        
    default:
        return FALSE;
    }

    return TRUE;
}


UINT_PTR
OpenExeWithArgsHookProc(
    HWND    hDlg,
    UINT    msg,
    WPARAM  wParam,
    LPARAM  lParam
    )

/*++

Routine Description:

    Allows the user to specify command line arguments when opening an
    executable.

Return Value:

    TRUE if we replaced default processing of the message, FALSE otherwise

--*/
{
    switch(msg)
    {
    case WM_INITDIALOG:
        CheckDlgButton(hDlg, IDC_EXEOPEN_CHILD_PROCESSES, FALSE);
        return TRUE;

    case WM_NOTIFY:
        if (((LPOFNOTIFY) lParam)->hdr.code == CDN_FILEOK)
        {
            *szOpenExeArgs = _T(' ');

            GetDlgItemText(hDlg,
                           IDC_EDIT_ARGS,
                           szOpenExeArgs + 1,
                           sizeof(szOpenExeArgs) - 1);
            
            if (IsDlgButtonChecked(hDlg, IDC_EXEOPEN_CHILD_PROCESSES) ==
                BST_CHECKED)
            {
                g_DebugCreateFlags |= DEBUG_PROCESS;
                g_DebugCreateFlags &= ~DEBUG_ONLY_THIS_PROCESS;
            }
            else
            {
                g_DebugCreateFlags &= ~DEBUG_PROCESS;
                g_DebugCreateFlags |= DEBUG_ONLY_THIS_PROCESS;
            }
            
            return 0;
        }
    }

    return DlgFile(hDlg, msg, wParam, lParam);
}

#define NO_SERVERS "No servers registered"

void
FillServersList(HWND List, PCSTR Machine)
{
    HRESULT Status;
    
    g_pUiClient->SetOutputCallbacks(&g_UiOutStateBuf);
    g_UiOutStateBuf.SetBuffer(&g_UiOutputCapture);
    g_UiOutStateBuf.Start(TRUE);
    Status = g_pUiClient->OutputServers(DEBUG_OUTCTL_THIS_CLIENT, Machine,
                                        DEBUG_SERVERS_DEBUGGER);
    g_pUiClient->SetOutputCallbacks(NULL);
    if (Status == S_OK)
    {
        Status = g_UiOutStateBuf.End(FALSE);
    }
    else
    {
        g_UiOutStateBuf.End(FALSE);
    }
    g_UiOutStateBuf.ReplaceChar('\n', 0);

    SendMessage(List, LB_RESETCONTENT, 0, 0);
    if (Status == S_OK)
    {
        PSTR Line, End;
    
        Line = (PSTR)g_UiOutputCapture.GetDataBuffer();
        End = Line + g_UiOutputCapture.GetDataLen() - 1;
        if (Line == NULL || Line >= End)
        {
            SendMessage(List, LB_ADDSTRING, 0, (LPARAM)NO_SERVERS);
        }
        else
        {
            while (Line < End)
            {
                SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Line);
                Line += strlen(Line) + 1;
            }
        }
    }
    else
    {
        SendLockStatusMessage(List, LB_ADDSTRING, Status);
    }
    SendMessage(List, LB_SETCURSEL, 0, 0);

    g_UiOutputCapture.Free();
}

INT_PTR CALLBACK
DlgProc_BrowseServers(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Machine[128];
    LRESULT Sel;

    switch(Message)
    {
    case WM_INITDIALOG:
        SendDlgItemMessage(Hwnd, IDC_BROWSE_MACHINE, EM_LIMITTEXT,
                           sizeof(Machine) - 1, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_BROWSE_MACHINE), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_BROWSE_REFRESH:
            if (SendDlgItemMessage(Hwnd, IDC_BROWSE_MACHINE, WM_GETTEXT,
                                   sizeof(Machine), (LPARAM)Machine))
            {
                FillServersList(GetDlgItem(Hwnd, IDC_BROWSE_SERVERS_LIST),
                                Machine);
            }
            break;

        case IDC_BROWSE_SERVERS_LIST:
            switch(HIWORD(Wpm))
            {
            case LBN_DBLCLK:
                Wpm = IDOK;
                goto SelectAndEnd;
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_CONNECT_TO_REMOTE_SESSION);
            break;
            
        case IDOK:
        SelectAndEnd:
            Sel = SendDlgItemMessage(Hwnd, IDC_BROWSE_SERVERS_LIST,
                                     LB_GETCURSEL, 0, 0);
            if (Sel >= 0 &&
                SendDlgItemMessage(Hwnd, IDC_BROWSE_SERVERS_LIST,
                                   LB_GETTEXT, Sel, (LPARAM)g_DlgString) > 0 &&
                strcmp(g_DlgString, NO_SERVERS) &&
                SendDlgItemMessage(Hwnd, IDC_BROWSE_MACHINE, WM_GETTEXT,
                                   sizeof(Machine), (LPARAM)Machine))
            {
                strcat(g_DlgString, ",Server=");
                strcat(g_DlgString, Machine);
            }
            else
            {
                g_DlgString[0] = 0;
            }
            
            // Fall through.
            
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

INT_PTR CALLBACK
DlgProc_ConnectToRemote(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    TCHAR ConnectString[1024];

    switch (Message)
    {
    case WM_INITDIALOG:
        //
        // Set up the controls to reflect current values
        //
        SendDlgItemMessage(Hwnd, IDC_REM_CONNECT, EM_LIMITTEXT,
                           sizeof(ConnectString) - 1, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_REM_CONNECT), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_REM_BROWSE:
            if (StartDialog(IDD_DLG_BROWSE_SERVERS, DlgProc_BrowseServers,
                            NULL) == IDOK && g_DlgString[0])
            {
                // Skip "<Server type> - " at the beginning.
                PSTR Start = strchr(g_DlgString, '-');
                if (Start != NULL)
                {
                    SetWindowText(GetDlgItem(Hwnd, IDC_REM_CONNECT),
                                  Start + 2);
                }
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_CONNECT_TO_REMOTE_SESSION);
            break;
            
        case IDOK:
            if (SendDlgItemMessage(Hwnd, IDC_REM_CONNECT, WM_GETTEXT,
                                   sizeof(ConnectString),
                                   (LPARAM)ConnectString))
            {
                if (CreateUiInterfaces(TRUE, ConnectString))
                {
                    StartDebugging();
                    EndDialog(Hwnd, IDOK);
                }
                else if (!CreateUiInterfaces(FALSE, NULL))
                {
                    // CreateUiInterfaces discards any previous
                    // interfaces so we need to recreate something
                    // so there are UI thread interfaces.
                    InformationBox(ERR_Internal_Error, E_OUTOFMEMORY,
                                   "CreateUiInterfaces");
                    ErrorExit(NULL, "Unable to recreate UI interfaces\n");
                }
            }

            return TRUE;

        case IDCANCEL:
            EndDialog(Hwnd, IDCANCEL);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

INT_PTR
CALLBACK
DlgProc_SymbolPath(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    TCHAR   SymPath[MAX_ENGINE_PATH];
    ULONG   PathSize;
    LRESULT Size;
    HRESULT Hr;

    switch (Message)
    {
    case WM_INITDIALOG:
        Hr = g_pUiSymbols->GetSymbolPath(SymPath, _tsizeof(SymPath),
                                         &PathSize);
        if (Hr != S_OK)
        {
            InformationBox(ERR_Internal_Error, Hr, "UI GetSymPath");
        }

        SendDlgItemMessage(Hwnd, IDC_SYMPATH, WM_SETTEXT, 0, (LPARAM)SymPath);

        //
        // Set up the controls to reflect current values
        //
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_SYMBOL_PATH);
            break;
            
        case IDOK:
            Size = SendDlgItemMessage(Hwnd, IDC_SYMPATH, WM_GETTEXT,
                                      sizeof(SymPath), (LPARAM)SymPath);
            if (Size == 0)
            {
                InformationBox(ERR_Path_Empty);
                SetFocus(Hwnd);
                return TRUE;
            }
            SymPath[Size] = 0;

            Hr = g_pUiSymbols->SetSymbolPath(SymPath);
            if (Hr != S_OK)
            {
                InformationBox(ERR_Internal_Error, Hr, "UI SetSymPath");
            }

            if (SAVE_ENGINE_WORKSPACE())
            {
                g_Workspace->SetString(WSP_GLOBAL_SYMBOL_PATH, SymPath);
            }
                                             
            EndDialog(Hwnd, IDOK);
            break;

        case IDCANCEL:
            EndDialog(Hwnd, IDCANCEL);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

INT_PTR CALLBACK
DlgProc_RegCustomize(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    CPUWIN_DATA* CpuWin;

    switch(Message)
    {
    case WM_INITDIALOG:
        SetWindowLongPtr(Hwnd, DWLP_USER, Lpm);
        GetRegisterMapText(GetDlgItem(Hwnd, IDC_REGCUST_ENTRY));
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_REGISTERS);
            break;
            
        case IDOK:
            ScanRegisterMapText(GetDlgItem(Hwnd, IDC_REGCUST_ENTRY));

            if (g_RegisterMap != NULL && g_Workspace != NULL)
            {
                g_Workspace->SetBuffer(WSP_GLOBAL_REGISTER_MAP,
                                       g_RegisterMap, g_RegisterMapEntries *
                                       sizeof(*g_RegisterMap));
            }
            
            CpuWin = (CPUWIN_DATA*)GetWindowLongPtr(Hwnd, DWLP_USER);
            CpuWin->UpdateNames((PSTR)g_RegisterNamesBuffer->GetDataBuffer());
            CpuWin->OnUpdate(UPDATE_BUFFER);
            
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_GotoLine(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Text[MAX_COMMAND_LEN];
    int Line;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SetWindowLongPtr(Hwnd, DWLP_USER, Lpm);
        SendDlgItemMessage(Hwnd, IDC_LINE_ENTRY, EM_LIMITTEXT,
                           sizeof(Text) - 1, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_LINE_ENTRY), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_GO_TO_LINE);
            break;
            
        case IDOK:
            COMMONWIN_DATA* CommonWinData;
            CommonWinData = (COMMONWIN_DATA*)GetWindowLongPtr(Hwnd, DWLP_USER);
            GetWindowText(GetDlgItem(Hwnd, IDC_LINE_ENTRY),
                          Text, sizeof(Text));
            Line = atoi(Text);
            if (Line > 0)
            {
                CommonWinData->GotoLine(Line);
            }
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_GotoAddress(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Text[MAX_COMMAND_LEN];
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SendDlgItemMessage(Hwnd, IDC_ADDRESS_ENTRY, EM_LIMITTEXT,
                           sizeof(Text) - 1, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_ADDRESS_ENTRY), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_GO_TO_ADDRESS);
            break;
            
        case IDOK:
            GetWindowText(GetDlgItem(Hwnd, IDC_ADDRESS_ENTRY),
                          Text, sizeof(Text));
            AddStringCommand(UIC_DISPLAY_CODE_EXPR, Text);
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_LogFile(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HRESULT Status;
    char LogFile[MAX_PATH];
    BOOL Append;
    HWND Ctrl;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        Append = FALSE;
        Ctrl = GetDlgItem(Hwnd, IDC_LOG_FILE_NAME);
        SendMessage(Ctrl, EM_LIMITTEXT, sizeof(LogFile) - 1, 0);
        Status = g_pUiControl->GetLogFile(LogFile, sizeof(LogFile), NULL,
                                          &Append);
        if (Status == E_NOINTERFACE)
        {
            // No current log file.
            SetWindowText(Ctrl, "");
        }
        else if (Status != S_OK)
        {
            SetWindowText(Ctrl, "Unable to retrieve name");
        }
        else
        {
            SetWindowText(Ctrl, LogFile);
        }
        EnableWindow(GetDlgItem(Hwnd, IDC_LOG_CLOSE), Status == S_OK);
        CheckDlgButton(Hwnd, IDC_LOG_APPEND,
                       Append ? BST_CHECKED : BST_UNCHECKED);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_LOG_CLOSE:
            g_pUiControl->CloseLogFile();
            DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_LOG_FILE);
            SetWindowText(GetDlgItem(Hwnd, IDC_LOG_FILE_NAME), "");
            EnableWindow(GetDlgItem(Hwnd, IDC_LOG_CLOSE), FALSE);
            break;

        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_LOG_FILE);
            break;
            
        case IDOK:
            GetWindowText(GetDlgItem(Hwnd, IDC_LOG_FILE_NAME),
                          LogFile, sizeof(LogFile));
            Append = IsDlgButtonChecked(Hwnd, IDC_LOG_APPEND) ==
                BST_CHECKED;
            if (LogFile[0] != 0)
            {
                g_pUiControl->OpenLogFile(LogFile, Append);
                DIRTY_ENGINE_WORKSPACE(WSPF_DIRTY_LOG_FILE);
            }
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

DLGPROC g_CurrentKd;

INT_PTR
CALLBACK
DlgProc_KernelCom(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    PSTR Sep;
    
    switch (Message)
    {
    case WM_INITDIALOG:
        //
        // Set up the controls to reflect current values
        //
        SetWindowText(GetDlgItem(Hwnd, IDC_KD_PORT), g_ComSettings);
        SetWindowText(GetDlgItem(Hwnd, IDC_KD_BAUDRATE),
                      g_ComSettings + strlen(g_ComSettings) + 1);
        break;

    case WM_NOTIFY:
        switch(((LPNMHDR)Lpm)->code)
        {
        case PSN_HELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_KERNEL_DEBUGGING);
            break;

        case PSN_SETACTIVE:
            g_CurrentKd = DlgProc_KernelCom;
            return 0;
            
        case PSN_KILLACTIVE:
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, FALSE);
            return FALSE;
            
        case PSN_APPLY:
            if (g_CurrentKd != DlgProc_KernelCom)
            {
                // This isn't the current page so ignore.
                break;
            }
            
            TCHAR Com[256];
            TCHAR Baud[256];

            if (SendDlgItemMessage(Hwnd, IDC_KD_PORT, WM_GETTEXT,
                                   _tsizeof(Com), (LPARAM)Com) &&
                SendDlgItemMessage(Hwnd, IDC_KD_BAUDRATE, WM_GETTEXT,
                                   _tsizeof(Baud), (LPARAM)Baud))
            {
                if (PrintAllocString(&g_KernelConnectOptions, 256,
                                     "com:port=%s,baud=%s", Com, Baud))
                {
                    strcpy(g_ComSettings, Com);
                    strcpy(g_ComSettings + strlen(Com) + 1, Baud);
                    if (SAVE_ENGINE_WORKSPACE())
                    {
                        PSTR Settings[2];

                        Settings[0] = Com;
                        Settings[1] = Baud;
                        g_Workspace->SetStrings(WSP_GLOBAL_COM_SETTINGS, 2,
                                                Settings);
                    }
                }
            }
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR
CALLBACK
DlgProc_Kernel1394(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    switch (Message)
    {
    case WM_INITDIALOG:
        //
        // Set up the controls to reflect current values
        //
        SetWindowText(GetDlgItem(Hwnd, IDC_KD_1394_CHANNEL), g_1394Settings);
        break;

    case WM_NOTIFY:
        switch(((LPNMHDR)Lpm)->code)
        {
        case PSN_HELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_KERNEL_DEBUGGING);
            break;

        case PSN_SETACTIVE:
            g_CurrentKd = DlgProc_Kernel1394;
            return 0;
            
        case PSN_KILLACTIVE:
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, FALSE);
            return FALSE;
            
        case PSN_APPLY:
            if (g_CurrentKd != DlgProc_Kernel1394)
            {
                // This isn't the current page so ignore.
                break;
            }
            
            TCHAR Channel[256];
            PSTR Options;

            if (SendDlgItemMessage(Hwnd, IDC_KD_1394_CHANNEL, WM_GETTEXT,
                                   _tsizeof(Channel), (LPARAM)Channel))
            {
                if (PrintAllocString(&g_KernelConnectOptions, 256,
                                     "1394:channel=%s", Channel))
                {
                    strcpy(g_1394Settings, Channel);
                    if (SAVE_ENGINE_WORKSPACE())
                    {
                        g_Workspace->SetString(WSP_GLOBAL_1394_SETTINGS,
                                               g_1394Settings);
                    }
                }
            }
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR
CALLBACK
DlgProc_KernelLocal(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    switch (Message)
    {
    case WM_NOTIFY:
        switch(((LPNMHDR)Lpm)->code)
        {
        case PSN_HELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_KERNEL_DEBUGGING);
            break;

        case PSN_SETACTIVE:
            g_CurrentKd = DlgProc_KernelLocal;
            return 0;
            
        case PSN_KILLACTIVE:
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, FALSE);
            return FALSE;
            
        case PSN_APPLY:
            if (g_CurrentKd != DlgProc_KernelLocal)
            {
                // This isn't the current page so ignore.
                break;
            }

            g_AttachKernelFlags = DEBUG_ATTACH_LOCAL_KERNEL;
            SetWindowLongPtr(Hwnd, DWLP_MSGRESULT, PSNRET_NOERROR);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

void
StartKdPropSheet(void)
{
    PROPSHEETHEADER Sheet;
    PROPSHEETPAGE Pages[3];

    ZeroMemory(&Sheet, sizeof(Sheet));
    Sheet.dwSize = sizeof(PROPSHEETHEADER);
    Sheet.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW | PSH_HASHELP;
    Sheet.hwndParent = g_hwndFrame;
    Sheet.hInstance = g_hInst;
    Sheet.pszCaption = "Kernel Debugging";
    Sheet.nPages = 3;
    Sheet.ppsp = Pages;

    ZeroMemory(Pages, sizeof(Pages[0]));
    Pages[0].dwSize = sizeof(Pages[0]);
    Pages[0].dwFlags = PSP_HASHELP;
    Pages[0].hInstance = g_hInst;

    Pages[1] = Pages[0];
    Pages[2] = Pages[0];
    
    Pages[0].pszTemplate = MAKEINTRESOURCE(IDD_DLG_KERNEL_COM);
    Pages[0].pfnDlgProc = DlgProc_KernelCom;

    Pages[1].pszTemplate = MAKEINTRESOURCE(IDD_DLG_KERNEL_1394);
    Pages[1].pfnDlgProc = DlgProc_Kernel1394;

    Pages[2].pszTemplate = MAKEINTRESOURCE(IDD_DLG_KERNEL_LOCAL);
    Pages[2].pfnDlgProc = DlgProc_KernelLocal;

    g_CurrentKd = NULL;

    INT_PTR Status = PropertySheet(&Sheet);
    if (Status == IDOK)
    {
        if (g_ExplicitWorkspace && g_Workspace != NULL)
        {
            g_Workspace->SetUlong(WSP_GLOBAL_ATTACH_KERNEL_FLAGS,
                                  g_AttachKernelFlags);
        }
        StartDebugging();
    }
}

INT_PTR
CALLBACK
DlgProc_ImagePath(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    TCHAR Path[MAX_ENGINE_PATH];
    LRESULT Size;
    HRESULT Hr;

    switch(Message)
    {
    case WM_INITDIALOG:
        Hr = g_pUiSymbols->GetImagePath(Path, _tsizeof(Path), NULL);
        if (Hr != S_OK)
        {
            InformationBox(ERR_Internal_Error, Hr, "UI GetImagePath");
        }

        SendDlgItemMessage(Hwnd, IDC_IMAGE_PATH, WM_SETTEXT, 0, (LPARAM)Path);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_IMAGE_PATH);
            break;
            
        case IDOK:
            Size = SendDlgItemMessage(Hwnd, IDC_IMAGE_PATH, WM_GETTEXT,
                                      sizeof(Path), (LPARAM)Path);
            if (Size == 0)
            {
                InformationBox(ERR_Path_Empty);
                SetFocus(Hwnd);
                return TRUE;
            }
            Path[Size] = 0;

            Hr = g_pUiSymbols->SetImagePath(Path);
            if (Hr != S_OK)
            {
                InformationBox(ERR_Internal_Error, Hr, "UI SetImagePath");
            }

            if (SAVE_ENGINE_WORKSPACE())
            {
                g_Workspace->SetString(WSP_GLOBAL_IMAGE_PATH, Path);
            }
                                             
            EndDialog(Hwnd, IDOK);
            break;

        case IDCANCEL:
            EndDialog(Hwnd, IDCANCEL);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}


INT_PTR
CALLBACK
DlgProc_SourcePath(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    TCHAR Path[MAX_ENGINE_PATH];
    LRESULT Size;
    HRESULT Hr;
    HWND Ctrl;
    ULONG Tag;

    switch(Message)
    {
    case WM_INITDIALOG:
        Ctrl = GetDlgItem(Hwnd, IDC_LOCAL_SOURCE);
        SendMessage(Ctrl, BM_SETCHECK, TRUE, 0);
        if (!g_RemoteClient)
        {
            EnableWindow(Ctrl, FALSE);
            
            Hr = g_pUiSymbols->GetSourcePath(Path, _tsizeof(Path), NULL);
        }
        else
        {
            EnableWindow(Ctrl, TRUE);

            Hr = g_pUiLocSymbols->GetSourcePath(Path, _tsizeof(Path), NULL);
        }
        if (Hr != S_OK)
        {
            InformationBox(ERR_Internal_Error, Hr, "UI GetSourcePath");
        }

        SendDlgItemMessage(Hwnd, IDC_SOURCE_PATH, WM_SETTEXT, 0, (LPARAM)Path);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_LOCAL_SOURCE:
            if (IsDlgButtonChecked(Hwnd, IDC_LOCAL_SOURCE) != BST_CHECKED)
            {
                Hr = g_pUiSymbols->GetSourcePath(Path, _tsizeof(Path), NULL);
            }
            else
            {
                Hr = g_pUiLocSymbols->GetSourcePath(Path, _tsizeof(Path),
                                                    NULL);
            }
            if (Hr != S_OK)
            {
                InformationBox(ERR_Internal_Error, Hr, "UI GetSourcePath");
            }

            SendDlgItemMessage(Hwnd, IDC_SOURCE_PATH, WM_SETTEXT, 0,
                               (LPARAM)Path);
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_SOURCE_PATH);
            break;
            
        case IDOK:
            Size = SendDlgItemMessage(Hwnd, IDC_SOURCE_PATH, WM_GETTEXT,
                                      sizeof(Path), (LPARAM)Path);
            if (Size == 0)
            {
                InformationBox(ERR_Path_Empty);
                SetFocus(Hwnd);
                return TRUE;
            }
            Path[Size] = 0;

            if (!g_RemoteClient ||
                IsDlgButtonChecked(Hwnd, IDC_LOCAL_SOURCE) != BST_CHECKED)
            {
                Hr = g_pUiSymbols->SetSourcePath(Path);
                Tag = WSP_GLOBAL_SOURCE_PATH;
            }
            else
            {
                Hr = g_pUiLocSymbols->SetSourcePath(Path);
                Tag = WSP_GLOBAL_LOCAL_SOURCE_PATH;
            }
            if (Hr != S_OK)
            {
                InformationBox(ERR_Internal_Error, Hr, "UI SetSourcePath");
            }

            if (g_Workspace != NULL)
            {
                g_Workspace->SetString(Tag, Path);
            }
                                             
            EndDialog(Hwnd, IDOK);
            break;

        case IDCANCEL:
            EndDialog(Hwnd, IDCANCEL);
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

#define MAX_IDS 4096

void
FillProcessList(HWND List)
{
    HRESULT Status;
    ULONG Ids[MAX_IDS];
    ULONG IdCount;
    ULONG i;
    ULONG64 Server = 0;
    ULONG Extent = 0;

    SendMessage(List, LB_RESETCONTENT, 0, 0);
    
    if (g_ProcessServer != NULL)
    {
        if (g_pUiClient->
            ConnectProcessServer(g_ProcessServer, &Server) != S_OK)
        {
            SendMessage(List, LB_ADDSTRING,
                        0, (LPARAM)_T("Unable to connect to process server"));
            return;
        }
    }
    
    if ((Status = g_pUiClient->
         GetRunningProcessSystemIds(Server, Ids, MAX_IDS,
                                    &IdCount)) != S_OK)
    {
        SendMessage(List, LB_ADDSTRING,
                    0, (LPARAM)_T("Unable to get process list"));
        goto Disconnect;
    }

    if (IdCount > MAX_IDS)
    {
        SendMessage(List, LB_ADDSTRING,
                    0, (LPARAM)_T("Incomplete process list"));
        IdCount = MAX_IDS;
    }
    
    for (i = 0; i < IdCount; i++)
    {
        char IdAndExeName[MAX_PATH + 16];
        char DescBuf[2 * MAX_PATH];
        PSTR ExeName, Desc;
        LRESULT Idx;
        ULONG ItemEx;

        if (Ids[i] >= 0x80000000)
        {
            sprintf(IdAndExeName, "0x%x", Ids[i]);
        }
        else
        {
            sprintf(IdAndExeName, "%4d", Ids[i]);
        }
        ExeName = IdAndExeName + strlen(IdAndExeName);
        *ExeName++ = ' ';
        Desc = DescBuf;
        while ((Desc - DescBuf) < (ExeName - IdAndExeName))
        {
            *Desc++ = ' ';
        }

        Status = g_pUiClient->
            GetRunningProcessDescription(Server, Ids[i],
                                         DEBUG_PROC_DESC_NO_PATHS,
                                         ExeName,
                                         (ULONG)(sizeof(IdAndExeName) -
                                                 (ExeName - IdAndExeName)),
                                         NULL,
                                         Desc,
                                         (ULONG)(sizeof(DescBuf) -
                                                 (Desc - DescBuf)),
                                         NULL);
        if (FAILED(Status))
        {
            sprintf(ExeName, "Error 0x%08X", Status);
        }

        Idx = SendMessage(List, LB_ADDSTRING, 0, (LPARAM)IdAndExeName);
        SendMessage(List, LB_SETITEMDATA, Idx, TRUE);
        ItemEx = (strlen(IdAndExeName) + 1) *
            g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
        if (ItemEx > Extent)
        {
            Extent = ItemEx;
        }
        if (*Desc)
        {
            SendMessage(List, LB_ADDSTRING, 0, (LPARAM)DescBuf);
            ItemEx = (strlen(DescBuf) + 1) *
                g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
            if (ItemEx > Extent)
            {
                Extent = ItemEx;
            }
        }
    }

    SendMessage(List, LB_SETHORIZONTALEXTENT, Extent, 0);

 Disconnect:
    if (Server != 0)
    {
        g_pUiClient->DisconnectProcessServer(Server);
    }
}

INT_PTR CALLBACK
DlgProc_AttachProcess(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Text[MAX_PATH];
    HWND List;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        List = GetDlgItem(Hwnd, IDC_ATTACH_PROC_LIST);
        SendMessage(List, WM_SETFONT, (WPARAM)g_Fonts[FONT_FIXED].Font, FALSE);
        FillProcessList(List);
        SendDlgItemMessage(Hwnd, IDC_ATTACH_PID, WM_SETFONT,
                           (WPARAM)g_Fonts[FONT_FIXED].Font, FALSE);
        SendDlgItemMessage(Hwnd, IDC_ATTACH_PID, EM_LIMITTEXT,
                           sizeof(Text) - 1, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_ATTACH_PID), "");
        CheckDlgButton(Hwnd, IDC_ATTACH_NONINVASIVE, BST_UNCHECKED);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_ATTACH_PROC_LIST:
            LRESULT Sel;
            
            List = GetDlgItem(Hwnd, IDC_ATTACH_PROC_LIST);
            Sel = SendMessage(List, LB_GETCURSEL, 0, 0);
            if (Sel >= 0)
            {
                if (!SendMessage(List, LB_GETITEMDATA, Sel, 0))
                {
                    Sel = -1;
                }
            }
            if (Sel < 0)
            {
                SetWindowText(GetDlgItem(Hwnd, IDC_ATTACH_PID), "");
            }
            else
            {
                SendMessage(List, LB_GETTEXT, Sel, (LPARAM)Text);
                SetWindowText(GetDlgItem(Hwnd, IDC_ATTACH_PID), Text);
            }
            break;

        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_ATTACH_TO_PROCESS);
            break;
            
        case IDOK:
            GetWindowText(GetDlgItem(Hwnd, IDC_ATTACH_PID),
                          Text, sizeof(Text));
            g_PidToDebug = strtoul(Text, NULL, 0);
            if (IsDlgButtonChecked(Hwnd, IDC_ATTACH_NONINVASIVE) ==
                BST_CHECKED)
            {
                g_AttachProcessFlags |= DEBUG_ATTACH_NONINVASIVE;
            }

            StartDebugging();

            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

void
FillEventFilters(HWND List)
{
    HRESULT Status;
    ULONG Width;
    RECT ListRect;

    LRESULT ListSel = SendMessage(List, LB_GETCURSEL, 0, 0);
    SendMessage(List, WM_SETREDRAW, FALSE, 0);
    SendMessage(List, LB_RESETCONTENT, 0, 0);

    GetClientRect(List, &ListRect);
    Width = ListRect.right - ListRect.left;
    
    Status = g_FilterBuffer->UiLockForRead();
    if (Status != S_OK)
    {
        SendLockStatusMessage(List, LB_ADDSTRING, Status);
        goto Update;
    }
    Status = g_FilterTextBuffer->UiLockForRead();
    if (Status != S_OK)
    {
        SendLockStatusMessage(List, LB_ADDSTRING, Status);
        goto UnlockFilter;
    }
    
    ULONG i;
    PSTR FilterArg, FilterText;
    PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
    PDEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
    char Str[256];
    
    FilterText = (PSTR)g_FilterTextBuffer->GetDataBuffer();
    SpecParams =
        (PDEBUG_SPECIFIC_FILTER_PARAMETERS)g_FilterBuffer->GetDataBuffer();
    ExParams =
        (PDEBUG_EXCEPTION_FILTER_PARAMETERS)(SpecParams + g_NumSpecEvents);
    FilterArg = (PSTR)g_FilterBuffer->GetDataBuffer() + g_FilterArgsOffset;

    for (i = 0; i < g_NumSpecEvents; i++)
    {
        strcpy(Str, FilterText);
        if (SpecParams->ArgumentSize > 1)
        {
            strcat(Str, " ");
            strcat(Str, FilterArg);
            FilterArg += strlen(FilterArg) + 1;
        }
        sprintf(Str + strlen(Str), " - %s - %s",
                g_ExecutionNames[SpecParams->ExecutionOption],
                g_ContinueNames[SpecParams->ContinueOption]);
        SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Str);
        FilterText += strlen(FilterText) + 1;
        SpecParams++;
    }
    for (i = 0; i < g_NumSpecEx; i++)
    {
        sprintf(Str, "%s - %s - %s",
                FilterText,
                g_ExecutionNames[ExParams->ExecutionOption],
                g_ContinueNames[ExParams->ContinueOption]);
        SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Str);
        FilterText += strlen(FilterText) + 1;
        ExParams++;
    }
    for (i = 0; i < g_NumArbEx; i++)
    {
        sprintf(Str, "Exception %08X - %s - %s",
                ExParams->ExceptionCode,
                g_ExecutionNames[ExParams->ExecutionOption],
                g_ContinueNames[ExParams->ContinueOption]);
        SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Str);
        ExParams++;
    }

    UnlockStateBuffer(g_FilterTextBuffer);
 UnlockFilter:
    UnlockStateBuffer(g_FilterBuffer);

 Update:
    SendMessage(List, LB_SETHORIZONTALEXTENT, Width, 0);
    SendMessage(List, WM_SETREDRAW, TRUE, 0);
    if (ListSel != LB_ERR)
    {
        SendMessage(List, LB_SETCURSEL, ListSel, 0);
    }
}

void
GetFilterOptions(int Index, PULONG CodeArg, PULONG Exe, PULONG Cont)
{
    *CodeArg = 0;
    *Exe = 0;
    *Cont = 0;
    
    if (g_FilterBuffer->UiLockForRead() != S_OK)
    {
        return;
    }

    PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
    PDEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
    
    SpecParams =
        (PDEBUG_SPECIFIC_FILTER_PARAMETERS)g_FilterBuffer->GetDataBuffer();
    ExParams =
        (PDEBUG_EXCEPTION_FILTER_PARAMETERS)(SpecParams + g_NumSpecEvents);

    if ((ULONG)Index < g_NumSpecEvents)
    {
        SpecParams += Index;
        *CodeArg = SpecParams->ArgumentSize;
        *Exe = SpecParams->ExecutionOption;
        *Cont = SpecParams->ContinueOption;
    }
    else
    {
        ExParams += Index - g_NumSpecEvents;
        *CodeArg = ExParams->ExceptionCode;
        *Exe = ExParams->ExecutionOption;
        *Cont = ExParams->ContinueOption;
    }
    
    UnlockStateBuffer(g_FilterBuffer);
}

void
GetFilterArgument(int Index, PTSTR Argument, ULONG Size)
{
    *Argument = 0;
    
    if (g_FilterBuffer->UiLockForRead() != S_OK)
    {
        return;
    }

    int i;
    PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
    PSTR FilterArg;
    
    SpecParams =
        (PDEBUG_SPECIFIC_FILTER_PARAMETERS)g_FilterBuffer->GetDataBuffer();
    FilterArg = (PSTR)g_FilterBuffer->GetDataBuffer() + g_FilterArgsOffset;
    
    for (i = 0; i < Index; i++)
    {
        if (SpecParams->ArgumentSize > 1)
        {
            FilterArg += strlen(FilterArg) + 1;
        }

        SpecParams++;
    }

    if (SpecParams->ArgumentSize > 1)
    {
        strncat(Argument, FilterArg, Size);
    }
    
    UnlockStateBuffer(g_FilterBuffer);
}

void
GetFilterCommands(ULONG Index,
                  PTSTR Argument1, ULONG Size1,
                  PTSTR Argument2, ULONG Size2)
{
    *Argument1 = 0;
    *Argument2 = 0;
    
    if (g_FilterBuffer->UiLockForRead() != S_OK)
    {
        return;
    }

    ULONG i, Limit;
    PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
    PSTR FilterCmd;
    ULONG CmdSize1, CmdSize2;
    
    SpecParams =
        (PDEBUG_SPECIFIC_FILTER_PARAMETERS)g_FilterBuffer->GetDataBuffer();
    FilterCmd = (PSTR)g_FilterBuffer->GetDataBuffer() + g_FilterCmdsOffset;

    if (Index < g_NumSpecEvents)
    {
        Limit = Index;
    }
    else
    {
        Limit = g_NumSpecEvents;
    }
    
    for (i = 0; i < Limit; i++)
    {
        if (SpecParams->CommandSize > 0)
        {
            FilterCmd += strlen(FilterCmd) + 1;
        }

        SpecParams++;
    }

    if (Index >= g_NumSpecEvents)
    {
        PDEBUG_EXCEPTION_FILTER_PARAMETERS ExParams =
            (PDEBUG_EXCEPTION_FILTER_PARAMETERS)SpecParams;
        while (i < Index)
        {
            if (ExParams->CommandSize > 0)
            {
                FilterCmd += strlen(FilterCmd) + 1;
            }
            if (ExParams->SecondCommandSize > 0)
            {
                FilterCmd += strlen(FilterCmd) + 1;
            }

            i++;
            ExParams++;
        }

        CmdSize1 = ExParams->CommandSize;
        CmdSize2 = ExParams->SecondCommandSize;
    }
    else
    {
        CmdSize1 = SpecParams->CommandSize;
        CmdSize2 = 0;
    }

    if (CmdSize1 > 0)
    {
        strncat(Argument1, FilterCmd, Size1-1);
    }
    if (CmdSize2 > 0)
    {
        FilterCmd += strlen(FilterCmd) + 1;
        strncat(Argument2, FilterCmd, Size2-1);
    }
    
    UnlockStateBuffer(g_FilterBuffer);
}

INT_PTR CALLBACK
DlgProc_EventFilters(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    UIC_SET_FILTER_DATA* SetFilter;
    HWND List = GetDlgItem(Hwnd, IDC_FILTERS);
    int Sel = (int)SendMessage(List, LB_GETCURSEL, 0, 0);
    HWND Remove, Command, Arg;
    ULONG CodeArg, Exe, Cont;
    UINT Id;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        g_FilterBuffer->m_Win = Hwnd;
        SendMessage(List, WM_SETFONT, (WPARAM)g_Fonts[FONT_FIXED].Font, FALSE);
        FillEventFilters(List);
        break;

    case WM_COMMAND:
        Id = LOWORD(Wpm);
        switch(Id)
        {
        case IDC_FILTERS:
            Remove = GetDlgItem(Hwnd, IDC_FILTER_REMOVE);
            Arg = GetDlgItem(Hwnd, IDC_FILTER_ARGUMENT);
            Command = GetDlgItem(Hwnd, IDC_FILTER_COMMAND);
            if (Sel >= 0)
            {
                EnableWindow(Remove,
                             (ULONG)Sel >= g_NumSpecEvents + g_NumSpecEx);
                GetFilterOptions(Sel, &CodeArg, &Exe, &Cont);
                CheckRadioButton(Hwnd, IDC_FILTER_ENABLED,
                                 IDC_FILTER_IGNORE,
                                 (Exe - DEBUG_FILTER_BREAK) +
                                 IDC_FILTER_ENABLED);
                CheckRadioButton(Hwnd, IDC_FILTER_HANDLED,
                                 IDC_FILTER_NOT_HANDLED,
                                 (Cont - DEBUG_FILTER_GO_HANDLED) +
                                 IDC_FILTER_HANDLED);
                
                if ((ULONG)Sel < g_NumSpecEvents)
                {
                    GetFilterOptions(Sel, &CodeArg, &Exe, &Cont);
                    EnableWindow(Arg, CodeArg > 0);
                }

                EnableWindow(Command, TRUE);
            }
            else
            {
                EnableWindow(Remove, FALSE);
                EnableWindow(Arg, FALSE);
                EnableWindow(Command, FALSE);
            }
            break;

        case IDC_FILTER_ADD:
            StartDialog(IDD_DLG_EXCEPTION_FILTER, DlgProc_ExceptionFilter,
                        NULL);
            break;

        case IDC_FILTER_REMOVE:
            if (Sel >= 0)
            {
                SetFilter = StartStructCommand(UIC_SET_FILTER);
                if (SetFilter != NULL)
                {
                    SetFilter->Index = 0xffffffff;
                    GetFilterOptions(Sel,
                                     &SetFilter->Code,
                                     &SetFilter->Execution,
                                     &SetFilter->Continue);
                    SetFilter->Execution = DEBUG_FILTER_REMOVE;
                    FinishCommand();
                }
            }
            break;

        case IDC_FILTER_ARGUMENT:
            if (Sel >= 0 && (ULONG)Sel < g_NumSpecEvents)
            {
                GetFilterArgument(Sel, g_DlgString,
                                  sizeof(g_DlgString));
                
                if (StartDialog(IDD_DLG_FILTER_ARGUMENT,
                                DlgProc_FilterArgument,
                                NULL) == IDOK)
                {
                    UIC_SET_FILTER_ARGUMENT_DATA* SetFilterArg;
                    SetFilterArg = (UIC_SET_FILTER_ARGUMENT_DATA*)
                        StartCommand(UIC_SET_FILTER_ARGUMENT,
                                     sizeof(*SetFilterArg) +
                                     strlen(g_DlgString));
                    if (SetFilterArg != NULL)
                    {
                        SetFilterArg->Index = Sel;
                        strcpy(SetFilterArg->Argument, g_DlgString);
                        FinishCommand();
                    }
                }
            }
            break;
            
        case IDC_FILTER_COMMAND:
            if (Sel >= 0)
            {
                GetFilterCommands(Sel,
                                  g_DlgString, sizeof(g_DlgString),
                                  g_DlgString2, sizeof(g_DlgString2));

                if (StartDialog(IDD_DLG_FILTER_COMMAND,
                                DlgProc_FilterCommand,
                                (ULONG)Sel >= g_NumSpecEvents) == IDOK)
                {
                    UIC_SET_FILTER_COMMAND_DATA* SetFilterCmd;
                    
                    SetFilterCmd = (UIC_SET_FILTER_COMMAND_DATA*)
                        StartCommand(UIC_SET_FILTER_COMMAND,
                                     sizeof(*SetFilterCmd) +
                                     strlen(g_DlgString));
                    if (SetFilterCmd != NULL)
                    {
                        SetFilterCmd->Which = 0;
                        SetFilterCmd->Index = Sel;
                        strcpy(SetFilterCmd->Command, g_DlgString);
                        FinishCommand();
                    }

                    if ((ULONG)Sel <= g_NumSpecEvents)
                    {
                        break;
                    }
                    
                    SetFilterCmd = (UIC_SET_FILTER_COMMAND_DATA*)
                        StartCommand(UIC_SET_FILTER_COMMAND,
                                     sizeof(*SetFilterCmd) +
                                     strlen(g_DlgString2));
                    if (SetFilterCmd != NULL)
                    {
                        SetFilterCmd->Which = 1;
                        SetFilterCmd->Index = Sel;
                        strcpy(SetFilterCmd->Command, g_DlgString2);
                        FinishCommand();
                    }
                }
            }
            break;
            
        case IDC_FILTER_ENABLED:
        case IDC_FILTER_DISABLED:
        case IDC_FILTER_OUTPUT:
        case IDC_FILTER_IGNORE:
        case IDC_FILTER_HANDLED:
        case IDC_FILTER_NOT_HANDLED:
            if (Sel >= 0)
            {
                SetFilter = StartStructCommand(UIC_SET_FILTER);
                if (SetFilter != NULL)
                {
                    SetFilter->Index = (ULONG)Sel < g_NumSpecEvents ?
                        Sel : 0xffffffff;
                    GetFilterOptions(Sel,
                                     &SetFilter->Code,
                                     &SetFilter->Execution,
                                     &SetFilter->Continue);
                    if (Id >= IDC_FILTER_ENABLED && Id <= IDC_FILTER_IGNORE)
                    {
                        SetFilter->Execution = (Id - IDC_FILTER_ENABLED) +
                            DEBUG_FILTER_BREAK;
                    }
                    else
                    {
                        SetFilter->Continue = (Id - IDC_FILTER_HANDLED) +
                            DEBUG_FILTER_GO_HANDLED;
                    }
                    FinishCommand();
                }
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_EVENT_FILTERS);
            break;
            
        case IDCANCEL:
            g_FilterBuffer->m_Win = NULL;
            EndDialog(Hwnd, Id);
            break;
        }
        break;

    case LB_RESETCONTENT:
        FillEventFilters(List);
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_ExceptionFilter(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HWND Code = GetDlgItem(Hwnd, IDC_FILTER_CODE);
    char CodeText[80];
    UIC_SET_FILTER_DATA* SetFilter;
    static UINT s_Execution;
    static UINT s_Continue;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SendMessage(Code, EM_LIMITTEXT, sizeof(CodeText) - 1, 0);
        SetWindowText(Code, "");
        s_Execution = IDC_FILTER_ENABLED;
        s_Continue = IDC_FILTER_NOT_HANDLED;
        CheckRadioButton(Hwnd, IDC_FILTER_ENABLED,
                         IDC_FILTER_IGNORE, s_Execution);
        CheckRadioButton(Hwnd, IDC_FILTER_HANDLED,
                         IDC_FILTER_NOT_HANDLED, s_Continue);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_FILTER_ENABLED:
        case IDC_FILTER_DISABLED:
        case IDC_FILTER_OUTPUT:
        case IDC_FILTER_IGNORE:
            s_Execution = LOWORD(Wpm);
            break;

        case IDC_FILTER_HANDLED:
        case IDC_FILTER_NOT_HANDLED:
            s_Continue = LOWORD(Wpm);
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_EVENT_FILTERS);
            break;
            
        case IDOK:
            int InputCode;

            if (GetWindowText(Code, CodeText, sizeof(CodeText)) == 0)
            {
                MessageBeep(-1);
                break;
            }

            if (sscanf(CodeText, "%x", &InputCode) != 1)
            {
                MessageBeep(-1);
                break;
            }
            
            SetFilter = StartStructCommand(UIC_SET_FILTER);
            if (SetFilter != NULL)
            {
                SetFilter->Index = 0xffffffff;
                SetFilter->Code = InputCode;
                SetFilter->Execution = (s_Execution - IDC_FILTER_ENABLED) +
                    DEBUG_FILTER_BREAK;
                SetFilter->Continue = (s_Continue - IDC_FILTER_HANDLED) +
                    DEBUG_FILTER_GO_HANDLED;
                FinishCommand();
            }

            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_FilterArgument(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HWND Arg = GetDlgItem(Hwnd, IDC_FILTER_ARGUMENT);
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SendMessage(Arg, EM_LIMITTEXT, sizeof(g_DlgString) - 1, 0);
        SetWindowText(Arg, g_DlgString);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_EVENT_FILTERS);
            break;
            
        case IDOK:
            if (GetWindowText(Arg, g_DlgString,
                              sizeof(g_DlgString)) == 0)
            {
                g_DlgString[0] = 0;
            }

            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

INT_PTR CALLBACK
DlgProc_FilterCommand(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HWND Cmd1 = GetDlgItem(Hwnd, IDC_FILTER_COMMAND);
    HWND Cmd2 = GetDlgItem(Hwnd, IDC_FILTER_COMMAND2);
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SendMessage(Cmd1, EM_LIMITTEXT, sizeof(g_DlgString) - 1, 0);
        SetWindowText(Cmd1, g_DlgString);
        SendMessage(Cmd2, EM_LIMITTEXT, sizeof(g_DlgString2) - 1, 0);
        SetWindowText(Cmd2, g_DlgString2);
        EnableWindow(Cmd2, (BOOL)Lpm);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_EVENT_FILTERS);
            break;
            
        case IDOK:
            if (GetWindowText(Cmd1, g_DlgString, sizeof(g_DlgString)) == 0)
            {
                g_DlgString[0] = 0;
            }
            if (GetWindowText(Cmd2, g_DlgString2, sizeof(g_DlgString2)) == 0)
            {
                g_DlgString2[0] = 0;
            }

            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

ULONG
EditTabToUserTab(ULONG EditTab)
{
    return EditTab / 4;
}

ULONG
UserTabToEditTab(ULONG UserTab)
{
    return UserTab * 4;
}

INT_PTR CALLBACK
DlgProc_Options(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HWND Ctrl;
    char Text[256];
    LRESULT Sel, Idx;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        ULONG EngOptions;
        LONG Width;
        HDC FontDc;
            
        SendDlgItemMessage(Hwnd, IDC_OPTION_TAB_WIDTH, EM_LIMITTEXT,
                           sizeof(Text) - 1, 0);
        _itoa(EditTabToUserTab(g_TabWidth), Text, 10);
        SetWindowText(GetDlgItem(Hwnd, IDC_OPTION_TAB_WIDTH), Text);
        
        g_pUiControl->GetEngineOptions(&EngOptions);
        CheckDlgButton(Hwnd, IDC_OPTION_REPEAT_COMMANDS,
                       (EngOptions & DEBUG_ENGOPT_NO_EXECUTE_REPEAT) == 0 ?
                       BST_CHECKED : BST_UNCHECKED);

        Ctrl = GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST);
        SendMessage(Ctrl, WM_SETFONT, (WPARAM)g_Fonts[FONT_VARIABLE].Font, 0);
        FontDc = GetDC(Ctrl);
        SelectObject(FontDc, g_Fonts[FONT_VARIABLE].Font);
        Width = 0;
        SendMessage(Ctrl, LB_RESETCONTENT, 0, 0);
        for (Sel = 0; Sel < COL_COUNT; Sel++)
        {
            Idx = SendMessage(Ctrl, LB_ADDSTRING,
                              0, (LPARAM)g_Colors[Sel].Name);
            SendMessage(Ctrl, LB_SETITEMDATA, Idx, Sel);
            if (FontDc != NULL)
            {
                SIZE Extent;

                if (GetTextExtentPoint32(FontDc, g_Colors[Sel].Name,
                                         strlen(g_Colors[Sel].Name),
                                         &Extent) &&
                    Extent.cx > Width)
                {
                    Width = Extent.cx;
                }
            }
        }
        for (Sel = 0; Sel < OUT_MASK_COL_COUNT; Sel++)
        {
            if (g_OutMaskColors[Sel].Name != NULL)
            {
                Idx = SendMessage(Ctrl, LB_ADDSTRING,
                                  0, (LPARAM)g_OutMaskColors[Sel].Name);
                SendMessage(Ctrl, LB_SETITEMDATA,
                            Idx, Sel + OUT_MASK_COL_BASE);
                if (FontDc != NULL)
                {
                    SIZE Extent;

                    if (GetTextExtentPoint32(FontDc, g_OutMaskColors[Sel].Name,
                                             strlen(g_OutMaskColors[Sel].Name),
                                             &Extent) &&
                        Extent.cx > Width)
                    {
                        Width = Extent.cx;
                    }
                }
            }
        }
        SendMessage(Ctrl, LB_SETCURSEL, 0, 0);
        if (FontDc != NULL)
        {
            ReleaseDC(Ctrl, FontDc);
        }
        Width += g_Fonts[FONT_VARIABLE].Metrics.tmAveCharWidth;
        SendMessage(Ctrl, LB_SETHORIZONTALEXTENT, Width, 0);
        
        CheckDlgButton(Hwnd, IDC_OPTION_DISASM_ACTIVATE_SOURCE,
                       g_DisasmActivateSource ? BST_CHECKED : BST_UNCHECKED);
        CheckDlgButton(Hwnd, IDC_OPTION_AUTO_CMD_SCROLL,
                       g_AutoCmdScroll ? BST_CHECKED : BST_UNCHECKED);
        break;

    case WM_CTLCOLORSTATIC:
        if ((HWND)Lpm == GetDlgItem(Hwnd, IDC_OPTION_COLOR_DISPLAY))
        {
            Sel = SendMessage(GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST),
                              LB_GETCURSEL, 0, 0);
            if (Sel >= 0)
            {
                Idx = SendMessage(GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST),
                                  LB_GETITEMDATA, Sel, 0);
                INDEXED_COLOR* IdxCol = GetIndexedColor((ULONG)Idx);
                if (IdxCol != NULL)
                {
                    return (INT_PTR)IdxCol->Brush;
                }
            }
        }
        break;
        
    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_OPTION_COLOR_LIST:
            Sel = SendMessage(GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST),
                              LB_GETCURSEL, 0, 0);
            if (Sel >= 0)
            {
                InvalidateRect(GetDlgItem(Hwnd, IDC_OPTION_COLOR_DISPLAY),
                               NULL, TRUE);
            }
            break;

        case IDC_OPTION_COLOR_CHANGE:
            Sel = SendMessage(GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST),
                              LB_GETCURSEL, 0, 0);
            if (Sel >= 0)
            {
                Idx = SendMessage(GetDlgItem(Hwnd, IDC_OPTION_COLOR_LIST),
                                  LB_GETITEMDATA, Sel, 0);
                if (SelectColor(g_hwndFrame, (ULONG)Idx))
                {
                    InvalidateRect(GetDlgItem(Hwnd, IDC_OPTION_COLOR_DISPLAY),
                                   NULL, TRUE);
                }
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_OPTIONS);
            break;
            
        case IDOK:
            if (GetWindowText(GetDlgItem(Hwnd, IDC_OPTION_TAB_WIDTH),
                              Text, sizeof(Text)) == 0)
            {
                MessageBeep(-1);
                break;
            }

            if (sscanf(Text, "%d", &g_TabWidth) != 1)
            {
                MessageBeep(-1);
            }
            
            g_TabWidth = UserTabToEditTab(g_TabWidth);
            SetTabWidth(g_TabWidth);

            ULONG RepeatState;
            
            if (IsDlgButtonChecked(Hwnd, IDC_OPTION_REPEAT_COMMANDS) ==
                BST_CHECKED)
            {
                g_pUiControl->
                    RemoveEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
                RepeatState = TRUE;
            }
            else
            {
                g_pUiControl->
                    AddEngineOptions(DEBUG_ENGOPT_NO_EXECUTE_REPEAT);
                RepeatState = FALSE;
            }
            if (g_Workspace != NULL)
            {
                g_Workspace->SetUlong(WSP_GLOBAL_REPEAT_COMMANDS, RepeatState);
            }

            UpdateAllColors();
            
            g_DisasmActivateSource =
                IsDlgButtonChecked(Hwnd, IDC_OPTION_DISASM_ACTIVATE_SOURCE) ==
                BST_CHECKED;
            if (g_Workspace != NULL)
            {
                g_Workspace->SetUlong(WSP_GLOBAL_DISASM_ACTIVATE_SOURCE,
                                      g_DisasmActivateSource);
            }
            
            g_AutoCmdScroll =
                IsDlgButtonChecked(Hwnd, IDC_OPTION_AUTO_CMD_SCROLL) ==
                BST_CHECKED;
            if (g_Workspace != NULL)
            {
                g_Workspace->SetUlong(WSP_GLOBAL_AUTO_CMD_SCROLL,
                                      g_AutoCmdScroll);
            }
            
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

void
ClwFillSaveList(HWND List)
{
    SendMessage(List, LB_RESETCONTENT, 0, 0);

    if (g_Workspace != NULL)
    {
        WSP_ENTRY* Entry = NULL;
        LRESULT Idx;
        BOOL CommonWinDone = FALSE;
    
        while ((Entry = g_Workspace->NextEntry(Entry)) != NULL)
        {
            if (Entry->Tag == WSP_WINDOW_COMMONWIN_1)
            {
                if (CommonWinDone)
                {
                    continue;
                }
                
                CommonWinDone = TRUE;
            }
            
            Idx = SendMessage(List, LB_ADDSTRING, 0, (LPARAM)
                              GetWspTagName(Entry->Tag));
            SendMessage(List, LB_SETITEMDATA, Idx, (LPARAM)Entry->Tag);
        }
    }
    
    SendMessage(List, LB_SETCURSEL, 0, 0);
}

void
ClwMoveBetweenLists(HWND From, HWND To, LRESULT Item)
{
    LPARAM Data = SendMessage(From, LB_GETITEMDATA, Item, 0);
    SendMessage(From, LB_DELETESTRING, Item, 0);
    Item = SendMessage(To, LB_ADDSTRING, 0, (LPARAM)
                       GetWspTagName((WSP_TAG)Data));
    SendMessage(To, LB_SETITEMDATA, Item, Data);
}

void
ClwMoveAllBetweenLists(HWND From, HWND To)
{
    LRESULT Count = SendMessage(From, LB_GETCOUNT, 0, 0);
    while (Count-- > 0)
    {
        ClwMoveBetweenLists(From, To, 0);
    }
}

void
ClwMoveSelectedBetweenLists(HWND From, HWND To)
{
    int Sel[1];
    LRESULT Count;

    // Move items one at a time as the indices change
    // when items are moved.
    for (;;)
    {
        Count = SendMessage(From, LB_GETSELITEMS,
                            sizeof(Sel) / sizeof(Sel[0]), (LPARAM)Sel);
        if (Count <= 0)
        {
            break;
        }

        ClwMoveBetweenLists(From, To, Sel[0]);
    }
}

void
ClwProcessClearList(HWND List)
{
    LRESULT Count = SendMessage(List, LB_GETCOUNT, 0, 0);
    LRESULT i;
    WSP_TAG Tag;

    for (i = 0; i < Count; i++)
    {
        Tag = (WSP_TAG)SendMessage(List, LB_GETITEMDATA, i, 0);
        g_Workspace->Delete(Tag, WSP_GROUP_MASK | WSP_ITEM_MASK);
    }

    // If everything is deleted from an explicit workspace
    // delete the workspace from the explicit registry area.
    if (g_ExplicitWorkspace && g_Workspace->IsEmpty())
    {
        g_Workspace->DeleteReg(TRUE);
        delete g_Workspace;
        g_Workspace = NULL;
    }
}

INT_PTR CALLBACK
DlgProc_ClearWorkspace(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    LRESULT Sel;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        ClwFillSaveList(GetDlgItem(Hwnd, IDC_CLW_SAVE_LIST));
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_CLW_CLEAR:
            ClwMoveSelectedBetweenLists(GetDlgItem(Hwnd, IDC_CLW_SAVE_LIST),
                                        GetDlgItem(Hwnd, IDC_CLW_CLEAR_LIST));
            break;
        case IDC_CLW_CLEAR_ALL:
            ClwMoveAllBetweenLists(GetDlgItem(Hwnd, IDC_CLW_SAVE_LIST),
                                   GetDlgItem(Hwnd, IDC_CLW_CLEAR_LIST));
            break;
        case IDC_CLW_SAVE:
            ClwMoveSelectedBetweenLists(GetDlgItem(Hwnd, IDC_CLW_CLEAR_LIST),
                                        GetDlgItem(Hwnd, IDC_CLW_SAVE_LIST));
            break;
        case IDC_CLW_SAVE_ALL:
            ClwMoveAllBetweenLists(GetDlgItem(Hwnd, IDC_CLW_CLEAR_LIST),
                                   GetDlgItem(Hwnd, IDC_CLW_SAVE_LIST));
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_CLEAR_WORKSPACE);
            break;

        case IDOK:
            ClwProcessClearList(GetDlgItem(Hwnd, IDC_CLW_CLEAR_LIST));
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

enum
{
    MODCOL_NAME,
    MODCOL_START,
    MODCOL_END,
    MODCOL_TIMESTAMP,
    MODCOL_CHECKSUM,
    MODCOL_SYMBOL_TYPE,
    MODCOL_SYMBOL_FILE
};

void
InitializeModuleList(HWND List)
{
    SendMessage(List, WM_SETFONT, (WPARAM)g_Fonts[FONT_FIXED].Font, FALSE);
    
    LVCOLUMN Column;

    // First column is for the module name.
    Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
    Column.fmt = LVCFMT_LEFT;
    Column.pszText = "Name";
    Column.cx = 15 * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_NAME;
    ListView_InsertColumn(List, 0, &Column);

    // Second column is for start address.
    Column.fmt = LVCFMT_CENTER;
    Column.pszText = "Start";
    Column.cx = (10 + (g_Ptr64 ? 9 : 0)) *
        g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_START;
    ListView_InsertColumn(List, 1, &Column);

    // Third column is for end address.
    Column.fmt = LVCFMT_CENTER;
    Column.pszText = "End";
    Column.cx = (10 + (g_Ptr64 ? 9 : 0)) *
        g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_END;
    ListView_InsertColumn(List, 2, &Column);

    // Fourth column is for timestamp.
    Column.fmt = LVCFMT_CENTER;
    Column.pszText = "Timestamp";
    Column.cx = 37 * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_TIMESTAMP;
    ListView_InsertColumn(List, 3, &Column);

    // Fifth column is for checksum.
    Column.fmt = LVCFMT_CENTER;
    Column.pszText = "Checksum";
    Column.cx = 10 * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_CHECKSUM;
    ListView_InsertColumn(List, 4, &Column);

    // Sixth column is for symbol type.
    Column.fmt = LVCFMT_CENTER;
    Column.pszText = "Symbols";
    Column.cx = 9 * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_SYMBOL_TYPE;
    ListView_InsertColumn(List, 5, &Column);

    // Seventh column is for symbol file.
    Column.fmt = LVCFMT_LEFT;
    Column.pszText = "Symbol file";
    Column.cx = 80 * g_Fonts[FONT_FIXED].Metrics.tmAveCharWidth;
    Column.iSubItem = MODCOL_SYMBOL_FILE;
    ListView_InsertColumn(List, 6, &Column);
}

void
FillModuleList(HWND List)
{
    LVCOLUMN Column;

    Column.mask = LVCF_FMT;
    if (!ListView_GetColumn(List, 0, &Column))
    {
        InitializeModuleList(List);
    }

    LVITEM Item;
    ULONG i;
    char Image[128];
    char Text[128];
    char Path[MAX_PATH];
    
    ListView_DeleteAllItems(List);

    if (g_ModuleBuffer->UiLockForRead() != S_OK)
    {
        Item.mask = LVIF_TEXT;
        Item.iItem = 0;
        Item.iSubItem = 0;
        Item.pszText = "Unable to retrieve module list";
        ListView_InsertItem(List, &Item);
        return;
    }

    PDEBUG_MODULE_PARAMETERS Params = (PDEBUG_MODULE_PARAMETERS)
        g_ModuleBuffer->GetDataBuffer();
    
    for (i = 0; i < g_NumModules; i++)
    {
        PSTR LoadBuf;
        ULONG LoadBufLen;
        
        if ((Params->Flags & DEBUG_MODULE_UNLOADED) ||
            g_pUiSymbols2 == NULL ||
            g_pUiSymbols2->
            GetModuleNameString(DEBUG_MODNAME_SYMBOL_FILE, DEBUG_ANY_ID,
                                Params->Base, Path, sizeof(Path),
                                NULL) != S_OK)
        {
            LoadBuf = Path;
            LoadBufLen = sizeof(Path);
        }
        else
        {
            LoadBuf = NULL;
            LoadBufLen = 0;
        }
        
        if (g_pUiSymbols->GetModuleNames(DEBUG_ANY_ID, Params->Base,
                                         Image, sizeof(Image), NULL,
                                         Text, sizeof(Text), NULL,
                                         LoadBuf, LoadBufLen, NULL) != S_OK)
        {
            strcpy(Text, "<Unable to get name>");
            if (LoadBuf != NULL)
            {
                strcpy(LoadBuf, "<Unable to get name>");
            }
        }

        if (Params->Flags & DEBUG_MODULE_UNLOADED)
        {
            strcpy(Text, "<Unloaded>");
            strcpy(Path, Image);
        }
        
        Item.mask = LVIF_TEXT | LVIF_PARAM;
        Item.iItem = i;
        Item.iSubItem = MODCOL_NAME;
        Item.pszText = Text;
        Item.lParam = i;
        ListView_InsertItem(List, &Item);

        ListView_SetItemText(List, i, MODCOL_START,
                             FormatAddr64(Params->Base));
        
        ListView_SetItemText(List, i, MODCOL_END,
                             FormatAddr64(Params->Base + Params->Size));

        if (Params->TimeDateStamp < 0xfffffffe)
        {
            time_t Time = (time_t)Params->TimeDateStamp;
            strcpy(Text, ctime(&Time));
            sprintf(Text + strlen(Text) - 1, " (%08x)", Params->TimeDateStamp);
        }
        else
        {
            sprintf(Text, "Unavailable");
        }
        ListView_SetItemText(List, i, MODCOL_TIMESTAMP, Text);

        sprintf(Text, "%08x", Params->Checksum);
        ListView_SetItemText(List, i, MODCOL_CHECKSUM, Text);

        if (Params->SymbolType != DEBUG_SYMTYPE_DEFERRED &&
            Params->SymbolType <
            sizeof(g_SymbolTypeNames) / sizeof(g_SymbolTypeNames[0]))
        {
            ListView_SetItemText(List, i, MODCOL_SYMBOL_TYPE,
                                 g_SymbolTypeNames[Params->SymbolType]);
        }
        else
        {
            ListView_SetItemText(List, i, MODCOL_SYMBOL_TYPE, "");
        }

        ListView_SetItemText(List, i, MODCOL_SYMBOL_FILE, Path);
        
        Params++;
    }

    UnlockStateBuffer(g_ModuleBuffer);
}

struct SORT_MODULE
{
    HWND List;
    int Column;
};

int CALLBACK
SortModuleCompare(LPARAM Lpm1, LPARAM Lpm2, LPARAM LpmSort)
{
    SORT_MODULE* Sort = (SORT_MODULE*)LpmSort;
    LVITEM Item;
    char Text1[MAX_PATH], Text2[MAX_PATH];
    PDEBUG_MODULE_PARAMETERS Param1, Param2;

    switch(Sort->Column)
    {
    case MODCOL_NAME:
    case MODCOL_START:
    case MODCOL_END:
    case MODCOL_CHECKSUM:
    case MODCOL_SYMBOL_TYPE:
    case MODCOL_SYMBOL_FILE:
        Item.mask = LVIF_TEXT;
        Item.iItem = (int)Lpm1;
        Item.iSubItem = Sort->Column;
        Item.pszText = Text1;
        Item.cchTextMax = sizeof(Text1);
        ListView_GetItem(Sort->List, &Item);
        Item.iItem = (int)Lpm2;
        Item.pszText = Text2;
        Item.cchTextMax = sizeof(Text2);
        ListView_GetItem(Sort->List, &Item);
        
        // Sort all empty text towards the bottom.
        if (Text1[0] == 0 && Text2[0] != 0)
        {
            return 1;
        }
        else if (Text2[0] == 0 && Text1[0] != 0)
        {
            return -1;
        }
        
        return _strcmpi(Text1, Text2);
        
    case MODCOL_TIMESTAMP:
        Item.mask = LVIF_PARAM;
        Item.iItem = (int)Lpm1;
        Item.iSubItem = 0;
        ListView_GetItem(Sort->List, &Item);
        Param1 = (PDEBUG_MODULE_PARAMETERS)
            g_ModuleBuffer->GetDataBuffer() + Item.lParam;
        Item.iItem = (int)Lpm2;
        ListView_GetItem(Sort->List, &Item);
        Param2 = (PDEBUG_MODULE_PARAMETERS)
            g_ModuleBuffer->GetDataBuffer() + Item.lParam;
        switch(Sort->Column)
        {
        case MODCOL_TIMESTAMP:
            return Param1->TimeDateStamp < Param2->TimeDateStamp ?
                -1 : (Param1->TimeDateStamp > Param2->TimeDateStamp ?
                      1 : 0);
        }
    }

    return 0;
}

void
SortModuleColumns(HWND List, int Column)
{
    if (g_ModuleBuffer->UiLockForRead() != S_OK)
    {
        return;
    }

    SORT_MODULE Sort = {List, Column};
    ListView_SortItemsEx(List, SortModuleCompare, (LPARAM)&Sort);

    UnlockStateBuffer(g_ModuleBuffer);
}

INT_PTR CALLBACK
DlgProc_Modules(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    LRESULT Sel;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        g_ModuleBuffer->m_Win = Hwnd;
        FillModuleList(GetDlgItem(Hwnd, IDC_MODULE_LIST));
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_MODULES);
            break;

        case IDCANCEL:
            g_ModuleBuffer->m_Win = NULL;
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    case WM_NOTIFY:
        if (Wpm == IDC_MODULE_LIST)
        {
            LPNMLISTVIEW Notify = (LPNMLISTVIEW)Lpm;
            
            if (Notify->hdr.code == LVN_COLUMNCLICK)
            {
                SortModuleColumns(GetDlgItem(Hwnd, IDC_MODULE_LIST),
                                  Notify->iSubItem);
            }
        }
        break;
        
    case LB_RESETCONTENT:
        FillModuleList(GetDlgItem(Hwnd, IDC_MODULE_LIST));
        break;
        
    default:
        return FALSE;
    }

    return TRUE;
}

void
EnumWorkspaceKey(HWND List, HKEY BaseKey, ULONG SubKey, BOOL ShowSubKey,
                 BOOL OmitCurrent)
{
    LONG Status;
    HKEY Key;
    char Text[128];

    sprintf(Text, WSP_REG_KEY "\\%s", g_WorkspaceKeyNames[SubKey]);
    if ((Status = RegOpenKeyEx(BaseKey, Text,
                               0, KEY_ALL_ACCESS, &Key)) != ERROR_SUCCESS)
    {
        return;
    }

    ULONG Index;
    char Name[MAX_WSP_NAME];
    ULONG NameLen;
    ULONG Type;
    ULONG Item;

    Index = 0;
    for (;;)
    {
        NameLen = sizeof(Name);
        if ((Status = RegEnumValue(Key, Index, Name, &NameLen,
                                   NULL, &Type, NULL, 0)) != ERROR_SUCCESS)
        {
            // Done with the enumeration.
            break;
        }
        if (Type != REG_BINARY)
        {
            // Only binary values should be present.
            break;
        }

        if (OmitCurrent && g_Workspace &&
            SubKey == g_Workspace->GetKey() &&
            !strcmp(g_Workspace->GetValue(), Name))
        {
            Index++;
            continue;
        }
        
        if (ShowSubKey)
        {
            sprintf(Text, "%s - %s", g_WorkspaceKeyDescriptions[SubKey], Name);
            Item = (ULONG)SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Text);
        }
        else
        {
            Item = (ULONG)SendMessage(List, LB_ADDSTRING, 0, (LPARAM)Name);
        }
        SendMessage(List, LB_SETITEMDATA, Item, SubKey);
        
        Index++;
    }

    RegCloseKey(Key);
}

void
FillWorkspaceList(HWND List)
{
    SendMessage(List, LB_RESETCONTENT, 0, 0);
    EnumWorkspaceKey(List, HKEY_CURRENT_USER, WSP_NAME_EXPLICIT,
                     FALSE, FALSE);
    EnumWorkspaceKey(List, HKEY_LOCAL_MACHINE, WSP_NAME_EXPLICIT,
                     FALSE, FALSE);
    SendMessage(List, LB_SETCURSEL, 0, 0);
}

INT_PTR CALLBACK
DlgProc_OpenWorkspace(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Name[MAX_WSP_NAME];
    LRESULT Sel;
    HRESULT Status;

    switch(Message)
    {
    case WM_INITDIALOG:
        SetWindowText(Hwnd, "Open Workspace");
        SendDlgItemMessage(Hwnd, IDC_WORKSPACE_NAME, EM_LIMITTEXT,
                           sizeof(Name) - 1, 0);
        FillWorkspaceList(GetDlgItem(Hwnd, IDC_WORKSPACE_LIST));
        SetWindowText(GetDlgItem(Hwnd, IDC_WORKSPACE_NAME), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_WORKSPACE_LIST:
            Sel = SendDlgItemMessage(Hwnd, IDC_WORKSPACE_LIST,
                                     LB_GETCURSEL, 0, 0);
            if (Sel >= 0 &&
                SendDlgItemMessage(Hwnd, IDC_WORKSPACE_LIST, LB_GETTEXT,
                                   Sel, (LPARAM)Name))
            {
                SetWindowText(GetDlgItem(Hwnd, IDC_WORKSPACE_NAME), Name);
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_OPEN_WORKSPACE);
            break;
            
        case IDOK:
            if (SendDlgItemMessage(Hwnd, IDC_WORKSPACE_NAME, WM_GETTEXT,
                                   sizeof(Name), (LPARAM)Name))
            {
                int Starts = 0;
                
                if ((Status = UiSwitchWorkspace(WSP_NAME_EXPLICIT,
                                                Name, FALSE,
                                                WSP_APPLY_EXPLICIT,
                                                &Starts)) != S_OK)
                {
                    if (Starts < 0)
                    {
                        InformationBox(ERR_Workspace_Session_Conflict, Status);
                    }
                    else
                    {
                        InformationBox(ERR_Cant_Open_Workspace, Status);
                    }
                    break;
                }
                else
                {
                    g_ExplicitWorkspace = TRUE;
                    if (Starts == 1)
                    {
                        StartDebugging();
                    }
                }
            }
            
            // Fall through.
            
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

INT_PTR CALLBACK
DlgProc_SaveWorkspaceAs(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Name[MAX_WSP_NAME];
    LRESULT Sel;
    HRESULT Status;

    switch(Message)
    {
    case WM_INITDIALOG:
        SetWindowText(Hwnd, "Save Workspace As");
        SendDlgItemMessage(Hwnd, IDC_WORKSPACE_NAME, EM_LIMITTEXT,
                           sizeof(Name) - 1, 0);
        FillWorkspaceList(GetDlgItem(Hwnd, IDC_WORKSPACE_LIST));
        SetWindowText(GetDlgItem(Hwnd, IDC_WORKSPACE_NAME),
                      g_Workspace->GetValue());
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_WORKSPACE_LIST:
            Sel = SendDlgItemMessage(Hwnd, IDC_WORKSPACE_LIST,
                                     LB_GETCURSEL, 0, 0);
            if (Sel >= 0 &&
                SendDlgItemMessage(Hwnd, IDC_WORKSPACE_LIST, LB_GETTEXT,
                                   Sel, (LPARAM)Name))
            {
                SetWindowText(GetDlgItem(Hwnd, IDC_WORKSPACE_NAME), Name);
            }
            break;
            
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_SAVE_WORKSPACE_AS);
            break;
            
        case IDOK:
            if (SendDlgItemMessage(Hwnd, IDC_WORKSPACE_NAME, WM_GETTEXT,
                                   sizeof(Name), (LPARAM)Name) == 0)
            {
                MessageBeep(0);
                break;
            }

            Status = g_Workspace->ChangeName(WSP_NAME_EXPLICIT, Name, FALSE);
            if (Status == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
            {
                if (QuestionBox(ERR_Workspace_Already_Exists, MB_YESNO,
                                Name) == IDNO)
                {
                    break;
                }
                
                Status = g_Workspace->ChangeName(WSP_NAME_EXPLICIT, Name,
                                                 TRUE);
            }

            if (Status != S_OK)
            {
                InformationBox(ERR_Cant_Save_Workspace, Status);
                break;
            }

            g_Workspace->Flush(TRUE, FALSE);

            // Fall through.
            
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return FALSE;
}

INT_PTR CALLBACK
DlgProc_AddToCommandHistory(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    char Text[MAX_COMMAND_LEN];
    PSTR TextEnd;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        SendDlgItemMessage(Hwnd, IDC_ATCH_TEXT, EM_LIMITTEXT,
                           sizeof(Text) - 2, 0);
        SetWindowText(GetDlgItem(Hwnd, IDC_ATCH_TEXT), "");
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDHELP:
            OpenHelpTopic(HELP_TOPIC_POPUP_ADD_TO_COMMAND_OUTPUT);
            break;
            
        case IDOK:
            GetWindowText(GetDlgItem(Hwnd, IDC_ATCH_TEXT),
                          Text, sizeof(Text) - 1);
            TextEnd = Text + strlen(Text);
            *TextEnd++ = '\n';
            *TextEnd = 0;
            CmdOutput(Text,
                      g_OutMaskColors[USER_OUT_MASK_COL].Color,
                      g_OutMaskColors[USER_OUT_MASK_COL + 1].Color);
            // Send output for logging only.
            g_pUiControl->ControlledOutput(DEBUG_OUTCTL_LOG_ONLY,
                                           DEBUG_OUTPUT_NORMAL,
                                           "%s", Text);
            // Fall through.
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

void
FillDeleteWorkspacesList(HWND List)
{
    int i;
    
    SendMessage(List, LB_RESETCONTENT, 0, 0);

    for (i = 0; i < WSP_NAME_COUNT; i++)
    {
        EnumWorkspaceKey(List, HKEY_CURRENT_USER, i, TRUE, TRUE);
        EnumWorkspaceKey(List, HKEY_LOCAL_MACHINE, i, TRUE, TRUE);
    }
    
    SendMessage(List, LB_SETCURSEL, 0, 0);
}

INT_PTR CALLBACK
DlgProc_DeleteWorkspaces(HWND Hwnd, UINT Message, WPARAM Wpm, LPARAM Lpm)
{
    HWND List = GetDlgItem(Hwnd, IDC_WORKSPACE_LIST);
    LRESULT Sel;
    char NameBuf[MAX_WSP_NAME];
    PSTR Name;
    
    switch(Message)
    {
    case WM_INITDIALOG:
        FillDeleteWorkspacesList(List);
        break;

    case WM_COMMAND:
        switch(LOWORD(Wpm))
        {
        case IDC_WORKSPACE_LIST:
            if (HIWORD(Wpm) == LBN_DBLCLK)
            {
                goto DelItem;
            }
            break;

        case IDC_DELETE_WORKSPACE:
        DelItem:
            Sel = SendMessage(List, LB_GETCURSEL, 0, 0);
            if (Sel >= 0)
            {
                ULONG Key;

                Key = (ULONG)SendMessage(List, LB_GETITEMDATA, Sel, 0);
                SendMessage(List, LB_GETTEXT, Sel, (LPARAM)NameBuf);
                // Skip over introductory key description.
                Name = NameBuf + strlen(g_WorkspaceKeyDescriptions[Key]) + 3;
                Workspace::DeleteRegKey(TRUE, Key, Name);
                Workspace::DeleteRegKey(FALSE, Key, Name);
                FillDeleteWorkspacesList(List);
            }
            break;
            
        case IDHELP:
            // XXX drewb - Topic.
            // OpenHelpTopic(HELP_TOPIC_POPUP_GO_TO_ADDRESS);
            break;
            
        case IDCANCEL:
            EndDialog(Hwnd, LOWORD(Wpm));
            break;
        }
        break;

    default:
        return FALSE;
    }

    return TRUE;
}

BOOL
CreateIndexedFont(ULONG FontIndex, BOOL SetAll)
{
    HFONT Font;

    Font = CreateFontIndirect(&g_Fonts[FontIndex].LogFont);
    if (Font != NULL)
    {
        if (g_Fonts[FontIndex].Font)
        {
            DeleteObject(g_Fonts[FontIndex].Font);
        }
        g_Fonts[FontIndex].Font = Font;
        g_Fonts[FontIndex].LogFontSet = TRUE;
            
        HDC Dc = GetDC(NULL);
        if (Dc != NULL)
        {
            SelectObject(Dc, Font);
            GetTextMetrics(Dc, &g_Fonts[FontIndex].Metrics);
            ReleaseDC(NULL, Dc);
        }

        if (SetAll)
        {
            SetAllFonts(FontIndex);
        }
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void
SelectFont(HWND Parent, ULONG FontIndex)
{
    CHOOSEFONT Choose;

    ZeroMemory(&Choose, sizeof(Choose));
    Choose.lStructSize = sizeof(Choose);
    Choose.hwndOwner = Parent;
    Choose.lpLogFont = &g_Fonts[FontIndex].LogFont;
    Choose.Flags = CF_FIXEDPITCHONLY | CF_FORCEFONTEXIST |
        CF_SCREENFONTS | (g_Fonts[FontIndex].LogFontSet ?
                          CF_INITTOLOGFONTSTRUCT : 0);
    if (ChooseFont(&Choose))
    {
        if (CreateIndexedFont(FontIndex, TRUE) && g_Workspace != NULL)
        {
            // If this function really is used to select different
            // fonts the tag will have to be dynamically chosen.
            g_Workspace->SetBuffer(WSP_GLOBAL_FIXED_LOGFONT,
                                   &g_Fonts[FontIndex].LogFont,
                                   sizeof(g_Fonts[FontIndex].LogFont));
        }
    }
}

BOOL
SelectColor(HWND Parent, ULONG Index)
{
    CHOOSECOLOR Choose;
    INDEXED_COLOR* IdxCol = GetIndexedColor(Index);

    ZeroMemory(&Choose, sizeof(Choose));
    Choose.lStructSize = sizeof(Choose);
    Choose.hwndOwner = Parent;
    Choose.rgbResult = IdxCol->Color;
    Choose.lpCustColors = g_CustomColors;
    Choose.Flags = CC_ANYCOLOR | CC_RGBINIT;
    if (ChooseColor(&Choose))
    {
        if (g_Workspace != NULL)
        {
            g_Workspace->SetUlong(DEF_WSP_TAG(WSP_GROUP_COLORS, Index),
                                  (ULONG)Choose.rgbResult);
        }

        SetColor(Index, Choose.rgbResult);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}