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