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