Уроки Iczelion'а

       

Операции над текстом в RichEdit


&nbsp В этом туториале вы узнаете больше о операциях над текстом, доступных в RichEdit, например о том, как искать/заменять текст и переходить к определенной строке.

&nbsp Скачайте пример.

Теория:

&nbsp Поиск текста

&nbsp В RichEdit есть несколько текстовых операций. Поиск определенного текста - одна из них. Поиск текста осуществляется с помощью сообщений EM_FINDTEXT или EM_FINDTEXTEX. Эти сообщения мало отличаются друг от друга.

EM_FINDTEXT

&nbsp wParam == опции поиска.

&nbsp Может быть комбинацией значений, приведенных ниже. Эти опции идентичны как для EM_FINDTEXT, так и для EM_FINDTEXTEX.

  • FR_DOWN - если этот флаг указан, поиск начинается с конца текущего выделенного текста и до самого конца (вперед). Этот флаг действует только в RichEdit 2.0 или выше: этот способ применяется по умолчанию в RichEdit 1.0. В RichEdit 2.0 по умолчанию поиск осуществляется от конца выделенного текста до начала (назад). Кратко говоря, если вы используете RichEdit 1.0, вы ничего не можете сделать, чтобы изменить направление поиска: он всегда ищет вперед. Если вы используете RichEdit 2.0 и хотите искать вперед, вам нужно указать этот флаг, иначе поиск будет производиться назад.
  • FR_MATCHCASE - если этот флаг указан, будет учитываться регистр.
  • FR_WHOLEWORD - если этот флаг указан, поиск будет искать место в тексте, которое будет удовлетворять указанной поисковой строке.
  • &nbsp Вообще-то, есть еще несколько флагов, но они относятся к языкам, отличным от английского.

    &nbsp lParam == указатель на структуру FINDTEXT.

    FINDTEXT STRUCT chrg CHARRANGE <> lpstrText DWORD ? FINDTEXT ENDS

    &nbsp chrg - это структура CHARRANGE, которая определена следующим образом:



    CHARRANGE STRUCT cpMin DWORD ? cpMax DWORD ? CHARRANGE ENDS

    &nbsp cpMin содержит индекс первого символа в массиве символов (диапазон).

    &nbsp cpMax содержит индекс символа, который следует непосредственно за последним символов в массиве символов.

    &nbsp Фактически, чтобы найти строку текста, вам нужно указать диапазон символом, в котором нужно искать. Значение cpMin и cpMax будут зависеть от того, проводится ли поиск назад или вперед. Если поиск идет вперед, cpMin задает начальный индекс, а cpMax - конечный. Если поиск идет назад, тогда cpMin содержит конечное значение индекса, в то время как cpMax задает начальный индекс.


    &nbsp lpstrText - это указатель на текстовую строку, которую нужно искать.
    &nbsp EM_FINDTEXT возвращает индекс первого символа в заданной текстовой строке в RichEdit. Оно возвращает -1, если указанный текст не был найден.
    &nbsp EM_FINDTEXTEX
    &nbsp wParam == опции поиска. То же самое, что и EM_FINDTEXT. lParam == указатель на структуру FINDTEXTEX.
    FINDTEXTEX STRUCT chrg CHARRANGE <> lpstrText DWORD ? chrgText CHARRANGE <> FINDTEXTEX ENDS
    &nbsp Первые два члена FINDTEXTEX идентичны соответствующим полям в структуре FINDTEXT. chrgText - это структура CHARRANGE, которая будет заполнена начальным и конечным индексами, если будут найдены какие-либо совпадения.
    &nbsp Возвращаемое значение EM_FINDTEXTEX - то же самое, что и у EM_FINDTEXT.
    &nbsp Разница между EM_FINDTEXT и EM_FINDTEXTEXT - это то, что у структуры FINDTEXTEX есть дополнительное поле, chrgText, которое будет заполнено начальным и конечным индексами, если будут найдены совпадения. Это удобно, если мы хотим иметь возможность осуществлять больше текстовых операций над строкой.
    &nbsp Замещение/вставка текста
    &nbsp Контрол RichEdit предоставляет EM_SETTEXTEX для замещения/вставки текста. Это сообщение комбинирует функциональность WM_SETTEXT и WM_REPLACESEL. У него следующий синтаксис:
    &nbsp EM_SETTEXTEXT wParam == указатель на структуру SETTEXTEX.
    SETTEXTEX STRUCT flags DWORD ? codepage DWORD ? SETTEXTEX ENDS
    &nbsp Поле 'flags' может быть комбинацией следующих значений:
  • ST_DEFAULT - удаляет стек совершенных операций, сбрасывает форматирование RichEdit.
  • ST_KEEPUNDO - сохраняет стек совершенных операций.
  • ST_SELECTION - замещает выделенный текст и сохраняет форматирование.

  • &nbsp Поле 'codepage' - это константа, которая указывает кодовую страницу. Обычно мы указываем CP_ACP.
    &nbsp Выделение текста
    &nbsp Мы можем выделить текст программно с помощью сообщений EM_SETSEL или EM_EXSETSEL. Обе прекрасно работают. Выбирать, какое из сообщений необходимо использовать, зависит от доступного формата индексов символов. Если они уже сохранены в структуры CHARRANGE, проще использовать EM_EXSETSEL.


    &nbsp EM_EXSETSEL
    &nbsp wParam == не используется. Должен быть равен 0. lParam == указатель на структуру CHARRANGE, который содержит диапазон символов, который необходимо выделить.
    &nbsp Уведомительные события
    &nbsp В случае с многолинейным edit control'ом вам необходимо сабклассировать его, чтобы получить входные сообщения, такие как события мыши/клавиатуры. RichEdit предоставляет лучший способ: он будет уведомлять родительское окно о таких событиях. Чтобы указать RichEdit, какие события нужно посылать, родительское окно посылает сообщение EM_SETEVENTMASK контролу RichEdit, указывая, в каких событиях он заинтересован. У EM_SETEVENTMASK следующий синтаксис:
    &nbsp EM_SETEVENTMASK
    &nbsp wParam == не используется. Должен быть равен нулю. lParam == маска событий. Это должна быть комбинация флагов, указанных ниже.
  • ENM_CHANGE - прием уведомлений EN_CHANGE.
  • ENM_CORRECTTEXT - прием уведомлений EN_CORRECTTEXT.
  • ENM_DRAGDROPDONE - прием уведомлений EN_DRAGDROPDONE.
  • ENM_DROPFILES - прием уведомлений EN_DROPFILES.
  • ENM_KEYEVENTS - прием уведомлений EN_MSGFILTER, относящихся к событиям от клавиатуры.
  • ENM_LINK - RichEdit 2.0 и выше: прием уведомлений EN_LINK, когда курсор мыши находится над текстом, который принимает CFE_LINK и/или другие события от мыши.
  • ENM_MOUSEEVENTS - прием уведомлений EN_MSGFILTER, относящихся к событиям от мыши.
  • ENM_OBJECTPOSITIONS - прием уведомлений EN_OBJECTPOSITIONS.
  • ENM_PROTECTED - прием уведомлений EN_PROTECTED.
  • ENM_REQUESTRESIZE - прием уведомлений EN_REQUESTRESIZE.
  • ENM_SCROLL - прием уведомлений EN_HSCROLL и EN_VSCROLL.
  • ENM_SCROLLEVENTS - прием уведомлений EN_MSGFILTER от колесика мыши.
  • ENM_SELCHANGE - прием уведомлений EN_SELCHANGE.
  • ENM_UPDATE - прием уведомлений EN_UPDATE. RichEdit 2.0 и выше: этот флаг игнорирует, а сообщения EN_UPDATE отсылаются всегда. Тем не менее, если Rich Edit 3.0 эмулирует Rich Edit 1.0, вы должны указать этот флаг для того, чтобы родительское окно принимало уведомления EN_UPDATE.

  • &nbsp Все вышеуказанные уведомления будут отсылаться через сообщение WM_NOTIFY: вы должны проверить поле 'code' структуры NMHDR, чтобы узнать, какое уведомление вы получили. Например, если вы хотите зарегистрировать сообщения от мыши (скажем, чтобы отображать контекстное меню по нажатию на правую кнопку мыши), вы должны сделать что-то вроде следующего:


    invoke SendMessage,hwndRichEdit,EM_SETEVENTMASK, 0,ENM_MOUSEEVENTS ..... ..... WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD ..... .... .elseif uMsg==WM_NOTIFY push esi mov esi,lParam assume esi:ptr NMHDR .if [esi].code==EN_MSGFILTER .... [ do something here] .... .endif pop esi
    ПРИМЕР
    & nbsp Следующий пример является обновлением IczEdit, шедшего вместе с туториалом 33. Были добавлены возможности поиска/замещения и акселераторы. Также обрабатываются события от мыши и отображается контекстное меню.
    .386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\comdlg32.inc include \masm32\include\gdi32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\gdi32.lib includelib \masm32\lib\comdlg32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib
    WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
    .const IDR_MAINMENU equ 101 IDM_OPEN equ 40001 IDM_SAVE equ 40002 IDM_CLOSE equ 40003 IDM_SAVEAS equ 40004 IDM_EXIT equ 40005 IDM_COPY equ 40006 IDM_CUT equ 40007 IDM_PASTE equ 40008 IDM_DELETE equ 40009 IDM_SELECTALL equ 40010 IDM_OPTION equ 40011 IDM_UNDO equ 40012 IDM_REDO equ 40013 IDD_OPTIONDLG equ 101 IDC_BACKCOLORBOX equ 1000 IDC_TEXTCOLORBOX equ 1001 IDR_MAINACCEL equ 105 IDD_FINDDLG equ 102 IDD_GOTODLG equ 103 IDD_REPLACEDLG equ 104 IDC_FINDEDIT equ 1000 IDC_MATCHCASE equ 1001 IDC_REPLACEEDIT equ 1001 IDC_WHOLEWORD equ 1002 IDC_DOWN equ 1003 IDC_UP equ 1004 IDC_LINENO equ 1005 IDM_FIND equ 40014 IDM_FINDNEXT equ 40015 IDM_REPLACE equ 40016 IDM_GOTOLINE equ 40017 IDM_FINDPREV equ 40018 RichEditID equ 300
    .data ClassName db "IczEditClass",0 AppName db "IczEdit version 2.0",0 RichEditDLL db "riched20.dll",0 RichEditClass db "RichEdit20A",0 NoRichEdit db "Cannot find riched20.dll",0 ASMFilterString db "ASM Source code (*.asm)",0,"*.asm",0 db "All Files (*.*)",0,"*.*",0,0 OpenFileFail db "Cannot open the file",0 WannaSave db "The data in the control is modified. Want to save it?",0 FileOpened dd FALSE BackgroundColor dd 0FFFFFFh ; default to white TextColor dd 0 ; default to black hSearch dd ? ; handle to the search/replace dialog box hAccel dd ?


    .data? hInstance dd ? hRichEdit dd ? hwndRichEdit dd ? FileName db 256 dup(?) AlternateFileName db 256 dup(?) CustomColors dd 16 dup(?) FindBuffer db 256 dup(?) ReplaceBuffer db 256 dup(?) uFlags dd ? findtext FINDTEXTEX <>
    . code start: mov byte ptr [FindBuffer],0 mov byte ptr [ReplaceBuffer],0 invoke GetModuleHandle, NULL mov hInstance,eax invoke LoadLibrary,addr RichEditDLL .if eax!=0 mov hRichEdit,eax invoke WinMain, hInstance,0,0, SW_SHOWDEFAULT invoke FreeLibrary,hRichEdit .else invoke MessageBox,0,addr NoRichEdit,addr AppName,MB_OK or MB_ICONERROR .endif invoke ExitProcess,eax
    WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:DWORD mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,IDR_MAINMENU mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd invoke LoadAccelerators,hInstance,IDR_MAINACCEL mov hAccel,eax .while TRUE invoke GetMessage, ADDR msg,0,0,0 .break .if (!eax) invoke IsDialogMessage,hSearch,addr msg .if eax==FALSE invoke TranslateAccelerator,hwnd,hAccel,addr msg .if eax==0 invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endif .endif .endw mov eax,msg.wParam ret WinMain endp
    StreamInProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesRead:DWORD invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0 xor eax,1 ret StreamInProc endp
    StreamOutProc proc hFile:DWORD,pBuffer:DWORD, NumBytes:DWORD, pBytesWritten:DWORD invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0 xor eax,1 ret StreamOutProc endp


    CheckModifyState proc hWnd:DWORD invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0 .if eax!=0 invoke MessageBox,hWnd,addr WannaSave,addr AppName,MB_YESNOCANCEL .if eax==IDYES invoke SendMessage,hWnd,WM_COMMAND,IDM_SAVE,0 .elseif eax==IDCANCEL mov eax,FALSE ret .endif .endif mov eax,TRUE ret CheckModifyState endp
    SetColor proc LOCAL cfm:CHARFORMAT invoke SendMessage,hwndRichEdit,EM_SETBKGNDCOLOR,0,BackgroundColor invoke RtlZeroMemory,addr cfm,sizeof cfm mov cfm.cbSize,sizeof cfm mov cfm.dwMask,CFM_COLOR push TextColor pop cfm.crTextColor invoke SendMessage,hwndRichEdit,EM_SETCHARFORMAT,SCF_ALL,addr cfm ret SetColor endp
    OptionProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL clr:CHOOSECOLOR .if uMsg==WM_INITDIALOG .elseif uMsg==WM_COMMAND mov eax,wParam shr eax,16 .if ax==BN_CLICKED mov eax,wParam .if ax==IDCANCEL invoke SendMessage,hWnd,WM_CLOSE,0,0 .elseif ax==IDC_BACKCOLORBOX invoke RtlZeroMemory,addr clr,sizeof clr mov clr.lStructSize, sizeof clr push hWnd pop clr.hwndOwner push hInstance pop clr.hInstance push BackgroundColor pop clr.rgbResult mov clr.lpCustColors,offset CustomColors mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT invoke ChooseColor,addr clr .if eax!=0 push clr.rgbResult pop BackgroundColor invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX invoke InvalidateRect,eax,0,TRUE .endif .elseif ax==IDC_TEXTCOLORBOX invoke RtlZeroMemory,addr clr,sizeof clr mov clr.lStructSize,sizeof clr push hWnd pop clr.hwndOwner push hInstance pop clr.hInstance push TextColor pop clr.rgbResult mov clr.lpCustColors,offset CustomColors mov clr.Flags,CC_ANYCOLOR or CC_RGBINIT invoke ChooseColor,addr clr .if eax!=0 push clr.rgbResult pop TextColor invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX invoke InvalidateRect,eax,0,TRUE .endif .elseif ax==IDOK invoke SendMessage,hwndRichEdit,EM_GETMODIFY,0,0 push eax invoke SetColor pop eax invoke SendMessage,hwndRichEdit,EM_SETMODIFY,eax,0 invoke EndDialog,hWnd,0 .endif .endif .elseif uMsg==WM_CTLCOLORSTATIC invoke GetDlgItem,hWnd,IDC_BACKCOLORBOX .if eax==lParam invoke CreateSolidBrush,BackgroundColor ret .else invoke GetDlgItem,hWnd,IDC_TEXTCOLORBOX .if eax==lParam invoke CreateSolidBrush,TextColor ret .endif .endif mov eax,FALSE ret .elseif uMsg==WM_CLOSE invoke EndDialog,hWnd,0 .else mov eax,FALSE ret .endif mov eax,TRUE ret OptionProc endp


    SearchProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD .if uMsg== WM_INITDIALOG push hWnd pop hSearch invoke CheckRadioButton,hWnd,IDC_DOWN,IDC_UP,IDC_DOWN invoke SendDlgItemMessage,hWnd,IDC_FINDEDIT, WM_SETTEXT,0,addr FindBuffer .elseif uMsg==WM_COMMAND mov eax,wParam shr eax,16 .if ax==BN_CLICKED mov eax,wParam .if ax==IDOK mov uFlags,0 invoke SendMessage,hwndRichEdit, EM_EXGETSEL,0,addr findtext.chrg invoke GetDlgItemText,hWnd,IDC_FINDEDIT, addr FindBuffer,sizeof FindBuffer .if eax!=0 invoke IsDlgButtonChecked,hWnd,IDC_DOWN .if eax==BST_CHECKED or uFlags,FR_DOWN mov eax,findtext.chrg.cpMin .if eax!=findtext.chrg.cpMax push findtext.chrg.cpMax pop findtext.chrg.cpMin .endif mov findtext.chrg.cpMax,-1 .else mov findtext.chrg.cpMax,0 .endif invoke IsDlgButtonChecked,hWnd,IDC_MATCHCASE .if eax==BST_CHECKED or uFlags,FR_MATCHCASE .endif invoke IsDlgButtonChecked,hWnd,IDC_WHOLEWORD .if eax==BST_CHECKED or uFlags,FR_WHOLEWORD .endif mov findtext.lpstrText,offset FindBuffer invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX, uFlags,addr findtext .if eax!=-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0, addr findtext.chrgText .endif .endif .elseif ax==IDCANCEL invoke SendMessage,hWnd,WM_CLOSE,0,0 .else mov eax,FALSE ret .endif .endif .elseif uMsg==WM_CLOSE mov hSearch,0 invoke EndDialog,hWnd,0 .else mov eax,FALSE ret .endif mov eax,TRUE ret SearchProc endp
    ReplaceProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL settext:SETTEXTEX .if uMsg==WM_INITDIALOG push hWnd pop hSearch invoke SetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer invoke SetDlgItemText,hWnd,IDC_REPLACEEDIT,addr ReplaceBuffer .elseif uMsg==WM_COMMAND mov eax,wParam shr eax,16 .if ax==BN_CLICKED mov eax,wParam .if ax==IDCANCEL invoke SendMessage,hWnd,WM_CLOSE,0,0 .elseif ax==IDOK invoke GetDlgItemText,hWnd,IDC_FINDEDIT, addr FindBuffer,sizeof FindBuffer invoke GetDlgItemText,hWnd,IDC_REPLACEEDIT, addr ReplaceBuffer,sizeof ReplaceBuffer mov findtext.chrg.cpMin,0 mov findtext.chrg.cpMax,-1 mov findtext.lpstrText,offset FindBuffer mov settext.flags,ST_SELECTION mov settext.codepage,CP_ACP .while TRUE invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX, FR_DOWN,addr findtext .if eax==-1 .break .else invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0, addr findtext.chrgText invoke SendMessage,hwndRichEdit,EM_SETTEXTEX, addr settext,addr ReplaceBuffer .endif .endw .endif .endif .elseif uMsg==WM_CLOSE mov hSearch,0 invoke EndDialog,hWnd,0 .else mov eax,FALSE ret .endif mov eax,TRUE ret ReplaceProc endp


    GoToProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL LineNo:DWORD LOCAL chrg:CHARRANGE .if uMsg== WM_INITDIALOG push hWnd pop hSearch .elseif uMsg==WM_COMMAND mov eax,wParam shr eax,16 .if ax==BN_CLICKED mov eax,wParam .if ax==IDCANCEL invoke SendMessage,hWnd,WM_CLOSE,0,0 .elseif ax==IDOK invoke GetDlgItemInt,hWnd,IDC_LINENO,NULL,FALSE mov LineNo,eax invoke SendMessage,hwndRichEdit,EM_GETLINECOUNT,0,0 .if eax>LineNo invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0 mov chrg.cpMin,eax mov chrg.cpMax,eax invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg invoke SetFocus,hwndRichEdit .endif .endif .endif .elseif uMsg==WM_CLOSE mov hSearch,0 invoke EndDialog,hWnd,0 .else mov eax,FALSE ret .endif mov eax,TRUE ret GoToProc endp
    PrepareEditMenu proc hSubMenu:DWORD LOCAL chrg:CHARRANGE invoke SendMessage,hwndRichEdit,EM_CANPASTE,CF_TEXT,0 .if eax==0 ; no text in the clipboard invoke EnableMenuItem,hSubMenu,IDM_PASTE,MF_GRAYED .else invoke EnableMenuItem,hSubMenu,IDM_PASTE,MF_ENABLED .endif invoke SendMessage,hwndRichEdit,EM_CANUNDO,0,0 .if eax==0 invoke EnableMenuItem,hSubMenu,IDM_UNDO,MF_GRAYED .else invoke EnableMenuItem,hSubMenu,IDM_UNDO,MF_ENABLED .endif invoke SendMessage,hwndRichEdit,EM_CANREDO,0,0 .if eax==0 invoke EnableMenuItem,hSubMenu,IDM_REDO,MF_GRAYED .else invoke EnableMenuItem,hSubMenu,IDM_REDO,MF_ENABLED .endif invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr chrg mov eax,chrg.cpMin .if eax==chrg.cpMax ; no current selection invoke EnableMenuItem,hSubMenu,IDM_COPY,MF_GRAYED invoke EnableMenuItem,hSubMenu,IDM_CUT,MF_GRAYED invoke EnableMenuItem,hSubMenu,IDM_DELETE,MF_GRAYED .else invoke EnableMenuItem,hSubMenu,IDM_COPY,MF_ENABLED invoke EnableMenuItem,hSubMenu,IDM_CUT,MF_ENABLED invoke EnableMenuItem,hSubMenu,IDM_DELETE,MF_ENABLED .endif ret PrepareEditMenu endp
    WndProc proc hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD LOCAL ofn:OPENFILENAME LOCAL buffer[256]:BYTE LOCAL editstream:EDITSTREAM LOCAL hFile:DWORD LOCAL hPopup:DWORD LOCAL pt:POINT LOCAL chrg:CHARRANGE .if uMsg==WM_CREATE invoke CreateWindowEx,WS_EX_CLIENTEDGE,addr RichEditClass,0, \ WS_CHILD or WS_VISIBLE or ES_MULTILINE or WS_VSCROLL or WS_HSCROLL or ES_NOHIDESEL,\ CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,hWnd,RichEditID,hInstance,0 mov hwndRichEdit,eax invoke SendMessage,hwndRichEdit,EM_LIMITTEXT,-1,0 invoke SetColor invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke SendMessage,hwndRichEdit,EM_SETEVENTMASK,0,ENM_MOUSEEVENTS invoke SendMessage,hwndRichEdit,EM_EMPTYUNDOBUFFER,0,0 .elseif uMsg==WM_NOTIFY push esi mov esi,lParam assume esi:ptr NMHDR .if [esi].code==EN_MSGFILTER assume esi:ptr MSGFILTER .if [esi].msg==WM_RBUTTONDOWN invoke GetMenu,hWnd invoke GetSubMenu,eax,1 mov hPopup,eax invoke PrepareEditMenu,hPopup mov edx,[esi].lParam mov ecx,edx and edx,0FFFFh shr ecx,16 mov pt.x,edx mov pt.y,ecx invoke ClientToScreen,hWnd,addr pt invoke TrackPopupMenu,hPopup,TPM_LEFTALIGN or TPM_BOTTOMALIGN, pt.x,pt.y,NULL,hWnd,NULL .endif .endif pop esi .elseif uMsg==WM_INITMENUPOPUP mov eax,lParam .if ax==0 ; file menu .if FileOpened==TRUE ; a file is already opened invoke EnableMenuItem,wParam,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,wParam,IDM_CLOSE,MF_ENABLED invoke EnableMenuItem,wParam,IDM_SAVE,MF_ENABLED invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_ENABLED .else invoke EnableMenuItem,wParam,IDM_OPEN,MF_ENABLED invoke EnableMenuItem,wParam,IDM_CLOSE,MF_GRAYED invoke EnableMenuItem,wParam,IDM_SAVE,MF_GRAYED invoke EnableMenuItem,wParam,IDM_SAVEAS,MF_GRAYED .endif .elseif ax==1 ; edit menu invoke PrepareEditMenu,wParam .elseif ax==2 ; search menu bar .if FileOpened==TRUE invoke EnableMenuItem,wParam,IDM_FIND,MF_ENABLED invoke EnableMenuItem,wParam,IDM_FINDNEXT,MF_ENABLED invoke EnableMenuItem,wParam,IDM_FINDPREV,MF_ENABLED invoke EnableMenuItem,wParam,IDM_REPLACE,MF_ENABLED invoke EnableMenuItem,wParam,IDM_GOTOLINE,MF_ENABLED .else invoke EnableMenuItem,wParam,IDM_FIND,MF_GRAYED invoke EnableMenuItem,wParam,IDM_FINDNEXT,MF_GRAYED invoke EnableMenuItem,wParam,IDM_FINDPREV,MF_GRAYED invoke EnableMenuItem,wParam,IDM_REPLACE,MF_GRAYED invoke EnableMenuItem,wParam,IDM_GOTOLINE,MF_GRAYED .endif .endif .elseif uMsg==WM_COMMAND .if lParam==0 ; menu commands mov eax,wParam .if ax==IDM_OPEN invoke RtlZeroMemory,addr ofn,sizeof ofn mov ofn.lStructSize,sizeof ofn push hWnd pop ofn.hwndOwner push hInstance pop ofn.hInstance mov ofn.lpstrFilter,offset ASMFilterString mov ofn.lpstrFile,offset FileName mov byte ptr [FileName],0 mov ofn.nMaxFile,sizeof FileName mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST invoke GetOpenFileName,addr ofn .if eax!=0 invoke CreateFile,addr FileName,GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING, \ FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE mov hFile,eax ;========================================== ; записываем текст в контрол richedit ;========================================== mov editstream.dwCookie,eax mov editstream.pfnCallback,offset StreamInProc invoke SendMessage,hwndRichEdit,EM_STREAMIN, SF_TEXT,addr editstream ;========================================== ; Устанавливаем флаг модификации в false ;========================================== invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke CloseHandle,hFile mov FileOpened,TRUE .else invoke MessageBox,hWnd,addr OpenFileFail, addr AppName,MB_OK or MB_ICONERROR .endif .endif .elseif ax==IDM_CLOSE invoke CheckModifyState,hWnd .if eax==TRUE invoke SetWindowText,hwndRichEdit,0 mov FileOpened,FALSE .endif .elseif ax==IDM_SAVE invoke CreateFile,addr FileName,GENERIC_WRITE, FILE_SHARE_READ,NULL,CREATE_ALWAYS, \ FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE @@: mov hFile,eax ;======================================================= ; записываем текст в файл ;======================================================= mov editstream.dwCookie,eax mov editstream.pfnCallback,offset StreamOutProc invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT, addr editstream ;========================================================== ; Initialize the modify state to false ; Устанавливаем флаг модификации в false ;========================================================== invoke SendMessage,hwndRichEdit,EM_SETMODIFY,FALSE,0 invoke CloseHandle,hFile .else invoke MessageBox,hWnd,addr OpenFileFail, addr AppName,MB_OK or MB_ICONERROR .endif .elseif ax==IDM_COPY invoke SendMessage,hwndRichEdit,WM_COPY,0,0 .elseif ax==IDM_CUT invoke SendMessage,hwndRichEdit,WM_CUT,0,0 .elseif ax==IDM_PASTE invoke SendMessage,hwndRichEdit,WM_PASTE,0,0 .elseif ax==IDM_DELETE invoke SendMessage,hwndRichEdit,EM_REPLACESEL,TRUE,0 .elseif ax==IDM_SELECTALL mov chrg.cpMin,0 mov chrg.cpMax,-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr chrg .elseif ax==IDM_UNDO invoke SendMessage,hwndRichEdit,EM_UNDO,0,0 .elseif ax==IDM_REDO invoke SendMessage,hwndRichEdit,EM_REDO,0,0 .elseif ax==IDM_OPTION invoke DialogBoxParam,hInstance,IDD_OPTIONDLG, hWnd,addr OptionProc,0 .elseif ax==IDM_SAVEAS invoke RtlZeroMemory,addr ofn,sizeof ofn mov ofn.lStructSize,sizeof ofn push hWnd pop ofn.hwndOwner push hInstance pop ofn.hInstance mov ofn.lpstrFilter,offset ASMFilterString mov ofn.lpstrFile,offset AlternateFileName mov byte ptr [AlternateFileName],0 mov ofn.nMaxFile,sizeof AlternateFileName mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST invoke GetSaveFileName,addr ofn .if eax!=0 invoke CreateFile,addr AlternateFileName, GENERIC_WRITE,FILE_SHARE_READ, \ NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0 .if eax!=INVALID_HANDLE_VALUE jmp @B .endif .endif .elseif ax==IDM_FIND .if hSearch==0 invoke CreateDialogParam,hInstance,IDD_FINDDLG, hWnd,addr SearchProc,0 .endif .elseif ax==IDM_REPLACE .if hSearch==0 invoke CreateDialogParam,hInstance,IDD_REPLACEDLG, hWnd,addr ReplaceProc,0 .endif .elseif ax==IDM_GOTOLINE .if hSearch==0 invoke CreateDialogParam,hInstance,IDD_GOTODLG, hWnd,addr GoToProc,0 .endif .elseif ax==IDM_FINDNEXT invoke lstrlen,addr FindBuffer .if eax!=0 invoke SendMessage,hwndRichEdit,EM_EXGETSEL, 0,addr findtext.chrg mov eax,findtext.chrg.cpMin .if eax!=findtext.chrg.cpMax push findtext.chrg.cpMax pop findtext.chrg.cpMin .endif mov findtext.chrg.cpMax,-1 mov findtext.lpstrText,offset FindBuffer invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX, FR_DOWN,addr findtext .if eax!=-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL, 0,addr findtext.chrgText .endif .endif .elseif ax==IDM_FINDPREV invoke lstrlen,addr FindBuffer .if eax!=0 invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0, addr findtext.chrg mov findtext.chrg.cpMax,0 mov findtext.lpstrText,offset FindBuffer invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX, 0,addr findtext .if eax!=-1 invoke SendMessage,hwndRichEdit, EM_EXSETSEL,0,addr findtext.chrgText .endif .endif .elseif ax==IDM_EXIT invoke SendMessage,hWnd,WM_CLOSE,0,0 .endif .endif .elseif uMsg==WM_CLOSE invoke CheckModifyState,hWnd .if eax==TRUE invoke DestroyWindow,hWnd .endif .elseif uMsg==WM_SIZE mov eax,lParam mov edx,eax and eax,0FFFFh shr edx,16 invoke MoveWindow,hwndRichEdit,0,0,eax,edx,TRUE .elseif uMsg==WM_DESTROY invoke PostQuitMessage,NULL .else invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .endif xor eax,eax ret WndProc endp end start


    АНАЛИЗ
    &nbsp Поиск текста реализуется с помощью EM_FINDTEXTEX. Когда пользователь кликает на пункте меню 'Find', отсылается сообщение IDM_FIND и отображается соответствующее диалоговое окно.
    invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer .if eax!=0
    &nbsp Когда пользователь задает текст, который нужно найти, а затем нажимает кнопку 'OK', текст, который мы собираемся искать, оказывается в FindBuffer.
    mov uFlags,0 invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,addr findtext.chrg
    &nbsp Если текстовая строка не равняется NULL, мы устанавливаем значение переменной uFlags равной 0. Эта переменная используется для хранения флагов поиска, отсылаемых вместе с сообщением EM_FINDTEXTEX. После этого мы получаем текущий выделенный текст с помощью EM_EXGETSEL, потому что нам нужно знать, откуда будет производиться поиск.
    invoke IsDlgButtonChecked,hWnd,IDC_DOWN .if eax==BST_CHECKED or uFlags,FR_DOWN mov eax,findtext.chrg.cpMin .if eax!=findtext.chrg.cpMax push findtext.chrg.cpMax pop findtext.chrg.cpMin .endif mov findtext.chrg.cpMax,-1 .else mov findtext.chrg.cpMax,0 .endif
    &nbsp Следующая часть несколько сложнее. Мы проверяем значение кнопки, задающей направление поиска. Если указан поиск вперед, мы задаем флаг FR_DOWN в uFlags. После этого мы проверяем, выделен ли сейчас какой-нибудь текст, проверяя значения cpMin и cpMax. Если оба значения не равны, это означает, что выделение существует, и мы должны продолжать поиск от конца текущего выделения до конца текста в контроле. Поэтому нам требуется заменить значение cpMax на значение cpMin, а cpMax приравнять к -1 (0FFFFFFFFh). если выделения нет, поиск будет производиться от текущей позиции курсора до конца текста.
    &nbsp Если пользователь выберет поиск назад, мы используем диапазон от начала выделения до начала текста в контроле. Поэтому на нужно только изменить значение cpMax на 0. В случае с поиском назад cpMin содержит индекс последнего символа в диапазоне поиска, а cpMax - индекс первого символа. Это инверсия поиска вперед.


    invoke IsDlgButtonChecked,hWnd,IDC_MATCHCASE .if eax==BST_CHECKED or uFlags,FR_MATCHCASE .endif invoke IsDlgButtonChecked,hWnd,IDC_WHOLEWORD .if eax==BST_CHECKED or uFlags,FR_WHOLEWORD .endif mov findtext.lpstrText,offset FindBuffer
    &nbsp Мы продолжаем проверку checkbox'ов, чтобы задать значения FR_MATCHCASE и FR_WHOLEWORD. Наконец, мы помещаем смещение текста, который нужно найти в поле lpstrText.
    invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,uFlags,addr findtext .if eax!=-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText .endif
    &nbsp Мы сейчас готовы послать сообщение EM_FINDTEXTEX. После этого мы проверяем результаты поиска, возвращенные SendMessage. Если возвращаемое значение равно -1, значит никаких совпадений не было найдено. В противном случае поле chrgText структуры FINDTEXTEX заполняется индексами совпавшего текста. Мы выделяем найденный текст с помощью EM_EXSETSEL.
    &nbsp Операция замещения делается подобным же образом.
    invoke GetDlgItemText,hWnd,IDC_FINDEDIT, addr FindBuffer,sizeof FindBuffer invoke GetDlgItemText,hWnd,IDC_REPLACEEDIT, addr ReplaceBuffer,sizeof ReplaceBuffer
    &nbsp Мы получаем текст, который нужно найти, и текст, который нужно заменить.
    mov findtext.chrg.cpMin,0 mov findtext.chrg.cpMax,-1 mov findtext.lpstrText,offset FindBuffer
    &nbsp Чтобы сделать проще, операция замещения действует на весь текст в контроле. Поэтому начальный индекс равен 0, а конечный - -1.
    mov settext.flags,ST_SELECTION mov settext.codepage,CP_ACP
    &nbsp Мы инициализируем структуру SETTEXTEX, чтобы задать то, что мы хотим заменить текущее выделение и использовать системную страницу кодировки по умолчанию.
    .while TRUE invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX, FR_DOWN,addr findtext .if eax==-1 .break .else invoke SendMessage,hwndRichEdit,EM_EXSETSEL, 0,addr findtext.chrgText invoke SendMessage,hwndRichEdit,EM_SETTEXTEX, addr settext,addr ReplaceBuffer .endif .endw
    &nbsp Мы входим в бесконечный цикл и ищем совпадающий текст. Если он был найден, мы выбираем его с помощью EM_EXSETSEL и заменяем его EM_SETTEXTEX. Если больше совпадений не было найдено, мы выходим из цикла.


    &nbsp 'Find Next' и 'Find Prev.' используют сообщение EM_FINDTEXTEX похожим образом.
    &nbsp Теперь мы рассмотрим 'Go to Line'. Когда пользователь кликает по этому пункту меню, мы отображаем диалоговое окно.
    &nbsp Когда пользователь набирает номер линии и нажимет кнопку 'Ok', мы начинаем операцию.
    invoke GetDlgItemInt,hWnd,IDC_LINENO,NULL,FALSE mov LineNo,eax
    &nbsp Получаем номер линии из edit control'а.
    invoke SendMessage,hwndRichEdit,EM_GETLINECOUNT,0,0 .if eax>LineNo
    &nbsp Получаем количество линий в контроле. Проверяем, не указал ли пользователь номер линии, который выходит за рамки допустимых значений.
    invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0
    &nbsp Если номер линии верен, мы передвигает курсор к первому символу в этой строке. Поэтому мы посылаем сообщение EM_LINEINDEX контролу richedit. Это сообщение возвращает индекс первого символа в заданной строке. Мы посылаем номер строки через wParam, а получаем индекс символа.
    invoke SendMessage,hwndRichEdit,EM_SETSEL,eax,eax
    &nbsp Чтобы установить текущее выделение, в этот раз мы используем EM_SETSEL, потому что индексы символов уже не находятся в структуре CHARRANGE, что сохраняет нам две инструкции (помещение этих индексов в структуру CHARRANGE).
    invoke SetFocus,hwndRichEdit .endif
    &nbsp Курсор не будет отображен, пока контрол RichEdit не получит фокус. Поэтому мы вызываем SetFocus.
    [C] Iczelion, пер. Aquila.

    Содержание раздела