Don't call strtok twice if it failed the first time.
[wine.git] / controls / edit.c
blob97e631827d8d4444a23c411577e26ab63b42b141
1 /*
2 * Edit control
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
8 */
11 * please read EDIT.TODO (and update it when you change things)
14 #include "config.h"
16 #include <string.h>
17 #include <stdlib.h>
19 #include "winbase.h"
20 #include "winnt.h"
21 #include "win.h"
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
24 #include "wine/unicode.h"
25 #include "controls.h"
26 #include "local.h"
27 #include "user.h"
28 #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 GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
38 #define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
39 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
42 * extra flags for EDITSTATE.flags field
44 #define EF_MODIFIED 0x0001 /* text has been modified */
45 #define EF_FOCUSED 0x0002 /* we have input focus */
46 #define EF_UPDATE 0x0004 /* notify parent of changed state */
47 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
48 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
49 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
50 wrapped line, instead of in front of the next character */
51 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
53 typedef enum
55 END_0 = 0, /* line ends with terminating '\0' character */
56 END_WRAP, /* line is wrapped */
57 END_HARD, /* line ends with a hard return '\r\n' */
58 END_SOFT /* line ends with a soft return '\r\r\n' */
59 } LINE_END;
61 typedef struct tagLINEDEF {
62 INT length; /* bruto length of a line in bytes */
63 INT net_length; /* netto length of a line in visible characters */
64 LINE_END ending;
65 INT width; /* width of the line in pixels */
66 INT index; /* line index into the buffer */
67 struct tagLINEDEF *next;
68 } LINEDEF;
70 typedef struct
72 BOOL is_unicode; /* how the control was created */
73 LPWSTR text; /* the actual contents of the control */
74 UINT buffer_size; /* the size of the buffer in characters */
75 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
76 HFONT font; /* NULL means standard system font */
77 INT x_offset; /* scroll offset for multi lines this is in pixels
78 for single lines it's in characters */
79 INT line_height; /* height of a screen line in pixels */
80 INT char_width; /* average character width in pixels */
81 DWORD style; /* sane version of wnd->dwStyle */
82 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
83 INT undo_insert_count; /* number of characters inserted in sequence */
84 UINT undo_position; /* character index of the insertion and deletion */
85 LPWSTR undo_text; /* deleted text */
86 UINT undo_buffer_size; /* size of the deleted text buffer */
87 INT selection_start; /* == selection_end if no selection */
88 INT selection_end; /* == current caret position */
89 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
90 INT left_margin; /* in pixels */
91 INT right_margin; /* in pixels */
92 RECT format_rect;
93 INT text_width; /* width of the widest line in pixels for multi line controls
94 and just line width for single line controls */
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 void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
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 hwndParent; /* Handle of parent for sending EN_* messages.
104 Even if parent will change, EN_* messages
105 should be sent to the first parent. */
106 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
108 * only for multi line controls
110 INT lock_count; /* amount of re-entries in the EditWndProc */
111 INT tabs_count;
112 LPINT tabs;
113 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
114 HLOCAL hloc32W; /* our unicode local memory block */
115 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
116 or EM_SETHANDLE16 */
117 HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
118 or EM_SETHANDLE */
119 } EDITSTATE;
122 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
123 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
125 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
126 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
128 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
129 do {TRACE("notification " str " sent to hwnd=%08x\n", \
130 (UINT)(hwnd));} while(0)
132 /* used for disabled or read-only edit control */
133 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
134 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
135 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
136 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
137 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
138 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
139 #define EDIT_NOTIFY_PARENT(es, wNotifyCode, str) \
140 do \
141 { /* Notify parent which has created this edit control */ \
142 DPRINTF_EDIT_NOTIFY((es)->hwndParent, str); \
143 SendMessageW((es)->hwndParent, WM_COMMAND, \
144 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
145 (LPARAM)(wnd)->hwndSelf); \
146 } while(0)
147 #define DPRINTF_EDIT_MSG16(str) \
148 TRACE(\
149 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
150 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
151 #define DPRINTF_EDIT_MSG32(str) \
152 TRACE(\
153 "32 bit %c : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
154 unicode ? 'W' : 'A', \
155 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
157 /*********************************************************************
159 * Declarations
164 * These functions have trivial implementations
165 * We still like to call them internally
166 * "static inline" makes them more like macro's
168 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es);
169 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es);
170 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
171 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
174 * Helper functions only valid for one type of control
176 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es, INT iStart, INT iEnd, INT delta, HRGN hrgn);
177 static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es);
178 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
179 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
180 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
181 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
182 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
184 * Helper functions valid for both single line _and_ multi line controls
186 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action);
187 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
188 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y);
189 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
190 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end);
191 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
192 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size);
193 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size);
194 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend);
195 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend);
196 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend);
197 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend);
198 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend);
199 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend);
200 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
201 static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
202 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
203 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
204 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
205 static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es);
206 static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
208 * EM_XXX message handlers
210 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y);
211 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol);
212 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es);
213 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
214 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode);
215 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end);
216 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
217 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
218 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
219 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
220 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
221 static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy);
222 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
223 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update);
224 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
225 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
226 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc);
227 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
228 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit);
229 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, INT left, INT right);
230 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, WCHAR c);
231 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
232 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs);
233 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs);
234 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, LPARAM lParam);
235 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
236 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
238 * WM_XXX message handlers
240 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, WCHAR c);
241 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
242 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y);
243 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
244 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCWSTR name);
245 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
246 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc);
247 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode);
248 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
249 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key);
250 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es);
251 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es);
252 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
253 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es);
254 static LRESULT EDIT_WM_MButtonDown(WND *wnd);
255 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y);
256 static LRESULT EDIT_WM_NCCreate(WND *wnd, DWORD style, HWND hwndParent, BOOL unicode);
257 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
258 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
259 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es);
260 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
261 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPARAM lParam, BOOL unicode);
262 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
263 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
264 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es);
265 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
266 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
267 static void EDIT_UpdateTextRegion(WND *wnd, HRGN hrgn, BOOL bErase);
269 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
270 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
272 /*********************************************************************
273 * edit class descriptor
275 const struct builtin_class_descr EDIT_builtin_class =
277 "Edit", /* name */
278 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
279 EditWndProcA, /* procA */
280 EditWndProcW, /* procW */
281 sizeof(EDITSTATE *), /* extra */
282 IDC_IBEAMA, /* cursor */
283 0 /* brush */
287 /*********************************************************************
289 * EM_CANUNDO
292 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es)
294 return (es->undo_insert_count || strlenW(es->undo_text));
298 /*********************************************************************
300 * EM_EMPTYUNDOBUFFER
303 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
305 es->undo_insert_count = 0;
306 *es->undo_text = '\0';
310 /*********************************************************************
312 * WM_CLEAR
315 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
317 static const WCHAR empty_stringW[] = {0};
319 /* Protect read-only edit control from modification */
320 if(es->style & ES_READONLY)
321 return;
323 EDIT_EM_ReplaceSel(wnd, es, TRUE, empty_stringW, TRUE);
327 /*********************************************************************
329 * WM_CUT
332 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
334 EDIT_WM_Copy(wnd, es);
335 EDIT_WM_Clear(wnd, es);
339 /**********************************************************************
340 * get_app_version
342 * Returns the window version in case Wine emulates a later version
343 * of windows then the application expects.
345 * In a number of cases when windows runs an application that was
346 * designed for an earlier windows version, windows reverts
347 * to "old" behaviour of that earlier version.
349 * An example is a disabled edit control that needs to be painted.
350 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
351 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
352 * applications with an expected version 0f 4.0 or higher.
355 static DWORD get_app_version(void)
357 static DWORD version;
358 if (!version)
360 DWORD dwEmulatedVersion;
361 OSVERSIONINFOW info;
362 DWORD dwProcVersion = GetProcessVersion(0);
364 GetVersionExW( &info );
365 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
366 /* fixme: this may not be 100% correct; see discussion on the
367 * wine developer list in Nov 1999 */
368 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
370 return version;
374 /*********************************************************************
376 * EditWndProc_locked
378 * The messages are in the order of the actual integer values
379 * (which can be found in include/windows.h)
380 * Whereever possible the 16 bit versions are converted to
381 * the 32 bit ones, so that we can 'fall through' to the
382 * helper functions. These are mostly 32 bit (with a few
383 * exceptions, clearly indicated by a '16' extension to their
384 * names).
387 static LRESULT WINAPI EditWndProc_locked( WND *wnd, UINT msg,
388 WPARAM wParam, LPARAM lParam, BOOL unicode )
390 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
391 LRESULT result = 0;
393 switch (msg) {
394 case WM_DESTROY:
395 DPRINTF_EDIT_MSG32("WM_DESTROY");
396 EDIT_WM_Destroy(wnd, es);
397 result = 0;
398 goto END;
400 case WM_NCCREATE:
401 DPRINTF_EDIT_MSG32("WM_NCCREATE");
402 if(unicode)
404 LPCREATESTRUCTW cs = (LPCREATESTRUCTW)lParam;
405 result = EDIT_WM_NCCreate(wnd, cs->style, cs->hwndParent, TRUE);
407 else
409 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
410 result = EDIT_WM_NCCreate(wnd, cs->style, cs->hwndParent, FALSE);
412 goto END;
415 if (!es)
417 if(unicode)
418 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
419 else
420 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
421 goto END;
425 EDIT_LockBuffer(wnd, es);
426 switch (msg) {
427 case EM_GETSEL16:
428 DPRINTF_EDIT_MSG16("EM_GETSEL");
429 wParam = 0;
430 lParam = 0;
431 /* fall through */
432 case EM_GETSEL:
433 DPRINTF_EDIT_MSG32("EM_GETSEL");
434 result = EDIT_EM_GetSel(es, (LPUINT)wParam, (LPUINT)lParam);
435 break;
437 case EM_SETSEL16:
438 DPRINTF_EDIT_MSG16("EM_SETSEL");
439 if (SLOWORD(lParam) == -1)
440 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
441 else
442 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
443 if (!wParam)
444 EDIT_EM_ScrollCaret(wnd, es);
445 result = 1;
446 break;
447 case EM_SETSEL:
448 DPRINTF_EDIT_MSG32("EM_SETSEL");
449 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
450 EDIT_EM_ScrollCaret(wnd, es);
451 result = 1;
452 break;
454 case EM_GETRECT16:
455 DPRINTF_EDIT_MSG16("EM_GETRECT");
456 if (lParam)
457 CONV_RECT32TO16(&es->format_rect, MapSL(lParam));
458 break;
459 case EM_GETRECT:
460 DPRINTF_EDIT_MSG32("EM_GETRECT");
461 if (lParam)
462 CopyRect((LPRECT)lParam, &es->format_rect);
463 break;
465 case EM_SETRECT16:
466 DPRINTF_EDIT_MSG16("EM_SETRECT");
467 if ((es->style & ES_MULTILINE) && lParam) {
468 RECT rc;
469 CONV_RECT16TO32(MapSL(lParam), &rc);
470 EDIT_SetRectNP(wnd, es, &rc);
471 EDIT_UpdateText(wnd, NULL, TRUE);
473 break;
474 case EM_SETRECT:
475 DPRINTF_EDIT_MSG32("EM_SETRECT");
476 if ((es->style & ES_MULTILINE) && lParam) {
477 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
478 EDIT_UpdateText(wnd, NULL, TRUE);
480 break;
482 case EM_SETRECTNP16:
483 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
484 if ((es->style & ES_MULTILINE) && lParam) {
485 RECT rc;
486 CONV_RECT16TO32(MapSL(lParam), &rc);
487 EDIT_SetRectNP(wnd, es, &rc);
489 break;
490 case EM_SETRECTNP:
491 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
492 if ((es->style & ES_MULTILINE) && lParam)
493 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
494 break;
496 case EM_SCROLL16:
497 DPRINTF_EDIT_MSG16("EM_SCROLL");
498 /* fall through */
499 case EM_SCROLL:
500 DPRINTF_EDIT_MSG32("EM_SCROLL");
501 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
502 break;
504 case EM_LINESCROLL16:
505 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
506 wParam = (WPARAM)(INT)SHIWORD(lParam);
507 lParam = (LPARAM)(INT)SLOWORD(lParam);
508 /* fall through */
509 case EM_LINESCROLL:
510 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
511 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
512 break;
514 case EM_SCROLLCARET16:
515 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
516 /* fall through */
517 case EM_SCROLLCARET:
518 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
519 EDIT_EM_ScrollCaret(wnd, es);
520 result = 1;
521 break;
523 case EM_GETMODIFY16:
524 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
525 /* fall through */
526 case EM_GETMODIFY:
527 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
528 result = ((es->flags & EF_MODIFIED) != 0);
529 break;
531 case EM_SETMODIFY16:
532 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
533 /* fall through */
534 case EM_SETMODIFY:
535 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
536 if (wParam)
537 es->flags |= EF_MODIFIED;
538 else
539 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
540 break;
542 case EM_GETLINECOUNT16:
543 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
544 /* fall through */
545 case EM_GETLINECOUNT:
546 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
547 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
548 break;
550 case EM_LINEINDEX16:
551 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
552 if ((INT16)wParam == -1)
553 wParam = (WPARAM)-1;
554 /* fall through */
555 case EM_LINEINDEX:
556 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
557 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
558 break;
560 case EM_SETHANDLE16:
561 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
562 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
563 break;
564 case EM_SETHANDLE:
565 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
566 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
567 break;
569 case EM_GETHANDLE16:
570 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
571 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
572 break;
573 case EM_GETHANDLE:
574 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
575 result = (LRESULT)EDIT_EM_GetHandle(es);
576 break;
578 case EM_GETTHUMB16:
579 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
580 /* fall through */
581 case EM_GETTHUMB:
582 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
583 result = EDIT_EM_GetThumb(wnd, es);
584 break;
586 /* messages 0x00bf and 0x00c0 missing from specs */
588 case WM_USER+15:
589 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
590 /* fall through */
591 case 0x00bf:
592 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
593 if(unicode)
594 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
595 else
596 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
597 break;
599 case WM_USER+16:
600 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
601 /* fall through */
602 case 0x00c0:
603 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
604 if(unicode)
605 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
606 else
607 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
608 break;
610 case EM_LINELENGTH16:
611 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
612 /* fall through */
613 case EM_LINELENGTH:
614 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
615 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
616 break;
618 case EM_REPLACESEL16:
619 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
620 lParam = (LPARAM)MapSL(lParam);
621 /* fall through */
622 case EM_REPLACESEL:
624 LPWSTR textW;
625 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
627 if(unicode)
628 textW = (LPWSTR)lParam;
629 else
631 LPSTR textA = (LPSTR)lParam;
632 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
633 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
634 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
637 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, textW, TRUE);
638 result = 1;
640 if(!unicode)
641 HeapFree(GetProcessHeap(), 0, textW);
642 break;
644 /* message 0x00c3 missing from specs */
646 case WM_USER+19:
647 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
648 /* fall through */
649 case 0x00c3:
650 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
651 if(unicode)
652 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
653 else
654 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
655 break;
657 case EM_GETLINE16:
658 DPRINTF_EDIT_MSG16("EM_GETLINE");
659 lParam = (LPARAM)MapSL(lParam);
660 /* fall through */
661 case EM_GETLINE:
662 DPRINTF_EDIT_MSG32("EM_GETLINE");
663 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, lParam, unicode);
664 break;
666 case EM_LIMITTEXT16:
667 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
668 /* fall through */
669 case EM_SETLIMITTEXT:
670 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
671 EDIT_EM_SetLimitText(es, (INT)wParam);
672 break;
674 case EM_CANUNDO16:
675 DPRINTF_EDIT_MSG16("EM_CANUNDO");
676 /* fall through */
677 case EM_CANUNDO:
678 DPRINTF_EDIT_MSG32("EM_CANUNDO");
679 result = (LRESULT)EDIT_EM_CanUndo(es);
680 break;
682 case EM_UNDO16:
683 DPRINTF_EDIT_MSG16("EM_UNDO");
684 /* fall through */
685 case EM_UNDO:
686 /* fall through */
687 case WM_UNDO:
688 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
689 result = (LRESULT)EDIT_EM_Undo(wnd, es);
690 break;
692 case EM_FMTLINES16:
693 DPRINTF_EDIT_MSG16("EM_FMTLINES");
694 /* fall through */
695 case EM_FMTLINES:
696 DPRINTF_EDIT_MSG32("EM_FMTLINES");
697 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
698 break;
700 case EM_LINEFROMCHAR16:
701 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
702 /* fall through */
703 case EM_LINEFROMCHAR:
704 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
705 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
706 break;
708 /* message 0x00ca missing from specs */
710 case WM_USER+26:
711 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
712 /* fall through */
713 case 0x00ca:
714 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
715 if(unicode)
716 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
717 else
718 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
719 break;
721 case EM_SETTABSTOPS16:
722 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
723 result = (LRESULT)EDIT_EM_SetTabStops16(es, (INT)wParam, MapSL(lParam));
724 break;
725 case EM_SETTABSTOPS:
726 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
727 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
728 break;
730 case EM_SETPASSWORDCHAR16:
731 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
732 /* fall through */
733 case EM_SETPASSWORDCHAR:
735 WCHAR charW = 0;
736 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
738 if(unicode)
739 charW = (WCHAR)wParam;
740 else
742 CHAR charA = wParam;
743 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
746 EDIT_EM_SetPasswordChar(wnd, es, charW);
747 break;
750 case EM_EMPTYUNDOBUFFER16:
751 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
752 /* fall through */
753 case EM_EMPTYUNDOBUFFER:
754 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
755 EDIT_EM_EmptyUndoBuffer(es);
756 break;
758 case EM_GETFIRSTVISIBLELINE16:
759 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
760 result = es->y_offset;
761 break;
762 case EM_GETFIRSTVISIBLELINE:
763 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
764 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
765 break;
767 case EM_SETREADONLY16:
768 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
769 /* fall through */
770 case EM_SETREADONLY:
771 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
772 if (wParam) {
773 wnd->dwStyle |= ES_READONLY;
774 es->style |= ES_READONLY;
775 } else {
776 wnd->dwStyle &= ~ES_READONLY;
777 es->style &= ~ES_READONLY;
779 result = 1;
780 break;
782 case EM_SETWORDBREAKPROC16:
783 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
784 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
785 break;
786 case EM_SETWORDBREAKPROC:
787 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
788 EDIT_EM_SetWordBreakProc(wnd, es, lParam);
789 break;
791 case EM_GETWORDBREAKPROC16:
792 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
793 result = (LRESULT)es->word_break_proc16;
794 break;
795 case EM_GETWORDBREAKPROC:
796 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
797 result = (LRESULT)es->word_break_proc;
798 break;
800 case EM_GETPASSWORDCHAR16:
801 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
802 /* fall through */
803 case EM_GETPASSWORDCHAR:
805 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
807 if(unicode)
808 result = es->password_char;
809 else
811 WCHAR charW = es->password_char;
812 CHAR charA = 0;
813 WideCharToMultiByte(CP_ACP, 0, &charW, 1, &charA, 1, NULL, NULL);
814 result = charA;
816 break;
819 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
821 case EM_SETMARGINS:
822 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
823 EDIT_EM_SetMargins(es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
824 break;
826 case EM_GETMARGINS:
827 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
828 result = MAKELONG(es->left_margin, es->right_margin);
829 break;
831 case EM_GETLIMITTEXT:
832 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
833 result = es->buffer_limit;
834 break;
836 case EM_POSFROMCHAR:
837 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
838 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
839 break;
841 case EM_CHARFROMPOS:
842 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
843 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
844 break;
846 case WM_GETDLGCODE:
847 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
848 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
850 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
852 int vk = (int)((LPMSG)lParam)->wParam;
854 if ((wnd->dwStyle & ES_WANTRETURN) && vk == VK_RETURN)
856 result |= DLGC_WANTMESSAGE;
858 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
860 if (SendMessageW(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
861 result |= DLGC_WANTMESSAGE;
864 break;
866 case WM_CHAR:
868 WCHAR charW;
869 DPRINTF_EDIT_MSG32("WM_CHAR");
871 if(unicode)
872 charW = wParam;
873 else
875 CHAR charA = wParam;
876 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
879 if ((charW == VK_RETURN || charW == VK_ESCAPE) && es->hwndListBox)
881 if (SendMessageW(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
882 SendMessageW(wnd->parent->hwndSelf, WM_KEYDOWN, charW, 0);
883 break;
885 EDIT_WM_Char(wnd, es, charW);
886 break;
889 case WM_CLEAR:
890 DPRINTF_EDIT_MSG32("WM_CLEAR");
891 EDIT_WM_Clear(wnd, es);
892 break;
894 case WM_COMMAND:
895 DPRINTF_EDIT_MSG32("WM_COMMAND");
896 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
897 break;
899 case WM_CONTEXTMENU:
900 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
901 EDIT_WM_ContextMenu(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
902 break;
904 case WM_COPY:
905 DPRINTF_EDIT_MSG32("WM_COPY");
906 EDIT_WM_Copy(wnd, es);
907 break;
909 case WM_CREATE:
910 DPRINTF_EDIT_MSG32("WM_CREATE");
911 if(unicode)
912 result = EDIT_WM_Create(wnd, es, ((LPCREATESTRUCTW)lParam)->lpszName);
913 else
915 LPCSTR nameA = ((LPCREATESTRUCTA)lParam)->lpszName;
916 LPWSTR nameW = NULL;
917 if(nameA)
919 INT countW = MultiByteToWideChar(CP_ACP, 0, nameA, -1, NULL, 0);
920 if((nameW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
921 MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW);
923 result = EDIT_WM_Create(wnd, es, nameW);
924 if(nameW)
925 HeapFree(GetProcessHeap(), 0, nameW);
927 break;
929 case WM_CUT:
930 DPRINTF_EDIT_MSG32("WM_CUT");
931 EDIT_WM_Cut(wnd, es);
932 break;
934 case WM_ENABLE:
935 DPRINTF_EDIT_MSG32("WM_ENABLE");
936 es->bEnableState = (BOOL) wParam;
937 EDIT_UpdateText(wnd, NULL, TRUE);
938 break;
940 case WM_ERASEBKGND:
941 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
942 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
943 break;
945 case WM_GETFONT:
946 DPRINTF_EDIT_MSG32("WM_GETFONT");
947 result = (LRESULT)es->font;
948 break;
950 case WM_GETTEXT:
951 DPRINTF_EDIT_MSG32("WM_GETTEXT");
952 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, lParam, unicode);
953 break;
955 case WM_GETTEXTLENGTH:
956 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
957 result = strlenW(es->text);
958 break;
960 case WM_HSCROLL:
961 DPRINTF_EDIT_MSG32("WM_HSCROLL");
962 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
963 break;
965 case WM_KEYDOWN:
966 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
967 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam);
968 break;
970 case WM_KILLFOCUS:
971 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
972 result = EDIT_WM_KillFocus(wnd, es);
973 break;
975 case WM_LBUTTONDBLCLK:
976 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
977 result = EDIT_WM_LButtonDblClk(wnd, es);
978 break;
980 case WM_LBUTTONDOWN:
981 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
982 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
983 break;
985 case WM_LBUTTONUP:
986 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
987 result = EDIT_WM_LButtonUp(wnd->hwndSelf, es);
988 break;
990 case WM_MBUTTONDOWN:
991 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
992 result = EDIT_WM_MButtonDown(wnd);
993 break;
995 case WM_MOUSEACTIVATE:
997 * FIXME: maybe DefWindowProc() screws up, but it seems that
998 * modeless dialog boxes need this. If we don't do this, the focus
999 * will _not_ be set by DefWindowProc() for edit controls in a
1000 * modeless dialog box ???
1002 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
1003 SetFocus(wnd->hwndSelf);
1004 result = MA_ACTIVATE;
1005 break;
1007 case WM_MOUSEMOVE:
1009 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
1011 result = EDIT_WM_MouseMove(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
1012 break;
1014 case WM_PAINT:
1015 DPRINTF_EDIT_MSG32("WM_PAINT");
1016 EDIT_WM_Paint(wnd, es, wParam);
1017 break;
1019 case WM_PASTE:
1020 DPRINTF_EDIT_MSG32("WM_PASTE");
1021 EDIT_WM_Paste(wnd, es);
1022 break;
1024 case WM_SETFOCUS:
1025 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
1026 EDIT_WM_SetFocus(wnd, es);
1027 break;
1029 case WM_SETFONT:
1030 DPRINTF_EDIT_MSG32("WM_SETFONT");
1031 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
1032 break;
1034 case WM_SETREDRAW:
1035 /* FIXME: actually set an internal flag and behave accordingly */
1036 break;
1038 case WM_SETTEXT:
1039 DPRINTF_EDIT_MSG32("WM_SETTEXT");
1040 EDIT_WM_SetText(wnd, es, lParam, unicode);
1041 result = TRUE;
1042 break;
1044 case WM_SIZE:
1045 DPRINTF_EDIT_MSG32("WM_SIZE");
1046 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
1047 break;
1049 case WM_SYSKEYDOWN:
1050 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
1051 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
1052 break;
1054 case WM_TIMER:
1055 DPRINTF_EDIT_MSG32("WM_TIMER");
1056 EDIT_WM_Timer(wnd, es);
1057 break;
1059 case WM_VSCROLL:
1060 DPRINTF_EDIT_MSG32("WM_VSCROLL");
1061 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
1062 break;
1064 case WM_MOUSEWHEEL:
1066 int gcWheelDelta = 0;
1067 UINT pulScrollLines = 3;
1068 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1070 if (wParam & (MK_SHIFT | MK_CONTROL)) {
1071 if(unicode)
1072 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
1073 else
1074 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
1075 break;
1077 gcWheelDelta -= SHIWORD(wParam);
1078 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1080 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
1081 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1082 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
1085 break;
1086 default:
1087 if(unicode)
1088 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
1089 else
1090 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
1091 break;
1093 EDIT_UnlockBuffer(wnd, es, FALSE);
1094 END:
1095 return result;
1098 /*********************************************************************
1100 * EditWndProcW (USER32.@)
1102 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1104 LRESULT res;
1105 WND *wndPtr = WIN_FindWndPtr(hWnd);
1107 res = EditWndProc_locked(wndPtr, uMsg, wParam, lParam, TRUE);
1109 WIN_ReleaseWndPtr(wndPtr);
1110 return res;
1113 /*********************************************************************
1115 * EditWndProcA (USER32.@)
1117 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1119 LRESULT res;
1120 WND *wndPtr = WIN_FindWndPtr(hWnd);
1122 res = EditWndProc_locked(wndPtr, uMsg, wParam, lParam, FALSE);
1124 WIN_ReleaseWndPtr(wndPtr);
1125 return res;
1128 /*********************************************************************
1130 * EDIT_BuildLineDefs_ML
1132 * Build linked list of text lines.
1133 * Lines can end with '\0' (last line), a character (if it is wrapped),
1134 * a soft return '\r\r\n' or a hard return '\r\n'
1137 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
1139 HDC dc;
1140 HFONT old_font = 0;
1141 LPWSTR current_position, cp;
1142 INT fw;
1143 LINEDEF *current_line;
1144 LINEDEF *previous_line;
1145 LINEDEF *start_line;
1146 INT line_index = 0, nstart_line = 0, nstart_index = 0;
1147 INT line_count = es->line_count;
1148 INT orig_net_length;
1149 RECT rc;
1151 if (istart == iend && delta == 0)
1152 return;
1154 dc = GetDC(wnd->hwndSelf);
1155 if (es->font)
1156 old_font = SelectObject(dc, es->font);
1158 previous_line = NULL;
1159 current_line = es->first_line_def;
1161 /* Find starting line. istart must lie inside an existing line or
1162 * at the end of buffer */
1163 do {
1164 if (istart < current_line->index + current_line->length ||
1165 current_line->ending == END_0)
1166 break;
1168 previous_line = current_line;
1169 current_line = current_line->next;
1170 line_index++;
1171 } while (current_line);
1173 if (!current_line) /* Error occurred start is not inside previous buffer */
1175 FIXME(" modification occurred outside buffer\n");
1176 return;
1179 /* Remember start of modifications in order to calculate update region */
1180 nstart_line = line_index;
1181 nstart_index = current_line->index;
1183 /* We must start to reformat from the previous line since the modifications
1184 * may have caused the line to wrap upwards. */
1185 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
1187 line_index--;
1188 current_line = previous_line;
1190 start_line = current_line;
1192 fw = es->format_rect.right - es->format_rect.left;
1193 current_position = es->text + current_line->index;
1194 do {
1195 if (current_line != start_line)
1197 if (!current_line || current_line->index + delta > current_position - es->text)
1199 /* The buffer has been expanded, create a new line and
1200 insert it into the link list */
1201 LINEDEF *new_line = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
1202 new_line->next = previous_line->next;
1203 previous_line->next = new_line;
1204 current_line = new_line;
1205 es->line_count++;
1207 else if (current_line->index + delta < current_position - es->text)
1209 /* The previous line merged with this line so we delete this extra entry */
1210 previous_line->next = current_line->next;
1211 HeapFree(GetProcessHeap(), 0, current_line);
1212 current_line = previous_line->next;
1213 es->line_count--;
1214 continue;
1216 else /* current_line->index + delta == current_position */
1218 if (current_position - es->text > iend)
1219 break; /* We reached end of line modifications */
1220 /* else recalulate this line */
1224 current_line->index = current_position - es->text;
1225 orig_net_length = current_line->net_length;
1227 /* Find end of line */
1228 cp = current_position;
1229 while (*cp) {
1230 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1231 break;
1232 cp++;
1235 /* Mark type of line termination */
1236 if (!(*cp)) {
1237 current_line->ending = END_0;
1238 current_line->net_length = strlenW(current_position);
1239 } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
1240 current_line->ending = END_SOFT;
1241 current_line->net_length = cp - current_position - 1;
1242 } else {
1243 current_line->ending = END_HARD;
1244 current_line->net_length = cp - current_position;
1247 /* Calculate line width */
1248 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1249 current_position, current_line->net_length,
1250 es->tabs_count, es->tabs));
1252 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1253 if ((!(es->style & ES_AUTOHSCROLL)) && (current_line->width > fw)) {
1254 INT next = 0;
1255 INT prev;
1256 do {
1257 prev = next;
1258 next = EDIT_CallWordBreakProc(es, current_position - es->text,
1259 prev + 1, current_line->net_length, WB_RIGHT);
1260 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1261 current_position, next, es->tabs_count, es->tabs));
1262 } while (current_line->width <= fw);
1263 if (!prev) { /* Didn't find a line break so force a break */
1264 next = 0;
1265 do {
1266 prev = next;
1267 next++;
1268 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1269 current_position, next, es->tabs_count, es->tabs));
1270 } while (current_line->width <= fw);
1271 if (!prev)
1272 prev = 1;
1275 /* If the first line we are calculating, wrapped before istart, we must
1276 * adjust istart in order for this to be reflected in the update region. */
1277 if (current_line->index == nstart_index && istart > current_line->index + prev)
1278 istart = current_line->index + prev;
1279 /* else if we are updating the previous line before the first line we
1280 * are re-caulculating and it expanded */
1281 else if (current_line == start_line &&
1282 current_line->index != nstart_index && orig_net_length < prev)
1284 /* Line expanded due to an upwards line wrap so we must partially include
1285 * previous line in update region */
1286 nstart_line = line_index;
1287 nstart_index = current_line->index;
1288 istart = current_line->index + orig_net_length;
1291 current_line->net_length = prev;
1292 current_line->ending = END_WRAP;
1293 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc, current_position,
1294 current_line->net_length, es->tabs_count, es->tabs));
1298 /* Adjust length to include line termination */
1299 switch (current_line->ending) {
1300 case END_SOFT:
1301 current_line->length = current_line->net_length + 3;
1302 break;
1303 case END_HARD:
1304 current_line->length = current_line->net_length + 2;
1305 break;
1306 case END_WRAP:
1307 case END_0:
1308 current_line->length = current_line->net_length;
1309 break;
1311 es->text_width = max(es->text_width, current_line->width);
1312 current_position += current_line->length;
1313 previous_line = current_line;
1314 current_line = current_line->next;
1315 line_index++;
1316 } while (previous_line->ending != END_0);
1318 /* Finish adjusting line index's by delta or remove hanging lines */
1319 if (previous_line->ending == END_0)
1321 LINEDEF *pnext = NULL;
1323 previous_line->next = NULL;
1324 while (current_line)
1326 pnext = current_line->next;
1327 HeapFree(GetProcessHeap(), 0, current_line);
1328 current_line = pnext;
1329 es->line_count--;
1332 else
1334 while (current_line)
1336 current_line->index += delta;
1337 current_line = current_line->next;
1341 /* Calculate rest of modification rectangle */
1342 if (hrgn)
1344 HRGN tmphrgn;
1346 * We calculate two rectangles. One for the first line which may have
1347 * an indent with respect to the format rect. The other is a format-width
1348 * rectangle that spans the rest of the lines that changed or moved.
1350 rc.top = es->format_rect.top + nstart_line * es->line_height -
1351 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
1352 rc.bottom = rc.top + es->line_height;
1353 rc.left = es->format_rect.left + (INT)LOWORD(GetTabbedTextExtentW(dc,
1354 es->text + nstart_index, istart - nstart_index,
1355 es->tabs_count, es->tabs)) - es->x_offset; /* Adjust for horz scroll */
1356 rc.right = es->format_rect.right;
1357 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
1359 rc.top = rc.bottom;
1360 rc.left = es->format_rect.left;
1361 rc.right = es->format_rect.right;
1363 * If lines were added or removed we must re-paint the remainder of the
1364 * lines since the remaining lines were either shifted up or down.
1366 if (line_count < es->line_count) /* We added lines */
1367 rc.bottom = es->line_count * es->line_height;
1368 else if (line_count > es->line_count) /* We removed lines */
1369 rc.bottom = line_count * es->line_height;
1370 else
1371 rc.bottom = line_index * es->line_height;
1372 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
1373 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1374 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
1375 DeleteObject(tmphrgn);
1378 if (es->font)
1379 SelectObject(dc, old_font);
1381 ReleaseDC(wnd->hwndSelf, dc);
1384 /*********************************************************************
1386 * EDIT_CalcLineWidth_SL
1389 static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es)
1391 es->text_width = SLOWORD(EDIT_EM_PosFromChar(wnd, es, strlenW(es->text), FALSE));
1394 /*********************************************************************
1396 * EDIT_CallWordBreakProc
1398 * Call appropriate WordBreakProc (internal or external).
1400 * Note: The "start" argument should always be an index refering
1401 * to es->text. The actual wordbreak proc might be
1402 * 16 bit, so we can't always pass any 32 bit LPSTR.
1403 * Hence we assume that es->text is the buffer that holds
1404 * the string under examination (we can decide this for ourselves).
1407 /* ### start build ### */
1408 extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1409 /* ### stop build ### */
1410 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
1412 INT ret, iWndsLocks;
1414 /* To avoid any deadlocks, all the locks on the windows structures
1415 must be suspended before the control is passed to the application */
1416 iWndsLocks = WIN_SuspendWndsLock();
1418 if (es->word_break_proc16) {
1419 HGLOBAL16 hglob16;
1420 SEGPTR segptr;
1421 INT countA;
1423 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1424 hglob16 = GlobalAlloc16(GMEM_MOVEABLE | GMEM_ZEROINIT, countA);
1425 segptr = K32WOWGlobalLock16(hglob16);
1426 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, MapSL(segptr), countA, NULL, NULL);
1427 ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1428 segptr, index, countA, action);
1429 GlobalUnlock16(hglob16);
1430 GlobalFree16(hglob16);
1432 else if (es->word_break_proc)
1434 if(es->is_unicode)
1436 EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
1438 TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1439 es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
1440 ret = wbpW(es->text + start, index, count, action);
1442 else
1444 EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
1445 INT countA;
1446 CHAR *textA;
1448 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1449 textA = HeapAlloc(GetProcessHeap(), 0, countA);
1450 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
1451 TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1452 es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
1453 ret = wbpA(textA, index, countA, action);
1454 HeapFree(GetProcessHeap(), 0, textA);
1457 else
1458 ret = EDIT_WordBreakProc(es->text + start, index, count, action);
1460 WIN_RestoreWndsLock(iWndsLocks);
1461 return ret;
1465 /*********************************************************************
1467 * EDIT_CharFromPos
1469 * Beware: This is not the function called on EM_CHARFROMPOS
1470 * The position _can_ be outside the formatting / client
1471 * rectangle
1472 * The return value is only the character index
1475 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1477 INT index;
1478 HDC dc;
1479 HFONT old_font = 0;
1481 if (es->style & ES_MULTILINE) {
1482 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1483 INT line_index = 0;
1484 LINEDEF *line_def = es->first_line_def;
1485 INT low, high;
1486 while ((line > 0) && line_def->next) {
1487 line_index += line_def->length;
1488 line_def = line_def->next;
1489 line--;
1491 x += es->x_offset - es->format_rect.left;
1492 if (x >= line_def->width) {
1493 if (after_wrap)
1494 *after_wrap = (line_def->ending == END_WRAP);
1495 return line_index + line_def->net_length;
1497 if (x <= 0) {
1498 if (after_wrap)
1499 *after_wrap = FALSE;
1500 return line_index;
1502 dc = GetDC(wnd->hwndSelf);
1503 if (es->font)
1504 old_font = SelectObject(dc, es->font);
1505 low = line_index + 1;
1506 high = line_index + line_def->net_length + 1;
1507 while (low < high - 1)
1509 INT mid = (low + high) / 2;
1510 if (LOWORD(GetTabbedTextExtentW(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1511 else low = mid;
1513 index = low;
1515 if (after_wrap)
1516 *after_wrap = ((index == line_index + line_def->net_length) &&
1517 (line_def->ending == END_WRAP));
1518 } else {
1519 LPWSTR text;
1520 SIZE size;
1521 if (after_wrap)
1522 *after_wrap = FALSE;
1523 x -= es->format_rect.left;
1524 if (!x)
1525 return es->x_offset;
1526 text = EDIT_GetPasswordPointer_SL(es);
1527 dc = GetDC(wnd->hwndSelf);
1528 if (es->font)
1529 old_font = SelectObject(dc, es->font);
1530 if (x < 0)
1532 INT low = 0;
1533 INT high = es->x_offset;
1534 while (low < high - 1)
1536 INT mid = (low + high) / 2;
1537 GetTextExtentPoint32W( dc, text + mid,
1538 es->x_offset - mid, &size );
1539 if (size.cx > -x) low = mid;
1540 else high = mid;
1542 index = low;
1544 else
1546 INT low = es->x_offset;
1547 INT high = strlenW(es->text) + 1;
1548 while (low < high - 1)
1550 INT mid = (low + high) / 2;
1551 GetTextExtentPoint32W( dc, text + es->x_offset,
1552 mid - es->x_offset, &size );
1553 if (size.cx > x) high = mid;
1554 else low = mid;
1556 index = low;
1558 if (es->style & ES_PASSWORD)
1559 HeapFree(GetProcessHeap(), 0, text);
1561 if (es->font)
1562 SelectObject(dc, old_font);
1563 ReleaseDC(wnd->hwndSelf, dc);
1564 return index;
1568 /*********************************************************************
1570 * EDIT_ConfinePoint
1572 * adjusts the point to be within the formatting rectangle
1573 * (so CharFromPos returns the nearest _visible_ character)
1576 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y)
1578 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1579 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1583 /*********************************************************************
1585 * EDIT_GetLineRect
1587 * Calculates the bounding rectangle for a line from a starting
1588 * column to an ending column.
1591 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1593 INT line_index = EDIT_EM_LineIndex(es, line);
1595 if (es->style & ES_MULTILINE)
1596 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1597 else
1598 rc->top = es->format_rect.top;
1599 rc->bottom = rc->top + es->line_height;
1600 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1601 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1605 /*********************************************************************
1607 * EDIT_GetPasswordPointer_SL
1609 * note: caller should free the (optionally) allocated buffer
1612 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
1614 if (es->style & ES_PASSWORD) {
1615 INT len = strlenW(es->text);
1616 LPWSTR text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1617 text[len] = '\0';
1618 while(len) text[--len] = es->password_char;
1619 return text;
1620 } else
1621 return es->text;
1625 /*********************************************************************
1627 * EDIT_LockBuffer
1629 * This acts as a LOCAL_Lock(), but it locks only once. This way
1630 * you can call it whenever you like, without unlocking.
1633 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1635 if (!es) {
1636 ERR("no EDITSTATE ... please report\n");
1637 return;
1639 if (!es->text) {
1640 CHAR *textA = NULL;
1641 UINT countA = 0;
1642 BOOL _16bit = FALSE;
1644 if(es->hloc32W)
1646 if(es->hloc32A)
1648 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1649 textA = LocalLock(es->hloc32A);
1650 countA = strlen(textA) + 1;
1652 else if(es->hloc16)
1654 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1655 textA = LOCAL_Lock(wnd->hInstance, es->hloc16);
1656 countA = strlen(textA) + 1;
1657 _16bit = TRUE;
1660 else {
1661 ERR("no buffer ... please report\n");
1662 return;
1665 if(textA)
1667 HLOCAL hloc32W_new;
1668 UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
1669 TRACE("%d bytes translated to %d WCHARs\n", countA, countW_new);
1670 if(countW_new > es->buffer_size + 1)
1672 UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1673 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1674 hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1675 if(hloc32W_new)
1677 es->hloc32W = hloc32W_new;
1678 es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1679 TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1681 else
1682 WARN("FAILED! Will synchronize partially\n");
1686 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1687 es->text = LocalLock(es->hloc32W);
1689 if(textA)
1691 MultiByteToWideChar(CP_ACP, 0, textA, countA, es->text, es->buffer_size + 1);
1692 if(_16bit)
1693 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1694 else
1695 LocalUnlock(es->hloc32A);
1698 es->lock_count++;
1702 /*********************************************************************
1704 * EDIT_SL_InvalidateText
1706 * Called from EDIT_InvalidateText().
1707 * Does the job for single-line controls only.
1710 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1712 RECT line_rect;
1713 RECT rc;
1715 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1716 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1717 EDIT_UpdateText(wnd, &rc, FALSE);
1721 /*********************************************************************
1723 * EDIT_ML_InvalidateText
1725 * Called from EDIT_InvalidateText().
1726 * Does the job for multi-line controls only.
1729 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1731 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1732 INT sl = EDIT_EM_LineFromChar(es, start);
1733 INT el = EDIT_EM_LineFromChar(es, end);
1734 INT sc;
1735 INT ec;
1736 RECT rc1;
1737 RECT rcWnd;
1738 RECT rcLine;
1739 RECT rcUpdate;
1740 INT l;
1742 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1743 return;
1745 sc = start - EDIT_EM_LineIndex(es, sl);
1746 ec = end - EDIT_EM_LineIndex(es, el);
1747 if (sl < es->y_offset) {
1748 sl = es->y_offset;
1749 sc = 0;
1751 if (el > es->y_offset + vlc) {
1752 el = es->y_offset + vlc;
1753 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1755 GetClientRect(wnd->hwndSelf, &rc1);
1756 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1757 if (sl == el) {
1758 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1759 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1760 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1761 } else {
1762 EDIT_GetLineRect(wnd, es, sl, sc,
1763 EDIT_EM_LineLength(es,
1764 EDIT_EM_LineIndex(es, sl)),
1765 &rcLine);
1766 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1767 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1768 for (l = sl + 1 ; l < el ; l++) {
1769 EDIT_GetLineRect(wnd, es, l, 0,
1770 EDIT_EM_LineLength(es,
1771 EDIT_EM_LineIndex(es, l)),
1772 &rcLine);
1773 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1774 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1776 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1777 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1778 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1783 /*********************************************************************
1785 * EDIT_InvalidateText
1787 * Invalidate the text from offset start upto, but not including,
1788 * offset end. Useful for (re)painting the selection.
1789 * Regions outside the linewidth are not invalidated.
1790 * end == -1 means end == TextLength.
1791 * start and end need not be ordered.
1794 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1796 if (end == start)
1797 return;
1799 if (end == -1)
1800 end = strlenW(es->text);
1802 ORDER_INT(start, end);
1804 if (es->style & ES_MULTILINE)
1805 EDIT_ML_InvalidateText(wnd, es, start, end);
1806 else
1807 EDIT_SL_InvalidateText(wnd, es, start, end);
1811 /*********************************************************************
1813 * EDIT_MakeFit
1815 * Try to fit size + 1 characters in the buffer. Constrain to limits.
1818 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size)
1820 HLOCAL hNew32W;
1822 if (size <= es->buffer_size)
1823 return TRUE;
1824 if (size > es->buffer_limit) {
1825 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT, "EN_MAXTEXT");
1826 return FALSE;
1828 if (size > es->buffer_limit)
1829 size = es->buffer_limit;
1831 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1833 /* Force edit to unlock it's buffer. es->text now NULL */
1834 EDIT_UnlockBuffer(wnd, es, TRUE);
1836 if (es->hloc32W) {
1837 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1838 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1839 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32W, hNew32W);
1840 es->hloc32W = hNew32W;
1841 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1845 EDIT_LockBuffer(wnd, es);
1847 if (es->buffer_size < size) {
1848 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1849 EDIT_NOTIFY_PARENT(es, EN_ERRSPACE, "EN_ERRSPACE");
1850 return FALSE;
1851 } else {
1852 TRACE("We now have %d+1\n", es->buffer_size);
1853 return TRUE;
1858 /*********************************************************************
1860 * EDIT_MakeUndoFit
1862 * Try to fit size + 1 bytes in the undo buffer.
1865 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1867 UINT alloc_size;
1869 if (size <= es->undo_buffer_size)
1870 return TRUE;
1872 TRACE("trying to ReAlloc to %d+1\n", size);
1874 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1875 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1876 es->undo_buffer_size = alloc_size/sizeof(WCHAR);
1877 return TRUE;
1879 else
1881 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1882 return FALSE;
1887 /*********************************************************************
1889 * EDIT_MoveBackward
1892 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1894 INT e = es->selection_end;
1896 if (e) {
1897 e--;
1898 if ((es->style & ES_MULTILINE) && e &&
1899 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1900 e--;
1901 if (e && (es->text[e - 1] == '\r'))
1902 e--;
1905 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1906 EDIT_EM_ScrollCaret(wnd, es);
1910 /*********************************************************************
1912 * EDIT_MoveDown_ML
1914 * Only for multi line controls
1915 * Move the caret one line down, on a column with the nearest
1916 * x coordinate on the screen (might be a different column).
1919 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1921 INT s = es->selection_start;
1922 INT e = es->selection_end;
1923 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1924 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1925 INT x = SLOWORD(pos);
1926 INT y = SHIWORD(pos);
1928 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1929 if (!extend)
1930 s = e;
1931 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1932 EDIT_EM_ScrollCaret(wnd, es);
1936 /*********************************************************************
1938 * EDIT_MoveEnd
1941 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1943 BOOL after_wrap = FALSE;
1944 INT e;
1946 /* Pass a high value in x to make sure of receiving the end of the line */
1947 if (es->style & ES_MULTILINE)
1948 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1949 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1950 else
1951 e = strlenW(es->text);
1952 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1953 EDIT_EM_ScrollCaret(wnd, es);
1957 /*********************************************************************
1959 * EDIT_MoveForward
1962 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1964 INT e = es->selection_end;
1966 if (es->text[e]) {
1967 e++;
1968 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1969 if (es->text[e] == '\n')
1970 e++;
1971 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1972 e += 2;
1975 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1976 EDIT_EM_ScrollCaret(wnd, es);
1980 /*********************************************************************
1982 * EDIT_MoveHome
1984 * Home key: move to beginning of line.
1987 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1989 INT e;
1991 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1992 if (es->style & ES_MULTILINE)
1993 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1994 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1995 else
1996 e = 0;
1997 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1998 EDIT_EM_ScrollCaret(wnd, es);
2002 /*********************************************************************
2004 * EDIT_MovePageDown_ML
2006 * Only for multi line controls
2007 * Move the caret one page down, on a column with the nearest
2008 * x coordinate on the screen (might be a different column).
2011 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
2013 INT s = es->selection_start;
2014 INT e = es->selection_end;
2015 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2016 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
2017 INT x = SLOWORD(pos);
2018 INT y = SHIWORD(pos);
2020 e = EDIT_CharFromPos(wnd, es, x,
2021 y + (es->format_rect.bottom - es->format_rect.top),
2022 &after_wrap);
2023 if (!extend)
2024 s = e;
2025 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
2026 EDIT_EM_ScrollCaret(wnd, es);
2030 /*********************************************************************
2032 * EDIT_MovePageUp_ML
2034 * Only for multi line controls
2035 * Move the caret one page up, on a column with the nearest
2036 * x coordinate on the screen (might be a different column).
2039 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
2041 INT s = es->selection_start;
2042 INT e = es->selection_end;
2043 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2044 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
2045 INT x = SLOWORD(pos);
2046 INT y = SHIWORD(pos);
2048 e = EDIT_CharFromPos(wnd, es, x,
2049 y - (es->format_rect.bottom - es->format_rect.top),
2050 &after_wrap);
2051 if (!extend)
2052 s = e;
2053 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
2054 EDIT_EM_ScrollCaret(wnd, es);
2058 /*********************************************************************
2060 * EDIT_MoveUp_ML
2062 * Only for multi line controls
2063 * Move the caret one line up, on a column with the nearest
2064 * x coordinate on the screen (might be a different column).
2067 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
2069 INT s = es->selection_start;
2070 INT e = es->selection_end;
2071 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2072 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
2073 INT x = SLOWORD(pos);
2074 INT y = SHIWORD(pos);
2076 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
2077 if (!extend)
2078 s = e;
2079 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
2080 EDIT_EM_ScrollCaret(wnd, es);
2084 /*********************************************************************
2086 * EDIT_MoveWordBackward
2089 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
2091 INT s = es->selection_start;
2092 INT e = es->selection_end;
2093 INT l;
2094 INT ll;
2095 INT li;
2097 l = EDIT_EM_LineFromChar(es, e);
2098 ll = EDIT_EM_LineLength(es, e);
2099 li = EDIT_EM_LineIndex(es, l);
2100 if (e - li == 0) {
2101 if (l) {
2102 li = EDIT_EM_LineIndex(es, l - 1);
2103 e = li + EDIT_EM_LineLength(es, li);
2105 } else {
2106 e = li + (INT)EDIT_CallWordBreakProc(es,
2107 li, e - li, ll, WB_LEFT);
2109 if (!extend)
2110 s = e;
2111 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
2112 EDIT_EM_ScrollCaret(wnd, es);
2116 /*********************************************************************
2118 * EDIT_MoveWordForward
2121 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
2123 INT s = es->selection_start;
2124 INT e = es->selection_end;
2125 INT l;
2126 INT ll;
2127 INT li;
2129 l = EDIT_EM_LineFromChar(es, e);
2130 ll = EDIT_EM_LineLength(es, e);
2131 li = EDIT_EM_LineIndex(es, l);
2132 if (e - li == ll) {
2133 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2134 e = EDIT_EM_LineIndex(es, l + 1);
2135 } else {
2136 e = li + EDIT_CallWordBreakProc(es,
2137 li, e - li + 1, ll, WB_RIGHT);
2139 if (!extend)
2140 s = e;
2141 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
2142 EDIT_EM_ScrollCaret(wnd, es);
2146 /*********************************************************************
2148 * EDIT_PaintLine
2151 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
2153 INT s = es->selection_start;
2154 INT e = es->selection_end;
2155 INT li;
2156 INT ll;
2157 INT x;
2158 INT y;
2159 LRESULT pos;
2161 if (es->style & ES_MULTILINE) {
2162 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2163 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2164 return;
2165 } else if (line)
2166 return;
2168 TRACE("line=%d\n", line);
2170 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(es, line), FALSE);
2171 x = SLOWORD(pos);
2172 y = SHIWORD(pos);
2173 li = EDIT_EM_LineIndex(es, line);
2174 ll = EDIT_EM_LineLength(es, li);
2175 s = es->selection_start;
2176 e = es->selection_end;
2177 ORDER_INT(s, e);
2178 s = min(li + ll, max(li, s));
2179 e = min(li + ll, max(li, e));
2180 if (rev && (s != e) &&
2181 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2182 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2183 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2184 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2185 } else
2186 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2190 /*********************************************************************
2192 * EDIT_PaintText
2195 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2197 COLORREF BkColor;
2198 COLORREF TextColor;
2199 INT ret;
2200 INT li;
2201 SIZE size;
2203 if (!count)
2204 return 0;
2205 BkColor = GetBkColor(dc);
2206 TextColor = GetTextColor(dc);
2207 if (rev) {
2208 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2209 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2211 li = EDIT_EM_LineIndex(es, line);
2212 if (es->style & ES_MULTILINE) {
2213 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2214 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2215 } else {
2216 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2217 TextOutW(dc, x, y, text + li + col, count);
2218 GetTextExtentPoint32W(dc, text + li + col, count, &size);
2219 ret = size.cx;
2220 if (es->style & ES_PASSWORD)
2221 HeapFree(GetProcessHeap(), 0, text);
2223 if (rev) {
2224 SetBkColor(dc, BkColor);
2225 SetTextColor(dc, TextColor);
2227 return ret;
2231 /*********************************************************************
2233 * EDIT_SetCaretPos
2236 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
2237 BOOL after_wrap)
2239 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
2240 SetCaretPos(SLOWORD(res), SHIWORD(res));
2244 /*********************************************************************
2246 * EDIT_SetRectNP
2248 * note: this is not (exactly) the handler called on EM_SETRECTNP
2249 * it is also used to set the rect of a single line control
2252 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
2254 CopyRect(&es->format_rect, rc);
2255 if (es->style & WS_BORDER) {
2256 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
2257 if(TWEAK_WineLook == WIN31_LOOK)
2258 bw += 2;
2259 es->format_rect.left += bw;
2260 es->format_rect.top += bw;
2261 es->format_rect.right -= bw;
2262 es->format_rect.bottom -= bw;
2264 es->format_rect.left += es->left_margin;
2265 es->format_rect.right -= es->right_margin;
2266 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2267 if (es->style & ES_MULTILINE)
2269 INT fw, vlc, max_x_offset, max_y_offset;
2271 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2272 es->format_rect.bottom = es->format_rect.top + max(1, vlc) * es->line_height;
2274 /* correct es->x_offset */
2275 fw = es->format_rect.right - es->format_rect.left;
2276 max_x_offset = es->text_width - fw;
2277 if(max_x_offset < 0) max_x_offset = 0;
2278 if(es->x_offset > max_x_offset)
2279 es->x_offset = max_x_offset;
2281 /* correct es->y_offset */
2282 max_y_offset = es->line_count - vlc;
2283 if(max_y_offset < 0) max_y_offset = 0;
2284 if(es->y_offset > max_y_offset)
2285 es->y_offset = max_y_offset;
2287 /* force scroll info update */
2288 EDIT_UpdateScrollInfo(wnd, es);
2290 else
2291 /* Windows doesn't care to fix text placement for SL controls */
2292 es->format_rect.bottom = es->format_rect.top + es->line_height;
2294 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2295 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
2299 /*********************************************************************
2301 * EDIT_UnlockBuffer
2304 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
2306 /* Edit window might be already destroyed */
2307 if(!IsWindow(wnd->hwndSelf))
2309 WARN("edit wnd %04x already destroyed\n", wnd->hwndSelf);
2310 return;
2313 if (!es) {
2314 ERR("no EDITSTATE ... please report\n");
2315 return;
2317 if (!es->lock_count) {
2318 ERR("lock_count == 0 ... please report\n");
2319 return;
2321 if (!es->text) {
2322 ERR("es->text == 0 ... please report\n");
2323 return;
2326 if (force || (es->lock_count == 1)) {
2327 if (es->hloc32W) {
2328 CHAR *textA = NULL;
2329 BOOL _16bit = FALSE;
2330 UINT countA = 0;
2331 UINT countW = strlenW(es->text) + 1;
2333 if(es->hloc32A)
2335 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2336 TRACE("Synchronizing with 32-bit ANSI buffer\n");
2337 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2338 countA = LocalSize(es->hloc32A);
2339 if(countA_new > countA)
2341 HLOCAL hloc32A_new;
2342 UINT alloc_size = ROUND_TO_GROW(countA_new);
2343 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2344 hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2345 if(hloc32A_new)
2347 es->hloc32A = hloc32A_new;
2348 countA = LocalSize(hloc32A_new);
2349 TRACE("Real new size %d bytes\n", countA);
2351 else
2352 WARN("FAILED! Will synchronize partially\n");
2354 textA = LocalLock(es->hloc32A);
2356 else if(es->hloc16)
2358 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2359 TRACE("Synchronizing with 16-bit ANSI buffer\n");
2360 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2361 countA = LOCAL_Size(wnd->hInstance, es->hloc16);
2362 if(countA_new > countA)
2364 HLOCAL16 hloc16_new;
2365 UINT alloc_size = ROUND_TO_GROW(countA_new);
2366 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2367 hloc16_new = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2368 if(hloc16_new)
2370 es->hloc16 = hloc16_new;
2371 countA = LOCAL_Size(wnd->hInstance, hloc16_new);
2372 TRACE("Real new size %d bytes\n", countA);
2374 else
2375 WARN("FAILED! Will synchronize partially\n");
2377 textA = LOCAL_Lock(wnd->hInstance, es->hloc16);
2378 _16bit = TRUE;
2381 if(textA)
2383 WideCharToMultiByte(CP_ACP, 0, es->text, countW, textA, countA, NULL, NULL);
2384 if(_16bit)
2385 LOCAL_Unlock(wnd->hInstance, es->hloc16);
2386 else
2387 LocalUnlock(es->hloc32A);
2390 LocalUnlock(es->hloc32W);
2391 es->text = NULL;
2393 else {
2394 ERR("no buffer ... please report\n");
2395 return;
2398 es->lock_count--;
2402 /*********************************************************************
2404 * EDIT_UpdateScrollInfo
2407 static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es)
2409 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2411 SCROLLINFO si;
2412 si.cbSize = sizeof(SCROLLINFO);
2413 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2414 si.nMin = 0;
2415 si.nMax = es->line_count - 1;
2416 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2417 si.nPos = es->y_offset;
2418 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2419 si.nMin, si.nMax, si.nPage, si.nPos);
2420 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
2423 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2425 SCROLLINFO si;
2426 si.cbSize = sizeof(SCROLLINFO);
2427 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2428 si.nMin = 0;
2429 si.nMax = es->text_width - 1;
2430 si.nPage = es->format_rect.right - es->format_rect.left;
2431 si.nPos = es->x_offset;
2432 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2433 si.nMin, si.nMax, si.nPage, si.nPos);
2434 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
2438 /*********************************************************************
2440 * EDIT_WordBreakProc
2442 * Find the beginning of words.
2443 * Note: unlike the specs for a WordBreakProc, this function only
2444 * allows to be called without linebreaks between s[0] upto
2445 * s[count - 1]. Remember it is only called
2446 * internally, so we can decide this for ourselves.
2449 static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action)
2451 INT ret = 0;
2453 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
2455 if(!s) return 0;
2457 switch (action) {
2458 case WB_LEFT:
2459 if (!count)
2460 break;
2461 if (index)
2462 index--;
2463 if (s[index] == ' ') {
2464 while (index && (s[index] == ' '))
2465 index--;
2466 if (index) {
2467 while (index && (s[index] != ' '))
2468 index--;
2469 if (s[index] == ' ')
2470 index++;
2472 } else {
2473 while (index && (s[index] != ' '))
2474 index--;
2475 if (s[index] == ' ')
2476 index++;
2478 ret = index;
2479 break;
2480 case WB_RIGHT:
2481 if (!count)
2482 break;
2483 if (index)
2484 index--;
2485 if (s[index] == ' ')
2486 while ((index < count) && (s[index] == ' ')) index++;
2487 else {
2488 while (s[index] && (s[index] != ' ') && (index < count))
2489 index++;
2490 while ((s[index] == ' ') && (index < count)) index++;
2492 ret = index;
2493 break;
2494 case WB_ISDELIMITER:
2495 ret = (s[index] == ' ');
2496 break;
2497 default:
2498 ERR("unknown action code, please report !\n");
2499 break;
2501 return ret;
2505 /*********************************************************************
2507 * EM_CHARFROMPOS
2509 * returns line number (not index) in high-order word of result.
2510 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2511 * to Richedit, not to the edit control. Original documentation is valid.
2512 * FIXME: do the specs mean to return -1 if outside client area or
2513 * if outside formatting rectangle ???
2516 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
2518 POINT pt;
2519 RECT rc;
2520 INT index;
2522 pt.x = x;
2523 pt.y = y;
2524 GetClientRect(wnd->hwndSelf, &rc);
2525 if (!PtInRect(&rc, pt))
2526 return -1;
2528 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
2529 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2533 /*********************************************************************
2535 * EM_FMTLINES
2537 * Enable or disable soft breaks.
2539 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2541 es->flags &= ~EF_USE_SOFTBRK;
2542 if (add_eol) {
2543 es->flags |= EF_USE_SOFTBRK;
2544 FIXME("soft break enabled, not implemented\n");
2546 return add_eol;
2550 /*********************************************************************
2552 * EM_GETHANDLE
2554 * Hopefully this won't fire back at us.
2555 * We always start with a fixed buffer in the local heap.
2556 * Despite of the documentation says that the local heap is used
2557 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2558 * buffer on the local heap.
2561 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2563 HLOCAL hLocal;
2565 if (!(es->style & ES_MULTILINE))
2566 return 0;
2568 if(es->is_unicode)
2569 hLocal = es->hloc32W;
2570 else
2572 if(!es->hloc32A)
2574 CHAR *textA;
2575 UINT countA, alloc_size;
2576 TRACE("Allocating 32-bit ANSI alias buffer\n");
2577 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2578 alloc_size = ROUND_TO_GROW(countA);
2579 if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2581 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2582 return 0;
2584 textA = LocalLock(es->hloc32A);
2585 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2586 LocalUnlock(es->hloc32A);
2588 hLocal = es->hloc32A;
2591 TRACE("Returning %04X, LocalSize() = %d\n", hLocal, LocalSize(hLocal));
2592 return hLocal;
2596 /*********************************************************************
2598 * EM_GETHANDLE16
2600 * Hopefully this won't fire back at us.
2601 * We always start with a buffer in 32 bit linear memory.
2602 * However, with this message a 16 bit application requests
2603 * a handle of 16 bit local heap memory, where it expects to find
2604 * the text.
2605 * It's a pitty that from this moment on we have to use this
2606 * local heap, because applications may rely on the handle
2607 * in the future.
2609 * In this function we'll try to switch to local heap.
2611 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2613 CHAR *textA;
2614 UINT countA, alloc_size;
2616 if (!(es->style & ES_MULTILINE))
2617 return 0;
2619 if (es->hloc16)
2620 return es->hloc16;
2622 if (!LOCAL_HeapSize(wnd->hInstance)) {
2623 if (!LocalInit16(wnd->hInstance, 0,
2624 GlobalSize16(wnd->hInstance))) {
2625 ERR("could not initialize local heap\n");
2626 return 0;
2628 TRACE("local heap initialized\n");
2631 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2632 alloc_size = ROUND_TO_GROW(countA);
2634 TRACE("Allocating 16-bit ANSI alias buffer\n");
2635 if (!(es->hloc16 = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) {
2636 ERR("could not allocate new 16 bit buffer\n");
2637 return 0;
2640 if (!(textA = (LPSTR)LOCAL_Lock(wnd->hInstance, es->hloc16))) {
2641 ERR("could not lock new 16 bit buffer\n");
2642 LOCAL_Free(wnd->hInstance, es->hloc16);
2643 es->hloc16 = 0;
2644 return 0;
2647 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2648 LOCAL_Unlock(wnd->hInstance, es->hloc16);
2650 TRACE("Returning %04X, LocalSize() = %d\n", es->hloc16, LOCAL_Size(wnd->hInstance, es->hloc16));
2651 return es->hloc16;
2655 /*********************************************************************
2657 * EM_GETLINE
2660 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode)
2662 LPWSTR src;
2663 INT line_len, dst_len;
2664 INT i;
2666 if (es->style & ES_MULTILINE) {
2667 if (line >= es->line_count)
2668 return 0;
2669 } else
2670 line = 0;
2671 i = EDIT_EM_LineIndex(es, line);
2672 src = es->text + i;
2673 line_len = EDIT_EM_LineLength(es, i);
2674 dst_len = *(WORD *)lParam;
2675 if(unicode)
2677 LPWSTR dst = (LPWSTR)lParam;
2678 if(dst_len <= line_len)
2680 memcpy(dst, src, dst_len * sizeof(WCHAR));
2681 return dst_len;
2683 else /* Append 0 if enough space */
2685 memcpy(dst, src, line_len * sizeof(WCHAR));
2686 dst[line_len] = 0;
2687 return line_len;
2690 else
2692 LPSTR dst = (LPSTR)lParam;
2693 INT ret;
2694 ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, dst, dst_len, NULL, NULL);
2695 if(!ret) /* Insufficient buffer size */
2696 return dst_len;
2697 if(ret < dst_len) /* Append 0 if enough space */
2698 dst[ret] = 0;
2699 return ret;
2704 /*********************************************************************
2706 * EM_GETSEL
2709 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end)
2711 UINT s = es->selection_start;
2712 UINT e = es->selection_end;
2714 ORDER_UINT(s, e);
2715 if (start)
2716 *start = s;
2717 if (end)
2718 *end = e;
2719 return MAKELONG(s, e);
2723 /*********************************************************************
2725 * EM_GETTHUMB
2727 * FIXME: is this right ? (or should it be only VSCROLL)
2728 * (and maybe only for edit controls that really have their
2729 * own scrollbars) (and maybe only for multiline controls ?)
2730 * All in all: very poorly documented
2733 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2735 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0),
2736 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0));
2740 /*********************************************************************
2742 * EM_LINEFROMCHAR
2745 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
2747 INT line;
2748 LINEDEF *line_def;
2750 if (!(es->style & ES_MULTILINE))
2751 return 0;
2752 if (index > (INT)strlenW(es->text))
2753 return es->line_count - 1;
2754 if (index == -1)
2755 index = min(es->selection_start, es->selection_end);
2757 line = 0;
2758 line_def = es->first_line_def;
2759 index -= line_def->length;
2760 while ((index >= 0) && line_def->next) {
2761 line++;
2762 line_def = line_def->next;
2763 index -= line_def->length;
2765 return line;
2769 /*********************************************************************
2771 * EM_LINEINDEX
2774 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line)
2776 INT line_index;
2777 LINEDEF *line_def;
2779 if (!(es->style & ES_MULTILINE))
2780 return 0;
2781 if (line >= es->line_count)
2782 return -1;
2784 line_index = 0;
2785 line_def = es->first_line_def;
2786 if (line == -1) {
2787 INT index = es->selection_end - line_def->length;
2788 while ((index >= 0) && line_def->next) {
2789 line_index += line_def->length;
2790 line_def = line_def->next;
2791 index -= line_def->length;
2793 } else {
2794 while (line > 0) {
2795 line_index += line_def->length;
2796 line_def = line_def->next;
2797 line--;
2800 return line_index;
2804 /*********************************************************************
2806 * EM_LINELENGTH
2809 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
2811 LINEDEF *line_def;
2813 if (!(es->style & ES_MULTILINE))
2814 return strlenW(es->text);
2816 if (index == -1) {
2817 /* get the number of remaining non-selected chars of selected lines */
2818 INT32 li;
2819 INT32 count;
2820 li = EDIT_EM_LineFromChar(es, es->selection_start);
2821 /* # chars before start of selection area */
2822 count = es->selection_start - EDIT_EM_LineIndex(es, li);
2823 li = EDIT_EM_LineFromChar(es, es->selection_end);
2824 /* # chars after end of selection */
2825 count += EDIT_EM_LineIndex(es, li) +
2826 EDIT_EM_LineLength(es, li) - es->selection_end;
2827 return count;
2829 line_def = es->first_line_def;
2830 index -= line_def->length;
2831 while ((index >= 0) && line_def->next) {
2832 line_def = line_def->next;
2833 index -= line_def->length;
2835 return line_def->net_length;
2839 /*********************************************************************
2841 * EM_LINESCROLL
2843 * NOTE: dx is in average character widths, dy - in lines;
2846 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2848 if (!(es->style & ES_MULTILINE))
2849 return FALSE;
2851 dx *= es->char_width;
2852 return EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
2855 /*********************************************************************
2857 * EDIT_EM_LineScroll_internal
2859 * Version of EDIT_EM_LineScroll for internal use.
2860 * It doesn't refuse if ES_MULTILINE is set and assumes that
2861 * dx is in pixels, dy - in lines.
2864 static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2866 INT nyoff;
2867 INT x_offset_in_pixels;
2869 if (es->style & ES_MULTILINE)
2871 x_offset_in_pixels = es->x_offset;
2873 else
2875 dy = 0;
2876 x_offset_in_pixels = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->x_offset, FALSE));
2879 if (-dx > x_offset_in_pixels)
2880 dx = -x_offset_in_pixels;
2881 if (dx > es->text_width - x_offset_in_pixels)
2882 dx = es->text_width - x_offset_in_pixels;
2883 nyoff = max(0, es->y_offset + dy);
2884 if (nyoff >= es->line_count)
2885 nyoff = es->line_count - 1;
2886 dy = (es->y_offset - nyoff) * es->line_height;
2887 if (dx || dy) {
2888 RECT rc1;
2889 RECT rc;
2891 es->y_offset = nyoff;
2892 if(es->style & ES_MULTILINE)
2893 es->x_offset += dx;
2894 else
2895 es->x_offset += dx / es->char_width;
2897 GetClientRect(wnd->hwndSelf, &rc1);
2898 IntersectRect(&rc, &rc1, &es->format_rect);
2899 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2900 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2901 /* force scroll info update */
2902 EDIT_UpdateScrollInfo(wnd, es);
2904 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2905 EDIT_NOTIFY_PARENT(es, EN_HSCROLL, "EN_HSCROLL");
2906 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2907 EDIT_NOTIFY_PARENT(es, EN_VSCROLL, "EN_VSCROLL");
2908 return TRUE;
2912 /*********************************************************************
2914 * EM_POSFROMCHAR
2917 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2919 INT len = strlenW(es->text);
2920 INT l;
2921 INT li;
2922 INT x;
2923 INT y = 0;
2924 HDC dc;
2925 HFONT old_font = 0;
2926 SIZE size;
2928 index = min(index, len);
2929 dc = GetDC(wnd->hwndSelf);
2930 if (es->font)
2931 old_font = SelectObject(dc, es->font);
2932 if (es->style & ES_MULTILINE) {
2933 l = EDIT_EM_LineFromChar(es, index);
2934 y = (l - es->y_offset) * es->line_height;
2935 li = EDIT_EM_LineIndex(es, l);
2936 if (after_wrap && (li == index) && l) {
2937 INT l2 = l - 1;
2938 LINEDEF *line_def = es->first_line_def;
2939 while (l2) {
2940 line_def = line_def->next;
2941 l2--;
2943 if (line_def->ending == END_WRAP) {
2944 l--;
2945 y -= es->line_height;
2946 li = EDIT_EM_LineIndex(es, l);
2949 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
2950 es->tabs_count, es->tabs)) - es->x_offset;
2951 } else {
2952 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2953 if (index < es->x_offset) {
2954 GetTextExtentPoint32W(dc, text + index,
2955 es->x_offset - index, &size);
2956 x = -size.cx;
2957 } else {
2958 GetTextExtentPoint32W(dc, text + es->x_offset,
2959 index - es->x_offset, &size);
2960 x = size.cx;
2962 y = 0;
2963 if (es->style & ES_PASSWORD)
2964 HeapFree(GetProcessHeap(), 0, text);
2966 x += es->format_rect.left;
2967 y += es->format_rect.top;
2968 if (es->font)
2969 SelectObject(dc, old_font);
2970 ReleaseDC(wnd->hwndSelf, dc);
2971 return MAKELONG((INT16)x, (INT16)y);
2975 /*********************************************************************
2977 * EM_REPLACESEL
2979 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2982 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update)
2984 UINT strl = strlenW(lpsz_replace);
2985 UINT tl = strlenW(es->text);
2986 UINT utl;
2987 UINT s;
2988 UINT e;
2989 UINT i;
2990 LPWSTR p;
2991 HRGN hrgn = 0;
2993 TRACE("%s, can_undo %d, send_update %d\n",
2994 debugstr_w(lpsz_replace), can_undo, send_update);
2996 s = es->selection_start;
2997 e = es->selection_end;
2999 if ((s == e) && !strl)
3000 return;
3002 ORDER_UINT(s, e);
3004 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
3005 return;
3007 if (e != s) {
3008 /* there is something to be deleted */
3009 if (can_undo) {
3010 utl = strlenW(es->undo_text);
3011 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
3012 /* undo-buffer is extended to the right */
3013 EDIT_MakeUndoFit(es, utl + e - s);
3014 strncpyW(es->undo_text + utl, es->text + s, e - s + 1);
3015 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
3016 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
3017 /* undo-buffer is extended to the left */
3018 EDIT_MakeUndoFit(es, utl + e - s);
3019 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
3020 p[e - s] = p[0];
3021 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
3022 p[i] = (es->text + s)[i];
3023 es->undo_position = s;
3024 } else {
3025 /* new undo-buffer */
3026 EDIT_MakeUndoFit(es, e - s);
3027 strncpyW(es->undo_text, es->text + s, e - s + 1);
3028 es->undo_text[e - s] = 0; /* ensure 0 termination */
3029 es->undo_position = s;
3031 /* any deletion makes the old insertion-undo invalid */
3032 es->undo_insert_count = 0;
3033 } else
3034 EDIT_EM_EmptyUndoBuffer(es);
3036 /* now delete */
3037 strcpyW(es->text + s, es->text + e);
3039 if (strl) {
3040 /* there is an insertion */
3041 if (can_undo) {
3042 if ((s == es->undo_position) ||
3043 ((es->undo_insert_count) &&
3044 (s == es->undo_position + es->undo_insert_count)))
3046 * insertion is new and at delete position or
3047 * an extension to either left or right
3049 es->undo_insert_count += strl;
3050 else {
3051 /* new insertion undo */
3052 es->undo_position = s;
3053 es->undo_insert_count = strl;
3054 /* new insertion makes old delete-buffer invalid */
3055 *es->undo_text = '\0';
3057 } else
3058 EDIT_EM_EmptyUndoBuffer(es);
3060 /* now insert */
3061 tl = strlenW(es->text);
3062 for (p = es->text + tl ; p >= es->text + s ; p--)
3063 p[strl] = p[0];
3064 for (i = 0 , p = es->text + s ; i < strl ; i++)
3065 p[i] = lpsz_replace[i];
3066 if(es->style & ES_UPPERCASE)
3067 CharUpperBuffW(p, strl);
3068 else if(es->style & ES_LOWERCASE)
3069 CharLowerBuffW(p, strl);
3070 s += strl;
3072 if (es->style & ES_MULTILINE)
3074 INT s = min(es->selection_start, es->selection_end);
3076 hrgn = CreateRectRgn(0, 0, 0, 0);
3077 EDIT_BuildLineDefs_ML(wnd, es, s, s + strl,
3078 strl - (es->selection_end - es->selection_start), hrgn);
3080 else
3081 EDIT_CalcLineWidth_SL(wnd, es);
3083 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
3084 es->flags |= EF_MODIFIED;
3085 if (send_update) es->flags |= EF_UPDATE;
3086 EDIT_EM_ScrollCaret(wnd, es);
3088 /* force scroll info update */
3089 EDIT_UpdateScrollInfo(wnd, es);
3091 if (hrgn)
3093 EDIT_UpdateTextRegion(wnd, hrgn, TRUE);
3094 DeleteObject(hrgn);
3096 else
3097 EDIT_UpdateText(wnd, NULL, TRUE);
3099 if(es->flags & EF_UPDATE)
3101 es->flags &= ~EF_UPDATE;
3102 EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE");
3107 /*********************************************************************
3109 * EM_SCROLL
3112 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
3114 INT dy;
3116 if (!(es->style & ES_MULTILINE))
3117 return (LRESULT)FALSE;
3119 dy = 0;
3121 switch (action) {
3122 case SB_LINEUP:
3123 if (es->y_offset)
3124 dy = -1;
3125 break;
3126 case SB_LINEDOWN:
3127 if (es->y_offset < es->line_count - 1)
3128 dy = 1;
3129 break;
3130 case SB_PAGEUP:
3131 if (es->y_offset)
3132 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
3133 break;
3134 case SB_PAGEDOWN:
3135 if (es->y_offset < es->line_count - 1)
3136 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3137 break;
3138 default:
3139 return (LRESULT)FALSE;
3141 if (dy) {
3142 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3143 /* check if we are going to move too far */
3144 if(es->y_offset + dy > es->line_count - vlc)
3145 dy = es->line_count - vlc - es->y_offset;
3147 /* Notification is done in EDIT_EM_LineScroll */
3148 if(dy)
3149 EDIT_EM_LineScroll(wnd, es, 0, dy);
3151 return MAKELONG((INT16)dy, (BOOL16)TRUE);
3155 /*********************************************************************
3157 * EM_SCROLLCARET
3160 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
3162 if (es->style & ES_MULTILINE) {
3163 INT l;
3164 INT li;
3165 INT vlc;
3166 INT ww;
3167 INT cw = es->char_width;
3168 INT x;
3169 INT dy = 0;
3170 INT dx = 0;
3172 l = EDIT_EM_LineFromChar(es, es->selection_end);
3173 li = EDIT_EM_LineIndex(es, l);
3174 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
3175 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3176 if (l >= es->y_offset + vlc)
3177 dy = l - vlc + 1 - es->y_offset;
3178 if (l < es->y_offset)
3179 dy = l - es->y_offset;
3180 ww = es->format_rect.right - es->format_rect.left;
3181 if (x < es->format_rect.left)
3182 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
3183 if (x > es->format_rect.right)
3184 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
3185 if (dy || dx)
3187 /* check if we are going to move too far */
3188 if(es->x_offset + dx + ww > es->text_width)
3189 dx = es->text_width - ww - es->x_offset;
3190 if(dx || dy)
3191 EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
3193 } else {
3194 INT x;
3195 INT goal;
3196 INT format_width;
3198 if (!(es->style & ES_AUTOHSCROLL))
3199 return;
3201 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3202 format_width = es->format_rect.right - es->format_rect.left;
3203 if (x < es->format_rect.left) {
3204 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
3205 do {
3206 es->x_offset--;
3207 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3208 } while ((x < goal) && es->x_offset);
3209 /* FIXME: use ScrollWindow() somehow to improve performance */
3210 EDIT_UpdateText(wnd, NULL, TRUE);
3211 } else if (x > es->format_rect.right) {
3212 INT x_last;
3213 INT len = strlenW(es->text);
3214 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
3215 do {
3216 es->x_offset++;
3217 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3218 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
3219 } while ((x > goal) && (x_last > es->format_rect.right));
3220 /* FIXME: use ScrollWindow() somehow to improve performance */
3221 EDIT_UpdateText(wnd, NULL, TRUE);
3225 if(es->flags & EF_FOCUSED)
3226 EDIT_SetCaretPos(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3230 /*********************************************************************
3232 * EM_SETHANDLE
3234 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3237 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
3239 if (!(es->style & ES_MULTILINE))
3240 return;
3242 if (!hloc) {
3243 WARN("called with NULL handle\n");
3244 return;
3247 EDIT_UnlockBuffer(wnd, es, TRUE);
3249 if(es->hloc16)
3251 LOCAL_Free(wnd->hInstance, es->hloc16);
3252 es->hloc16 = (HLOCAL16)NULL;
3255 if(es->is_unicode)
3257 if(es->hloc32A)
3259 LocalFree(es->hloc32A);
3260 es->hloc32A = (HLOCAL)NULL;
3262 es->hloc32W = hloc;
3264 else
3266 INT countW, countA;
3267 HLOCAL hloc32W_new;
3268 WCHAR *textW;
3269 CHAR *textA;
3271 countA = LocalSize(hloc);
3272 textA = LocalLock(hloc);
3273 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3274 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3276 ERR("Could not allocate new unicode buffer\n");
3277 return;
3279 textW = LocalLock(hloc32W_new);
3280 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3281 LocalUnlock(hloc32W_new);
3282 LocalUnlock(hloc);
3284 if(es->hloc32W)
3285 LocalFree(es->hloc32W);
3287 es->hloc32W = hloc32W_new;
3288 es->hloc32A = hloc;
3291 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3293 EDIT_LockBuffer(wnd, es);
3295 es->x_offset = es->y_offset = 0;
3296 es->selection_start = es->selection_end = 0;
3297 EDIT_EM_EmptyUndoBuffer(es);
3298 es->flags &= ~EF_MODIFIED;
3299 es->flags &= ~EF_UPDATE;
3300 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3301 EDIT_UpdateText(wnd, NULL, TRUE);
3302 EDIT_EM_ScrollCaret(wnd, es);
3303 /* force scroll info update */
3304 EDIT_UpdateScrollInfo(wnd, es);
3308 /*********************************************************************
3310 * EM_SETHANDLE16
3312 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3315 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
3317 INT countW, countA;
3318 HLOCAL hloc32W_new;
3319 WCHAR *textW;
3320 CHAR *textA;
3322 if (!(es->style & ES_MULTILINE))
3323 return;
3325 if (!hloc) {
3326 WARN("called with NULL handle\n");
3327 return;
3330 EDIT_UnlockBuffer(wnd, es, TRUE);
3332 if(es->hloc32A)
3334 LocalFree(es->hloc32A);
3335 es->hloc32A = (HLOCAL)NULL;
3338 countA = LOCAL_Size(wnd->hInstance, hloc);
3339 textA = LOCAL_Lock(wnd->hInstance, hloc);
3340 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3341 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3343 ERR("Could not allocate new unicode buffer\n");
3344 return;
3346 textW = LocalLock(hloc32W_new);
3347 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3348 LocalUnlock(hloc32W_new);
3349 LOCAL_Unlock(wnd->hInstance, hloc);
3351 if(es->hloc32W)
3352 LocalFree(es->hloc32W);
3354 es->hloc32W = hloc32W_new;
3355 es->hloc16 = hloc;
3357 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3359 EDIT_LockBuffer(wnd, es);
3361 es->x_offset = es->y_offset = 0;
3362 es->selection_start = es->selection_end = 0;
3363 EDIT_EM_EmptyUndoBuffer(es);
3364 es->flags &= ~EF_MODIFIED;
3365 es->flags &= ~EF_UPDATE;
3366 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3367 EDIT_UpdateText(wnd, NULL, TRUE);
3368 EDIT_EM_ScrollCaret(wnd, es);
3369 /* force scroll info update */
3370 EDIT_UpdateScrollInfo(wnd, es);
3374 /*********************************************************************
3376 * EM_SETLIMITTEXT
3378 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3379 * However, the windows version is not complied to yet in all of edit.c
3382 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit)
3384 if (es->style & ES_MULTILINE) {
3385 if (limit)
3386 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
3387 else
3388 es->buffer_limit = BUFLIMIT_MULTI;
3389 } else {
3390 if (limit)
3391 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
3392 else
3393 es->buffer_limit = BUFLIMIT_SINGLE;
3398 /*********************************************************************
3400 * EM_SETMARGINS
3402 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3403 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3404 * of the char's width, according to the new docs.
3407 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
3408 INT left, INT right)
3410 if (action & EC_LEFTMARGIN) {
3411 if (left != EC_USEFONTINFO)
3412 es->left_margin = left;
3413 else
3414 es->left_margin = es->char_width / 3;
3417 if (action & EC_RIGHTMARGIN) {
3418 if (right != EC_USEFONTINFO)
3419 es->right_margin = right;
3420 else
3421 es->right_margin = es->char_width / 3;
3423 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3427 /*********************************************************************
3429 * EM_SETPASSWORDCHAR
3432 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, WCHAR c)
3434 if (es->style & ES_MULTILINE)
3435 return;
3437 if (es->password_char == c)
3438 return;
3440 es->password_char = c;
3441 if (c) {
3442 wnd->dwStyle |= ES_PASSWORD;
3443 es->style |= ES_PASSWORD;
3444 } else {
3445 wnd->dwStyle &= ~ES_PASSWORD;
3446 es->style &= ~ES_PASSWORD;
3448 EDIT_UpdateText(wnd, NULL, TRUE);
3452 /*********************************************************************
3454 * EDIT_EM_SetSel
3456 * note: unlike the specs say: the order of start and end
3457 * _is_ preserved in Windows. (i.e. start can be > end)
3458 * In other words: this handler is OK
3461 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
3463 UINT old_start = es->selection_start;
3464 UINT old_end = es->selection_end;
3465 UINT len = strlenW(es->text);
3467 if (start == (UINT)-1) {
3468 start = es->selection_end;
3469 end = es->selection_end;
3470 } else {
3471 start = min(start, len);
3472 end = min(end, len);
3474 es->selection_start = start;
3475 es->selection_end = end;
3476 if (after_wrap)
3477 es->flags |= EF_AFTER_WRAP;
3478 else
3479 es->flags &= ~EF_AFTER_WRAP;
3480 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3481 ORDER_UINT(start, end);
3482 ORDER_UINT(end, old_end);
3483 ORDER_UINT(start, old_start);
3484 ORDER_UINT(old_start, old_end);
3485 if (end != old_start)
3488 * One can also do
3489 * ORDER_UINT32(end, old_start);
3490 * EDIT_InvalidateText(wnd, es, start, end);
3491 * EDIT_InvalidateText(wnd, es, old_start, old_end);
3492 * in place of the following if statement.
3494 if (old_start > end )
3496 EDIT_InvalidateText(wnd, es, start, end);
3497 EDIT_InvalidateText(wnd, es, old_start, old_end);
3499 else
3501 EDIT_InvalidateText(wnd, es, start, old_start);
3502 EDIT_InvalidateText(wnd, es, end, old_end);
3505 else EDIT_InvalidateText(wnd, es, start, old_end);
3509 /*********************************************************************
3511 * EM_SETTABSTOPS
3514 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs)
3516 if (!(es->style & ES_MULTILINE))
3517 return FALSE;
3518 if (es->tabs)
3519 HeapFree(GetProcessHeap(), 0, es->tabs);
3520 es->tabs_count = count;
3521 if (!count)
3522 es->tabs = NULL;
3523 else {
3524 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3525 memcpy(es->tabs, tabs, count * sizeof(INT));
3527 return TRUE;
3531 /*********************************************************************
3533 * EM_SETTABSTOPS16
3536 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs)
3538 if (!(es->style & ES_MULTILINE))
3539 return FALSE;
3540 if (es->tabs)
3541 HeapFree(GetProcessHeap(), 0, es->tabs);
3542 es->tabs_count = count;
3543 if (!count)
3544 es->tabs = NULL;
3545 else {
3546 INT i;
3547 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3548 for (i = 0 ; i < count ; i++)
3549 es->tabs[i] = *tabs++;
3551 return TRUE;
3555 /*********************************************************************
3557 * EM_SETWORDBREAKPROC
3560 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, LPARAM lParam)
3562 if (es->word_break_proc == (void *)lParam)
3563 return;
3565 es->word_break_proc = (void *)lParam;
3566 es->word_break_proc16 = NULL;
3568 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3569 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3570 EDIT_UpdateText(wnd, NULL, TRUE);
3575 /*********************************************************************
3577 * EM_SETWORDBREAKPROC16
3580 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
3582 if (es->word_break_proc16 == wbp)
3583 return;
3585 es->word_break_proc = NULL;
3586 es->word_break_proc16 = wbp;
3587 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3588 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3589 EDIT_UpdateText(wnd, NULL, TRUE);
3594 /*********************************************************************
3596 * EM_UNDO / WM_UNDO
3599 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
3601 INT ulength;
3602 LPWSTR utext;
3604 /* Protect read-only edit control from modification */
3605 if(es->style & ES_READONLY)
3606 return FALSE;
3608 ulength = strlenW(es->undo_text);
3609 utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3611 strcpyW(utext, es->undo_text);
3613 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3614 es->undo_insert_count, debugstr_w(utext));
3616 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3617 EDIT_EM_EmptyUndoBuffer(es);
3618 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, FALSE);
3619 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3620 /* send the notification after the selection start and end are set */
3621 EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE");
3622 EDIT_EM_ScrollCaret(wnd, es);
3623 HeapFree(GetProcessHeap(), 0, utext);
3625 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3626 es->undo_insert_count, debugstr_w(es->undo_text));
3627 return TRUE;
3631 /*********************************************************************
3633 * WM_CHAR
3636 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, WCHAR c)
3638 BOOL control;
3640 /* Protect read-only edit control from modification */
3641 if(es->style & ES_READONLY)
3642 return;
3644 control = GetKeyState(VK_CONTROL) & 0x8000;
3646 switch (c) {
3647 case '\r':
3648 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3649 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3650 break;
3651 case '\n':
3652 if (es->style & ES_MULTILINE) {
3653 if (es->style & ES_READONLY) {
3654 EDIT_MoveHome(wnd, es, FALSE);
3655 EDIT_MoveDown_ML(wnd, es, FALSE);
3656 } else {
3657 static const WCHAR cr_lfW[] = {'\r','\n',0};
3658 EDIT_EM_ReplaceSel(wnd, es, TRUE, cr_lfW, TRUE);
3661 break;
3662 case '\t':
3663 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3665 static const WCHAR tabW[] = {'\t',0};
3666 EDIT_EM_ReplaceSel(wnd, es, TRUE, tabW, TRUE);
3668 break;
3669 case VK_BACK:
3670 if (!(es->style & ES_READONLY) && !control) {
3671 if (es->selection_start != es->selection_end)
3672 EDIT_WM_Clear(wnd, es);
3673 else {
3674 /* delete character left of caret */
3675 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3676 EDIT_MoveBackward(wnd, es, TRUE);
3677 EDIT_WM_Clear(wnd, es);
3680 break;
3681 case 0x03: /* ^C */
3682 SendMessageW(wnd->hwndSelf, WM_COPY, 0, 0);
3683 break;
3684 case 0x16: /* ^V */
3685 SendMessageW(wnd->hwndSelf, WM_PASTE, 0, 0);
3686 break;
3687 case 0x18: /* ^X */
3688 SendMessageW(wnd->hwndSelf, WM_CUT, 0, 0);
3689 break;
3691 default:
3692 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3693 WCHAR str[2];
3694 str[0] = c;
3695 str[1] = '\0';
3696 EDIT_EM_ReplaceSel(wnd, es, TRUE, str, TRUE);
3698 break;
3703 /*********************************************************************
3705 * WM_COMMAND
3708 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
3710 if (code || control)
3711 return;
3713 switch (id) {
3714 case EM_UNDO:
3715 EDIT_EM_Undo(wnd, es);
3716 break;
3717 case WM_CUT:
3718 EDIT_WM_Cut(wnd, es);
3719 break;
3720 case WM_COPY:
3721 EDIT_WM_Copy(wnd, es);
3722 break;
3723 case WM_PASTE:
3724 EDIT_WM_Paste(wnd, es);
3725 break;
3726 case WM_CLEAR:
3727 EDIT_WM_Clear(wnd, es);
3728 break;
3729 case EM_SETSEL:
3730 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
3731 EDIT_EM_ScrollCaret(wnd, es);
3732 break;
3733 default:
3734 ERR("unknown menu item, please report\n");
3735 break;
3740 /*********************************************************************
3742 * WM_CONTEXTMENU
3744 * Note: the resource files resource/sysres_??.rc cannot define a
3745 * single popup menu. Hence we use a (dummy) menubar
3746 * containing the single popup menu as its first item.
3748 * FIXME: the message identifiers have been chosen arbitrarily,
3749 * hence we use MF_BYPOSITION.
3750 * We might as well use the "real" values (anybody knows ?)
3751 * The menu definition is in resources/sysres_??.rc.
3752 * Once these are OK, we better use MF_BYCOMMAND here
3753 * (as we do in EDIT_WM_Command()).
3756 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y)
3758 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3759 HMENU popup = GetSubMenu(menu, 0);
3760 UINT start = es->selection_start;
3761 UINT end = es->selection_end;
3763 ORDER_UINT(start, end);
3765 /* undo */
3766 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3767 /* cut */
3768 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3769 /* copy */
3770 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3771 /* paste */
3772 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3773 /* delete */
3774 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3775 /* select all */
3776 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlenW(es->text)) ? MF_ENABLED : MF_GRAYED));
3778 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3779 DestroyMenu(menu);
3783 /*********************************************************************
3785 * WM_COPY
3788 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3790 INT s = es->selection_start;
3791 INT e = es->selection_end;
3792 HGLOBAL hdst;
3793 LPWSTR dst;
3795 if (e == s)
3796 return;
3797 ORDER_INT(s, e);
3798 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (DWORD)(e - s + 1) * sizeof(WCHAR));
3799 dst = GlobalLock(hdst);
3800 strncpyW(dst, es->text + s, e - s);
3801 dst[e - s] = 0; /* ensure 0 termination */
3802 TRACE("%s\n", debugstr_w(dst));
3803 GlobalUnlock(hdst);
3804 OpenClipboard(wnd->hwndSelf);
3805 EmptyClipboard();
3806 SetClipboardData(CF_UNICODETEXT, hdst);
3807 CloseClipboard();
3811 /*********************************************************************
3813 * WM_CREATE
3816 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCWSTR name)
3818 TRACE("%s\n", debugstr_w(name));
3820 * To initialize some final structure members, we call some helper
3821 * functions. However, since the EDITSTATE is not consistent (i.e.
3822 * not fully initialized), we should be very careful which
3823 * functions can be called, and in what order.
3825 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3826 EDIT_EM_EmptyUndoBuffer(es);
3828 if (name && *name) {
3829 EDIT_EM_ReplaceSel(wnd, es, FALSE, name, FALSE);
3830 /* if we insert text to the editline, the text scrolls out
3831 * of the window, as the caret is placed after the insert
3832 * pos normally; thus we reset es->selection... to 0 and
3833 * update caret
3835 es->selection_start = es->selection_end = 0;
3836 /* send the notification after the selection start and end are set */
3837 EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE");
3838 EDIT_EM_ScrollCaret(wnd, es);
3840 /* force scroll info update */
3841 EDIT_UpdateScrollInfo(wnd, es);
3842 return 0;
3846 /*********************************************************************
3848 * WM_DESTROY
3851 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3853 LINEDEF *pc, *pp;
3855 if (es->hloc32W) {
3856 while (LocalUnlock(es->hloc32W)) ;
3857 LocalFree(es->hloc32W);
3859 if (es->hloc32A) {
3860 while (LocalUnlock(es->hloc32A)) ;
3861 LocalFree(es->hloc32A);
3863 if (es->hloc16) {
3864 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3865 LOCAL_Free(wnd->hInstance, es->hloc16);
3868 pc = es->first_line_def;
3869 while (pc)
3871 pp = pc->next;
3872 HeapFree(GetProcessHeap(), 0, pc);
3873 pc = pp;
3876 HeapFree(GetProcessHeap(), 0, es);
3877 *(EDITSTATE **)wnd->wExtra = NULL;
3881 /*********************************************************************
3883 * WM_ERASEBKGND
3886 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3888 HBRUSH brush;
3889 RECT rc;
3891 if ( get_app_version() >= 0x40000 &&(
3892 !es->bEnableState || (es->style & ES_READONLY)))
3893 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3894 else
3895 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3897 if (!brush)
3898 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3900 GetClientRect(wnd->hwndSelf, &rc);
3901 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3902 GetClipBox(dc, &rc);
3904 * FIXME: specs say that we should UnrealizeObject() the brush,
3905 * but the specs of UnrealizeObject() say that we shouldn't
3906 * unrealize a stock object. The default brush that
3907 * DefWndProc() returns is ... a stock object.
3909 FillRect(dc, &rc, brush);
3910 return -1;
3914 /*********************************************************************
3916 * WM_GETTEXT
3919 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode)
3921 if(!count) return 0;
3923 if(unicode)
3925 LPWSTR textW = (LPWSTR)lParam;
3926 strncpyW(textW, es->text, count);
3927 textW[count - 1] = 0; /* ensure 0 termination */
3928 return strlenW(textW);
3930 else
3932 LPSTR textA = (LPSTR)lParam;
3933 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL);
3934 textA[count - 1] = 0; /* ensure 0 termination */
3935 return strlen(textA);
3939 /*********************************************************************
3941 * WM_HSCROLL
3944 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
3946 INT dx;
3947 INT fw;
3949 if (!(es->style & ES_MULTILINE))
3950 return 0;
3952 if (!(es->style & ES_AUTOHSCROLL))
3953 return 0;
3955 dx = 0;
3956 fw = es->format_rect.right - es->format_rect.left;
3957 switch (action) {
3958 case SB_LINELEFT:
3959 TRACE("SB_LINELEFT\n");
3960 if (es->x_offset)
3961 dx = -es->char_width;
3962 break;
3963 case SB_LINERIGHT:
3964 TRACE("SB_LINERIGHT\n");
3965 if (es->x_offset < es->text_width)
3966 dx = es->char_width;
3967 break;
3968 case SB_PAGELEFT:
3969 TRACE("SB_PAGELEFT\n");
3970 if (es->x_offset)
3971 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3972 break;
3973 case SB_PAGERIGHT:
3974 TRACE("SB_PAGERIGHT\n");
3975 if (es->x_offset < es->text_width)
3976 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3977 break;
3978 case SB_LEFT:
3979 TRACE("SB_LEFT\n");
3980 if (es->x_offset)
3981 dx = -es->x_offset;
3982 break;
3983 case SB_RIGHT:
3984 TRACE("SB_RIGHT\n");
3985 if (es->x_offset < es->text_width)
3986 dx = es->text_width - es->x_offset;
3987 break;
3988 case SB_THUMBTRACK:
3989 TRACE("SB_THUMBTRACK %d\n", pos);
3990 es->flags |= EF_HSCROLL_TRACK;
3991 if(es->style & WS_HSCROLL)
3992 dx = pos - es->x_offset;
3993 else
3995 INT fw, new_x;
3996 /* Sanity check */
3997 if(pos < 0 || pos > 100) return 0;
3998 /* Assume default scroll range 0-100 */
3999 fw = es->format_rect.right - es->format_rect.left;
4000 new_x = pos * (es->text_width - fw) / 100;
4001 dx = es->text_width ? (new_x - es->x_offset) : 0;
4003 break;
4004 case SB_THUMBPOSITION:
4005 TRACE("SB_THUMBPOSITION %d\n", pos);
4006 es->flags &= ~EF_HSCROLL_TRACK;
4007 if(wnd->dwStyle & WS_HSCROLL)
4008 dx = pos - es->x_offset;
4009 else
4011 INT fw, new_x;
4012 /* Sanity check */
4013 if(pos < 0 || pos > 100) return 0;
4014 /* Assume default scroll range 0-100 */
4015 fw = es->format_rect.right - es->format_rect.left;
4016 new_x = pos * (es->text_width - fw) / 100;
4017 dx = es->text_width ? (new_x - es->x_offset) : 0;
4019 if (!dx) {
4020 /* force scroll info update */
4021 EDIT_UpdateScrollInfo(wnd, es);
4022 EDIT_NOTIFY_PARENT(es, EN_HSCROLL, "EN_HSCROLL");
4024 break;
4025 case SB_ENDSCROLL:
4026 TRACE("SB_ENDSCROLL\n");
4027 break;
4029 * FIXME : the next two are undocumented !
4030 * Are we doing the right thing ?
4031 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4032 * although it's also a regular control message.
4034 case EM_GETTHUMB: /* this one is used by NT notepad */
4035 case EM_GETTHUMB16:
4037 LRESULT ret;
4038 if(wnd->dwStyle & WS_HSCROLL)
4039 ret = GetScrollPos(wnd->hwndSelf, SB_HORZ);
4040 else
4042 /* Assume default scroll range 0-100 */
4043 INT fw = es->format_rect.right - es->format_rect.left;
4044 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
4046 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4047 return ret;
4049 case EM_LINESCROLL16:
4050 TRACE("EM_LINESCROLL16\n");
4051 dx = pos;
4052 break;
4054 default:
4055 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4056 action, action);
4057 return 0;
4059 if (dx)
4061 INT fw = es->format_rect.right - es->format_rect.left;
4062 /* check if we are going to move too far */
4063 if(es->x_offset + dx + fw > es->text_width)
4064 dx = es->text_width - fw - es->x_offset;
4065 if(dx)
4066 EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
4068 return 0;
4072 /*********************************************************************
4074 * EDIT_CheckCombo
4077 static BOOL EDIT_CheckCombo(WND *wnd, EDITSTATE *es, UINT msg, INT key)
4079 HWND hLBox = es->hwndListBox;
4080 HWND hCombo;
4081 BOOL bDropped;
4082 int nEUI;
4084 if (!hLBox)
4085 return FALSE;
4087 hCombo = wnd->parent->hwndSelf;
4088 bDropped = TRUE;
4089 nEUI = 0;
4091 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
4092 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
4094 if (key == VK_UP || key == VK_DOWN)
4096 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
4097 nEUI = 1;
4099 if (msg == WM_KEYDOWN || nEUI)
4100 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
4103 switch (msg)
4105 case WM_KEYDOWN:
4106 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
4108 /* make sure ComboLBox pops up */
4109 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
4110 key = VK_F4;
4111 nEUI = 2;
4114 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
4115 break;
4117 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
4118 if (nEUI)
4119 SendMessageW(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
4120 else
4121 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
4122 break;
4125 if(nEUI == 2)
4126 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
4128 return TRUE;
4132 /*********************************************************************
4134 * WM_KEYDOWN
4136 * Handling of special keys that don't produce a WM_CHAR
4137 * (i.e. non-printable keys) & Backspace & Delete
4140 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key)
4142 BOOL shift;
4143 BOOL control;
4145 if (GetKeyState(VK_MENU) & 0x8000)
4146 return 0;
4148 shift = GetKeyState(VK_SHIFT) & 0x8000;
4149 control = GetKeyState(VK_CONTROL) & 0x8000;
4151 switch (key) {
4152 case VK_F4:
4153 case VK_UP:
4154 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key) || key == VK_F4)
4155 break;
4157 /* fall through */
4158 case VK_LEFT:
4159 if ((es->style & ES_MULTILINE) && (key == VK_UP))
4160 EDIT_MoveUp_ML(wnd, es, shift);
4161 else
4162 if (control)
4163 EDIT_MoveWordBackward(wnd, es, shift);
4164 else
4165 EDIT_MoveBackward(wnd, es, shift);
4166 break;
4167 case VK_DOWN:
4168 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key))
4169 break;
4170 /* fall through */
4171 case VK_RIGHT:
4172 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
4173 EDIT_MoveDown_ML(wnd, es, shift);
4174 else if (control)
4175 EDIT_MoveWordForward(wnd, es, shift);
4176 else
4177 EDIT_MoveForward(wnd, es, shift);
4178 break;
4179 case VK_HOME:
4180 EDIT_MoveHome(wnd, es, shift);
4181 break;
4182 case VK_END:
4183 EDIT_MoveEnd(wnd, es, shift);
4184 break;
4185 case VK_PRIOR:
4186 if (es->style & ES_MULTILINE)
4187 EDIT_MovePageUp_ML(wnd, es, shift);
4188 else
4189 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
4190 break;
4191 case VK_NEXT:
4192 if (es->style & ES_MULTILINE)
4193 EDIT_MovePageDown_ML(wnd, es, shift);
4194 else
4195 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
4196 break;
4197 case VK_DELETE:
4198 if (!(es->style & ES_READONLY) && !(shift && control)) {
4199 if (es->selection_start != es->selection_end) {
4200 if (shift)
4201 EDIT_WM_Cut(wnd, es);
4202 else
4203 EDIT_WM_Clear(wnd, es);
4204 } else {
4205 if (shift) {
4206 /* delete character left of caret */
4207 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4208 EDIT_MoveBackward(wnd, es, TRUE);
4209 EDIT_WM_Clear(wnd, es);
4210 } else if (control) {
4211 /* delete to end of line */
4212 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4213 EDIT_MoveEnd(wnd, es, TRUE);
4214 EDIT_WM_Clear(wnd, es);
4215 } else {
4216 /* delete character right of caret */
4217 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4218 EDIT_MoveForward(wnd, es, TRUE);
4219 EDIT_WM_Clear(wnd, es);
4223 break;
4224 case VK_INSERT:
4225 if (shift) {
4226 if (!(es->style & ES_READONLY))
4227 EDIT_WM_Paste(wnd, es);
4228 } else if (control)
4229 EDIT_WM_Copy(wnd, es);
4230 break;
4231 case VK_RETURN:
4232 /* If the edit doesn't want the return send a message to the default object */
4233 if(!(es->style & ES_WANTRETURN))
4235 HWND hwndParent = GetParent(wnd->hwndSelf);
4236 DWORD dw = SendMessageW( hwndParent, DM_GETDEFID, 0, 0 );
4237 if (HIWORD(dw) == DC_HASDEFID)
4239 SendMessageW( hwndParent, WM_COMMAND,
4240 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
4241 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
4244 break;
4246 return 0;
4250 /*********************************************************************
4252 * WM_KILLFOCUS
4255 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es)
4257 es->flags &= ~EF_FOCUSED;
4258 DestroyCaret();
4259 if(!(es->style & ES_NOHIDESEL))
4260 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
4261 EDIT_NOTIFY_PARENT(es, EN_KILLFOCUS, "EN_KILLFOCUS");
4262 return 0;
4266 /*********************************************************************
4268 * WM_LBUTTONDBLCLK
4270 * The caret position has been set on the WM_LBUTTONDOWN message
4273 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es)
4275 INT s;
4276 INT e = es->selection_end;
4277 INT l;
4278 INT li;
4279 INT ll;
4281 if (!(es->flags & EF_FOCUSED))
4282 return 0;
4284 l = EDIT_EM_LineFromChar(es, e);
4285 li = EDIT_EM_LineIndex(es, l);
4286 ll = EDIT_EM_LineLength(es, e);
4287 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
4288 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
4289 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
4290 EDIT_EM_ScrollCaret(wnd, es);
4291 return 0;
4295 /*********************************************************************
4297 * WM_LBUTTONDOWN
4300 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
4302 INT e;
4303 BOOL after_wrap;
4305 if (!(es->flags & EF_FOCUSED))
4306 return 0;
4308 es->bCaptureState = TRUE;
4309 SetCapture(wnd->hwndSelf);
4310 EDIT_ConfinePoint(es, &x, &y);
4311 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
4312 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
4313 EDIT_EM_ScrollCaret(wnd, es);
4314 es->region_posx = es->region_posy = 0;
4315 SetTimer(wnd->hwndSelf, 0, 100, NULL);
4316 return 0;
4320 /*********************************************************************
4322 * WM_LBUTTONUP
4325 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es)
4327 if (es->bCaptureState && GetCapture() == hwndSelf) {
4328 KillTimer(hwndSelf, 0);
4329 ReleaseCapture();
4331 es->bCaptureState = FALSE;
4332 return 0;
4336 /*********************************************************************
4338 * WM_MBUTTONDOWN
4341 static LRESULT EDIT_WM_MButtonDown(WND *wnd)
4343 SendMessageW(wnd->hwndSelf,WM_PASTE,0,0);
4344 return 0;
4348 /*********************************************************************
4350 * WM_MOUSEMOVE
4353 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y)
4355 INT e;
4356 BOOL after_wrap;
4357 INT prex, prey;
4359 if (GetCapture() != wnd->hwndSelf)
4360 return 0;
4363 * FIXME: gotta do some scrolling if outside client
4364 * area. Maybe reset the timer ?
4366 prex = x; prey = y;
4367 EDIT_ConfinePoint(es, &x, &y);
4368 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
4369 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
4370 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
4371 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
4372 return 0;
4376 /*********************************************************************
4378 * WM_NCCREATE
4381 static LRESULT EDIT_WM_NCCreate(WND *wnd, DWORD style, HWND hwndParent, BOOL unicode)
4383 EDITSTATE *es;
4384 UINT alloc_size;
4386 TRACE("Creating %s edit control, style = %08lx\n",
4387 unicode ? "Unicode" : "ANSI", style);
4389 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
4390 return FALSE;
4391 *(EDITSTATE **)wnd->wExtra = es;
4394 * Note: since the EDITSTATE has not been fully initialized yet,
4395 * we can't use any API calls that may send
4396 * WM_XXX messages before WM_NCCREATE is completed.
4399 es->is_unicode = unicode;
4400 es->style = style;
4402 es->bEnableState = !(style & WS_DISABLED);
4405 * In Win95 look and feel, the WS_BORDER style is replaced by the
4406 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4407 * control a non client area.
4409 if (TWEAK_WineLook != WIN31_LOOK)
4411 if (es->style & WS_BORDER)
4413 es->style &= ~WS_BORDER;
4414 wnd->dwStyle &= ~WS_BORDER;
4415 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
4418 else
4420 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
4421 wnd->dwStyle &= ~WS_BORDER;
4424 /* Save parent, which will be notified by EN_* messages */
4425 es->hwndParent = hwndParent;
4427 if (es->style & ES_COMBO)
4428 es->hwndListBox = GetDlgItem(hwndParent, ID_CB_LISTBOX);
4430 if (es->style & ES_MULTILINE) {
4431 es->buffer_limit = BUFLIMIT_MULTI;
4432 if (es->style & WS_VSCROLL)
4433 es->style |= ES_AUTOVSCROLL;
4434 if (es->style & WS_HSCROLL)
4435 es->style |= ES_AUTOHSCROLL;
4436 es->style &= ~ES_PASSWORD;
4437 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4438 if (es->style & ES_RIGHT)
4439 es->style &= ~ES_CENTER;
4440 es->style &= ~WS_HSCROLL;
4441 es->style &= ~ES_AUTOHSCROLL;
4444 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4445 es->style |= ES_AUTOVSCROLL;
4446 } else {
4447 es->buffer_limit = BUFLIMIT_SINGLE;
4448 es->style &= ~ES_CENTER;
4449 es->style &= ~ES_RIGHT;
4450 es->style &= ~WS_HSCROLL;
4451 es->style &= ~WS_VSCROLL;
4452 es->style &= ~ES_AUTOVSCROLL;
4453 es->style &= ~ES_WANTRETURN;
4454 if (es->style & ES_UPPERCASE) {
4455 es->style &= ~ES_LOWERCASE;
4456 es->style &= ~ES_NUMBER;
4457 } else if (es->style & ES_LOWERCASE)
4458 es->style &= ~ES_NUMBER;
4459 if (es->style & ES_PASSWORD)
4460 es->password_char = '*';
4462 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4463 es->style |= ES_AUTOHSCROLL;
4466 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4467 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4468 return FALSE;
4469 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4471 if (!(es->undo_text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (es->buffer_size + 1) * sizeof(WCHAR))))
4472 return FALSE;
4473 es->undo_buffer_size = es->buffer_size;
4475 if (es->style & ES_MULTILINE)
4476 if (!(es->first_line_def = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
4477 return FALSE;
4478 es->line_count = 1;
4480 return TRUE;
4483 /*********************************************************************
4485 * WM_PAINT
4488 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
4490 PAINTSTRUCT ps;
4491 INT i;
4492 HDC dc;
4493 HFONT old_font = 0;
4494 RECT rc;
4495 RECT rcLine;
4496 RECT rcRgn;
4497 BOOL rev = es->bEnableState &&
4498 ((es->flags & EF_FOCUSED) ||
4499 (es->style & ES_NOHIDESEL));
4500 if (!wParam)
4501 dc = BeginPaint(wnd->hwndSelf, &ps);
4502 else
4503 dc = (HDC) wParam;
4504 if(es->style & WS_BORDER) {
4505 GetClientRect(wnd->hwndSelf, &rc);
4506 if(es->style & ES_MULTILINE) {
4507 if(es->style & WS_HSCROLL) rc.bottom++;
4508 if(es->style & WS_VSCROLL) rc.right++;
4510 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
4512 IntersectClipRect(dc, es->format_rect.left,
4513 es->format_rect.top,
4514 es->format_rect.right,
4515 es->format_rect.bottom);
4516 if (es->style & ES_MULTILINE) {
4517 GetClientRect(wnd->hwndSelf, &rc);
4518 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
4520 if (es->font)
4521 old_font = SelectObject(dc, es->font);
4522 if ( get_app_version() >= 0x40000 &&(
4523 !es->bEnableState || (es->style & ES_READONLY)))
4524 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
4525 else
4526 EDIT_SEND_CTLCOLOR(wnd, dc);
4528 if (!es->bEnableState)
4529 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
4530 GetClipBox(dc, &rcRgn);
4531 if (es->style & ES_MULTILINE) {
4532 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4533 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
4534 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
4535 if (IntersectRect(&rc, &rcRgn, &rcLine))
4536 EDIT_PaintLine(wnd, es, dc, i, rev);
4538 } else {
4539 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
4540 if (IntersectRect(&rc, &rcRgn, &rcLine))
4541 EDIT_PaintLine(wnd, es, dc, 0, rev);
4543 if (es->font)
4544 SelectObject(dc, old_font);
4546 if (!wParam)
4547 EndPaint(wnd->hwndSelf, &ps);
4551 /*********************************************************************
4553 * WM_PASTE
4556 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
4558 HGLOBAL hsrc;
4559 LPWSTR src;
4561 /* Protect read-only edit control from modification */
4562 if(es->style & ES_READONLY)
4563 return;
4565 OpenClipboard(wnd->hwndSelf);
4566 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
4567 src = (LPWSTR)GlobalLock(hsrc);
4568 EDIT_EM_ReplaceSel(wnd, es, TRUE, src, TRUE);
4569 GlobalUnlock(hsrc);
4571 CloseClipboard();
4575 /*********************************************************************
4577 * WM_SETFOCUS
4580 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es)
4582 es->flags |= EF_FOCUSED;
4583 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4584 EDIT_SetCaretPos(wnd, es, es->selection_end,
4585 es->flags & EF_AFTER_WRAP);
4586 if(!(es->style & ES_NOHIDESEL))
4587 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
4588 ShowCaret(wnd->hwndSelf);
4589 EDIT_NOTIFY_PARENT(es, EN_SETFOCUS, "EN_SETFOCUS");
4593 /*********************************************************************
4595 * WM_SETFONT
4597 * With Win95 look the margins are set to default font value unless
4598 * the system font (font == 0) is being set, in which case they are left
4599 * unchanged.
4602 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
4604 TEXTMETRICW tm;
4605 HDC dc;
4606 HFONT old_font = 0;
4607 RECT r;
4609 es->font = font;
4610 dc = GetDC(wnd->hwndSelf);
4611 if (font)
4612 old_font = SelectObject(dc, font);
4613 GetTextMetricsW(dc, &tm);
4614 es->line_height = tm.tmHeight;
4615 es->char_width = tm.tmAveCharWidth;
4616 if (font)
4617 SelectObject(dc, old_font);
4618 ReleaseDC(wnd->hwndSelf, dc);
4619 if (font && (TWEAK_WineLook > WIN31_LOOK))
4620 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
4621 EC_USEFONTINFO, EC_USEFONTINFO);
4623 /* Force the recalculation of the format rect for each font change */
4624 GetClientRect(wnd->hwndSelf, &r);
4625 EDIT_SetRectNP(wnd, es, &r);
4627 if (es->style & ES_MULTILINE)
4628 EDIT_BuildLineDefs_ML(wnd, es, 0, strlenW(es->text), 0, (HRGN)0);
4629 else
4630 EDIT_CalcLineWidth_SL(wnd, es);
4632 if (redraw)
4633 EDIT_UpdateText(wnd, NULL, TRUE);
4634 if (es->flags & EF_FOCUSED) {
4635 DestroyCaret();
4636 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4637 EDIT_SetCaretPos(wnd, es, es->selection_end,
4638 es->flags & EF_AFTER_WRAP);
4639 ShowCaret(wnd->hwndSelf);
4644 /*********************************************************************
4646 * WM_SETTEXT
4648 * NOTES
4649 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4650 * The modified flag is reset. No notifications are sent.
4652 * For single-line controls, reception of WM_SETTEXT triggers:
4653 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4656 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPARAM lParam, BOOL unicode)
4658 LPWSTR text = NULL;
4660 if(unicode)
4661 text = (LPWSTR)lParam;
4662 else if (lParam)
4664 LPCSTR textA = (LPCSTR)lParam;
4665 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
4666 if((text = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
4667 MultiByteToWideChar(CP_ACP, 0, textA, -1, text, countW);
4670 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
4671 if (text) {
4672 TRACE("%s\n", debugstr_w(text));
4673 EDIT_EM_ReplaceSel(wnd, es, FALSE, text, FALSE);
4674 if(!unicode)
4675 HeapFree(GetProcessHeap(), 0, text);
4676 } else {
4677 static const WCHAR empty_stringW[] = {0};
4678 TRACE("<NULL>\n");
4679 EDIT_EM_ReplaceSel(wnd, es, FALSE, empty_stringW, FALSE);
4681 es->x_offset = 0;
4682 es->flags &= ~EF_MODIFIED;
4683 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
4684 /* Send the notification after the selection start and end have been set
4685 * edit control doesn't send notification on WM_SETTEXT
4686 * if it is multiline, or it is part of combobox
4688 if( !((es->style & ES_MULTILINE) || es->hwndListBox))
4689 EDIT_NOTIFY_PARENT(es, EN_CHANGE, "EN_CHANGE");
4690 EDIT_EM_ScrollCaret(wnd, es);
4694 /*********************************************************************
4696 * WM_SIZE
4699 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
4701 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4702 RECT rc;
4703 TRACE("width = %d, height = %d\n", width, height);
4704 SetRect(&rc, 0, 0, width, height);
4705 EDIT_SetRectNP(wnd, es, &rc);
4706 EDIT_UpdateText(wnd, NULL, TRUE);
4711 /*********************************************************************
4713 * WM_SYSKEYDOWN
4716 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
4718 if ((key == VK_BACK) && (key_data & 0x2000)) {
4719 if (EDIT_EM_CanUndo(es))
4720 EDIT_EM_Undo(wnd, es);
4721 return 0;
4722 } else if (key == VK_UP || key == VK_DOWN) {
4723 if (EDIT_CheckCombo(wnd, es, WM_SYSKEYDOWN, key))
4724 return 0;
4726 return DefWindowProcW(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4730 /*********************************************************************
4732 * WM_TIMER
4735 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es)
4737 if (es->region_posx < 0) {
4738 EDIT_MoveBackward(wnd, es, TRUE);
4739 } else if (es->region_posx > 0) {
4740 EDIT_MoveForward(wnd, es, TRUE);
4743 * FIXME: gotta do some vertical scrolling here, like
4744 * EDIT_EM_LineScroll(wnd, 0, 1);
4748 /*********************************************************************
4750 * WM_VSCROLL
4753 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
4755 INT dy;
4757 if (!(es->style & ES_MULTILINE))
4758 return 0;
4760 if (!(es->style & ES_AUTOVSCROLL))
4761 return 0;
4763 dy = 0;
4764 switch (action) {
4765 case SB_LINEUP:
4766 case SB_LINEDOWN:
4767 case SB_PAGEUP:
4768 case SB_PAGEDOWN:
4769 TRACE("action %d\n", action);
4770 EDIT_EM_Scroll(wnd, es, action);
4771 return 0;
4772 case SB_TOP:
4773 TRACE("SB_TOP\n");
4774 dy = -es->y_offset;
4775 break;
4776 case SB_BOTTOM:
4777 TRACE("SB_BOTTOM\n");
4778 dy = es->line_count - 1 - es->y_offset;
4779 break;
4780 case SB_THUMBTRACK:
4781 TRACE("SB_THUMBTRACK %d\n", pos);
4782 es->flags |= EF_VSCROLL_TRACK;
4783 if(es->style & WS_VSCROLL)
4784 dy = pos - es->y_offset;
4785 else
4787 /* Assume default scroll range 0-100 */
4788 INT vlc, new_y;
4789 /* Sanity check */
4790 if(pos < 0 || pos > 100) return 0;
4791 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4792 new_y = pos * (es->line_count - vlc) / 100;
4793 dy = es->line_count ? (new_y - es->y_offset) : 0;
4794 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4795 es->line_count, es->y_offset, pos, dy);
4797 break;
4798 case SB_THUMBPOSITION:
4799 TRACE("SB_THUMBPOSITION %d\n", pos);
4800 es->flags &= ~EF_VSCROLL_TRACK;
4801 if(es->style & WS_VSCROLL)
4802 dy = pos - es->y_offset;
4803 else
4805 /* Assume default scroll range 0-100 */
4806 INT vlc, new_y;
4807 /* Sanity check */
4808 if(pos < 0 || pos > 100) return 0;
4809 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4810 new_y = pos * (es->line_count - vlc) / 100;
4811 dy = es->line_count ? (new_y - es->y_offset) : 0;
4812 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4813 es->line_count, es->y_offset, pos, dy);
4815 if (!dy)
4817 /* force scroll info update */
4818 EDIT_UpdateScrollInfo(wnd, es);
4819 EDIT_NOTIFY_PARENT(es, EN_VSCROLL, "EN_VSCROLL");
4821 break;
4822 case SB_ENDSCROLL:
4823 TRACE("SB_ENDSCROLL\n");
4824 break;
4826 * FIXME : the next two are undocumented !
4827 * Are we doing the right thing ?
4828 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4829 * although it's also a regular control message.
4831 case EM_GETTHUMB: /* this one is used by NT notepad */
4832 case EM_GETTHUMB16:
4834 LRESULT ret;
4835 if(wnd->dwStyle & WS_VSCROLL)
4836 ret = GetScrollPos(wnd->hwndSelf, SB_VERT);
4837 else
4839 /* Assume default scroll range 0-100 */
4840 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4841 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
4843 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4844 return ret;
4846 case EM_LINESCROLL16:
4847 TRACE("EM_LINESCROLL16 %d\n", pos);
4848 dy = pos;
4849 break;
4851 default:
4852 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4853 action, action);
4854 return 0;
4856 if (dy)
4857 EDIT_EM_LineScroll(wnd, es, 0, dy);
4858 return 0;
4861 /*********************************************************************
4863 * EDIT_UpdateText
4866 static void EDIT_UpdateTextRegion(WND *wnd, HRGN hrgn, BOOL bErase)
4868 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4870 if (es->flags & EF_UPDATE)
4871 EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE");
4873 InvalidateRgn(wnd->hwndSelf, hrgn, bErase);
4877 /*********************************************************************
4879 * EDIT_UpdateText
4882 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase)
4884 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4886 if (es->flags & EF_UPDATE)
4887 EDIT_NOTIFY_PARENT(es, EN_UPDATE, "EN_UPDATE");
4889 InvalidateRect(wnd->hwndSelf, rc, bErase);