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