Tutorial 34: control RichEdit: Más Operacionescon Texto

Aprenderás más acera de la operaciones de texto bajo control RichEdit. Especíificamente sabrás como buscar/reemplazar texto, saltando a un número especifico de línea.

Baja el ejemplo.

Teoría

Buscando texto

Hay varias operaciones de texto bajo control RichEdit. Buscar texto es una de ellas. La búsqueda de texto se hace enviando el mensaje EM_FINDTEXT o EM_FINDTEXTEX. Estos mensajes tienen una pequeña diferencia.

EM_FINDTEXT wParam == opciones de búsqueda. Puede ser cualquier combinación de los 
valores de la siguiente tabla. Estas opciones son identicas para ambos EM_FINDTEXT y EM_FINDTEXTEX
FR_DOWN
Si esta bandera [flag] esta especificado la búsqueda comienza desde el final de la actual selección hasta el final del texto en el control (downward). Esta bandera [flag] tiene efecto solo para el RichEdit 2.0 o posterio. Este comportamiento es por defecto para el RichEdit 1.0. El comportamiento por defecto del RichEdit 2.0 o posterior es buscar desde el final de la actual selección hasta el comienzo del texto upward).
En resumen, si usas RichEdit 1.0, no puedes hacer nada sobre la dirección de búsqueda, siempre busca hacia abajo. Si usas RichEdit 2.0 y quieres buscar hacia abajo
debes especificar esta bandera [flag] de otro modo la búsqueda será hacia arriba.
FR_MATCHCASE
Con esta bandera [flag] , la búsqueda es sensible a la diferencia entre mayúsculas y minúsculas.
FR_WHOLEWORD
Con esta bandera [flag] , se buscarán las palabras completas coincidentes con la cadena de búsqueda.
Actualmente hay unos pocos flags más pero no son relevantes para lenguajes no-Ingleses. 
lParam== puntero a la estructura FINDTEXT. 	
FINDTEXT STRUCT 
     chrg CHARRANGE <> 
     lpstrText DWORD ? 
FINDTEXT ENDS 

chrg Es una estructura CHARRANGE la cual se define como sigue:  
CHARRANGE STRUCT 
     cpMin DWORD ? 
     cpMax DWORD ? 
CHARRANGE ENDS 

cpMin contiene el índice del primer caracter en el array de caracteres (rango). 
cpMax contiene el índice del caracter inmediatamente siguiente al último caracter en el array de caracteres. 

En esencia, para buscar un texto, tienes que especificar el rango en el cual buscar. El significado de cpMin y cpMax difiere 
en cuanto a si la búsqueda es hacia arriba o hacia abajo. Si la búsqueda es hacia abajo, cpMin especifica el índice del 
caracter de comienzo de la búsqueda y cpMax el índice del caracter del final. Si la búsqueda es hacia arriba será al revés, por 
ej. cpMin contiene el índice del caracter final mientras cpMax el índice del caracter del principio. lpstrText es el puntero a la 
cadena de texto a buscar. EM_FINDTEXT devuelve el índice del primer caracter del texto coincidente en el control RichEdit. Si 
devuelve -1 es que no se encontró nada. EM_FINDTEXTEX wParam == las opciones de búsqueda. Como en EM_FINDTEXT. 
lParam == puntero a la estructura FINDTEXTEX.  

FINDTEXTEX STRUCT 
     chrg CHARRANGE <> 
     lpstrText DWORD ? 
     chrgText CHARRANGE <> 
FINDTEXTEX ENDS 

Los dos primeros miembros de FINDTEXTEX son idénticos a los de la estructura FINDTEXT. chrgText es una estructura 
CHARRANGE que será rellenada con los índices de comienzo/final si se encuentra alguna coincidencia. El valor de retorno de 
EM_FINDTEXTEX es el mismo que en EM_FINDTEXT. La diferencia entre EM_FINDTEXT y  EM_FINDTEXTEX es que la 
estructura FINDTEXTEX tiene un miembro adicional, chrgText, el cual será rellenado con el índice de comienzo/final si se 
encuentra coincidencia. Esto es conveniente si queremos hacer mas operaciones de texto sobre la cadena.

Reemplazar/Insertar Texto

El control RichEdit provee EM_SETTEXTEX para reemplazar/insertar texto. Este mensaje combina la funcionalidad de WM_SETTEXT y EM_REPLACESEL.Y tiene la siguiente sintaxis:

EM_SETTEXTEX wParam == puntero a la estructura SETTEXTEX.  
SETTEXTEX STRUCT 
     flags DWORD ? 
     codepage DWORD ? 
SETTEXTEX ENDS 

flags [banderas] puede ser una combinación de los siguientes valores:
ST_DEFAULT
Borra la pila [stack] de 'undo'. Descarta el formato de texto enrriquecido [RTF] , reemplazando todo el texto.
ST_KEEPUNDO
Mantiene la pila [stack] de 'undo'.
ST_SELECTION
Reemplaza la selección y mantiene formato de texto enrriquecido [RTF]
 codepage Es la constante que especifica la página de códigos que quieres usar. Usualmente, solemos usar CP_ACP.

Selección de texto

Podemos seleccionar texto programáticamente con EM_SETSEL o EM_EXSETSEL. Los dos funcionan muy bien. El mensaje que escojamos depende del formato disponible para los índices de caracteres. Si ya están almacenados en una estructura CHARRANGE es mas facil usar EM_EXSETSEL.

	
EM_EXSETSEL wParam == no usado. Debe ser cero
 lParam == puntero a una estructura CHARRANGE que contiene el rango de caracteres que serán seleccionados.

Notificación de Eventos

En el caso de un control edit multilíneas, tienes subclases en orden a obtener mensajes de entrada tales como eventos del ratón/teclado. El control RichEdit provee un mejor esquema que notificará a la ventana padre de tales eventos. Para registrar las notificaciones, la ventana padre envía el mensaje EM_SETEVENTMASK al control RichEdit, especificando en cuáles eventos está interesado. EM_SETEVENTMASK tiene la siguiente sintaxis:

 EM_SETEVENTMASK 
wParam == no usado. Debe ser cero 
lParam == Valor de la mascara del evento. Puede ser una combinación de los siguientes banderas.
ENM_CHANGE
Envía notificaciones EN_CHANGE
ENM_CORRECTTEXT
Envía notificaciones EN_CORRECTTEXT
ENM_DRAGDROPDONE
Envía notificaciones EN_DRAGDROPDONE
ENM_DROPFILES
Envía notificaciones EN_DROPFILES
ENM_KEYEVENTS
Envía notificaciones EN_MSGFILTER para eventos de teclado
ENM_LINK
Rich Edit 2.0 y posterior: Envía notificaciones EN_LINK cuando el puntero del ratón está sobre el texto que tiene CFE_LINK y son realizadas una o varias acciones del ratón.
ENM_MOUSEEVENTS
Envía notificaciones EN_MSGFILTER para eventos del ratón
ENM_OBJECTPOSITIONS
Envía notificaciones EN_OBJECTPOSITIONS
ENM_PROTECTED
Envía notificaciones EN_PROTECTED
ENM_REQUESTRESIZE
Envía notificaciones EN_REQUESTRESIZE
ENM_SCROLL
Envía notificaciones EN_HSCROLL y EN_VSCROLL
ENM_SCROLLEVENTS
Envía notificaciones EN_MSGFILTER para eventos de la rueda del ratón
ENM_SELCHANGE
Envía notificaciones EN_SELCHANGE
ENM_UPDATE
Envía notificaciones EN_UPDATE
Rich Edit 2.0 and later: esta bandera [flag] es ignoradoay las notificaciones
EN_UPDATE son siempre enviadas. Sin embargo, si Rich Edit 3.0 emula a Rich Edit 1.0, debes usar esta bandera [flag] para enviar notificaciones EN_UPDATE

Todas las notificaciones de arriba seran enviadas como mensajes WM_NOTIFY:Tienes que mirar el código del miembro de la estructura NMHDR para el mensaje de notificación. Por ejemplo, si quieres registrar eventos del ratón (ej. quieres proveer un contexto sensitivo popup menu), debes hacer algo como esto:

 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

Ejemplo:

El siguiente ejemplo es la actualización del IczEdit en tutorial no. 33. añadido buscar/reemplazar funcionalidad y teclas aceleradoras al programa. También procesa los eventos del ratón y provee un menú emergente al hacer click en el botón derecho del ratón.

.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		; blanco por defecto
TextColor dd 0		; negro por defecto
hSearch dd ?		; handle al cuadro de diálogo buscar/reemplazar [search/replace]
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 hay selección en este momento
		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		; menú 'file'			
			.if FileOpened==TRUE	; ya está abierto un archivo
				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		; órdenes del menú
			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
						;================================================================
						; introduce el stream de texto en el control richedit
						;================================================================						
						mov editstream.dwCookie,eax
						mov editstream.pfnCallback,offset StreamInProc
						invoke SendMessage,hwndRichEdit,EM_STREAMIN,SF_TEXT,addr editstream
						;==========================================================
						; Inicializa el estado de notificación poniéndolo 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
					;==========================================================
					;  Lleva  el stream de texto al archivo 
                  ; ==========================================================						
					mov editstream.dwCookie,eax
					mov editstream.pfnCallback,offset StreamOutProc
					invoke SendMessage,hwndRichEdit,EM_STREAMOUT,SF_TEXT,addr editstream
					;==========================================================
					; Inicializa el estado de notificación poniéndolo 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

Análisis

La capacidad de búsqueda de texto es implementada con EM_FINDTEXTEX. Cuando el usuario hace click sobre el ítem de menú 'Find', el mensaje IDM_FIND es enviado y será mostrado el diálogo de búsqueda.

 invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer 
.if eax!=0

Cuando el usuario pone el texto de búsqueda y presiona el boton OK, obtenemos el texto a buscar en FindBuffer.

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

Si la cadena de texto no es NULL, continuamos para inicializar la variable uFlags a cero. Esta variable es usada para guardar las banderas [flags] de búsqueda usadas con EM_FINDTEXTEX. Tras esto, obtenemos la actual selección con EM_EXGETSEL ya que necesitamos conocer el punto de comienzo de la operación de búsqueda.

	
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

La siguiente parte es un poqueño truco. Miramos el botón 'radio' de dirección para saber que dirección de búsqueda debe tomar, si está indicada la dirección de búsqueda hacia abajo ponemos la bandera [flag] FR_DOWN a uFlags. Tra esto, miramos si una selección esta actualmente en efecto comparando los valores de cpMin y cpMax. Si estos valores no son iguales, significa que hay una selección y debemos continuar la búsqueda desde el final de esa selección hasta el final del texto en el control. Así necesitamos reemplazar el valor de cpMax con el de cpMin y cambiar el valor de cpMax a -1 (0FFFFFFFFh). Si no hay selección, el rango de búsqueda es desde la posición actual del cursor hasta el final del texto.

Si el usuario escoge buscar hacia arriba, usamos el rango desde el comienzo de la selección hasta el comienzo del texto. Esto es porque sólo modifica el valor de cpMax a cero. En el caso de búsqueda hacia arriba, cpMin contiene el índice del último caracter en el rango de búsqueda y cpMax el índice del primer caracter en el rango de búsqueda; es lo contrario de buscar hacia abajo.

 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

Continuamos para revisar los 'checkboxes' [casillas de chequeo] para los banderas [flags] de búsqueda, por ej. FR_MATCHCASE y FR_WHOLEWORD. Por ultimo ponemos el desplazamiento del texto a buscar en el miembro lpstrText.

      invoke SendMessage,hwndRichEdit,EM_FINDTEXTEX,uFlags,addr findtext 
     .if eax!=-1 
           invoke SendMessage,hwndRichEdit,EM_EXSETSEL,0,addr findtext.chrgText 
     .endif 
.endif

Ahora estamos listos para emitir EM_FINDTEXTEX. Después de esto, examinamos el resultado de la búsqueda devuelto por SendMessage. Si el valor de retorno es -1, no se encontraron coincidencias en el rango de búsqueda. De otra forma el miembro chrgText de la estructura FINDTEXTEX es llenado con el índice del caracter del texto que coincidió. Asi que procedemos a seleccionarlo con EM_EXSETSEL.

La operación de reemplazo se hace de la misma forma.

	
invoke GetDlgItemText,hWnd,IDC_FINDEDIT,addr FindBuffer,sizeof FindBuffer invoke 
GetDlgItemText,hWnd,IDC_REPLACEEDIT,addr ReplaceBuffer,sizeof ReplaceBuffer

Obtenemos el texto a buscar y el texto usado para hacer el reemplazo.

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

para hacerlo facil, la operación de reemplazo afecta a todo el texto en el control. Asi que el índice del comienzo es cero y el índice final es -1.

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

Inicializamos la estructura SETTEXTEX para indicar que queremos reemplazar la actual selección y usar la pagina de códigos por defecto del sistema.

 .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

Entramos en un bucle infinito buscando el texto coincidente. Si se encuentra alguno lo seleccionamos con EM_EXSETSEL y lo reemplazamos con EM_SETTEXTEX. Cuando no se encuentran mas , salimos del bicle.

Find Next y Find Prev usan el mensaje EM_FINDTEXTEX de manera similar a la operación de búsqueda.

Ahora examinaremos la caracteristica "Go to Line". Cuando el usuario hace click en el menu "Go To Line", mostramos el diálogo de abajo:

Cuando el usuario pone un numero de linea y presiona el boton Ok,comenzamos la operacion.

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

Obtiene el numero de linea del edit control

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

Obtiene el numero de lineas del control. Revisa si el número de linea especificado por el usuario esta fuera de rango.

 invoke SendMessage,hwndRichEdit,EM_LINEINDEX,LineNo,0

Si el número de línea es válido, queremos mover el cursor al primer caracter de esa línea. Asi que enviamos el mensaje EM_LINEINDEX al control RichEdit. Este mensaje devuelve el índice del primer caracter en la línea especificada. Enviamos el número de linea en wParam y en el retorno tenemos el caracter índice.

 invoke SendMessage,hwndRichEdit,EM_SETSEL,eax,eax

Para poner la actual selección esta vez usamos EM_SETSEL ya que el caracter índice ya no está en una estructura CHARRANGE así ahorramos dos instrucciones ( para poner estos índices en una estructura CHARRANGE ).

 invoke SetFocus,hwndRichEdit .endif

El cursor no será mostrado a menos que el control RichEdit tenga el foco. Así que llamaremos a SetFocus sobre él.


Índice

Next

[Iczelion's Win32 Assembly Homepage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r

Página personal de mnemox

www.000webhost.com