comment $
'==================================================================='
'  Программа предварительного просмотра для RichEdit контрола.      '
'                                                                   '
'  chib_odessa@bk.ru      .                                         '
'  XXControls@bk.ru                                                '
'==================================================================='$
 
.686
.MODEL flat,stdcall
OPTION CASEMAP:NONE

;-------------------------------------------------------------------

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\gdi32.inc
include \masm32\include\Comctl32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\Comctl32.lib

;-------------------------------------------------------------------

include \masm32\include\masm32.inc
includeLib \masm32\lib\masm32.lib

include \masm32\include\debug.inc
includeLib \masm32\lib\debug.lib

;-------------------------------------------------------------------

; библиотка для использования меню с иконками

include XXControls.inc
includeLib XXControls.lib

; библиотка для использования меню с иконками

include XXAbout.inc
includeLib XXAbout.lib

;-------------------------------------------------------------------


PreViewProc         PROTO :DWORD,:DWORD,:DWORD,:DWORD
Paint_Proc_View     PROTO :DWORD
ViewProc            PROTO :DWORD,:DWORD,:DWORD,:DWORD

GetMsgProc          PROTO :DWORD, :WPARAM, :LPARAM

DrawPaper           PROTO :HDC,:DWORD,:DWORD,:DWORD
px                  PROTO :DWORD,:DWORD
py                  PROTO :DWORD,:DWORD
screen_to_dpi       PROTO :DWORD
SetPaper            PROTO
SetDefaultPSD       PROTO
SetZoom             PROTO :DWORD
DoToolBar           PROTO
DlgProc_ToPage      PROTO :HWND, :UINT, :WPARAM, :LPARAM
Get_Doc_Page_Info   PROTO :HWND

;original procedure
;DrawPaperFrame      PROTO :HDC,:DWORD,:DWORD,:DWORD,:DWORD,:COLORREF,:COLORREF,:BYTE
DrawPaperFrame      PROTO :HDC,:DWORD,:DWORD,:DWORD
DrawPaperShade      PROTO :HDC,:DWORD
DrawBorderProc   PROTO :HMENU,:HDC,:DWORD

include XXRichEdit.inc

include \masm32\MACROS\Macros.asm

      ;=============
      ; Local macros
      ;=============

      m2m MACRO M1, M2
        push M2
        pop  M1
      ENDM

      return MACRO arg
        mov eax, arg
        ret
      ENDM

$invoke MACRO Fun:REQ, A:VARARG
  IFB <A>
    invoke Fun
  else
    invoke Fun, A
  endif
  exitm <eax>
        ENDM

LOWORD MACRO bigword    ; Возврвщает младщее слово  
   mov  eax,bigword
   and  eax,0FFFFh     ;; Set to low word 
ENDM


HIWORD macro bigword    ; Возврвщает старшее слово 
   mov  eax,bigword
   shr  eax,16         ;; Get high word
endm

IF_MSG macro WMsg:REQ, DoJmp:REQ
   cmp uMsg, WMsg
   jz  DoJmp
endm

WM_CASE macro reg,msgs
   irp msg,<msgs>
      cmp reg,WM_&msg
      je @@&msg
   endm
endm

.data
szPreViewClass      db          "PreViewClass32",0
szClassView         db          "View32",0
ToolClass           db          "tooltips_class32",0
szToolBarClass      db          "ToolbarWindow32",0
;szPreViewName       db          "Окно предварительного просмотра",0
szPreViewName       db          "Print Preview",0

DocName             db          "Print document",0 ;имя документа для печати

XXRichEdit          db          "XXRichEdit",0

; сообщения, для MsgBox если в системе нет принтера

;szNoPrinter         db          "Не найдено ни одного принтера",0
;szNoPreView         db          "Предварительный просмотр невозможен",0
;szPrepare           db          "Документ подготоавливаеться к предварительному просмотру. Пожалуйста подождите.",0
szPrepare           db          "Document prepare", 0

szNoPrinter         db          "No printer",0
szNoPreView         db          "Print Preview failed",0


;szError             db          "Ошибка!",0
;szErrPageNumber     db          "Вы ввели недопустимый номер страницы",0
;szErrTickNumber     db          "Данная страница сейчас отображаеться",0

szError             db          "Error!",0
szErrPageNumber     db          "You entered invalid number of the page",0
szErrTickNumber     db          "This is a current page",0

; Строки для пунктов меню

szFile              db          "File",0
szPrintSetup        db          " Page Setup",0
szPrint             db          " Print",0
szClose             db          " Close",0
szView              db          "&View",0
szFirstPage         db          "&First Page",0
szPreviousPage      db          "&Previous Page",0
szToPage            db          "&Go to page",0
szNextPage          db          "&Next Page",0
szLastPage          db          "&Last Page",0
szZoom              db          "Zoom...",0
szZoomIn            db          "Zoom in",0
szZoomOut           db          "Zoom out",0
szZoom10            db          "10",0
szZoom25            db          "25",0
szZoom50            db          "50",0
szZoom75            db          "75",0
szZoom100           db          "100",0
szZoom150           db          "150",0
szZoom200           db          "200",0
szZoom400           db          "400",0
szZoom500           db          "500",0
szSelectOnly        db          "&Selection Only",0
szAbout		        db	        "About XXRechEdit",0

;szFile              db          "Файл",0
;szPrintSetup        db          " Параметры страницы",0
;szPrint             db          " Печать",0
;szClose             db          " Закрыть",0
;szView              db          "&Вид",0
;szFirstPage         db          "&Первая страница",0
;szPreviousPage      db          "&Предыдущая страница",0
;szToPage            db          "&Перейти к странице",0
;szNextPage          db          "&Следующая страница",0
;szLastPage          db          "&Последняя страница",0
;szZoom              db          "Zoom...",0
;szZoomIn            db          "Увеличить",0
;szZoomOut           db          "Уменьшить",0
;szZoom10            db          "10",0
;szZoom25            db          "25",0
;szZoom50            db          "50",0
;szZoom75            db          "75",0
;szZoom100           db          "100",0
;szZoom150           db          "150",0
;szZoom200           db          "200",0
;szZoom400           db          "400",0
;szZoom500           db          "500",0
;szSelectOnly        db          "&Только выделенное",0
;szTools             db          "&Инструменты",0
;szAbout             db          "О программе",0
NameFile            db          "save.emf",0
szNameFontNP        db          "Courier New",0
m_nZoom             dd          100                  ; 100 for 1:1
m_nScale            dd          96                   ; GetDeviceCaps(hDC, LOGPIXELSX) * zoom / 100
m_nSurfaceWidth     dd          320                  ; total surface width  in pixel
m_nSurfaceHeight    dd          240                  ; total surface height in pixel
;lf                  LOGFONTA    <-15, 9, 0, 0, 400, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN, "Courier New">
;lf_prn              LOGFONTA    <-150, 90, 0, 0, 400, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN, "Courier New">
lf_mnu_brd          LOGFONTA    <22, 0, 900, 900, 600, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN, "Courier New">
fPrnPageNumbers     db          TRUE                 ; по умолчснию печатаем номера страниц
TabTitlePageNumbers db          "Номера страниц",0
cRed                dd          128 ;значение красного цвета
cGreen              dd          128 ;--------- зелёного цвета
cBlue               dd          128 ;--------- синего цвета

.data?
hWinMain            HWND        ?
hInst               HINSTANCE   ?
hWnd                HWND        ?
hHook               HHOOK       ?
h_Accel             HACCEL      ?
hStatus             HWND        ?
hToolBar            HWND        ?
hWndView            HWND        ?
hMenu               HMENU       ?
hSysMenu            HMENU       ?
hContextMenu        HMENU       ?
m_psd               PAGESETUPDLG <?>
m_pd                PRINTDLG   <?>

m_Paper             SIZEL      <?>                ; in BASE_DPI
m_MinMargin         RECT       <?>                ; минимальные размеры полей 
m_Margin            RECT       <?>                ; текущие размеры полей
pDevNames           dd          ?
pDevMode            dd          ?
hRichEdit           HWND        ?
PageRect            RECT       <?>
PageWidth           dd          ?
PageHeight          dd          ?

; SCROLL

nHorizDiff          dd          ?
nVertDiff           dd          ?

; ИНФОРМАЦИЯ О ТЕКСТЕ

chrg                CHARRANGE  <?>
nCountPage          DWORD       ?                    ; число страниц в документе
nCurrentPage        DWORD       ?                    ; текущая страница
fSelOnly            db          ?
pChar               DWORD       ?                    ; указатель на массив номеров символов начала и конца страницы для всех страниц

; Настройка


; ВСПОМОГАТЕЛЬНЫЕ ДАННЫЕ ДЛЯ МЕНЮ
hImageList          HIMAGELIST  ?

.const
PREVIEWMENU         equ         601
IDD_PREVIEW         equ         101
IDD_FORMVIEW        equ         102
IDD_TOPAGE          equ         103
IDC_SBR1            equ         1100
IDC_TBR1            equ         1180
IDC_ED_TOPAGE       equ         1185

IDM_PAGE_SETUP      equ         1002
IDM_PRINT           equ         1004
IDM_EXIT            equ         1006

IDM_FIRSTPAGE       equ         1012
IDM_PREVIOUSPAGE    equ         1014
IDM_TOPAGE          equ         1015
IDM_NEXTPAGE        equ         1016
IDM_LASTPAGE        equ         1018
IDM_ZOOM            equ         1020
IDM_VIEW_ZOOM10     equ         1100    ; zoom
IDM_VIEW_ZOOM25     equ         1102
IDM_VIEW_ZOOM50     equ         1106
IDM_VIEW_ZOOM75     equ         1108
IDM_VIEW_ZOOM100    equ         1110
IDM_VIEW_ZOOM150    equ         1112
IDM_VIEW_ZOOM200    equ         1114
IDM_VIEW_ZOOM400    equ         1116
IDM_VIEW_ZOOM500    equ         1118
IDM_SELECTONLY      equ         1022

IDM_PAGE_NUMBER     equ         1032

IDM_HELP_ABOUT      equ         1050

ACC                 equ         110

BASE_DPI            equ         1440
MARGIN_X            equ         16
MARGIN_Y            equ         16


; ######################## TOOLBAR ########################
IDB_BITMAP1         equ         101
PreViewToolBar      equ         1700
IDC_TBB             equ         350
IDT_ZOOM_IN         equ         1200
IDT_ZOOM_OUT        equ         1201
tbrbtns             TBBUTTON <0,IDM_PRINT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <1,IDM_PAGE_SETUP,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
                    TBBUTTON <10,IDM_SELECTONLY,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
                    TBBUTTON <2,IDM_FIRSTPAGE,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <3,IDM_PREVIOUSPAGE,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <4,IDM_TOPAGE,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <5,IDM_NEXTPAGE,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <6,IDM_LASTPAGE,TBSTATE_INDETERMINATE,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
                    TBBUTTON <7,IDT_ZOOM_IN,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <8,IDT_ZOOM_OUT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
                    TBBUTTON <9,IDM_EXIT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
                    TBBUTTON <12,IDM_HELP_ABOUT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
                    TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>                    
ntbrbtns            equ         18

mnubtns             MNUBUTTON <IDM_PAGE_SETUP,1>
                    MNUBUTTON <IDM_PRINT,0>
                    MNUBUTTON <IDM_EXIT,9>
                    
                    MNUBUTTON <IDM_FIRSTPAGE,2>
                    MNUBUTTON <IDM_PREVIOUSPAGE,3>
                    MNUBUTTON <IDM_TOPAGE,4>
                    MNUBUTTON <IDM_NEXTPAGE,5>
                    MNUBUTTON <IDM_LASTPAGE,6>
                    MNUBUTTON <IDM_ZOOM,11>
                    MNUBUTTON <IDM_SELECTONLY,10>
                    
                    MNUBUTTON <IDM_HELP_ABOUT,12>            
nmnubtns            equ         12

.code

;===========================================================================;
; Name:     DllEntry                                                        ;
; Function: Performs an undo action (if any)                                ;
;                                                                           ;
; Params:   lpEditData = Address of the RS Edit control's data structure    ;
;                                                                           ;
; Returns:  Nothing                                                         ;
;===========================================================================;

DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD

LOCAL icex  :INITCOMMONCONTROLSEX

    .if reason==DLL_PROCESS_ATTACH                 ; DLL загружена
        m2m hInst, hInstance
        invoke SetDefaultPSD
        
        ; Установим информацию о листе бумаги
        call SetPaper
        
        mov icex.dwSize, sizeof (INITCOMMONCONTROLSEX)
        mov icex.dwICC, ICC_BAR_CLASSES
        invoke InitCommonControlsEx, ADDR icex
                
        return TRUE
    .elseif reason==DLL_PROCESS_DETACH             ; DLL выгружаеться

        invoke GlobalFree, m_pd.hDevMode
        invoke GlobalFree, m_pd.hDevNames
        
        .if pChar
            invoke GlobalFree, pChar        ;освободим память
        .endif

    .elseif reason==DLL_THREAD_ATTACH

    .else        ; DLL_THREAD_DETACH

    .endif
    mov  eax,1
    ret
DllEntry endp

 ; --------------------------------------
 ; Create main window for preview
 ; --------------------------------------

PUBLIC CreatePreView

CreatePreView proc hWin :HWND, hREdit :DWORD

    call CreatePrinterDC
    .if eax == 0
    
        invoke MessageBox, hWin, ADDR szNoPrinter, ADDR szNoPreView, MB_OK or MB_ICONSTOP
           
    .else
    
        ; Загружаем таблицу акселераторов.
        mov h_Accel, $invoke (LoadAccelerators, hInst, ACC)
    
        m2m hRichEdit, hREdit
        m2m hWinMain, hWin
        invoke DialogBoxParamA,hInst,IDD_PREVIEW,hWin,ADDR PreViewProc, NULL
    
        invoke DestroyAcceleratorTable, h_Accel 
        
    .endif

    ret 
CreatePreView endp

 ; =======================================================================
 ;  Процедура окна предварительного просмотра
 ; =======================================================================

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

    LOCAL hDC :HDC
    LOCAL Rct :RECT
    LOCAL Ps  :PAINTSTRUCT
    LOCAL sbh :DWORD
    LOCAL sbParts[3] :DWORD
    LOCAL buff[32]:BYTE
    LOCAL buffer1[128]:BYTE
    LOCAL var :DWORD
    LOCAL charrange :CHARRANGE
    LOCAL mii :MENUITEMINFO
    

    WM_CASE uMsg, <INITDIALOG, COMMAND, SIZE, CLOSE, SYSCOMMAND>
     
@@end:    
    xor eax, eax 
    ret     
     
@@INITDIALOG:  
        
        ; Подключим оперативные клавиши
        ; Устанавливаем локальный хук WH_GETMESSAGE на текущий поток.
      
        call GetCurrentThreadId
        invoke SetWindowsHookEx, WH_GETMESSAGE, ADDR GetMsgProc, NULL, eax 
        mov hHook, eax
        
        ; Сохраняем хэндл диалога.
        m2m hWnd, hWin        
       
        ; Установим иконку
        
        invoke SendMessage,hWin,WM_SETICON,1, $invoke ( GetClassLong, hWinMain, GCL_HICON )
        
        ; Создадим строку состояния
        
        mov hStatus, $invoke ( GetDlgItem, hWin, IDC_SBR1 )

        mov [sbParts +  0], 160
        mov [sbParts +  4], 320
        mov [sbParts +  8], -1

        invoke SendMessage,hStatus,SB_SETPARTS, 3,ADDR sbParts
        ; ToolBar
        call DoToolBar
        mov hToolBar, eax
        
;        ; Создадим окно представления (это окно содержит полосы прокрутки)

        mov hWndView, $invoke ( CreateDialogParam,hInst,IDD_FORMVIEW,hWin,ADDR ViewProc, NULL )

        invoke ShowWindow, hWin, SW_MAXIMIZE        ; развернём на весь экран, раньше нельзя

        ; Установим информацию о листе бумаги, возможно она была измененна в основной программе

        call SetPaper
        
        ; При каждом появлении окна маштаб устанавлеваеться 100%
        
        invoke SetZoom, 100
        
        ; Создадим меню        
        
        mov hMenu, $invoke (GetMenu, hWin)              ; выберем дескриптор меню       
        mov hContextMenu, $invoke (GetSubMenu, hMenu, 1); выберем дескриптор конекстного меню
        call SetMenuXX  ; Добавим в меню иконки
        invoke GetSubMenu, hMenu, 1
        invoke AddLeftBorder, eax, ADDR DrawBorderProc, TRUE        
        
        ; системному меню новый стиль
        mov hSysMenu, $invoke (GetSystemMenu, hWin, FALSE) ; выберем дескриптор меню
        invoke AppendMenu, hSysMenu, MF_SEPARATOR, 0, 0        ; Добавим к системному меню пункт меню сепартор
        ; Добавим к системному меню пункт меню "О программе"
        invoke GetMenuString, hMenu, IDM_HELP_ABOUT, ADDR buffer1, 128, MF_BYCOMMAND    ; получим строку
        invoke AppendMenu, hSysMenu, MF_STRING, IDM_HELP_ABOUT, ADDR buffer1    
        ; Установим иконку на добавленом пункте меню.
        invoke SetMenuItemIcon, hSysMenu, IDM_HELP_ABOUT, $invoke (ImageList_GetIcon, hImageList, 12, ILD_NORMAL)
        call GetStyleMenu       
        invoke RedrawMenu, hWin, hSysMenu, eax
        
;       инициализация меню      
        
        invoke SendMessage, hRichEdit, EM_EXGETSEL, NULL, ADDR charrange ; есть выделенный текст ?
        mov eax, charrange.cpMin

        ; Если есть выделенный фрагмент текста включить режим SELECTONLY  
        
        ; если charrange.cpMax == charrange.cpMin тогда выделенного фрагмента нет. 
        ; такое равенство получаеться если при первом появлении окна превью был 
        ; выделен фрагмент текста, а потом выделение текста снято, и превью было 
        ; вызвано снова.
        
        .if ( !charrange.cpMax )  || ( charrange.cpMax == eax )       ; нет выделенного фрагмента текста               
         mov fSelOnly, FALSE
         invoke SendMessage, hToolBar, TB_CHECKBUTTON, IDM_SELECTONLY, FALSE
;        invoke EnableMenuItem, hMenu, IDM_SELECTONLY, MF_GRAYED or MF_BYCOMMAND    ; в ресурсах запретим пункт меню, а можно и здесь        
        .else                         ; есть выделенный фрагмент текста 
         mov fSelOnly, TRUE
         invoke EnableMenuItem, hMenu, IDM_SELECTONLY, MF_ENABLED or MF_BYCOMMAND
         invoke CheckMenuItem, hMenu, IDM_SELECTONLY,MF_CHECKED or MF_BYCOMMAND
         invoke SendMessage, hToolBar, TB_SETSTATE, IDM_SELECTONLY, TBSTATE_ENABLED
         invoke SendMessage, hToolBar, TB_CHECKBUTTON, IDM_SELECTONLY, TRUE                 
        .endif
        
        invoke CheckMenuRadioItem, hMenu, IDM_VIEW_ZOOM10, IDM_VIEW_ZOOM500, IDM_VIEW_ZOOM100, MF_BYCOMMAND        


        invoke GetDC, hWin
        mov hDC, eax
        invoke SetBkMode, hDC, TRANSPARENT
        invoke GetClientRect, hWin, ADDR Rct
        invoke szLen, ADDR szPrepare
        mov var, eax
        invoke GetStockObject, SYSTEM_FONT
        invoke SelectObject, hDC, eax
        push eax
        invoke DrawText, hDC, ADDR szPrepare, var, ADDR Rct, DT_CENTER or DT_VCENTER or DT_SINGLELINE
        push hDC
        call SelectObject
        invoke ReleaseDC, hWin, hDC

       ; Определим кол - во страниц в документе, для данных размеров страницы, 
       ; устанавливаеться в глобальную перемнную nCountPage
        
        invoke Get_Doc_Page_Info, hRichEdit
        
        .if nCountPage > 1
              ; Установим состояние пунктов меню, в соответствии с кол - ом страниц в документе
        
              mov nCurrentPage, 1
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_TOPAGE, MF_ENABLED or MF_BYCOMMAND
              
              ; Установим состояние кнопок ToolBara, в соответствии с кол - ом страниц в документе 
              
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_TOPAGE, TBSTATE_ENABLED
                                      
        .else 
              mov nCurrentPage, 1              
        .endif 
        
        ; Информацию в строку состояния        
         
        invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
        invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
        invoke wsprintf,ADDR buff,SADD(" Zoom: %i%%"), m_nZoom
        invoke SendMessage, hStatus, SB_SETTEXT, 1, ADDR buff
        
        jmp @@end
          
@@COMMAND:
        mov eax,wParam
        
        .if ax == IDM_PAGE_SETUP

             invoke CreatePageSetup, hWin        
             call SetPaper
             
            ; Определим кол - во страниц в документе, для данных размеров страницы, 
            ; устанавливаеться в глобальную перемнную nCountPage
            
             invoke Get_Doc_Page_Info, hRichEdit

; ================================!!!!!!!!!!!!!=========================================== ;
; СДЕЛАЕМ ЗАЩИТУ ОТ ТОГО ЧТО ТЕКУЩАЯ СТРАНИЦА МОЖЕТ ПРЕВЫСИТЬ ОБЩЕЕ ЧИСЛО СТРАНИЦ          ; 
; ВСЛЕДСТВИИ УВЕЛИЧЕНИЯ ИЛИ УМЕНЬШЕНИЯ ОТСТУПОВ (НЕ ЗАБЫТЬ В БУДУЩЕМ ОФОРМИТЬ В ВИДЕ proc) ;
; ================================!!!!!!!!!!!!!=========================================== ;
           
            mov edx, nCountPage     ; Если текущая страница больше общего числа страниц, исправим ошибку
            
            .if nCurrentPage > edx
               m2m nCurrentPage, nCountPage
               
            .elseif nCurrentPage < edx ; если всё нормально, тогда проверим после увеличения числа страниц
                                       ; не остались закрытыми кнопки, далее, последняя страница,
                                       ; проявляется только если текущая страница, установлена последней
                                       ; и пользователь установил отображать номера страниц
               invoke SendMessage, hToolBar, TB_ISBUTTONENABLED, IDM_LASTPAGE, 0
               .if !eax
                invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED   ; ToolBar
                invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED
                invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED  or MF_BYCOMMAND             ; Menu
                invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
               .endif
                
            .endif          
        
            ; Информацию в строку состояния         
            invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
            invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
             
; ЕСЛИ ИЗМЕНИЛСЯ ФОРМАТ, РАЗМЕР и т.д. ЛИСТА, ИЗМЕНИМ РАЗМЕРЫ ПОЛОС ПРОКРУТКИ - СООТВЕТСТВЕННО            
             
            call CalculateSize
            call SetSize
            
            invoke GetClientRect, hWin, ADDR Rct 
            invoke InvalidateRect, hWndView, ADDR Rct, FALSE
        
        .elseif ax == IDM_PRINT

            invoke PrintREdit, hWin, hRichEdit        
        
        .elseif ax == IDM_PAGE_NUMBER       ; печатать номра страниц ?
        
             invoke GetMenuState, hMenu, IDM_PAGE_NUMBER, MF_BYCOMMAND        
             and eax, MF_CHECKED
             
             .if eax == MF_CHECKED
                mov fPrnPageNumbers,  FALSE
                invoke CheckMenuItem,hMenu, IDM_PAGE_NUMBER, MF_BYCOMMAND or MF_UNCHECKED                        
             .else
                mov fPrnPageNumbers, TRUE                
                invoke CheckMenuItem,hMenu, IDM_PAGE_NUMBER, MF_BYCOMMAND or MF_CHECKED
             .endif

               ; обновим отображение страницы (число стр. могло увеличиться и уменьшиться)
                
                invoke Get_Doc_Page_Info, hRichEdit
                
                mov edx, nCountPage     ; Если текущая страница больше общего числа страниц, исправим ошибку
                
                .if nCurrentPage > edx
                   m2m nCurrentPage, nCountPage
                   
                .elseif nCurrentPage < edx ; если всё нормально, тогда проверим после увеличения числа страниц
                                           ; не остались закрытыми кнопки, далее, последняя страница,
                                           ; проявляется только если текущая страница, установлена последней
                                           ; и пользователь установил отображать номера страниц
                   invoke SendMessage, hToolBar, TB_ISBUTTONENABLED, IDM_LASTPAGE, 0
                   .if !eax
                    invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED   ; ToolBar
                    invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED
                    invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND              ; Menu
                    invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
                   .endif
                    
                .endif              
                
                ; Информацию в строку состояния         
                invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
                invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
                invoke InvalidateRect, hWndView, NULL, FALSE
        
        .elseif ax == IDM_HELP_ABOUT    ; О программе          
          
             invoke CreateXXAboutDlg, hWin         
       
        .elseif ax == IDM_SELECTONLY
        
             invoke GetMenuState, hMenu, IDM_SELECTONLY, MF_BYCOMMAND or MF_BYCOMMAND
             
             and eax, MF_CHECKED
             
             .if eax == MF_CHECKED           ; отображаем весь документ            
                mov fSelOnly,  FALSE
                invoke SendMessage, hToolBar, TB_CHECKBUTTON, IDM_SELECTONLY, FALSE
                invoke CheckMenuItem,hMenu, IDM_SELECTONLY, MF_BYCOMMAND or MF_UNCHECKED                        
             .else              ; только выделенное             
                mov fSelOnly, TRUE
                invoke SendMessage, hToolBar, TB_CHECKBUTTON, IDM_SELECTONLY, TRUE
                invoke CheckMenuItem,hMenu, IDM_SELECTONLY, MF_BYCOMMAND or MF_CHECKED             
             .endif

             invoke Get_Doc_Page_Info, hRichEdit
             mov nCurrentPage, 1     ; утанвливаем в 1, для того что бы избавиться от глюков
             
             .if nCountPage > 1
              
              invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_GRAYED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_GRAYED or MF_BYCOMMAND                         
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
      
              ; ToolBar button State

              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_INDETERMINATE   
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_INDETERMINATE
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED
              
             .else     

              invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_GRAYED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_GRAYED or MF_BYCOMMAND                          
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_GRAYED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_GRAYED or MF_BYCOMMAND
      
              ; ToolBar button State

              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_INDETERMINATE   
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_INDETERMINATE
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_INDETERMINATE
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_INDETERMINATE

                            
             .endif             
             
             invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
             invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
             
             invoke InvalidateRect, hWndView, NULL, FALSE
       
        .elseif ax == IDM_EXIT  ; ВЫХОД     

             invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL            
            
        .elseif ax == IDM_VIEW_ZOOM10        
             
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_IN, TBSTATE_ENABLED        ; разрешим на ToolBare увеличение
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_OUT, TBSTATE_INDETERMINATE ; запретим на ToolBare уменьшение
             invoke SetZoom, 10
             jmp check
        
        .elseif ax == IDM_VIEW_ZOOM25

             invoke SetZoom, 25
             jmp tbr_enable
            
        .elseif ax == IDM_VIEW_ZOOM50

             invoke SetZoom, 50
             jmp tbr_enable     
        
        .elseif ax == IDM_VIEW_ZOOM75
   
             invoke SetZoom, 75
             jmp tbr_enable
            
        .elseif ax == IDM_VIEW_ZOOM100
     
             invoke SetZoom, 100
             jmp tbr_enable    
        
        .elseif ax == IDM_VIEW_ZOOM150

             invoke SetZoom, 150
             jmp tbr_enable
            
        .elseif ax == IDM_VIEW_ZOOM200

             invoke SetZoom, 200
             jmp tbr_enable
        
        .elseif ax == IDM_VIEW_ZOOM400
             
             invoke SetZoom, 400
             jmp tbr_enable     
            
        .elseif ax == IDM_VIEW_ZOOM500
        
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_OUT, TBSTATE_ENABLED       ; разрешим на ToolBare уменьшение
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_IN, TBSTATE_INDETERMINATE  ; запретим на ToolBare увеличение
             invoke SetZoom, 500
             jmp check
             
tbr_enable:  ; разрешим кнопки ToolBara маштабирование в диапозоне 25% - 400% - включительно
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_OUT, TBSTATE_ENABLED
             invoke SendMessage, hToolBar, TB_SETSTATE, IDT_ZOOM_IN, TBSTATE_ENABLED                          
        
check:       invoke CheckMenuRadioItem, hMenu, IDM_VIEW_ZOOM10, IDM_VIEW_ZOOM500, wParam, MF_BYCOMMAND
             call CalculateSize
             call SetSize         ; Изменим размеры полос прокрутки а соответствии с новым маштабом
             invoke GetClientRect, hWin, ADDR Rct
             invoke InvalidateRect, hWin, ADDR Rct, FALSE
             ; Отобразим в строке состояния новый маштаб
              
             mov buff, 0
             invoke SendMessage, hStatus, SB_SETTEXT, 1, ADDR buff
             invoke wsprintf,ADDR buff,SADD(" Zoom: %i%%"), m_nZoom
             invoke SendMessage, hStatus, SB_SETTEXT, 1, ADDR buff
              
        .endif
        
        .if ax == IDM_FIRSTPAGE
        
              ; MenuItem State
        
              mov nCurrentPage, 1
              invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_GRAYED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_GRAYED or MF_BYCOMMAND                          
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
      
              ; ToolBar button State

              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_INDETERMINATE   
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_INDETERMINATE
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED            
              
              ; Изменим номер страницы в строке состояния
              
              mov buff, 0
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff           
              invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
              
              invoke InvalidateRect, hWin, NULL, FALSE  ; Отобразим новую стрницу 
   
        .elseif ax == IDM_PREVIOUSPAGE
        
              dec nCurrentPage
              .if nCurrentPage == 1
                 invoke SendMessage, hWin, WM_COMMAND, IDM_FIRSTPAGE, NULL               
              .else
                 invoke InvalidateRect, hWin, NULL, FALSE              
              .endif
              
              ; MenuItem State            
              
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
              
              ; ToolBar button State
              
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED                        

              ; Изменим номер страницы в строке состояния
              
              mov buff, 0
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff           
              invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
        
        .elseif ax == IDM_NEXTPAGE
              inc nCurrentPage
              mov edx, nCountPage 
              .if nCurrentPage == edx
                 invoke SendMessage, hWin, WM_COMMAND, IDM_LASTPAGE, NULL
              .else
                 invoke InvalidateRect, hWin, NULL, FALSE   
              .endif
              
              ; MenuItem State            
              
              invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_ENABLED or MF_BYCOMMAND
              
              ; ToolBar button State
              
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_ENABLED 
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_ENABLED
              
             ; Изменим номер страницы в строке состояния
              
              mov buff, 0
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff           
              invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
        
        .elseif ax == IDM_LASTPAGE

              m2m nCurrentPage, nCountPage
              
              ; MenuItem State              
              invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_ENABLED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_ENABLED  or MF_BYCOMMAND            
              invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_GRAYED or MF_BYCOMMAND
              invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_GRAYED or MF_BYCOMMAND
              
              ; ToolBar button State
              
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_ENABLED 
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_ENABLED
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_INDETERMINATE
              invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_INDETERMINATE
                                                
              ; Изменим номер страницы в строке состояния
              
              mov buff, 0
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff           
              invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
              invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
              
              invoke InvalidateRect, hWin, NULL, FALSE  ; Отобразим новую стрницу
              
        .elseif ax == IDM_TOPAGE        ; to page № ->
        
            xor eax, eax
            mov ax, IDD_TOPAGE
        
            invoke DialogBoxParam, hInst, eax, hWin, ADDR DlgProc_ToPage, 0
                 
        .endif
        
        .if ax == IDT_ZOOM_IN         ; Увеличить

          .if m_nZoom == 10

            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM25, NULL
          
          .elseif m_nZoom == 25
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM50, NULL
          
          .elseif m_nZoom == 50
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM75, NULL
          
          .elseif m_nZoom == 75
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM100, NULL
          
          .elseif m_nZoom == 100
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM150, NULL
          
          .elseif m_nZoom == 150
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM200, NULL
          
          .elseif m_nZoom == 200
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM400, NULL
          
          .elseif m_nZoom == 400
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM500, NULL
          
          .endif  

        .elseif ax == IDT_ZOOM_OUT    ; Уменьшить
        
          .if m_nZoom == 25
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM10, NULL
            
          .elseif m_nZoom == 50
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM25, NULL          
          
          .elseif m_nZoom == 75
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM50, NULL
          
          .elseif m_nZoom == 100
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM75, NULL
          
          .elseif m_nZoom == 150
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM100, NULL
          
          .elseif m_nZoom == 200
         
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM150, NULL
          
          .elseif m_nZoom == 400
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM200, NULL
          
          .elseif m_nZoom == 500
          
            invoke SendMessage, hWin, WM_COMMAND, IDM_VIEW_ZOOM400, NULL

          .endif   

        .endif
        jmp @@end
        
@@SYSCOMMAND:

        mov eax, wParam    
        .if ax == IDM_HELP_ABOUT        ; О ПРОГРАММЕ
            invoke CreateXXAboutDlg, hWin 
        .endif
        
        jmp @@end        
        
@@SIZE:
        invoke SendMessage,hToolBar,TB_AUTOSIZE,0,0
        invoke MoveWindow, hStatus, 0, 0, 0, 0, TRUE        
        invoke GetClientRect,hStatus,ADDR Rct
        m2m sbh, Rct.bottom         ;высота строки состояния
        invoke GetClientRect,hToolBar,ADDR Rct
        m2m var, Rct.bottom         ;высота строки TOOLBARA
      ; --------------------------------------------------------------------
      ; Use a valid handle in the following MoveWindow() call and uncomment
      ; --------------------------------------------------------------------
;после изменения размера главного окна изменим размер окна для рисования
        invoke GetClientRect, hWin, ADDR Rct
        mov eax, Rct.bottom 
        sub eax, sbh            ;сделаем поправку на высоту строки состояния
        sub eax, var
        mov edx, Rct.top
        add edx, var
        invoke MoveWindow,hWndView,Rct.left,edx,Rct.right,eax,TRUE  
        jmp @@end
        
@@CLOSE:

        ; Снимаем хук для оперативных клавиш.
        invoke UnhookWindowsHookEx, hHook 

        invoke ImageList_Destroy, hImageList

        ; установим флаг в TRUE что бы при вызове процедуры печати, из основной программы, не учитывалось

        mov fSelOnly, TRUE

        invoke EndDialog, hWin, 0

        jmp @@end
    
PreViewProc endp

; ########################################################################
; Процедура хука, генерирует сообщение WM_COMMAND для оперативных клавиш
; ########################################################################

GetMsgProc proc uses ebx nCode :DWORD, wParam :WPARAM, lParam :LPARAM

mov ebx, lParam
assume ebx: ptr MSG

    .if [ebx].message >= WM_KEYFIRST && [ebx].message <= WM_KEYLAST
    
        ; Преобразуем нажатия клавиш в WM_COMMAND.
        invoke TranslateAccelerator, hWnd, h_Accel, lParam
        
        .if eax
            ; Если сообщение преобразовано, уничтожаем его, устанавливая код в WM_NULL.
            ; Если этого не сделать, сообщение попадёт в диалог, и он недовольно "запищит".
            mov [ebx].message, WM_NULL
        .endif  
    .endif

assume ebx: nothing

    ; Вызываем следующий фильтр.
    invoke CallNextHookEx, hHook, nCode, wParam, lParam 
    xor eax, eax
    ret
GetMsgProc endp

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

ViewProc proc uses ebx hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD

   LOCAL hDC :HDC
   LOCAL Rct :RECT
   LOCAL NewPos :SDWORD         ; new position
   LOCAL ScrollPix :SDWORD
   LOCAL v_dy   :SDWORD
   LOCAL h_dx   :SDWORD
   
   LOCAL hRgn :HRGN

    WM_CASE uMsg, <INITDIALOG, PAINT, SIZE, CONTEXTMENU, HSCROLL, VSCROLL>

@@end:
    
    xor eax, eax 
    ret
    
@@INITDIALOG:

        call CalculateSize
        call SetSize
        jmp  @@end

@@PAINT:

        invoke Paint_Proc_View,hWin
;        return 0
        jmp  @@end
        
@@SIZE:   
        call CalculateSize
        call SetSize
        invoke GetClientRect, hWin, ADDR Rct
        invoke InvalidateRect, hWin, ADDR Rct, FALSE
        
        jmp  @@end

@@CONTEXTMENU:
        push 0
        push hWnd
        push 0
        HIWORD lParam           ; вертикальное расположение всплывающего меню
        push eax
        LOWORD lParam           ; горизонтальное расположение всплывающего меню
        push eax
        push 0
        push hContextMenu
        call TrackPopupMenu
        jmp @@end        
        
@@HSCROLL:   

    invoke GetScrollPos, hWin, SB_HORZ
    mov ebx, eax

    LOWORD wParam

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

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

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

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

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

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

        HIWORD wParam
        mov NewPos, eax 

    .else                                 ; default  
    
        m2m NewPos, ebx
     
    .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

     invoke SetScrollPos,hWin, SB_HORZ, NewPos, TRUE ; Reset the scroll bar.

     m2m eax, NewPos                      ; Determine the amount scrolled (in pixels).
     sub eax, ebx
     neg eax
     mov h_dx, 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, h_dx, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE

     jmp  @@end      
        
@@VSCROLL:

    invoke GetScrollPos, hWin, SB_VERT
    mov ebx, eax

    LOWORD wParam

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

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

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

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

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

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

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

    .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

     invoke SetScrollPos,hWin, SB_VERT, NewPos, TRUE  ; Reset the scroll bar.

     m2m eax, NewPos                      ; Determine the amount scrolled (in pixels). 
     sub eax, ebx 
     neg eax
     mov v_dy, 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, v_dy, NULL, NULL, NULL, NULL, SW_INVALIDATE

     jmp  @@end
    
ViewProc endp

 ;========================================================================
 ;  Процедура обработки сообщения WM_PAINT окна предв. просмотра
 ; =======================================================================

Paint_Proc_View proc hWin:DWORD

    LOCAL hDC   :HDC
    LOCAL Ps    :PAINTSTRUCT
    LOCAL Rct   :RECT
    LOCAL rect  :RECT
    LOCAL tect  :RECT
    LOCAL fr    :FORMATRANGE
    LOCAL hMetaDC:HDC
    LOCAL hMetaFile:HENHMETAFILE
    LOCAL widthmm   :DWORD
    LOCAL heightmm  :DWORD
    LOCAL widthpixel:DWORD
    LOCAL heightpixel:DWORD
    LOCAL m_nZoomOld :DWORD
    LOCAL ViewOrgOld :POINT
    LOCAl HorizScrollPos :SDWORD
    LOCAl VertScrollPos :SDWORD
    LOCAL buff[32]:BYTE  ; these are two spare buffers
    LOCAL hFontPageNumber:HFONT
    LOCAL hFontOld:HFONT
    LOCAL lf    :LOGFONTA
    LOCAL xNP       :DWORD  ; высота печатноя области (для печати номера стр.)
    LOCAL yNP       :DWORD
    
    mov hDC, $invoke (BeginPaint,hWin,ADDR Ps)       
 
;    invoke GetClientRect, hWin, ADDR Rct
;    invoke SelectObject, hDC, $invoke ( GetStockObject, LTGRAY_BRUSH)   
;    invoke PatBlt, hDC, 0, 0, Rct.right, Rct.bottom, PATCOPY    

;    invoke GetClientRect, hWin, ADDR Rct
;    invoke FillRect, hDC, ADDR Ps.rcPaint, $invoke ( GetStockObject, LTGRAY_BRUSH) ; закрасим фон

    invoke SetWindowOrgEx, hDC, 0, 0, NULL
    mov HorizScrollPos, $invoke ( GetScrollPos, hWin, SB_HORZ )
    mov VertScrollPos, $invoke ( GetScrollPos, hWin, SB_VERT )
    neg HorizScrollPos
    neg VertScrollPos
    invoke SetViewportOrgEx, hDC, HorizScrollPos, VertScrollPos, ADDR ViewOrgOld
    
    invoke DrawPaper, hDC, ADDR Ps.rcPaint, 0, 0 
    
    invoke SetViewportOrgEx, hDC, ViewOrgOld.x, ViewOrgOld.y, NULL

; Вывод текста

    m2m m_nZoomOld, m_nZoom
    invoke SetZoom, 100
    
    mov tect.left, $invoke (px, m_Margin.left, 0)
    mov tect.top, $invoke (py, m_Margin.top, 0)
    
    mov eax, m_Paper.x
    sub eax, m_Margin.right
    mov tect.right, $invoke (px, eax, 0)
        
    mov eax, m_Paper.y
    sub eax, m_Margin.bottom
    mov tect.bottom, $invoke (py, eax, 0)
    
    invoke SetZoom, m_nZoomOld     
    
     
    m2m rect.left, 0
    m2m rect.top, 0
    
    mov eax, tect.right
    sub eax, tect.left
    mov rect.right, eax
    
    invoke IntDiv, eax, 2
    mov xNP, eax
    
    mov eax, tect.bottom
    sub eax, tect.top
    mov rect.bottom, eax
    
    mov yNP, eax                    ; запомном для печати номера страницы

; #####################################################################
; Преобразовать прямоугольник из логических координат
; в физические единицы 0,01 мм
; #####################################################################

    mov widthmm, $invoke ( GetDeviceCaps, hDC, HORZSIZE ) 
    mov heightmm, $invoke ( GetDeviceCaps, hDC, VERTSIZE )
    mov widthpixel, $invoke ( GetDeviceCaps, hDC, HORZRES ) 
    mov heightpixel, $invoke ( GetDeviceCaps, hDC, VERTRES )
    
    invoke LPtoDP, hDC, ADDR rect, 2    
    
    invoke IntMul, rect.right, widthmm
    invoke IntMul, eax, 100
    mov edx, eax 
    invoke IntDiv, widthpixel, 2        
    add eax, edx
    invoke IntDiv, eax, widthpixel
    mov rect.right, eax
    
    invoke IntMul, rect.bottom, heightmm
    invoke IntMul, eax, 100
    mov edx, eax 
    invoke IntDiv, heightpixel, 2        
    add eax, edx
    invoke IntDiv, eax, heightpixel
    mov rect.bottom, eax
    
    mov hMetaDC, $invoke ( CreateEnhMetaFile, hDC, NULL, ADDR rect, NULL )
    
    ; -----------------------------------------------------------------------   
    
    m2m fr.hdc, hMetaDC           ; устро - во представления 
    m2m fr.hdcTarget, hMetaDC     ; 0   
    
    .if pChar
    
        mov eax, pChar
        mov edx, nCurrentPage
        dec edx
        m2m fr.chrg.cpMin, [eax][edx*4]
        
        inc edx
        m2m fr.chrg.cpMax, [eax][edx*4]
            
    .else
        mov fr.chrg.cpMin, 0
        mov fr.chrg.cpMax, 0
    .endif
    
        mov fr.rc.left, 0
        mov fr.rc.top, 0
        
        mov eax, m_Paper.x
        sub eax, m_Margin.right
        sub eax, m_Margin.left             
        mov fr.rc.right, eax
        
        mov eax, m_Paper.y
        sub eax, m_Margin.top
        sub eax, m_Margin.bottom
        mov fr.rc.bottom, eax
 
    .if fPrnPageNumbers           ; Печатать номера страниц
    
        ; Создадим шрифт для вывода номеров страниц
       
        invoke MulDiv, 10, $invoke (GetDeviceCaps, hDC, LOGPIXELSY), 72
        neg eax     
        mov lf.lfHeight, eax
        mov lf.lfWidth, 0
        mov lf.lfEscapement, 0
        mov lf.lfOrientation,0
        mov lf.lfWeight, FW_NORMAL
        mov lf.lfItalic, FALSE
        mov lf.lfUnderline, FALSE
        mov lf.lfStrikeOut, FALSE
        mov lf.lfCharSet, ANSI_CHARSET
        mov lf.lfOutPrecision, OUT_DEFAULT_PRECIS
        mov lf.lfClipPrecision, CLIP_DEFAULT_PRECIS
        mov lf.lfQuality, DEFAULT_QUALITY
        mov lf.lfPitchAndFamily, FF_MODERN
        invoke lstrcpy, ADDR lf.lfFaceName, ADDR szNameFontNP
        mov hFontPageNumber, $invoke ( CreateFontIndirect, ADDR lf )

        mov hFontOld, $invoke ( SelectObject, hMetaDC, hFontPageNumber )        

        invoke wsprintf,ADDR buff,SADD("-- Page %i of %i --"), nCurrentPage, nCountPage
        invoke SetTextAlign, hMetaDC, TA_CENTER or TA_BOTTOM
        invoke SetTextColor, hMetaDC, Black
        invoke TextOut, hMetaDC, xNP, yNP, ADDR buff, $invoke (lnstr, ADDR buff)       
            
        invoke SelectObject, hMetaDC, hFontOld
        invoke DeleteObject, hFontPageNumber        ; удалим шрифт для печати номеров старниц
        
        invoke MulDiv, 200, $invoke (GetDeviceCaps, hDC, LOGPIXELSY), 72
        sub fr.rc.bottom, eax
        
    .endif    

    invoke CopyRect, ADDR fr.rcPage, ADDR fr.rc 
    
    ; Форматируем данные для контекста и печатаем их
    
    invoke SendMessage, hRichEdit, EM_FORMATRANGE, TRUE, ADDR fr
    
    ; Удаляем информацию, которая хранится в органе управления Rich Edit
    
    invoke SendMessage, hRichEdit, EM_FORMATRANGE, FALSE, NULL

    ; -----------------------------------------------------------------------
    
    mov hMetaFile, $invoke ( CloseEnhMetaFile, hMetaDC )

    invoke SetViewportOrgEx, hDC, HorizScrollPos, VertScrollPos, ADDR ViewOrgOld

    m2m Rct.left, PageRect.left
    m2m Rct.top, PageRect.top
    m2m Rct.right, PageRect.right
    m2m Rct.bottom, PageRect.bottom 
    invoke PlayEnhMetaFile, hDC, hMetaFile, ADDR Rct    
    invoke CopyEnhMetaFile, hMetaFile, ADDR NameFile
    invoke DeleteEnhMetaFile, hMetaFile

    invoke EndPaint,hWin,ADDR Ps

    ret

Paint_Proc_View endp

DrawPaper proc uses ebx hDC :HDC, rcPaint :DWORD, col :DWORD, row :DWORD 
    
    LOCAL RctPaperFrame :RECT
    LOCAL RctMinMargin  :RECT
    LOCAL RctMargin     :RECT
    
    LOCAL RctBkgndPage  :RECT
    
;   // paper frame
;   DrawFrame(hDC, px(0, col), py(0, row), px(m_Paper.cx, col), py(m_Paper.cy, row), 
;       RGB(0, 0, 0), RGB(0xE0, 0xE0, 0xE0), true);

            mov RctPaperFrame.left, $invoke (px, 0, col)
            mov RctPaperFrame.top, $invoke (py, 0, row)
            mov RctPaperFrame.right, $invoke (px, m_Paper.x, col)
            mov RctPaperFrame.bottom, $invoke (py, m_Paper.y, row)            

            ;ВОСТАНОВИМ ФОН В КОНТЕКСТЕ, можно и до вызова DrawPaperFrame

            ; нижняя часть, 
            
            m2m RctBkgndPage.left, RctPaperFrame.left
            dec RctBkgndPage.left
            m2m RctBkgndPage.top, RctPaperFrame.bottom
            add RctBkgndPage.top, 3
            m2m RctBkgndPage.right, RctPaperFrame.right
            add RctBkgndPage.right, 3
            m2m RctBkgndPage.bottom, RctPaperFrame.bottom
            add RctBkgndPage.bottom, MARGIN_Y         
            invoke FillRect, hDC, ADDR RctBkgndPage, COLOR_MENU+1

            ; верхняя часть

            .if !row
                m2m RctBkgndPage.left, RctPaperFrame.left
                dec RctBkgndPage.left
                m2m RctBkgndPage.top, RctPaperFrame.top
                sub RctBkgndPage.top, MARGIN_Y
;                add RctBkgndPage.top, 3
                m2m RctBkgndPage.right, RctPaperFrame.right
                add RctBkgndPage.right, 3
                m2m RctBkgndPage.bottom, RctPaperFrame.top
                dec RctBkgndPage.bottom                    
                invoke FillRect, hDC, ADDR RctBkgndPage, COLOR_MENU+1
            .endif

            ; левая часть

            .if !col
                m2m RctBkgndPage.left, RctPaperFrame.left
                sub RctBkgndPage.left, MARGIN_Y
                m2m RctBkgndPage.top, RctPaperFrame.top
                dec RctBkgndPage.top
                m2m RctBkgndPage.right, RctPaperFrame.left
                dec RctBkgndPage.right
                m2m RctBkgndPage.bottom, RctPaperFrame.bottom
                add RctBkgndPage.bottom, 3        
                invoke FillRect, hDC, ADDR RctBkgndPage, COLOR_MENU+1
            .endif            
            
            ; правая часть, 
            
            m2m RctBkgndPage.left, RctPaperFrame.right
            add RctBkgndPage.left, 3
            m2m RctBkgndPage.top, RctPaperFrame.top
            dec RctBkgndPage.top
            m2m RctBkgndPage.right, RctPaperFrame.right
            add RctBkgndPage.right, MARGIN_Y
            m2m RctBkgndPage.bottom, RctPaperFrame.bottom
            add RctBkgndPage.bottom, 3 
            invoke FillRect, hDC, ADDR RctBkgndPage, COLOR_MENU+1

           ; пара пикселов для тени, слева - внизу.
            
            invoke GetSysColor, COLOR_MENU
            mov ebx, eax
            
            push RctPaperFrame.left     ; можно и не сохранять
            push RctPaperFrame.bottom            
           
            add RctPaperFrame.bottom, 2
            dec RctPaperFrame.left
            invoke SetPixel, hDC, RctPaperFrame.left, RctPaperFrame.bottom, ebx
            dec RctPaperFrame.bottom
            invoke SetPixel, hDC, RctPaperFrame.left, RctPaperFrame.bottom, ebx
            inc RctPaperFrame.left
            invoke SetPixel, hDC, RctPaperFrame.left, RctPaperFrame.bottom, ebx
            inc RctPaperFrame.bottom
            invoke SetPixel, hDC, RctPaperFrame.left, RctPaperFrame.bottom, ebx           
            
            pop RctPaperFrame.bottom
            pop RctPaperFrame.left
;            
;           ; пара пикселов для тени, спарва - вверху.
;            
;            invoke GetSysColor, COLOR_MENU
;            mov ebx, eax
            
            push RctPaperFrame.right
            push RctPaperFrame.top
           
            inc RctPaperFrame.right
            invoke SetPixel, hDC, RctPaperFrame.right, RctPaperFrame.top, ebx
            inc RctPaperFrame.right
            invoke SetPixel, hDC, RctPaperFrame.right, RctPaperFrame.top, ebx
            dec RctPaperFrame.top
            invoke SetPixel, hDC, RctPaperFrame.right, RctPaperFrame.top, ebx
            dec RctPaperFrame.right
            invoke SetPixel, hDC, RctPaperFrame.right, RctPaperFrame.top, ebx

            pop RctPaperFrame.top
            pop RctPaperFrame.right
            
;   // minimum margin : printable margin
;   DrawFrame(hDC, px(m_MinMargin.left, col), py(m_MinMargin.top, row),
;                  px(m_Paper.cx - m_MinMargin.right, col),
;                  py(m_Paper.cy - m_MinMargin.bottom, row), 
;                  RGB(0xF0, 0xF0, 0xF0), RGB(0xF0, 0xF0, 0xF0), false);
            
            mov RctMinMargin.left, $invoke (px, m_MinMargin.left, col)
            mov RctMinMargin.top, $invoke (py, m_MinMargin.top, row)
            
            mov eax, m_Paper.x
            sub eax, m_MinMargin.right
            mov RctMinMargin.right, $invoke (px, eax, col)
            
            mov eax, m_Paper.y
            sub eax, m_MinMargin.bottom
            mov RctMinMargin.bottom, $invoke (py, eax, row)
            
;   // margin
;   DrawFrame(hDC, px(m_Margin.left, col), py(m_Margin.top, row),
;                  px(m_Paper.cx - m_Margin.right, col), py(m_Paper.cy - m_Margin.bottom, row), 
;                  RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF), false);

             mov RctMargin.left, $invoke (px, m_Margin.left, col)
             mov RctMargin.top, $invoke (py, m_Margin.top, row)
             
             mov eax, m_Paper.x
             sub eax, m_Margin.right
             mov RctMargin.right, $invoke (px, eax, col)
            
             mov eax, m_Paper.y
             sub eax, m_Margin.bottom
             mov RctMargin.bottom, $invoke (py, eax, row)           

; =@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@
            
             invoke DrawPaperFrame, hDC, ADDR RctPaperFrame, ADDR RctMinMargin, ADDR RctMargin
                
; =@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@=@
           
             m2m PageRect.left, RctMargin.left
             m2m PageRect.top, RctMargin.top
             m2m PageRect.right, RctMargin.right
             m2m PageRect.bottom, RctMargin.bottom
             
    ret
DrawPaper endp

;========================================================================

DrawPaperFrame proc hDC :HDC, lpPaperFrame :DWORD, lpMinMargin :DWORD, lpRctMargin :DWORD 

    LOCAL hRgnPaperFrame :HRGN
    LOCAL hRgnMinMargin  :HRGN
    LOCAL hRgnMargin     :HRGN
    
    LOCAL hRgnTemp       :HRGN
    
    LOCAL hBrushPaperFrame:HBRUSH
    LOCAL hBrushMinMargin:HBRUSH

    ; создадим кисти для прорисовки границ листа    
    RGB 224, 224, 224
    invoke CreateSolidBrush, eax
    mov hBrushPaperFrame, eax       ; paper frame
    
    RGB 240, 240, 240
    invoke CreateSolidBrush, eax
    mov hBrushMinMargin, eax        ; minimum margin : printable margin 
    
    ; увеличим на 1 пиксель.
    invoke InflateRect, lpPaperFrame, 1, 1  ; по аналогии с "original" - DrawPaperFrame 
    invoke InflateRect, lpMinMargin, 1, 1
    invoke InflateRect, lpRctMargin, 1, 1
    
    mov hRgnPaperFrame, $invoke ( CreateRectRgnIndirect, lpPaperFrame )
    mov hRgnMinMargin, $invoke ( CreateRectRgnIndirect, lpMinMargin )
    mov hRgnMargin, $invoke ( CreateRectRgnIndirect, lpRctMargin )
    mov hRgnTemp, $invoke ( CreateRectRgn, 0, 0, 1, 1)
    
    ; внутреняя часть, где отображаеться предпечатная информация
    invoke FillRgn, hDC, hRgnMargin, $invoke ( GetStockObject, WHITE_BRUSH)
    invoke CombineRgn, hRgnTemp, hRgnMinMargin, hRgnMargin, RGN_DIFF
    ; поля
    invoke FillRgn, hDC, hRgnTemp, hBrushMinMargin
    invoke CombineRgn, hRgnTemp, hRgnPaperFrame, hRgnMinMargin, RGN_DIFF
    ; непечатемая область
    invoke FillRgn, hDC, hRgnTemp, hBrushPaperFrame
    
    ; чёрный бордюр вокруг непечатаемой области (т.е. листа).
    invoke FrameRect, hDC, lpPaperFrame, $invoke ( GetStockObject, BLACK_BRUSH)
    
    ; востановим, уменьшим на 1 пиксель.
    invoke InflateRect, lpPaperFrame, -1, -1 ; по аналогии с "original" - DrawPaperFrame
    invoke InflateRect, lpMinMargin, -1, -1
    invoke InflateRect, lpRctMargin, -1, -1
    
    invoke DrawPaperShade, hDC, lpPaperFrame    ; нарисуем тень от страницы
    
    ; Осводбодим ресурсы GDI
    invoke DeleteObject, hBrushPaperFrame
    invoke DeleteObject, hBrushMinMargin
    
    invoke DeleteObject, hRgnTemp
    invoke DeleteObject, hRgnPaperFrame
    invoke DeleteObject, hRgnMinMargin
    invoke DeleteObject, hRgnMargin
    
    ret
DrawPaperFrame endp

DrawPaperShade proc uses edx ebx  hDC :HDC, lpPaperFrame :DWORD

    LOCAL rectr :RECT
    LOCAL rectb :RECT
    LOCAL hBlackBrush :HBRUSH

            invoke GetStockObject, BLACK_BRUSH
            mov hBlackBrush, eax

assume ebx: ptr RECT
mov ebx, lpPaperFrame 

        ; RECT rectr = { x1+1, y0+1, x1+3, y1+3 } Правая часть тени от листа
            mov edx, [ebx].right
            inc edx
            mov rectr.left, edx
            mov eax, [ebx].top
            inc eax            
            mov rectr.top, eax            
            add edx, 2
            mov rectr.right, edx
            mov eax, [ebx].bottom
            add eax, 3
            mov rectr.bottom, eax
            
            invoke FillRect, hDC, ADDR rectr, hBlackBrush
 
        ; RECT rectb = { x0+1, y1+1, x1+3, y1+3 } Нижняя часть тени от листа
            mov eax, [ebx].left
            inc eax
            mov rectb.left, eax
            mov edx, [ebx].bottom
            inc edx
            mov rectb.top, edx
            mov eax, [ebx].right
            add eax, 3
            mov rectb.right, eax
            add edx, 2
            mov rectb.bottom, edx                        
        
            invoke FillRect, hDC, ADDR rectb, hBlackBrush

assume ebx: nothing

    ret
DrawPaperShade endp 

;========================================================================

;DrawPaperFrame proc hDC :HDC, x0 :DWORD, y0 :DWORD, x1 :DWORD, y1 :DWORD, 
;               crPen :COLORREF, crBrush :COLORREF, frame :BYTE
;               
;    LOCAL hPen  :HPEN               
;    LOCAL hBrush:HBRUSH
;    LOCAL hOldPen:HPEN  
;    LOCAL hOldBrush :HBRUSH
;    LOCAL rectr :RECT
;    LOCAL rectb :RECT
;    LOCAL var   :DWORD                
;
;        mov hPen, $invoke (CreatePen,PS_SOLID, 0, crPen)             
;        mov hBrush, $invoke (CreateSolidBrush, crBrush)               
;        mov hOldPen, $invoke (SelectObject, hDC, hPen)
;        mov hOldBrush, $invoke (SelectObject, hDC, hBrush)
;        
;        mov eax, x0
;        dec eax
;        m2m var, y0
;        dec var        
;        mov edx, x1
;        inc edx
;        mov ecx, y1
;        inc ecx        
;        invoke Rectangle, hDC, eax, var, edx, ecx
;        
;        invoke SelectObject, hDC, hOldPen
;        invoke DeleteObject, hPen        
;        invoke SelectObject, hDC, hOldBrush
;        invoke DeleteObject, hBrush
;        


;        .if frame
;        
;        ; RECT rectr = { x1+1, y0+1, x1+3, y1+3 } Правая часть тени от листа
;            mov edx, x1
;            inc edx
;            mov rectr.left, edx
;            mov eax, y0
;            inc eax            
;            mov rectr.top, eax            
;            add edx, 2
;            mov rectr.right, edx
;            mov eax, y1            
;            add eax, 3
;            mov rectr.bottom, eax
;            
;            invoke FillRect, hDC, ADDR rectr, $invoke (GetStockObject, BLACK_BRUSH)
; 
;        ; RECT rectb = { x0+1, y1+1, x1+3, y1+3 } Нижняя часть тени от листа
;            mov eax, x0
;            inc eax
;            mov rectb.left, eax
;            mov edx, y1
;            inc edx
;            mov rectb.top, edx
;            mov eax, x1
;            add eax, 3
;            mov rectb.right, eax
;            add edx, 2
;            mov rectb.bottom, edx                        
;        
;            invoke FillRect, hDC, ADDR rectb, $invoke (GetStockObject, BLACK_BRUSH)
;    
;        .endif                
;
;    ret
;DrawPaperFrame endp

;========================================================================
; from base_dpi to screen
; -----------------------------------------------------------------------
; ( x + m_Paper.cx * col ) * m_nScale / BASE_DPI + MARGIN_X * (col+1)
;========================================================================

px proc x :DWORD, col :DWORD            
 
            invoke IntMul, m_Paper.x, col
            add eax, x
            invoke IntMul, eax, m_nScale
            invoke IntDiv, eax, BASE_DPI
            mov edx, eax

            mov eax, col
            inc eax
            invoke IntMul, eax, MARGIN_X
            
            add eax, edx 

    ret
px endp

;========================================================================
; from base_dpi to screen
; -----------------------------------------------------------------------
; ( y + m_Paper.cy * row ) * m_nScale / BASE_DPI + MARGIN_Y * (row+1)
;========================================================================

py proc y :DWORD, row :DWORD            
            
            invoke IntMul, m_Paper.y, row
            add eax, y
            invoke IntMul, eax, m_nScale
            invoke IntDiv, eax, BASE_DPI
            mov edx, eax
            
            mov eax, row
            inc eax
            invoke IntMul, eax, MARGIN_Y

            add eax, edx
            
    ret
py endp

;========================================================================
; from screen  to base_dpi
;========================================================================

screen_to_dpi proc xy :DWORD
 
            invoke IntMul, xy, BASE_DPI
            invoke IntDiv, eax, m_nScale

    ret
screen_to_dpi endp

;========================================================================
; convert from 1/1000 inch to BASE_DPI
;========================================================================

SetPaper proc

    LOCAL paper :POINT 
    LOCAL margin :RECT  
    LOCAL minmargin :RECT
    
    m2m paper.x, m_psd.ptPaperSize.x    
    m2m paper.y, m_psd.ptPaperSize.y       

    invoke CopyRect,ADDR margin,ADDR m_psd.rtMargin
    invoke CopyRect,ADDR minmargin,ADDR m_psd.rtMinMargin

    mov eax, paper.x
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000
    mov m_Paper.x, eax
    
    mov eax, paper.y
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_Paper.y, eax
    
    mov eax, margin.left
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_Margin.left, eax
 
    mov eax, margin.right
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_Margin.right, eax
    
    mov eax, margin.top
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_Margin.top, eax
 
    mov eax, margin.bottom
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_Margin.bottom, eax
    
    mov eax, minmargin.left
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_MinMargin.left, eax
 
    mov eax, minmargin.right
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_MinMargin.right, eax
    
    mov eax, minmargin.top
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_MinMargin.top, eax
 
    mov eax, minmargin.bottom
    invoke IntMul, eax, BASE_DPI   
    invoke IntDiv, eax, 1000    
    mov m_MinMargin.bottom, eax        

    ret
SetPaper endp

SetDefaultPSD proc

    LOCAL dpix :DWORD
    LOCAL dpiy :DWORD
    LOCAL hDC  :HDC
    LOCAL var  :DWORD 

        mov m_pd.lStructSize, sizeof m_pd
        m2m m_pd.hInstance, hInst
        mov m_pd.Flags, PD_RETURNDEFAULT
        invoke PrintDlg, offset m_pd

        call CreatePrinterDC
        mov hDC, eax

        mov m_psd.rtMargin.left, 1000       ; 1.00 дюйма
        mov m_psd.rtMargin.right, 1000      ; 1.00 дюйма
        mov m_psd.rtMargin.top, 750         ; 0.75 дюйм
        mov m_psd.rtMargin.bottom, 750      ; 0.75 дюйм
    
        mov dpix, $invoke (GetDeviceCaps, hDC, LOGPIXELSX)
        mov dpiy, $invoke (GetDeviceCaps, hDC, LOGPIXELSY)
        
        invoke GetDeviceCaps, hDC, PHYSICALWIDTH
        invoke IntMul, eax, 1000
        invoke IntDiv, eax, dpix   
        mov m_psd.ptPaperSize.x, eax
        
        invoke GetDeviceCaps, hDC, PHYSICALHEIGHT
        invoke IntMul, eax, 1000       
        invoke IntDiv, eax, dpiy   
        mov m_psd.ptPaperSize.y, eax
        
        invoke GetDeviceCaps, hDC, PHYSICALOFFSETX
        invoke IntMul, eax, 1000
        invoke IntDiv, eax, dpix   
        mov m_psd.rtMinMargin.left, eax
    
        invoke GetDeviceCaps, hDC, PHYSICALOFFSETY
        invoke IntMul, eax, 1000
        invoke IntDiv, eax, dpiy   
        mov m_psd.rtMinMargin.top, eax
        
        invoke GetDeviceCaps, hDC, HORZRES
        invoke IntMul, eax, 1000
        invoke IntDiv, eax, dpix    

        m2m var, m_psd.ptPaperSize.x    ; m_psd.rtMinMargin.right = ebx - edx - eax
        mov edx, m_psd.rtMinMargin.left
        
        sub var, edx
        sub var, eax
        m2m m_psd.rtMinMargin.right, var
        
        
        invoke GetDeviceCaps, hDC, VERTRES
        invoke IntMul, eax, 1000
        invoke IntDiv, eax, dpiy    

        m2m var, m_psd.ptPaperSize.y    ; m_psd.rtMinMargin.bottom = ebx - edx - eax
        mov edx, m_psd.rtMinMargin.top
        
        sub var, edx
        sub var, eax   
        
        m2m m_psd.rtMinMargin.bottom, var
        
        invoke DeleteDC, hDC                 

    ret
SetDefaultPSD endp

CreatePrinterDC proc

        mov pDevMode, $invoke (GlobalLock, m_pd.hDevMode)
        mov pDevNames, $invoke (GlobalLock, m_pd.hDevNames)

        mov edx, pDevMode
        invoke CreateDC, NULL,ADDR [edx].DEVMODE.dmDeviceName, NULL, pDevMode        

    ret
CreatePrinterDC endp

SetZoom proc zoom :DWORD

LOCAL hDC :HDC

    mov eax, zoom

        .if eax == m_nZoom 
          return FALSE
        .else
        
          m2m m_nZoom, zoom
          
          mov hDC, $invoke (GetDC, NULL)
          
          invoke GetDeviceCaps, eax, LOGPIXELSX      
          invoke IntMul, eax, zoom 
          invoke IntDiv, eax, 100
          mov m_nScale, eax
          invoke ReleaseDC, NULL, hDC
          
        .endif

    ret
SetZoom endp

SetSize proc

    LOCAL rect  :RECT
    LOCAL lpSi  :SCROLLINFO
    LOCAL tmp   :BYTE

@@:
    
        invoke GetClientRect,hWndView,ADDR rect

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

;--------------------------------
        mov eax, m_nSurfaceWidth
        sub eax, rect.right
        mov nHorizDiff, eax
;--------------------------------
        mov eax, m_nSurfaceWidth
    .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, hWndView, SB_HORZ, ADDR lpSi, TRUE

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



        invoke GetClientRect,hWndView,ADDR rect ; об - но по новому замеряем размер 
                                                ; кл - ой области, потому что линейка 
                                                ; прокрутки (ГОРИЗОНТАЛЬНАЯ)
                                                ; могла исчеснуть или появиться.
        ; ВЕРТИКАЛЬНАЯ ЛИНЕЙКА ПРОКРУТКИ
    
;--------------------------------
        mov eax, m_nSurfaceHeight
        sub eax, rect.bottom
        mov nVertDiff, eax
;--------------------------------
            mov eax, m_nSurfaceHeight
        .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, hWndView, SB_VERT, ADDR lpSi, TRUE
        
        .else
        
            invoke SetScrollRange, hWndView, SB_VERT, 0, 0, TRUE        ;не отображать линейки прокрутки
            
        .endif
        
        cmp tmp, 1
        je e_t        
        mov tmp, 1
        jmp @b
        
e_t:

    ret
SetSize endp

CalculateSize  proc

           invoke px, m_Paper.x, 0
           add eax, MARGIN_X
           mov m_nSurfaceWidth, eax
           
           invoke px, m_Paper.y, 0
           add eax, MARGIN_Y
           mov m_nSurfaceHeight, eax

    ret
CalculateSize endp

; #####################################################################
; Процедура настраивает ToolBar 
; #####################################################################

DoToolBar proc
    LOCAL   tbab:TBADDBITMAP
    LOCAL   hTBar:HWND
    LOCAL   m_hToolTip:HWND
    LOCAL   ti:TOOLINFO
    LOCAL   tbi:TBBUTTONINFO
    
    mov hTBar, $invoke ( GetDlgItem, hWnd, IDC_TBR1 ) ; Get Handle toolbar    
    invoke SendMessage,hTBar,TB_BUTTONSTRUCTSIZE,sizeof TBBUTTON,0 ; Set toolbar struct size    
    m2m tbab.hInst, hInst
    mov tbab.nID, IDB_BITMAP1
    invoke SendMessage,hTBar,TB_ADDBITMAP,13,addr tbab  ;Set toolbar bitmap 
    invoke SendMessage,hTBar,TB_ADDBUTTONS,ntbrbtns,addr tbrbtns ;Set toolbar buttons

    ; Создадим подсказки 

    invoke CreateWindowEx, 0, ADDR ToolClass, NULL, TTS_ALWAYSTIP,
                        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                        hWnd, NULL,hInst, NULL  
    mov m_hToolTip, eax
    
    mov ti.cbSize, sizeof(TOOLINFO)
    m2m ti.hWnd, hTBar
    m2m ti.hInst, hInst 
    m2m ti.uFlags, TTF_SUBCLASS
    
    invoke SendMessage, hTBar, TB_GETITEMRECT, 0, ADDR ti.rect
    mov ti.uId, IDM_PRINT
    mov ti.lpszText, offset szPrint
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_PAGE_SETUP
    mov ti.lpszText, offset szPrintSetup
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti    
   
    mov ti.uId, IDM_SELECTONLY
    mov ti.lpszText, offset szSelectOnly
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_FIRSTPAGE
    mov ti.lpszText, offset szFirstPage
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_PREVIOUSPAGE
    mov ti.lpszText, offset szPreviousPage
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_TOPAGE
    mov ti.lpszText, offset szToPage
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_NEXTPAGE
    mov ti.lpszText, offset szNextPage
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_LASTPAGE
    mov ti.lpszText, offset szLastPage 
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDT_ZOOM_IN
    mov ti.lpszText, offset szZoomIn
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDT_ZOOM_OUT
    mov ti.lpszText, offset szZoomOut
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_EXIT
    mov ti.lpszText, offset szClose
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti
    
    mov ti.uId, IDM_HELP_ABOUT
    mov ti.lpszText, offset szAbout
    invoke SendMessage, m_hToolTip, TTM_ADDTOOL, 0, ADDR ti    
   
    ; set the tooltip control as part of the toolbar
    
    invoke SendMessage,hTBar, TB_SETTOOLTIPS, m_hToolTip, 0 
    
    ; добавим текстовые строки

    mov tbi.cbSize, sizeof (TBBUTTONINFO)    
    mov tbi.dwMask, TBIF_TEXT or TBIF_SIZE or TBIF_STYLE
    mov tbi.pszText, offset szPrint
    mov tbi.cchText, $invoke (lnstr, ADDR szPrint)
    mov tbi.idCommand, IDM_PRINT
    mov tbi.fsStyle, BTNS_SHOWTEXT or BTNS_AUTOSIZE or BTNS_BUTTON 

    invoke SendMessage, hTBar, TB_SETBUTTONINFO, IDM_PRINT, ADDR tbi        ; ПЕЧАТЬ
    
    mov tbi.pszText, offset szClose
    mov tbi.cchText, $invoke (lnstr, ADDR szClose)
    mov tbi.idCommand, IDM_EXIT
    
    invoke SendMessage, hTBar, TB_SETBUTTONINFO, IDM_EXIT, ADDR tbi        ; ЗАКРЫТЬ
    
    mov tbi.pszText, offset szSelectOnly
    mov tbi.cchText, $invoke (lnstr, ADDR szSelectOnly)
    mov tbi.idCommand, IDM_EXIT
    mov tbi.fsStyle, BTNS_SHOWTEXT or BTNS_AUTOSIZE or BTNS_BUTTON or BTNS_CHECK
    
    invoke SendMessage, hTBar, TB_SETBUTTONINFO, IDM_SELECTONLY, ADDR tbi  ; ТОЛЬКО ВЫДЕЛЕННОЕ
    
    ; Установим для ToolBara расширенный стиль, что бы только кнопки с текстом были со стилнм LIST 
        
    invoke SendMessage, hTBar, TB_SETEXTENDEDSTYLE, NULL, TBSTYLE_EX_MIXEDBUTTONS
    
    mov eax,hTBar   
    ret

DoToolBar endp

; #####################################################################
; Процедура окна "Перейти к странице" 
; #####################################################################

DlgProc_ToPage proc hDlg :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD

    LOCAL bTranslated   :BYTE
    LOCAL buff[32]:BYTE

    WM_CASE uMsg, <INITDIALOG,CLOSE,COMMAND>
     
@@end:    
    xor eax, eax 
    ret     
     
@@INITDIALOG:

        invoke GetDlgItem,hDlg, IDC_ED_TOPAGE
        invoke SetFocus, eax

        return TRUE

@@COMMAND:

        .if wParam == IDOK

               invoke GetDlgItemInt, hDlg, IDC_ED_TOPAGE,ADDR bTranslated, FALSE
               .if bTranslated
                    .if (eax <= nCountPage) && (eax != nCurrentPage) && eax
                        
                        ; Меню и тоолбар приведём в соответствие с текущей страницей
                        .if eax != 1 && eax != nCountPage   ; открыть все
                        
                            mov nCurrentPage, eax
                        
                            invoke EnableMenuItem, hMenu, IDM_FIRSTPAGE, MF_ENABLED or MF_BYCOMMAND
                            invoke EnableMenuItem, hMenu, IDM_PREVIOUSPAGE, MF_ENABLED or MF_BYCOMMAND                         
                            invoke EnableMenuItem, hMenu, IDM_NEXTPAGE, MF_ENABLED or MF_BYCOMMAND
                            invoke EnableMenuItem, hMenu, IDM_LASTPAGE, MF_ENABLED or MF_BYCOMMAND
                            
                            ; ToolBar button State
                            
                            invoke SendMessage, hToolBar, TB_SETSTATE, IDM_FIRSTPAGE, TBSTATE_ENABLED   
                            invoke SendMessage, hToolBar, TB_SETSTATE, IDM_PREVIOUSPAGE, TBSTATE_ENABLED
                            invoke SendMessage, hToolBar, TB_SETSTATE, IDM_NEXTPAGE, TBSTATE_ENABLED
                            invoke SendMessage, hToolBar, TB_SETSTATE, IDM_LASTPAGE, TBSTATE_ENABLED
                        
                            ; Изменим номер страницы в строке состояния                        
                            mov buff, 0
                            invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff           
                            invoke wsprintf,ADDR buff,SADD(" Page %i/%i"), nCurrentPage, nCountPage
                            invoke SendMessage, hStatus, SB_SETTEXT, 0, ADDR buff
                            
                            invoke InvalidateRect, hWndView, NULL, FALSE  ; Отобразим новую стрницу
                        
                        .elseif eax == 1        ; первая старница
                        
                            xor eax, eax
                            mov ax, IDM_FIRSTPAGE
                            invoke SendMessage, hWnd, WM_COMMAND, eax, 0                            
                            
                        .elseif eax == nCountPage   ; последняя старница
                        
                            xor eax, eax
                            mov ax, IDM_LASTPAGE
                            invoke SendMessage, hWnd, WM_COMMAND, eax, 0
                        
                        .endif
                                           
                    .elseif eax == nCurrentPage     ; это текущая стртаница
                    
                        invoke MessageBox, hDlg, ADDR szErrTickNumber, ADDR szError, MB_OK
                        jmp @@end 
                    
                    .else                           ; недопустимая страница
                    
                        invoke MessageBox, hDlg, ADDR szErrPageNumber, ADDR szError, MB_OK
                        jmp @@end                    
                    .endif
               
               .endif

               jmp  @@CLOSE
        .elseif  wParam == IDCANCEL
               jmp  @@CLOSE
        .endif


        jmp @@end        

@@CLOSE:

        invoke EndDialog, hDlg, 0
        jmp @@end
    
DlgProc_ToPage endp

; #####################################################################
; Процедура настраивает МЕНЮ 
; #####################################################################

SetMenuXX proc

    LOCAL hBmp        :HBITMAP
    LOCAL imageinfo   :IMAGEINFO
    LOCAL IdmStyle    :DWORD

; создадим список изображений
        mov hImageList, $invoke (ImageList_Create, 16, 16, ILC_COLOR or ILC_MASK, 14,0)
        mov hBmp, $invoke (LoadBitmap, hInst, IDB_BITMAP1)
        RGB 192, 192, 192
        invoke ImageList_AddMasked, hImageList, hBmp, eax
        invoke DeleteObject, hBmp

; установим иконки на пункты меню
        invoke SetMenuIcon, hMenu, hImageList, nmnubtns, ADDR mnubtns

; можно и так...
;invoke SetMenuItemIcon, hMenu, IDM_PAGE_SETUP, $invoke (ImageList_GetIcon, hImageList, 1, ILD_NORMAL)      
;invoke SetMenuItemIcon, hMenu, IDM_PRINT, $invoke (ImageList_GetIcon, hImageList, 0, ILD_NORMAL)       
;invoke SetMenuItemIcon, hMenu, IDM_EXIT, $invoke (ImageList_GetIcon, hImageList, 8, ILD_NORMAL)        
;invoke SetMenuItemIcon, hMenu, IDM_FIRSTPAGE, $invoke (ImageList_GetIcon, hImageList, 2, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_PREVIOUSPAGE, $invoke (ImageList_GetIcon, hImageList, 3, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_NEXTPAGE, $invoke (ImageList_GetIcon, hImageList, 4, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_LASTPAGE, $invoke (ImageList_GetIcon, hImageList, 5, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_ZOOM, $invoke (ImageList_GetIcon, hImageList, 10, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_SELECTONLY, $invoke (ImageList_GetIcon, hImageList, 9, ILD_NORMAL)
;invoke SetMenuItemIcon, hMenu, IDM_HELP_ABOUT, $invoke (ImageList_GetIcon, hImageList, 11, ILD_NORMAL)
        
        invoke RedrawMenu, hWnd, hMenu, STYLE_OFFICE_2000           ; инициализируем меню
        
        ; установим переключатель номера страниц
        
        .if fPrnPageNumbers
            invoke CheckMenuItem, hMenu, IDM_PAGE_NUMBER, MF_BYCOMMAND or MF_CHECKED                
        .endif
        
    ret
SetMenuXX endp

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

DrawBorderProc proc uses ebx h_Menu:HMENU, hDC:HDC, lpRect:DWORD

    LOCAL hFont :HFONT
    LOCAL Rct   :RECT
    LOCAL lenStr:DWORD
    LOCAL nX    :DWORD
    LOCAL nY    :DWORD

    invoke IsMenu, h_Menu
    .if !eax
        return 0
    .endif
    
    .if hDC && lpRect        
        
        call NumColorBits
        .if eax > 8
            mov ebx, $invoke ( GetSysColor, COLOR_MENU )
            invoke LightenColor, 128, ebx
            invoke Gradient_Fill, hDC, lpRect, eax, ebx, RECT_H
        .else
            invoke FillRect, hDC, lpRect, COLOR_MENU+1
        .endif
        
        invoke GetSysColor, COLOR_GRAYTEXT
        invoke SetTextColor, hDC, eax
        
        mov hFont, $invoke ( CreateFontIndirect, ADDR lf_mnu_brd )
        invoke SelectObject, hDC, hFont
        mov hFont, eax
        
        invoke szLen, ADDR XXRichEdit
        mov lenStr, eax
        
        mov edx, lpRect
        m2m nX, [edx].RECT.left
        m2m nY, [edx].RECT.bottom
        
        sub nY, 3
        
        invoke TextOut, hDC, nX, nY, ADDR XXRichEdit, lenStr
        
        invoke SelectObject, hDC, hFont
        invoke DeleteObject, eax

        mov eax, FALSE
    
    .else

        mov eax, 6
    
    .endif
    

	ret
DrawBorderProc endp

PUBLIC CreatePageSetup

; #####################################################################
; Функция вызывает диалог настройки параметров страницы. Функция 
; экспортируеться в основную программу, что бы параметры старницы
; хранились в единной структуре m_psd.
; #####################################################################
CreatePageSetup  proc hWin :HWND

             m2m m_psd.lStructSize, sizeof (PAGESETUPDLG)
             m2m m_psd.hwndOwner, hWin
             m2m m_psd.hDevMode, m_pd.hDevMode          ; это нужно, 0 - нельзя.
             m2m m_psd.hDevNames, 0;m_pd.hDevNames
             mov m_psd.Flags, PSD_MARGINS or PSD_MINMARGINS or PSD_INTHOUSANDTHSOFINCHES            
             invoke PageSetupDlg, ADDR m_psd

    ret
CreatePageSetup endp

comment $
 #####################################################################
 
 Процедура печати для RichEdit контрола.
 
 ПАРАМЕТРЫ:
           hWin - хендл окна владельца диалога печати (отн - но него
           это окно будет модальным).
           hREdit - хендл RichEdit контрола содержимое которого будет
           выведено на печать.
 
 Если в данный момент в RichEdit выделен текст, то 
 
 #####################################################################$

PUBLIC PrintREdit

PrintREdit proc hWin :HWND, hREdit :HWND

LOCAL fr        :FORMATRANGE
LOCAL docinfo   :DOCINFO
LOCAL pd        :PRINTDLG
LOCAL hPrintDC  :HDC

LOCAL charrange :CHARRANGE

LOCAL flag_print:DWORD
LOCAL fSelOnlyOld:BYTE

LOCAL szErr[128]:BYTE

LOCAL buff[32]:BYTE  ; these are two spare buffers
LOCAL hFontPageNumber:HFONT
LOCAL hFontOld  :HFONT
LOCAL lf        :LOGFONTA
LOCAL OldClr    :COLORREF
LOCAL OldAlign  :DWORD
LOCAL nPageMax  :DWORD
LOCAL nPageMin  :DWORD
LOCAL scale     :DWORD
LOCAL xNP       :DWORD  ; высота печатноя области (для печати номера стр.)
LOCAL yNP       :DWORD

; Инициализируем поля структуры PRINTDLG

        mov pd.lStructSize, sizeof (PRINTDLG)
        m2m pd.hwndOwner, hWin
        m2m pd.hDevMode, 0 ;m_pd.hDevMode ;- можно и так но в Win9x глючит (Err: недостаточно памяти) 
        m2m pd.hDevNames, 0;m_pd.hDevNames

        invoke SendMessage, hREdit, EM_EXGETSEL, NULL, ADDR charrange ; есть выделенный текст ?
        mov eax, charrange.cpMin

; если charrange.cpMax == charrange.cpMin тогда выделенного фрагмента нет. 
; такое равенство получаеться если при первом появлении окна превью был 
; выделен фрагмент текста, а потом выделение текста снято, и превью было 
; вызвано снова.
    
        .if ( !charrange.cpMax )  || ( charrange.cpMax == eax )       ; нет выделенного фрагмента текста
                mov flag_print, PD_ALLPAGES or PD_NOSELECTION
        .else                         ; есть выделенный фрагмент текста            
            .if fSelOnly == TRUE      ; пункт толко выделенное отмечен      
                mov flag_print, PD_SELECTION
            .else                     ; отображать весь документ
                mov flag_print, PD_ALLPAGES
            .endif
        .endif
                                                      ; PD_NONETWORKBUTTON
;                                                         |
        mov eax, flag_print
        or  eax, PD_RETURNDC or PD_HIDEPRINTTOFILE or 20000h
        mov pd.Flags,eax
        
        m2m pd.hInstance, hInst

; Диапазон печатаемых страниц устаноавливаем для всего документа

        mov al, fSelOnly
        mov fSelOnlyOld, al
        
        mov fSelOnly, FALSE
        invoke Get_Doc_Page_Info, hREdit
        mov al, fSelOnlyOld
        mov fSelOnly, al
                    
        mov pd.nFromPage, 1 ; минимально допустивое число страниц которое может быть введённо пользователем
        m2m pd.nToPage, nCountPage  ; максимально допустивое число страниц которое может быть введённо пользователем
        
        mov pd.nMinPage, 1  ; минимально введённое число страниц (по умолчанию)
        m2m pd.nMaxPage, nCountPage ; максимально введённое число страниц (по умолчанию)
        mov pd.nCopies, 1

        invoke Get_Doc_Page_Info, hREdit 
     
        invoke PrintDlg, ADDR pd
        .if eax                    ; ОК

            m2m hPrintDC, pd.hDC
            
            ; Будем печатать с использованием контекста 
            ; принтера, полученного от функции PrintDlg
            
            m2m fr.hdc, hPrintDC
            m2m fr.hdcTarget, 0

            mov eax, pd.Flags
            and eax, PD_ALLPAGES or PD_SELECTION or PD_PAGENUMS            

            .if eax == PD_ALLPAGES              ; Печатаем весь документ

                mov nPageMin, 0
                m2m nPageMax, nCountPage
                
            .elseif eax == PD_SELECTION
            
                mov nPageMin, 0
                m2m nPageMax, nCountPage
                
            .elseif eax == PD_PAGENUMS          

                m2m nPageMin, pd.nFromPage
                dec nPageMin              ; так как считаем страницы включительно по...
                                          ; (напр. с 1 -3, 3-1=2, а печатем 3 стр., т.е. 1,2,3)
                m2m nPageMax, pd.nToPage
                
            .endif

; Устанавливаем размеры страницы в TWIPS-ах
             mov eax, m_Margin.left
             mov fr.rc.left,eax
             mov eax, m_Margin.top
             mov fr.rc.top, eax
             mov eax, m_Paper.x
             sub eax, m_Margin.right
             mov fr.rc.right, eax       
             mov eax, m_Paper.y
             sub eax, m_Margin.bottom
             mov fr.rc.bottom, eax
             
            .if fPrnPageNumbers           ; Печатать номера страниц (Оставляем поля)
                
                mov scale, $invoke (GetDeviceCaps, hPrintDC, LOGPIXELSY)                
                
                ;invoke MulDiv, 10, scale, 72
                mov eax, m_Margin.top ; должно быть add не забыть исправить
                mov fr.rc.top, eax
                
                ; создадим шрифт для вывода страниц
                
                invoke MulDiv, 10, scale, 72
                neg eax     
                mov lf.lfHeight, eax
                mov lf.lfWidth, 0
                mov lf.lfEscapement, 0
                mov lf.lfOrientation,0
                mov lf.lfWeight, FW_NORMAL
                mov lf.lfItalic, FALSE
                mov lf.lfUnderline, FALSE
                mov lf.lfStrikeOut, FALSE
                mov lf.lfCharSet, ANSI_CHARSET
                mov lf.lfOutPrecision, OUT_DEFAULT_PRECIS
                mov lf.lfClipPrecision, CLIP_DEFAULT_PRECIS
                mov lf.lfQuality, DEFAULT_QUALITY
                mov lf.lfPitchAndFamily, FF_MODERN
                invoke lstrcpy, ADDR lf.lfFaceName, ADDR szNameFontNP
                mov hFontPageNumber, $invoke ( CreateFontIndirect, ADDR lf )
                
                mov yNP, $invoke ( GetDeviceCaps, hPrintDC, VERTRES )
                invoke GetDeviceCaps, hPrintDC, HORZRES
                invoke IntDiv, eax, 2
                mov xNP, eax
                
;               invoke MulDiv, 200, scale, 72
;               sub fr.rc.bottom, eax
                           
            .endif
            
            mov fr.rcPage.left,0
            mov fr.rcPage.top,0
            m2m fr.rcPage.right, m_Paper.x
            m2m fr.rcPage.bottom, m_Paper.y  

            ; Заполняем поля структуры DOCINFO

            mov docinfo.cbSize, sizeof(DOCINFO)
            mov docinfo.lpszDocName, offset DocName
            mov docinfo.lpszOutput, NULL
            mov docinfo.lpszDatatype, NULL
            mov docinfo.fwType, NULL             
 
            ; Начинаем печать документа
                            
            ; Если произошла ошибка, получаем и выводим на экран код ошибки            
            
            invoke StartDoc, hPrintDC, ADDR docinfo
            
            .if !eax
                
                call GetLastError
                invoke wsprintf, szErr, SADD("Print Error %ld"), eax
                invoke MessageBox, hWin, szErr, offset DocName, MB_OK or MB_ICONEXCLAMATION
                ret
            .endif                
            
            .if nPageMax   
@@:       
                inc nPageMin         
                invoke StartPage, hPrintDC    ; Начинаем новую страницу            
                
                .if fPrnPageNumbers           ; Печатать номера страниц (Оставляем поля)
                
                    mov hFontOld, $invoke ( SelectObject, hPrintDC, hFontPageNumber )                                       
                    invoke wsprintf,ADDR buff,SADD("-- Page %i of %i --"), nPageMin, nCountPage
                    
                    mov OldAlign, $invoke ( SetTextAlign, hPrintDC, TA_CENTER or TA_BOTTOM )
                    mov OldClr, $invoke ( SetTextColor, hPrintDC, Black )                   
                                    
                    invoke TextOut, hPrintDC, xNP, yNP, ADDR buff, $invoke (lnstr, ADDR buff)
                    invoke SetTextColor, hPrintDC, OldClr
                    invoke SelectObject, hPrintDC, hFontOld
                    invoke SetTextAlign, hPrintDC, OldAlign                 
                                                    
                .endif
                    
                .if pChar
                
                    mov eax, pChar
                    mov edx,nPageMin
                    dec edx
                    m2m fr.chrg.cpMin, [eax][edx*4]
 
                    inc edx
                    m2m fr.chrg.cpMax, [eax][edx*4]
                        
                .else

                    mov fr.chrg.cpMin, 0
                    mov fr.chrg.cpMax, 0
                .endif
    
                invoke SendMessage, hREdit, EM_FORMATRANGE, TRUE, ADDR fr               
                
                invoke EndPage,hPrintDC     ; Завершаем печать очередной страницы               
                
                mov eax, nPageMax
                cmp nPageMin, eax
                jb  @b              
                
            .endif            



; ############################################################################### 
                   
;           .if fPrnPageNumbers           ; Печатать номера страниц (Оставляем поля)
;               invoke DeleteObject, hFontPageNumber        ; удалим шрифт для печати номеров старниц
;           .endif
            
            ; Удаляем информацию, которая хранится в органе управления Rich Edit
            invoke SendMessage, hREdit, EM_FORMATRANGE, NULL, NULL
            invoke EndDoc, hPrintDC         ; Завершаем печать документа
            invoke DeleteDC, hPrintDC

         .else               ; CANSEL
        
            
        
         .endif

    ret
PrintREdit endp

comment $
 #####################################################################
 Выбирает информацию о документе (число страниц, старт.символ и кон -й 
 в каждой станице...).
 #####################################################################$

Get_Doc_Page_Info proc uses esi edi hREdit :HWND

    LOCAL nLastChar :DWORD
    LOCAL nLastOldChar :DWORD
    LOCAL fr        :FORMATRANGE
    LOCAL nMaxLen   :SDWORD
    LOCAL hDC       :DWORD
    LOCAL charrange :CHARRANGE
    LOCAL pCharOld  :DWORD

            mov hDC, $invoke ( GetDC, NULL )
            mov nCountPage, 0
            
            invoke SendMessage, hREdit, EM_EXGETSEL, NULL, ADDR charrange ; есть выделенный текст ?         
            mov eax, charrange.cpMin
    
; если charrange.cpMax == charrange.cpMin тогда выделенного фрагмента нет. 
; такое равенство получаеться если при первом появлении окна превью был 
; выделен фрагмент текста, а потом выделение текста снято, и превью было 
; вызвано снова.
        
                .if ( !charrange.cpMax )  || ( charrange.cpMax == eax )       ; нет выделенного фрагмента текста
                    mov nLastChar, 0
                    mov nMaxLen, -1
                .else                         ; есть выделенный фрагмент текста            
                    .if fSelOnly == TRUE      ; пункт толко выделенное отмечен
                        m2m nLastChar, charrange.cpMin      
                        m2m nMaxLen, charrange.cpMax
                    .else                     ; отображать весь документ
                        m2m nLastChar, 0
                        mov nMaxLen, -1
                    .endif
                .endif

;       ПОДГОТОВИМ СТРУКТУРУ FORMATRANGE СОДЕРЖАЩУЮ ПАРАМЕТРЫ ВЫВОДА

            m2m fr.hdc, hDC               ; устро - во представления 
            m2m fr.hdcTarget, hDC         ; 0

       ; размер области вывода в TWIPS ( 1 / 1440 часть дюйма )
            
            mov fr.rc.left, 0
            mov fr.rc.top, 0
            
            mov eax, m_Paper.x
            sub eax, m_Margin.right
            sub eax, m_Margin.left          
            mov fr.rc.right, eax
            
            mov eax, m_Paper.y
            sub eax, m_Margin.top
            sub eax, m_Margin.bottom
            mov fr.rc.bottom, eax   
            
            .if fPrnPageNumbers
                invoke MulDiv, 200, $invoke (GetDeviceCaps, hDC, LOGPIXELSY), 72
                sub fr.rc.bottom, eax               
;           .else
;               mov fr.rc.bottom, 0            
            .endif
            

            
;            invoke CopyRect, ADDR fr.rcPage, ADDR fr.rc
    
            jmp start_c
           
new_page:   
            inc nCountPage                ; ++ число страниц в документе
 
            m2m pCharOld, pChar
            
            ; выделим блок памяти для обновившегося числа страниц
            invoke IntMul, nCountPage, 4
            invoke GlobalAlloc,GPTR, eax         ;возьмём память
            mov pChar, eax      
            
            .if pCharOld     ; скопируем туда данные из блока памяти для предыдущего числа страниц              
                
                dec nCountPage
                mov ecx, nCountPage
                mov esi, pCharOld                       ; источник
                mov edi, pChar                          ; приёмник
                rep movsd 
                inc nCountPage
                
                invoke GlobalFree, pCharOld     ;освободим память
            
            .endif
            
            ; скопируем "координаты" текущей старницы            
            
            lea esi, nLastOldChar            ; источник
            mov edx, nCountPage
            dec edx
            invoke IntMul, edx, 4
            mov edi, pChar
            add edi, eax                     ; приёмник
            movsd
                
start_c:       
            m2m fr.chrg.cpMin, nLastChar
            m2m fr.chrg.cpMax, nMaxLen
       
            ; Форматируем данные для контекста          
            m2m nLastOldChar, nLastChar
            mov nLastChar, $invoke ( SendMessage, hREdit, EM_FORMATRANGE, FALSE, ADDR fr )

;            PrintDec nLastChar
;            PrintDec nLastOldChar

            mov eax, nLastOldChar
            cmp nLastChar, eax
            ja  new_page

; ############################ !!!!!!!!!!!!!! ############################
;    ВАЖНО! ЗАПИСЫВАЕМ КОНЕЦ МАССИВА                                                                    ;
; ########################################################################

            inc nCountPage                ; ++ число страниц в документе
 
            m2m pCharOld, pChar
            
            ; выделим блок памяти для обновившегося числа страниц
            invoke IntMul, nCountPage, 4
            invoke GlobalAlloc,GPTR, eax         ;возьмём память
            mov pChar, eax      
            
            .if pCharOld     ; скопируем туда данные из блока памяти для предыдущего числа страниц              
                
                dec nCountPage
                mov ecx, nCountPage
                mov esi, pCharOld                       ; источник
                mov edi, pChar                          ; приёмник
                rep movsd 
                inc nCountPage
                
                invoke GlobalFree, pCharOld     ;освободим память
            
            .endif
            
            ; скопируем "координаты" текущей старницы            
            
            lea esi, nLastOldChar            ; источник
            mov edx, nCountPage
            dec edx
            invoke IntMul, edx, 4
            mov edi, pChar
            add edi, eax                     ; приёмник
            movsd 

            dec nCountPage

            invoke ReleaseDC, NULL, hDC
            
            ; Удаляем информацию, которая хранится в органе управления Rich Edit            
            invoke SendMessage, hREdit, EM_FORMATRANGE, NULL, NULL


            ; !!!!!!!!!ВАЖНО!!!!!!!! 
            ; Возникает ситуация, когда в органе управления RichEdit нет введённого текста
            ; тогда в глобальную переменную nCountPage, устанавливаеться значение 0. Это вызывает
            ; путаницу, сообщение об ошибке припечати пустой страницы, и т.п.
            ; Следующие строки делают защиту от такой ситуации. 

            .if !nCountPage
                mov nCountPage, 1                
            .endif
            

    ret
Get_Doc_Page_Info endp

end DllEntry