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