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