4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
11 * please read EDIT.TODO (and update it when you change things)
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
26 #include "selectors.h"
27 #include "debugtools.h"
30 DEFAULT_DEBUG_CHANNEL(edit);
31 DECLARE_DEBUG_CHANNEL(combo);
32 DECLARE_DEBUG_CHANNEL(relay);
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 */
43 * extra flags for EDITSTATE.flags field
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. */
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' */
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 */
68 INT width; /* width of the line in pixels */
69 struct tagLINEDEF *next;
74 HANDLE heap; /* our own heap */
75 LPSTR text; /* the actual contents of the control */
76 UINT buffer_size; /* the size of the buffer */
77 UINT buffer_limit; /* the maximum size to which the buffer may grow */
78 HFONT font; /* NULL means standard system font */
79 INT x_offset; /* scroll offset for multi lines this is in pixels
80 for single lines it's in characters */
81 INT line_height; /* height of a screen line in pixels */
82 INT char_width; /* average character width in pixels */
83 DWORD style; /* sane version of wnd->dwStyle */
84 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
85 INT undo_insert_count; /* number of characters inserted in sequence */
86 UINT undo_position; /* character index of the insertion and deletion */
87 LPSTR undo_text; /* deleted text */
88 INT undo_buffer_size; /* size of the deleted text buffer */
89 INT selection_start; /* == selection_end if no selection */
90 INT selection_end; /* == current caret position */
91 CHAR password_char; /* == 0 if no password char, and for multi line controls */
92 INT left_margin; /* in pixels */
93 INT right_margin; /* in pixels */
95 INT region_posx; /* Position of cursor relative to region: */
96 INT region_posy; /* -1: to left, 0: within, 1: to right */
97 EDITWORDBREAKPROC16 word_break_proc16;
98 EDITWORDBREAKPROCA word_break_proc32A;
99 INT line_count; /* number of lines */
100 INT y_offset; /* scroll offset in number of lines */
101 BOOL bCaptureState; /* flag indicating whether mouse was captured */
102 BOOL bEnableState; /* flag keeping the enable state */
103 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
105 * only for multi line controls
107 INT lock_count; /* amount of re-entries in the EditWndProc */
110 INT text_width; /* width of the widest line in pixels */
111 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
112 HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
113 HLOCAL hloc32; /* for controls receiving EM_GETHANDLE */
117 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
118 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
120 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
121 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
123 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
124 do {TRACE("notification " str " sent to hwnd=%08x\n", \
125 (UINT)(hwnd));} while(0)
127 /* used for disabled or read-only edit control */
128 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
129 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
130 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
131 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
132 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
133 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
134 #define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
135 do {DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str); \
136 SendMessageA((wnd)->parent->hwndSelf, WM_COMMAND, \
137 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
138 (LPARAM)(wnd)->hwndSelf);} while(0)
139 #define DPRINTF_EDIT_MSG16(str) \
141 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
142 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
143 #define DPRINTF_EDIT_MSG32(str) \
145 "32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
146 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
148 /*********************************************************************
155 * These functions have trivial implementations
156 * We still like to call them internally
157 * "static inline" makes them more like macro's
159 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es);
160 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es);
161 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
162 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
165 * Helper functions only valid for one type of control
167 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
168 static LPSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
169 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
170 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
171 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
172 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
174 * Helper functions valid for both single line _and_ multi line controls
176 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
177 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
178 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y);
179 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
180 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end);
181 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
182 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size);
183 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, INT size);
184 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend);
185 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend);
186 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend);
187 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend);
188 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend);
189 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend);
190 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
191 static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
192 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
193 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
194 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
195 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
197 * EM_XXX message handlers
199 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y);
200 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol);
201 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es);
202 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
203 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPSTR lpch);
204 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end);
205 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
206 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
207 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
208 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
209 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
210 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
211 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update);
212 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
213 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
214 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc);
215 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
216 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit);
217 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, INT left, INT right);
218 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c);
219 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
220 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs);
221 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs);
222 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
223 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
224 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
226 * WM_XXX message handlers
228 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c);
229 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
230 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y);
231 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
232 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs);
233 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
234 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc);
235 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPSTR text);
236 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
237 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key);
238 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es);
239 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es);
240 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
241 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es);
242 static LRESULT EDIT_WM_MButtonDown(WND *wnd);
243 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y);
244 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs);
245 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
246 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
247 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es);
248 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
249 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
250 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
251 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
252 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es);
253 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
254 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
257 /*********************************************************************
262 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es)
264 return (es->undo_insert_count || strlen(es->undo_text));
268 /*********************************************************************
273 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
275 es->undo_insert_count = 0;
276 *es->undo_text = '\0';
280 /*********************************************************************
285 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
287 EDIT_EM_ReplaceSel(wnd, es, TRUE, "", TRUE);
289 if (es->flags & EF_UPDATE) {
290 es->flags &= ~EF_UPDATE;
291 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
296 /*********************************************************************
301 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
303 EDIT_WM_Copy(wnd, es);
304 EDIT_WM_Clear(wnd, es);
308 /**********************************************************************
311 * Returns the window version in case Wine emulates a later version
312 * of windows then the application expects.
314 * In a number of cases when windows runs an application that was
315 * designed for an earlier windows version, windows reverts
316 * to "old" behaviour of that earlier version.
318 * An example is a disabled edit control that needs to be painted.
319 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
320 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
321 * applications with an expected version 0f 4.0 or higher.
324 static DWORD get_app_version(void)
326 static DWORD version;
329 DWORD dwEmulatedVersion;
331 DWORD dwProcVersion = GetProcessVersion(0);
333 GetVersionExA( &info );
334 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
335 /* fixme: this may not be 100% correct; see discussion on the
336 * wine developer list in Nov 1999 */
337 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
343 /*********************************************************************
347 * The messages are in the order of the actual integer values
348 * (which can be found in include/windows.h)
349 * Whereever possible the 16 bit versions are converted to
350 * the 32 bit ones, so that we can 'fall through' to the
351 * helper functions. These are mostly 32 bit (with a few
352 * exceptions, clearly indicated by a '16' extension to their
356 LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
357 WPARAM wParam, LPARAM lParam )
359 WND *wnd = WIN_FindWndPtr(hwnd);
360 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
365 DPRINTF_EDIT_MSG32("WM_DESTROY");
366 EDIT_WM_Destroy(wnd, es);
371 DPRINTF_EDIT_MSG32("WM_NCCREATE");
372 result = EDIT_WM_NCCreate(wnd, (LPCREATESTRUCTA)lParam);
378 result = DefWindowProcA(hwnd, msg, wParam, lParam);
383 EDIT_LockBuffer(wnd, es);
386 DPRINTF_EDIT_MSG16("EM_GETSEL");
391 DPRINTF_EDIT_MSG32("EM_GETSEL");
392 result = EDIT_EM_GetSel(es, (LPUINT)wParam, (LPUINT)lParam);
396 DPRINTF_EDIT_MSG16("EM_SETSEL");
397 if (SLOWORD(lParam) == -1)
398 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
400 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
402 EDIT_EM_ScrollCaret(wnd, es);
406 DPRINTF_EDIT_MSG32("EM_SETSEL");
407 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
408 EDIT_EM_ScrollCaret(wnd, es);
413 DPRINTF_EDIT_MSG16("EM_GETRECT");
415 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
418 DPRINTF_EDIT_MSG32("EM_GETRECT");
420 CopyRect((LPRECT)lParam, &es->format_rect);
424 DPRINTF_EDIT_MSG16("EM_SETRECT");
425 if ((es->style & ES_MULTILINE) && lParam) {
427 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
428 EDIT_SetRectNP(wnd, es, &rc);
429 EDIT_UpdateText(wnd, NULL, TRUE);
433 DPRINTF_EDIT_MSG32("EM_SETRECT");
434 if ((es->style & ES_MULTILINE) && lParam) {
435 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
436 EDIT_UpdateText(wnd, NULL, TRUE);
441 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
442 if ((es->style & ES_MULTILINE) && lParam) {
444 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
445 EDIT_SetRectNP(wnd, es, &rc);
449 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
450 if ((es->style & ES_MULTILINE) && lParam)
451 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
455 DPRINTF_EDIT_MSG16("EM_SCROLL");
458 DPRINTF_EDIT_MSG32("EM_SCROLL");
459 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
462 case EM_LINESCROLL16:
463 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
464 wParam = (WPARAM)(INT)SHIWORD(lParam);
465 lParam = (LPARAM)(INT)SLOWORD(lParam);
468 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
469 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
472 case EM_SCROLLCARET16:
473 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
476 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
477 EDIT_EM_ScrollCaret(wnd, es);
482 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
485 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
486 result = ((es->flags & EF_MODIFIED) != 0);
490 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
493 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
495 es->flags |= EF_MODIFIED;
497 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
500 case EM_GETLINECOUNT16:
501 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
503 case EM_GETLINECOUNT:
504 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
505 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
509 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
510 if ((INT16)wParam == -1)
514 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
515 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
519 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
520 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
523 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
524 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
528 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
529 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
532 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
533 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
537 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
540 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
541 result = EDIT_EM_GetThumb(wnd, es);
544 /* messages 0x00bf and 0x00c0 missing from specs */
547 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
550 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
551 result = DefWindowProcA(hwnd, msg, wParam, lParam);
555 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
558 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
559 result = DefWindowProcA(hwnd, msg, wParam, lParam);
562 case EM_LINELENGTH16:
563 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
566 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
567 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
570 case EM_REPLACESEL16:
571 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
572 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
575 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
576 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, (LPCSTR)lParam, TRUE);
577 if (es->flags & EF_UPDATE) {
578 es->flags &= ~EF_UPDATE;
579 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
584 /* message 0x00c3 missing from specs */
587 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
590 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
591 result = DefWindowProcA(hwnd, msg, wParam, lParam);
595 DPRINTF_EDIT_MSG16("EM_GETLINE");
596 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
599 DPRINTF_EDIT_MSG32("EM_GETLINE");
600 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPSTR)lParam);
604 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
606 case EM_SETLIMITTEXT:
607 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
608 EDIT_EM_SetLimitText(es, (INT)wParam);
612 DPRINTF_EDIT_MSG16("EM_CANUNDO");
615 DPRINTF_EDIT_MSG32("EM_CANUNDO");
616 result = (LRESULT)EDIT_EM_CanUndo(es);
620 DPRINTF_EDIT_MSG16("EM_UNDO");
625 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
626 result = (LRESULT)EDIT_EM_Undo(wnd, es);
630 DPRINTF_EDIT_MSG16("EM_FMTLINES");
633 DPRINTF_EDIT_MSG32("EM_FMTLINES");
634 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
637 case EM_LINEFROMCHAR16:
638 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
640 case EM_LINEFROMCHAR:
641 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
642 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
645 /* message 0x00ca missing from specs */
648 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
651 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
652 result = DefWindowProcA(hwnd, msg, wParam, lParam);
655 case EM_SETTABSTOPS16:
656 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
657 result = (LRESULT)EDIT_EM_SetTabStops16(es, (INT)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
660 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
661 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
664 case EM_SETPASSWORDCHAR16:
665 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
667 case EM_SETPASSWORDCHAR:
668 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
669 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
672 case EM_EMPTYUNDOBUFFER16:
673 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
675 case EM_EMPTYUNDOBUFFER:
676 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
677 EDIT_EM_EmptyUndoBuffer(es);
680 case EM_GETFIRSTVISIBLELINE16:
681 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
682 result = es->y_offset;
684 case EM_GETFIRSTVISIBLELINE:
685 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
686 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
689 case EM_SETREADONLY16:
690 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
693 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
695 wnd->dwStyle |= ES_READONLY;
696 es->style |= ES_READONLY;
698 wnd->dwStyle &= ~ES_READONLY;
699 es->style &= ~ES_READONLY;
704 case EM_SETWORDBREAKPROC16:
705 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
706 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
708 case EM_SETWORDBREAKPROC:
709 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
710 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROCA)lParam);
713 case EM_GETWORDBREAKPROC16:
714 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
715 result = (LRESULT)es->word_break_proc16;
717 case EM_GETWORDBREAKPROC:
718 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
719 result = (LRESULT)es->word_break_proc32A;
722 case EM_GETPASSWORDCHAR16:
723 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
725 case EM_GETPASSWORDCHAR:
726 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
727 result = es->password_char;
730 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
733 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
734 EDIT_EM_SetMargins(es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
738 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
739 result = MAKELONG(es->left_margin, es->right_margin);
742 case EM_GETLIMITTEXT:
743 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
744 result = es->buffer_limit;
748 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
749 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
753 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
754 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
758 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
759 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
761 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
763 int vk = (int)((LPMSG)lParam)->wParam;
765 if ((wnd->dwStyle & ES_WANTRETURN) && vk == VK_RETURN)
767 result |= DLGC_WANTMESSAGE;
769 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
771 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
772 result |= DLGC_WANTMESSAGE;
778 DPRINTF_EDIT_MSG32("WM_CHAR");
779 if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) && es->hwndListBox)
781 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
782 SendMessageA(wnd->parent->hwndSelf, WM_KEYDOWN, wParam, 0);
785 EDIT_WM_Char(wnd, es, (CHAR)wParam);
789 DPRINTF_EDIT_MSG32("WM_CLEAR");
790 EDIT_WM_Clear(wnd, es);
794 DPRINTF_EDIT_MSG32("WM_COMMAND");
795 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
799 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
800 EDIT_WM_ContextMenu(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
804 DPRINTF_EDIT_MSG32("WM_COPY");
805 EDIT_WM_Copy(wnd, es);
809 DPRINTF_EDIT_MSG32("WM_CREATE");
810 result = EDIT_WM_Create(wnd, es, (LPCREATESTRUCTA)lParam);
814 DPRINTF_EDIT_MSG32("WM_CUT");
815 EDIT_WM_Cut(wnd, es);
819 DPRINTF_EDIT_MSG32("WM_ENABLE");
820 es->bEnableState = (BOOL) wParam;
821 EDIT_UpdateText(wnd, NULL, TRUE);
825 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
826 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
830 DPRINTF_EDIT_MSG32("WM_GETFONT");
831 result = (LRESULT)es->font;
835 DPRINTF_EDIT_MSG32("WM_GETTEXT");
836 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPSTR)lParam);
839 case WM_GETTEXTLENGTH:
840 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
841 result = strlen(es->text);
845 DPRINTF_EDIT_MSG32("WM_HSCROLL");
846 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
850 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
851 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam);
855 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
856 result = EDIT_WM_KillFocus(wnd, es);
859 case WM_LBUTTONDBLCLK:
860 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
861 result = EDIT_WM_LButtonDblClk(wnd, es);
865 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
866 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
870 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
871 result = EDIT_WM_LButtonUp(hwnd, es);
875 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
876 result = EDIT_WM_MButtonDown(wnd);
879 case WM_MOUSEACTIVATE:
881 * FIXME: maybe DefWindowProc() screws up, but it seems that
882 * modeless dialog boxes need this. If we don't do this, the focus
883 * will _not_ be set by DefWindowProc() for edit controls in a
884 * modeless dialog box ???
886 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
887 SetFocus(wnd->hwndSelf);
888 result = MA_ACTIVATE;
893 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
895 result = EDIT_WM_MouseMove(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
899 DPRINTF_EDIT_MSG32("WM_PAINT");
900 EDIT_WM_Paint(wnd, es, wParam);
904 DPRINTF_EDIT_MSG32("WM_PASTE");
905 EDIT_WM_Paste(wnd, es);
909 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
910 EDIT_WM_SetFocus(wnd, es);
914 DPRINTF_EDIT_MSG32("WM_SETFONT");
915 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
919 DPRINTF_EDIT_MSG32("WM_SETTEXT");
920 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
925 DPRINTF_EDIT_MSG32("WM_SIZE");
926 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
930 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
931 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
935 DPRINTF_EDIT_MSG32("WM_TIMER");
936 EDIT_WM_Timer(wnd, es);
940 DPRINTF_EDIT_MSG32("WM_VSCROLL");
941 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
946 int gcWheelDelta = 0;
947 UINT pulScrollLines = 3;
948 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
950 if (wParam & (MK_SHIFT | MK_CONTROL)) {
951 result = DefWindowProcA(hwnd, msg, wParam, lParam);
954 gcWheelDelta -= SHIWORD(wParam);
955 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
957 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
958 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
959 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
964 result = DefWindowProcA(hwnd, msg, wParam, lParam);
967 EDIT_UnlockBuffer(wnd, es, FALSE);
969 WIN_ReleaseWndPtr(wnd);
975 /*********************************************************************
977 * EDIT_BuildLineDefs_ML
979 * Build linked list of text lines.
980 * Lines can end with '\0' (last line), a character (if it is wrapped),
981 * a soft return '\r\r\n' or a hard return '\r\n'
984 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
990 LINEDEF *current_def;
991 LINEDEF **previous_next;
993 current_def = es->first_line_def;
995 LINEDEF *next_def = current_def->next;
996 HeapFree(es->heap, 0, current_def);
997 current_def = next_def;
998 } while (current_def);
1002 dc = GetDC(wnd->hwndSelf);
1004 old_font = SelectObject(dc, es->font);
1006 fw = es->format_rect.right - es->format_rect.left;
1008 previous_next = &es->first_line_def;
1010 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
1011 current_def->next = NULL;
1014 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1019 current_def->ending = END_0;
1020 current_def->net_length = strlen(start);
1021 } else if ((cp > start) && (*(cp - 1) == '\r')) {
1022 current_def->ending = END_SOFT;
1023 current_def->net_length = cp - start - 1;
1025 current_def->ending = END_HARD;
1026 current_def->net_length = cp - start;
1028 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1029 start, current_def->net_length,
1030 es->tabs_count, es->tabs));
1031 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1032 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
1037 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
1038 prev + 1, current_def->net_length, WB_RIGHT);
1039 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1040 start, next, es->tabs_count, es->tabs));
1041 } while (current_def->width <= fw);
1047 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1048 start, next, es->tabs_count, es->tabs));
1049 } while (current_def->width <= fw);
1053 current_def->net_length = prev;
1054 current_def->ending = END_WRAP;
1055 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc, start,
1056 current_def->net_length, es->tabs_count, es->tabs));
1058 switch (current_def->ending) {
1060 current_def->length = current_def->net_length + 3;
1063 current_def->length = current_def->net_length + 2;
1067 current_def->length = current_def->net_length;
1070 es->text_width = max(es->text_width, current_def->width);
1071 start += current_def->length;
1072 *previous_next = current_def;
1073 previous_next = ¤t_def->next;
1075 } while (current_def->ending != END_0);
1077 SelectObject(dc, old_font);
1078 ReleaseDC(wnd->hwndSelf, dc);
1082 /*********************************************************************
1084 * EDIT_CallWordBreakProc
1086 * Call appropriate WordBreakProc (internal or external).
1088 * Note: The "start" argument should always be an index refering
1089 * to es->text. The actual wordbreak proc might be
1090 * 16 bit, so we can't always pass any 32 bit LPSTR.
1091 * Hence we assume that es->text is the buffer that holds
1092 * the string under examination (we can decide this for ourselves).
1095 /* ### start build ### */
1096 extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1097 /* ### stop build ### */
1098 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
1100 if (es->word_break_proc16) {
1101 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
1102 SEGPTR segptr = LocalLock16(hloc16);
1103 INT ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1104 segptr + start, index, count, action);
1105 LocalUnlock16(hloc16);
1108 else if (es->word_break_proc32A)
1110 TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
1111 es->word_break_proc32A, es->text + start, index,
1113 return (INT)es->word_break_proc32A( es->text + start, index,
1117 return EDIT_WordBreakProc(es->text + start, index, count, action);
1121 /*********************************************************************
1125 * Beware: This is not the function called on EM_CHARFROMPOS
1126 * The position _can_ be outside the formatting / client
1128 * The return value is only the character index
1131 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1137 if (es->style & ES_MULTILINE) {
1138 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1140 LINEDEF *line_def = es->first_line_def;
1142 while ((line > 0) && line_def->next) {
1143 line_index += line_def->length;
1144 line_def = line_def->next;
1147 x += es->x_offset - es->format_rect.left;
1148 if (x >= line_def->width) {
1150 *after_wrap = (line_def->ending == END_WRAP);
1151 return line_index + line_def->net_length;
1155 *after_wrap = FALSE;
1158 dc = GetDC(wnd->hwndSelf);
1160 old_font = SelectObject(dc, es->font);
1161 low = line_index + 1;
1162 high = line_index + line_def->net_length + 1;
1163 while (low < high - 1)
1165 INT mid = (low + high) / 2;
1166 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1172 *after_wrap = ((index == line_index + line_def->net_length) &&
1173 (line_def->ending == END_WRAP));
1178 *after_wrap = FALSE;
1179 x -= es->format_rect.left;
1181 return es->x_offset;
1182 text = EDIT_GetPasswordPointer_SL(es);
1183 dc = GetDC(wnd->hwndSelf);
1185 old_font = SelectObject(dc, es->font);
1189 INT high = es->x_offset;
1190 while (low < high - 1)
1192 INT mid = (low + high) / 2;
1193 GetTextExtentPoint32A( dc, text + mid,
1194 es->x_offset - mid, &size );
1195 if (size.cx > -x) low = mid;
1202 INT low = es->x_offset;
1203 INT high = strlen(es->text) + 1;
1204 while (low < high - 1)
1206 INT mid = (low + high) / 2;
1207 GetTextExtentPoint32A( dc, text + es->x_offset,
1208 mid - es->x_offset, &size );
1209 if (size.cx > x) high = mid;
1214 if (es->style & ES_PASSWORD)
1215 HeapFree(es->heap, 0 ,text);
1218 SelectObject(dc, old_font);
1219 ReleaseDC(wnd->hwndSelf, dc);
1224 /*********************************************************************
1228 * adjusts the point to be within the formatting rectangle
1229 * (so CharFromPos returns the nearest _visible_ character)
1232 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y)
1234 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1235 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1239 /*********************************************************************
1243 * Calculates the bounding rectangle for a line from a starting
1244 * column to an ending column.
1247 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1249 INT line_index = EDIT_EM_LineIndex(es, line);
1251 if (es->style & ES_MULTILINE)
1252 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1254 rc->top = es->format_rect.top;
1255 rc->bottom = rc->top + es->line_height;
1256 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1257 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1261 /*********************************************************************
1263 * EDIT_GetPasswordPointer_SL
1265 * note: caller should free the (optionally) allocated buffer
1268 static LPSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
1270 if (es->style & ES_PASSWORD) {
1271 INT len = strlen(es->text);
1272 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1273 RtlFillMemory(text, len, es->password_char);
1281 /*********************************************************************
1285 * This acts as a LOCAL_Lock(), but it locks only once. This way
1286 * you can call it whenever you like, without unlocking.
1289 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1292 ERR("no EDITSTATE ... please report\n");
1295 if (!(es->style & ES_MULTILINE))
1299 es->text = LocalLock(es->hloc32);
1300 else if (es->hloc16)
1301 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1303 ERR("no buffer ... please report\n");
1311 /*********************************************************************
1313 * EDIT_SL_InvalidateText
1315 * Called from EDIT_InvalidateText().
1316 * Does the job for single-line controls only.
1319 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1324 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1325 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1326 EDIT_UpdateText(wnd, &rc, FALSE);
1330 /*********************************************************************
1332 * EDIT_ML_InvalidateText
1334 * Called from EDIT_InvalidateText().
1335 * Does the job for multi-line controls only.
1338 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1340 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1341 INT sl = EDIT_EM_LineFromChar(es, start);
1342 INT el = EDIT_EM_LineFromChar(es, end);
1351 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1354 sc = start - EDIT_EM_LineIndex(es, sl);
1355 ec = end - EDIT_EM_LineIndex(es, el);
1356 if (sl < es->y_offset) {
1360 if (el > es->y_offset + vlc) {
1361 el = es->y_offset + vlc;
1362 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1364 GetClientRect(wnd->hwndSelf, &rc1);
1365 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1367 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1368 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1369 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1371 EDIT_GetLineRect(wnd, es, sl, sc,
1372 EDIT_EM_LineLength(es,
1373 EDIT_EM_LineIndex(es, sl)),
1375 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1376 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1377 for (l = sl + 1 ; l < el ; l++) {
1378 EDIT_GetLineRect(wnd, es, l, 0,
1379 EDIT_EM_LineLength(es,
1380 EDIT_EM_LineIndex(es, l)),
1382 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1383 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1385 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1386 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1387 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1392 /*********************************************************************
1394 * EDIT_InvalidateText
1396 * Invalidate the text from offset start upto, but not including,
1397 * offset end. Useful for (re)painting the selection.
1398 * Regions outside the linewidth are not invalidated.
1399 * end == -1 means end == TextLength.
1400 * start and end need not be ordered.
1403 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1409 end = strlen(es->text);
1411 ORDER_INT(start, end);
1413 if (es->style & ES_MULTILINE)
1414 EDIT_ML_InvalidateText(wnd, es, start, end);
1416 EDIT_SL_InvalidateText(wnd, es, start, end);
1420 /*********************************************************************
1424 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1427 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size)
1432 if (size <= es->buffer_size)
1434 if (size > es->buffer_limit) {
1435 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1438 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1439 if (size > es->buffer_limit)
1440 size = es->buffer_limit;
1442 TRACE("trying to ReAlloc to %d+1\n", size);
1444 EDIT_UnlockBuffer(wnd, es, TRUE);
1446 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1447 es->buffer_size = min(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1449 es->buffer_size = 0;
1450 } else if (es->hloc32) {
1451 if ((hNew32 = LocalReAlloc(es->hloc32, size + 1, 0))) {
1452 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1453 es->hloc32 = hNew32;
1454 es->buffer_size = min(LocalSize(es->hloc32) - 1, es->buffer_limit);
1456 } else if (es->hloc16) {
1457 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1458 TRACE("Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1459 es->hloc16 = hNew16;
1460 es->buffer_size = min((UINT)LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1463 if (es->buffer_size < size) {
1464 EDIT_LockBuffer(wnd, es);
1465 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1466 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1469 EDIT_LockBuffer(wnd, es);
1470 TRACE("We now have %d+1\n", es->buffer_size);
1476 /*********************************************************************
1480 * Try to fit size + 1 bytes in the undo buffer.
1483 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, INT size)
1485 if (size <= es->undo_buffer_size)
1487 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1489 TRACE("trying to ReAlloc to %d+1\n", size);
1491 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1492 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1493 if (es->undo_buffer_size < size) {
1494 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1503 /*********************************************************************
1508 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1510 INT e = es->selection_end;
1514 if ((es->style & ES_MULTILINE) && e &&
1515 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1517 if (e && (es->text[e - 1] == '\r'))
1521 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1522 EDIT_EM_ScrollCaret(wnd, es);
1526 /*********************************************************************
1530 * Only for multi line controls
1531 * Move the caret one line down, on a column with the nearest
1532 * x coordinate on the screen (might be a different column).
1535 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1537 INT s = es->selection_start;
1538 INT e = es->selection_end;
1539 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1540 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1541 INT x = SLOWORD(pos);
1542 INT y = SHIWORD(pos);
1544 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1547 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1548 EDIT_EM_ScrollCaret(wnd, es);
1552 /*********************************************************************
1557 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1559 BOOL after_wrap = FALSE;
1562 /* Pass a high value in x to make sure of receiving the en of the line */
1563 if (es->style & ES_MULTILINE)
1564 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1565 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1567 e = strlen(es->text);
1568 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1569 EDIT_EM_ScrollCaret(wnd, es);
1573 /*********************************************************************
1578 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1580 INT e = es->selection_end;
1584 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1585 if (es->text[e] == '\n')
1587 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1591 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1592 EDIT_EM_ScrollCaret(wnd, es);
1596 /*********************************************************************
1600 * Home key: move to beginning of line.
1603 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1607 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1608 if (es->style & ES_MULTILINE)
1609 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1610 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1613 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1614 EDIT_EM_ScrollCaret(wnd, es);
1618 /*********************************************************************
1620 * EDIT_MovePageDown_ML
1622 * Only for multi line controls
1623 * Move the caret one page down, on a column with the nearest
1624 * x coordinate on the screen (might be a different column).
1627 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1629 INT s = es->selection_start;
1630 INT e = es->selection_end;
1631 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1632 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1633 INT x = SLOWORD(pos);
1634 INT y = SHIWORD(pos);
1636 e = EDIT_CharFromPos(wnd, es, x,
1637 y + (es->format_rect.bottom - es->format_rect.top),
1641 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1642 EDIT_EM_ScrollCaret(wnd, es);
1646 /*********************************************************************
1648 * EDIT_MovePageUp_ML
1650 * Only for multi line controls
1651 * Move the caret one page up, on a column with the nearest
1652 * x coordinate on the screen (might be a different column).
1655 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1657 INT s = es->selection_start;
1658 INT e = es->selection_end;
1659 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1660 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1661 INT x = SLOWORD(pos);
1662 INT y = SHIWORD(pos);
1664 e = EDIT_CharFromPos(wnd, es, x,
1665 y - (es->format_rect.bottom - es->format_rect.top),
1669 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1670 EDIT_EM_ScrollCaret(wnd, es);
1674 /*********************************************************************
1678 * Only for multi line controls
1679 * Move the caret one line up, on a column with the nearest
1680 * x coordinate on the screen (might be a different column).
1683 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1685 INT s = es->selection_start;
1686 INT e = es->selection_end;
1687 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1688 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1689 INT x = SLOWORD(pos);
1690 INT y = SHIWORD(pos);
1692 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1695 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1696 EDIT_EM_ScrollCaret(wnd, es);
1700 /*********************************************************************
1702 * EDIT_MoveWordBackward
1705 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1707 INT s = es->selection_start;
1708 INT e = es->selection_end;
1713 l = EDIT_EM_LineFromChar(es, e);
1714 ll = EDIT_EM_LineLength(es, e);
1715 li = EDIT_EM_LineIndex(es, l);
1718 li = EDIT_EM_LineIndex(es, l - 1);
1719 e = li + EDIT_EM_LineLength(es, li);
1722 e = li + (INT)EDIT_CallWordBreakProc(wnd, es,
1723 li, e - li, ll, WB_LEFT);
1727 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1728 EDIT_EM_ScrollCaret(wnd, es);
1732 /*********************************************************************
1734 * EDIT_MoveWordForward
1737 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
1739 INT s = es->selection_start;
1740 INT e = es->selection_end;
1745 l = EDIT_EM_LineFromChar(es, e);
1746 ll = EDIT_EM_LineLength(es, e);
1747 li = EDIT_EM_LineIndex(es, l);
1749 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1750 e = EDIT_EM_LineIndex(es, l + 1);
1752 e = li + EDIT_CallWordBreakProc(wnd, es,
1753 li, e - li + 1, ll, WB_RIGHT);
1757 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1758 EDIT_EM_ScrollCaret(wnd, es);
1762 /*********************************************************************
1767 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1769 INT s = es->selection_start;
1770 INT e = es->selection_end;
1777 if (es->style & ES_MULTILINE) {
1778 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1779 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1784 TRACE("line=%d\n", line);
1786 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(es, line), FALSE);
1789 li = EDIT_EM_LineIndex(es, line);
1790 ll = EDIT_EM_LineLength(es, li);
1791 s = es->selection_start;
1792 e = es->selection_end;
1794 s = min(li + ll, max(li, s));
1795 e = min(li + ll, max(li, e));
1796 if (rev && (s != e) &&
1797 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1798 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
1799 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
1800 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
1802 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
1806 /*********************************************************************
1811 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1821 BkColor = GetBkColor(dc);
1822 TextColor = GetTextColor(dc);
1824 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1825 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1827 li = EDIT_EM_LineIndex(es, line);
1828 if (es->style & ES_MULTILINE) {
1829 ret = (INT)LOWORD(TabbedTextOutA(dc, x, y, es->text + li + col, count,
1830 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1832 LPSTR text = EDIT_GetPasswordPointer_SL(es);
1833 TextOutA(dc, x, y, text + li + col, count);
1834 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1836 if (es->style & ES_PASSWORD)
1837 HeapFree(es->heap, 0, text);
1840 SetBkColor(dc, BkColor);
1841 SetTextColor(dc, TextColor);
1847 /*********************************************************************
1852 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
1855 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
1856 INT x = SLOWORD(res);
1857 INT y = SHIWORD(res);
1859 if(x < es->format_rect.left)
1860 x = es->format_rect.left;
1861 if(x > es->format_rect.right - 2)
1862 x = es->format_rect.right - 2;
1863 if(y > es->format_rect.bottom)
1864 y = es->format_rect.bottom;
1865 if(y < es->format_rect.top)
1866 y = es->format_rect.top;
1872 /*********************************************************************
1876 * note: this is not (exactly) the handler called on EM_SETRECTNP
1877 * it is also used to set the rect of a single line control
1880 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
1882 CopyRect(&es->format_rect, rc);
1883 if (es->style & WS_BORDER) {
1884 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
1885 if(TWEAK_WineLook == WIN31_LOOK)
1887 es->format_rect.left += bw;
1888 es->format_rect.top += bw;
1889 es->format_rect.right -= bw;
1890 es->format_rect.bottom -= bw;
1892 es->format_rect.left += es->left_margin;
1893 es->format_rect.right -= es->right_margin;
1894 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
1895 if (es->style & ES_MULTILINE)
1896 es->format_rect.bottom = es->format_rect.top +
1897 max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1899 es->format_rect.bottom = es->format_rect.top + es->line_height;
1900 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1901 EDIT_BuildLineDefs_ML(wnd, es);
1905 /*********************************************************************
1910 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
1913 ERR("no EDITSTATE ... please report\n");
1916 if (!(es->style & ES_MULTILINE))
1918 if (!es->lock_count) {
1919 ERR("lock_count == 0 ... please report\n");
1923 ERR("es->text == 0 ... please report\n");
1926 if (force || (es->lock_count == 1)) {
1928 LocalUnlock(es->hloc32);
1930 } else if (es->hloc16) {
1931 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1939 /*********************************************************************
1941 * EDIT_WordBreakProc
1943 * Find the beginning of words.
1944 * Note: unlike the specs for a WordBreakProc, this function only
1945 * allows to be called without linebreaks between s[0] upto
1946 * s[count - 1]. Remember it is only called
1947 * internally, so we can decide this for ourselves.
1950 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1954 TRACE("s=%p, index=%u, count=%u, action=%d\n",
1955 s, index, count, action);
1963 if (s[index] == ' ') {
1964 while (index && (s[index] == ' '))
1967 while (index && (s[index] != ' '))
1969 if (s[index] == ' ')
1973 while (index && (s[index] != ' '))
1975 if (s[index] == ' ')
1985 if (s[index] == ' ')
1986 while ((index < count) && (s[index] == ' ')) index++;
1988 while (s[index] && (s[index] != ' ') && (index < count))
1990 while ((s[index] == ' ') && (index < count)) index++;
1994 case WB_ISDELIMITER:
1995 ret = (s[index] == ' ');
1998 ERR("unknown action code, please report !\n");
2005 /*********************************************************************
2009 * returns line number (not index) in high-order word of result.
2010 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2011 * to Richedit, not to the edit control. Original documentation is valid.
2012 * FIXME: do the specs mean to return -1 if outside client area or
2013 * if outside formatting rectangle ???
2016 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
2024 GetClientRect(wnd->hwndSelf, &rc);
2025 if (!PtInRect(&rc, pt))
2028 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
2029 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2033 /*********************************************************************
2037 * Enable or disable soft breaks.
2039 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2041 es->flags &= ~EF_USE_SOFTBRK;
2043 es->flags |= EF_USE_SOFTBRK;
2044 FIXME("soft break enabled, not implemented\n");
2050 /*********************************************************************
2054 * Hopefully this won't fire back at us.
2055 * We always start with a fixed buffer in our own heap.
2056 * However, with this message a 32 bit application requests
2057 * a handle to 32 bit moveable local heap memory, where it expects
2059 * It's a pity that from this moment on we have to use this
2060 * local heap, because applications may rely on the handle
2063 * In this function we'll try to switch to local heap.
2066 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
2072 if (!(es->style & ES_MULTILINE))
2077 else if (es->hloc16)
2078 return (HLOCAL)es->hloc16;
2080 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, strlen(es->text) + 1))) {
2081 ERR("could not allocate new 32 bit buffer\n");
2084 newSize = min(LocalSize(newBuf) - 1, es->buffer_limit);
2085 if (!(newText = LocalLock(newBuf))) {
2086 ERR("could not lock new 32 bit buffer\n");
2090 strcpy(newText, es->text);
2091 EDIT_UnlockBuffer(wnd, es, TRUE);
2093 HeapFree(es->heap, 0, es->text);
2094 es->hloc32 = newBuf;
2095 es->hloc16 = (HLOCAL16)NULL;
2096 es->buffer_size = newSize;
2098 EDIT_LockBuffer(wnd, es);
2099 TRACE("switched to 32 bit local heap\n");
2105 /*********************************************************************
2109 * Hopefully this won't fire back at us.
2110 * We always start with a buffer in 32 bit linear memory.
2111 * However, with this message a 16 bit application requests
2112 * a handle of 16 bit local heap memory, where it expects to find
2114 * It's a pitty that from this moment on we have to use this
2115 * local heap, because applications may rely on the handle
2118 * In this function we'll try to switch to local heap.
2120 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2126 if (!(es->style & ES_MULTILINE))
2132 if (!LOCAL_HeapSize(wnd->hInstance)) {
2133 if (!LocalInit16(wnd->hInstance, 0,
2134 GlobalSize16(wnd->hInstance))) {
2135 ERR("could not initialize local heap\n");
2138 TRACE("local heap initialized\n");
2140 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, strlen(es->text) + 1))) {
2141 ERR("could not allocate new 16 bit buffer\n");
2144 newSize = min((UINT)LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2145 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2146 ERR("could not lock new 16 bit buffer\n");
2147 LOCAL_Free(wnd->hInstance, newBuf);
2150 strcpy(newText, es->text);
2151 EDIT_UnlockBuffer(wnd, es, TRUE);
2153 HeapFree(es->heap, 0, es->text);
2154 else if (es->hloc32) {
2155 while (LocalFree(es->hloc32)) ;
2156 LocalFree(es->hloc32);
2158 es->hloc32 = (HLOCAL)NULL;
2159 es->hloc16 = newBuf;
2160 es->buffer_size = newSize;
2162 EDIT_LockBuffer(wnd, es);
2163 TRACE("switched to 16 bit buffer\n");
2169 /*********************************************************************
2174 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPSTR lpch)
2180 if (es->style & ES_MULTILINE) {
2181 if (line >= es->line_count)
2185 i = EDIT_EM_LineIndex(es, line);
2187 len = min(*(WORD *)lpch, EDIT_EM_LineLength(es, i));
2188 for (i = 0 ; i < len ; i++) {
2193 return (LRESULT)len;
2197 /*********************************************************************
2202 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end)
2204 UINT s = es->selection_start;
2205 UINT e = es->selection_end;
2212 return MAKELONG(s, e);
2216 /*********************************************************************
2220 * FIXME: is this right ? (or should it be only VSCROLL)
2221 * (and maybe only for edit controls that really have their
2222 * own scrollbars) (and maybe only for multiline controls ?)
2223 * All in all: very poorly documented
2225 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2226 * WM_VSCROLL handlers
2229 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2231 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0),
2232 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0));
2236 /*********************************************************************
2241 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
2246 if (!(es->style & ES_MULTILINE))
2248 if (index > (INT)strlen(es->text))
2249 return es->line_count - 1;
2251 index = min(es->selection_start, es->selection_end);
2254 line_def = es->first_line_def;
2255 index -= line_def->length;
2256 while ((index >= 0) && line_def->next) {
2258 line_def = line_def->next;
2259 index -= line_def->length;
2265 /*********************************************************************
2270 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line)
2275 if (!(es->style & ES_MULTILINE))
2277 if (line >= es->line_count)
2281 line_def = es->first_line_def;
2283 INT index = es->selection_end - line_def->length;
2284 while ((index >= 0) && line_def->next) {
2285 line_index += line_def->length;
2286 line_def = line_def->next;
2287 index -= line_def->length;
2291 line_index += line_def->length;
2292 line_def = line_def->next;
2300 /*********************************************************************
2305 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
2309 if (!(es->style & ES_MULTILINE))
2310 return strlen(es->text);
2313 /* get the number of remaining non-selected chars of selected lines */
2316 li = EDIT_EM_LineFromChar(es, es->selection_start);
2317 /* # chars before start of selection area */
2318 count = es->selection_start - EDIT_EM_LineIndex(es, li);
2319 li = EDIT_EM_LineFromChar(es, es->selection_end);
2320 /* # chars after end of selection */
2321 count += EDIT_EM_LineIndex(es, li) +
2322 EDIT_EM_LineLength(es, li) - es->selection_end;
2325 line_def = es->first_line_def;
2326 index -= line_def->length;
2327 while ((index >= 0) && line_def->next) {
2328 line_def = line_def->next;
2329 index -= line_def->length;
2331 return line_def->net_length;
2335 /*********************************************************************
2339 * FIXME: dx is in average character widths
2340 * However, we assume it is in pixels when we use this
2341 * function internally
2344 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2348 if (!(es->style & ES_MULTILINE))
2351 if (-dx > es->x_offset)
2353 if (dx > es->text_width - es->x_offset)
2354 dx = es->text_width - es->x_offset;
2355 nyoff = max(0, es->y_offset + dy);
2356 if (nyoff >= es->line_count)
2357 nyoff = es->line_count - 1;
2358 dy = (es->y_offset - nyoff) * es->line_height;
2362 GetClientRect(wnd->hwndSelf, &rc1);
2363 IntersectRect(&rc, &rc1, &es->format_rect);
2364 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2365 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2366 es->y_offset = nyoff;
2369 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2370 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2371 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2372 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2377 /*********************************************************************
2382 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2384 INT len = strlen(es->text);
2393 index = min(index, len);
2394 dc = GetDC(wnd->hwndSelf);
2396 old_font = SelectObject(dc, es->font);
2397 if (es->style & ES_MULTILINE) {
2398 l = EDIT_EM_LineFromChar(es, index);
2399 y = (l - es->y_offset) * es->line_height;
2400 li = EDIT_EM_LineIndex(es, l);
2401 if (after_wrap && (li == index) && l) {
2403 LINEDEF *line_def = es->first_line_def;
2405 line_def = line_def->next;
2408 if (line_def->ending == END_WRAP) {
2410 y -= es->line_height;
2411 li = EDIT_EM_LineIndex(es, l);
2414 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2415 es->tabs_count, es->tabs)) - es->x_offset;
2417 LPSTR text = EDIT_GetPasswordPointer_SL(es);
2418 if (index < es->x_offset) {
2419 GetTextExtentPoint32A(dc, text + index,
2420 es->x_offset - index, &size);
2423 GetTextExtentPoint32A(dc, text + es->x_offset,
2424 index - es->x_offset, &size);
2428 if (es->style & ES_PASSWORD)
2429 HeapFree(es->heap, 0 ,text);
2431 x += es->format_rect.left;
2432 y += es->format_rect.top;
2434 SelectObject(dc, old_font);
2435 ReleaseDC(wnd->hwndSelf, dc);
2436 return MAKELONG((INT16)x, (INT16)y);
2440 /*********************************************************************
2444 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2447 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update)
2449 UINT strl = strlen(lpsz_replace);
2450 UINT tl = strlen(es->text);
2457 s = es->selection_start;
2458 e = es->selection_end;
2460 if ((s == e) && !strl)
2465 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2469 /* there is something to be deleted */
2471 utl = strlen(es->undo_text);
2472 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2473 /* undo-buffer is extended to the right */
2474 EDIT_MakeUndoFit(es, utl + e - s);
2475 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2476 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2477 /* undo-buffer is extended to the left */
2478 EDIT_MakeUndoFit(es, utl + e - s);
2479 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2481 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2482 p[i] = (es->text + s)[i];
2483 es->undo_position = s;
2485 /* new undo-buffer */
2486 EDIT_MakeUndoFit(es, e - s);
2487 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2488 es->undo_position = s;
2490 /* any deletion makes the old insertion-undo invalid */
2491 es->undo_insert_count = 0;
2493 EDIT_EM_EmptyUndoBuffer(es);
2496 strcpy(es->text + s, es->text + e);
2499 /* there is an insertion */
2501 if ((s == es->undo_position) ||
2502 ((es->undo_insert_count) &&
2503 (s == es->undo_position + es->undo_insert_count)))
2505 * insertion is new and at delete position or
2506 * an extension to either left or right
2508 es->undo_insert_count += strl;
2510 /* new insertion undo */
2511 es->undo_position = s;
2512 es->undo_insert_count = strl;
2513 /* new insertion makes old delete-buffer invalid */
2514 *es->undo_text = '\0';
2517 EDIT_EM_EmptyUndoBuffer(es);
2520 tl = strlen(es->text);
2521 for (p = es->text + tl ; p >= es->text + s ; p--)
2523 for (i = 0 , p = es->text + s ; i < strl ; i++)
2524 p[i] = lpsz_replace[i];
2525 if(es->style & ES_UPPERCASE)
2526 CharUpperBuffA(p, strl);
2527 else if(es->style & ES_LOWERCASE)
2528 CharLowerBuffA(p, strl);
2531 /* FIXME: really inefficient */
2532 if (es->style & ES_MULTILINE)
2533 EDIT_BuildLineDefs_ML(wnd, es);
2535 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2536 es->flags |= EF_MODIFIED;
2537 if (send_update) es->flags |= EF_UPDATE;
2538 EDIT_EM_ScrollCaret(wnd, es);
2540 /* FIXME: really inefficient */
2541 EDIT_UpdateText(wnd, NULL, TRUE);
2545 /*********************************************************************
2550 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
2554 if (!(es->style & ES_MULTILINE))
2555 return (LRESULT)FALSE;
2565 if (es->y_offset < es->line_count - 1)
2570 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2573 if (es->y_offset < es->line_count - 1)
2574 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2577 return (LRESULT)FALSE;
2580 EDIT_EM_LineScroll(wnd, es, 0, dy);
2581 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2583 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2587 /*********************************************************************
2592 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
2594 if (es->style & ES_MULTILINE) {
2599 INT cw = es->char_width;
2604 l = EDIT_EM_LineFromChar(es, es->selection_end);
2605 li = EDIT_EM_LineIndex(es, l);
2606 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2607 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2608 if (l >= es->y_offset + vlc)
2609 dy = l - vlc + 1 - es->y_offset;
2610 if (l < es->y_offset)
2611 dy = l - es->y_offset;
2612 ww = es->format_rect.right - es->format_rect.left;
2613 if (x < es->format_rect.left)
2614 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2615 if (x > es->format_rect.right)
2616 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2618 EDIT_EM_LineScroll(wnd, es, dx, dy);
2624 if (!(es->style & ES_AUTOHSCROLL))
2627 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2628 format_width = es->format_rect.right - es->format_rect.left;
2629 if (x < es->format_rect.left) {
2630 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2633 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2634 } while ((x < goal) && es->x_offset);
2635 /* FIXME: use ScrollWindow() somehow to improve performance */
2636 EDIT_UpdateText(wnd, NULL, TRUE);
2637 } else if (x > es->format_rect.right) {
2639 INT len = strlen(es->text);
2640 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2643 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2644 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
2645 } while ((x > goal) && (x_last > es->format_rect.right));
2646 /* FIXME: use ScrollWindow() somehow to improve performance */
2647 EDIT_UpdateText(wnd, NULL, TRUE);
2653 /*********************************************************************
2657 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2660 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
2662 if (!(es->style & ES_MULTILINE))
2666 WARN("called with NULL handle\n");
2670 EDIT_UnlockBuffer(wnd, es, TRUE);
2672 * old buffer is freed by caller, unless
2673 * it is still in our own heap. (in that case
2674 * we free it, correcting the buggy caller.)
2677 HeapFree(es->heap, 0, es->text);
2679 es->hloc16 = (HLOCAL16)NULL;
2682 es->buffer_size = LocalSize(es->hloc32) - 1;
2683 EDIT_LockBuffer(wnd, es);
2685 es->x_offset = es->y_offset = 0;
2686 es->selection_start = es->selection_end = 0;
2687 EDIT_EM_EmptyUndoBuffer(es);
2688 es->flags &= ~EF_MODIFIED;
2689 es->flags &= ~EF_UPDATE;
2690 EDIT_BuildLineDefs_ML(wnd, es);
2691 EDIT_UpdateText(wnd, NULL, TRUE);
2692 EDIT_EM_ScrollCaret(wnd, es);
2696 /*********************************************************************
2700 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2703 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2705 if (!(es->style & ES_MULTILINE))
2709 WARN("called with NULL handle\n");
2713 EDIT_UnlockBuffer(wnd, es, TRUE);
2715 * old buffer is freed by caller, unless
2716 * it is still in our own heap. (in that case
2717 * we free it, correcting the buggy caller.)
2720 HeapFree(es->heap, 0, es->text);
2723 es->hloc32 = (HLOCAL)NULL;
2725 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2726 EDIT_LockBuffer(wnd, es);
2728 es->x_offset = es->y_offset = 0;
2729 es->selection_start = es->selection_end = 0;
2730 EDIT_EM_EmptyUndoBuffer(es);
2731 es->flags &= ~EF_MODIFIED;
2732 es->flags &= ~EF_UPDATE;
2733 EDIT_BuildLineDefs_ML(wnd, es);
2734 EDIT_UpdateText(wnd, NULL, TRUE);
2735 EDIT_EM_ScrollCaret(wnd, es);
2739 /*********************************************************************
2743 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2744 * However, the windows version is not complied to yet in all of edit.c
2747 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit)
2749 if (es->style & ES_MULTILINE) {
2751 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
2753 es->buffer_limit = BUFLIMIT_MULTI;
2756 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
2758 es->buffer_limit = BUFLIMIT_SINGLE;
2763 /*********************************************************************
2767 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2768 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2769 * of the char's width, according to the new docs.
2772 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
2773 INT left, INT right)
2775 if (action & EC_LEFTMARGIN) {
2776 if (left != EC_USEFONTINFO)
2777 es->left_margin = left;
2779 es->left_margin = es->char_width / 3;
2782 if (action & EC_RIGHTMARGIN) {
2783 if (right != EC_USEFONTINFO)
2784 es->right_margin = right;
2786 es->right_margin = es->char_width / 3;
2788 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2792 /*********************************************************************
2794 * EM_SETPASSWORDCHAR
2797 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2799 if (es->style & ES_MULTILINE)
2802 if (es->password_char == c)
2805 es->password_char = c;
2807 wnd->dwStyle |= ES_PASSWORD;
2808 es->style |= ES_PASSWORD;
2810 wnd->dwStyle &= ~ES_PASSWORD;
2811 es->style &= ~ES_PASSWORD;
2813 EDIT_UpdateText(wnd, NULL, TRUE);
2817 /*********************************************************************
2821 * note: unlike the specs say: the order of start and end
2822 * _is_ preserved in Windows. (i.e. start can be > end)
2823 * In other words: this handler is OK
2826 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2828 UINT old_start = es->selection_start;
2829 UINT old_end = es->selection_end;
2830 UINT len = strlen(es->text);
2832 if (start == (UINT)-1) {
2833 start = es->selection_end;
2834 end = es->selection_end;
2836 start = min(start, len);
2837 end = min(end, len);
2839 es->selection_start = start;
2840 es->selection_end = end;
2842 es->flags |= EF_AFTER_WRAP;
2844 es->flags &= ~EF_AFTER_WRAP;
2845 if (es->flags & EF_FOCUSED)
2846 EDIT_SetCaretPos(wnd, es, end, after_wrap);
2847 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
2848 ORDER_UINT(start, end);
2849 ORDER_UINT(end, old_end);
2850 ORDER_UINT(start, old_start);
2851 ORDER_UINT(old_start, old_end);
2852 if (end != old_start)
2856 * ORDER_UINT32(end, old_start);
2857 * EDIT_InvalidateText(wnd, es, start, end);
2858 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2859 * in place of the following if statement.
2861 if (old_start > end )
2863 EDIT_InvalidateText(wnd, es, start, end);
2864 EDIT_InvalidateText(wnd, es, old_start, old_end);
2868 EDIT_InvalidateText(wnd, es, start, old_start);
2869 EDIT_InvalidateText(wnd, es, end, old_end);
2872 else EDIT_InvalidateText(wnd, es, start, old_end);
2876 /*********************************************************************
2881 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs)
2883 if (!(es->style & ES_MULTILINE))
2886 HeapFree(es->heap, 0, es->tabs);
2887 es->tabs_count = count;
2891 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2892 memcpy(es->tabs, tabs, count * sizeof(INT));
2898 /*********************************************************************
2903 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs)
2905 if (!(es->style & ES_MULTILINE))
2908 HeapFree(es->heap, 0, es->tabs);
2909 es->tabs_count = count;
2914 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2915 for (i = 0 ; i < count ; i++)
2916 es->tabs[i] = *tabs++;
2922 /*********************************************************************
2924 * EM_SETWORDBREAKPROC
2927 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2929 if (es->word_break_proc32A == wbp)
2932 es->word_break_proc32A = wbp;
2933 es->word_break_proc16 = NULL;
2934 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2935 EDIT_BuildLineDefs_ML(wnd, es);
2936 EDIT_UpdateText(wnd, NULL, TRUE);
2941 /*********************************************************************
2943 * EM_SETWORDBREAKPROC16
2946 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2948 if (es->word_break_proc16 == wbp)
2951 es->word_break_proc32A = NULL;
2952 es->word_break_proc16 = wbp;
2953 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2954 EDIT_BuildLineDefs_ML(wnd, es);
2955 EDIT_UpdateText(wnd, NULL, TRUE);
2960 /*********************************************************************
2965 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2967 INT ulength = strlen(es->undo_text);
2968 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2970 strcpy(utext, es->undo_text);
2972 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2973 es->undo_insert_count, utext);
2975 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2976 EDIT_EM_EmptyUndoBuffer(es);
2977 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
2978 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2979 HeapFree(es->heap, 0, utext);
2981 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2982 es->undo_insert_count, es->undo_text);
2984 if (es->flags & EF_UPDATE) {
2985 es->flags &= ~EF_UPDATE;
2986 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
2993 /*********************************************************************
2998 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c)
3000 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
3003 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3004 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3007 if (es->style & ES_MULTILINE) {
3008 if (es->style & ES_READONLY) {
3009 EDIT_MoveHome(wnd, es, FALSE);
3010 EDIT_MoveDown_ML(wnd, es, FALSE);
3012 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n", TRUE);
3013 if (es->flags & EF_UPDATE) {
3014 es->flags &= ~EF_UPDATE;
3015 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3021 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3023 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t", TRUE);
3024 if (es->flags & EF_UPDATE) {
3025 es->flags &= ~EF_UPDATE;
3026 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3031 if (!(es->style & ES_READONLY) && !control) {
3032 if (es->selection_start != es->selection_end)
3033 EDIT_WM_Clear(wnd, es);
3035 /* delete character left of caret */
3036 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3037 EDIT_MoveBackward(wnd, es, TRUE);
3038 EDIT_WM_Clear(wnd, es);
3043 SendMessageA(wnd->hwndSelf, WM_COPY, 0, 0);
3046 SendMessageA(wnd->hwndSelf, WM_PASTE, 0, 0);
3049 SendMessageA(wnd->hwndSelf, WM_CUT, 0, 0);
3053 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
3057 EDIT_EM_ReplaceSel(wnd, es, TRUE, str, TRUE);
3058 if (es->flags & EF_UPDATE) {
3059 es->flags &= ~EF_UPDATE;
3060 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3068 /*********************************************************************
3073 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
3075 if (code || control)
3080 EDIT_EM_Undo(wnd, es);
3083 EDIT_WM_Cut(wnd, es);
3086 EDIT_WM_Copy(wnd, es);
3089 EDIT_WM_Paste(wnd, es);
3092 EDIT_WM_Clear(wnd, es);
3095 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
3096 EDIT_EM_ScrollCaret(wnd, es);
3099 ERR("unknown menu item, please report\n");
3105 /*********************************************************************
3109 * Note: the resource files resource/sysres_??.rc cannot define a
3110 * single popup menu. Hence we use a (dummy) menubar
3111 * containing the single popup menu as its first item.
3113 * FIXME: the message identifiers have been chosen arbitrarily,
3114 * hence we use MF_BYPOSITION.
3115 * We might as well use the "real" values (anybody knows ?)
3116 * The menu definition is in resources/sysres_??.rc.
3117 * Once these are OK, we better use MF_BYCOMMAND here
3118 * (as we do in EDIT_WM_Command()).
3121 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y)
3123 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3124 HMENU popup = GetSubMenu(menu, 0);
3125 UINT start = es->selection_start;
3126 UINT end = es->selection_end;
3128 ORDER_UINT(start, end);
3131 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) ? MF_ENABLED : MF_GRAYED));
3133 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3135 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3137 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
3139 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
3141 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlen(es->text)) ? MF_ENABLED : MF_GRAYED));
3143 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3148 /*********************************************************************
3153 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3155 INT s = es->selection_start;
3156 INT e = es->selection_end;
3163 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3164 dst = GlobalLock(hdst);
3165 lstrcpynA(dst, es->text + s, e - s + 1);
3167 OpenClipboard(wnd->hwndSelf);
3169 SetClipboardData(CF_TEXT, hdst);
3174 /*********************************************************************
3179 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3182 * To initialize some final structure members, we call some helper
3183 * functions. However, since the EDITSTATE is not consistent (i.e.
3184 * not fully initialized), we should be very careful which
3185 * functions can be called, and in what order.
3187 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3188 EDIT_EM_EmptyUndoBuffer(es);
3190 if (cs->lpszName && *(cs->lpszName) != '\0') {
3191 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName, TRUE);
3192 /* if we insert text to the editline, the text scrolls out
3193 * of the window, as the caret is placed after the insert
3194 * pos normally; thus we reset es->selection... to 0 and
3197 es->selection_start = es->selection_end = 0;
3198 EDIT_EM_ScrollCaret(wnd, es);
3199 if (es->flags & EF_UPDATE) {
3200 es->flags &= ~EF_UPDATE;
3201 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3208 /*********************************************************************
3213 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3216 while (LocalUnlock(es->hloc32)) ;
3217 LocalFree(es->hloc32);
3220 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3221 LOCAL_Free(wnd->hInstance, es->hloc16);
3223 HeapDestroy(es->heap);
3224 HeapFree(GetProcessHeap(), 0, es);
3225 *(EDITSTATE **)wnd->wExtra = NULL;
3229 /*********************************************************************
3234 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3239 if ( get_app_version() >= 0x40000 &&(
3240 !es->bEnableState || (es->style & ES_READONLY)))
3241 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3243 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3246 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3248 GetClientRect(wnd->hwndSelf, &rc);
3249 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3250 GetClipBox(dc, &rc);
3252 * FIXME: specs say that we should UnrealizeObject() the brush,
3253 * but the specs of UnrealizeObject() say that we shouldn't
3254 * unrealize a stock object. The default brush that
3255 * DefWndProc() returns is ... a stock object.
3257 FillRect(dc, &rc, brush);
3262 /*********************************************************************
3267 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPSTR text)
3269 lstrcpynA(text, es->text, count);
3270 return strlen(text);
3274 /*********************************************************************
3278 * 16 bit notepad needs this. Actually it is not _our_ hack,
3279 * it is notepad's. Notepad is sending us scrollbar messages with
3280 * undocumented parameters without us even having a scrollbar ... !?!?
3283 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos)
3286 INT fw = es->format_rect.right - es->format_rect.left;
3289 if (!(es->flags & EF_HSCROLL_HACK)) {
3290 ERR("hacked WM_HSCROLL handler invoked\n");
3291 ERR(" if you are _not_ running 16 bit notepad, please report\n");
3292 ERR(" (this message is only displayed once per edit control)\n");
3293 es->flags |= EF_HSCROLL_HACK;
3299 dx = -es->char_width;
3302 if (es->x_offset < es->text_width)
3303 dx = es->char_width;
3307 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3310 if (es->x_offset < es->text_width)
3311 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3318 if (es->x_offset < es->text_width)
3319 dx = es->text_width - es->x_offset;
3322 es->flags |= EF_HSCROLL_TRACK;
3323 dx = pos * es->text_width / 100 - es->x_offset;
3325 case SB_THUMBPOSITION:
3326 es->flags &= ~EF_HSCROLL_TRACK;
3327 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3328 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3334 * FIXME : the next two are undocumented !
3335 * Are we doing the right thing ?
3336 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3337 * although it's also a regular control message.
3340 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3342 case EM_LINESCROLL16:
3347 ERR("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3351 EDIT_EM_LineScroll(wnd, es, dx, 0);
3356 /*********************************************************************
3361 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
3366 if (!(es->style & ES_MULTILINE))
3369 if (!(es->style & ES_AUTOHSCROLL))
3372 if (!(es->style & WS_HSCROLL))
3373 return EDIT_HScroll_Hack(wnd, es, action, pos);
3376 fw = es->format_rect.right - es->format_rect.left;
3380 dx = -es->char_width;
3383 if (es->x_offset < es->text_width)
3384 dx = es->char_width;
3388 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3391 if (es->x_offset < es->text_width)
3392 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3399 if (es->x_offset < es->text_width)
3400 dx = es->text_width - es->x_offset;
3403 es->flags |= EF_HSCROLL_TRACK;
3404 dx = pos - es->x_offset;
3406 case SB_THUMBPOSITION:
3407 es->flags &= ~EF_HSCROLL_TRACK;
3408 if (!(dx = pos - es->x_offset)) {
3409 SetScrollPos(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3410 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3417 ERR("undocumented WM_HSCROLL parameter, please report\n");
3421 EDIT_EM_LineScroll(wnd, es, dx, 0);
3426 /*********************************************************************
3431 static BOOL EDIT_CheckCombo(WND *wnd, EDITSTATE *es, UINT msg, INT key)
3433 HWND hLBox = es->hwndListBox;
3441 hCombo = wnd->parent->hwndSelf;
3445 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3446 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3448 if (key == VK_UP || key == VK_DOWN)
3450 if (SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0))
3453 if (msg == WM_KEYDOWN || nEUI)
3454 bDropped = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3460 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3462 /* make sure ComboLBox pops up */
3463 SendMessageA(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3468 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3471 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3473 SendMessageA(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
3475 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
3480 SendMessageA(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3486 /*********************************************************************
3490 * Handling of special keys that don't produce a WM_CHAR
3491 * (i.e. non-printable keys) & Backspace & Delete
3494 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key)
3499 if (GetKeyState(VK_MENU) & 0x8000)
3502 shift = GetKeyState(VK_SHIFT) & 0x8000;
3503 control = GetKeyState(VK_CONTROL) & 0x8000;
3508 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key) || key == VK_F4)
3513 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3514 EDIT_MoveUp_ML(wnd, es, shift);
3517 EDIT_MoveWordBackward(wnd, es, shift);
3519 EDIT_MoveBackward(wnd, es, shift);
3522 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key))
3526 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3527 EDIT_MoveDown_ML(wnd, es, shift);
3529 EDIT_MoveWordForward(wnd, es, shift);
3531 EDIT_MoveForward(wnd, es, shift);
3534 EDIT_MoveHome(wnd, es, shift);
3537 EDIT_MoveEnd(wnd, es, shift);
3540 if (es->style & ES_MULTILINE)
3541 EDIT_MovePageUp_ML(wnd, es, shift);
3543 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
3546 if (es->style & ES_MULTILINE)
3547 EDIT_MovePageDown_ML(wnd, es, shift);
3549 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
3552 if (!(es->style & ES_READONLY) && !(shift && control)) {
3553 if (es->selection_start != es->selection_end) {
3555 EDIT_WM_Cut(wnd, es);
3557 EDIT_WM_Clear(wnd, es);
3560 /* delete character left of caret */
3561 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3562 EDIT_MoveBackward(wnd, es, TRUE);
3563 EDIT_WM_Clear(wnd, es);
3564 } else if (control) {
3565 /* delete to end of line */
3566 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3567 EDIT_MoveEnd(wnd, es, TRUE);
3568 EDIT_WM_Clear(wnd, es);
3570 /* delete character right of caret */
3571 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3572 EDIT_MoveForward(wnd, es, TRUE);
3573 EDIT_WM_Clear(wnd, es);
3580 if (!(es->style & ES_READONLY))
3581 EDIT_WM_Paste(wnd, es);
3583 EDIT_WM_Copy(wnd, es);
3586 /* If the edit doesn't want the return send a message to the default object */
3587 if(!(es->style & ES_WANTRETURN))
3589 HWND hwndParent = GetParent(wnd->hwndSelf);
3590 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
3591 if (HIWORD(dw) == DC_HASDEFID)
3593 SendMessageA( hwndParent, WM_COMMAND,
3594 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3595 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3604 /*********************************************************************
3609 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es)
3611 es->flags &= ~EF_FOCUSED;
3613 if(!(es->style & ES_NOHIDESEL))
3614 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3615 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3620 /*********************************************************************
3624 * The caret position has been set on the WM_LBUTTONDOWN message
3627 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es)
3630 INT e = es->selection_end;
3635 if (!(es->flags & EF_FOCUSED))
3638 l = EDIT_EM_LineFromChar(es, e);
3639 li = EDIT_EM_LineIndex(es, l);
3640 ll = EDIT_EM_LineLength(es, e);
3641 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3642 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3643 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3644 EDIT_EM_ScrollCaret(wnd, es);
3649 /*********************************************************************
3654 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3659 if (!(es->flags & EF_FOCUSED))
3662 es->bCaptureState = TRUE;
3663 SetCapture(wnd->hwndSelf);
3664 EDIT_ConfinePoint(es, &x, &y);
3665 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3666 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3667 EDIT_EM_ScrollCaret(wnd, es);
3668 es->region_posx = es->region_posy = 0;
3669 SetTimer(wnd->hwndSelf, 0, 100, NULL);
3674 /*********************************************************************
3679 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es)
3681 if (es->bCaptureState && GetCapture() == hwndSelf) {
3682 KillTimer(hwndSelf, 0);
3685 es->bCaptureState = FALSE;
3690 /*********************************************************************
3695 static LRESULT EDIT_WM_MButtonDown(WND *wnd)
3697 SendMessageA(wnd->hwndSelf,WM_PASTE,0,0);
3702 /*********************************************************************
3707 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y)
3713 if (GetCapture() != wnd->hwndSelf)
3717 * FIXME: gotta do some scrolling if outside client
3718 * area. Maybe reset the timer ?
3721 EDIT_ConfinePoint(es, &x, &y);
3722 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3723 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3724 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3725 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3730 /*********************************************************************
3735 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs)
3739 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3741 *(EDITSTATE **)wnd->wExtra = es;
3744 * Note: since the EDITSTATE has not been fully initialized yet,
3745 * we can't use any API calls that may send
3746 * WM_XXX messages before WM_NCCREATE is completed.
3749 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3751 es->style = cs->style;
3753 es->bEnableState = !(cs->style & WS_DISABLED);
3756 * In Win95 look and feel, the WS_BORDER style is replaced by the
3757 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3758 * control a non client area.
3760 if (TWEAK_WineLook != WIN31_LOOK)
3762 if (es->style & WS_BORDER)
3764 es->style &= ~WS_BORDER;
3765 wnd->dwStyle &= ~WS_BORDER;
3766 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
3771 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
3772 wnd->dwStyle &= ~WS_BORDER;
3775 if (es->style & ES_COMBO)
3776 es->hwndListBox = GetDlgItem(cs->hwndParent, ID_CB_LISTBOX);
3778 if (es->style & ES_MULTILINE) {
3779 es->buffer_size = BUFSTART_MULTI;
3780 es->buffer_limit = BUFLIMIT_MULTI;
3781 if (es->style & WS_VSCROLL)
3782 es->style |= ES_AUTOVSCROLL;
3783 if (es->style & WS_HSCROLL)
3784 es->style |= ES_AUTOHSCROLL;
3785 es->style &= ~ES_PASSWORD;
3786 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3787 if (es->style & ES_RIGHT)
3788 es->style &= ~ES_CENTER;
3789 es->style &= ~WS_HSCROLL;
3790 es->style &= ~ES_AUTOHSCROLL;
3793 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3794 es->style |= ES_AUTOVSCROLL;
3796 es->buffer_size = BUFSTART_SINGLE;
3797 es->buffer_limit = BUFLIMIT_SINGLE;
3798 es->style &= ~ES_CENTER;
3799 es->style &= ~ES_RIGHT;
3800 es->style &= ~WS_HSCROLL;
3801 es->style &= ~WS_VSCROLL;
3802 es->style &= ~ES_AUTOVSCROLL;
3803 es->style &= ~ES_WANTRETURN;
3804 if (es->style & ES_UPPERCASE) {
3805 es->style &= ~ES_LOWERCASE;
3806 es->style &= ~ES_NUMBER;
3807 } else if (es->style & ES_LOWERCASE)
3808 es->style &= ~ES_NUMBER;
3809 if (es->style & ES_PASSWORD)
3810 es->password_char = '*';
3812 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3813 es->style |= ES_AUTOHSCROLL;
3815 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3817 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3818 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3820 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3822 if (es->style & ES_MULTILINE)
3823 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3830 /*********************************************************************
3835 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
3844 BOOL rev = es->bEnableState &&
3845 ((es->flags & EF_FOCUSED) ||
3846 (es->style & ES_NOHIDESEL));
3848 dc = BeginPaint(wnd->hwndSelf, &ps);
3851 if(es->style & WS_BORDER) {
3852 GetClientRect(wnd->hwndSelf, &rc);
3853 if(es->style & ES_MULTILINE) {
3854 if(es->style & WS_HSCROLL) rc.bottom++;
3855 if(es->style & WS_VSCROLL) rc.right++;
3857 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
3859 IntersectClipRect(dc, es->format_rect.left,
3860 es->format_rect.top,
3861 es->format_rect.right,
3862 es->format_rect.bottom);
3863 if (es->style & ES_MULTILINE) {
3864 GetClientRect(wnd->hwndSelf, &rc);
3865 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3868 old_font = SelectObject(dc, es->font);
3869 if ( get_app_version() >= 0x40000 &&(
3870 !es->bEnableState || (es->style & ES_READONLY)))
3871 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3873 EDIT_SEND_CTLCOLOR(wnd, dc);
3875 if (!es->bEnableState)
3876 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3877 GetClipBox(dc, &rcRgn);
3878 if (es->style & ES_MULTILINE) {
3879 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3880 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3881 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3882 if (IntersectRect(&rc, &rcRgn, &rcLine))
3883 EDIT_PaintLine(wnd, es, dc, i, rev);
3886 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3887 if (IntersectRect(&rc, &rcRgn, &rcLine))
3888 EDIT_PaintLine(wnd, es, dc, 0, rev);
3891 SelectObject(dc, old_font);
3892 if (es->flags & EF_FOCUSED)
3893 EDIT_SetCaretPos(wnd, es, es->selection_end,
3894 es->flags & EF_AFTER_WRAP);
3896 EndPaint(wnd->hwndSelf, &ps);
3897 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3898 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3900 si.cbSize = sizeof(SCROLLINFO);
3901 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3903 si.nMax = es->line_count + vlc - 2;
3905 si.nPos = es->y_offset;
3906 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
3908 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3910 INT fw = es->format_rect.right - es->format_rect.left;
3911 si.cbSize = sizeof(SCROLLINFO);
3912 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3914 si.nMax = es->text_width + fw - 1;
3916 si.nPos = es->x_offset;
3917 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3922 /*********************************************************************
3927 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3932 OpenClipboard(wnd->hwndSelf);
3933 if ((hsrc = GetClipboardData(CF_TEXT))) {
3934 src = (LPSTR)GlobalLock(hsrc);
3935 EDIT_EM_ReplaceSel(wnd, es, TRUE, src, TRUE);
3938 if (es->flags & EF_UPDATE) {
3939 es->flags &= ~EF_UPDATE;
3940 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3947 /*********************************************************************
3952 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es)
3954 es->flags |= EF_FOCUSED;
3955 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
3956 EDIT_SetCaretPos(wnd, es, es->selection_end,
3957 es->flags & EF_AFTER_WRAP);
3958 if(!(es->style & ES_NOHIDESEL))
3959 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3960 ShowCaret(wnd->hwndSelf);
3961 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3965 /*********************************************************************
3969 * With Win95 look the margins are set to default font value unless
3970 * the system font (font == 0) is being set, in which case they are left
3974 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
3982 dc = GetDC(wnd->hwndSelf);
3984 old_font = SelectObject(dc, font);
3985 GetTextMetricsA(dc, &tm);
3986 es->line_height = tm.tmHeight;
3987 es->char_width = tm.tmAveCharWidth;
3989 SelectObject(dc, old_font);
3990 ReleaseDC(wnd->hwndSelf, dc);
3991 if (font && (TWEAK_WineLook > WIN31_LOOK))
3992 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3993 EC_USEFONTINFO, EC_USEFONTINFO);
3995 /* Force the recalculation of the format rect for each font change */
3996 GetClientRect(wnd->hwndSelf, &r);
3997 EDIT_SetRectNP(wnd, es, &r);
3999 if (es->style & ES_MULTILINE)
4000 EDIT_BuildLineDefs_ML(wnd, es);
4003 EDIT_UpdateText(wnd, NULL, TRUE);
4004 if (es->flags & EF_FOCUSED) {
4006 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4007 EDIT_SetCaretPos(wnd, es, es->selection_end,
4008 es->flags & EF_AFTER_WRAP);
4009 ShowCaret(wnd->hwndSelf);
4014 /*********************************************************************
4019 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4020 * The modified flag is reset. No notifications are sent.
4022 * For single-line controls, reception of WM_SETTEXT triggers:
4023 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4026 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
4028 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
4030 TRACE("\t'%p'\n", text);
4031 EDIT_EM_ReplaceSel(wnd, es, FALSE, text, !(es->style & ES_MULTILINE));
4033 TRACE("\t<NULL>\n");
4034 EDIT_EM_ReplaceSel(wnd, es, FALSE, "", !(es->style & ES_MULTILINE));
4037 es->flags &= ~EF_MODIFIED;
4038 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
4039 EDIT_EM_ScrollCaret(wnd, es);
4041 if (es->flags & EF_UPDATE) {
4042 es->flags &= ~EF_UPDATE;
4043 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
4048 /*********************************************************************
4053 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
4055 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4057 SetRect(&rc, 0, 0, width, height);
4058 EDIT_SetRectNP(wnd, es, &rc);
4059 EDIT_UpdateText(wnd, NULL, TRUE);
4064 /*********************************************************************
4069 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
4071 if ((key == VK_BACK) && (key_data & 0x2000)) {
4072 if (EDIT_EM_CanUndo(es))
4073 EDIT_EM_Undo(wnd, es);
4075 } else if (key == VK_UP || key == VK_DOWN) {
4076 if (EDIT_CheckCombo(wnd, es, WM_SYSKEYDOWN, key))
4079 return DefWindowProcA(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4083 /*********************************************************************
4088 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es)
4090 if (es->region_posx < 0) {
4091 EDIT_MoveBackward(wnd, es, TRUE);
4092 } else if (es->region_posx > 0) {
4093 EDIT_MoveForward(wnd, es, TRUE);
4096 * FIXME: gotta do some vertical scrolling here, like
4097 * EDIT_EM_LineScroll(wnd, 0, 1);
4102 /*********************************************************************
4106 * 16 bit notepad needs this. Actually it is not _our_ hack,
4107 * it is notepad's. Notepad is sending us scrollbar messages with
4108 * undocumented parameters without us even having a scrollbar ... !?!?
4111 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos)
4116 if (!(es->flags & EF_VSCROLL_HACK)) {
4117 ERR("hacked WM_VSCROLL handler invoked\n");
4118 ERR(" if you are _not_ running 16 bit notepad, please report\n");
4119 ERR(" (this message is only displayed once per edit control)\n");
4120 es->flags |= EF_VSCROLL_HACK;
4128 EDIT_EM_Scroll(wnd, es, action);
4134 dy = es->line_count - 1 - es->y_offset;
4137 es->flags |= EF_VSCROLL_TRACK;
4138 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4140 case SB_THUMBPOSITION:
4141 es->flags &= ~EF_VSCROLL_TRACK;
4142 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4143 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4149 * FIXME : the next two are undocumented !
4150 * Are we doing the right thing ?
4151 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4152 * although it's also a regular control message.
4155 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
4157 case EM_LINESCROLL16:
4162 ERR("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4166 EDIT_EM_LineScroll(wnd, es, 0, dy);
4171 /*********************************************************************
4176 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
4180 if (!(es->style & ES_MULTILINE))
4183 if (!(es->style & ES_AUTOVSCROLL))
4186 if (!(es->style & WS_VSCROLL))
4187 return EDIT_VScroll_Hack(wnd, es, action, pos);
4195 EDIT_EM_Scroll(wnd, es, action);
4202 dy = es->line_count - 1 - es->y_offset;
4205 es->flags |= EF_VSCROLL_TRACK;
4206 dy = pos - es->y_offset;
4208 case SB_THUMBPOSITION:
4209 es->flags &= ~EF_VSCROLL_TRACK;
4210 if (!(dy = pos - es->y_offset)) {
4211 SetScrollPos(wnd->hwndSelf, SB_VERT, pos, TRUE);
4212 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4219 ERR("undocumented WM_VSCROLL action %d, please report\n",
4224 EDIT_EM_LineScroll(wnd, es, 0, dy);
4229 /*********************************************************************
4234 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase)
4236 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4238 /* EF_UPDATE will be turned off in paint */
4239 if (es->flags & EF_UPDATE)
4240 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
4242 InvalidateRect(wnd->hwndSelf, rc, bErase);