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