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