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