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