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