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