xref: /reactos/dll/win32/comctl32/edit.c (revision e5993f13)
1 /*
2  *	Edit control
3  *
4  *	Copyright  David W. Metcalfe, 1994
5  *	Copyright  William Magro, 1995, 1996
6  *	Copyright  Frans van Dorsselaer, 1996, 1997
7  *	Copyright  Frank Richter, 2005
8  *
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  *
24  * TODO:
25  *   - EDITBALLOONTIP structure
26  *   - EM_GETCUEBANNER/Edit_GetCueBannerText
27  *   - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
28  *   - EM_SETCUEBANNER/Edit_SetCueBannerText
29  *   - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
30  *   - EM_GETIMESTATUS, EM_SETIMESTATUS
31  *   - EN_ALIGN_LTR_EC
32  *   - EN_ALIGN_RTL_EC
33  *   - ES_OEMCONVERT
34  *
35  */
36 
37 #include "config.h"
38 
39 #include <stdarg.h>
40 #include <string.h>
41 #include <stdlib.h>
42 
43 #include "windef.h"
44 #include "winbase.h"
45 #include "wingdi.h"
46 #include "winuser.h"
47 #include "imm.h"
48 #ifdef __REACTOS__
49     #include <immdev.h>
50 #endif
51 #include "usp10.h"
52 #include "commctrl.h"
53 #include "uxtheme.h"
54 #include "vsstyle.h"
55 #include "wine/unicode.h"
56 #include "wine/debug.h"
57 #include "wine/heap.h"
58 
59 WINE_DEFAULT_DEBUG_CHANNEL(edit);
60 
61 #define BUFLIMIT_INITIAL    30000   /* initial buffer size */
62 #define GROWLENGTH		32	/* buffers granularity in bytes: must be power of 2 */
63 #define ROUND_TO_GROW(size)	(((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
64 #define HSCROLL_FRACTION	3	/* scroll window by 1/3 width */
65 
66 /*
67  *	extra flags for EDITSTATE.flags field
68  */
69 #define EF_MODIFIED		0x0001	/* text has been modified */
70 #define EF_FOCUSED		0x0002	/* we have input focus */
71 #define EF_UPDATE		0x0004	/* notify parent of changed state */
72 #define EF_VSCROLL_TRACK	0x0008	/* don't SetScrollPos() since we are tracking the thumb */
73 #define EF_HSCROLL_TRACK	0x0010	/* don't SetScrollPos() since we are tracking the thumb */
74 #define EF_AFTER_WRAP		0x0080	/* the caret is displayed after the last character of a
75 					   wrapped line, instead of in front of the next character */
76 #define EF_USE_SOFTBRK		0x0100	/* Enable soft breaks in text. */
77 #define EF_DIALOGMODE           0x0200  /* Indicates that we are inside a dialog window */
78 
79 #define ID_CB_LISTBOX 1000
80 
81 typedef enum
82 {
83 	END_0 = 0,			/* line ends with terminating '\0' character */
84 	END_WRAP,			/* line is wrapped */
85 	END_HARD,			/* line ends with a hard return '\r\n' */
86         END_SOFT,       		/* line ends with a soft return '\r\r\n' */
87         END_RICH        		/* line ends with a single '\n' */
88 } LINE_END;
89 
90 typedef struct tagLINEDEF {
91 	INT length;			/* bruto length of a line in bytes */
92 	INT net_length;			/* netto length of a line in visible characters */
93 	LINE_END ending;
94 	INT width;			/* width of the line in pixels */
95 	INT index; 			/* line index into the buffer */
96 	SCRIPT_STRING_ANALYSIS ssa;	/* Uniscribe Data */
97 	struct tagLINEDEF *next;
98 } LINEDEF;
99 
100 typedef struct
101 {
102 	LPWSTR text;			/* the actual contents of the control */
103         UINT text_length;               /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
104 	UINT buffer_size;		/* the size of the buffer in characters */
105 	UINT buffer_limit;		/* the maximum size to which the buffer may grow in characters */
106 	HFONT font;			/* NULL means standard system font */
107 	INT x_offset;			/* scroll offset	for multi lines this is in pixels
108 								for single lines it's in characters */
109 #ifdef __REACTOS__
110     DWORD dwCaretWidth;
111 #endif
112 	INT line_height;		/* height of a screen line in pixels */
113 	INT char_width;			/* average character width in pixels */
114 	DWORD style;			/* sane version of wnd->dwStyle */
115 	WORD flags;			/* flags that are not in es->style or wnd->flags (EF_XXX) */
116 	INT undo_insert_count;		/* number of characters inserted in sequence */
117 	UINT undo_position;		/* character index of the insertion and deletion */
118 	LPWSTR undo_text;		/* deleted text */
119 	UINT undo_buffer_size;		/* size of the deleted text buffer */
120 	INT selection_start;		/* == selection_end if no selection */
121 	INT selection_end;		/* == current caret position */
122 	WCHAR password_char;		/* == 0 if no password char, and for multi line controls */
123 	INT left_margin;		/* in pixels */
124 	INT right_margin;		/* in pixels */
125 	RECT format_rect;
126 	INT text_width;			/* width of the widest line in pixels for multi line controls
127 					   and just line width for single line controls	*/
128 	INT region_posx;		/* Position of cursor relative to region: */
129 	INT region_posy;		/* -1: to left, 0: within, 1: to right */
130 	EDITWORDBREAKPROCW word_break_proc;
131 	INT line_count;			/* number of lines */
132 	INT y_offset;			/* scroll offset in number of lines */
133 	BOOL bCaptureState; 		/* flag indicating whether mouse was captured */
134 	BOOL bEnableState;		/* flag keeping the enable state */
135 	HWND hwndSelf;			/* the our window handle */
136 	HWND hwndParent;		/* Handle of parent for sending EN_* messages.
137 				           Even if parent will change, EN_* messages
138 					   should be sent to the first parent. */
139 	HWND hwndListBox;		/* handle of ComboBox's listbox or NULL */
140 	INT wheelDeltaRemainder;        /* scroll wheel delta left over after scrolling whole lines */
141 	/*
142 	 *	only for multi line controls
143 	 */
144 	INT lock_count;			/* amount of re-entries in the EditWndProc */
145 	INT tabs_count;
146 	LPINT tabs;
147 	LINEDEF *first_line_def;	/* linked list of (soft) linebreaks */
148 	HLOCAL hloc32W;			/* our unicode local memory block */
149         HLOCAL hlocapp;                 /* The text buffer handle belongs to the app */
150 	/*
151 	 * IME Data
152 	 */
153 #ifndef __REACTOS__ /* Rely on the composition window */
154 	UINT composition_len;   /* length of composition, 0 == no composition */
155 	int composition_start;  /* the character position for the composition */
156 #endif
157 	/*
158 	 * Uniscribe Data
159 	 */
160 	SCRIPT_LOGATTR *logAttr;
161 	SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data for single line controls */
162 } EDITSTATE;
163 
164 
165 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
166 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
167 
168 static inline BOOL notify_parent(const EDITSTATE *es, INT code)
169 {
170     HWND hwnd = es->hwndSelf;
171     TRACE("notification %d sent to %p.\n", code, es->hwndParent);
172     SendMessageW(es->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLongPtrW(es->hwndSelf, GWLP_ID), code), (LPARAM)es->hwndSelf);
173     return IsWindow(hwnd);
174 }
175 
176 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
177 
178 /*********************************************************************
179  *
180  *	EM_CANUNDO
181  *
182  */
183 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
184 {
185 	return (es->undo_insert_count || strlenW(es->undo_text));
186 }
187 
188 
189 /*********************************************************************
190  *
191  *	EM_EMPTYUNDOBUFFER
192  *
193  */
194 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
195 {
196 	es->undo_insert_count = 0;
197 	*es->undo_text = '\0';
198 }
199 
200 static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
201 {
202     HBRUSH hbrush;
203     UINT msg;
204 
205     if ((!es->bEnableState || (es->style & ES_READONLY)))
206         msg = WM_CTLCOLORSTATIC;
207     else
208         msg = WM_CTLCOLOREDIT;
209 
210     /* Why do we notify to es->hwndParent, and we send this one to GetParent()? */
211     hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
212     if (!hbrush)
213         hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
214     return hbrush;
215 }
216 
217 
218 static inline UINT get_text_length(EDITSTATE *es)
219 {
220     if(es->text_length == (UINT)-1)
221         es->text_length = strlenW(es->text);
222     return es->text_length;
223 }
224 
225 
226 /*********************************************************************
227  *
228  *	EDIT_WordBreakProc
229  *
230  *	Find the beginning of words.
231  *	Note:	unlike the specs for a WordBreakProc, this function can
232  *		only be called without linebreaks between s[0] up to
233  *		s[count - 1].  Remember it is only called
234  *		internally, so we can decide this for ourselves.
235  *		Additionally we will always be breaking the full string.
236  *
237  */
238 static INT EDIT_WordBreakProc(EDITSTATE *es, LPWSTR s, INT index, INT count, INT action)
239 {
240     INT ret = 0;
241 
242     TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
243 
244     if(!s) return 0;
245 
246     if (!es->logAttr)
247     {
248         SCRIPT_ANALYSIS psa;
249 
250         memset(&psa,0,sizeof(SCRIPT_ANALYSIS));
251         psa.eScript = SCRIPT_UNDEFINED;
252 
253         es->logAttr = heap_alloc(sizeof(SCRIPT_LOGATTR) * get_text_length(es));
254         ScriptBreak(es->text, get_text_length(es), &psa, es->logAttr);
255     }
256 
257     switch (action) {
258     case WB_LEFT:
259         if (index)
260             index--;
261         while (index && !es->logAttr[index].fSoftBreak)
262             index--;
263         ret = index;
264         break;
265     case WB_RIGHT:
266         if (!count)
267             break;
268         while (index < count && s[index] && !es->logAttr[index].fSoftBreak)
269             index++;
270         ret = index;
271         break;
272     case WB_ISDELIMITER:
273         ret = es->logAttr[index].fWhiteSpace;
274         break;
275     default:
276         ERR("unknown action code, please report !\n");
277         break;
278     }
279     return ret;
280 }
281 
282 
283 /*********************************************************************
284  *
285  *	EDIT_CallWordBreakProc
286  *
287  *	Call appropriate WordBreakProc (internal or external).
288  *
289  *	Note: The "start" argument should always be an index referring
290  *		to es->text.  The actual wordbreak proc might be
291  *		16 bit, so we can't always pass any 32 bit LPSTR.
292  *		Hence we assume that es->text is the buffer that holds
293  *		the string under examination (we can decide this for ourselves).
294  *
295  */
296 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
297 {
298     INT ret;
299 
300     if (es->word_break_proc)
301         ret = es->word_break_proc(es->text + start, index, count, action);
302     else
303         ret = EDIT_WordBreakProc(es, es->text, index + start, count + start, action) - start;
304 
305     return ret;
306 }
307 
308 static inline void EDIT_InvalidateUniscribeData_linedef(LINEDEF *line_def)
309 {
310 	if (line_def->ssa)
311 	{
312 		ScriptStringFree(&line_def->ssa);
313 		line_def->ssa = NULL;
314 	}
315 }
316 
317 static inline void EDIT_InvalidateUniscribeData(EDITSTATE *es)
318 {
319 	LINEDEF *line_def = es->first_line_def;
320 	while (line_def)
321 	{
322 		EDIT_InvalidateUniscribeData_linedef(line_def);
323 		line_def = line_def->next;
324 	}
325 	if (es->ssa)
326 	{
327 		ScriptStringFree(&es->ssa);
328 		es->ssa = NULL;
329 	}
330 }
331 
332 static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData_linedef(EDITSTATE *es, HDC dc, LINEDEF *line_def)
333 {
334 	if (!line_def)
335 		return NULL;
336 
337 	if (line_def->net_length && !line_def->ssa)
338 	{
339 		int index = line_def->index;
340 		HFONT old_font = NULL;
341 		HDC udc = dc;
342 		SCRIPT_TABDEF tabdef;
343 		HRESULT hr;
344 
345 		if (!udc)
346 			udc = GetDC(es->hwndSelf);
347 		if (es->font)
348 			old_font = SelectObject(udc, es->font);
349 
350 		tabdef.cTabStops = es->tabs_count;
351 		tabdef.iScale = GdiGetCharDimensions(udc, NULL, NULL);
352 		tabdef.pTabStops = es->tabs;
353 		tabdef.iTabOrigin = 0;
354 
355 		hr = ScriptStringAnalyse(udc, &es->text[index], line_def->net_length,
356                                          (1.5*line_def->net_length+16), -1,
357                                          SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_TAB, -1,
358                                          NULL, NULL, NULL, &tabdef, NULL, &line_def->ssa);
359 		if (FAILED(hr))
360 		{
361 			WARN("ScriptStringAnalyse failed (%x)\n",hr);
362 			line_def->ssa = NULL;
363 		}
364 
365 		if (es->font)
366 			SelectObject(udc, old_font);
367 		if (udc != dc)
368 			ReleaseDC(es->hwndSelf, udc);
369 	}
370 
371 	return line_def->ssa;
372 }
373 
374 static SCRIPT_STRING_ANALYSIS EDIT_UpdateUniscribeData(EDITSTATE *es, HDC dc, INT line)
375 {
376 	LINEDEF *line_def;
377 
378 	if (!(es->style & ES_MULTILINE))
379 	{
380 		if (!es->ssa)
381 		{
382 			INT length = get_text_length(es);
383 			HFONT old_font = NULL;
384 			HDC udc = dc;
385 
386 			if (!udc)
387 				udc = GetDC(es->hwndSelf);
388 			if (es->font)
389 				old_font = SelectObject(udc, es->font);
390 
391 			if (es->style & ES_PASSWORD)
392 				ScriptStringAnalyse(udc, &es->password_char, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_PASSWORD, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
393 			else
394 				ScriptStringAnalyse(udc, es->text, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
395 
396 			if (es->font)
397 				SelectObject(udc, old_font);
398 			if (udc != dc)
399 				ReleaseDC(es->hwndSelf, udc);
400 		}
401 		return es->ssa;
402 	}
403 	else
404 	{
405 		line_def = es->first_line_def;
406 		while (line_def && line)
407 		{
408 			line_def = line_def->next;
409 			line--;
410 		}
411 
412 		return EDIT_UpdateUniscribeData_linedef(es,dc,line_def);
413 	}
414 }
415 
416 static inline INT get_vertical_line_count(EDITSTATE *es)
417 {
418 	INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
419 	return max(1,vlc);
420 }
421 
422 /*********************************************************************
423  *
424  *	EDIT_BuildLineDefs_ML
425  *
426  *	Build linked list of text lines.
427  *	Lines can end with '\0' (last line), a character (if it is wrapped),
428  *	a soft return '\r\r\n' or a hard return '\r\n'
429  *
430  */
431 static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
432 {
433 	LPWSTR current_position, cp;
434 	INT fw;
435 	LINEDEF *current_line;
436 	LINEDEF *previous_line;
437 	LINEDEF *start_line;
438 	INT line_index = 0, nstart_line, nstart_index;
439 	INT line_count = es->line_count;
440 	INT orig_net_length;
441 	RECT rc;
442 	INT vlc;
443 
444 	if (istart == iend && delta == 0)
445 		return;
446 
447 	previous_line = NULL;
448 	current_line = es->first_line_def;
449 
450 	/* Find starting line. istart must lie inside an existing line or
451 	 * at the end of buffer */
452 	do {
453 		if (istart < current_line->index + current_line->length ||
454 				current_line->ending == END_0)
455 			break;
456 
457 		previous_line = current_line;
458 		current_line = current_line->next;
459 		line_index++;
460 	} while (current_line);
461 
462 	if (!current_line) /* Error occurred start is not inside previous buffer */
463 	{
464 		FIXME(" modification occurred outside buffer\n");
465 		return;
466 	}
467 
468 	/* Remember start of modifications in order to calculate update region */
469 	nstart_line = line_index;
470 	nstart_index = current_line->index;
471 
472 	/* We must start to reformat from the previous line since the modifications
473 	 * may have caused the line to wrap upwards. */
474 	if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
475 	{
476 		line_index--;
477 		current_line = previous_line;
478 	}
479 	start_line = current_line;
480 
481 	fw = es->format_rect.right - es->format_rect.left;
482 	current_position = es->text + current_line->index;
483 	vlc = get_vertical_line_count(es);
484 	do {
485 		if (current_line != start_line)
486 		{
487 			if (!current_line || current_line->index + delta > current_position - es->text)
488 			{
489 				/* The buffer has been expanded, create a new line and
490 				   insert it into the link list */
491 				LINEDEF *new_line = heap_alloc_zero(sizeof(*new_line));
492 				new_line->next = previous_line->next;
493 				previous_line->next = new_line;
494 				current_line = new_line;
495 				es->line_count++;
496 			}
497 			else if (current_line->index + delta < current_position - es->text)
498 			{
499 				/* The previous line merged with this line so we delete this extra entry */
500 				previous_line->next = current_line->next;
501 				heap_free(current_line);
502 				current_line = previous_line->next;
503 				es->line_count--;
504 				continue;
505 			}
506 			else /* current_line->index + delta == current_position */
507 			{
508 				if (current_position - es->text > iend)
509 					break; /* We reached end of line modifications */
510 				/* else recalculate this line */
511 			}
512 		}
513 
514 		current_line->index = current_position - es->text;
515 		orig_net_length = current_line->net_length;
516 
517 		/* Find end of line */
518 		cp = current_position;
519 		while (*cp) {
520                     if (*cp == '\n') break;
521                     if ((*cp == '\r') && (*(cp + 1) == '\n'))
522                         break;
523                     cp++;
524 		}
525 
526 		/* Mark type of line termination */
527 		if (!(*cp)) {
528 			current_line->ending = END_0;
529 			current_line->net_length = strlenW(current_position);
530 		} else if ((cp > current_position) && (*(cp - 1) == '\r')) {
531 			current_line->ending = END_SOFT;
532 			current_line->net_length = cp - current_position - 1;
533                 } else if (*cp == '\n') {
534 			current_line->ending = END_RICH;
535 			current_line->net_length = cp - current_position;
536 		} else {
537 			current_line->ending = END_HARD;
538 			current_line->net_length = cp - current_position;
539 		}
540 
541 		if (current_line->net_length)
542 		{
543 			const SIZE *sz;
544 			EDIT_InvalidateUniscribeData_linedef(current_line);
545 			EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
546 			if (current_line->ssa)
547 			{
548 				sz = ScriptString_pSize(current_line->ssa);
549 				/* Calculate line width */
550 				current_line->width = sz->cx;
551 			}
552 			else current_line->width = es->char_width * current_line->net_length;
553 		}
554 		else current_line->width = 0;
555 
556 		/* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
557 
558 /* Line breaks just look back from the end and find the next break and try that. */
559 
560 		if (!(es->style & ES_AUTOHSCROLL)) {
561 		   if (current_line->width > fw && fw > es->char_width) {
562 
563 			INT prev, next;
564 			int w;
565 			const SIZE *sz;
566 			float d;
567 
568 			prev = current_line->net_length - 1;
569 			w = current_line->net_length;
570 			d = (float)current_line->width/(float)fw;
571 			if (d > 1.2f) d -= 0.2f;
572 			next = prev/d;
573 			if (next >= prev) next = prev-1;
574 			do {
575 				prev = EDIT_CallWordBreakProc(es, current_position - es->text,
576 						next, current_line->net_length, WB_LEFT);
577 				current_line->net_length = prev;
578 				EDIT_InvalidateUniscribeData_linedef(current_line);
579 				EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
580 				if (current_line->ssa)
581 					sz = ScriptString_pSize(current_line->ssa);
582 				else sz = 0;
583 				if (sz)
584 					current_line->width = sz->cx;
585 				else
586 					prev = 0;
587 				next = prev - 1;
588 			} while (prev && current_line->width > fw);
589 			current_line->net_length = w;
590 
591 			if (prev == 0) { /* Didn't find a line break so force a break */
592 				INT *piDx;
593 				const INT *count;
594 
595 				EDIT_InvalidateUniscribeData_linedef(current_line);
596 				EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
597 
598 				if (current_line->ssa)
599 				{
600 					count = ScriptString_pcOutChars(current_line->ssa);
601 					piDx = heap_alloc(sizeof(INT) * (*count));
602 					ScriptStringGetLogicalWidths(current_line->ssa,piDx);
603 
604 					prev = current_line->net_length-1;
605 					do {
606 						current_line->width -= piDx[prev];
607 						prev--;
608 					} while ( prev > 0 && current_line->width > fw);
609 					if (prev<=0)
610 						prev = 1;
611 					heap_free(piDx);
612 				}
613 				else
614 					prev = (fw / es->char_width);
615 			}
616 
617 			/* If the first line we are calculating, wrapped before istart, we must
618 			 * adjust istart in order for this to be reflected in the update region. */
619 			if (current_line->index == nstart_index && istart > current_line->index + prev)
620 				istart = current_line->index + prev;
621 			/* else if we are updating the previous line before the first line we
622 			 * are re-calculating and it expanded */
623 			else if (current_line == start_line &&
624 					current_line->index != nstart_index && orig_net_length < prev)
625 			{
626 			  /* Line expanded due to an upwards line wrap so we must partially include
627 			   * previous line in update region */
628 				nstart_line = line_index;
629 				nstart_index = current_line->index;
630 				istart = current_line->index + orig_net_length;
631 			}
632 
633 			current_line->net_length = prev;
634 			current_line->ending = END_WRAP;
635 
636 			if (current_line->net_length > 0)
637 			{
638 				EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
639 				if (current_line->ssa)
640 				{
641 					sz = ScriptString_pSize(current_line->ssa);
642 					current_line->width = sz->cx;
643 				}
644 				else
645 					current_line->width = 0;
646 			}
647 			else current_line->width = 0;
648 		    }
649 		    else if (current_line == start_line &&
650                              current_line->index != nstart_index &&
651                              orig_net_length < current_line->net_length) {
652 			/* The previous line expanded but it's still not as wide as the client rect */
653 			/* The expansion is due to an upwards line wrap so we must partially include
654 			   it in the update region */
655 			nstart_line = line_index;
656 			nstart_index = current_line->index;
657 			istart = current_line->index + orig_net_length;
658 		    }
659 		}
660 
661 
662 		/* Adjust length to include line termination */
663 		switch (current_line->ending) {
664 		case END_SOFT:
665 			current_line->length = current_line->net_length + 3;
666 			break;
667                 case END_RICH:
668 			current_line->length = current_line->net_length + 1;
669 			break;
670 		case END_HARD:
671 			current_line->length = current_line->net_length + 2;
672 			break;
673 		case END_WRAP:
674 		case END_0:
675 			current_line->length = current_line->net_length;
676 			break;
677 		}
678 		es->text_width = max(es->text_width, current_line->width);
679 		current_position += current_line->length;
680 		previous_line = current_line;
681 
682 		/* Discard data for non-visible lines. It will be calculated as needed */
683 		if ((line_index < es->y_offset) || (line_index > es->y_offset + vlc))
684 			EDIT_InvalidateUniscribeData_linedef(current_line);
685 
686 		current_line = current_line->next;
687 		line_index++;
688 	} while (previous_line->ending != END_0);
689 
690 	/* Finish adjusting line indexes by delta or remove hanging lines */
691 	if (previous_line->ending == END_0)
692 	{
693 		LINEDEF *pnext = NULL;
694 
695 		previous_line->next = NULL;
696 		while (current_line)
697 		{
698 			pnext = current_line->next;
699 			EDIT_InvalidateUniscribeData_linedef(current_line);
700 			heap_free(current_line);
701 			current_line = pnext;
702 			es->line_count--;
703 		}
704 	}
705 	else if (delta != 0)
706 	{
707 		while (current_line)
708 		{
709 			current_line->index += delta;
710 			current_line = current_line->next;
711 		}
712 	}
713 
714 	/* Calculate rest of modification rectangle */
715 	if (hrgn)
716 	{
717 		HRGN tmphrgn;
718 	   /*
719 		* We calculate two rectangles. One for the first line which may have
720 		* an indent with respect to the format rect. The other is a format-width
721 		* rectangle that spans the rest of the lines that changed or moved.
722 		*/
723 		rc.top = es->format_rect.top + nstart_line * es->line_height -
724 			(es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
725 		rc.bottom = rc.top + es->line_height;
726 		if ((es->style & ES_CENTER) || (es->style & ES_RIGHT))
727 			rc.left = es->format_rect.left;
728 		else
729 #ifdef __REACTOS__ /* CORE-11475 */
730 			rc.left = (short)LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
731 #else
732                         rc.left = LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
733 #endif
734 		rc.right = es->format_rect.right;
735 		SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
736 
737 		rc.top = rc.bottom;
738 		rc.left = es->format_rect.left;
739 		rc.right = es->format_rect.right;
740 	   /*
741 		* If lines were added or removed we must re-paint the remainder of the
742 	    * lines since the remaining lines were either shifted up or down.
743 		*/
744 		if (line_count < es->line_count) /* We added lines */
745 			rc.bottom = es->line_count * es->line_height;
746 		else if (line_count > es->line_count) /* We removed lines */
747 			rc.bottom = line_count * es->line_height;
748 		else
749 			rc.bottom = line_index * es->line_height;
750 		rc.bottom += es->format_rect.top;
751 		rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
752 		tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
753 		CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
754 		DeleteObject(tmphrgn);
755 	}
756 }
757 
758 /*********************************************************************
759  *
760  *	EDIT_CalcLineWidth_SL
761  *
762  */
763 static void EDIT_CalcLineWidth_SL(EDITSTATE *es)
764 {
765 	EDIT_UpdateUniscribeData(es, NULL, 0);
766 	if (es->ssa)
767 	{
768 		const SIZE *size;
769 		size = ScriptString_pSize(es->ssa);
770 		es->text_width = size->cx;
771 	}
772 	else
773 		es->text_width = 0;
774 }
775 
776 /*********************************************************************
777  *
778  *	EDIT_CharFromPos
779  *
780  *	Beware: This is not the function called on EM_CHARFROMPOS
781  *		The position _can_ be outside the formatting / client
782  *		rectangle
783  *		The return value is only the character index
784  *
785  */
786 static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
787 {
788 	INT index;
789 
790 	if (es->style & ES_MULTILINE) {
791 		int trailing;
792 		INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
793 		INT line_index = 0;
794 		LINEDEF *line_def = es->first_line_def;
795 		EDIT_UpdateUniscribeData(es, NULL, line);
796 		while ((line > 0) && line_def->next) {
797 			line_index += line_def->length;
798 			line_def = line_def->next;
799 			line--;
800 		}
801 
802 		x += es->x_offset - es->format_rect.left;
803 		if (es->style & ES_RIGHT)
804 			x -= (es->format_rect.right - es->format_rect.left) - line_def->width;
805 		else if (es->style & ES_CENTER)
806 			x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2;
807 		if (x >= line_def->width) {
808 			if (after_wrap)
809 				*after_wrap = (line_def->ending == END_WRAP);
810 			return line_index + line_def->net_length;
811 		}
812 		if (x <= 0 || !line_def->ssa) {
813 			if (after_wrap)
814 				*after_wrap = FALSE;
815 			return line_index;
816 		}
817 
818 		ScriptStringXtoCP(line_def->ssa, x , &index, &trailing);
819 		if (trailing) index++;
820 		index += line_index;
821 		if (after_wrap)
822 			*after_wrap = ((index == line_index + line_def->net_length) &&
823 							(line_def->ending == END_WRAP));
824 	} else {
825 		INT xoff = 0;
826 		INT trailing;
827 		if (after_wrap)
828 			*after_wrap = FALSE;
829 		x -= es->format_rect.left;
830 		if (!x)
831 			return es->x_offset;
832 
833 		if (!es->x_offset)
834 		{
835 			INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width;
836 			if (es->style & ES_RIGHT)
837 				x -= indent;
838 			else if (es->style & ES_CENTER)
839 				x -= indent / 2;
840 		}
841 
842 		EDIT_UpdateUniscribeData(es, NULL, 0);
843 		if (es->x_offset)
844 		{
845 			if (es->ssa)
846 			{
847 				if (es->x_offset>= get_text_length(es))
848 				{
849 					const SIZE *size;
850 					size = ScriptString_pSize(es->ssa);
851 					xoff = size->cx;
852 				}
853 				ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
854 			}
855 			else
856 				xoff = 0;
857 		}
858 		if (x < 0)
859 		{
860 			if (x + xoff > 0 || !es->ssa)
861 			{
862 				ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
863 				if (trailing) index++;
864 			}
865 			else
866 				index = 0;
867 		}
868 		else
869 		{
870 			if (x)
871 			{
872 				const SIZE *size = NULL;
873 				if (es->ssa)
874 					size = ScriptString_pSize(es->ssa);
875 				if (!size)
876 					index = 0;
877 				else if (x > size->cx)
878 					index = get_text_length(es);
879 				else if (es->ssa)
880 				{
881 					ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
882 					if (trailing) index++;
883 				}
884 				else
885 					index = 0;
886 			}
887 			else
888 				index = es->x_offset;
889 		}
890 	}
891 	return index;
892 }
893 
894 
895 /*********************************************************************
896  *
897  *	EDIT_ConfinePoint
898  *
899  *	adjusts the point to be within the formatting rectangle
900  *	(so CharFromPos returns the nearest _visible_ character)
901  *
902  */
903 static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y)
904 {
905 	*x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
906 	*y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
907 }
908 
909 
910 /*********************************************************************
911  *
912  *	EM_LINEFROMCHAR
913  *
914  */
915 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
916 {
917 	INT line;
918 	LINEDEF *line_def;
919 
920 	if (!(es->style & ES_MULTILINE))
921 		return 0;
922 	if (index > (INT)get_text_length(es))
923 		return es->line_count - 1;
924 	if (index == -1)
925 		index = min(es->selection_start, es->selection_end);
926 
927 	line = 0;
928 	line_def = es->first_line_def;
929 	index -= line_def->length;
930 	while ((index >= 0) && line_def->next) {
931 		line++;
932 		line_def = line_def->next;
933 		index -= line_def->length;
934 	}
935 	return line;
936 }
937 
938 
939 /*********************************************************************
940  *
941  *	EM_LINEINDEX
942  *
943  */
944 static INT EDIT_EM_LineIndex(const EDITSTATE *es, INT line)
945 {
946 	INT line_index;
947 	const LINEDEF *line_def;
948 
949 	if (!(es->style & ES_MULTILINE))
950 		return 0;
951 	if (line >= es->line_count)
952 		return -1;
953 
954 	line_index = 0;
955 	line_def = es->first_line_def;
956 	if (line == -1) {
957 		INT index = es->selection_end - line_def->length;
958 		while ((index >= 0) && line_def->next) {
959 			line_index += line_def->length;
960 			line_def = line_def->next;
961 			index -= line_def->length;
962 		}
963 	} else {
964 		while (line > 0) {
965 			line_index += line_def->length;
966 			line_def = line_def->next;
967 			line--;
968 		}
969 	}
970 	return line_index;
971 }
972 
973 
974 /*********************************************************************
975  *
976  *	EM_LINELENGTH
977  *
978  */
979 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
980 {
981 	LINEDEF *line_def;
982 
983 	if (!(es->style & ES_MULTILINE))
984 		return get_text_length(es);
985 
986 	if (index == -1) {
987 		/* get the number of remaining non-selected chars of selected lines */
988 		INT32 l; /* line number */
989 		INT32 li; /* index of first char in line */
990 		INT32 count;
991 		l = EDIT_EM_LineFromChar(es, es->selection_start);
992 		/* # chars before start of selection area */
993 		count = es->selection_start - EDIT_EM_LineIndex(es, l);
994 		l = EDIT_EM_LineFromChar(es, es->selection_end);
995 		/* # chars after end of selection */
996 		li = EDIT_EM_LineIndex(es, l);
997 		count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
998 		return count;
999 	}
1000 	line_def = es->first_line_def;
1001 	index -= line_def->length;
1002 	while ((index >= 0) && line_def->next) {
1003 		line_def = line_def->next;
1004 		index -= line_def->length;
1005 	}
1006 	return line_def->net_length;
1007 }
1008 
1009 
1010 /*********************************************************************
1011  *
1012  *	EM_POSFROMCHAR
1013  *
1014  */
1015 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap)
1016 {
1017 	INT len = get_text_length(es);
1018 	INT l;
1019 	INT li;
1020 	INT x = 0;
1021 	INT y = 0;
1022 	INT w;
1023 	INT lw;
1024 	LINEDEF *line_def;
1025 
1026 	index = min(index, len);
1027 	if (es->style & ES_MULTILINE) {
1028 		l = EDIT_EM_LineFromChar(es, index);
1029 		EDIT_UpdateUniscribeData(es, NULL, l);
1030 
1031 		y = (l - es->y_offset) * es->line_height;
1032 		li = EDIT_EM_LineIndex(es, l);
1033 		if (after_wrap && (li == index) && l) {
1034 			INT l2 = l - 1;
1035 			line_def = es->first_line_def;
1036 			while (l2) {
1037 				line_def = line_def->next;
1038 				l2--;
1039 			}
1040 			if (line_def->ending == END_WRAP) {
1041 				l--;
1042 				y -= es->line_height;
1043 				li = EDIT_EM_LineIndex(es, l);
1044 			}
1045 		}
1046 
1047 		line_def = es->first_line_def;
1048 		while (line_def->index != li)
1049 			line_def = line_def->next;
1050 
1051 		lw = line_def->width;
1052 		w = es->format_rect.right - es->format_rect.left;
1053 		if (line_def->ssa)
1054 		{
1055 			ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x);
1056 			x -= es->x_offset;
1057 		}
1058 		else
1059 #ifdef __REACTOS__ /* CORE-15780 */
1060 			x = (lw > 0 ? es->x_offset : x - es->x_offset);
1061 #else
1062 			x = es->x_offset;
1063 #endif
1064 
1065 		if (es->style & ES_RIGHT)
1066 			x = w - (lw - x);
1067 		else if (es->style & ES_CENTER)
1068 			x += (w - lw) / 2;
1069 	} else {
1070 		INT xoff = 0;
1071 		INT xi = 0;
1072 		EDIT_UpdateUniscribeData(es, NULL, 0);
1073 		if (es->x_offset)
1074 		{
1075 			if (es->ssa)
1076 			{
1077 				if (es->x_offset >= get_text_length(es))
1078 				{
1079 					int leftover = es->x_offset - get_text_length(es);
1080 					if (es->ssa)
1081 					{
1082 						const SIZE *size;
1083 						size = ScriptString_pSize(es->ssa);
1084 						xoff = size->cx;
1085 					}
1086 					else
1087 						xoff = 0;
1088 					xoff += es->char_width * leftover;
1089 				}
1090 				else
1091 					ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
1092 			}
1093 			else
1094 				xoff = 0;
1095 		}
1096 		if (index)
1097 		{
1098 			if (index >= get_text_length(es))
1099 			{
1100 				if (es->ssa)
1101 				{
1102 					const SIZE *size;
1103 					size = ScriptString_pSize(es->ssa);
1104 					xi = size->cx;
1105 				}
1106 				else
1107 					xi = 0;
1108 			}
1109 			else if (es->ssa)
1110 				ScriptStringCPtoX(es->ssa, index, FALSE, &xi);
1111 			else
1112 				xi = 0;
1113 		}
1114 		x = xi - xoff;
1115 
1116 		if (index >= es->x_offset) {
1117 			if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER)))
1118 			{
1119 				w = es->format_rect.right - es->format_rect.left;
1120 				if (w > es->text_width)
1121 				{
1122 					if (es->style & ES_RIGHT)
1123 						x += w - es->text_width;
1124 					else if (es->style & ES_CENTER)
1125 						x += (w - es->text_width) / 2;
1126 				}
1127 			}
1128 		}
1129 		y = 0;
1130 	}
1131 	x += es->format_rect.left;
1132 	y += es->format_rect.top;
1133 	return MAKELONG((INT16)x, (INT16)y);
1134 }
1135 
1136 
1137 /*********************************************************************
1138  *
1139  *	EDIT_GetLineRect
1140  *
1141  *	Calculates the bounding rectangle for a line from a starting
1142  *	column to an ending column.
1143  *
1144  */
1145 static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1146 {
1147 	SCRIPT_STRING_ANALYSIS ssa;
1148 	INT line_index = 0;
1149 	INT pt1, pt2, pt3;
1150 
1151 	if (es->style & ES_MULTILINE)
1152 	{
1153 		const LINEDEF *line_def = NULL;
1154 		rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1155 		if (line >= es->line_count)
1156 			return;
1157 
1158 		line_def = es->first_line_def;
1159 		if (line == -1) {
1160 			INT index = es->selection_end - line_def->length;
1161 			while ((index >= 0) && line_def->next) {
1162 				line_index += line_def->length;
1163 				line_def = line_def->next;
1164 				index -= line_def->length;
1165 			}
1166 		} else {
1167 			while (line > 0) {
1168 				line_index += line_def->length;
1169 				line_def = line_def->next;
1170 				line--;
1171 			}
1172 		}
1173 		ssa = line_def->ssa;
1174 	}
1175 	else
1176 	{
1177 		line_index = 0;
1178 		rc->top = es->format_rect.top;
1179 		ssa = es->ssa;
1180 	}
1181 
1182 	rc->bottom = rc->top + es->line_height;
1183 	pt1 = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
1184 	pt2 = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
1185 	if (ssa)
1186 	{
1187 		ScriptStringCPtoX(ssa, scol, FALSE, &pt3);
1188 		pt3+=es->format_rect.left;
1189 	}
1190 	else pt3 = pt1;
1191 	rc->right = max(max(pt1 , pt2),pt3);
1192 	rc->left = min(min(pt1, pt2),pt3);
1193 }
1194 
1195 
1196 static inline void text_buffer_changed(EDITSTATE *es)
1197 {
1198     es->text_length = (UINT)-1;
1199 
1200     heap_free( es->logAttr );
1201     es->logAttr = NULL;
1202     EDIT_InvalidateUniscribeData(es);
1203 }
1204 
1205 /*********************************************************************
1206  * EDIT_LockBuffer
1207  *
1208  */
1209 static void EDIT_LockBuffer(EDITSTATE *es)
1210 {
1211     if (!es->text)
1212     {
1213         if (!es->hloc32W)
1214             return;
1215 
1216         es->text = LocalLock(es->hloc32W);
1217     }
1218 
1219     es->lock_count++;
1220 }
1221 
1222 
1223 /*********************************************************************
1224  *
1225  *	EDIT_UnlockBuffer
1226  *
1227  */
1228 static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force)
1229 {
1230     /* Edit window might be already destroyed */
1231     if (!IsWindow(es->hwndSelf))
1232     {
1233         WARN("edit hwnd %p already destroyed\n", es->hwndSelf);
1234         return;
1235     }
1236 
1237     if (!es->lock_count)
1238     {
1239         ERR("lock_count == 0 ... please report\n");
1240         return;
1241     }
1242 
1243     if (!es->text)
1244     {
1245         ERR("es->text == 0 ... please report\n");
1246         return;
1247     }
1248 
1249     if (force || (es->lock_count == 1))
1250     {
1251         if (es->hloc32W)
1252         {
1253             LocalUnlock(es->hloc32W);
1254             es->text = NULL;
1255         }
1256         else
1257         {
1258             ERR("no buffer ... please report\n");
1259             return;
1260         }
1261 
1262     }
1263 
1264     es->lock_count--;
1265 }
1266 
1267 
1268 /*********************************************************************
1269  *
1270  *	EDIT_MakeFit
1271  *
1272  * Try to fit size + 1 characters in the buffer.
1273  */
1274 static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size)
1275 {
1276 	HLOCAL hNew32W;
1277 
1278 	if (size <= es->buffer_size)
1279 		return TRUE;
1280 
1281 	TRACE("trying to ReAlloc to %d+1 characters\n", size);
1282 
1283         /* Force edit to unlock its buffer. es->text now NULL */
1284 	EDIT_UnlockBuffer(es, TRUE);
1285 
1286 	if (es->hloc32W) {
1287 	    UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1288 	    if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1289 		TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W);
1290 		es->hloc32W = hNew32W;
1291 		es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1292 	    }
1293 	}
1294 
1295 	EDIT_LockBuffer(es);
1296 
1297 	if (es->buffer_size < size) {
1298 		WARN("FAILED !  We now have %d+1\n", es->buffer_size);
1299 		notify_parent(es, EN_ERRSPACE);
1300 		return FALSE;
1301 	} else {
1302 		TRACE("We now have %d+1\n", es->buffer_size);
1303 		return TRUE;
1304 	}
1305 }
1306 
1307 
1308 /*********************************************************************
1309  *
1310  *	EDIT_MakeUndoFit
1311  *
1312  *	Try to fit size + 1 bytes in the undo buffer.
1313  *
1314  */
1315 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1316 {
1317 	UINT alloc_size;
1318 
1319 	if (size <= es->undo_buffer_size)
1320 		return TRUE;
1321 
1322 	TRACE("trying to ReAlloc to %d+1\n", size);
1323 
1324 	alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1325 	if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1326 		es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1327 		return TRUE;
1328 	}
1329 	else
1330 	{
1331 		WARN("FAILED !  We now have %d+1\n", es->undo_buffer_size);
1332 		return FALSE;
1333 	}
1334 }
1335 
1336 
1337 /*********************************************************************
1338  *
1339  *	EDIT_UpdateTextRegion
1340  *
1341  */
1342 static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
1343 {
1344     if (es->flags & EF_UPDATE) {
1345         es->flags &= ~EF_UPDATE;
1346         if (!notify_parent(es, EN_UPDATE)) return;
1347     }
1348     InvalidateRgn(es->hwndSelf, hrgn, bErase);
1349 }
1350 
1351 
1352 /*********************************************************************
1353  *
1354  *	EDIT_UpdateText
1355  *
1356  */
1357 static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase)
1358 {
1359     if (es->flags & EF_UPDATE) {
1360         es->flags &= ~EF_UPDATE;
1361         if (!notify_parent(es, EN_UPDATE)) return;
1362     }
1363     InvalidateRect(es->hwndSelf, rc, bErase);
1364 }
1365 
1366 /*********************************************************************
1367  *
1368  *	EDIT_SL_InvalidateText
1369  *
1370  *	Called from EDIT_InvalidateText().
1371  *	Does the job for single-line controls only.
1372  *
1373  */
1374 static void EDIT_SL_InvalidateText(EDITSTATE *es, INT start, INT end)
1375 {
1376 	RECT line_rect;
1377 	RECT rc;
1378 
1379 	EDIT_GetLineRect(es, 0, start, end, &line_rect);
1380 	if (IntersectRect(&rc, &line_rect, &es->format_rect))
1381 		EDIT_UpdateText(es, &rc, TRUE);
1382 }
1383 
1384 /*********************************************************************
1385  *
1386  *	EDIT_ML_InvalidateText
1387  *
1388  *	Called from EDIT_InvalidateText().
1389  *	Does the job for multi-line controls only.
1390  *
1391  */
1392 static void EDIT_ML_InvalidateText(EDITSTATE *es, INT start, INT end)
1393 {
1394 	INT vlc = get_vertical_line_count(es);
1395 	INT sl = EDIT_EM_LineFromChar(es, start);
1396 	INT el = EDIT_EM_LineFromChar(es, end);
1397 	INT sc;
1398 	INT ec;
1399 	RECT rc1;
1400 	RECT rcWnd;
1401 	RECT rcLine;
1402 	RECT rcUpdate;
1403 	INT l;
1404 
1405 	if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1406 		return;
1407 
1408 	sc = start - EDIT_EM_LineIndex(es, sl);
1409 	ec = end - EDIT_EM_LineIndex(es, el);
1410 	if (sl < es->y_offset) {
1411 		sl = es->y_offset;
1412 		sc = 0;
1413 	}
1414 	if (el > es->y_offset + vlc) {
1415 		el = es->y_offset + vlc;
1416 		ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1417 	}
1418 	GetClientRect(es->hwndSelf, &rc1);
1419 	IntersectRect(&rcWnd, &rc1, &es->format_rect);
1420 	if (sl == el) {
1421 		EDIT_GetLineRect(es, sl, sc, ec, &rcLine);
1422 		if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1423 			EDIT_UpdateText(es, &rcUpdate, TRUE);
1424 	} else {
1425 		EDIT_GetLineRect(es, sl, sc,
1426 				EDIT_EM_LineLength(es,
1427 					EDIT_EM_LineIndex(es, sl)),
1428 				&rcLine);
1429 		if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1430 			EDIT_UpdateText(es, &rcUpdate, TRUE);
1431 		for (l = sl + 1 ; l < el ; l++) {
1432 			EDIT_GetLineRect(es, l, 0,
1433 				EDIT_EM_LineLength(es,
1434 					EDIT_EM_LineIndex(es, l)),
1435 				&rcLine);
1436 			if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1437 				EDIT_UpdateText(es, &rcUpdate, TRUE);
1438 		}
1439 		EDIT_GetLineRect(es, el, 0, ec, &rcLine);
1440 		if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1441 			EDIT_UpdateText(es, &rcUpdate, TRUE);
1442 	}
1443 }
1444 
1445 
1446 /*********************************************************************
1447  *
1448  *	EDIT_InvalidateText
1449  *
1450  *	Invalidate the text from offset start up to, but not including,
1451  *	offset end.  Useful for (re)painting the selection.
1452  *	Regions outside the linewidth are not invalidated.
1453  *	end == -1 means end == TextLength.
1454  *	start and end need not be ordered.
1455  *
1456  */
1457 static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end)
1458 {
1459 	if (end == start)
1460 		return;
1461 
1462 	if (end == -1)
1463 		end = get_text_length(es);
1464 
1465 	if (end < start) {
1466 	    INT tmp = start;
1467 	    start = end;
1468 	    end = tmp;
1469 	}
1470 
1471 	if (es->style & ES_MULTILINE)
1472 		EDIT_ML_InvalidateText(es, start, end);
1473 	else
1474 		EDIT_SL_InvalidateText(es, start, end);
1475 }
1476 
1477 
1478 /*********************************************************************
1479  *
1480  *	EDIT_EM_SetSel
1481  *
1482  *	note:	unlike the specs say: the order of start and end
1483  *		_is_ preserved in Windows.  (i.e. start can be > end)
1484  *		In other words: this handler is OK
1485  *
1486  */
1487 static BOOL EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
1488 {
1489 	UINT old_start = es->selection_start;
1490 	UINT old_end = es->selection_end;
1491 	UINT len = get_text_length(es);
1492 
1493         if (start == old_start && end == old_end)
1494             return FALSE;
1495 
1496 	if (start == (UINT)-1) {
1497 		start = es->selection_end;
1498 		end = es->selection_end;
1499 	} else {
1500 		start = min(start, len);
1501 		end = min(end, len);
1502 	}
1503 	es->selection_start = start;
1504 	es->selection_end = end;
1505 	if (after_wrap)
1506 		es->flags |= EF_AFTER_WRAP;
1507 	else
1508 		es->flags &= ~EF_AFTER_WRAP;
1509 	/* Compute the necessary invalidation region. */
1510 	/* Note that we don't need to invalidate regions which have
1511 	 * "never" been selected, or those which are "still" selected.
1512 	 * In fact, every time we hit a selection boundary, we can
1513 	 * *toggle* whether we need to invalidate.  Thus we can optimize by
1514 	 * *sorting* the interval endpoints.  Let's assume that we sort them
1515 	 * in this order:
1516 	 *        start <= end <= old_start <= old_end
1517 	 * Knuth 5.3.1 (p 183) assures us that this can be done optimally
1518 	 * in 5 comparisons; i.e. it is impossible to do better than the
1519 	 * following: */
1520         ORDER_UINT(end, old_end);
1521         ORDER_UINT(start, old_start);
1522         ORDER_UINT(old_start, old_end);
1523         ORDER_UINT(start, end);
1524 	/* Note that at this point 'end' and 'old_start' are not in order, but
1525 	 * start is definitely the min. and old_end is definitely the max. */
1526 	if (end != old_start)
1527         {
1528 /*
1529  * One can also do
1530  *          ORDER_UINT32(end, old_start);
1531  *          EDIT_InvalidateText(es, start, end);
1532  *          EDIT_InvalidateText(es, old_start, old_end);
1533  * in place of the following if statement.
1534  * (That would complete the optimal five-comparison four-element sort.)
1535  */
1536             if (old_start > end )
1537             {
1538                 EDIT_InvalidateText(es, start, end);
1539                 EDIT_InvalidateText(es, old_start, old_end);
1540             }
1541             else
1542             {
1543                 EDIT_InvalidateText(es, start, old_start);
1544                 EDIT_InvalidateText(es, end, old_end);
1545             }
1546 	}
1547         else EDIT_InvalidateText(es, start, old_end);
1548 
1549         return TRUE;
1550 }
1551 
1552 
1553 /*********************************************************************
1554  *
1555  *	EDIT_UpdateScrollInfo
1556  *
1557  */
1558 static void EDIT_UpdateScrollInfo(EDITSTATE *es)
1559 {
1560     if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
1561     {
1562 	SCROLLINFO si;
1563 	si.cbSize	= sizeof(SCROLLINFO);
1564 	si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1565 	si.nMin		= 0;
1566 	si.nMax		= es->line_count - 1;
1567 	si.nPage	= (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1568 	si.nPos		= es->y_offset;
1569 	TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1570 		si.nMin, si.nMax, si.nPage, si.nPos);
1571 	SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
1572     }
1573 
1574     if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
1575     {
1576 	SCROLLINFO si;
1577 	si.cbSize	= sizeof(SCROLLINFO);
1578 	si.fMask	= SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1579 	si.nMin		= 0;
1580 	si.nMax		= es->text_width - 1;
1581 	si.nPage	= es->format_rect.right - es->format_rect.left;
1582 	si.nPos		= es->x_offset;
1583 	TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1584 		si.nMin, si.nMax, si.nPage, si.nPos);
1585 	SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
1586     }
1587 }
1588 
1589 
1590 /*********************************************************************
1591  *
1592  *	EDIT_EM_LineScroll_internal
1593  *
1594  *	Version of EDIT_EM_LineScroll for internal use.
1595  *	It doesn't refuse if ES_MULTILINE is set and assumes that
1596  *	dx is in pixels, dy - in lines.
1597  *
1598  */
1599 static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy)
1600 {
1601 	INT nyoff;
1602 	INT x_offset_in_pixels;
1603 	INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) /
1604 			      es->line_height;
1605 
1606 	if (es->style & ES_MULTILINE)
1607 	{
1608 	    x_offset_in_pixels = es->x_offset;
1609 	}
1610 	else
1611 	{
1612 	    dy = 0;
1613 	    x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE));
1614 	}
1615 
1616 	if (-dx > x_offset_in_pixels)
1617 		dx = -x_offset_in_pixels;
1618 	if (dx > es->text_width - x_offset_in_pixels)
1619 		dx = es->text_width - x_offset_in_pixels;
1620 	nyoff = max(0, es->y_offset + dy);
1621 	if (nyoff >= es->line_count - lines_per_page)
1622 		nyoff = max(0, es->line_count - lines_per_page);
1623 	dy = (es->y_offset - nyoff) * es->line_height;
1624 	if (dx || dy) {
1625 		RECT rc1;
1626 		RECT rc;
1627 
1628 		es->y_offset = nyoff;
1629 		if(es->style & ES_MULTILINE)
1630 		    es->x_offset += dx;
1631 		else
1632 		    es->x_offset += dx / es->char_width;
1633 
1634 		GetClientRect(es->hwndSelf, &rc1);
1635 		IntersectRect(&rc, &rc1, &es->format_rect);
1636 		ScrollWindowEx(es->hwndSelf, -dx, dy,
1637 				NULL, &rc, NULL, NULL, SW_INVALIDATE);
1638 		/* force scroll info update */
1639 		EDIT_UpdateScrollInfo(es);
1640 	}
1641 	if (dx && !(es->flags & EF_HSCROLL_TRACK))
1642 		notify_parent(es, EN_HSCROLL);
1643 	if (dy && !(es->flags & EF_VSCROLL_TRACK))
1644 		notify_parent(es, EN_VSCROLL);
1645 	return TRUE;
1646 }
1647 
1648 /*********************************************************************
1649  *
1650  *	EM_LINESCROLL
1651  *
1652  *	NOTE: dx is in average character widths, dy - in lines;
1653  *
1654  */
1655 static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy)
1656 {
1657 	if (!(es->style & ES_MULTILINE))
1658 		return FALSE;
1659 
1660 	dx *= es->char_width;
1661 	return EDIT_EM_LineScroll_internal(es, dx, dy);
1662 }
1663 
1664 
1665 /*********************************************************************
1666  *
1667  *	EM_SCROLL
1668  *
1669  */
1670 static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action)
1671 {
1672 	INT dy;
1673 
1674 	if (!(es->style & ES_MULTILINE))
1675 		return (LRESULT)FALSE;
1676 
1677 	dy = 0;
1678 
1679 	switch (action) {
1680 	case SB_LINEUP:
1681 		if (es->y_offset)
1682 			dy = -1;
1683 		break;
1684 	case SB_LINEDOWN:
1685 		if (es->y_offset < es->line_count - 1)
1686 			dy = 1;
1687 		break;
1688 	case SB_PAGEUP:
1689 		if (es->y_offset)
1690 			dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
1691 		break;
1692 	case SB_PAGEDOWN:
1693 		if (es->y_offset < es->line_count - 1)
1694 			dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1695 		break;
1696 	default:
1697 		return (LRESULT)FALSE;
1698 	}
1699 	if (dy) {
1700 	    INT vlc = get_vertical_line_count(es);
1701 	    /* check if we are going to move too far */
1702 	    if(es->y_offset + dy > es->line_count - vlc)
1703 		dy = max(es->line_count - vlc, 0) - es->y_offset;
1704 
1705 	    /* Notification is done in EDIT_EM_LineScroll */
1706 	    if(dy) {
1707 		EDIT_EM_LineScroll(es, 0, dy);
1708 		return MAKELONG(dy, TRUE);
1709 	    }
1710 
1711 	}
1712 	return (LRESULT)FALSE;
1713 }
1714 
1715 
1716 #ifdef __REACTOS__
1717 static void EDIT_ImmSetCompositionWindow(EDITSTATE *es, POINT pt)
1718 {
1719     COMPOSITIONFORM CompForm;
1720     HIMC hIMC = ImmGetContext(es->hwndSelf);
1721     if (!hIMC)
1722     {
1723         ERR("!hIMC\n");
1724         return;
1725     }
1726 
1727     CompForm.ptCurrentPos = pt;
1728     if (es->style & ES_MULTILINE)
1729     {
1730         CompForm.dwStyle = CFS_RECT;
1731         CompForm.rcArea = es->format_rect;
1732     }
1733     else
1734     {
1735         CompForm.dwStyle = CFS_POINT;
1736         SetRectEmpty(&CompForm.rcArea);
1737     }
1738 
1739     ImmSetCompositionWindow(hIMC, &CompForm);
1740     ImmReleaseContext(es->hwndSelf, hIMC);
1741 }
1742 #endif
1743 /*********************************************************************
1744  *
1745  *	EDIT_SetCaretPos
1746  *
1747  */
1748 static void EDIT_SetCaretPos(EDITSTATE *es, INT pos,
1749 			     BOOL after_wrap)
1750 {
1751 	LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap);
1752 #ifdef __REACTOS__
1753     HKL hKL = GetKeyboardLayout(0);
1754     POINT pt = { (short)LOWORD(res), (short)HIWORD(res) };
1755     SetCaretPos(pt.x, pt.y);
1756 
1757     if (!ImmIsIME(hKL))
1758         return;
1759 
1760     EDIT_ImmSetCompositionWindow(es, pt);
1761 #else
1762 	TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res));
1763 	SetCaretPos((short)LOWORD(res), (short)HIWORD(res));
1764 #endif
1765 }
1766 
1767 
1768 /*********************************************************************
1769  *
1770  *	EM_SCROLLCARET
1771  *
1772  */
1773 static void EDIT_EM_ScrollCaret(EDITSTATE *es)
1774 {
1775 	if (es->style & ES_MULTILINE) {
1776 		INT l;
1777 		INT vlc;
1778 		INT ww;
1779 		INT cw = es->char_width;
1780 		INT x;
1781 		INT dy = 0;
1782 		INT dx = 0;
1783 
1784 		l = EDIT_EM_LineFromChar(es, es->selection_end);
1785 		x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP));
1786 		vlc = get_vertical_line_count(es);
1787 		if (l >= es->y_offset + vlc)
1788 			dy = l - vlc + 1 - es->y_offset;
1789 		if (l < es->y_offset)
1790 			dy = l - es->y_offset;
1791 		ww = es->format_rect.right - es->format_rect.left;
1792 		if (x < es->format_rect.left)
1793 			dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1794 		if (x > es->format_rect.right)
1795 			dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1796 		if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1797 		{
1798 		    /* check if we are going to move too far */
1799 		    if(es->x_offset + dx + ww > es->text_width)
1800 			dx = es->text_width - ww - es->x_offset;
1801 		    if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1802 			EDIT_EM_LineScroll_internal(es, dx, dy);
1803 		}
1804 	} else {
1805 		INT x;
1806 		INT goal;
1807 		INT format_width;
1808 
1809 		x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1810 		format_width = es->format_rect.right - es->format_rect.left;
1811 		if (x < es->format_rect.left) {
1812 			goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1813 			do {
1814 				es->x_offset--;
1815 				x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1816 			} while ((x < goal) && es->x_offset);
1817 			/* FIXME: use ScrollWindow() somehow to improve performance */
1818 			EDIT_UpdateText(es, NULL, TRUE);
1819 		} else if (x > es->format_rect.right) {
1820 			INT x_last;
1821 			INT len = get_text_length(es);
1822 			goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1823 			do {
1824 				es->x_offset++;
1825 				x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1826 				x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE));
1827 			} while ((x > goal) && (x_last > es->format_rect.right));
1828 			/* FIXME: use ScrollWindow() somehow to improve performance */
1829 			EDIT_UpdateText(es, NULL, TRUE);
1830 		}
1831 	}
1832 
1833     if(es->flags & EF_FOCUSED)
1834 	EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
1835 }
1836 
1837 
1838 /*********************************************************************
1839  *
1840  *	EDIT_MoveBackward
1841  *
1842  */
1843 static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend)
1844 {
1845 	INT e = es->selection_end;
1846 
1847 	if (e) {
1848 		e--;
1849 		if ((es->style & ES_MULTILINE) && e &&
1850 				(es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1851 			e--;
1852 			if (e && (es->text[e - 1] == '\r'))
1853 				e--;
1854 		}
1855 	}
1856 	EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1857 	EDIT_EM_ScrollCaret(es);
1858 }
1859 
1860 
1861 /*********************************************************************
1862  *
1863  *	EDIT_MoveDown_ML
1864  *
1865  *	Only for multi line controls
1866  *	Move the caret one line down, on a column with the nearest
1867  *	x coordinate on the screen (might be a different column).
1868  *
1869  */
1870 static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend)
1871 {
1872 	INT s = es->selection_start;
1873 	INT e = es->selection_end;
1874 	BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1875 	LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1876 	INT x = (short)LOWORD(pos);
1877 	INT y = (short)HIWORD(pos);
1878 
1879 	e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap);
1880 	if (!extend)
1881 		s = e;
1882 	EDIT_EM_SetSel(es, s, e, after_wrap);
1883 	EDIT_EM_ScrollCaret(es);
1884 }
1885 
1886 
1887 /*********************************************************************
1888  *
1889  *	EDIT_MoveEnd
1890  *
1891  */
1892 static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl)
1893 {
1894 	BOOL after_wrap = FALSE;
1895 	INT e;
1896 
1897 	/* Pass a high value in x to make sure of receiving the end of the line */
1898 	if (!ctrl && (es->style & ES_MULTILINE))
1899 		e = EDIT_CharFromPos(es, 0x3fffffff,
1900 			HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1901 	else
1902 		e = get_text_length(es);
1903 	EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap);
1904 	EDIT_EM_ScrollCaret(es);
1905 }
1906 
1907 
1908 /*********************************************************************
1909  *
1910  *	EDIT_MoveForward
1911  *
1912  */
1913 static void EDIT_MoveForward(EDITSTATE *es, BOOL extend)
1914 {
1915 	INT e = es->selection_end;
1916 
1917 	if (es->text[e]) {
1918 		e++;
1919 		if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1920 			if (es->text[e] == '\n')
1921 				e++;
1922 			else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1923 				e += 2;
1924 		}
1925 	}
1926 	EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1927 	EDIT_EM_ScrollCaret(es);
1928 }
1929 
1930 
1931 /*********************************************************************
1932  *
1933  *	EDIT_MoveHome
1934  *
1935  *	Home key: move to beginning of line.
1936  *
1937  */
1938 static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl)
1939 {
1940 	INT e;
1941 
1942 	/* Pass the x_offset in x to make sure of receiving the first position of the line */
1943 	if (!ctrl && (es->style & ES_MULTILINE))
1944 		e = EDIT_CharFromPos(es, -es->x_offset,
1945 			HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1946 	else
1947 		e = 0;
1948 	EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1949 	EDIT_EM_ScrollCaret(es);
1950 }
1951 
1952 
1953 /*********************************************************************
1954  *
1955  *	EDIT_MovePageDown_ML
1956  *
1957  *	Only for multi line controls
1958  *	Move the caret one page down, on a column with the nearest
1959  *	x coordinate on the screen (might be a different column).
1960  *
1961  */
1962 static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend)
1963 {
1964 	INT s = es->selection_start;
1965 	INT e = es->selection_end;
1966 	BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1967 	LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1968 	INT x = (short)LOWORD(pos);
1969 	INT y = (short)HIWORD(pos);
1970 
1971 	e = EDIT_CharFromPos(es, x,
1972 		y + (es->format_rect.bottom - es->format_rect.top),
1973 		&after_wrap);
1974 	if (!extend)
1975 		s = e;
1976 	EDIT_EM_SetSel(es, s, e, after_wrap);
1977 	EDIT_EM_ScrollCaret(es);
1978 }
1979 
1980 
1981 /*********************************************************************
1982  *
1983  *	EDIT_MovePageUp_ML
1984  *
1985  *	Only for multi line controls
1986  *	Move the caret one page up, on a column with the nearest
1987  *	x coordinate on the screen (might be a different column).
1988  *
1989  */
1990 static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend)
1991 {
1992 	INT s = es->selection_start;
1993 	INT e = es->selection_end;
1994 	BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1995 	LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1996 	INT x = (short)LOWORD(pos);
1997 	INT y = (short)HIWORD(pos);
1998 
1999 	e = EDIT_CharFromPos(es, x,
2000 		y - (es->format_rect.bottom - es->format_rect.top),
2001 		&after_wrap);
2002 	if (!extend)
2003 		s = e;
2004 	EDIT_EM_SetSel(es, s, e, after_wrap);
2005 	EDIT_EM_ScrollCaret(es);
2006 }
2007 
2008 
2009 /*********************************************************************
2010  *
2011  *	EDIT_MoveUp_ML
2012  *
2013  *	Only for multi line controls
2014  *	Move the caret one line up, on a column with the nearest
2015  *	x coordinate on the screen (might be a different column).
2016  *
2017  */
2018 static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend)
2019 {
2020 	INT s = es->selection_start;
2021 	INT e = es->selection_end;
2022 	BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2023 	LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2024 	INT x = (short)LOWORD(pos);
2025 	INT y = (short)HIWORD(pos);
2026 
2027 	e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap);
2028 	if (!extend)
2029 		s = e;
2030 	EDIT_EM_SetSel(es, s, e, after_wrap);
2031 	EDIT_EM_ScrollCaret(es);
2032 }
2033 
2034 
2035 /*********************************************************************
2036  *
2037  *	EDIT_MoveWordBackward
2038  *
2039  */
2040 static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend)
2041 {
2042 	INT s = es->selection_start;
2043 	INT e = es->selection_end;
2044 	INT l;
2045 	INT ll;
2046 	INT li;
2047 
2048 	l = EDIT_EM_LineFromChar(es, e);
2049 	ll = EDIT_EM_LineLength(es, e);
2050 	li = EDIT_EM_LineIndex(es, l);
2051 	if (e - li == 0) {
2052 		if (l) {
2053 			li = EDIT_EM_LineIndex(es, l - 1);
2054 			e = li + EDIT_EM_LineLength(es, li);
2055 		}
2056 	} else {
2057 		e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
2058 	}
2059 	if (!extend)
2060 		s = e;
2061 	EDIT_EM_SetSel(es, s, e, FALSE);
2062 	EDIT_EM_ScrollCaret(es);
2063 }
2064 
2065 
2066 /*********************************************************************
2067  *
2068  *	EDIT_MoveWordForward
2069  *
2070  */
2071 static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend)
2072 {
2073 	INT s = es->selection_start;
2074 	INT e = es->selection_end;
2075 	INT l;
2076 	INT ll;
2077 	INT li;
2078 
2079 	l = EDIT_EM_LineFromChar(es, e);
2080 	ll = EDIT_EM_LineLength(es, e);
2081 	li = EDIT_EM_LineIndex(es, l);
2082 	if (e - li == ll) {
2083 		if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2084 			e = EDIT_EM_LineIndex(es, l + 1);
2085 	} else {
2086 		e = li + EDIT_CallWordBreakProc(es,
2087 				li, e - li + 1, ll, WB_RIGHT);
2088 	}
2089 	if (!extend)
2090 		s = e;
2091 	EDIT_EM_SetSel(es, s, e, FALSE);
2092 	EDIT_EM_ScrollCaret(es);
2093 }
2094 
2095 
2096 /*********************************************************************
2097  *
2098  *	EDIT_PaintText
2099  *
2100  */
2101 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2102 {
2103 	COLORREF BkColor;
2104 	COLORREF TextColor;
2105 	LOGFONTW underline_font;
2106 	HFONT hUnderline = 0;
2107 	HFONT old_font = 0;
2108 	INT ret;
2109 	INT li;
2110 	INT BkMode;
2111 	SIZE size;
2112 
2113 	if (!count)
2114 		return 0;
2115 	BkMode = GetBkMode(dc);
2116 	BkColor = GetBkColor(dc);
2117 	TextColor = GetTextColor(dc);
2118 	if (rev) {
2119 #ifdef __REACTOS__
2120             if (TRUE)
2121 #else
2122 	        if (es->composition_len == 0)
2123 #endif
2124 	        {
2125 			SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2126 			SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2127 			SetBkMode( dc, OPAQUE);
2128 	        }
2129 		else
2130 		{
2131 			HFONT current = GetCurrentObject(dc,OBJ_FONT);
2132 			GetObjectW(current,sizeof(LOGFONTW),&underline_font);
2133 			underline_font.lfUnderline = TRUE;
2134 			hUnderline = CreateFontIndirectW(&underline_font);
2135 			old_font = SelectObject(dc,hUnderline);
2136 	        }
2137 	}
2138 	li = EDIT_EM_LineIndex(es, line);
2139 	if (es->style & ES_MULTILINE) {
2140 		ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2141 					es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2142 	} else {
2143 		TextOutW(dc, x, y, es->text + li + col, count);
2144 		GetTextExtentPoint32W(dc, es->text + li + col, count, &size);
2145 		ret = size.cx;
2146 	}
2147 	if (rev) {
2148 #ifdef __REACTOS__
2149         if (TRUE)
2150 #else
2151 		if (es->composition_len == 0)
2152 #endif
2153 		{
2154 			SetBkColor(dc, BkColor);
2155 			SetTextColor(dc, TextColor);
2156 			SetBkMode( dc, BkMode);
2157 		}
2158 		else
2159 		{
2160 			if (old_font)
2161 				SelectObject(dc,old_font);
2162 			if (hUnderline)
2163 				DeleteObject(hUnderline);
2164 	        }
2165 	}
2166 	return ret;
2167 }
2168 
2169 
2170 /*********************************************************************
2171  *
2172  *	EDIT_PaintLine
2173  *
2174  */
2175 static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev)
2176 {
2177 	INT s = 0;
2178 	INT e = 0;
2179 	INT li = 0;
2180 	INT ll = 0;
2181 	INT x;
2182 	INT y;
2183 	LRESULT pos;
2184 	SCRIPT_STRING_ANALYSIS ssa;
2185 
2186 	if (es->style & ES_MULTILINE) {
2187 		INT vlc = get_vertical_line_count(es);
2188 
2189 		if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2190 			return;
2191 	} else if (line)
2192 		return;
2193 
2194 	TRACE("line=%d\n", line);
2195 
2196 	ssa = EDIT_UpdateUniscribeData(es, dc, line);
2197 	pos = EDIT_EM_PosFromChar(es, EDIT_EM_LineIndex(es, line), FALSE);
2198 	x = (short)LOWORD(pos);
2199 	y = (short)HIWORD(pos);
2200 
2201 	if (es->style & ES_MULTILINE)
2202 	{
2203 		int line_idx = line;
2204 		x =  -es->x_offset;
2205 		if (es->style & ES_RIGHT || es->style & ES_CENTER)
2206 		{
2207 			LINEDEF *line_def = es->first_line_def;
2208 			int w, lw;
2209 
2210 			while (line_def && line_idx)
2211 			{
2212 				line_def = line_def->next;
2213 				line_idx--;
2214 			}
2215 			w = es->format_rect.right - es->format_rect.left;
2216 			lw = line_def->width;
2217 
2218 			if (es->style & ES_RIGHT)
2219 				x = w - (lw - x);
2220 			else if (es->style & ES_CENTER)
2221 				x += (w - lw) / 2;
2222 		}
2223 		x += es->format_rect.left;
2224 	}
2225 
2226 	if (rev)
2227 	{
2228 		li = EDIT_EM_LineIndex(es, line);
2229 		ll = EDIT_EM_LineLength(es, li);
2230 		s = min(es->selection_start, es->selection_end);
2231 		e = max(es->selection_start, es->selection_end);
2232 		s = min(li + ll, max(li, s));
2233 		e = min(li + ll, max(li, e));
2234 	}
2235 
2236 	if (ssa)
2237 		ScriptStringOut(ssa, x, y, 0, &es->format_rect, s - li, e - li, FALSE);
2238 	else if (rev && (s != e) &&
2239 			((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2240 		x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2241 		x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2242 		x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2243 	} else
2244 		x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2245 }
2246 
2247 
2248 /*********************************************************************
2249  *
2250  *	EDIT_AdjustFormatRect
2251  *
2252  *	Adjusts the format rectangle for the current font and the
2253  *	current client rectangle.
2254  *
2255  */
2256 static void EDIT_AdjustFormatRect(EDITSTATE *es)
2257 {
2258 	RECT ClientRect;
2259 
2260 	es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2261 	if (es->style & ES_MULTILINE)
2262 	{
2263 	    INT fw, vlc, max_x_offset, max_y_offset;
2264 
2265 	    vlc = get_vertical_line_count(es);
2266 	    es->format_rect.bottom = es->format_rect.top + vlc * es->line_height;
2267 
2268 	    /* correct es->x_offset */
2269 	    fw = es->format_rect.right - es->format_rect.left;
2270 	    max_x_offset = es->text_width - fw;
2271 	    if(max_x_offset < 0) max_x_offset = 0;
2272 	    if(es->x_offset > max_x_offset)
2273 		es->x_offset = max_x_offset;
2274 
2275 	    /* correct es->y_offset */
2276 	    max_y_offset = es->line_count - vlc;
2277 	    if(max_y_offset < 0) max_y_offset = 0;
2278 	    if(es->y_offset > max_y_offset)
2279 		es->y_offset = max_y_offset;
2280 
2281 	    /* force scroll info update */
2282 	    EDIT_UpdateScrollInfo(es);
2283 	}
2284 	else
2285 	/* Windows doesn't care to fix text placement for SL controls */
2286 		es->format_rect.bottom = es->format_rect.top + es->line_height;
2287 
2288 	/* Always stay within the client area */
2289 	GetClientRect(es->hwndSelf, &ClientRect);
2290 	es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom);
2291 
2292 	if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2293 		EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2294 
2295 	EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
2296 }
2297 
2298 
2299 /*********************************************************************
2300  *
2301  *	EDIT_SetRectNP
2302  *
2303  *	note:	this is not (exactly) the handler called on EM_SETRECTNP
2304  *		it is also used to set the rect of a single line control
2305  *
2306  */
2307 static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc)
2308 {
2309 	LONG_PTR ExStyle;
2310 	INT bw, bh;
2311 	ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE);
2312 
2313 	CopyRect(&es->format_rect, rc);
2314 
2315 	if (ExStyle & WS_EX_CLIENTEDGE) {
2316 		es->format_rect.left++;
2317 		es->format_rect.right--;
2318 
2319 		if (es->format_rect.bottom - es->format_rect.top
2320 		    >= es->line_height + 2)
2321 		{
2322 			es->format_rect.top++;
2323 			es->format_rect.bottom--;
2324 		}
2325 	}
2326 	else if (es->style & WS_BORDER) {
2327 		bw = GetSystemMetrics(SM_CXBORDER) + 1;
2328 		bh = GetSystemMetrics(SM_CYBORDER) + 1;
2329                 InflateRect(&es->format_rect, -bw, 0);
2330                 if (es->format_rect.bottom - es->format_rect.top >= es->line_height + 2 * bh)
2331                     InflateRect(&es->format_rect, 0, -bh);
2332 	}
2333 
2334 	es->format_rect.left += es->left_margin;
2335 	es->format_rect.right -= es->right_margin;
2336 	EDIT_AdjustFormatRect(es);
2337 }
2338 
2339 
2340 /*********************************************************************
2341  *
2342  *	EM_CHARFROMPOS
2343  *
2344  *      returns line number (not index) in high-order word of result.
2345  *      NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2346  *      to Richedit, not to the edit control. Original documentation is valid.
2347  *	FIXME: do the specs mean to return -1 if outside client area or
2348  *		if outside formatting rectangle ???
2349  *
2350  */
2351 static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y)
2352 {
2353 	POINT pt;
2354 	RECT rc;
2355 	INT index;
2356 
2357 	pt.x = x;
2358 	pt.y = y;
2359 	GetClientRect(es->hwndSelf, &rc);
2360 	if (!PtInRect(&rc, pt))
2361 		return -1;
2362 
2363 	index = EDIT_CharFromPos(es, x, y, NULL);
2364 	return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2365 }
2366 
2367 
2368 /*********************************************************************
2369  *
2370  *	EM_FMTLINES
2371  *
2372  * Enable or disable soft breaks.
2373  *
2374  * This means: insert or remove the soft linebreak character (\r\r\n).
2375  * Take care to check if the text still fits the buffer after insertion.
2376  * If not, notify with EN_ERRSPACE.
2377  *
2378  */
2379 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2380 {
2381 	es->flags &= ~EF_USE_SOFTBRK;
2382 	if (add_eol) {
2383 		es->flags |= EF_USE_SOFTBRK;
2384 		FIXME("soft break enabled, not implemented\n");
2385 	}
2386 	return add_eol;
2387 }
2388 
2389 
2390 /*********************************************************************
2391  *
2392  *	EM_GETHANDLE
2393  *
2394  *	Hopefully this won't fire back at us.
2395  *	We always start with a fixed buffer in the local heap.
2396  *	Despite of the documentation says that the local heap is used
2397  *	only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2398  *	buffer on the local heap.
2399  *
2400  */
2401 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2402 {
2403     if (!(es->style & ES_MULTILINE))
2404         return 0;
2405 
2406     EDIT_UnlockBuffer(es, TRUE);
2407 
2408     /* The text buffer handle belongs to the app */
2409     es->hlocapp = es->hloc32W;
2410 
2411     TRACE("Returning %p, LocalSize() = %ld\n", es->hlocapp, LocalSize(es->hlocapp));
2412     return es->hlocapp;
2413 }
2414 
2415 
2416 /*********************************************************************
2417  *
2418  *	EM_GETLINE
2419  *
2420  */
2421 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst)
2422 {
2423     INT line_len, dst_len;
2424     LPWSTR src;
2425     INT i;
2426 
2427     if (es->style & ES_MULTILINE)
2428     {
2429         if (line >= es->line_count)
2430             return 0;
2431     }
2432     else
2433         line = 0;
2434 
2435     i = EDIT_EM_LineIndex(es, line);
2436     src = es->text + i;
2437     line_len = EDIT_EM_LineLength(es, i);
2438     dst_len = *(WORD *)dst;
2439 
2440     if (dst_len <= line_len)
2441     {
2442         memcpy(dst, src, dst_len * sizeof(WCHAR));
2443         return dst_len;
2444     }
2445     else /* Append 0 if enough space */
2446     {
2447         memcpy(dst, src, line_len * sizeof(WCHAR));
2448         dst[line_len] = 0;
2449         return line_len;
2450     }
2451 }
2452 
2453 
2454 /*********************************************************************
2455  *
2456  *	EM_GETSEL
2457  *
2458  */
2459 static LRESULT EDIT_EM_GetSel(const EDITSTATE *es, PUINT start, PUINT end)
2460 {
2461 	UINT s = es->selection_start;
2462 	UINT e = es->selection_end;
2463 
2464 	ORDER_UINT(s, e);
2465 	if (start)
2466 		*start = s;
2467 	if (end)
2468 		*end = e;
2469 	return MAKELONG(s, e);
2470 }
2471 
2472 
2473 /*********************************************************************
2474  *
2475  *	EM_REPLACESEL
2476  *
2477  *	FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2478  *
2479  */
2480 static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_replace, UINT strl,
2481                                BOOL send_update, BOOL honor_limit)
2482 {
2483 	UINT tl = get_text_length(es);
2484 	UINT utl;
2485 	UINT s;
2486 	UINT e;
2487 	UINT i;
2488 	UINT size;
2489 	LPWSTR p;
2490 	HRGN hrgn = 0;
2491 	LPWSTR buf = NULL;
2492 	UINT bufl;
2493 
2494 	TRACE("%s, can_undo %d, send_update %d\n",
2495 	    debugstr_wn(lpsz_replace, strl), can_undo, send_update);
2496 
2497 	s = es->selection_start;
2498 	e = es->selection_end;
2499 
2500 	EDIT_InvalidateUniscribeData(es);
2501 	if ((s == e) && !strl)
2502 		return;
2503 
2504 	ORDER_UINT(s, e);
2505 
2506 	size = tl - (e - s) + strl;
2507 	if (!size)
2508 		es->text_width = 0;
2509 
2510 	/* Issue the EN_MAXTEXT notification and continue with replacing text
2511          * so that buffer limit is honored. */
2512 	if ((honor_limit) && (size > es->buffer_limit))
2513 	{
2514 		if (!notify_parent(es, EN_MAXTEXT)) return;
2515 		/* Buffer limit can be smaller than the actual length of text in combobox */
2516 		if (es->buffer_limit < (tl - (e-s)))
2517 			strl = 0;
2518 		else
2519 			strl = min(strl, es->buffer_limit - (tl - (e-s)));
2520 	}
2521 
2522 	if (!EDIT_MakeFit(es, tl - (e - s) + strl))
2523 		return;
2524 
2525 	if (e != s) {
2526 		/* there is something to be deleted */
2527 		TRACE("deleting stuff.\n");
2528 		bufl = e - s;
2529 		buf = heap_alloc((bufl + 1) * sizeof(WCHAR));
2530 		if (!buf) return;
2531 		memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
2532 		buf[bufl] = 0; /* ensure 0 termination */
2533 		/* now delete */
2534 		strcpyW(es->text + s, es->text + e);
2535                 text_buffer_changed(es);
2536 	}
2537 	if (strl) {
2538 		/* there is an insertion */
2539 		tl = get_text_length(es);
2540 		TRACE("inserting stuff (tl %d, strl %d, selstart %d (%s), text %s)\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text));
2541 		for (p = es->text + tl ; p >= es->text + s ; p--)
2542 			p[strl] = p[0];
2543 		for (i = 0 , p = es->text + s ; i < strl ; i++)
2544 			p[i] = lpsz_replace[i];
2545 		if(es->style & ES_UPPERCASE)
2546 			CharUpperBuffW(p, strl);
2547 		else if(es->style & ES_LOWERCASE)
2548 			CharLowerBuffW(p, strl);
2549                 text_buffer_changed(es);
2550 	}
2551 	if (es->style & ES_MULTILINE)
2552 	{
2553 		INT st = min(es->selection_start, es->selection_end);
2554 		INT vlc = get_vertical_line_count(es);
2555 
2556 		hrgn = CreateRectRgn(0, 0, 0, 0);
2557 		EDIT_BuildLineDefs_ML(es, st, st + strl,
2558 				strl - abs(es->selection_end - es->selection_start), hrgn);
2559 		/* if text is too long undo all changes */
2560 		if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
2561 			if (strl)
2562 				strcpyW(es->text + e, es->text + e + strl);
2563 			if (e != s)
2564 				for (i = 0 , p = es->text ; i < e - s ; i++)
2565 					p[i + s] = buf[i];
2566                         text_buffer_changed(es);
2567 			EDIT_BuildLineDefs_ML(es, s, e,
2568 				abs(es->selection_end - es->selection_start) - strl, hrgn);
2569 			strl = 0;
2570 			e = s;
2571 			hrgn = CreateRectRgn(0, 0, 0, 0);
2572 			if (!notify_parent(es, EN_MAXTEXT)) return;
2573 		}
2574 	}
2575 	else {
2576 		INT fw = es->format_rect.right - es->format_rect.left;
2577 		EDIT_InvalidateUniscribeData(es);
2578 		EDIT_CalcLineWidth_SL(es);
2579 		/* remove chars that don't fit */
2580 		if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
2581 			while ((es->text_width > fw) && s + strl >= s) {
2582 				strcpyW(es->text + s + strl - 1, es->text + s + strl);
2583 				strl--;
2584 				es->text_length = -1;
2585 				EDIT_InvalidateUniscribeData(es);
2586 				EDIT_CalcLineWidth_SL(es);
2587 			}
2588                         text_buffer_changed(es);
2589 			if (!notify_parent(es, EN_MAXTEXT)) return;
2590 		}
2591 	}
2592 
2593 	if (e != s) {
2594 		if (can_undo) {
2595 			utl = strlenW(es->undo_text);
2596 			if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2597 				/* undo-buffer is extended to the right */
2598 				EDIT_MakeUndoFit(es, utl + e - s);
2599 				memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR));
2600 				(es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2601 			} else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2602 				/* undo-buffer is extended to the left */
2603 				EDIT_MakeUndoFit(es, utl + e - s);
2604 				for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2605 					p[e - s] = p[0];
2606 				for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2607 					p[i] = buf[i];
2608 				es->undo_position = s;
2609 			} else {
2610 				/* new undo-buffer */
2611 				EDIT_MakeUndoFit(es, e - s);
2612 				memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR));
2613 				es->undo_text[e - s] = 0; /* ensure 0 termination */
2614 				es->undo_position = s;
2615 			}
2616 			/* any deletion makes the old insertion-undo invalid */
2617 			es->undo_insert_count = 0;
2618 		} else
2619 			EDIT_EM_EmptyUndoBuffer(es);
2620 	}
2621 	if (strl) {
2622 		if (can_undo) {
2623 			if ((s == es->undo_position) ||
2624 				((es->undo_insert_count) &&
2625 				(s == es->undo_position + es->undo_insert_count)))
2626 				/*
2627 				 * insertion is new and at delete position or
2628 				 * an extension to either left or right
2629 				 */
2630 				es->undo_insert_count += strl;
2631 			else {
2632 				/* new insertion undo */
2633 				es->undo_position = s;
2634 				es->undo_insert_count = strl;
2635 				/* new insertion makes old delete-buffer invalid */
2636 				*es->undo_text = '\0';
2637 			}
2638 		} else
2639 			EDIT_EM_EmptyUndoBuffer(es);
2640 	}
2641 
2642 	heap_free(buf);
2643 
2644 	s += strl;
2645 
2646 	/* If text has been deleted and we're right or center aligned then scroll rightward */
2647 	if (es->style & (ES_RIGHT | ES_CENTER))
2648 	{
2649 		INT delta = strl - abs(es->selection_end - es->selection_start);
2650 
2651 		if (delta < 0 && es->x_offset)
2652 		{
2653 			if (abs(delta) > es->x_offset)
2654 				es->x_offset = 0;
2655 			else
2656 				es->x_offset += delta;
2657 		}
2658 	}
2659 
2660 	EDIT_EM_SetSel(es, s, s, FALSE);
2661 	es->flags |= EF_MODIFIED;
2662 	if (send_update) es->flags |= EF_UPDATE;
2663 	if (hrgn)
2664 	{
2665 		EDIT_UpdateTextRegion(es, hrgn, TRUE);
2666 		DeleteObject(hrgn);
2667 	}
2668 	else
2669             EDIT_UpdateText(es, NULL, TRUE);
2670 
2671 	EDIT_EM_ScrollCaret(es);
2672 
2673 	/* force scroll info update */
2674 	EDIT_UpdateScrollInfo(es);
2675 
2676 
2677         if(send_update || (es->flags & EF_UPDATE))
2678 	{
2679 	    es->flags &= ~EF_UPDATE;
2680 	    if (!notify_parent(es, EN_CHANGE)) return;
2681 	}
2682 	EDIT_InvalidateUniscribeData(es);
2683 }
2684 
2685 
2686 /*********************************************************************
2687  *
2688  *	EM_SETHANDLE
2689  *
2690  *	FIXME:	ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2691  *
2692  */
2693 static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc)
2694 {
2695     if (!(es->style & ES_MULTILINE))
2696         return;
2697 
2698     if (!hloc)
2699         return;
2700 
2701     EDIT_UnlockBuffer(es, TRUE);
2702 
2703     es->hloc32W = hloc;
2704     es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2705 
2706     /* The text buffer handle belongs to the control */
2707     es->hlocapp = NULL;
2708 
2709     EDIT_LockBuffer(es);
2710     text_buffer_changed(es);
2711 
2712     es->x_offset = es->y_offset = 0;
2713     es->selection_start = es->selection_end = 0;
2714     EDIT_EM_EmptyUndoBuffer(es);
2715     es->flags &= ~EF_MODIFIED;
2716     es->flags &= ~EF_UPDATE;
2717     EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2718     EDIT_UpdateText(es, NULL, TRUE);
2719     EDIT_EM_ScrollCaret(es);
2720     /* force scroll info update */
2721     EDIT_UpdateScrollInfo(es);
2722 }
2723 
2724 
2725 /*********************************************************************
2726  *
2727  *	EM_SETLIMITTEXT
2728  *
2729  *	NOTE: this version currently implements WinNT limits
2730  *
2731  */
2732 static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
2733 {
2734     if (!limit) limit = ~0u;
2735     if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe);
2736     es->buffer_limit = limit;
2737 }
2738 
2739 
2740 /*********************************************************************
2741  *
2742  *	EM_SETMARGINS
2743  *
2744  * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2745  * action wParam despite what the docs say. EC_USEFONTINFO calculates the
2746  * margin according to the textmetrics of the current font.
2747  *
2748  * When EC_USEFONTINFO is used in the non_cjk case the margins only
2749  * change if the edit control is equal to or larger than a certain
2750  * size.  Though there is an exception for the empty client rect case
2751  * with small font sizes.
2752  */
2753 static BOOL is_cjk(UINT charset)
2754 {
2755     switch(charset)
2756     {
2757     case SHIFTJIS_CHARSET:
2758     case HANGUL_CHARSET:
2759     case GB2312_CHARSET:
2760     case CHINESEBIG5_CHARSET:
2761         return TRUE;
2762     }
2763     /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is
2764      * not by other versions including Win 10. */
2765     return FALSE;
2766 }
2767 
2768 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
2769 			       WORD left, WORD right, BOOL repaint)
2770 {
2771 	TEXTMETRICW tm;
2772 	INT default_left_margin  = 0; /* in pixels */
2773 	INT default_right_margin = 0; /* in pixels */
2774 
2775         /* Set the default margins depending on the font */
2776         if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
2777             HDC dc = GetDC(es->hwndSelf);
2778             HFONT old_font = SelectObject(dc, es->font);
2779             LONG width = GdiGetCharDimensions(dc, &tm, NULL);
2780             RECT rc;
2781 
2782             /* The default margins are only non zero for TrueType or Vector fonts */
2783             if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
2784                 if (!is_cjk(tm.tmCharSet)) {
2785                     default_left_margin = width / 2;
2786                     default_right_margin = width / 2;
2787 
2788                     GetClientRect(es->hwndSelf, &rc);
2789                     if (rc.right - rc.left < (width / 2 + width) * 2 &&
2790                         (width >= 28 || !IsRectEmpty(&rc)) ) {
2791                         default_left_margin = es->left_margin;
2792                         default_right_margin = es->right_margin;
2793                     }
2794                 } else {
2795                     /* FIXME: figure out the CJK values. They are not affected by the client rect. */
2796                     default_left_margin = width / 2;
2797                     default_right_margin = width / 2;
2798                 }
2799             }
2800             SelectObject(dc, old_font);
2801             ReleaseDC(es->hwndSelf, dc);
2802         }
2803 
2804 	if (action & EC_LEFTMARGIN) {
2805 		es->format_rect.left -= es->left_margin;
2806 		if (left != EC_USEFONTINFO)
2807 			es->left_margin = left;
2808 		else
2809 			es->left_margin = default_left_margin;
2810 		es->format_rect.left += es->left_margin;
2811 	}
2812 
2813 	if (action & EC_RIGHTMARGIN) {
2814 		es->format_rect.right += es->right_margin;
2815 		if (right != EC_USEFONTINFO)
2816 			es->right_margin = right;
2817 		else
2818 			es->right_margin = default_right_margin;
2819 		es->format_rect.right -= es->right_margin;
2820 	}
2821 
2822 	if (action & (EC_LEFTMARGIN | EC_RIGHTMARGIN)) {
2823 		EDIT_AdjustFormatRect(es);
2824 		if (repaint) EDIT_UpdateText(es, NULL, TRUE);
2825 	}
2826 
2827 	TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2828 }
2829 
2830 
2831 /*********************************************************************
2832  *
2833  *	EM_SETPASSWORDCHAR
2834  *
2835  */
2836 static void EDIT_EM_SetPasswordChar(EDITSTATE *es, WCHAR c)
2837 {
2838 	LONG style;
2839 
2840 	if (es->style & ES_MULTILINE)
2841 		return;
2842 
2843 	if (es->password_char == c)
2844 		return;
2845 
2846         style = GetWindowLongW( es->hwndSelf, GWL_STYLE );
2847 	es->password_char = c;
2848 	if (c) {
2849             SetWindowLongW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD );
2850             es->style |= ES_PASSWORD;
2851 	} else {
2852             SetWindowLongW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD );
2853             es->style &= ~ES_PASSWORD;
2854 	}
2855 	EDIT_InvalidateUniscribeData(es);
2856 	EDIT_UpdateText(es, NULL, TRUE);
2857 }
2858 
2859 
2860 /*********************************************************************
2861  *
2862  *	EM_SETTABSTOPS
2863  *
2864  */
2865 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, const INT *tabs)
2866 {
2867 	if (!(es->style & ES_MULTILINE))
2868 		return FALSE;
2869         heap_free(es->tabs);
2870 	es->tabs_count = count;
2871 	if (!count)
2872 		es->tabs = NULL;
2873 	else {
2874 		es->tabs = heap_alloc(count * sizeof(INT));
2875 		memcpy(es->tabs, tabs, count * sizeof(INT));
2876 	}
2877 	EDIT_InvalidateUniscribeData(es);
2878 	return TRUE;
2879 }
2880 
2881 
2882 /*********************************************************************
2883  *
2884  *	EM_SETWORDBREAKPROC
2885  *
2886  */
2887 static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, EDITWORDBREAKPROCW wbp)
2888 {
2889 	if (es->word_break_proc == wbp)
2890 		return;
2891 
2892 	es->word_break_proc = wbp;
2893 
2894 	if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2895 		EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2896 		EDIT_UpdateText(es, NULL, TRUE);
2897 	}
2898 }
2899 
2900 
2901 /*********************************************************************
2902  *
2903  *	EM_UNDO / WM_UNDO
2904  *
2905  */
2906 static BOOL EDIT_EM_Undo(EDITSTATE *es)
2907 {
2908 	INT ulength;
2909 	LPWSTR utext;
2910 
2911 	/* As per MSDN spec, for a single-line edit control,
2912 	   the return value is always TRUE */
2913 	if( es->style & ES_READONLY )
2914             return !(es->style & ES_MULTILINE);
2915 
2916 	ulength = strlenW(es->undo_text);
2917 
2918 	utext = heap_alloc((ulength + 1) * sizeof(WCHAR));
2919 
2920 	strcpyW(utext, es->undo_text);
2921 
2922 	TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2923 		     es->undo_insert_count, debugstr_w(utext));
2924 
2925 	EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2926 	EDIT_EM_EmptyUndoBuffer(es);
2927 	EDIT_EM_ReplaceSel(es, TRUE, utext, ulength, TRUE, TRUE);
2928 	EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2929         /* send the notification after the selection start and end are set */
2930         if (!notify_parent(es, EN_CHANGE)) return TRUE;
2931 	EDIT_EM_ScrollCaret(es);
2932 	heap_free(utext);
2933 
2934 	TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2935 			es->undo_insert_count, debugstr_w(es->undo_text));
2936 	return TRUE;
2937 }
2938 
2939 
2940 /* Helper function for WM_CHAR
2941  *
2942  * According to an MSDN blog article titled "Just because you're a control
2943  * doesn't mean that you're necessarily inside a dialog box," multiline edit
2944  * controls without ES_WANTRETURN would attempt to detect whether it is inside
2945  * a dialog box or not.
2946  */
2947 static inline BOOL EDIT_IsInsideDialog(EDITSTATE *es)
2948 {
2949     return (es->flags & EF_DIALOGMODE);
2950 }
2951 
2952 
2953 /*********************************************************************
2954  *
2955  *	WM_PASTE
2956  *
2957  */
2958 static void EDIT_WM_Paste(EDITSTATE *es)
2959 {
2960 	HGLOBAL hsrc;
2961 	LPWSTR src, ptr;
2962 	int len;
2963 
2964 	/* Protect read-only edit control from modification */
2965 	if(es->style & ES_READONLY)
2966 	    return;
2967 
2968 	OpenClipboard(es->hwndSelf);
2969 	if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
2970 		src = GlobalLock(hsrc);
2971                 len = strlenW(src);
2972 		/* Protect single-line edit against pasting new line character */
2973 		if (!(es->style & ES_MULTILINE) && ((ptr = strchrW(src, '\n')))) {
2974 			len = ptr - src;
2975 			if (len && src[len - 1] == '\r')
2976 				--len;
2977 		}
2978                 EDIT_EM_ReplaceSel(es, TRUE, src, len, TRUE, TRUE);
2979 		GlobalUnlock(hsrc);
2980 	}
2981         else if (es->style & ES_PASSWORD) {
2982             /* clear selected text in password edit box even with empty clipboard */
2983             EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE);
2984         }
2985 	CloseClipboard();
2986 }
2987 
2988 
2989 /*********************************************************************
2990  *
2991  *	WM_COPY
2992  *
2993  */
2994 static void EDIT_WM_Copy(EDITSTATE *es)
2995 {
2996 	INT s = min(es->selection_start, es->selection_end);
2997 	INT e = max(es->selection_start, es->selection_end);
2998 	HGLOBAL hdst;
2999 	LPWSTR dst;
3000 	DWORD len;
3001 
3002 	if (e == s) return;
3003 
3004 	len = e - s;
3005 	hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR));
3006 	dst = GlobalLock(hdst);
3007 	memcpy(dst, es->text + s, len * sizeof(WCHAR));
3008 	dst[len] = 0; /* ensure 0 termination */
3009 	TRACE("%s\n", debugstr_w(dst));
3010 	GlobalUnlock(hdst);
3011 	OpenClipboard(es->hwndSelf);
3012 	EmptyClipboard();
3013 	SetClipboardData(CF_UNICODETEXT, hdst);
3014 	CloseClipboard();
3015 }
3016 
3017 
3018 /*********************************************************************
3019  *
3020  *	WM_CLEAR
3021  *
3022  */
3023 static inline void EDIT_WM_Clear(EDITSTATE *es)
3024 {
3025 	/* Protect read-only edit control from modification */
3026 	if(es->style & ES_READONLY)
3027 	    return;
3028 
3029 	EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE);
3030 }
3031 
3032 
3033 /*********************************************************************
3034  *
3035  *	WM_CUT
3036  *
3037  */
3038 static inline void EDIT_WM_Cut(EDITSTATE *es)
3039 {
3040 	EDIT_WM_Copy(es);
3041 	EDIT_WM_Clear(es);
3042 }
3043 
3044 
3045 /*********************************************************************
3046  *
3047  *	WM_CHAR
3048  *
3049  */
3050 static LRESULT EDIT_WM_Char(EDITSTATE *es, WCHAR c)
3051 {
3052         BOOL control;
3053 
3054 	control = GetKeyState(VK_CONTROL) & 0x8000;
3055 
3056 	switch (c) {
3057 	case '\r':
3058             /* If it's not a multiline edit box, it would be ignored below.
3059              * For multiline edit without ES_WANTRETURN, we have to make a
3060              * special case.
3061              */
3062             if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3063                 if (EDIT_IsInsideDialog(es))
3064                     break;
3065 	case '\n':
3066 		if (es->style & ES_MULTILINE) {
3067 			if (es->style & ES_READONLY) {
3068 				EDIT_MoveHome(es, FALSE, FALSE);
3069 				EDIT_MoveDown_ML(es, FALSE);
3070 			} else {
3071 				static const WCHAR cr_lfW[] = {'\r','\n'};
3072 				EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, 2, TRUE, TRUE);
3073 			}
3074 		}
3075 		break;
3076 	case '\t':
3077 		if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3078 		{
3079 			static const WCHAR tabW[] = {'\t'};
3080                         if (EDIT_IsInsideDialog(es))
3081                             break;
3082 			EDIT_EM_ReplaceSel(es, TRUE, tabW, 1, TRUE, TRUE);
3083 		}
3084 		break;
3085 	case VK_BACK:
3086 		if (!(es->style & ES_READONLY) && !control) {
3087 			if (es->selection_start != es->selection_end)
3088 				EDIT_WM_Clear(es);
3089 			else {
3090 				/* delete character left of caret */
3091 				EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3092 				EDIT_MoveBackward(es, TRUE);
3093 				EDIT_WM_Clear(es);
3094 			}
3095 		}
3096 		break;
3097 	case 0x03: /* ^C */
3098 		if (!(es->style & ES_PASSWORD))
3099 		    SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3100 		break;
3101 	case 0x16: /* ^V */
3102 	        if (!(es->style & ES_READONLY))
3103 		    SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3104 		break;
3105 	case 0x18: /* ^X */
3106 	        if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD)))
3107 		    SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3108 		break;
3109 	case 0x1A: /* ^Z */
3110 	        if (!(es->style & ES_READONLY))
3111 		    SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3112 		break;
3113 
3114 	default:
3115 		/*If Edit control style is ES_NUMBER allow users to key in only numeric values*/
3116 		if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') )
3117 			break;
3118 
3119 		if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127))
3120 			EDIT_EM_ReplaceSel(es, TRUE, &c, 1, TRUE, TRUE);
3121 		break;
3122 	}
3123     return 1;
3124 }
3125 
3126 
3127 /*********************************************************************
3128  *
3129  *	EDIT_ContextMenuCommand
3130  *
3131  */
3132 static void EDIT_ContextMenuCommand(EDITSTATE *es, UINT id)
3133 {
3134 	switch (id) {
3135 		case EM_UNDO:
3136                         SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3137 			break;
3138 		case WM_CUT:
3139                         SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3140 			break;
3141 		case WM_COPY:
3142                         SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3143 			break;
3144 		case WM_PASTE:
3145                         SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3146 			break;
3147 		case WM_CLEAR:
3148                         SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0);
3149 			break;
3150 		case EM_SETSEL:
3151                         SendMessageW(es->hwndSelf, EM_SETSEL, 0, -1);
3152 			break;
3153 		default:
3154 			ERR("unknown menu item, please report\n");
3155 			break;
3156 	}
3157 }
3158 
3159 
3160 /*********************************************************************
3161  *
3162  *	WM_CONTEXTMENU
3163  *
3164  *	Note: the resource files resource/sysres_??.rc cannot define a
3165  *		single popup menu.  Hence we use a (dummy) menubar
3166  *		containing the single popup menu as its first item.
3167  *
3168  *	FIXME: the message identifiers have been chosen arbitrarily,
3169  *		hence we use MF_BYPOSITION.
3170  *		We might as well use the "real" values (anybody knows ?)
3171  *		The menu definition is in resources/sysres_??.rc.
3172  *		Once these are OK, we better use MF_BYCOMMAND here
3173  *		(as we do in EDIT_WM_Command()).
3174  *
3175  */
3176 static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y)
3177 {
3178     HMENU menu = LoadMenuA(GetModuleHandleA("user32.dll"), "EDITMENU");
3179     HMENU popup = GetSubMenu(menu, 0);
3180     UINT start = es->selection_start;
3181     UINT end = es->selection_end;
3182     UINT cmd;
3183     POINT pt;
3184 
3185     ORDER_UINT(start, end);
3186 
3187     /* undo */
3188     EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3189     /* cut */
3190     EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3191     /* copy */
3192     EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3193     /* paste */
3194     EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3195     /* delete */
3196     EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3197     /* select all */
3198     EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != get_text_length(es)) ? MF_ENABLED : MF_GRAYED));
3199 
3200     pt.x = x;
3201     pt.y = y;
3202 
3203     if (pt.x == -1 && pt.y == -1) /* passed via VK_APPS press/release */
3204     {
3205         RECT rc;
3206 
3207         /* Windows places the menu at the edit's center in this case */
3208         GetClientRect(es->hwndSelf, &rc);
3209         pt.x = rc.left + (rc.right - rc.left) / 2;
3210         pt.y = rc.top + (rc.bottom - rc.top) / 2;
3211         ClientToScreen(es->hwndSelf, &pt);
3212     }
3213 
3214     if (!(es->flags & EF_FOCUSED))
3215         SetFocus(es->hwndSelf);
3216 
3217     cmd = TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
3218             pt.x, pt.y, 0, es->hwndSelf, NULL);
3219 
3220     if (cmd)
3221         EDIT_ContextMenuCommand(es, cmd);
3222 
3223     DestroyMenu(menu);
3224 }
3225 
3226 
3227 /*********************************************************************
3228  *
3229  *	WM_GETTEXT
3230  *
3231  */
3232 static INT EDIT_WM_GetText(const EDITSTATE *es, INT count, LPWSTR dst)
3233 {
3234     if (!count)
3235         return 0;
3236 
3237     lstrcpynW(dst, es->text, count);
3238     return strlenW(dst);
3239 }
3240 
3241 /*********************************************************************
3242  *
3243  *	EDIT_CheckCombo
3244  *
3245  */
3246 static BOOL EDIT_CheckCombo(EDITSTATE *es, UINT msg, INT key)
3247 {
3248     HWND hLBox = es->hwndListBox;
3249     HWND hCombo;
3250     BOOL bDropped;
3251     int  nEUI;
3252 
3253     if (!hLBox)
3254         return FALSE;
3255 
3256     hCombo   = GetParent(es->hwndSelf);
3257     bDropped = TRUE;
3258     nEUI     = 0;
3259 
3260     TRACE("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key);
3261 
3262     if (key == VK_UP || key == VK_DOWN)
3263     {
3264         if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
3265             nEUI = 1;
3266 
3267         if (msg == WM_KEYDOWN || nEUI)
3268             bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3269     }
3270 
3271     switch (msg)
3272     {
3273     case WM_KEYDOWN:
3274         if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3275         {
3276             /* make sure ComboLBox pops up */
3277             SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3278             key = VK_F4;
3279             nEUI = 2;
3280         }
3281 
3282         SendMessageW(hLBox, WM_KEYDOWN, key, 0);
3283         break;
3284 
3285     case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3286         if (nEUI)
3287            SendMessageW(hCombo, CB_SHOWDROPDOWN, !bDropped, 0);
3288         else
3289            SendMessageW(hLBox, WM_KEYDOWN, VK_F4, 0);
3290         break;
3291     }
3292 
3293     if (nEUI == 2)
3294         SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3295 
3296     return TRUE;
3297 }
3298 
3299 
3300 /*********************************************************************
3301  *
3302  *	WM_KEYDOWN
3303  *
3304  *	Handling of special keys that don't produce a WM_CHAR
3305  *	(i.e. non-printable keys) & Backspace & Delete
3306  *
3307  */
3308 static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key)
3309 {
3310 	BOOL shift;
3311 	BOOL control;
3312 
3313 	if (GetKeyState(VK_MENU) & 0x8000)
3314 		return 0;
3315 
3316 	shift = GetKeyState(VK_SHIFT) & 0x8000;
3317 	control = GetKeyState(VK_CONTROL) & 0x8000;
3318 
3319 	switch (key) {
3320 	case VK_F4:
3321 	case VK_UP:
3322 		if (EDIT_CheckCombo(es, WM_KEYDOWN, key) || key == VK_F4)
3323 			break;
3324 
3325 		/* fall through */
3326 	case VK_LEFT:
3327 		if ((es->style & ES_MULTILINE) && (key == VK_UP))
3328 			EDIT_MoveUp_ML(es, shift);
3329 		else
3330 			if (control)
3331 				EDIT_MoveWordBackward(es, shift);
3332 			else
3333 				EDIT_MoveBackward(es, shift);
3334 		break;
3335 	case VK_DOWN:
3336 		if (EDIT_CheckCombo(es, WM_KEYDOWN, key))
3337 			break;
3338 		/* fall through */
3339 	case VK_RIGHT:
3340 		if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3341 			EDIT_MoveDown_ML(es, shift);
3342 		else if (control)
3343 			EDIT_MoveWordForward(es, shift);
3344 		else
3345 			EDIT_MoveForward(es, shift);
3346 		break;
3347 	case VK_HOME:
3348 		EDIT_MoveHome(es, shift, control);
3349 		break;
3350 	case VK_END:
3351 		EDIT_MoveEnd(es, shift, control);
3352 		break;
3353 	case VK_PRIOR:
3354 		if (es->style & ES_MULTILINE)
3355 			EDIT_MovePageUp_ML(es, shift);
3356 		else
3357 			EDIT_CheckCombo(es, WM_KEYDOWN, key);
3358 		break;
3359 	case VK_NEXT:
3360 		if (es->style & ES_MULTILINE)
3361 			EDIT_MovePageDown_ML(es, shift);
3362 		else
3363 			EDIT_CheckCombo(es, WM_KEYDOWN, key);
3364 		break;
3365 	case VK_DELETE:
3366 		if (!(es->style & ES_READONLY) && !(shift && control)) {
3367 			if (es->selection_start != es->selection_end) {
3368 				if (shift)
3369 					EDIT_WM_Cut(es);
3370 				else
3371 					EDIT_WM_Clear(es);
3372 			} else {
3373 				if (shift) {
3374 					/* delete character left of caret */
3375 					EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3376 					EDIT_MoveBackward(es, TRUE);
3377 					EDIT_WM_Clear(es);
3378 				} else if (control) {
3379 					/* delete to end of line */
3380 					EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3381 					EDIT_MoveEnd(es, TRUE, FALSE);
3382 					EDIT_WM_Clear(es);
3383 				} else {
3384 					/* delete character right of caret */
3385 					EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3386 					EDIT_MoveForward(es, TRUE);
3387 					EDIT_WM_Clear(es);
3388 				}
3389 			}
3390 		}
3391 		break;
3392 	case VK_INSERT:
3393 		if (shift) {
3394 			if (!(es->style & ES_READONLY))
3395 				EDIT_WM_Paste(es);
3396 		} else if (control)
3397 			EDIT_WM_Copy(es);
3398 		break;
3399 	case VK_RETURN:
3400 	    /* If the edit doesn't want the return send a message to the default object */
3401 	    if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN))
3402 	    {
3403                 DWORD dw;
3404 
3405                 if (!EDIT_IsInsideDialog(es)) break;
3406                 if (control) break;
3407                 dw = SendMessageW(es->hwndParent, DM_GETDEFID, 0, 0);
3408                 if (HIWORD(dw) == DC_HASDEFID)
3409                 {
3410                     HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw));
3411                     if (hwDefCtrl)
3412                     {
3413                         SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
3414                         PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
3415                     }
3416                 }
3417 	    }
3418 	    break;
3419         case VK_ESCAPE:
3420             if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3421                 PostMessageW(es->hwndParent, WM_CLOSE, 0, 0);
3422             break;
3423         case VK_TAB:
3424             if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3425                 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0);
3426             break;
3427         case 'A':
3428             if (control)
3429             {
3430                 if (EDIT_EM_SetSel(es, 0, get_text_length(es), FALSE))
3431                 {
3432                     if (!notify_parent(es, EN_UPDATE)) break;
3433                     notify_parent(es, EN_CHANGE);
3434                 }
3435             }
3436             break;
3437 	}
3438         return TRUE;
3439 }
3440 
3441 
3442 /*********************************************************************
3443  *
3444  *	WM_KILLFOCUS
3445  *
3446  */
3447 static LRESULT EDIT_WM_KillFocus(HTHEME theme, EDITSTATE *es)
3448 {
3449     UINT flags = RDW_INVALIDATE | RDW_UPDATENOW;
3450     HWND hwndSelf = es->hwndSelf;
3451 
3452     es->flags &= ~EF_FOCUSED;
3453     DestroyCaret();
3454     if (!(es->style & ES_NOHIDESEL))
3455         EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3456     if (!notify_parent(es, EN_KILLFOCUS)) return 0;
3457     /* Throw away left over scroll when we lose focus */
3458     es->wheelDeltaRemainder = 0;
3459 
3460     if (theme)
3461         flags |= RDW_FRAME;
3462 
3463     RedrawWindow(hwndSelf, NULL, NULL, flags);
3464     return 0;
3465 }
3466 
3467 
3468 /*********************************************************************
3469  *
3470  *	WM_LBUTTONDBLCLK
3471  *
3472  *	The caret position has been set on the WM_LBUTTONDOWN message
3473  *
3474  */
3475 static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es)
3476 {
3477 	INT s;
3478 	INT e = es->selection_end;
3479 	INT l;
3480 	INT li;
3481 	INT ll;
3482 
3483 	es->bCaptureState = TRUE;
3484 	SetCapture(es->hwndSelf);
3485 
3486 	l = EDIT_EM_LineFromChar(es, e);
3487 	li = EDIT_EM_LineIndex(es, l);
3488 	ll = EDIT_EM_LineLength(es, e);
3489 	s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
3490 	e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
3491 	EDIT_EM_SetSel(es, s, e, FALSE);
3492 	EDIT_EM_ScrollCaret(es);
3493 	es->region_posx = es->region_posy = 0;
3494 	SetTimer(es->hwndSelf, 0, 100, NULL);
3495 	return 0;
3496 }
3497 
3498 
3499 /*********************************************************************
3500  *
3501  *	WM_LBUTTONDOWN
3502  *
3503  */
3504 static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y)
3505 {
3506 	INT e;
3507 	BOOL after_wrap;
3508 
3509 	es->bCaptureState = TRUE;
3510 	SetCapture(es->hwndSelf);
3511 	EDIT_ConfinePoint(es, &x, &y);
3512 	e = EDIT_CharFromPos(es, x, y, &after_wrap);
3513 	EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3514 	EDIT_EM_ScrollCaret(es);
3515 	es->region_posx = es->region_posy = 0;
3516 	SetTimer(es->hwndSelf, 0, 100, NULL);
3517 
3518 	if (!(es->flags & EF_FOCUSED))
3519             SetFocus(es->hwndSelf);
3520 
3521 	return 0;
3522 }
3523 
3524 
3525 /*********************************************************************
3526  *
3527  *	WM_LBUTTONUP
3528  *
3529  */
3530 static LRESULT EDIT_WM_LButtonUp(EDITSTATE *es)
3531 {
3532 	if (es->bCaptureState) {
3533 		KillTimer(es->hwndSelf, 0);
3534 		if (GetCapture() == es->hwndSelf) ReleaseCapture();
3535 	}
3536 	es->bCaptureState = FALSE;
3537 	return 0;
3538 }
3539 
3540 
3541 /*********************************************************************
3542  *
3543  *	WM_MBUTTONDOWN
3544  *
3545  */
3546 static LRESULT EDIT_WM_MButtonDown(EDITSTATE *es)
3547 {
3548     SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3549     return 0;
3550 }
3551 
3552 
3553 /*********************************************************************
3554  *
3555  *	WM_MOUSEMOVE
3556  *
3557  */
3558 static LRESULT EDIT_WM_MouseMove(EDITSTATE *es, INT x, INT y)
3559 {
3560 	INT e;
3561 	BOOL after_wrap;
3562 	INT prex, prey;
3563 
3564         /* If the mouse has been captured by process other than the edit control itself,
3565          * the windows edit controls will not select the strings with mouse move.
3566          */
3567         if (!es->bCaptureState || GetCapture() != es->hwndSelf)
3568 		return 0;
3569 
3570 	/*
3571 	 *	FIXME: gotta do some scrolling if outside client
3572 	 *		area.  Maybe reset the timer ?
3573 	 */
3574 	prex = x; prey = y;
3575 	EDIT_ConfinePoint(es, &x, &y);
3576 	es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3577 	es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3578 	e = EDIT_CharFromPos(es, x, y, &after_wrap);
3579 	EDIT_EM_SetSel(es, es->selection_start, e, after_wrap);
3580 	EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP);
3581 	return 0;
3582 }
3583 
3584 
3585 /*********************************************************************
3586  *
3587  *	WM_PAINT
3588  *
3589  */
3590 static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc)
3591 {
3592 	PAINTSTRUCT ps;
3593 	INT i;
3594 	HDC dc;
3595 	HFONT old_font = 0;
3596 	RECT rc;
3597 	RECT rcClient;
3598 	RECT rcLine;
3599 	RECT rcRgn;
3600 	HBRUSH brush;
3601 	HBRUSH old_brush;
3602 	INT bw, bh;
3603 	BOOL rev = es->bEnableState &&
3604 				((es->flags & EF_FOCUSED) ||
3605 					(es->style & ES_NOHIDESEL));
3606         dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps);
3607 
3608 	/* The dc we use for calculating may not be the one we paint into.
3609 	   This is the safest action. */
3610 	EDIT_InvalidateUniscribeData(es);
3611 	GetClientRect(es->hwndSelf, &rcClient);
3612 
3613 	/* get the background brush */
3614 	brush = EDIT_NotifyCtlColor(es, dc);
3615 
3616 	/* paint the border and the background */
3617 	IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3618 
3619 	if(es->style & WS_BORDER) {
3620 		bw = GetSystemMetrics(SM_CXBORDER);
3621 		bh = GetSystemMetrics(SM_CYBORDER);
3622 		rc = rcClient;
3623 		if(es->style & ES_MULTILINE) {
3624 			if(es->style & WS_HSCROLL) rc.bottom+=bh;
3625 			if(es->style & WS_VSCROLL) rc.right+=bw;
3626 		}
3627 
3628 		/* Draw the frame. Same code as in nonclient.c */
3629 		old_brush = SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME));
3630 		PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY);
3631 		PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY);
3632 		PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY);
3633 		PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY);
3634 		SelectObject(dc, old_brush);
3635 
3636 		/* Keep the border clean */
3637 		IntersectClipRect(dc, rc.left+bw, rc.top+bh,
3638 		    max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh));
3639 	}
3640 
3641 	GetClipBox(dc, &rc);
3642 	FillRect(dc, &rc, brush);
3643 
3644 	IntersectClipRect(dc, es->format_rect.left,
3645 				es->format_rect.top,
3646 				es->format_rect.right,
3647 				es->format_rect.bottom);
3648 	if (es->style & ES_MULTILINE) {
3649 		rc = rcClient;
3650 		IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3651 	}
3652 	if (es->font)
3653 		old_font = SelectObject(dc, es->font);
3654 
3655 	if (!es->bEnableState)
3656 		SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3657 	GetClipBox(dc, &rcRgn);
3658 	if (es->style & ES_MULTILINE) {
3659 		INT vlc = get_vertical_line_count(es);
3660 		for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3661 			EDIT_UpdateUniscribeData(es, dc, i);
3662 			EDIT_GetLineRect(es, i, 0, -1, &rcLine);
3663 			if (IntersectRect(&rc, &rcRgn, &rcLine))
3664 				EDIT_PaintLine(es, dc, i, rev);
3665 		}
3666 	} else {
3667 		EDIT_UpdateUniscribeData(es, dc, 0);
3668 		EDIT_GetLineRect(es, 0, 0, -1, &rcLine);
3669 		if (IntersectRect(&rc, &rcRgn, &rcLine))
3670 			EDIT_PaintLine(es, dc, 0, rev);
3671 	}
3672 	if (es->font)
3673 		SelectObject(dc, old_font);
3674 
3675         if (!hdc)
3676             EndPaint(es->hwndSelf, &ps);
3677 }
3678 
3679 static void EDIT_WM_NCPaint(HWND hwnd, HRGN region)
3680 {
3681     DWORD exStyle = GetWindowLongW(hwnd, GWL_EXSTYLE);
3682     HTHEME theme = GetWindowTheme(hwnd);
3683     HRGN cliprgn = region;
3684 
3685     if (theme && exStyle & WS_EX_CLIENTEDGE)
3686     {
3687         HDC dc;
3688         RECT r;
3689         int cxEdge = GetSystemMetrics(SM_CXEDGE),
3690             cyEdge = GetSystemMetrics(SM_CYEDGE);
3691         const int part = EP_EDITTEXT;
3692         int state = ETS_NORMAL;
3693         DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
3694 
3695         if (!IsWindowEnabled(hwnd))
3696             state = ETS_DISABLED;
3697         else if (dwStyle & ES_READONLY)
3698             state = ETS_READONLY;
3699         else if (GetFocus() == hwnd)
3700             state = ETS_FOCUSED;
3701 
3702         GetWindowRect(hwnd, &r);
3703 
3704         /* New clipping region passed to default proc to exclude border */
3705         cliprgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge,
3706             r.right - cxEdge, r.bottom - cyEdge);
3707         if (region != (HRGN)1)
3708             CombineRgn(cliprgn, cliprgn, region, RGN_AND);
3709         OffsetRect(&r, -r.left, -r.top);
3710 
3711 #ifdef __REACTOS__ /* r73789 */
3712         dc = GetWindowDC(hwnd);
3713         /* Exclude client part */
3714         ExcludeClipRect(dc,
3715                         r.left + cxEdge,
3716                         r.top + cyEdge,
3717                         r.right - cxEdge,
3718                         r.bottom -cyEdge);
3719 #else
3720         dc = GetDCEx(hwnd, region, DCX_WINDOW|DCX_INTERSECTRGN);
3721         OffsetRect(&r, -r.left, -r.top);
3722 #endif
3723 
3724         if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
3725             DrawThemeParentBackground(hwnd, dc, &r);
3726         DrawThemeBackground(theme, dc, part, state, &r, 0);
3727         ReleaseDC(hwnd, dc);
3728     }
3729 
3730     /* Call default proc to get the scrollbars etc. also painted */
3731     DefWindowProcW (hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0);
3732 }
3733 
3734 /*********************************************************************
3735  *
3736  *	WM_SETFOCUS
3737  *
3738  */
3739 static void EDIT_WM_SetFocus(HTHEME theme, EDITSTATE *es)
3740 {
3741     UINT flags = RDW_INVALIDATE | RDW_UPDATENOW;
3742 
3743     es->flags |= EF_FOCUSED;
3744 
3745     if (!(es->style & ES_NOHIDESEL))
3746         EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3747 
3748 #ifdef __REACTOS__
3749     SystemParametersInfo(SPI_GETCARETWIDTH, 0, &es->dwCaretWidth, 0);
3750     CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height);
3751 #else
3752     CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3753 #endif
3754     EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
3755     ShowCaret(es->hwndSelf);
3756     if (!notify_parent(es, EN_SETFOCUS)) return;
3757 
3758     if (theme)
3759         flags |= RDW_FRAME | RDW_ERASE;
3760 
3761     RedrawWindow(es->hwndSelf, NULL, NULL, flags);
3762 }
3763 
3764 
3765 /*********************************************************************
3766  *
3767  *	WM_SETFONT
3768  *
3769  * With Win95 look the margins are set to default font value unless
3770  * the system font (font == 0) is being set, in which case they are left
3771  * unchanged.
3772  *
3773  */
3774 static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw)
3775 {
3776 	TEXTMETRICW tm;
3777 	HDC dc;
3778 	HFONT old_font = 0;
3779 	RECT clientRect;
3780 
3781 	es->font = font;
3782 	EDIT_InvalidateUniscribeData(es);
3783 	dc = GetDC(es->hwndSelf);
3784 	if (font)
3785 		old_font = SelectObject(dc, font);
3786 	GetTextMetricsW(dc, &tm);
3787 	es->line_height = tm.tmHeight;
3788 	es->char_width = tm.tmAveCharWidth;
3789 	if (font)
3790 		SelectObject(dc, old_font);
3791 	ReleaseDC(es->hwndSelf, dc);
3792 
3793 	/* Reset the format rect and the margins */
3794 	GetClientRect(es->hwndSelf, &clientRect);
3795 	EDIT_SetRectNP(es, &clientRect);
3796 	EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3797 			   EC_USEFONTINFO, EC_USEFONTINFO, FALSE);
3798 
3799 	if (es->style & ES_MULTILINE)
3800 		EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
3801 	else
3802 	    EDIT_CalcLineWidth_SL(es);
3803 
3804 	if (redraw)
3805 		EDIT_UpdateText(es, NULL, TRUE);
3806 	if (es->flags & EF_FOCUSED) {
3807 		DestroyCaret();
3808 #ifdef __REACTOS__
3809 		CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height);
3810 #else
3811 		CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3812 #endif
3813 		EDIT_SetCaretPos(es, es->selection_end,
3814 				 es->flags & EF_AFTER_WRAP);
3815 		ShowCaret(es->hwndSelf);
3816 	}
3817 #ifdef __REACTOS__
3818     if (ImmIsIME(GetKeyboardLayout(0)))
3819     {
3820         LOGFONTW lf;
3821         HIMC hIMC = ImmGetContext(es->hwndSelf);
3822         if (font == NULL)
3823             font = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
3824         GetObjectW(font, sizeof(lf), &lf);
3825         ImmSetCompositionFontW(hIMC, &lf);
3826         ImmReleaseContext(es->hwndSelf, hIMC);
3827     }
3828 #endif
3829 }
3830 
3831 
3832 /*********************************************************************
3833  *
3834  *	WM_SETTEXT
3835  *
3836  * NOTES
3837  *  For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3838  *  The modified flag is reset. No notifications are sent.
3839  *
3840  *  For single-line controls, reception of WM_SETTEXT triggers:
3841  *  The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3842  *
3843  */
3844 static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text)
3845 {
3846     if (es->flags & EF_UPDATE)
3847         /* fixed this bug once; complain if we see it about to happen again. */
3848         ERR("SetSel may generate UPDATE message whose handler may reset "
3849             "selection.\n");
3850 
3851     EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE);
3852     if (text)
3853     {
3854 	TRACE("%s\n", debugstr_w(text));
3855 	EDIT_EM_ReplaceSel(es, FALSE, text, strlenW(text), FALSE, FALSE);
3856     }
3857     else
3858     {
3859 	TRACE("<NULL>\n");
3860 	EDIT_EM_ReplaceSel(es, FALSE, NULL, 0, FALSE, FALSE);
3861     }
3862     es->x_offset = 0;
3863     es->flags &= ~EF_MODIFIED;
3864     EDIT_EM_SetSel(es, 0, 0, FALSE);
3865 
3866     /* Send the notification after the selection start and end have been set
3867      * edit control doesn't send notification on WM_SETTEXT
3868      * if it is multiline, or it is part of combobox
3869      */
3870     if( !((es->style & ES_MULTILINE) || es->hwndListBox))
3871     {
3872         if (!notify_parent(es, EN_UPDATE)) return;
3873         if (!notify_parent(es, EN_CHANGE)) return;
3874     }
3875     EDIT_EM_ScrollCaret(es);
3876     EDIT_UpdateScrollInfo(es);
3877     EDIT_InvalidateUniscribeData(es);
3878 }
3879 
3880 
3881 /*********************************************************************
3882  *
3883  *	WM_SIZE
3884  *
3885  */
3886 static void EDIT_WM_Size(EDITSTATE *es, UINT action)
3887 {
3888 	if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3889 		RECT rc;
3890 		GetClientRect(es->hwndSelf, &rc);
3891 		EDIT_SetRectNP(es, &rc);
3892 		EDIT_UpdateText(es, NULL, TRUE);
3893 	}
3894 }
3895 
3896 
3897 /*********************************************************************
3898  *
3899  *	WM_STYLECHANGED
3900  *
3901  * This message is sent by SetWindowLong on having changed either the Style
3902  * or the extended style.
3903  *
3904  * We ensure that the window's version of the styles and the EDITSTATE's agree.
3905  *
3906  * See also EDIT_WM_NCCreate
3907  *
3908  * It appears that the Windows version of the edit control allows the style
3909  * (as retrieved by GetWindowLong) to be any value and maintains an internal
3910  * style variable which will generally be different.  In this function we
3911  * update the internal style based on what changed in the externally visible
3912  * style.
3913  *
3914  * Much of this content as based upon the MSDN, especially:
3915  *  Platform SDK Documentation -> User Interface Services ->
3916  *      Windows User Interface -> Edit Controls -> Edit Control Reference ->
3917  *      Edit Control Styles
3918  */
3919 static LRESULT  EDIT_WM_StyleChanged ( EDITSTATE *es, WPARAM which, const STYLESTRUCT *style)
3920 {
3921         if (GWL_STYLE == which) {
3922                 DWORD style_change_mask;
3923                 DWORD new_style;
3924                 /* Only a subset of changes can be applied after the control
3925                  * has been created.
3926                  */
3927                 style_change_mask = ES_UPPERCASE | ES_LOWERCASE |
3928                                     ES_NUMBER;
3929                 if (es->style & ES_MULTILINE)
3930                         style_change_mask |= ES_WANTRETURN;
3931 
3932                 new_style = style->styleNew & style_change_mask;
3933 
3934                 /* Number overrides lowercase overrides uppercase (at least it
3935                  * does in Win95).  However I'll bet that ES_NUMBER would be
3936                  * invalid under Win 3.1.
3937                  */
3938                 if (new_style & ES_NUMBER) {
3939                         ; /* do not override the ES_NUMBER */
3940                 }  else if (new_style & ES_LOWERCASE) {
3941                         new_style &= ~ES_UPPERCASE;
3942                 }
3943 
3944                 es->style = (es->style & ~style_change_mask) | new_style;
3945         } else if (GWL_EXSTYLE == which) {
3946                 ; /* FIXME - what is needed here */
3947         } else {
3948                 WARN ("Invalid style change %ld\n",which);
3949         }
3950 
3951         return 0;
3952 }
3953 
3954 /*********************************************************************
3955  *
3956  *	WM_SYSKEYDOWN
3957  *
3958  */
3959 static LRESULT EDIT_WM_SysKeyDown(EDITSTATE *es, INT key, DWORD key_data)
3960 {
3961 	if ((key == VK_BACK) && (key_data & 0x2000)) {
3962 		if (EDIT_EM_CanUndo(es))
3963 			EDIT_EM_Undo(es);
3964 		return 0;
3965 	} else if (key == VK_UP || key == VK_DOWN) {
3966 		if (EDIT_CheckCombo(es, WM_SYSKEYDOWN, key))
3967 			return 0;
3968 	}
3969 	return DefWindowProcW(es->hwndSelf, WM_SYSKEYDOWN, key, key_data);
3970 }
3971 
3972 
3973 /*********************************************************************
3974  *
3975  *	WM_TIMER
3976  *
3977  */
3978 static void EDIT_WM_Timer(EDITSTATE *es)
3979 {
3980 	if (es->region_posx < 0) {
3981 		EDIT_MoveBackward(es, TRUE);
3982 	} else if (es->region_posx > 0) {
3983 		EDIT_MoveForward(es, TRUE);
3984 	}
3985 /*
3986  *	FIXME: gotta do some vertical scrolling here, like
3987  *		EDIT_EM_LineScroll(hwnd, 0, 1);
3988  */
3989 }
3990 
3991 /*********************************************************************
3992  *
3993  *	WM_HSCROLL
3994  *
3995  */
3996 static LRESULT EDIT_WM_HScroll(EDITSTATE *es, INT action, INT pos)
3997 {
3998 	INT dx;
3999 	INT fw;
4000 
4001 	if (!(es->style & ES_MULTILINE))
4002 		return 0;
4003 
4004 	if (!(es->style & ES_AUTOHSCROLL))
4005 		return 0;
4006 
4007 	dx = 0;
4008 	fw = es->format_rect.right - es->format_rect.left;
4009 	switch (action) {
4010 	case SB_LINELEFT:
4011 		TRACE("SB_LINELEFT\n");
4012 		if (es->x_offset)
4013 			dx = -es->char_width;
4014 		break;
4015 	case SB_LINERIGHT:
4016 		TRACE("SB_LINERIGHT\n");
4017 		if (es->x_offset < es->text_width)
4018 			dx = es->char_width;
4019 		break;
4020 	case SB_PAGELEFT:
4021 		TRACE("SB_PAGELEFT\n");
4022 		if (es->x_offset)
4023 			dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4024 		break;
4025 	case SB_PAGERIGHT:
4026 		TRACE("SB_PAGERIGHT\n");
4027 		if (es->x_offset < es->text_width)
4028 			dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4029 		break;
4030 	case SB_LEFT:
4031 		TRACE("SB_LEFT\n");
4032 		if (es->x_offset)
4033 			dx = -es->x_offset;
4034 		break;
4035 	case SB_RIGHT:
4036 		TRACE("SB_RIGHT\n");
4037 		if (es->x_offset < es->text_width)
4038 			dx = es->text_width - es->x_offset;
4039 		break;
4040 	case SB_THUMBTRACK:
4041 		TRACE("SB_THUMBTRACK %d\n", pos);
4042 		es->flags |= EF_HSCROLL_TRACK;
4043 		if(es->style & WS_HSCROLL)
4044 		    dx = pos - es->x_offset;
4045 		else
4046 		{
4047 		    INT fw, new_x;
4048 		    /* Sanity check */
4049 		    if(pos < 0 || pos > 100) return 0;
4050 		    /* Assume default scroll range 0-100 */
4051 		    fw = es->format_rect.right - es->format_rect.left;
4052 		    new_x = pos * (es->text_width - fw) / 100;
4053 		    dx = es->text_width ? (new_x - es->x_offset) : 0;
4054 		}
4055 		break;
4056 	case SB_THUMBPOSITION:
4057 		TRACE("SB_THUMBPOSITION %d\n", pos);
4058 		es->flags &= ~EF_HSCROLL_TRACK;
4059 		if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL)
4060 		    dx = pos - es->x_offset;
4061 		else
4062 		{
4063 		    INT fw, new_x;
4064 		    /* Sanity check */
4065 		    if(pos < 0 || pos > 100) return 0;
4066 		    /* Assume default scroll range 0-100 */
4067 		    fw = es->format_rect.right - es->format_rect.left;
4068 		    new_x = pos * (es->text_width - fw) / 100;
4069 		    dx = es->text_width ? (new_x - es->x_offset) : 0;
4070 		}
4071 		if (!dx) {
4072 			/* force scroll info update */
4073 			EDIT_UpdateScrollInfo(es);
4074 			notify_parent(es, EN_HSCROLL);
4075 		}
4076 		break;
4077 	case SB_ENDSCROLL:
4078 		TRACE("SB_ENDSCROLL\n");
4079 		break;
4080 	/*
4081 	 *	FIXME : the next two are undocumented !
4082 	 *	Are we doing the right thing ?
4083 	 *	At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4084 	 *	although it's also a regular control message.
4085 	 */
4086 	case EM_GETTHUMB: /* this one is used by NT notepad */
4087 	{
4088 		LRESULT ret;
4089 		if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL)
4090 		    ret = GetScrollPos(es->hwndSelf, SB_HORZ);
4091 		else
4092 		{
4093 		    /* Assume default scroll range 0-100 */
4094 		    INT fw = es->format_rect.right - es->format_rect.left;
4095 		    ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
4096 		}
4097 		TRACE("EM_GETTHUMB: returning %ld\n", ret);
4098 		return ret;
4099 	}
4100 	case EM_LINESCROLL:
4101 		TRACE("EM_LINESCROLL16\n");
4102 		dx = pos;
4103 		break;
4104 
4105 	default:
4106 		ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4107                     action, action);
4108 		return 0;
4109 	}
4110 	if (dx)
4111 	{
4112 	    INT fw = es->format_rect.right - es->format_rect.left;
4113 	    /* check if we are going to move too far */
4114 	    if(es->x_offset + dx + fw > es->text_width)
4115 		dx = es->text_width - fw - es->x_offset;
4116 	    if(dx)
4117 		EDIT_EM_LineScroll_internal(es, dx, 0);
4118 	}
4119 	return 0;
4120 }
4121 
4122 
4123 /*********************************************************************
4124  *
4125  *	WM_VSCROLL
4126  *
4127  */
4128 static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos)
4129 {
4130 	INT dy;
4131 
4132 	if (!(es->style & ES_MULTILINE))
4133 		return 0;
4134 
4135 	if (!(es->style & ES_AUTOVSCROLL))
4136 		return 0;
4137 
4138 	dy = 0;
4139 	switch (action) {
4140 	case SB_LINEUP:
4141 	case SB_LINEDOWN:
4142 	case SB_PAGEUP:
4143 	case SB_PAGEDOWN:
4144 		TRACE("action %d (%s)\n", action, (action == SB_LINEUP ? "SB_LINEUP" :
4145 						   (action == SB_LINEDOWN ? "SB_LINEDOWN" :
4146 						    (action == SB_PAGEUP ? "SB_PAGEUP" :
4147 						     "SB_PAGEDOWN"))));
4148 		EDIT_EM_Scroll(es, action);
4149 		return 0;
4150 	case SB_TOP:
4151 		TRACE("SB_TOP\n");
4152 		dy = -es->y_offset;
4153 		break;
4154 	case SB_BOTTOM:
4155 		TRACE("SB_BOTTOM\n");
4156 		dy = es->line_count - 1 - es->y_offset;
4157 		break;
4158 	case SB_THUMBTRACK:
4159 		TRACE("SB_THUMBTRACK %d\n", pos);
4160 		es->flags |= EF_VSCROLL_TRACK;
4161 		if(es->style & WS_VSCROLL)
4162 		    dy = pos - es->y_offset;
4163 		else
4164 		{
4165 		    /* Assume default scroll range 0-100 */
4166 		    INT vlc, new_y;
4167 		    /* Sanity check */
4168 		    if(pos < 0 || pos > 100) return 0;
4169 		    vlc = get_vertical_line_count(es);
4170 		    new_y = pos * (es->line_count - vlc) / 100;
4171 		    dy = es->line_count ? (new_y - es->y_offset) : 0;
4172 		    TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4173 			    es->line_count, es->y_offset, pos, dy);
4174 		}
4175 		break;
4176 	case SB_THUMBPOSITION:
4177 		TRACE("SB_THUMBPOSITION %d\n", pos);
4178 		es->flags &= ~EF_VSCROLL_TRACK;
4179 		if(es->style & WS_VSCROLL)
4180 		    dy = pos - es->y_offset;
4181 		else
4182 		{
4183 		    /* Assume default scroll range 0-100 */
4184 		    INT vlc, new_y;
4185 		    /* Sanity check */
4186 		    if(pos < 0 || pos > 100) return 0;
4187 		    vlc = get_vertical_line_count(es);
4188 		    new_y = pos * (es->line_count - vlc) / 100;
4189 		    dy = es->line_count ? (new_y - es->y_offset) : 0;
4190 		    TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4191 			    es->line_count, es->y_offset, pos, dy);
4192 		}
4193 		if (!dy)
4194 		{
4195 			/* force scroll info update */
4196 			EDIT_UpdateScrollInfo(es);
4197 			notify_parent(es, EN_VSCROLL);
4198 		}
4199 		break;
4200 	case SB_ENDSCROLL:
4201 		TRACE("SB_ENDSCROLL\n");
4202 		break;
4203 	/*
4204 	 *	FIXME : the next two are undocumented !
4205 	 *	Are we doing the right thing ?
4206 	 *	At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4207 	 *	although it's also a regular control message.
4208 	 */
4209 	case EM_GETTHUMB: /* this one is used by NT notepad */
4210 	{
4211 		LRESULT ret;
4212 		if(GetWindowLongW( es->hwndSelf, GWL_STYLE ) & WS_VSCROLL)
4213 		    ret = GetScrollPos(es->hwndSelf, SB_VERT);
4214 		else
4215 		{
4216 		    /* Assume default scroll range 0-100 */
4217 		    INT vlc = get_vertical_line_count(es);
4218 		    ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
4219 		}
4220 		TRACE("EM_GETTHUMB: returning %ld\n", ret);
4221 		return ret;
4222 	}
4223 	case EM_LINESCROLL:
4224 		TRACE("EM_LINESCROLL %d\n", pos);
4225 		dy = pos;
4226 		break;
4227 
4228 	default:
4229 		ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4230                     action, action);
4231 		return 0;
4232 	}
4233 	if (dy)
4234 		EDIT_EM_LineScroll(es, 0, dy);
4235 	return 0;
4236 }
4237 
4238 /*********************************************************************
4239  *
4240  *	EM_GETTHUMB
4241  *
4242  *	FIXME: is this right ?  (or should it be only VSCROLL)
4243  *	(and maybe only for edit controls that really have their
4244  *	own scrollbars) (and maybe only for multiline controls ?)
4245  *	All in all: very poorly documented
4246  *
4247  */
4248 static LRESULT EDIT_EM_GetThumb(EDITSTATE *es)
4249 {
4250 	return MAKELONG(EDIT_WM_VScroll(es, EM_GETTHUMB, 0),
4251                         EDIT_WM_HScroll(es, EM_GETTHUMB, 0));
4252 }
4253 
4254 
4255 /********************************************************************
4256  *
4257  * The Following code is to handle inline editing from IMEs
4258  */
4259 
4260 static void EDIT_GetCompositionStr(HIMC hIMC, LPARAM CompFlag, EDITSTATE *es)
4261 {
4262     LONG buflen;
4263     LPWSTR lpCompStr;
4264     LPSTR lpCompStrAttr = NULL;
4265     DWORD dwBufLenAttr;
4266 
4267     buflen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
4268 
4269     if (buflen < 0)
4270     {
4271         return;
4272     }
4273 
4274     lpCompStr = heap_alloc(buflen);
4275     if (!lpCompStr)
4276     {
4277         ERR("Unable to allocate IME CompositionString\n");
4278         return;
4279     }
4280 
4281     if (buflen)
4282         ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, buflen);
4283 
4284     if (CompFlag & GCS_COMPATTR)
4285     {
4286         /*
4287          * We do not use the attributes yet. it would tell us what characters
4288          * are in transition and which are converted or decided upon
4289          */
4290         dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
4291         if (dwBufLenAttr)
4292         {
4293             dwBufLenAttr ++;
4294             lpCompStrAttr = heap_alloc(dwBufLenAttr + 1);
4295             if (!lpCompStrAttr)
4296             {
4297                 ERR("Unable to allocate IME Attribute String\n");
4298                 heap_free(lpCompStr);
4299                 return;
4300             }
4301             ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr,
4302                     dwBufLenAttr);
4303             lpCompStrAttr[dwBufLenAttr] = 0;
4304         }
4305     }
4306 
4307 #ifndef __REACTOS__ /* We don't use internal composition string. Rely on the composition window */
4308     /* check for change in composition start */
4309     if (es->selection_end < es->composition_start)
4310         es->composition_start = es->selection_end;
4311 
4312     /* replace existing selection string */
4313     es->selection_start = es->composition_start;
4314 
4315     if (es->composition_len > 0)
4316         es->selection_end = es->composition_start + es->composition_len;
4317     else
4318         es->selection_end = es->selection_start;
4319 
4320     EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, buflen / sizeof(WCHAR), TRUE, TRUE);
4321     es->composition_len = abs(es->composition_start - es->selection_end);
4322 
4323     es->selection_start = es->composition_start;
4324     es->selection_end = es->selection_start + es->composition_len;
4325 #endif
4326 
4327     heap_free(lpCompStrAttr);
4328     heap_free(lpCompStr);
4329 }
4330 
4331 static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es)
4332 {
4333     LONG buflen;
4334     LPWSTR lpResultStr;
4335 
4336     buflen = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
4337     if (buflen <= 0)
4338     {
4339         return;
4340     }
4341 
4342     lpResultStr = heap_alloc(buflen);
4343     if (!lpResultStr)
4344     {
4345         ERR("Unable to alloc buffer for IME string\n");
4346         return;
4347     }
4348 
4349     ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, buflen);
4350 
4351 #ifndef __REACTOS__
4352     /* check for change in composition start */
4353     if (es->selection_end < es->composition_start)
4354         es->composition_start = es->selection_end;
4355 
4356     es->selection_start = es->composition_start;
4357     es->selection_end = es->composition_start + es->composition_len;
4358     EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, buflen / sizeof(WCHAR), TRUE, TRUE);
4359     es->composition_start = es->selection_end;
4360     es->composition_len = 0;
4361 #endif
4362 
4363     heap_free(lpResultStr);
4364 }
4365 
4366 static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es)
4367 {
4368     HIMC hIMC;
4369     int cursor;
4370 
4371 #ifdef __REACTOS__
4372     if (es->selection_start != es->selection_end)
4373         EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE);
4374 #else
4375     if (es->composition_len == 0 && es->selection_start != es->selection_end)
4376     {
4377         EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE);
4378         es->composition_start = es->selection_end;
4379     }
4380 #endif
4381 
4382     hIMC = ImmGetContext(hwnd);
4383     if (!hIMC)
4384         return;
4385 
4386     if (CompFlag & GCS_RESULTSTR)
4387     {
4388         EDIT_GetResultStr(hIMC, es);
4389         cursor = 0;
4390     }
4391     else
4392     {
4393         if (CompFlag & GCS_COMPSTR)
4394             EDIT_GetCompositionStr(hIMC, CompFlag, es);
4395 #ifdef __REACTOS__
4396         cursor = 0;
4397 #else
4398         cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, 0, 0);
4399 #endif
4400     }
4401     ImmReleaseContext(hwnd, hIMC);
4402     EDIT_SetCaretPos(es, es->selection_start + cursor, es->flags & EF_AFTER_WRAP);
4403 }
4404 
4405 
4406 /*********************************************************************
4407  *
4408  *	WM_NCCREATE
4409  *
4410  * See also EDIT_WM_StyleChanged
4411  */
4412 static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs)
4413 {
4414 	EDITSTATE *es;
4415 	UINT alloc_size;
4416 
4417     TRACE("Creating edit control, style = %08x\n", lpcs->style);
4418 
4419     if (!(es = heap_alloc_zero(sizeof(*es))))
4420         return FALSE;
4421     SetWindowLongPtrW( hwnd, 0, (LONG_PTR)es );
4422 
4423        /*
4424         *      Note: since the EDITSTATE has not been fully initialized yet,
4425         *            we can't use any API calls that may send
4426         *            WM_XXX messages before WM_NCCREATE is completed.
4427         */
4428 
4429     es->style = lpcs->style;
4430 
4431         es->bEnableState = !(es->style & WS_DISABLED);
4432 
4433 	es->hwndSelf = hwnd;
4434 	/* Save parent, which will be notified by EN_* messages */
4435 	es->hwndParent = lpcs->hwndParent;
4436 
4437 	if (es->style & ES_COMBO)
4438 	   es->hwndListBox = GetDlgItem(es->hwndParent, ID_CB_LISTBOX);
4439 
4440         /* FIXME: should we handle changes to WS_EX_RIGHT style after creation? */
4441         if (lpcs->dwExStyle & WS_EX_RIGHT) es->style |= ES_RIGHT;
4442 
4443         /* Number overrides lowercase overrides uppercase (at least it
4444          * does in Win95).  However I'll bet that ES_NUMBER would be
4445          * invalid under Win 3.1.
4446          */
4447         if (es->style & ES_NUMBER) {
4448                 ; /* do not override the ES_NUMBER */
4449         }  else if (es->style & ES_LOWERCASE) {
4450                 es->style &= ~ES_UPPERCASE;
4451         }
4452 	if (es->style & ES_MULTILINE) {
4453 		es->buffer_limit = BUFLIMIT_INITIAL;
4454 		if (es->style & WS_VSCROLL)
4455 			es->style |= ES_AUTOVSCROLL;
4456 		if (es->style & WS_HSCROLL)
4457 			es->style |= ES_AUTOHSCROLL;
4458 		es->style &= ~ES_PASSWORD;
4459 		if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4460                         /* Confirmed - RIGHT overrides CENTER */
4461 			if (es->style & ES_RIGHT)
4462 				es->style &= ~ES_CENTER;
4463 			es->style &= ~WS_HSCROLL;
4464 			es->style &= ~ES_AUTOHSCROLL;
4465 		}
4466 	} else {
4467 		es->buffer_limit = BUFLIMIT_INITIAL;
4468 		if ((es->style & ES_RIGHT) && (es->style & ES_CENTER))
4469 			es->style &= ~ES_CENTER;
4470 		es->style &= ~WS_HSCROLL;
4471 		es->style &= ~WS_VSCROLL;
4472 		if (es->style & ES_PASSWORD)
4473 			es->password_char = '*';
4474 	}
4475 
4476 	alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4477 	if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4478 	    goto cleanup;
4479 	es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4480 
4481 	if (!(es->undo_text = heap_alloc_zero((es->buffer_size + 1) * sizeof(WCHAR))))
4482 		goto cleanup;
4483 	es->undo_buffer_size = es->buffer_size;
4484 
4485 	if (es->style & ES_MULTILINE)
4486 	    if (!(es->first_line_def = heap_alloc_zero(sizeof(LINEDEF))))
4487 	        goto cleanup;
4488 	es->line_count = 1;
4489 
4490 	/*
4491 	 * In Win95 look and feel, the WS_BORDER style is replaced by the
4492 	 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4493 	 * control a nonclient area so we don't need to draw the border.
4494          * If WS_BORDER without WS_EX_CLIENTEDGE is specified we shouldn't have
4495          * a nonclient area and we should handle painting the border ourselves.
4496          *
4497          * When making modifications please ensure that the code still works
4498          * for edit controls created directly with style 0x50800000, exStyle 0
4499          * (which should have a single pixel border)
4500 	 */
4501 	if (lpcs->dwExStyle & WS_EX_CLIENTEDGE)
4502 		es->style &= ~WS_BORDER;
4503         else if (es->style & WS_BORDER)
4504 		SetWindowLongW(hwnd, GWL_STYLE, es->style & ~WS_BORDER);
4505 
4506 	return TRUE;
4507 
4508 cleanup:
4509 	SetWindowLongPtrW(es->hwndSelf, 0, 0);
4510 	EDIT_InvalidateUniscribeData(es);
4511 	heap_free(es->first_line_def);
4512 	heap_free(es->undo_text);
4513 	if (es->hloc32W) LocalFree(es->hloc32W);
4514 	heap_free(es->logAttr);
4515 	heap_free(es);
4516 	return FALSE;
4517 }
4518 
4519 
4520 /*********************************************************************
4521  *
4522  *	WM_CREATE
4523  *
4524  */
4525 static LRESULT EDIT_WM_Create(EDITSTATE *es, const WCHAR *name)
4526 {
4527     RECT clientRect;
4528 
4529     TRACE("%s\n", debugstr_w(name));
4530 
4531     /*
4532      * To initialize some final structure members, we call some helper
4533      * functions.  However, since the EDITSTATE is not consistent (i.e.
4534      * not fully initialized), we should be very careful which
4535      * functions can be called, and in what order.
4536      */
4537     EDIT_WM_SetFont(es, 0, FALSE);
4538     EDIT_EM_EmptyUndoBuffer(es);
4539 
4540     /* We need to calculate the format rect
4541        (applications may send EM_SETMARGINS before the control gets visible) */
4542     GetClientRect(es->hwndSelf, &clientRect);
4543     EDIT_SetRectNP(es, &clientRect);
4544 
4545     if (name && *name)
4546     {
4547         EDIT_EM_ReplaceSel(es, FALSE, name, strlenW(name), FALSE, FALSE);
4548         /* if we insert text to the editline, the text scrolls out
4549          * of the window, as the caret is placed after the insert
4550          * pos normally; thus we reset es->selection... to 0 and
4551          * update caret
4552          */
4553         es->selection_start = es->selection_end = 0;
4554         /* Adobe Photoshop does NOT like this. and MSDN says that EN_CHANGE
4555          * Messages are only to be sent when the USER does something to
4556          * change the contents. So I am removing this EN_CHANGE
4557          *
4558          * EDIT_NOTIFY_PARENT(es, EN_CHANGE);
4559          */
4560         EDIT_EM_ScrollCaret(es);
4561     }
4562 
4563     /* force scroll info update */
4564     EDIT_UpdateScrollInfo(es);
4565     OpenThemeData(es->hwndSelf, WC_EDITW);
4566 
4567     /* The rule seems to return 1 here for success */
4568     /* Power Builder masked edit controls will crash  */
4569     /* if not. */
4570     /* FIXME: is that in all cases so ? */
4571     return 1;
4572 }
4573 
4574 
4575 /*********************************************************************
4576  *
4577  *	WM_NCDESTROY
4578  *
4579  */
4580 static LRESULT EDIT_WM_NCDestroy(EDITSTATE *es)
4581 {
4582     LINEDEF *pc, *pp;
4583     HTHEME theme;
4584 
4585     theme = GetWindowTheme(es->hwndSelf);
4586     CloseThemeData(theme);
4587 
4588     /* The app can own the text buffer handle */
4589     if (es->hloc32W && (es->hloc32W != es->hlocapp))
4590         LocalFree(es->hloc32W);
4591 
4592     EDIT_InvalidateUniscribeData(es);
4593 
4594     pc = es->first_line_def;
4595     while (pc)
4596     {
4597         pp = pc->next;
4598         heap_free(pc);
4599         pc = pp;
4600     }
4601 
4602     SetWindowLongPtrW( es->hwndSelf, 0, 0 );
4603     heap_free(es->undo_text);
4604     heap_free(es);
4605 
4606     return 0;
4607 }
4608 
4609 static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
4610 {
4611     EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW(hwnd, 0);
4612     HTHEME theme = GetWindowTheme(hwnd);
4613     LRESULT result = 0;
4614     RECT *rect;
4615 
4616     TRACE("hwnd=%p msg=%#x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
4617 
4618     if (!es && msg != WM_NCCREATE)
4619         return DefWindowProcW(hwnd, msg, wParam, lParam);
4620 
4621     if (es && (msg != WM_NCDESTROY))
4622         EDIT_LockBuffer(es);
4623 
4624     switch (msg)
4625     {
4626     case EM_GETSEL:
4627         result = EDIT_EM_GetSel(es, (UINT *)wParam, (UINT *)lParam);
4628         break;
4629 
4630     case EM_SETSEL:
4631         EDIT_EM_SetSel(es, wParam, lParam, FALSE);
4632         EDIT_EM_ScrollCaret(es);
4633         result = 1;
4634         break;
4635 
4636     case EM_GETRECT:
4637         rect = (RECT *)lParam;
4638         if (rect)
4639             *rect = es->format_rect;
4640         break;
4641 
4642     case EM_SETRECT:
4643         if ((es->style & ES_MULTILINE) && lParam)
4644         {
4645             EDIT_SetRectNP(es, (RECT *)lParam);
4646             EDIT_UpdateText(es, NULL, TRUE);
4647         }
4648         break;
4649 
4650     case EM_SETRECTNP:
4651         if ((es->style & ES_MULTILINE) && lParam)
4652             EDIT_SetRectNP(es, (LPRECT)lParam);
4653         break;
4654 
4655     case EM_SCROLL:
4656         result = EDIT_EM_Scroll(es, (INT)wParam);
4657         break;
4658 
4659     case EM_LINESCROLL:
4660         result = (LRESULT)EDIT_EM_LineScroll(es, (INT)wParam, (INT)lParam);
4661         break;
4662 
4663     case EM_SCROLLCARET:
4664         EDIT_EM_ScrollCaret(es);
4665         result = 1;
4666         break;
4667 
4668     case EM_GETMODIFY:
4669         result = ((es->flags & EF_MODIFIED) != 0);
4670         break;
4671 
4672     case EM_SETMODIFY:
4673         if (wParam)
4674             es->flags |= EF_MODIFIED;
4675         else
4676             es->flags &= ~(EF_MODIFIED | EF_UPDATE);  /* reset pending updates */
4677         break;
4678 
4679     case EM_GETLINECOUNT:
4680         result = (es->style & ES_MULTILINE) ? es->line_count : 1;
4681         break;
4682 
4683     case EM_LINEINDEX:
4684         result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
4685         break;
4686 
4687     case EM_SETHANDLE:
4688         EDIT_EM_SetHandle(es, (HLOCAL)wParam);
4689         break;
4690 
4691     case EM_GETHANDLE:
4692         result = (LRESULT)EDIT_EM_GetHandle(es);
4693         break;
4694 
4695     case EM_GETTHUMB:
4696         result = EDIT_EM_GetThumb(es);
4697         break;
4698 
4699     /* these messages missing from specs */
4700     case 0x00bf:
4701     case 0x00c0:
4702     case 0x00c3:
4703     case 0x00ca:
4704         FIXME("undocumented message 0x%x, please report\n", msg);
4705         result = DefWindowProcW(hwnd, msg, wParam, lParam);
4706         break;
4707 
4708     case EM_LINELENGTH:
4709         result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
4710         break;
4711 
4712     case EM_REPLACESEL:
4713     {
4714         const WCHAR *textW = (const WCHAR *)lParam;
4715 
4716         EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, strlenW(textW), TRUE, TRUE);
4717         result = 1;
4718         break;
4719     }
4720 
4721     case EM_GETLINE:
4722         result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPWSTR)lParam);
4723         break;
4724 
4725     case EM_SETLIMITTEXT:
4726         EDIT_EM_SetLimitText(es, wParam);
4727         break;
4728 
4729     case EM_CANUNDO:
4730         result = (LRESULT)EDIT_EM_CanUndo(es);
4731         break;
4732 
4733     case EM_UNDO:
4734     case WM_UNDO:
4735         result = (LRESULT)EDIT_EM_Undo(es);
4736         break;
4737 
4738     case EM_FMTLINES:
4739         result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
4740         break;
4741 
4742     case EM_LINEFROMCHAR:
4743         result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
4744         break;
4745 
4746     case EM_SETTABSTOPS:
4747         result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
4748         break;
4749 
4750     case EM_SETPASSWORDCHAR:
4751         EDIT_EM_SetPasswordChar(es, wParam);
4752         break;
4753 
4754     case EM_EMPTYUNDOBUFFER:
4755         EDIT_EM_EmptyUndoBuffer(es);
4756         break;
4757 
4758     case EM_GETFIRSTVISIBLELINE:
4759         result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
4760         break;
4761 
4762     case EM_SETREADONLY:
4763     {
4764         DWORD old_style = es->style;
4765 
4766         if (wParam)
4767         {
4768             SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | ES_READONLY);
4769             es->style |= ES_READONLY;
4770         }
4771         else
4772         {
4773             SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) & ~ES_READONLY);
4774             es->style &= ~ES_READONLY;
4775         }
4776 
4777         if (old_style ^ es->style)
4778             InvalidateRect(es->hwndSelf, NULL, TRUE);
4779 
4780         result = 1;
4781         break;
4782     }
4783 
4784     case EM_SETWORDBREAKPROC:
4785         EDIT_EM_SetWordBreakProc(es, (void *)lParam);
4786         result = 1;
4787         break;
4788 
4789     case EM_GETWORDBREAKPROC:
4790         result = (LRESULT)es->word_break_proc;
4791         break;
4792 
4793     case EM_GETPASSWORDCHAR:
4794         result = es->password_char;
4795         break;
4796 
4797     case EM_SETMARGINS:
4798         EDIT_EM_SetMargins(es, (INT)wParam, LOWORD(lParam), HIWORD(lParam), TRUE);
4799         break;
4800 
4801     case EM_GETMARGINS:
4802         result = MAKELONG(es->left_margin, es->right_margin);
4803         break;
4804 
4805     case EM_GETLIMITTEXT:
4806         result = es->buffer_limit;
4807         break;
4808 
4809     case EM_POSFROMCHAR:
4810         if ((INT)wParam >= get_text_length(es)) result = -1;
4811         else result = EDIT_EM_PosFromChar(es, (INT)wParam, FALSE);
4812         break;
4813 
4814     case EM_CHARFROMPOS:
4815         result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
4816         break;
4817 
4818     /* End of the EM_ messages which were in numerical order; what order
4819      * are these in?  vaguely alphabetical?
4820      */
4821 
4822     case WM_NCCREATE:
4823         result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
4824         break;
4825 
4826     case WM_NCDESTROY:
4827         result = EDIT_WM_NCDestroy(es);
4828         es = NULL;
4829         break;
4830 
4831     case WM_GETDLGCODE:
4832         result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
4833 
4834         if (es->style & ES_MULTILINE)
4835             result |= DLGC_WANTALLKEYS;
4836 
4837         if (lParam)
4838         {
4839             MSG *msg = (MSG *)lParam;
4840             es->flags |= EF_DIALOGMODE;
4841 
4842             if (msg->message == WM_KEYDOWN)
4843             {
4844                 int vk = (int)msg->wParam;
4845 
4846                 if (es->hwndListBox)
4847                 {
4848                     if (vk == VK_RETURN || vk == VK_ESCAPE)
4849                         if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
4850                             result |= DLGC_WANTMESSAGE;
4851                 }
4852             }
4853         }
4854         break;
4855 
4856     case WM_IME_CHAR:
4857 #ifdef __REACTOS__
4858         result = DefWindowProcW(hwnd, msg, wParam, lParam);
4859         break;
4860 #endif
4861     case WM_CHAR:
4862     {
4863         WCHAR charW = wParam;
4864 
4865         if (es->hwndListBox)
4866         {
4867             if (charW == VK_RETURN || charW == VK_ESCAPE)
4868             {
4869                 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
4870                     SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0);
4871                 break;
4872             }
4873         }
4874         result = EDIT_WM_Char(es, charW);
4875         break;
4876     }
4877 
4878     case WM_UNICHAR:
4879         if (wParam == UNICODE_NOCHAR) return TRUE;
4880         if (wParam <= 0x000fffff)
4881         {
4882             if (wParam > 0xffff) /* convert to surrogates */
4883             {
4884                 wParam -= 0x10000;
4885                 EDIT_WM_Char(es, (wParam >> 10) + 0xd800);
4886                 EDIT_WM_Char(es, (wParam & 0x03ff) + 0xdc00);
4887             }
4888             else
4889                 EDIT_WM_Char(es, wParam);
4890         }
4891         return 0;
4892 
4893     case WM_CLEAR:
4894         EDIT_WM_Clear(es);
4895         break;
4896 
4897     case WM_CONTEXTMENU:
4898         EDIT_WM_ContextMenu(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
4899         break;
4900 
4901     case WM_COPY:
4902         EDIT_WM_Copy(es);
4903         break;
4904 
4905     case WM_CREATE:
4906         result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName);
4907         break;
4908 
4909     case WM_CUT:
4910         EDIT_WM_Cut(es);
4911         break;
4912 
4913     case WM_ENABLE:
4914         es->bEnableState = (BOOL) wParam;
4915         EDIT_UpdateText(es, NULL, TRUE);
4916         if (theme)
4917             RedrawWindow(hwnd, NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW);
4918         break;
4919 
4920     case WM_ERASEBKGND:
4921         /* we do the proper erase in EDIT_WM_Paint */
4922         result = 1;
4923         break;
4924 
4925     case WM_GETFONT:
4926         result = (LRESULT)es->font;
4927         break;
4928 
4929     case WM_GETTEXT:
4930         result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPWSTR)lParam);
4931         break;
4932 
4933     case WM_GETTEXTLENGTH:
4934         result = get_text_length(es);
4935         break;
4936 
4937     case WM_HSCROLL:
4938         result = EDIT_WM_HScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
4939         break;
4940 
4941     case WM_KEYDOWN:
4942         result = EDIT_WM_KeyDown(es, (INT)wParam);
4943         break;
4944 
4945     case WM_KILLFOCUS:
4946         result = EDIT_WM_KillFocus(theme, es);
4947         break;
4948 
4949     case WM_LBUTTONDBLCLK:
4950         result = EDIT_WM_LButtonDblClk(es);
4951         break;
4952 
4953     case WM_LBUTTONDOWN:
4954         result = EDIT_WM_LButtonDown(es, wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
4955         break;
4956 
4957     case WM_LBUTTONUP:
4958         result = EDIT_WM_LButtonUp(es);
4959         break;
4960 
4961     case WM_MBUTTONDOWN:
4962         result = EDIT_WM_MButtonDown(es);
4963         break;
4964 
4965     case WM_MOUSEMOVE:
4966         result = EDIT_WM_MouseMove(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
4967         break;
4968 
4969     case WM_PRINTCLIENT:
4970     case WM_PAINT:
4971         EDIT_WM_Paint(es, (HDC)wParam);
4972         break;
4973 
4974     case WM_NCPAINT:
4975         EDIT_WM_NCPaint(hwnd, (HRGN)wParam);
4976         break;
4977 
4978     case WM_PASTE:
4979         EDIT_WM_Paste(es);
4980         break;
4981 
4982     case WM_SETFOCUS:
4983         EDIT_WM_SetFocus(theme, es);
4984         break;
4985 
4986     case WM_SETFONT:
4987         EDIT_WM_SetFont(es, (HFONT)wParam, LOWORD(lParam) != 0);
4988         break;
4989 
4990     case WM_SETREDRAW:
4991         /* FIXME: actually set an internal flag and behave accordingly */
4992         break;
4993 
4994     case WM_SETTEXT:
4995         EDIT_WM_SetText(es, (const WCHAR *)lParam);
4996         result = TRUE;
4997         break;
4998 
4999     case WM_SIZE:
5000         EDIT_WM_Size(es, (UINT)wParam);
5001         break;
5002 
5003     case WM_STYLECHANGED:
5004         result = EDIT_WM_StyleChanged(es, wParam, (const STYLESTRUCT *)lParam);
5005         break;
5006 
5007     case WM_STYLECHANGING:
5008         result = 0; /* See EDIT_WM_StyleChanged */
5009         break;
5010 
5011     case WM_SYSKEYDOWN:
5012         result = EDIT_WM_SysKeyDown(es, (INT)wParam, (DWORD)lParam);
5013         break;
5014 
5015     case WM_TIMER:
5016         EDIT_WM_Timer(es);
5017         break;
5018 
5019     case WM_VSCROLL:
5020         result = EDIT_WM_VScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
5021         break;
5022 
5023     case WM_MOUSEWHEEL:
5024     {
5025         int wheelDelta;
5026         UINT pulScrollLines = 3;
5027         SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
5028 
5029         if (wParam & (MK_SHIFT | MK_CONTROL))
5030         {
5031             result = DefWindowProcW(hwnd, msg, wParam, lParam);
5032             break;
5033         }
5034 
5035         wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
5036         /* if scrolling changes direction, ignore left overs */
5037         if ((wheelDelta < 0 && es->wheelDeltaRemainder < 0) ||
5038                (wheelDelta > 0 && es->wheelDeltaRemainder > 0))
5039                 es->wheelDeltaRemainder += wheelDelta;
5040         else
5041             es->wheelDeltaRemainder = wheelDelta;
5042 
5043         if (es->wheelDeltaRemainder && pulScrollLines)
5044         {
5045             int cLineScroll;
5046             pulScrollLines = (int) min((UINT) es->line_count, pulScrollLines);
5047             cLineScroll = pulScrollLines * (float)es->wheelDeltaRemainder / WHEEL_DELTA;
5048             es->wheelDeltaRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
5049             result = EDIT_EM_LineScroll(es, 0, -cLineScroll);
5050         }
5051         break;
5052     }
5053 
5054     /* IME messages to make the edit control IME aware */
5055     case WM_IME_SETCONTEXT:
5056 #ifdef __REACTOS__
5057         if (FALSE) /* FIXME: Condition */
5058             lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
5059 
5060         if (wParam)
5061         {
5062             HIMC hIMC = ImmGetContext(hwnd);
5063             LPINPUTCONTEXTDX pIC = (LPINPUTCONTEXTDX)ImmLockIMC(hIMC);
5064             if (pIC)
5065             {
5066                 pIC->dwUIFlags &= ~0x40000;
5067                 ImmUnlockIMC(hIMC);
5068             }
5069             if (FALSE) /* FIXME: Condition */
5070                 ImmNotifyIME(hIMC, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
5071             ImmReleaseContext(hwnd, hIMC);
5072         }
5073 
5074         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5075 #endif
5076         break;
5077 
5078     case WM_IME_STARTCOMPOSITION:
5079 #ifdef __REACTOS__
5080         if (FALSE) /* FIXME: Condition */
5081             return TRUE;
5082         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5083 #else
5084         es->composition_start = es->selection_end;
5085         es->composition_len = 0;
5086 #endif
5087         break;
5088 
5089     case WM_IME_COMPOSITION:
5090         EDIT_ImeComposition(hwnd, lParam, es);
5091 #ifdef __REACTOS__
5092         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5093 #endif
5094         break;
5095 
5096     case WM_IME_ENDCOMPOSITION:
5097 #ifdef __REACTOS__
5098         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5099 #else
5100         if (es->composition_len > 0)
5101         {
5102             EDIT_EM_ReplaceSel(es, TRUE, NULL, 0, TRUE, TRUE);
5103             es->selection_end = es->selection_start;
5104             es->composition_len= 0;
5105         }
5106 #endif
5107         break;
5108 
5109     case WM_IME_COMPOSITIONFULL:
5110         break;
5111 
5112     case WM_IME_SELECT:
5113 #ifdef __REACTOS__
5114         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5115 #endif
5116         break;
5117 
5118     case WM_IME_CONTROL:
5119 #ifdef __REACTOS__
5120         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5121 #endif
5122         break;
5123 
5124     case WM_IME_REQUEST:
5125         switch (wParam)
5126         {
5127             case IMR_QUERYCHARPOSITION:
5128             {
5129                 IMECHARPOSITION *chpos = (IMECHARPOSITION *)lParam;
5130                 LRESULT pos;
5131 
5132                 pos = EDIT_EM_PosFromChar(es, es->selection_start + chpos->dwCharPos, FALSE);
5133                 chpos->pt.x = LOWORD(pos);
5134                 chpos->pt.y = HIWORD(pos);
5135                 chpos->cLineHeight = es->line_height;
5136                 chpos->rcDocument = es->format_rect;
5137                 MapWindowPoints(hwnd, 0, &chpos->pt, 1);
5138                 MapWindowPoints(hwnd, 0, (POINT*)&chpos->rcDocument, 2);
5139                 result = 1;
5140                 break;
5141             }
5142         }
5143         break;
5144 
5145     case WM_THEMECHANGED:
5146         CloseThemeData (theme);
5147         OpenThemeData(hwnd, WC_EDITW);
5148         break;
5149 
5150     default:
5151         result = DefWindowProcW(hwnd, msg, wParam, lParam);
5152         break;
5153     }
5154 
5155     if (IsWindow(hwnd) && es && msg != EM_GETHANDLE)
5156         EDIT_UnlockBuffer(es, FALSE);
5157 
5158     TRACE("hwnd=%p msg=%x -- 0x%08lx\n", hwnd, msg, result);
5159 
5160     return result;
5161 }
5162 
5163 void EDIT_Register(void)
5164 {
5165     WNDCLASSW wndClass;
5166 
5167     memset(&wndClass, 0, sizeof(wndClass));
5168     wndClass.style = CS_PARENTDC | CS_GLOBALCLASS | CS_DBLCLKS;
5169     wndClass.lpfnWndProc = EDIT_WindowProc;
5170     wndClass.cbClsExtra = 0;
5171 #ifdef __i386__
5172     wndClass.cbWndExtra = sizeof(EDITSTATE *) + sizeof(WORD);
5173 #else
5174     wndClass.cbWndExtra = sizeof(EDITSTATE *);
5175 #endif
5176     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_IBEAM);
5177     wndClass.hbrBackground = NULL;
5178     wndClass.lpszClassName = WC_EDITW;
5179     RegisterClassW(&wndClass);
5180 }
5181 
5182 #ifdef __REACTOS__
5183 void EDIT_Unregister(void)
5184 {
5185     UnregisterClassW(WC_EDITW, NULL);
5186 }
5187 #endif
5188