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