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