; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
; $     Автор Чеботарёв А.М. (Chib777)                                    $
; $     chib_odessa@bk.ru                                                 $
; $     chib_odessa@ua.fm                                                 $
; $     16 \\ 01 \\ 2004                                                  $
; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

      .586                      ; create 32 bit code
      .model flat, stdcall      ; 32 bit memory model
      option casemap :none      ; case sensitive
      include Scroll_2.inc      ; local includes for this file

.code

; #########################################################################

start:

        mov hInstance, $invoke (GetModuleHandle, NULL)
        invoke InitCommonControls

        call Main
        invoke ExitProcess,eax

; #########################################################################

Main proc

        ;====================
        ; Put LOCALs on stack
        ;====================

        LOCAL wc   :WNDCLASSEX
        LOCAL msg  :MSG

        LOCAL Wwd  :DWORD
        LOCAL Wht  :DWORD
        LOCAL Wtx  :DWORD
        LOCAL Wty  :DWORD

        ;==================================================
        ; Fill WNDCLASSEX structure with required variables
        ;==================================================

        mov wc.cbSize,         sizeof WNDCLASSEX
        mov wc.style,          CS_HREDRAW or CS_VREDRAW \
                               or CS_BYTEALIGNWINDOW
        mov wc.lpfnWndProc,    offset WndProc
        mov wc.cbClsExtra,     NULL
        mov wc.cbWndExtra,     NULL
        m2m wc.hInstance,      hInstance
        mov wc.hbrBackground,  COLOR_GRAYTEXT
        mov wc.lpszMenuName,   NULL
        mov wc.lpszClassName,  offset szClassName
        mov wc.hIcon, $invoke (LoadIcon,NULL,IDI_APPLICATION)
        mov wc.hCursor, $invoke (LoadCursor,NULL,IDC_ARROW)
        mov wc.hIconSm, 0

        invoke RegisterClassEx, ADDR wc

        ;================================
        ; Centre window at following size
        ;================================

        mov Wwd, 700
        mov Wht, 500

        invoke GetSystemMetrics,SM_CXSCREEN
        mov Wtx, $invoke (TopXY,Wwd,eax)

        invoke GetSystemMetrics,SM_CYSCREEN
        mov Wty, $invoke (TopXY,Wht,eax)

  ; -------------------------------------------------
        invoke LoadMenuA , hInstance, 600

        invoke CreateWindowEx,WS_EX_CLIENTEDGE,
                              ADDR szClassName,
                              ADDR szDisplayName,
                              WS_OVERLAPPEDWINDOW or WS_VISIBLE  or WS_VSCROLL or WS_HSCROLL,
                              Wtx,Wty,Wwd,Wht,
                              NULL,eax,
                              hInstance,NULL
        mov   hWnd,eax

      ;===================================
      ; Loop until PostQuitMessage is sent
      ;===================================

    StartLoop:
      invoke GetMessage,ADDR msg,NULL,0,0
      cmp eax, 0
      je ExitLoop
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage,  ADDR msg
      jmp StartLoop
    ExitLoop:

      return msg.wParam

Main endp

; #########################################################################

WndProc proc hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD

    LOCAL hDC       :HDC
    LOCAL ddbinfo   :BITMAP
    LOCAL lpSi      :SCROLLINFO
    LOCAL NewPos    :SDWORD         ; new position
    LOCAL rect      :RECT

    .if uMsg == WM_COMMAND

        .if wParam == IDM_OPEN

            ; --------------------------------------
            ; szFileName is defined in Filedlgs.asm
            ; --------------------------------------
            mov szFileName[0],0     ; set 1st byte to zero
            invoke GetFileName,hWin,ADDR szOpenTitle,ADDR szFilter
            cmp szFileName[0],0     ; zero if cancel pressed in dlgbox
            je @F
                  ; ---------------------------------
                  ; КОД ЗАГРУЗКИ
                  ; ---------------------------------
            .if hBitmapOld     ; УДАЛИМ РАСТР ИЗ КОНТЕКСТА ЕСЛИ ОН ЗАГРУЖЕН
                invoke SelectObject, hMemDC, hBitmapOld ; исключим hBitmap
                invoke DeleteObject, eax                ; удалим текущий растр
            .endif

            mov hBitmap, $invoke (LoadImage ,hInstance, ADDR szFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)
            mov hBitmapOld, $invoke (SelectObject, hMemDC, hBitmap)

            invoke GetObject, hBitmap, sizeof(BITMAP), ADDR ddbinfo    ; ПОЛУЧИМ ИНФОРМАЦИЮ О DDB - РАСТРЕ

            m2m nWidth, ddbinfo.bmWidth                ; ШИРИНА
            m2m nHeight, ddbinfo.bmHeight              ; ВЫСОТА

            invoke InvalidateRect, hWin, NULL, FALSE    ;Генерация сообщения WM_PAINT
            jmp set_size
            @@:

        .elseif wParam == IDM_EXIT  ; ВЫХОД

            invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL

        .elseif wParam == IDM_ABOUT ; О ПРОГРАММЕ

            invoke DialogBoxParamA, hInstance, IDD_ABOUT, hWin, AboutProc, NULL

        .endif

    .elseif uMsg == WM_CREATE

            ; СОЗДАДИМ СОВМЕСТИМЫЙ КОНТЕКСТ УСТРОЙСТВА
            mov hDC, $invoke( GetDC, hWin )
            mov hMemDC,$invoke ( CreateCompatibleDC, hDC )
            invoke ReleaseDC, hWin, hDC

    .elseif uMsg == WM_SIZE

set_size:    
        invoke GetClientRect, hWin, ADDR rect

        ; ГОРИЗОНТАЛЬНАЯ ЛИНЕЙКА ПРОКРУТКИ

;--------------------------------
        mov eax, nWidth     
        sub eax, rect.right
        mov nHorizDiff, eax
;--------------------------------
        mov eax, nWidth
    .if rect.right < eax    ; отображать линейки прокрутки

        mov lpSi.cbSize, sizeof(SCROLLINFO)
        mov lpSi.fMask, SIF_RANGE or SIF_PAGE
        m2m lpSi.nMin, 0    ; Минимальное значение диапахона прокрутки
        dec eax
        mov lpSi.nMax, eax  ; Максимальное значение диапахона прокрутки
        m2m lpSi.nPage, rect.right

        invoke SetScrollInfo, hWin, SB_HORZ, ADDR lpSi, TRUE

    .else           ;не отображать линейки прокрутки
        invoke SetScrollRange, hWin, SB_HORZ, 0, 0, TRUE
    .endif

        ; ВЕРТИКАЛЬНАЯ ЛИНЕЙКА ПРОКРУТКИ
    
        HIWORD lParam

;--------------------------------
        mov eax, nHeight
        sub eax, rect.bottom
        mov nVertDiff, eax
;--------------------------------
            mov eax, nHeight 
        .if rect.bottom < eax
        
            mov lpSi.cbSize, sizeof(SCROLLINFO)
            mov lpSi.fMask, SIF_RANGE or SIF_PAGE
            m2m lpSi.nMin, 0            ; Минимальное значение диапахона прокрутки
            dec eax                     ; ДИАПАЗОН ПРОКРУТКИ [Min;Max-1], поэтому вычитаем 1            
            mov lpSi.nMax, eax          ; Максимальное значение диапахона прокрутки
            m2m lpSi.nPage, rect.bottom ; РАЗМЕР СТРАНИЦЫ ДОЛЖЕН НАМОДИТЬСЯ МЕЖДУ nMin и nMax
        
            invoke SetScrollInfo, hWin, SB_VERT, ADDR lpSi, TRUE
        
        .else
        
            invoke SetScrollRange, hWin, SB_VERT, 0, 0, TRUE        ;не отображать линейки прокрутки
            
        .endif    
        
        mov fSize, TRUE
        invoke InvalidateRect, hWin, ADDR rect, FALSE    ;Генерация сообщения WM_PAINT

    .elseif uMsg == WM_VSCROLL
    
    LOWORD wParam

    .if eax == SB_LINEDOWN                ; User clicked the right arrow.  
            
            invoke IntDiv, nHeight, 100   ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nVertPos   
          add NewPos, eax

    .elseif eax == SB_LINEUP              ; User clicked the left arrow.  
    
           invoke IntDiv, nHeight, 100    ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nVertPos   
          sub NewPos, eax
          
    .elseif eax == SB_PAGEUP              ; User clicked the shaft left of the scroll box.   

          invoke IntDiv, nHeight, 10      ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax 
            .endif
          m2m NewPos, nVertPos   
          sub NewPos, eax

    .elseif eax == SB_PAGEDOWN            ; User clicked the shaft right of the scroll box.  

          invoke IntDiv, nHeight, 10      ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nVertPos   
          add NewPos, eax

    .elseif ( eax == SB_THUMBTRACK ) || ( eax == SB_THUMBPOSITION ) ; User dragged the scroll box.

        HIWORD wParam
        mov NewPos, ebx
     
    .else                                 ; default  
    
        m2m NewPos, nVertPos

    .endif

    .if NewPos < 0                        ; New position must be between 0 and the screen width.  
        mov NewPos, 0    
    .endif

    mov eax, nVertDiff
    .if NewPos > eax
        mov NewPos, eax
    .endif

     m2m eax, NewPos                      ; Determine the amount scrolled (in pixels).  
     sub eax, nVertPos
     neg eax

    ; Scroll the window. (The system repaints most of the 
    ; client area when ScrollWindowEx is called; however, it is
    ; necessary to call UpdateWindow in order to repaint the 
    ; rectangle of pixels that were invalidated.) 

     invoke ScrollWindowEx, hWin, 0, eax, NULL, NULL, NULL, NULL, SW_INVALIDATE
     m2m nVertPos, NewPos                 ; Reset the current scroll position.   
     invoke SetScrollPos,hWin, SB_VERT, nVertPos, TRUE  ; Reset the scroll bar.     
     invoke InvalidateRect, hWin, NULL, FALSE    ;Генерация сообщения WM_PAINT
    
    .elseif uMsg == WM_HSCROLL
    
    LOWORD wParam

    .if eax == SB_LINERIGHT               ; User clicked the right arrow. 
            
            invoke IntDiv, nWidth, 100    ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nHorizPos   
          add NewPos, eax

    .elseif eax == SB_LINELEFT            ; User clicked the left arrow.
    
           invoke IntDiv, nWidth, 100     ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nHorizPos  
          sub NewPos, eax
          
    .elseif eax == SB_PAGELEFT            ; User clicked the shaft left of the scroll box.  

          invoke IntDiv, nWidth, 10       ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nHorizPos  
          sub NewPos, eax

    .elseif eax == SB_PAGERIGHT           ; User clicked the shaft right of the scroll box.  

          invoke IntDiv, nWidth, 10       ; размер приращения (строки) = 1/100 размера документа (растра)
            .if eax < 1                   ; На случай если растр сильно маленький, что бы приращение не равнялось 0
              inc eax
            .endif
          m2m NewPos, nHorizPos   
          add NewPos, eax

    .elseif ( eax == SB_THUMBTRACK ) || ( eax == SB_THUMBPOSITION ) ; User dragged the scroll box.

        HIWORD wParam
        mov NewPos, ebx 

    .else                                 ; default  
    
        m2m NewPos, nHorizPos
     
    .endif

    .if NewPos < 0                        ; New position must be between 0 and the screen width.
        mov NewPos, 0     
    .endif

     mov eax, nHorizDiff
    .if NewPos > eax
        mov NewPos, eax
    .endif

     m2m eax, NewPos                      ; Determine the amount scrolled (in pixels).
     sub eax, nHorizPos
     neg eax

    ; Scroll the window. (The system repaints most of the 
    ; client area when ScrollWindowEx is called; however, it is 
    ; necessary to call UpdateWindow in order to repaint the 
    ; rectangle of pixels that were invalidated.)

     invoke ScrollWindowEx, hWin, eax, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE
     m2m nHorizPos, NewPos                ; Reset the current scroll position.  
     invoke SetScrollPos,hWin, SB_HORZ, nHorizPos, TRUE ; Reset the scroll bar.  
     invoke InvalidateRect, hWin, NULL, FALSE    ;Генерация сообщения WM_PAINT
     
    .elseif uMsg == WM_PAINT
        invoke Paint_Proc,hWin
        return 0

    .elseif uMsg == WM_CLOSE

    .elseif uMsg == WM_DESTROY

       .if hBitmapOld           ; ОСВОБОДИМ ПАМЯТЬ ЗАНИМАЕМУЮ РАСТРОМ, ЕСЛИ ОН ЗАГРУЖЕН.
        invoke SelectObject, hMemDC, hBitmapOld ; исключим hBitmap
        invoke DeleteObject, eax                ; удалим текущий растр
       .endif

        invoke DeleteDC, hMemDC                 ; освободим ресурсы
        invoke PostQuitMessage,NULL
        return 0

    .endif

def_window_proc:

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

    ret

WndProc endp

; ########################################################################

Paint_Proc proc hWin:DWORD

    LOCAL hDC   :HDC
    LOCAL Ps    :PAINTSTRUCT
    LOCAL rect      :RECT

    mov hDC, $invoke (BeginPaint,hWin,ADDR Ps)

        mov ebx, $invoke (GetScrollPos, hWin, SB_HORZ)
	    add ebx, Ps.rcPaint.left
	    
	    invoke GetScrollPos, hWin, SB_VERT
	    add eax, Ps.rcPaint.top
	
	    mov edx, Ps.rcPaint.right
	    sub edx, Ps.rcPaint.left
	    
	    mov ecx, Ps.rcPaint.bottom
	    sub ecx, Ps.rcPaint.top      
	
	    invoke BitBlt, hDC, Ps.rcPaint.left, Ps.rcPaint.top, edx, ecx, hMemDC, ebx, eax, SRCCOPY

; ----------------------------------------
        invoke EndPaint,hWin,ADDR Ps

    ret

Paint_Proc endp

; ########################################################################

TopXY proc wDim:DWORD, sDim:DWORD

    shr sDim, 1      ; divide screen dimension by 2
    shr wDim, 1      ; divide window dimension by 2
    mov eax, wDim    ; copy window dimension into eax
    sub sDim, eax    ; sub half win dimension from half screen dimension

    return sDim

TopXY endp

; ########################################################################

AboutProc proc hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD
    
   .if  (uMsg == WM_COMMAND && wParam == IDOK) || uMsg == WM_CLOSE
    
    invoke EndDialog, hWin, 0
    return TRUE
                
   .endif
    
    xor eax, eax    
    ret
    
AboutProc endp

; ########################################################################

end start