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