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