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