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