/*++

Copyright (c) 1993  Microsoft Corporation

Module Name:

    drwtsnui.c

Abstract:

    This function implements the ui (dialog) that controls the
    options maintenace for drwatson.

Author:

    Wesley Witt (wesw) 1-May-1993

Environment:

    User Mode

--*/

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <mmsystem.h>
#include <direct.h>
#include <shellapi.h>

#include "drwatson.h"
#include "proto.h"
#include "resource.h"


void InitializeDialog( HWND hwnd );
void InitializeCrashList( HWND hwnd );
BOOL GetDialogValues( HWND hwnd );
BOOL CALLBACK LogFileViewerDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT DrWatsonWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LPSTR ExpandPath(LPSTR lpPath);


void
DrWatsonWinMain( void )

/*++

Routine Description:

    This is the entry point for DRWTSN32

Arguments:

    None.

Return Value:

    None.

--*/

{
    HWND           hwnd;
    MSG            msg;
    WNDCLASS       wndclass;
    HINSTANCE      hInst;


    hInst                   = GetModuleHandle( NULL );
    wndclass.style          = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc    = DrWatsonWndProc;
    wndclass.cbClsExtra     = 0;
    wndclass.cbWndExtra     = DLGWINDOWEXTRA;
    wndclass.hInstance      = hInst;
    wndclass.hIcon          = LoadIcon( hInst, MAKEINTRESOURCE(APPICON) );
    wndclass.hCursor        = LoadCursor( NULL, IDC_ARROW );
    wndclass.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1);
    wndclass.lpszMenuName   = NULL;
    wndclass.lpszClassName  = "DrWatsonDialog";
    RegisterClass( &wndclass );

    hwnd = CreateDialog( hInst,
                         MAKEINTRESOURCE( DRWATSONDIALOG ),
                         0,
                         DrWatsonWndProc
                       );

    ShowWindow( hwnd, SW_SHOWNORMAL );

    while (GetMessage (&msg, NULL, 0, 0)) {
        if (!IsDialogMessage( hwnd, &msg )) {
            TranslateMessage (&msg) ;
            DispatchMessage (&msg) ;
        }
    }

    return;
}

LRESULT
DrWatsonWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

/*++

Routine Description:

    Window procedure for the DRWTSN32.EXE main user interface.

Arguments:

    hwnd       - window handle to the dialog box
    message    - message number
    wParam     - first message parameter
    lParam     - second message parameter

Return Value:

    TRUE       - did not process the message
    FALSE      - did process the message

--*/

{
    DWORD    helpId;
    char     szCurrDir[MAX_PATH];
    char     szWave[MAX_PATH];
    char     szHelpFileName[MAX_PATH];
    LPSTR    p;


    switch (message) {
        case WM_CREATE:
            return 0;

        case WM_INITDIALOG:
            SubclassControls( hwnd );
            InitializeDialog( hwnd );
            SetTimer( hwnd, 1, 50, NULL );
            return 1;

        case WM_TIMER:
            if (GetKeyState(VK_F1) & 0x8000) {
                if ((GetFocus() != hwnd) && (GetParent(GetFocus()) != hwnd)) {
                    return 0;
                }
                switch (GetDlgCtrlID( GetFocus() )) {
                    case ID_LOGPATH:
                        helpId = IDH_LOGFILELOCATION;
                        break;

                    case ID_BROWSE_LOGPATH:
                        helpId = IDH_LOGFILELOCATION;
                        break;

                    case ID_WAVEFILE_TEXT:
                        helpId = IDH_WAVEFILE;
                        break;

                    case ID_WAVE_FILE:
                        helpId = IDH_WAVEFILE;
                        break;

                    case ID_BROWSE_WAVEFILE:
                        helpId = IDH_WAVEFILE;
                        break;

                    case ID_TEST_WAVE:
                        helpId = IDH_WAVEFILE;
                        break;

                    case ID_INSTRUCTIONS:
                        helpId = IDH_NUMBEROFINSTR;
                        break;

                    case ID_NUM_CRASHES:
                        helpId = IDH_NUMBEROFCRASHES;
                        break;

                    case ID_DUMPSYMBOLS:
                        helpId = IDH_DUMPSYMBOLS;
                        break;

                    case ID_DUMPALLTHREADS:
                        helpId = IDH_DUMPALLTHREADS;
                        break;

                    case ID_APPENDTOLOGFILE:
                        helpId = IDH_APPENDTOLOGFILE;
                        break;

                    case ID_VISUAL:
                        helpId = IDH_VISUAL;
                        break;

                    case ID_SOUND:
                        helpId = IDH_SOUND;
                        break;

                    case ID_CRASHES:
                        helpId = IDH_APPERRORS;
                        break;

                    case ID_LOGFILE_VIEW:
                        helpId = IDH_VIEW;
                        break;

                    case ID_CLEAR:
                        helpId = IDH_CLEAR;
                        break;

                    case IDOK:
                        helpId = IDH_INDEX;
                        break;

                    case IDCANCEL:
                        helpId = IDH_INDEX;
                        break;

                    case ID_HELP:
                        helpId = IDH_INDEX;
                        break;

                    default:
                        helpId = IDH_INDEX;
                        break;
                }
                //
                // call winhelp
                //
                GetHelpFileName( szHelpFileName, sizeof(szHelpFileName ) );
                WinHelp( hwnd, szHelpFileName, HELP_CONTEXT, helpId );
            }
            return 1;

        case WM_ACTIVATEAPP:
        case WM_SETFOCUS:
            SetFocusToCurrentControl();
            return 0;

        case WM_SYSCOMMAND:
            if (wParam == ID_ABOUT) {
                char title[256];
                char extra[256];

                strcpy( title, LoadRcString( IDS_ABOUT_TITLE ) );
                strcpy( extra, LoadRcString( IDS_ABOUT_EXTRA ) );

                ShellAbout( hwnd,
                            title,
                            extra,
                            LoadIcon( GetModuleHandle(NULL),
                                      MAKEINTRESOURCE(APPICON)
                                    )
                          );

                return 0;
            }
            break;

        case WM_COMMAND:
            switch (wParam) {
                case IDOK:
                    if (GetDialogValues( hwnd )) {
                        PostQuitMessage( 0 );
                    }
                    break;

                case IDCANCEL:
                    PostQuitMessage( 0 );
                    break;

                case ID_BROWSE_LOGPATH:
                    GetDlgItemText( hwnd, ID_LOGPATH, szCurrDir, MAX_PATH );
                    p = ExpandPath( szCurrDir );
                    if (p) {
                        strcpy( szCurrDir, p );
                        free( p );
                    }
                    EnableWindow( GetDlgItem( hwnd, ID_BROWSE_LOGPATH ), FALSE );
                    if (BrowseForDirectory( szCurrDir )) {
                        SetDlgItemText( hwnd, ID_LOGPATH, szCurrDir );
                    }
                    EnableWindow( GetDlgItem( hwnd, ID_BROWSE_LOGPATH ), TRUE );
                    SetFocus( GetDlgItem(hwnd, ID_BROWSE_LOGPATH) );
                    return FALSE;
                    break;

                case ID_BROWSE_WAVEFILE:
                    szWave[0] = '\0';
                    EnableWindow( GetDlgItem( hwnd, ID_BROWSE_LOGPATH ), FALSE );
                    if (GetWaveFileName( szWave )) {
                        SetDlgItemText( hwnd, ID_WAVE_FILE, szWave );
                    }
                    EnableWindow( GetDlgItem( hwnd, ID_BROWSE_LOGPATH ), TRUE );
                    SetFocus( GetDlgItem(hwnd, ID_BROWSE_WAVEFILE) );
                    return FALSE;
                    break;

                case ID_CLEAR:
                    ElClearAllEvents();
                    InitializeCrashList( hwnd );
                    break;

                case ID_TEST_WAVE:
                    GetDlgItemText( hwnd, ID_WAVE_FILE, szWave, sizeof(szWave) );
                    PlaySound( szWave, NULL, SND_FILENAME );
                    break;

                case ID_LOGFILE_VIEW:
                    DialogBoxParam( GetModuleHandle( NULL ),
                           MAKEINTRESOURCE( LOGFILEVIEWERDIALOG ),
                           hwnd,
                           LogFileViewerDialogProc,
                           SendMessage((HWND)GetDlgItem(hwnd,ID_CRASHES),
                                        LB_GETCURSEL,0,0)
                         );
                    break;

                case ID_HELP:
                    //
                    // call winhelp
                    //
                    GetHelpFileName( szHelpFileName, sizeof(szHelpFileName ) );
                    WinHelp( hwnd, szHelpFileName, HELP_CONTEXT, IDH_INDEX );
                    SetFocus( GetDlgItem(hwnd, ID_HELP) );
                    break;

                default:
                    if (((HWND)lParam == GetDlgItem( hwnd, ID_CRASHES )) &&
                        (HIWORD( wParam ) == LBN_DBLCLK)) {
                        DialogBoxParam( GetModuleHandle( NULL ),
                               MAKEINTRESOURCE( LOGFILEVIEWERDIALOG ),
                               hwnd,
                               LogFileViewerDialogProc,
                               SendMessage((HWND)lParam,LB_GETCURSEL,0,0)
                             );
                    }
                    break;
            }
            break;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

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

BOOL CALLBACK
EnumCrashes( PCRASHINFO crashInfo )

/*++

Routine Description:

    Enumeration function for crash records.  This function is called
    once for each crash record.  This function places the formatted
    crash data in a listbox.

Arguments:

    crashInfo      - pointer to a CRASHINFO structure

Return Value:

    TRUE           - caller should continue calling the enum procedure
    FALSE          - caller should stop calling the enum procedure

--*/

{
    SIZE size;
    char buf[1024];

    wsprintf( buf, "%s  %08x  %s(%08x)",
              crashInfo->crash.szAppName,
              crashInfo->crash.dwExceptionCode,
              crashInfo->crash.szFunction,
              crashInfo->crash.dwAddress
            );
    SendMessage( crashInfo->hList, LB_ADDSTRING, 0, (LPARAM)buf );


    GetTextExtentPoint( crashInfo->hdc, buf, strlen(buf), &size );
    if (size.cx > (LONG)crashInfo->cxExtent) {
        crashInfo->cxExtent = size.cx;
    }

    return TRUE;
}


void
InitializeCrashList( HWND hwnd )

/*++

Routine Description:

    Initializes the listbox that contains the crash information.

Arguments:

    None.

Return Value:

    None.

--*/

{
    CRASHINFO     crashInfo;
    TEXTMETRIC    tm;
    HFONT         hFont;

    crashInfo.hList = GetDlgItem( hwnd, ID_CRASHES );
    SendMessage( crashInfo.hList, LB_RESETCONTENT, FALSE, 0L );
    SendMessage( crashInfo.hList, WM_SETREDRAW, FALSE, 0L );
    crashInfo.hdc = GetDC( crashInfo.hList );
    crashInfo.cxExtent = 0;

    ElEnumCrashes( &crashInfo, EnumCrashes );

    hFont = (HFONT)SendMessage( crashInfo.hList, WM_GETFONT, 0, 0L );
    if (hFont != NULL) {
        SelectObject( crashInfo.hdc, hFont );
    }
    GetTextMetrics( crashInfo.hdc, &tm );
    ReleaseDC( crashInfo.hList, crashInfo.hdc );
    SendMessage( crashInfo.hList, LB_SETHORIZONTALEXTENT, crashInfo.cxExtent, 0L );
    SendMessage( crashInfo.hList, WM_SETREDRAW, TRUE, 0L );

    return;
}

void
InitializeDialog( HWND hwnd )

/*++

Routine Description:

    Initializes the DRWTSN32 user interface dialog with the values
    stored in the registry.

Arguments:

    hwnd       - window handle to the dialog

Return Value:

    None.

--*/

{
    OPTIONS       o;
    char          buf[256];
    HMENU         hMenu;


    RegInitialize( &o );
    SetDlgItemText( hwnd, ID_LOGPATH, o.szLogPath );
    SetDlgItemText( hwnd, ID_WAVE_FILE, o.szWaveFile );
    wsprintf( buf, "%d", o.dwMaxCrashes );
    SetDlgItemText( hwnd, ID_NUM_CRASHES, buf );
    wsprintf( buf, "%d", o.dwInstructions );
    SetDlgItemText( hwnd, ID_INSTRUCTIONS, buf );
    SendMessage( GetDlgItem( hwnd, ID_DUMPSYMBOLS ), BM_SETCHECK, o.fDumpSymbols, 0 );
    SendMessage( GetDlgItem( hwnd, ID_DUMPALLTHREADS ), BM_SETCHECK, o.fDumpAllThreads, 0 );
    SendMessage( GetDlgItem( hwnd, ID_APPENDTOLOGFILE ), BM_SETCHECK, o.fAppendToLogFile, 0 );
    SendMessage( GetDlgItem( hwnd, ID_VISUAL ), BM_SETCHECK, o.fVisual, 0 );
    SendMessage( GetDlgItem( hwnd, ID_SOUND ), BM_SETCHECK, o.fSound, 0 );

    if (waveOutGetNumDevs() == 0) {
        EnableWindow( GetDlgItem( hwnd, ID_WAVEFILE_TEXT ), FALSE );
        EnableWindow( GetDlgItem( hwnd, ID_WAVE_FILE ), FALSE );
        EnableWindow( GetDlgItem( hwnd, ID_BROWSE_WAVEFILE ), FALSE );
    }

    InitializeCrashList( hwnd );

    if (SendMessage( GetDlgItem( hwnd, ID_CRASHES ), LB_GETCOUNT, 0 ,0 ) == 0) {
        EnableWindow( GetDlgItem( hwnd, ID_CLEAR ), FALSE );
        EnableWindow( GetDlgItem( hwnd, ID_LOGFILE_VIEW ), FALSE );
    }

    hMenu = GetSystemMenu( hwnd, FALSE );
    if (hMenu != NULL) {
        AppendMenu( hMenu, MF_SEPARATOR, 0, NULL );
        AppendMenu( hMenu, MF_STRING, ID_ABOUT, "About Dr. Watson..." );
    }

    return;
}

BOOL
GetDialogValues( HWND hwnd )

/*++

Routine Description:

    Retrieves the values in the DRWTSN32 dialog controls and saves
    them in the registry.

Arguments:

    hwnd       - window handle to the dialog

Return Value:

    TRUE       - all values were retrieved and saved
    FALSE      - an error occurred

--*/

{
    OPTIONS  o;
    char     buf[256];
    DWORD    dwFa;
    LPSTR    p;


    RegInitialize( &o );

    GetDlgItemText( hwnd, ID_LOGPATH, buf, sizeof(buf) );

    p = ExpandPath( buf );
    if (p) {
        dwFa = GetFileAttributes( p );
        free( p );
    } else {
        dwFa = GetFileAttributes( buf );
    }

    if ((dwFa == 0xffffffff) || (!(dwFa&FILE_ATTRIBUTE_DIRECTORY))) {
        NonFatalError( LoadRcString(IDS_INVALID_PATH) );
        return FALSE;
    }

    if (strlen(buf) > 0) {
        strcpy( o.szLogPath, buf );
    }

    GetDlgItemText( hwnd, ID_WAVE_FILE, buf, sizeof(buf) );
    if (strlen(buf) > 0) {
        dwFa = GetFileAttributes( buf );
        if ((dwFa == 0xffffffff) || (dwFa&FILE_ATTRIBUTE_DIRECTORY)) {
            NonFatalError( LoadRcString(IDS_INVALID_WAVE) );
            return FALSE;
        }
    }

    strcpy( o.szWaveFile, buf );

    GetDlgItemText( hwnd, ID_NUM_CRASHES, buf, sizeof(buf) );
    o.dwMaxCrashes = (DWORD) atol( buf );

    GetDlgItemText( hwnd, ID_INSTRUCTIONS, buf, sizeof(buf) );
    o.dwInstructions = (DWORD) atol( buf );

    o.fDumpSymbols = SendMessage( GetDlgItem( hwnd, ID_DUMPSYMBOLS ), BM_GETCHECK, 0, 0 );
    o.fDumpAllThreads = SendMessage( GetDlgItem( hwnd, ID_DUMPALLTHREADS ), BM_GETCHECK, 0, 0 );
    o.fAppendToLogFile = SendMessage( GetDlgItem( hwnd, ID_APPENDTOLOGFILE ), BM_GETCHECK, 0, 0 );
    o.fVisual = SendMessage( GetDlgItem( hwnd, ID_VISUAL ), BM_GETCHECK, 0, 0 );
    o.fSound = SendMessage( GetDlgItem( hwnd, ID_SOUND ), BM_GETCHECK, 0, 0 );

    RegSave( &o );

    return TRUE;
}

BOOL CALLBACK
EnumCrashesForViewer( PCRASHINFO crashInfo )

/*++

Routine Description:

    Enumeration function for crash records.  This function is called
    once for each crash record.  This function looks for s specific crash
    that is identified by the crashIndex.

Arguments:

    crashInfo      - pointer to a CRASHINFO structure

Return Value:

    TRUE           - caller should continue calling the enum procedure
    FALSE          - caller should stop calling the enum procedure

--*/

{
    char *p;

    if ((crashInfo->dwIndex == crashInfo->dwIndexDesired) &&
        (crashInfo->dwCrashDataSize > 0) ) {
        p = crashInfo->pCrashData;
        crashInfo->pCrashData = malloc( crashInfo->dwCrashDataSize+10 );
        memcpy( crashInfo->pCrashData, p, crashInfo->dwCrashDataSize+10 );
        crashInfo->pCrashData[crashInfo->dwCrashDataSize] = 0;
        return FALSE;
    }

    crashInfo->dwIndex++;

    return TRUE;
}

BOOL CALLBACK
LogFileViewerDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

/*++

Routine Description:

    Window procedure for the log file viewer dialog box.

Arguments:

    hwnd       - window handle to the dialog box
    message    - message number
    wParam     - first message parameter
    lParam     - second message parameter

Return Value:

    TRUE       - did not process the message
    FALSE      - did process the message

--*/

{
    CRASHINFO     crashInfo;
    HFONT         hFont;

    switch (message) {
        case WM_INITDIALOG:
            crashInfo.dwIndex = 0;
            crashInfo.dwIndexDesired = lParam;
            ElEnumCrashes( &crashInfo, EnumCrashesForViewer );
            if (crashInfo.dwIndex != crashInfo.dwIndexDesired) {
                MessageBeep( 0 );
                EndDialog( hwnd, 0 );
                return FALSE;
            }
            SetDlgItemText( hwnd, ID_LOGFILE_VIEW, crashInfo.pCrashData );

            hFont = GetStockObject( SYSTEM_FIXED_FONT );
            Assert( hFont != NULL );

            SendDlgItemMessage( hwnd,
                                ID_LOGFILE_VIEW,
                                WM_SETFONT,
                                (WPARAM) hFont,
                                (LPARAM) FALSE
                              );
            return TRUE;

        case WM_COMMAND:
            if (wParam == IDOK) {
                free( crashInfo.pCrashData );
                EndDialog( hwnd, 0 );
            }
            break;
    }

    return FALSE;
}
