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