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