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