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