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