Tutorial 33: RichEdit Control: Basics Tutorial 34: RichEdit Control: More Text Operations Tutorial 35: RichEdit Control: Syntax Hilighting

You'll learn more about text operations under RichEdit control. Specifically, you'll know how to search for/replace text, jumping to specific line number.

Download the example.

Theory

Searching for Text

There are several text operations under RichEdit control. Searching for text is one such operation. Searching for text is done by sending EM_FINDTEXT or EM_FINDTEXTEX. These messages has a small difference.

EM_FINDTEXT

In essence, to search for a text string, you have to specify the character range in which to search. The meaning of cpMin and cpMax differ according to whether the search is downward or upward. If the search is downward, cpMin specifies the starting character index to search in and cpMax the ending character index. If the search is upward, the reverse is true, ie. cpMin contains the ending character index while cpMax the starting character index.

lpstrText is the pointer to the text string to search for.

EM_FINDTEXT returns the character index of the first character in the matching text string in the richedit control. It returns -1 if no match is found.

EM_FINDTEXTEX

Replace/Insert Text

RichEdit control provides EM_SETTEXTEX for replacing/inserting text. This message combines the functionality of WM_SETTEXT and EM_REPLACESEL. It has the following syntax:

EM_SETTEXTEX

codepage is the constant that specifies the code page you want to text to be. Usually, we simply use CP_ACP.

Text Selection

We can select the text programmatically with EM_SETSEL or EM_EXSETSEL. Either one works fine. Choosing which message to use depends on the available format of the character indices. If they are already stored in a CHARRANGE structure, it's easier to use EM_EXSETSEL.

EM_EXSETSEL

Event Notification

In the case of a multiline edit control, you have to subclass it in order to obtain the input messages such as mouse/keyboard events. RichEdit control provides a better scheme that will notify the parent window of such events. In order to register for notifications, the parent window sends EM_SETEVENTMASK message to the RichEdit control, specifying which events it's interested in. EM_SETEVENTMASK has the following syntax:

EM_SETEVENTMASK

Example:

The following example is the update of IczEdit in tutorial no. 33. It adds search/replace functionality and accelerator keys to the program. It also processes the mouse events and provides a popup menu on right mouse click.

.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 ;============================================================ ; stream the text into the richedit control ;============================================================ mov editstream.dwCookie,eax mov editstream.pfnCallback,OFFSET StreamInProc invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT, \ ADDR editstream ;============================================================ ; Initialize the modify state to 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 ;============================================================ ; stream the text to the file ;============================================================ mov editstream.dwCookie,eax mov editstream.pfnCallback,OFFSET StreamOutProc invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT, \ ADDR editstream ;============================================================ ; Initialize the modify state to 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

Analysis

The search-for-text capability is implemented with EM_FINDTEXTEX. When the user clicks on Find menuitem, IDM_FIND message is sent and the Find dialog box is displayed.

invoke GetDlgItemText,hWnd,IDC_FINDEDIT,ADDR FindBuffer, \ sizeof FindBuffer .IF eax!=0

When the user types the text to search for and then press OK button, we get the text to be searched for into FindBuffer.

mov uFlags,0 invoke SendMessage,hwndRichEdit,EM_EXGETSEL,0,ADDR findtext.chrg

If the text string is not null, we continue to initialize uFlags variable to 0.This variable is used to store the search flags used withEM_FINDTEXTEX. After that, we obtain the current selection with EM_EXGETSEL because we need to know the starting point of the search operation.

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

The next part is a little tricky. We check the direction radio button to ascertain which direction the search should go. If the downward search is indicated, we set FR_DOWN flag to uFlags. After that, we check whether a selection is currently in effect by comparing the values of cpMin and cpMax. If both values are not equal, it means there is a current selection and we must continue the search from the END of that selection to the END of text in the control. Thus we need to replace the value of cpMax with that of cpMin and change the value of cpMax to -1 (0FFFFFFFFh). If there is no current selection, the range to search is from the current caret position to the END of text.

If the user chooses to search upward, we use the range from the start of the selection to the beginning of the text in the control. That's why we only modify the value of cpMax to 0. In the case of upward search, cpMin contains the character index of the last character in the search range and cpMax the character index of the first char in the search range. It's the inverse of the downward search.

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

We continue to check the checkboxes for the search flags, ie, FR_MATCHCASE and FR_WHOLEWORD. Lastly, we put the OFFSET of the text to search for in lpstrText member.

invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,uFlags, \ ADDR findtext .IF eax!=-1 invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0, \ ADDR findtext.chrgText .ENDIF .ENDIF

We are now ready to issue EM_FINDTEXTEX. After that, we examine the search result returned by SendMessage. If the return value is -1, no match is found in the search range. Otherwise, chrgText member of FINDTEXTEX structure is filled with the character indices of the matching text. We thus proceed to select it with EM_EXSETSEL

The replace operation is done in much the same manner.

invoke GetDlgItemText,hWnd,IDC_FINDEDIT,ADDR FindBuffer, \ sizeof FindBuffer invoke GetDlgItemText,hWnd,IDC_REPLACEEDIT,ADDR ReplaceBuffer, \ sizeof ReplaceBuffer

We retrieve the text to search for and the text used to replace.

mov findtext.chrg.cpMin,0 mov findtext.chrg.cpMax,-1 mov findtext.lpstrText,OFFSET FindBuffer

To make it easy, the replace operation affects all the text in the control. Thus the starting index is 0 and the ending index is -1.

mov settext.flags,ST_SELECTION mov settext.codepage,CP_ACP

We initialize SETTEXTEX structure to indicate that we want to replace the current selection and use the default system code page.

.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

We enter an infinite loop, searching for the matching text. If one is found, we select it with EM_EXSETSEL and replace it with EM_SETTEXTEX. When no more match is found, we exit the loop.

find next and find prev. features use EM_FINDTEXTEX message in the similar manner to the find operation.

We will examine the Go to Line feature next. When the user clicks Go To Line menuitem, we display a dialog box below:

When the user types a line number and presses Ok button, we begin the operation.

invoke GetDlgItemInt,hWnd,IDC_LINENO,NULL,FALSE mov LineNo,eax

Obtain the line number from the edit control

invoke SendMessage,hwndRichEdit,EM_GETLINECOUNT,0,0 .IF eax>LineNo

Obtain the number of lines in the control. Check whether the user specifies the line number that is out of the range.

invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0

If the line number is valid, we want to move the caret to the first character of that line. So we send EM_LINEINDEX message to the richedit control. This message returns the character index of the first character in the specified line. We send the line number in wParam and in return, we has the character index.

invoke SendMessage,hwndRichEdit,EM_SETSEL,eax,eax

To set the current selection, this time we use EM_SETSEL because the character indices are not already in a CHARRANGE structure thus it saves us two instructions (to put those indices into a CHARRANGE structure).

invoke SetFocus,hwndRichEdit .ENDIF

The caret will not be displayed unless the richedit control has the focus. So we call SetFocus on it.


Tutorial 32: Multiple Document Interface (MDI) Overview Tutorial 34: RichEdit Control: More Text Operations
Software » wiki » Assembler » X86 » icz » uk » tute034.html