//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1996 - 1999.
//
//  File:       brctrl.cxx
//
//  Contents:   
//
//  History:    15 Aug 1996     DLee    Created
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#define TheView (*_pView)
#define TheModel (*_pModel)

// Member functions responding to messages

LRESULT WINAPI BrowseWndProc(
    HWND   hwnd,
    UINT   msg,
    WPARAM wParam,
    LPARAM lParam)
{
    LRESULT lr = 0;
    CBrowseWindow *pbw = (CBrowseWindow *) GetWindowLongPtr( hwnd, 0 );

    switch (msg)
    {
        case WM_CREATE:
        {
            App.BrowseLastError() = S_OK;
            CREATESTRUCT *pcs = (CREATESTRUCT *) lParam;
            MDICREATESTRUCT *pmcs = (MDICREATESTRUCT *) pcs->lpCreateParams;
            CQueryResult *pResults = (CQueryResult *) pmcs->lParam;
            pbw = new CBrowseWindow();
            SetWindowLongPtr( hwnd, 0, (LONG_PTR) pbw);
            lr = pbw->TheBrowseController.Create( pResults,
                                                  hwnd,
                                                  &pbw->TheBrowseModel,
                                                  &pbw->TheBrowseView,
                                                  App.BrowseFont() );
            break;
        }
        case WM_MDIACTIVATE :
            pbw->TheBrowseController.Activate( hwnd, lParam );
            break;
        case WM_SIZE:
            lr = DefMDIChildProc( hwnd, msg, wParam, lParam );
            pbw->TheBrowseController.Size( hwnd, lParam );
            break;
        case WM_VSCROLL:
            pbw->TheBrowseController.VScroll(hwnd, wParam, lParam);
            break;
        case WM_HSCROLL:
            pbw->TheBrowseController.HScroll(hwnd, wParam, lParam);
            break;
        case WM_KEYDOWN:
            pbw->TheBrowseController.KeyDown (hwnd, wParam );
            lr = DefMDIChildProc(hwnd,msg,wParam,lParam);
            break;
        case WM_CHAR:
            pbw->TheBrowseController.Char (hwnd, wParam);
            lr = DefMDIChildProc(hwnd,msg,wParam,lParam);
            break;
        case WM_PAINT:
            pbw->TheBrowseController.Paint(hwnd);
            break;
        case wmMenuCommand:
            pbw->TheBrowseController.Command(hwnd, wParam);
            break;
        case wmInitMenu:
            pbw->TheBrowseController.EnableMenus();
            break;
        case WM_DESTROY:
            SetWindowLongPtr(hwnd,0,0);
            delete pbw;
            SetProcessWorkingSetSize( GetCurrentProcess(), -1, -1 );
            break;
        case wmNewFont:
            pbw->TheBrowseController.NewFont(hwnd, wParam);
            break;
        case WM_LBUTTONUP:
            pbw->TheBrowseView.ButtonUp( wParam, lParam );
            break;
        case WM_LBUTTONDOWN:
            pbw->TheBrowseView.ButtonDown( wParam, lParam );
            break;
        case WM_LBUTTONDBLCLK:
            pbw->TheBrowseView.DblClk( wParam, lParam );
            break;
        case WM_MOUSEMOVE :
            pbw->TheBrowseView.MouseMove( wParam, lParam );
            break;
        case WM_MOUSEWHEEL :
            lr = pbw->TheBrowseController.MouseWheel( hwnd, wParam, lParam );
            break;
        case WM_CONTEXTMENU :
            pbw->TheBrowseController.ContextMenu( hwnd, wParam, lParam );
            break;
        case EM_GETSEL:
        {
            if ( pbw->TheBrowseView.GetSelection().SelectionExists() )
                lr = MAKELRESULT( 1, 2 );
            else
                lr = 0;
            break;
        }
        default:
            lr = DefMDIChildProc(hwnd,msg,wParam,lParam);
            break;
    }

    return lr;
} //BrowseWndProc

void Control::ContextMenu(
    HWND   hwnd,
    WPARAM wParam,
    LPARAM lParam )
{
    POINT pt;
    pt.x = LOWORD( lParam );
    pt.y = HIWORD( lParam );

    GetCursorPos( &pt );

    HMENU hMenu = LoadMenu( App.Instance(), L"BrowseContextMenu" );

    if ( 0 != hMenu )
    {
        HMENU hTrackMenu = GetSubMenu( hMenu, 0 );
        if ( 0 != hTrackMenu )
        {
            if ( !TheView.GetSelection().SelectionExists() )
                EnableMenuItem( hTrackMenu,
                                IDM_EDITCOPY,
                                MF_BYCOMMAND | MF_GRAYED );

            // yes, the function returns a BOOL that you switch on

            BOOL b = TrackPopupMenuEx( hTrackMenu,
                                       TPM_LEFTALIGN | TPM_RIGHTBUTTON |
                                           TPM_RETURNCMD,
                                       pt.x,
                                       pt.y,
                                       hwnd,
                                       0 );
            switch ( b )
            {
                case IDM_BROWSE_OPEN :
                {
                    ViewFile( TheModel.Filename(),
                              fileOpen );
                    break;
                }
                case IDM_BROWSE_EDIT :
                {
                    POINT winpt;
                    winpt.x = 0;
                    winpt.y = 0;
                    ClientToScreen( hwnd, &winpt );
                    ViewFile( TheModel.Filename(),
                              fileEdit,
                              TheView.ParaFromY( pt.y - winpt.y ) );
                    break;
                }
                case IDM_EDITCOPY :
                {
                    Command( hwnd, b );
                    break;
                }
            }
        }

        DestroyMenu( hMenu );
    }
} //ContextMenu

LRESULT Control::Create(
    CQueryResult * pResults,
    HWND           hwnd,
    Model *        pModel,
    View *         pView,
    HFONT          hFont )
{
    LRESULT lr = 0;

    _iWheelRemainder = 0;
    _pModel = pModel;
    _pView = pView;
    SCODE sc = TheModel.CollectFiles( pResults );

    if ( SUCCEEDED( sc ) )
    {
        TheView.Init( hwnd, _pModel, hFont );

        // Go to first query hit (if any)

        Position pos;

        if ( TheModel.GetPositionCount() != 0 )
            pos = TheModel.GetPosition(0);

        TheView.SetRange ( TheModel.MaxParaLen(), TheModel.Paras());

        RECT rc;
        GetClientRect(hwnd,&rc);

        TheView.Size( rc.right, rc.bottom );

        TheView.SetScroll (pos);

        EnableMenus();
   }
   else
   {
       // no better way to get the error back

       App.BrowseLastError() = sc;
       lr = -1; // don't continue creating the window
   }

   return lr;
} //Create

void Control::Activate( HWND hwnd, LPARAM lParam )
{
    if ( hwnd == (HWND) lParam )
    {
        int apos[3] = { 0, 0, 0 };
        int cPos = 2;

        HDC hdc = GetDC( hwnd );

        if ( 0 == hdc )
            return;

        SIZE size;

        WCHAR awcLines[100];
        CResString strLines( IDS_BRSTAT_CLINES );
        wsprintf( awcLines, strLines.Get(), TheModel.ParaCount() );
        GetTextExtentPoint( hdc, awcLines, wcslen( awcLines ), &size );
        apos[0] = 2 * size.cx;

        WCHAR awcHits[100];
        CResString strHits( IDS_BRSTAT_CHITS );
        wsprintf( awcHits, strHits.Get(), TheModel.HitCount(), TheModel.HitCount() );
        GetTextExtentPoint( hdc, awcHits, wcslen( awcHits ), &size );
        apos[1] = 2 * size.cx + apos[0];

        ReleaseDC( hwnd, hdc );

        SendMessage( App.StatusBarWindow(), SB_SETPARTS, cPos, (LPARAM) apos );

        SendMessage( App.StatusBarWindow(), SB_SETTEXT, 0, (LPARAM) awcLines );

        static UINT aDisable[] = { IDM_SEARCH,
                                   IDM_SEARCHCLASSDEF,
                                   IDM_SEARCHFUNCDEF,
                                   IDM_BROWSE,
                                   IDM_DISPLAY_PROPS };
        UpdateButtons( aDisable, 5, FALSE );

        EnableMenus();
    }
} //Activate

void Control::NewFont ( HWND hwnd, WPARAM wParam)
{
    TheView.FontChange ( hwnd, (HFONT) wParam );
    InvalidateRect(hwnd, NULL, TRUE);
} //NewFont

void Control::SetScrollBars ( HWND hwnd )
{
    SetScrollPos ( hwnd, SB_VERT, TheView.VScrollPara(), TRUE );
    SetScrollPos ( hwnd, SB_HORZ, TheView.HScrollPos(), TRUE );
} //SetScrollBars

// Go to next query hit when 'n' pressed

void Control::Char ( HWND hwnd, WPARAM wparam )
{
    Position pos;
    HCURSOR hCursor;
    BOOL success;

    switch ( wparam )
    {
        case 'n':
        {
            if ( !TheModel.NextHit() )
                break;

            pos = TheModel.GetPosition(0);
            TheView.SetScroll( pos );
            SetScrollBars (hwnd);

            EnableMenus();
            InvalidateRect(hwnd, NULL, TRUE);
            break;
        }
        case 'p':
        {
            if ( !TheModel.PrevHit() )
                break;

            pos = TheModel.GetPosition(0);
            TheView.SetScroll( pos );
            SetScrollBars(hwnd);

            EnableMenus();
            InvalidateRect(hwnd, NULL, TRUE);
            break;
        }
        case 'N':
        {
            hCursor = SetCursor (LoadCursor(0, IDC_WAIT));
            ShowCursor (TRUE);
            success = S_OK == TheModel.NextDoc();
            ShowCursor(FALSE);
            SetCursor (hCursor);

            if ( success )
            {
                if ( TheModel.GetPositionCount() != 0 )
                {
                    pos = TheModel.GetPosition(0);
                }
                TheView.SetRange ( TheModel.MaxParaLen(), TheModel.Paras());
                TheView.SetScrollMax();
                TheView.SetScroll (pos);
                UpdateScroll(hwnd);

                InvalidateRect(hwnd, NULL, TRUE);
                EnableMenus();
                SetWindowText( hwnd, TheModel.Filename() );
            }
            break;
        }
        case 'P':
        {
            hCursor = SetCursor (LoadCursor(0, IDC_WAIT));
            ShowCursor (TRUE);
            success = TheModel.PrevDoc();
            ShowCursor(FALSE);
            SetCursor (hCursor);
            if ( success )
            {
                if ( TheModel.GetPositionCount() != 0 )
                    pos = TheModel.GetPosition(0);

                TheView.SetRange ( TheModel.MaxParaLen(), TheModel.Paras());
                TheView.SetScrollMax();
                TheView.SetScroll (pos);
                UpdateScroll(hwnd);
                InvalidateRect(hwnd, NULL, TRUE);
                EnableMenus();
                SetWindowText( hwnd, TheModel.Filename() );
            }
            break;
        }
    }
} //Char

void Control::EnableMenus()
{
    UINT ui = IDM_NEXT_HIT;
    UpdateButtons( &ui, 1, ! TheModel.isLastHit() );
    ui = IDM_PREVIOUS_HIT;
    UpdateButtons( &ui, 1, ! TheModel.isFirstHit() );

    HMENU hmenu = GetMenu( App.AppWindow() );

    EnableMenuItem(hmenu,IDM_NEXT_HIT,MF_BYCOMMAND |
                   (TheModel.isLastHit() ? MF_GRAYED | MF_DISABLED  :
                                           MF_ENABLED) );

    EnableMenuItem(hmenu,IDM_PREVIOUS_HIT,MF_BYCOMMAND |
                   (TheModel.isFirstHit() ? MF_GRAYED | MF_DISABLED  :
                                            MF_ENABLED) );

    EnableMenuItem( hmenu, IDM_NEWSEARCH, MF_ENABLED );

    int cHits = TheModel.HitCount();

    WCHAR awcHits[100];
    if ( 0 == cHits )
    {
        awcHits[0] = 0;
    }
    else
    {
        CResString strHits( IDS_BRSTAT_CHITS );
        wsprintf( awcHits,
                  strHits.Get(),
                  TheModel.CurrentHit() + 1,
                  cHits );
    }
    SendMessage( App.StatusBarWindow(), SB_SETTEXT, 1, (LPARAM) awcHits );
} //EnableMenus

void Control::Size ( HWND hwnd, LPARAM lParam )
{
    TheView.Size ( LOWORD(lParam), HIWORD(lParam) );
    TheView.SetScrollMax();
    UpdateScroll( hwnd );
    // in case we have to scroll to close up the gap
    // at the bottom of the window
    int delta = TheView.IncVScrollPos( 0 );
    if (delta != 0)
    {
        MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0 );
        SetScrollPos (hwnd, SB_VERT, TheView.VScrollPara(), TRUE );
        UpdateWindow(hwnd);
    }
} //Size

void Control::UpdateScroll( HWND hwnd)
{
    TheView.SetRange ( TheModel.MaxParaLen(), TheModel.Paras());

    SetScrollRange(hwnd, SB_VERT, 0, TheView.VScrollMax(), FALSE );
    SetScrollRange(hwnd, SB_HORZ, 0, TheView.HScrollMax(), FALSE );

    SetScrollBars (hwnd);

    // proportional scroll box
    SCROLLINFO si;
    si.cbSize = sizeof(si);
    si.fMask = SIF_PAGE;
    si.nPage = TheView.VisibleLines();
    SetScrollInfo( hwnd, SB_VERT, &si, TRUE );
} //UpdateScroll

LRESULT Control::MouseWheel( HWND hwnd, WPARAM wParam, LPARAM lParam )
{
    // forward what we don't process

    if ( wParam & ( MK_SHIFT | MK_CONTROL ) )
        return DefMDIChildProc( hwnd, WM_MOUSEWHEEL, wParam, lParam );

    // add the current scroll to the remainder from last time

    int iDelta = (int) (short) HIWORD( wParam );
    iDelta += _iWheelRemainder;

    // if there isn't enough to process this time, just return

    if ( abs( iDelta ) < WHEEL_DELTA )
    {
        _iWheelRemainder = iDelta;
        return 0;
    }

    // compute the remainder and amount to scroll

    _iWheelRemainder = ( iDelta % WHEEL_DELTA );
    iDelta /= WHEEL_DELTA;

    BOOL fDown;
    if ( iDelta < 0 )
    {
        fDown = TRUE;
        iDelta = -iDelta;
    }
    else
        fDown = FALSE;

    // get the # of lines to scroll per WHEEL_DELTA

    int cLines;
    SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &cLines, 0 );
    if ( 0 == cLines )
        return 0;

    int cVisibleLines = TheView.VisibleLines();

    // if scrolling a page, do so.  don't scroll more than one page

    if ( WHEEL_PAGESCROLL == cLines )
        iDelta = __max( 1, (cVisibleLines - 1) );
    else
    {
        iDelta *= cLines;
        if ( iDelta >= cVisibleLines )
            iDelta = __max( 1, (cVisibleLines - 1) );
    }

    // phew.  do the scroll

    if ( 0 != iDelta )
    {
        int delta = TheView.IncVScrollPos( fDown ? iDelta : -iDelta );
        MyScrollWindow( hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
        SetScrollPos( hwnd, SB_VERT, TheView.VScrollPara(), TRUE );
        UpdateWindow( hwnd );
    }

    return iDelta;
} //MouseWheel

void Control::VScroll( HWND hwnd, WPARAM wParam, LPARAM lParam )
{
    int nVScrollInc;
    int delta = 0;

    switch (LOWORD(wParam))
    {
        case SB_TOP:
            TheView.Home();
            InvalidateRect (hwnd, 0, TRUE);
            delta = 1;
            break;
        case SB_BOTTOM:
            TheView.End();
            InvalidateRect (hwnd, 0, TRUE);
            delta = 1;
            break;

        case SB_LINEUP:
            delta = TheView.IncVScrollPos( -1 );
            MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
            break;
        case SB_LINEDOWN:
            nVScrollInc = 1;
            delta = TheView.IncVScrollPos( 1 );
            MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
            break;

        case SB_PAGEUP:
            nVScrollInc = - max ( 1, TheView.VisibleLines() - 1);
            delta = TheView.IncVScrollPos( nVScrollInc );
            MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
            break;
        case SB_PAGEDOWN:
            nVScrollInc = max ( 1, TheView.VisibleLines() - 1);
            delta = TheView.IncVScrollPos( nVScrollInc );
            MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
            break;

        case SB_THUMBTRACK:
            delta = TheView.JumpToPara ( HIWORD(wParam) );
            MyScrollWindow (hwnd, 0, -delta * TheView.LineHeight(), 0, 0, FALSE );
            break;
        default:
            break;
    }

    if ( delta != 0 )
    {
        SetScrollPos (hwnd, SB_VERT, TheView.VScrollPara(), TRUE );
        UpdateWindow(hwnd);
    }
} //VScroll

void Control::HScroll( HWND hwnd, WPARAM wParam, LPARAM lParam )
{
}

void Control::KeyDown ( HWND hwnd, WPARAM wparam )
{
    switch(wparam)
    {
        case VK_HOME:
            SendMessage( hwnd, WM_VSCROLL, SB_TOP, 0L );
            break;
        case VK_END:
            SendMessage( hwnd, WM_VSCROLL, SB_BOTTOM, 0L );
            break;
        case VK_PRIOR:
            SendMessage( hwnd, WM_VSCROLL, SB_PAGEUP, 0L );
            break;
        case VK_NEXT:
            SendMessage( hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L );
            break;
        case VK_UP:
            SendMessage( hwnd, WM_VSCROLL, SB_LINEUP, 0L );
            break;
        case VK_DOWN:
            SendMessage( hwnd, WM_VSCROLL, SB_LINEDOWN, 0L );
            break;
        case VK_LEFT:
            SendMessage( hwnd, WM_HSCROLL, SB_PAGEUP, 0L );
            break;
        case VK_RIGHT:
            SendMessage( hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L );
            break;
    }
} //KeyDown

// Menu commands processing

void Control::Command ( HWND hwnd, WPARAM wParam )
{
    switch ( wParam )
    {
        case IDM_NEXT_HIT:
            SendMessage ( hwnd, WM_CHAR, 'n', 0L );
            break;
        case IDM_PREVIOUS_HIT:
            SendMessage ( hwnd, WM_CHAR, 'p', 0L );
            break;
        case IDM_NEWSEARCH:
            // close the browser window
            PostMessage( hwnd, WM_CLOSE, 0, 0 );
            break;
        case IDM_EDITCOPY :
            TheView.EditCopy( hwnd, wParam );
    }
} //Command