Updated all fields with Ukrainian values.
[wine.git] / controls / edit.c
blobcaec3e80fa2327242285d225607ab51aec94ed20
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 on next WM_PAINT */
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 struct tagLINEDEF *next;
67 } LINEDEF;
69 typedef struct
71 BOOL is_unicode; /* how the control was created */
72 LPWSTR text; /* the actual contents of the control */
73 UINT buffer_size; /* the size of the buffer in characters */
74 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
75 HFONT font; /* NULL means standard system font */
76 INT x_offset; /* scroll offset for multi lines this is in pixels
77 for single lines it's in characters */
78 INT line_height; /* height of a screen line in pixels */
79 INT char_width; /* average character width in pixels */
80 DWORD style; /* sane version of wnd->dwStyle */
81 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
82 INT undo_insert_count; /* number of characters inserted in sequence */
83 UINT undo_position; /* character index of the insertion and deletion */
84 LPWSTR undo_text; /* deleted text */
85 UINT undo_buffer_size; /* size of the deleted text buffer */
86 INT selection_start; /* == selection_end if no selection */
87 INT selection_end; /* == current caret position */
88 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
89 INT left_margin; /* in pixels */
90 INT right_margin; /* in pixels */
91 RECT format_rect;
92 INT text_width; /* width of the widest line in pixels for multi line controls
93 and just line width for single line controls */
94 INT region_posx; /* Position of cursor relative to region: */
95 INT region_posy; /* -1: to left, 0: within, 1: to right */
96 EDITWORDBREAKPROC16 word_break_proc16;
97 void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
98 INT line_count; /* number of lines */
99 INT y_offset; /* scroll offset in number of lines */
100 BOOL bCaptureState; /* flag indicating whether mouse was captured */
101 BOOL bEnableState; /* flag keeping the enable state */
102 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
104 * only for multi line controls
106 INT lock_count; /* amount of re-entries in the EditWndProc */
107 INT tabs_count;
108 LPINT tabs;
109 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
110 HLOCAL hloc32W; /* our unicode local memory block */
111 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
112 or EM_SETHANDLE16 */
113 HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
114 or EM_SETHANDLE */
115 } EDITSTATE;
118 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
119 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
121 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
122 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
124 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
125 do {TRACE("notification " str " sent to hwnd=%08x\n", \
126 (UINT)(hwnd));} while(0)
128 /* used for disabled or read-only edit control */
129 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
130 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
131 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
132 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
133 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
134 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
135 #define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
136 do {DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str); \
137 SendMessageW((wnd)->parent->hwndSelf, WM_COMMAND, \
138 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
139 (LPARAM)(wnd)->hwndSelf);} while(0)
140 #define DPRINTF_EDIT_MSG16(str) \
141 TRACE(\
142 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
143 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
144 #define DPRINTF_EDIT_MSG32(str) \
145 TRACE(\
146 "32 bit %c : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
147 unicode ? 'W' : 'A', \
148 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
150 /*********************************************************************
152 * Declarations
157 * These functions have trivial implementations
158 * We still like to call them internally
159 * "static inline" makes them more like macro's
161 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es);
162 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es);
163 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
164 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
167 * Helper functions only valid for one type of control
169 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
170 static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es);
171 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
172 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
173 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
174 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
175 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
177 * Helper functions valid for both single line _and_ multi line controls
179 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action);
180 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
181 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y);
182 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
183 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end);
184 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
185 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size);
186 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size);
187 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend);
188 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend);
189 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend);
190 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend);
191 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend);
192 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend);
193 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
194 static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
195 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
196 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
197 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
198 static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es);
199 static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
201 * EM_XXX message handlers
203 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y);
204 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol);
205 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es);
206 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
207 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode);
208 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end);
209 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
210 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
211 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
212 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
213 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
214 static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy);
215 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
216 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update);
217 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
218 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
219 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc);
220 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
221 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit);
222 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, INT left, INT right);
223 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, WCHAR c);
224 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
225 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs);
226 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs);
227 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, LPARAM lParam);
228 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
229 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
231 * WM_XXX message handlers
233 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, WCHAR c);
234 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
235 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y);
236 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
237 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCWSTR name);
238 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
239 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc);
240 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode);
241 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
242 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key);
243 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es);
244 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es);
245 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
246 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es);
247 static LRESULT EDIT_WM_MButtonDown(WND *wnd);
248 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y);
249 static LRESULT EDIT_WM_NCCreate(WND *wnd, DWORD style, HWND hwndParent, BOOL unicode);
250 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
251 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
252 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es);
253 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
254 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPARAM lParam, BOOL unicode);
255 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
256 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
257 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es);
258 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos);
259 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
261 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
262 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
264 /*********************************************************************
265 * edit class descriptor
267 const struct builtin_class_descr EDIT_builtin_class =
269 "Edit", /* name */
270 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
271 EditWndProcA, /* procA */
272 EditWndProcW, /* procW */
273 sizeof(EDITSTATE *), /* extra */
274 IDC_IBEAMA, /* cursor */
275 0 /* brush */
279 /*********************************************************************
281 * EM_CANUNDO
284 static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es)
286 return (es->undo_insert_count || strlenW(es->undo_text));
290 /*********************************************************************
292 * EM_EMPTYUNDOBUFFER
295 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
297 es->undo_insert_count = 0;
298 *es->undo_text = '\0';
302 /*********************************************************************
304 * WM_CLEAR
307 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
309 static const WCHAR empty_stringW[] = {0};
311 /* Protect read-only edit control from modification */
312 if(es->style & ES_READONLY)
313 return;
315 EDIT_EM_ReplaceSel(wnd, es, TRUE, empty_stringW, TRUE);
317 if (es->flags & EF_UPDATE) {
318 es->flags &= ~EF_UPDATE;
319 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
324 /*********************************************************************
326 * WM_CUT
329 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
331 EDIT_WM_Copy(wnd, es);
332 EDIT_WM_Clear(wnd, es);
336 /**********************************************************************
337 * get_app_version
339 * Returns the window version in case Wine emulates a later version
340 * of windows then the application expects.
342 * In a number of cases when windows runs an application that was
343 * designed for an earlier windows version, windows reverts
344 * to "old" behaviour of that earlier version.
346 * An example is a disabled edit control that needs to be painted.
347 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
348 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
349 * applications with an expected version 0f 4.0 or higher.
352 static DWORD get_app_version(void)
354 static DWORD version;
355 if (!version)
357 DWORD dwEmulatedVersion;
358 OSVERSIONINFOW info;
359 DWORD dwProcVersion = GetProcessVersion(0);
361 GetVersionExW( &info );
362 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
363 /* fixme: this may not be 100% correct; see discussion on the
364 * wine developer list in Nov 1999 */
365 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
367 return version;
371 /*********************************************************************
373 * EditWndProc_locked
375 * The messages are in the order of the actual integer values
376 * (which can be found in include/windows.h)
377 * Whereever possible the 16 bit versions are converted to
378 * the 32 bit ones, so that we can 'fall through' to the
379 * helper functions. These are mostly 32 bit (with a few
380 * exceptions, clearly indicated by a '16' extension to their
381 * names).
384 static LRESULT WINAPI EditWndProc_locked( WND *wnd, UINT msg,
385 WPARAM wParam, LPARAM lParam, BOOL unicode )
387 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
388 LRESULT result = 0;
390 switch (msg) {
391 case WM_DESTROY:
392 DPRINTF_EDIT_MSG32("WM_DESTROY");
393 EDIT_WM_Destroy(wnd, es);
394 result = 0;
395 goto END;
397 case WM_NCCREATE:
398 DPRINTF_EDIT_MSG32("WM_NCCREATE");
399 if(unicode)
401 LPCREATESTRUCTW cs = (LPCREATESTRUCTW)lParam;
402 result = EDIT_WM_NCCreate(wnd, cs->style, cs->hwndParent, TRUE);
404 else
406 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
407 result = EDIT_WM_NCCreate(wnd, cs->style, cs->hwndParent, FALSE);
409 goto END;
412 if (!es)
414 if(unicode)
415 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
416 else
417 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
418 goto END;
422 EDIT_LockBuffer(wnd, es);
423 switch (msg) {
424 case EM_GETSEL16:
425 DPRINTF_EDIT_MSG16("EM_GETSEL");
426 wParam = 0;
427 lParam = 0;
428 /* fall through */
429 case EM_GETSEL:
430 DPRINTF_EDIT_MSG32("EM_GETSEL");
431 result = EDIT_EM_GetSel(es, (LPUINT)wParam, (LPUINT)lParam);
432 break;
434 case EM_SETSEL16:
435 DPRINTF_EDIT_MSG16("EM_SETSEL");
436 if (SLOWORD(lParam) == -1)
437 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
438 else
439 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
440 if (!wParam)
441 EDIT_EM_ScrollCaret(wnd, es);
442 result = 1;
443 break;
444 case EM_SETSEL:
445 DPRINTF_EDIT_MSG32("EM_SETSEL");
446 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
447 EDIT_EM_ScrollCaret(wnd, es);
448 result = 1;
449 break;
451 case EM_GETRECT16:
452 DPRINTF_EDIT_MSG16("EM_GETRECT");
453 if (lParam)
454 CONV_RECT32TO16(&es->format_rect, MapSL(lParam));
455 break;
456 case EM_GETRECT:
457 DPRINTF_EDIT_MSG32("EM_GETRECT");
458 if (lParam)
459 CopyRect((LPRECT)lParam, &es->format_rect);
460 break;
462 case EM_SETRECT16:
463 DPRINTF_EDIT_MSG16("EM_SETRECT");
464 if ((es->style & ES_MULTILINE) && lParam) {
465 RECT rc;
466 CONV_RECT16TO32(MapSL(lParam), &rc);
467 EDIT_SetRectNP(wnd, es, &rc);
468 EDIT_UpdateText(wnd, NULL, TRUE);
470 break;
471 case EM_SETRECT:
472 DPRINTF_EDIT_MSG32("EM_SETRECT");
473 if ((es->style & ES_MULTILINE) && lParam) {
474 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
475 EDIT_UpdateText(wnd, NULL, TRUE);
477 break;
479 case EM_SETRECTNP16:
480 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
481 if ((es->style & ES_MULTILINE) && lParam) {
482 RECT rc;
483 CONV_RECT16TO32(MapSL(lParam), &rc);
484 EDIT_SetRectNP(wnd, es, &rc);
486 break;
487 case EM_SETRECTNP:
488 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
489 if ((es->style & ES_MULTILINE) && lParam)
490 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
491 break;
493 case EM_SCROLL16:
494 DPRINTF_EDIT_MSG16("EM_SCROLL");
495 /* fall through */
496 case EM_SCROLL:
497 DPRINTF_EDIT_MSG32("EM_SCROLL");
498 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
499 break;
501 case EM_LINESCROLL16:
502 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
503 wParam = (WPARAM)(INT)SHIWORD(lParam);
504 lParam = (LPARAM)(INT)SLOWORD(lParam);
505 /* fall through */
506 case EM_LINESCROLL:
507 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
508 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
509 break;
511 case EM_SCROLLCARET16:
512 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
513 /* fall through */
514 case EM_SCROLLCARET:
515 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
516 EDIT_EM_ScrollCaret(wnd, es);
517 result = 1;
518 break;
520 case EM_GETMODIFY16:
521 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
522 /* fall through */
523 case EM_GETMODIFY:
524 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
525 result = ((es->flags & EF_MODIFIED) != 0);
526 break;
528 case EM_SETMODIFY16:
529 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
530 /* fall through */
531 case EM_SETMODIFY:
532 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
533 if (wParam)
534 es->flags |= EF_MODIFIED;
535 else
536 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
537 break;
539 case EM_GETLINECOUNT16:
540 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
541 /* fall through */
542 case EM_GETLINECOUNT:
543 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
544 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
545 break;
547 case EM_LINEINDEX16:
548 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
549 if ((INT16)wParam == -1)
550 wParam = (WPARAM)-1;
551 /* fall through */
552 case EM_LINEINDEX:
553 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
554 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
555 break;
557 case EM_SETHANDLE16:
558 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
559 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
560 break;
561 case EM_SETHANDLE:
562 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
563 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
564 break;
566 case EM_GETHANDLE16:
567 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
568 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
569 break;
570 case EM_GETHANDLE:
571 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
572 result = (LRESULT)EDIT_EM_GetHandle(es);
573 break;
575 case EM_GETTHUMB16:
576 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
577 /* fall through */
578 case EM_GETTHUMB:
579 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
580 result = EDIT_EM_GetThumb(wnd, es);
581 break;
583 /* messages 0x00bf and 0x00c0 missing from specs */
585 case WM_USER+15:
586 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
587 /* fall through */
588 case 0x00bf:
589 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
590 if(unicode)
591 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
592 else
593 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
594 break;
596 case WM_USER+16:
597 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
598 /* fall through */
599 case 0x00c0:
600 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
601 if(unicode)
602 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
603 else
604 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
605 break;
607 case EM_LINELENGTH16:
608 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
609 /* fall through */
610 case EM_LINELENGTH:
611 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
612 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
613 break;
615 case EM_REPLACESEL16:
616 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
617 lParam = (LPARAM)MapSL(lParam);
618 /* fall through */
619 case EM_REPLACESEL:
621 LPWSTR textW;
622 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
624 if(unicode)
625 textW = (LPWSTR)lParam;
626 else
628 LPSTR textA = (LPSTR)lParam;
629 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
630 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
631 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
634 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, textW, TRUE);
635 if (es->flags & EF_UPDATE) {
636 es->flags &= ~EF_UPDATE;
637 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
639 result = 1;
641 if(!unicode)
642 HeapFree(GetProcessHeap(), 0, textW);
643 break;
645 /* message 0x00c3 missing from specs */
647 case WM_USER+19:
648 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
649 /* fall through */
650 case 0x00c3:
651 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
652 if(unicode)
653 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
654 else
655 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
656 break;
658 case EM_GETLINE16:
659 DPRINTF_EDIT_MSG16("EM_GETLINE");
660 lParam = (LPARAM)MapSL(lParam);
661 /* fall through */
662 case EM_GETLINE:
663 DPRINTF_EDIT_MSG32("EM_GETLINE");
664 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, lParam, unicode);
665 break;
667 case EM_LIMITTEXT16:
668 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
669 /* fall through */
670 case EM_SETLIMITTEXT:
671 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
672 EDIT_EM_SetLimitText(es, (INT)wParam);
673 break;
675 case EM_CANUNDO16:
676 DPRINTF_EDIT_MSG16("EM_CANUNDO");
677 /* fall through */
678 case EM_CANUNDO:
679 DPRINTF_EDIT_MSG32("EM_CANUNDO");
680 result = (LRESULT)EDIT_EM_CanUndo(es);
681 break;
683 case EM_UNDO16:
684 DPRINTF_EDIT_MSG16("EM_UNDO");
685 /* fall through */
686 case EM_UNDO:
687 /* fall through */
688 case WM_UNDO:
689 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
690 result = (LRESULT)EDIT_EM_Undo(wnd, es);
691 break;
693 case EM_FMTLINES16:
694 DPRINTF_EDIT_MSG16("EM_FMTLINES");
695 /* fall through */
696 case EM_FMTLINES:
697 DPRINTF_EDIT_MSG32("EM_FMTLINES");
698 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
699 break;
701 case EM_LINEFROMCHAR16:
702 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
703 /* fall through */
704 case EM_LINEFROMCHAR:
705 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
706 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
707 break;
709 /* message 0x00ca missing from specs */
711 case WM_USER+26:
712 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
713 /* fall through */
714 case 0x00ca:
715 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
716 if(unicode)
717 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
718 else
719 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
720 break;
722 case EM_SETTABSTOPS16:
723 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
724 result = (LRESULT)EDIT_EM_SetTabStops16(es, (INT)wParam, MapSL(lParam));
725 break;
726 case EM_SETTABSTOPS:
727 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
728 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
729 break;
731 case EM_SETPASSWORDCHAR16:
732 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
733 /* fall through */
734 case EM_SETPASSWORDCHAR:
736 WCHAR charW = 0;
737 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
739 if(unicode)
740 charW = (WCHAR)wParam;
741 else
743 CHAR charA = wParam;
744 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
747 EDIT_EM_SetPasswordChar(wnd, es, charW);
748 break;
751 case EM_EMPTYUNDOBUFFER16:
752 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
753 /* fall through */
754 case EM_EMPTYUNDOBUFFER:
755 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
756 EDIT_EM_EmptyUndoBuffer(es);
757 break;
759 case EM_GETFIRSTVISIBLELINE16:
760 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
761 result = es->y_offset;
762 break;
763 case EM_GETFIRSTVISIBLELINE:
764 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
765 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
766 break;
768 case EM_SETREADONLY16:
769 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
770 /* fall through */
771 case EM_SETREADONLY:
772 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
773 if (wParam) {
774 wnd->dwStyle |= ES_READONLY;
775 es->style |= ES_READONLY;
776 } else {
777 wnd->dwStyle &= ~ES_READONLY;
778 es->style &= ~ES_READONLY;
780 result = 1;
781 break;
783 case EM_SETWORDBREAKPROC16:
784 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
785 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
786 break;
787 case EM_SETWORDBREAKPROC:
788 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
789 EDIT_EM_SetWordBreakProc(wnd, es, lParam);
790 break;
792 case EM_GETWORDBREAKPROC16:
793 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
794 result = (LRESULT)es->word_break_proc16;
795 break;
796 case EM_GETWORDBREAKPROC:
797 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
798 result = (LRESULT)es->word_break_proc;
799 break;
801 case EM_GETPASSWORDCHAR16:
802 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
803 /* fall through */
804 case EM_GETPASSWORDCHAR:
806 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
808 if(unicode)
809 result = es->password_char;
810 else
812 WCHAR charW = es->password_char;
813 CHAR charA = 0;
814 WideCharToMultiByte(CP_ACP, 0, &charW, 1, &charA, 1, NULL, NULL);
815 result = charA;
817 break;
820 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
822 case EM_SETMARGINS:
823 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
824 EDIT_EM_SetMargins(es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
825 break;
827 case EM_GETMARGINS:
828 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
829 result = MAKELONG(es->left_margin, es->right_margin);
830 break;
832 case EM_GETLIMITTEXT:
833 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
834 result = es->buffer_limit;
835 break;
837 case EM_POSFROMCHAR:
838 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
839 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
840 break;
842 case EM_CHARFROMPOS:
843 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
844 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
845 break;
847 case WM_GETDLGCODE:
848 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
849 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
851 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
853 int vk = (int)((LPMSG)lParam)->wParam;
855 if ((wnd->dwStyle & ES_WANTRETURN) && vk == VK_RETURN)
857 result |= DLGC_WANTMESSAGE;
859 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
861 if (SendMessageW(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
862 result |= DLGC_WANTMESSAGE;
865 break;
867 case WM_CHAR:
869 WCHAR charW;
870 DPRINTF_EDIT_MSG32("WM_CHAR");
872 if(unicode)
873 charW = wParam;
874 else
876 CHAR charA = wParam;
877 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
880 if ((charW == VK_RETURN || charW == VK_ESCAPE) && es->hwndListBox)
882 if (SendMessageW(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
883 SendMessageW(wnd->parent->hwndSelf, WM_KEYDOWN, charW, 0);
884 break;
886 EDIT_WM_Char(wnd, es, charW);
887 break;
890 case WM_CLEAR:
891 DPRINTF_EDIT_MSG32("WM_CLEAR");
892 EDIT_WM_Clear(wnd, es);
893 break;
895 case WM_COMMAND:
896 DPRINTF_EDIT_MSG32("WM_COMMAND");
897 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
898 break;
900 case WM_CONTEXTMENU:
901 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
902 EDIT_WM_ContextMenu(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
903 break;
905 case WM_COPY:
906 DPRINTF_EDIT_MSG32("WM_COPY");
907 EDIT_WM_Copy(wnd, es);
908 break;
910 case WM_CREATE:
911 DPRINTF_EDIT_MSG32("WM_CREATE");
912 if(unicode)
913 result = EDIT_WM_Create(wnd, es, ((LPCREATESTRUCTW)lParam)->lpszName);
914 else
916 LPCSTR nameA = ((LPCREATESTRUCTA)lParam)->lpszName;
917 LPWSTR nameW = NULL;
918 if(nameA)
920 INT countW = MultiByteToWideChar(CP_ACP, 0, nameA, -1, NULL, 0);
921 if((nameW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
922 MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW);
924 result = EDIT_WM_Create(wnd, es, nameW);
925 if(nameW)
926 HeapFree(GetProcessHeap(), 0, nameW);
928 break;
930 case WM_CUT:
931 DPRINTF_EDIT_MSG32("WM_CUT");
932 EDIT_WM_Cut(wnd, es);
933 break;
935 case WM_ENABLE:
936 DPRINTF_EDIT_MSG32("WM_ENABLE");
937 es->bEnableState = (BOOL) wParam;
938 EDIT_UpdateText(wnd, NULL, TRUE);
939 break;
941 case WM_ERASEBKGND:
942 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
943 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
944 break;
946 case WM_GETFONT:
947 DPRINTF_EDIT_MSG32("WM_GETFONT");
948 result = (LRESULT)es->font;
949 break;
951 case WM_GETTEXT:
952 DPRINTF_EDIT_MSG32("WM_GETTEXT");
953 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, lParam, unicode);
954 break;
956 case WM_GETTEXTLENGTH:
957 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
958 result = strlenW(es->text);
959 break;
961 case WM_HSCROLL:
962 DPRINTF_EDIT_MSG32("WM_HSCROLL");
963 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
964 break;
966 case WM_KEYDOWN:
967 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
968 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam);
969 break;
971 case WM_KILLFOCUS:
972 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
973 result = EDIT_WM_KillFocus(wnd, es);
974 break;
976 case WM_LBUTTONDBLCLK:
977 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
978 result = EDIT_WM_LButtonDblClk(wnd, es);
979 break;
981 case WM_LBUTTONDOWN:
982 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
983 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
984 break;
986 case WM_LBUTTONUP:
987 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
988 result = EDIT_WM_LButtonUp(wnd->hwndSelf, es);
989 break;
991 case WM_MBUTTONDOWN:
992 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
993 result = EDIT_WM_MButtonDown(wnd);
994 break;
996 case WM_MOUSEACTIVATE:
998 * FIXME: maybe DefWindowProc() screws up, but it seems that
999 * modeless dialog boxes need this. If we don't do this, the focus
1000 * will _not_ be set by DefWindowProc() for edit controls in a
1001 * modeless dialog box ???
1003 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
1004 SetFocus(wnd->hwndSelf);
1005 result = MA_ACTIVATE;
1006 break;
1008 case WM_MOUSEMOVE:
1010 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
1012 result = EDIT_WM_MouseMove(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
1013 break;
1015 case WM_PAINT:
1016 DPRINTF_EDIT_MSG32("WM_PAINT");
1017 EDIT_WM_Paint(wnd, es, wParam);
1018 break;
1020 case WM_PASTE:
1021 DPRINTF_EDIT_MSG32("WM_PASTE");
1022 EDIT_WM_Paste(wnd, es);
1023 break;
1025 case WM_SETFOCUS:
1026 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
1027 EDIT_WM_SetFocus(wnd, es);
1028 break;
1030 case WM_SETFONT:
1031 DPRINTF_EDIT_MSG32("WM_SETFONT");
1032 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
1033 break;
1035 case WM_SETREDRAW:
1036 /* FIXME: actually set an internal flag and behave accordingly */
1037 break;
1039 case WM_SETTEXT:
1040 DPRINTF_EDIT_MSG32("WM_SETTEXT");
1041 EDIT_WM_SetText(wnd, es, lParam, unicode);
1042 result = TRUE;
1043 break;
1045 case WM_SIZE:
1046 DPRINTF_EDIT_MSG32("WM_SIZE");
1047 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
1048 break;
1050 case WM_SYSKEYDOWN:
1051 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
1052 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
1053 break;
1055 case WM_TIMER:
1056 DPRINTF_EDIT_MSG32("WM_TIMER");
1057 EDIT_WM_Timer(wnd, es);
1058 break;
1060 case WM_VSCROLL:
1061 DPRINTF_EDIT_MSG32("WM_VSCROLL");
1062 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam));
1063 break;
1065 case WM_MOUSEWHEEL:
1067 int gcWheelDelta = 0;
1068 UINT pulScrollLines = 3;
1069 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1071 if (wParam & (MK_SHIFT | MK_CONTROL)) {
1072 if(unicode)
1073 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
1074 else
1075 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
1076 break;
1078 gcWheelDelta -= SHIWORD(wParam);
1079 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1081 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
1082 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1083 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
1086 break;
1087 default:
1088 if(unicode)
1089 result = DefWindowProcW(wnd->hwndSelf, msg, wParam, lParam);
1090 else
1091 result = DefWindowProcA(wnd->hwndSelf, msg, wParam, lParam);
1092 break;
1094 EDIT_UnlockBuffer(wnd, es, FALSE);
1095 END:
1096 return result;
1099 /*********************************************************************
1101 * EditWndProcW (USER32.@)
1103 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1105 LRESULT res;
1106 WND *wndPtr = WIN_FindWndPtr(hWnd);
1108 res = EditWndProc_locked(wndPtr, uMsg, wParam, lParam, TRUE);
1110 WIN_ReleaseWndPtr(wndPtr);
1111 return res;
1114 /*********************************************************************
1116 * EditWndProcA (USER32.@)
1118 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1120 LRESULT res;
1121 WND *wndPtr = WIN_FindWndPtr(hWnd);
1123 res = EditWndProc_locked(wndPtr, uMsg, wParam, lParam, FALSE);
1125 WIN_ReleaseWndPtr(wndPtr);
1126 return res;
1129 /*********************************************************************
1131 * EDIT_BuildLineDefs_ML
1133 * Build linked list of text lines.
1134 * Lines can end with '\0' (last line), a character (if it is wrapped),
1135 * a soft return '\r\r\n' or a hard return '\r\n'
1138 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
1140 HDC dc;
1141 HFONT old_font = 0;
1142 LPWSTR start, cp;
1143 INT fw;
1144 LINEDEF *current_def;
1145 LINEDEF **previous_next;
1147 current_def = es->first_line_def;
1148 do {
1149 LINEDEF *next_def = current_def->next;
1150 HeapFree(GetProcessHeap(), 0, current_def);
1151 current_def = next_def;
1152 } while (current_def);
1153 es->line_count = 0;
1154 es->text_width = 0;
1156 dc = GetDC(wnd->hwndSelf);
1157 if (es->font)
1158 old_font = SelectObject(dc, es->font);
1160 fw = es->format_rect.right - es->format_rect.left;
1161 start = es->text;
1162 previous_next = &es->first_line_def;
1163 do {
1164 current_def = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
1165 current_def->next = NULL;
1166 cp = start;
1167 while (*cp) {
1168 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1169 break;
1170 cp++;
1172 if (!(*cp)) {
1173 current_def->ending = END_0;
1174 current_def->net_length = strlenW(start);
1175 } else if ((cp > start) && (*(cp - 1) == '\r')) {
1176 current_def->ending = END_SOFT;
1177 current_def->net_length = cp - start - 1;
1178 } else {
1179 current_def->ending = END_HARD;
1180 current_def->net_length = cp - start;
1182 current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1183 start, current_def->net_length,
1184 es->tabs_count, es->tabs));
1185 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1186 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
1187 INT next = 0;
1188 INT prev;
1189 do {
1190 prev = next;
1191 next = EDIT_CallWordBreakProc(es, start - es->text,
1192 prev + 1, current_def->net_length, WB_RIGHT);
1193 current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1194 start, next, es->tabs_count, es->tabs));
1195 } while (current_def->width <= fw);
1196 if (!prev) {
1197 next = 0;
1198 do {
1199 prev = next;
1200 next++;
1201 current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1202 start, next, es->tabs_count, es->tabs));
1203 } while (current_def->width <= fw);
1204 if (!prev)
1205 prev = 1;
1207 current_def->net_length = prev;
1208 current_def->ending = END_WRAP;
1209 current_def->width = (INT)LOWORD(GetTabbedTextExtentW(dc, start,
1210 current_def->net_length, es->tabs_count, es->tabs));
1212 switch (current_def->ending) {
1213 case END_SOFT:
1214 current_def->length = current_def->net_length + 3;
1215 break;
1216 case END_HARD:
1217 current_def->length = current_def->net_length + 2;
1218 break;
1219 case END_WRAP:
1220 case END_0:
1221 current_def->length = current_def->net_length;
1222 break;
1224 es->text_width = max(es->text_width, current_def->width);
1225 start += current_def->length;
1226 *previous_next = current_def;
1227 previous_next = &current_def->next;
1228 es->line_count++;
1229 } while (current_def->ending != END_0);
1230 if (es->font)
1231 SelectObject(dc, old_font);
1232 ReleaseDC(wnd->hwndSelf, dc);
1235 /*********************************************************************
1237 * EDIT_CalcLineWidth_SL
1240 static void EDIT_CalcLineWidth_SL(WND *wnd, EDITSTATE *es)
1242 es->text_width = SLOWORD(EDIT_EM_PosFromChar(wnd, es, strlenW(es->text), FALSE));
1245 /*********************************************************************
1247 * EDIT_CallWordBreakProc
1249 * Call appropriate WordBreakProc (internal or external).
1251 * Note: The "start" argument should always be an index refering
1252 * to es->text. The actual wordbreak proc might be
1253 * 16 bit, so we can't always pass any 32 bit LPSTR.
1254 * Hence we assume that es->text is the buffer that holds
1255 * the string under examination (we can decide this for ourselves).
1258 /* ### start build ### */
1259 extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1260 /* ### stop build ### */
1261 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
1263 INT ret, iWndsLocks;
1265 /* To avoid any deadlocks, all the locks on the windows structures
1266 must be suspended before the control is passed to the application */
1267 iWndsLocks = WIN_SuspendWndsLock();
1269 if (es->word_break_proc16) {
1270 HGLOBAL16 hglob16;
1271 SEGPTR segptr;
1272 INT countA;
1274 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1275 hglob16 = GlobalAlloc16(GMEM_MOVEABLE | GMEM_ZEROINIT, countA);
1276 segptr = K32WOWGlobalLock16(hglob16);
1277 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, MapSL(segptr), countA, NULL, NULL);
1278 ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1279 segptr, index, countA, action);
1280 GlobalUnlock16(hglob16);
1281 GlobalFree16(hglob16);
1283 else if (es->word_break_proc)
1285 if(es->is_unicode)
1287 EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
1289 TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1290 es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
1291 ret = wbpW(es->text + start, index, count, action);
1293 else
1295 EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
1296 INT countA;
1297 CHAR *textA;
1299 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1300 textA = HeapAlloc(GetProcessHeap(), 0, countA);
1301 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
1302 TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1303 es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
1304 ret = wbpA(textA, index, countA, action);
1305 HeapFree(GetProcessHeap(), 0, textA);
1308 else
1309 ret = EDIT_WordBreakProc(es->text + start, index, count, action);
1311 WIN_RestoreWndsLock(iWndsLocks);
1312 return ret;
1316 /*********************************************************************
1318 * EDIT_CharFromPos
1320 * Beware: This is not the function called on EM_CHARFROMPOS
1321 * The position _can_ be outside the formatting / client
1322 * rectangle
1323 * The return value is only the character index
1326 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1328 INT index;
1329 HDC dc;
1330 HFONT old_font = 0;
1332 if (es->style & ES_MULTILINE) {
1333 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1334 INT line_index = 0;
1335 LINEDEF *line_def = es->first_line_def;
1336 INT low, high;
1337 while ((line > 0) && line_def->next) {
1338 line_index += line_def->length;
1339 line_def = line_def->next;
1340 line--;
1342 x += es->x_offset - es->format_rect.left;
1343 if (x >= line_def->width) {
1344 if (after_wrap)
1345 *after_wrap = (line_def->ending == END_WRAP);
1346 return line_index + line_def->net_length;
1348 if (x <= 0) {
1349 if (after_wrap)
1350 *after_wrap = FALSE;
1351 return line_index;
1353 dc = GetDC(wnd->hwndSelf);
1354 if (es->font)
1355 old_font = SelectObject(dc, es->font);
1356 low = line_index + 1;
1357 high = line_index + line_def->net_length + 1;
1358 while (low < high - 1)
1360 INT mid = (low + high) / 2;
1361 if (LOWORD(GetTabbedTextExtentW(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1362 else low = mid;
1364 index = low;
1366 if (after_wrap)
1367 *after_wrap = ((index == line_index + line_def->net_length) &&
1368 (line_def->ending == END_WRAP));
1369 } else {
1370 LPWSTR text;
1371 SIZE size;
1372 if (after_wrap)
1373 *after_wrap = FALSE;
1374 x -= es->format_rect.left;
1375 if (!x)
1376 return es->x_offset;
1377 text = EDIT_GetPasswordPointer_SL(es);
1378 dc = GetDC(wnd->hwndSelf);
1379 if (es->font)
1380 old_font = SelectObject(dc, es->font);
1381 if (x < 0)
1383 INT low = 0;
1384 INT high = es->x_offset;
1385 while (low < high - 1)
1387 INT mid = (low + high) / 2;
1388 GetTextExtentPoint32W( dc, text + mid,
1389 es->x_offset - mid, &size );
1390 if (size.cx > -x) low = mid;
1391 else high = mid;
1393 index = low;
1395 else
1397 INT low = es->x_offset;
1398 INT high = strlenW(es->text) + 1;
1399 while (low < high - 1)
1401 INT mid = (low + high) / 2;
1402 GetTextExtentPoint32W( dc, text + es->x_offset,
1403 mid - es->x_offset, &size );
1404 if (size.cx > x) high = mid;
1405 else low = mid;
1407 index = low;
1409 if (es->style & ES_PASSWORD)
1410 HeapFree(GetProcessHeap(), 0, text);
1412 if (es->font)
1413 SelectObject(dc, old_font);
1414 ReleaseDC(wnd->hwndSelf, dc);
1415 return index;
1419 /*********************************************************************
1421 * EDIT_ConfinePoint
1423 * adjusts the point to be within the formatting rectangle
1424 * (so CharFromPos returns the nearest _visible_ character)
1427 static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y)
1429 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1430 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1434 /*********************************************************************
1436 * EDIT_GetLineRect
1438 * Calculates the bounding rectangle for a line from a starting
1439 * column to an ending column.
1442 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1444 INT line_index = EDIT_EM_LineIndex(es, line);
1446 if (es->style & ES_MULTILINE)
1447 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1448 else
1449 rc->top = es->format_rect.top;
1450 rc->bottom = rc->top + es->line_height;
1451 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1452 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1456 /*********************************************************************
1458 * EDIT_GetPasswordPointer_SL
1460 * note: caller should free the (optionally) allocated buffer
1463 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
1465 if (es->style & ES_PASSWORD) {
1466 INT len = strlenW(es->text);
1467 LPWSTR text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1468 text[len] = '\0';
1469 while(len) text[--len] = es->password_char;
1470 return text;
1471 } else
1472 return es->text;
1476 /*********************************************************************
1478 * EDIT_LockBuffer
1480 * This acts as a LOCAL_Lock(), but it locks only once. This way
1481 * you can call it whenever you like, without unlocking.
1484 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1486 if (!es) {
1487 ERR("no EDITSTATE ... please report\n");
1488 return;
1490 if (!es->text) {
1491 CHAR *textA = NULL;
1492 UINT countA = 0;
1493 BOOL _16bit = FALSE;
1495 if(es->hloc32W)
1497 if(es->hloc32A)
1499 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1500 textA = LocalLock(es->hloc32A);
1501 countA = strlen(textA) + 1;
1503 else if(es->hloc16)
1505 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1506 textA = LOCAL_Lock(wnd->hInstance, es->hloc16);
1507 countA = strlen(textA) + 1;
1508 _16bit = TRUE;
1511 else {
1512 ERR("no buffer ... please report\n");
1513 return;
1516 if(textA)
1518 HLOCAL hloc32W_new;
1519 UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
1520 TRACE("%d bytes translated to %d WCHARs\n", countA, countW_new);
1521 if(countW_new > es->buffer_size + 1)
1523 UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1524 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1525 hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1526 if(hloc32W_new)
1528 es->hloc32W = hloc32W_new;
1529 es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1530 TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1532 else
1533 WARN("FAILED! Will synchronize partially\n");
1537 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1538 es->text = LocalLock(es->hloc32W);
1540 if(textA)
1542 MultiByteToWideChar(CP_ACP, 0, textA, countA, es->text, es->buffer_size + 1);
1543 if(_16bit)
1544 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1545 else
1546 LocalUnlock(es->hloc32A);
1549 es->lock_count++;
1553 /*********************************************************************
1555 * EDIT_SL_InvalidateText
1557 * Called from EDIT_InvalidateText().
1558 * Does the job for single-line controls only.
1561 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1563 RECT line_rect;
1564 RECT rc;
1566 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1567 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1568 EDIT_UpdateText(wnd, &rc, FALSE);
1572 /*********************************************************************
1574 * EDIT_ML_InvalidateText
1576 * Called from EDIT_InvalidateText().
1577 * Does the job for multi-line controls only.
1580 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1582 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1583 INT sl = EDIT_EM_LineFromChar(es, start);
1584 INT el = EDIT_EM_LineFromChar(es, end);
1585 INT sc;
1586 INT ec;
1587 RECT rc1;
1588 RECT rcWnd;
1589 RECT rcLine;
1590 RECT rcUpdate;
1591 INT l;
1593 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1594 return;
1596 sc = start - EDIT_EM_LineIndex(es, sl);
1597 ec = end - EDIT_EM_LineIndex(es, el);
1598 if (sl < es->y_offset) {
1599 sl = es->y_offset;
1600 sc = 0;
1602 if (el > es->y_offset + vlc) {
1603 el = es->y_offset + vlc;
1604 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1606 GetClientRect(wnd->hwndSelf, &rc1);
1607 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1608 if (sl == el) {
1609 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1610 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1611 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1612 } else {
1613 EDIT_GetLineRect(wnd, es, sl, sc,
1614 EDIT_EM_LineLength(es,
1615 EDIT_EM_LineIndex(es, sl)),
1616 &rcLine);
1617 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1618 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1619 for (l = sl + 1 ; l < el ; l++) {
1620 EDIT_GetLineRect(wnd, es, l, 0,
1621 EDIT_EM_LineLength(es,
1622 EDIT_EM_LineIndex(es, l)),
1623 &rcLine);
1624 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1625 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1627 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1628 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1629 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1634 /*********************************************************************
1636 * EDIT_InvalidateText
1638 * Invalidate the text from offset start upto, but not including,
1639 * offset end. Useful for (re)painting the selection.
1640 * Regions outside the linewidth are not invalidated.
1641 * end == -1 means end == TextLength.
1642 * start and end need not be ordered.
1645 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1647 if (end == start)
1648 return;
1650 if (end == -1)
1651 end = strlenW(es->text);
1653 ORDER_INT(start, end);
1655 if (es->style & ES_MULTILINE)
1656 EDIT_ML_InvalidateText(wnd, es, start, end);
1657 else
1658 EDIT_SL_InvalidateText(wnd, es, start, end);
1662 /*********************************************************************
1664 * EDIT_MakeFit
1666 * Try to fit size + 1 characters in the buffer. Constrain to limits.
1669 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, UINT size)
1671 HLOCAL hNew32W;
1673 if (size <= es->buffer_size)
1674 return TRUE;
1675 if (size > es->buffer_limit) {
1676 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1677 return FALSE;
1679 if (size > es->buffer_limit)
1680 size = es->buffer_limit;
1682 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1684 /* Force edit to unlock it's buffer. es->text now NULL */
1685 EDIT_UnlockBuffer(wnd, es, TRUE);
1687 if (es->hloc32W) {
1688 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1689 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1690 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32W, hNew32W);
1691 es->hloc32W = hNew32W;
1692 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1696 EDIT_LockBuffer(wnd, es);
1698 if (es->buffer_size < size) {
1699 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1700 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1701 return FALSE;
1702 } else {
1703 TRACE("We now have %d+1\n", es->buffer_size);
1704 return TRUE;
1709 /*********************************************************************
1711 * EDIT_MakeUndoFit
1713 * Try to fit size + 1 bytes in the undo buffer.
1716 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1718 UINT alloc_size;
1720 if (size <= es->undo_buffer_size)
1721 return TRUE;
1723 TRACE("trying to ReAlloc to %d+1\n", size);
1725 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1726 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1727 es->undo_buffer_size = alloc_size/sizeof(WCHAR);
1728 return TRUE;
1730 else
1732 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1733 return FALSE;
1738 /*********************************************************************
1740 * EDIT_MoveBackward
1743 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1745 INT e = es->selection_end;
1747 if (e) {
1748 e--;
1749 if ((es->style & ES_MULTILINE) && e &&
1750 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1751 e--;
1752 if (e && (es->text[e - 1] == '\r'))
1753 e--;
1756 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1757 EDIT_EM_ScrollCaret(wnd, es);
1761 /*********************************************************************
1763 * EDIT_MoveDown_ML
1765 * Only for multi line controls
1766 * Move the caret one line down, on a column with the nearest
1767 * x coordinate on the screen (might be a different column).
1770 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1772 INT s = es->selection_start;
1773 INT e = es->selection_end;
1774 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1775 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1776 INT x = SLOWORD(pos);
1777 INT y = SHIWORD(pos);
1779 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1780 if (!extend)
1781 s = e;
1782 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1783 EDIT_EM_ScrollCaret(wnd, es);
1787 /*********************************************************************
1789 * EDIT_MoveEnd
1792 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1794 BOOL after_wrap = FALSE;
1795 INT e;
1797 /* Pass a high value in x to make sure of receiving the end of the line */
1798 if (es->style & ES_MULTILINE)
1799 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1800 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1801 else
1802 e = strlenW(es->text);
1803 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1804 EDIT_EM_ScrollCaret(wnd, es);
1808 /*********************************************************************
1810 * EDIT_MoveForward
1813 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1815 INT e = es->selection_end;
1817 if (es->text[e]) {
1818 e++;
1819 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1820 if (es->text[e] == '\n')
1821 e++;
1822 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1823 e += 2;
1826 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1827 EDIT_EM_ScrollCaret(wnd, es);
1831 /*********************************************************************
1833 * EDIT_MoveHome
1835 * Home key: move to beginning of line.
1838 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1840 INT e;
1842 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1843 if (es->style & ES_MULTILINE)
1844 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1845 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1846 else
1847 e = 0;
1848 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1849 EDIT_EM_ScrollCaret(wnd, es);
1853 /*********************************************************************
1855 * EDIT_MovePageDown_ML
1857 * Only for multi line controls
1858 * Move the caret one page down, on a column with the nearest
1859 * x coordinate on the screen (might be a different column).
1862 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1864 INT s = es->selection_start;
1865 INT e = es->selection_end;
1866 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1867 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1868 INT x = SLOWORD(pos);
1869 INT y = SHIWORD(pos);
1871 e = EDIT_CharFromPos(wnd, es, x,
1872 y + (es->format_rect.bottom - es->format_rect.top),
1873 &after_wrap);
1874 if (!extend)
1875 s = e;
1876 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1877 EDIT_EM_ScrollCaret(wnd, es);
1881 /*********************************************************************
1883 * EDIT_MovePageUp_ML
1885 * Only for multi line controls
1886 * Move the caret one page up, on a column with the nearest
1887 * x coordinate on the screen (might be a different column).
1890 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1892 INT s = es->selection_start;
1893 INT e = es->selection_end;
1894 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1895 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1896 INT x = SLOWORD(pos);
1897 INT y = SHIWORD(pos);
1899 e = EDIT_CharFromPos(wnd, es, x,
1900 y - (es->format_rect.bottom - es->format_rect.top),
1901 &after_wrap);
1902 if (!extend)
1903 s = e;
1904 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1905 EDIT_EM_ScrollCaret(wnd, es);
1909 /*********************************************************************
1911 * EDIT_MoveUp_ML
1913 * Only for multi line controls
1914 * Move the caret one line up, on a column with the nearest
1915 * x coordinate on the screen (might be a different column).
1918 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1920 INT s = es->selection_start;
1921 INT e = es->selection_end;
1922 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1923 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1924 INT x = SLOWORD(pos);
1925 INT y = SHIWORD(pos);
1927 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1928 if (!extend)
1929 s = e;
1930 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1931 EDIT_EM_ScrollCaret(wnd, es);
1935 /*********************************************************************
1937 * EDIT_MoveWordBackward
1940 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1942 INT s = es->selection_start;
1943 INT e = es->selection_end;
1944 INT l;
1945 INT ll;
1946 INT li;
1948 l = EDIT_EM_LineFromChar(es, e);
1949 ll = EDIT_EM_LineLength(es, e);
1950 li = EDIT_EM_LineIndex(es, l);
1951 if (e - li == 0) {
1952 if (l) {
1953 li = EDIT_EM_LineIndex(es, l - 1);
1954 e = li + EDIT_EM_LineLength(es, li);
1956 } else {
1957 e = li + (INT)EDIT_CallWordBreakProc(es,
1958 li, e - li, ll, WB_LEFT);
1960 if (!extend)
1961 s = e;
1962 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1963 EDIT_EM_ScrollCaret(wnd, es);
1967 /*********************************************************************
1969 * EDIT_MoveWordForward
1972 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
1974 INT s = es->selection_start;
1975 INT e = es->selection_end;
1976 INT l;
1977 INT ll;
1978 INT li;
1980 l = EDIT_EM_LineFromChar(es, e);
1981 ll = EDIT_EM_LineLength(es, e);
1982 li = EDIT_EM_LineIndex(es, l);
1983 if (e - li == ll) {
1984 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1985 e = EDIT_EM_LineIndex(es, l + 1);
1986 } else {
1987 e = li + EDIT_CallWordBreakProc(es,
1988 li, e - li + 1, ll, WB_RIGHT);
1990 if (!extend)
1991 s = e;
1992 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1993 EDIT_EM_ScrollCaret(wnd, es);
1997 /*********************************************************************
1999 * EDIT_PaintLine
2002 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
2004 INT s = es->selection_start;
2005 INT e = es->selection_end;
2006 INT li;
2007 INT ll;
2008 INT x;
2009 INT y;
2010 LRESULT pos;
2012 if (es->style & ES_MULTILINE) {
2013 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2014 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2015 return;
2016 } else if (line)
2017 return;
2019 TRACE("line=%d\n", line);
2021 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(es, line), FALSE);
2022 x = SLOWORD(pos);
2023 y = SHIWORD(pos);
2024 li = EDIT_EM_LineIndex(es, line);
2025 ll = EDIT_EM_LineLength(es, li);
2026 s = es->selection_start;
2027 e = es->selection_end;
2028 ORDER_INT(s, e);
2029 s = min(li + ll, max(li, s));
2030 e = min(li + ll, max(li, e));
2031 if (rev && (s != e) &&
2032 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2033 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2034 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2035 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2036 } else
2037 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2041 /*********************************************************************
2043 * EDIT_PaintText
2046 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2048 COLORREF BkColor;
2049 COLORREF TextColor;
2050 INT ret;
2051 INT li;
2052 SIZE size;
2054 if (!count)
2055 return 0;
2056 BkColor = GetBkColor(dc);
2057 TextColor = GetTextColor(dc);
2058 if (rev) {
2059 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2060 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2062 li = EDIT_EM_LineIndex(es, line);
2063 if (es->style & ES_MULTILINE) {
2064 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2065 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2066 } else {
2067 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2068 TextOutW(dc, x, y, text + li + col, count);
2069 GetTextExtentPoint32W(dc, text + li + col, count, &size);
2070 ret = size.cx;
2071 if (es->style & ES_PASSWORD)
2072 HeapFree(GetProcessHeap(), 0, text);
2074 if (rev) {
2075 SetBkColor(dc, BkColor);
2076 SetTextColor(dc, TextColor);
2078 return ret;
2082 /*********************************************************************
2084 * EDIT_SetCaretPos
2087 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
2088 BOOL after_wrap)
2090 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
2091 SetCaretPos(SLOWORD(res), SHIWORD(res));
2095 /*********************************************************************
2097 * EDIT_SetRectNP
2099 * note: this is not (exactly) the handler called on EM_SETRECTNP
2100 * it is also used to set the rect of a single line control
2103 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
2105 CopyRect(&es->format_rect, rc);
2106 if (es->style & WS_BORDER) {
2107 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
2108 if(TWEAK_WineLook == WIN31_LOOK)
2109 bw += 2;
2110 es->format_rect.left += bw;
2111 es->format_rect.top += bw;
2112 es->format_rect.right -= bw;
2113 es->format_rect.bottom -= bw;
2115 es->format_rect.left += es->left_margin;
2116 es->format_rect.right -= es->right_margin;
2117 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2118 if (es->style & ES_MULTILINE)
2120 INT fw, vlc, max_x_offset, max_y_offset;
2122 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2123 es->format_rect.bottom = es->format_rect.top + max(1, vlc) * es->line_height;
2125 /* correct es->x_offset */
2126 fw = es->format_rect.right - es->format_rect.left;
2127 max_x_offset = es->text_width - fw;
2128 if(max_x_offset < 0) max_x_offset = 0;
2129 if(es->x_offset > max_x_offset)
2130 es->x_offset = max_x_offset;
2132 /* correct es->y_offset */
2133 max_y_offset = es->line_count - vlc;
2134 if(max_y_offset < 0) max_y_offset = 0;
2135 if(es->y_offset > max_y_offset)
2136 es->y_offset = max_y_offset;
2138 /* force scroll info update */
2139 EDIT_UpdateScrollInfo(wnd, es);
2141 else
2142 /* Windows doesn't care to fix text placement for SL controls */
2143 es->format_rect.bottom = es->format_rect.top + es->line_height;
2145 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2146 EDIT_BuildLineDefs_ML(wnd, es);
2150 /*********************************************************************
2152 * EDIT_UnlockBuffer
2155 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
2157 if (!es) {
2158 ERR("no EDITSTATE ... please report\n");
2159 return;
2161 if (!es->lock_count) {
2162 ERR("lock_count == 0 ... please report\n");
2163 return;
2165 if (!es->text) {
2166 ERR("es->text == 0 ... please report\n");
2167 return;
2170 if (force || (es->lock_count == 1)) {
2171 if (es->hloc32W) {
2172 CHAR *textA = NULL;
2173 BOOL _16bit = FALSE;
2174 UINT countA = 0;
2175 UINT countW = strlenW(es->text) + 1;
2177 if(es->hloc32A)
2179 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2180 TRACE("Synchronizing with 32-bit ANSI buffer\n");
2181 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2182 countA = LocalSize(es->hloc32A);
2183 if(countA_new > countA)
2185 HLOCAL hloc32A_new;
2186 UINT alloc_size = ROUND_TO_GROW(countA_new);
2187 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2188 hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2189 if(hloc32A_new)
2191 es->hloc32A = hloc32A_new;
2192 countA = LocalSize(hloc32A_new);
2193 TRACE("Real new size %d bytes\n", countA);
2195 else
2196 WARN("FAILED! Will synchronize partially\n");
2198 textA = LocalLock(es->hloc32A);
2200 else if(es->hloc16)
2202 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2203 TRACE("Synchronizing with 16-bit ANSI buffer\n");
2204 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2205 countA = LOCAL_Size(wnd->hInstance, es->hloc16);
2206 if(countA_new > countA)
2208 HLOCAL16 hloc16_new;
2209 UINT alloc_size = ROUND_TO_GROW(countA_new);
2210 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2211 hloc16_new = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2212 if(hloc16_new)
2214 es->hloc16 = hloc16_new;
2215 countA = LOCAL_Size(wnd->hInstance, hloc16_new);
2216 TRACE("Real new size %d bytes\n", countA);
2218 else
2219 WARN("FAILED! Will synchronize partially\n");
2221 textA = LOCAL_Lock(wnd->hInstance, es->hloc16);
2222 _16bit = TRUE;
2225 if(textA)
2227 WideCharToMultiByte(CP_ACP, 0, es->text, countW, textA, countA, NULL, NULL);
2228 if(_16bit)
2229 LOCAL_Unlock(wnd->hInstance, es->hloc16);
2230 else
2231 LocalUnlock(es->hloc32A);
2234 LocalUnlock(es->hloc32W);
2235 es->text = NULL;
2237 else {
2238 ERR("no buffer ... please report\n");
2239 return;
2242 es->lock_count--;
2246 /*********************************************************************
2248 * EDIT_UpdateScrollInfo
2251 static void EDIT_UpdateScrollInfo(WND *wnd, EDITSTATE *es)
2253 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2255 SCROLLINFO si;
2256 si.cbSize = sizeof(SCROLLINFO);
2257 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2258 si.nMin = 0;
2259 si.nMax = es->line_count - 1;
2260 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2261 si.nPos = es->y_offset;
2262 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2263 si.nMin, si.nMax, si.nPage, si.nPos);
2264 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
2267 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2269 SCROLLINFO si;
2270 si.cbSize = sizeof(SCROLLINFO);
2271 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2272 si.nMin = 0;
2273 si.nMax = es->text_width - 1;
2274 si.nPage = es->format_rect.right - es->format_rect.left;
2275 si.nPos = es->x_offset;
2276 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2277 si.nMin, si.nMax, si.nPage, si.nPos);
2278 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
2282 /*********************************************************************
2284 * EDIT_WordBreakProc
2286 * Find the beginning of words.
2287 * Note: unlike the specs for a WordBreakProc, this function only
2288 * allows to be called without linebreaks between s[0] upto
2289 * s[count - 1]. Remember it is only called
2290 * internally, so we can decide this for ourselves.
2293 static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action)
2295 INT ret = 0;
2297 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
2299 if(!s) return 0;
2301 switch (action) {
2302 case WB_LEFT:
2303 if (!count)
2304 break;
2305 if (index)
2306 index--;
2307 if (s[index] == ' ') {
2308 while (index && (s[index] == ' '))
2309 index--;
2310 if (index) {
2311 while (index && (s[index] != ' '))
2312 index--;
2313 if (s[index] == ' ')
2314 index++;
2316 } else {
2317 while (index && (s[index] != ' '))
2318 index--;
2319 if (s[index] == ' ')
2320 index++;
2322 ret = index;
2323 break;
2324 case WB_RIGHT:
2325 if (!count)
2326 break;
2327 if (index)
2328 index--;
2329 if (s[index] == ' ')
2330 while ((index < count) && (s[index] == ' ')) index++;
2331 else {
2332 while (s[index] && (s[index] != ' ') && (index < count))
2333 index++;
2334 while ((s[index] == ' ') && (index < count)) index++;
2336 ret = index;
2337 break;
2338 case WB_ISDELIMITER:
2339 ret = (s[index] == ' ');
2340 break;
2341 default:
2342 ERR("unknown action code, please report !\n");
2343 break;
2345 return ret;
2349 /*********************************************************************
2351 * EM_CHARFROMPOS
2353 * returns line number (not index) in high-order word of result.
2354 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2355 * to Richedit, not to the edit control. Original documentation is valid.
2356 * FIXME: do the specs mean to return -1 if outside client area or
2357 * if outside formatting rectangle ???
2360 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
2362 POINT pt;
2363 RECT rc;
2364 INT index;
2366 pt.x = x;
2367 pt.y = y;
2368 GetClientRect(wnd->hwndSelf, &rc);
2369 if (!PtInRect(&rc, pt))
2370 return -1;
2372 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
2373 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2377 /*********************************************************************
2379 * EM_FMTLINES
2381 * Enable or disable soft breaks.
2383 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2385 es->flags &= ~EF_USE_SOFTBRK;
2386 if (add_eol) {
2387 es->flags |= EF_USE_SOFTBRK;
2388 FIXME("soft break enabled, not implemented\n");
2390 return add_eol;
2394 /*********************************************************************
2396 * EM_GETHANDLE
2398 * Hopefully this won't fire back at us.
2399 * We always start with a fixed buffer in the local heap.
2400 * Despite of the documentation says that the local heap is used
2401 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2402 * buffer on the local heap.
2405 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2407 HLOCAL hLocal;
2409 if (!(es->style & ES_MULTILINE))
2410 return 0;
2412 if(es->is_unicode)
2413 hLocal = es->hloc32W;
2414 else
2416 if(!es->hloc32A)
2418 CHAR *textA;
2419 UINT countA, alloc_size;
2420 TRACE("Allocating 32-bit ANSI alias buffer\n");
2421 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2422 alloc_size = ROUND_TO_GROW(countA);
2423 if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2425 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2426 return 0;
2428 textA = LocalLock(es->hloc32A);
2429 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2430 LocalUnlock(es->hloc32A);
2432 hLocal = es->hloc32A;
2435 TRACE("Returning %04X, LocalSize() = %d\n", hLocal, LocalSize(hLocal));
2436 return hLocal;
2440 /*********************************************************************
2442 * EM_GETHANDLE16
2444 * Hopefully this won't fire back at us.
2445 * We always start with a buffer in 32 bit linear memory.
2446 * However, with this message a 16 bit application requests
2447 * a handle of 16 bit local heap memory, where it expects to find
2448 * the text.
2449 * It's a pitty that from this moment on we have to use this
2450 * local heap, because applications may rely on the handle
2451 * in the future.
2453 * In this function we'll try to switch to local heap.
2455 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2457 CHAR *textA;
2458 UINT countA, alloc_size;
2460 if (!(es->style & ES_MULTILINE))
2461 return 0;
2463 if (es->hloc16)
2464 return es->hloc16;
2466 if (!LOCAL_HeapSize(wnd->hInstance)) {
2467 if (!LocalInit16(wnd->hInstance, 0,
2468 GlobalSize16(wnd->hInstance))) {
2469 ERR("could not initialize local heap\n");
2470 return 0;
2472 TRACE("local heap initialized\n");
2475 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2476 alloc_size = ROUND_TO_GROW(countA);
2478 TRACE("Allocating 16-bit ANSI alias buffer\n");
2479 if (!(es->hloc16 = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) {
2480 ERR("could not allocate new 16 bit buffer\n");
2481 return 0;
2484 if (!(textA = (LPSTR)LOCAL_Lock(wnd->hInstance, es->hloc16))) {
2485 ERR("could not lock new 16 bit buffer\n");
2486 LOCAL_Free(wnd->hInstance, es->hloc16);
2487 es->hloc16 = 0;
2488 return 0;
2491 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2492 LOCAL_Unlock(wnd->hInstance, es->hloc16);
2494 TRACE("Returning %04X, LocalSize() = %d\n", es->hloc16, LOCAL_Size(wnd->hInstance, es->hloc16));
2495 return es->hloc16;
2499 /*********************************************************************
2501 * EM_GETLINE
2504 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode)
2506 LPWSTR src;
2507 INT line_len, dst_len;
2508 INT i;
2510 if (es->style & ES_MULTILINE) {
2511 if (line >= es->line_count)
2512 return 0;
2513 } else
2514 line = 0;
2515 i = EDIT_EM_LineIndex(es, line);
2516 src = es->text + i;
2517 line_len = EDIT_EM_LineLength(es, i);
2518 dst_len = *(WORD *)lParam;
2519 if(unicode)
2521 LPWSTR dst = (LPWSTR)lParam;
2522 if(dst_len <= line_len)
2524 memcpy(dst, src, dst_len * sizeof(WCHAR));
2525 return dst_len;
2527 else /* Append 0 if enough space */
2529 memcpy(dst, src, line_len * sizeof(WCHAR));
2530 dst[line_len] = 0;
2531 return line_len;
2534 else
2536 LPSTR dst = (LPSTR)lParam;
2537 INT ret;
2538 ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, dst, dst_len, NULL, NULL);
2539 if(!ret) /* Insufficient buffer size */
2540 return dst_len;
2541 if(ret < dst_len) /* Append 0 if enough space */
2542 dst[ret] = 0;
2543 return ret;
2548 /*********************************************************************
2550 * EM_GETSEL
2553 static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end)
2555 UINT s = es->selection_start;
2556 UINT e = es->selection_end;
2558 ORDER_UINT(s, e);
2559 if (start)
2560 *start = s;
2561 if (end)
2562 *end = e;
2563 return MAKELONG(s, e);
2567 /*********************************************************************
2569 * EM_GETTHUMB
2571 * FIXME: is this right ? (or should it be only VSCROLL)
2572 * (and maybe only for edit controls that really have their
2573 * own scrollbars) (and maybe only for multiline controls ?)
2574 * All in all: very poorly documented
2577 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2579 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0),
2580 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0));
2584 /*********************************************************************
2586 * EM_LINEFROMCHAR
2589 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
2591 INT line;
2592 LINEDEF *line_def;
2594 if (!(es->style & ES_MULTILINE))
2595 return 0;
2596 if (index > (INT)strlenW(es->text))
2597 return es->line_count - 1;
2598 if (index == -1)
2599 index = min(es->selection_start, es->selection_end);
2601 line = 0;
2602 line_def = es->first_line_def;
2603 index -= line_def->length;
2604 while ((index >= 0) && line_def->next) {
2605 line++;
2606 line_def = line_def->next;
2607 index -= line_def->length;
2609 return line;
2613 /*********************************************************************
2615 * EM_LINEINDEX
2618 static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line)
2620 INT line_index;
2621 LINEDEF *line_def;
2623 if (!(es->style & ES_MULTILINE))
2624 return 0;
2625 if (line >= es->line_count)
2626 return -1;
2628 line_index = 0;
2629 line_def = es->first_line_def;
2630 if (line == -1) {
2631 INT index = es->selection_end - line_def->length;
2632 while ((index >= 0) && line_def->next) {
2633 line_index += line_def->length;
2634 line_def = line_def->next;
2635 index -= line_def->length;
2637 } else {
2638 while (line > 0) {
2639 line_index += line_def->length;
2640 line_def = line_def->next;
2641 line--;
2644 return line_index;
2648 /*********************************************************************
2650 * EM_LINELENGTH
2653 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
2655 LINEDEF *line_def;
2657 if (!(es->style & ES_MULTILINE))
2658 return strlenW(es->text);
2660 if (index == -1) {
2661 /* get the number of remaining non-selected chars of selected lines */
2662 INT32 li;
2663 INT32 count;
2664 li = EDIT_EM_LineFromChar(es, es->selection_start);
2665 /* # chars before start of selection area */
2666 count = es->selection_start - EDIT_EM_LineIndex(es, li);
2667 li = EDIT_EM_LineFromChar(es, es->selection_end);
2668 /* # chars after end of selection */
2669 count += EDIT_EM_LineIndex(es, li) +
2670 EDIT_EM_LineLength(es, li) - es->selection_end;
2671 return count;
2673 line_def = es->first_line_def;
2674 index -= line_def->length;
2675 while ((index >= 0) && line_def->next) {
2676 line_def = line_def->next;
2677 index -= line_def->length;
2679 return line_def->net_length;
2683 /*********************************************************************
2685 * EM_LINESCROLL
2687 * NOTE: dx is in average character widths, dy - in lines;
2690 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2692 if (!(es->style & ES_MULTILINE))
2693 return FALSE;
2695 dx *= es->char_width;
2696 return EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
2699 /*********************************************************************
2701 * EDIT_EM_LineScroll_internal
2703 * Version of EDIT_EM_LineScroll for internal use.
2704 * It doesn't refuse if ES_MULTILINE is set and assumes that
2705 * dx is in pixels, dy - in lines.
2708 static BOOL EDIT_EM_LineScroll_internal(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2710 INT nyoff;
2711 INT x_offset_in_pixels;
2713 if (es->style & ES_MULTILINE)
2715 x_offset_in_pixels = es->x_offset;
2717 else
2719 dy = 0;
2720 x_offset_in_pixels = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->x_offset, FALSE));
2723 if (-dx > x_offset_in_pixels)
2724 dx = -x_offset_in_pixels;
2725 if (dx > es->text_width - x_offset_in_pixels)
2726 dx = es->text_width - x_offset_in_pixels;
2727 nyoff = max(0, es->y_offset + dy);
2728 if (nyoff >= es->line_count)
2729 nyoff = es->line_count - 1;
2730 dy = (es->y_offset - nyoff) * es->line_height;
2731 if (dx || dy) {
2732 RECT rc1;
2733 RECT rc;
2735 es->y_offset = nyoff;
2736 if(es->style & ES_MULTILINE)
2737 es->x_offset += dx;
2738 else
2739 es->x_offset += dx / es->char_width;
2741 GetClientRect(wnd->hwndSelf, &rc1);
2742 IntersectRect(&rc, &rc1, &es->format_rect);
2743 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2744 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2745 /* force scroll info update */
2746 EDIT_UpdateScrollInfo(wnd, es);
2748 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2749 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2750 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2751 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2752 return TRUE;
2756 /*********************************************************************
2758 * EM_POSFROMCHAR
2761 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2763 INT len = strlenW(es->text);
2764 INT l;
2765 INT li;
2766 INT x;
2767 INT y = 0;
2768 HDC dc;
2769 HFONT old_font = 0;
2770 SIZE size;
2772 index = min(index, len);
2773 dc = GetDC(wnd->hwndSelf);
2774 if (es->font)
2775 old_font = SelectObject(dc, es->font);
2776 if (es->style & ES_MULTILINE) {
2777 l = EDIT_EM_LineFromChar(es, index);
2778 y = (l - es->y_offset) * es->line_height;
2779 li = EDIT_EM_LineIndex(es, l);
2780 if (after_wrap && (li == index) && l) {
2781 INT l2 = l - 1;
2782 LINEDEF *line_def = es->first_line_def;
2783 while (l2) {
2784 line_def = line_def->next;
2785 l2--;
2787 if (line_def->ending == END_WRAP) {
2788 l--;
2789 y -= es->line_height;
2790 li = EDIT_EM_LineIndex(es, l);
2793 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
2794 es->tabs_count, es->tabs)) - es->x_offset;
2795 } else {
2796 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2797 if (index < es->x_offset) {
2798 GetTextExtentPoint32W(dc, text + index,
2799 es->x_offset - index, &size);
2800 x = -size.cx;
2801 } else {
2802 GetTextExtentPoint32W(dc, text + es->x_offset,
2803 index - es->x_offset, &size);
2804 x = size.cx;
2806 y = 0;
2807 if (es->style & ES_PASSWORD)
2808 HeapFree(GetProcessHeap(), 0, text);
2810 x += es->format_rect.left;
2811 y += es->format_rect.top;
2812 if (es->font)
2813 SelectObject(dc, old_font);
2814 ReleaseDC(wnd->hwndSelf, dc);
2815 return MAKELONG((INT16)x, (INT16)y);
2819 /*********************************************************************
2821 * EM_REPLACESEL
2823 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2826 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update)
2828 UINT strl = strlenW(lpsz_replace);
2829 UINT tl = strlenW(es->text);
2830 UINT utl;
2831 UINT s;
2832 UINT e;
2833 UINT i;
2834 LPWSTR p;
2836 TRACE("%s, can_undo %d, send_update %d\n",
2837 debugstr_w(lpsz_replace), can_undo, send_update);
2839 s = es->selection_start;
2840 e = es->selection_end;
2842 if ((s == e) && !strl)
2843 return;
2845 ORDER_UINT(s, e);
2847 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2848 return;
2850 if (e != s) {
2851 /* there is something to be deleted */
2852 if (can_undo) {
2853 utl = strlenW(es->undo_text);
2854 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2855 /* undo-buffer is extended to the right */
2856 EDIT_MakeUndoFit(es, utl + e - s);
2857 strncpyW(es->undo_text + utl, es->text + s, e - s + 1);
2858 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2859 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2860 /* undo-buffer is extended to the left */
2861 EDIT_MakeUndoFit(es, utl + e - s);
2862 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2863 p[e - s] = p[0];
2864 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2865 p[i] = (es->text + s)[i];
2866 es->undo_position = s;
2867 } else {
2868 /* new undo-buffer */
2869 EDIT_MakeUndoFit(es, e - s);
2870 strncpyW(es->undo_text, es->text + s, e - s + 1);
2871 es->undo_text[e - s] = 0; /* ensure 0 termination */
2872 es->undo_position = s;
2874 /* any deletion makes the old insertion-undo invalid */
2875 es->undo_insert_count = 0;
2876 } else
2877 EDIT_EM_EmptyUndoBuffer(es);
2879 /* now delete */
2880 strcpyW(es->text + s, es->text + e);
2882 if (strl) {
2883 /* there is an insertion */
2884 if (can_undo) {
2885 if ((s == es->undo_position) ||
2886 ((es->undo_insert_count) &&
2887 (s == es->undo_position + es->undo_insert_count)))
2889 * insertion is new and at delete position or
2890 * an extension to either left or right
2892 es->undo_insert_count += strl;
2893 else {
2894 /* new insertion undo */
2895 es->undo_position = s;
2896 es->undo_insert_count = strl;
2897 /* new insertion makes old delete-buffer invalid */
2898 *es->undo_text = '\0';
2900 } else
2901 EDIT_EM_EmptyUndoBuffer(es);
2903 /* now insert */
2904 tl = strlenW(es->text);
2905 for (p = es->text + tl ; p >= es->text + s ; p--)
2906 p[strl] = p[0];
2907 for (i = 0 , p = es->text + s ; i < strl ; i++)
2908 p[i] = lpsz_replace[i];
2909 if(es->style & ES_UPPERCASE)
2910 CharUpperBuffW(p, strl);
2911 else if(es->style & ES_LOWERCASE)
2912 CharLowerBuffW(p, strl);
2913 s += strl;
2915 /* FIXME: really inefficient */
2916 if (es->style & ES_MULTILINE)
2917 EDIT_BuildLineDefs_ML(wnd, es);
2918 else
2919 EDIT_CalcLineWidth_SL(wnd, es);
2921 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2922 es->flags |= EF_MODIFIED;
2923 if (send_update) es->flags |= EF_UPDATE;
2924 EDIT_EM_ScrollCaret(wnd, es);
2926 /* force scroll info update */
2927 EDIT_UpdateScrollInfo(wnd, es);
2929 /* FIXME: really inefficient */
2930 EDIT_UpdateText(wnd, NULL, TRUE);
2934 /*********************************************************************
2936 * EM_SCROLL
2939 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
2941 INT dy;
2943 if (!(es->style & ES_MULTILINE))
2944 return (LRESULT)FALSE;
2946 dy = 0;
2948 switch (action) {
2949 case SB_LINEUP:
2950 if (es->y_offset)
2951 dy = -1;
2952 break;
2953 case SB_LINEDOWN:
2954 if (es->y_offset < es->line_count - 1)
2955 dy = 1;
2956 break;
2957 case SB_PAGEUP:
2958 if (es->y_offset)
2959 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2960 break;
2961 case SB_PAGEDOWN:
2962 if (es->y_offset < es->line_count - 1)
2963 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2964 break;
2965 default:
2966 return (LRESULT)FALSE;
2968 if (dy) {
2969 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2970 /* check if we are going to move too far */
2971 if(es->y_offset + dy > es->line_count - vlc)
2972 dy = es->line_count - vlc - es->y_offset;
2974 /* Notification is done in EDIT_EM_LineScroll */
2975 if(dy)
2976 EDIT_EM_LineScroll(wnd, es, 0, dy);
2978 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2982 /*********************************************************************
2984 * EM_SCROLLCARET
2987 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
2989 if (es->style & ES_MULTILINE) {
2990 INT l;
2991 INT li;
2992 INT vlc;
2993 INT ww;
2994 INT cw = es->char_width;
2995 INT x;
2996 INT dy = 0;
2997 INT dx = 0;
2999 l = EDIT_EM_LineFromChar(es, es->selection_end);
3000 li = EDIT_EM_LineIndex(es, l);
3001 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
3002 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3003 if (l >= es->y_offset + vlc)
3004 dy = l - vlc + 1 - es->y_offset;
3005 if (l < es->y_offset)
3006 dy = l - es->y_offset;
3007 ww = es->format_rect.right - es->format_rect.left;
3008 if (x < es->format_rect.left)
3009 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
3010 if (x > es->format_rect.right)
3011 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
3012 if (dy || dx)
3014 /* check if we are going to move too far */
3015 if(es->x_offset + dx + ww > es->text_width)
3016 dx = es->text_width - ww - es->x_offset;
3017 if(dx || dy)
3018 EDIT_EM_LineScroll_internal(wnd, es, dx, dy);
3020 } else {
3021 INT x;
3022 INT goal;
3023 INT format_width;
3025 if (!(es->style & ES_AUTOHSCROLL))
3026 return;
3028 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3029 format_width = es->format_rect.right - es->format_rect.left;
3030 if (x < es->format_rect.left) {
3031 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
3032 do {
3033 es->x_offset--;
3034 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3035 } while ((x < goal) && es->x_offset);
3036 /* FIXME: use ScrollWindow() somehow to improve performance */
3037 EDIT_UpdateText(wnd, NULL, TRUE);
3038 } else if (x > es->format_rect.right) {
3039 INT x_last;
3040 INT len = strlenW(es->text);
3041 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
3042 do {
3043 es->x_offset++;
3044 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
3045 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
3046 } while ((x > goal) && (x_last > es->format_rect.right));
3047 /* FIXME: use ScrollWindow() somehow to improve performance */
3048 EDIT_UpdateText(wnd, NULL, TRUE);
3052 if(es->flags & EF_FOCUSED)
3053 EDIT_SetCaretPos(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3057 /*********************************************************************
3059 * EM_SETHANDLE
3061 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3064 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
3066 if (!(es->style & ES_MULTILINE))
3067 return;
3069 if (!hloc) {
3070 WARN("called with NULL handle\n");
3071 return;
3074 EDIT_UnlockBuffer(wnd, es, TRUE);
3076 if(es->hloc16)
3078 LOCAL_Free(wnd->hInstance, es->hloc16);
3079 es->hloc16 = (HLOCAL16)NULL;
3082 if(es->is_unicode)
3084 if(es->hloc32A)
3086 LocalFree(es->hloc32A);
3087 es->hloc32A = (HLOCAL)NULL;
3089 es->hloc32W = hloc;
3091 else
3093 INT countW, countA;
3094 HLOCAL hloc32W_new;
3095 WCHAR *textW;
3096 CHAR *textA;
3098 countA = LocalSize(hloc);
3099 textA = LocalLock(hloc);
3100 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3101 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3103 ERR("Could not allocate new unicode buffer\n");
3104 return;
3106 textW = LocalLock(hloc32W_new);
3107 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3108 LocalUnlock(hloc32W_new);
3109 LocalUnlock(hloc);
3111 if(es->hloc32W)
3112 LocalFree(es->hloc32W);
3114 es->hloc32W = hloc32W_new;
3115 es->hloc32A = hloc;
3118 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3120 EDIT_LockBuffer(wnd, es);
3122 es->x_offset = es->y_offset = 0;
3123 es->selection_start = es->selection_end = 0;
3124 EDIT_EM_EmptyUndoBuffer(es);
3125 es->flags &= ~EF_MODIFIED;
3126 es->flags &= ~EF_UPDATE;
3127 EDIT_BuildLineDefs_ML(wnd, es);
3128 EDIT_UpdateText(wnd, NULL, TRUE);
3129 EDIT_EM_ScrollCaret(wnd, es);
3130 /* force scroll info update */
3131 EDIT_UpdateScrollInfo(wnd, es);
3135 /*********************************************************************
3137 * EM_SETHANDLE16
3139 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3142 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
3144 INT countW, countA;
3145 HLOCAL hloc32W_new;
3146 WCHAR *textW;
3147 CHAR *textA;
3149 if (!(es->style & ES_MULTILINE))
3150 return;
3152 if (!hloc) {
3153 WARN("called with NULL handle\n");
3154 return;
3157 EDIT_UnlockBuffer(wnd, es, TRUE);
3159 if(es->hloc32A)
3161 LocalFree(es->hloc32A);
3162 es->hloc32A = (HLOCAL)NULL;
3165 countA = LOCAL_Size(wnd->hInstance, hloc);
3166 textA = LOCAL_Lock(wnd->hInstance, hloc);
3167 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3168 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3170 ERR("Could not allocate new unicode buffer\n");
3171 return;
3173 textW = LocalLock(hloc32W_new);
3174 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3175 LocalUnlock(hloc32W_new);
3176 LOCAL_Unlock(wnd->hInstance, hloc);
3178 if(es->hloc32W)
3179 LocalFree(es->hloc32W);
3181 es->hloc32W = hloc32W_new;
3182 es->hloc16 = hloc;
3184 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3186 EDIT_LockBuffer(wnd, es);
3188 es->x_offset = es->y_offset = 0;
3189 es->selection_start = es->selection_end = 0;
3190 EDIT_EM_EmptyUndoBuffer(es);
3191 es->flags &= ~EF_MODIFIED;
3192 es->flags &= ~EF_UPDATE;
3193 EDIT_BuildLineDefs_ML(wnd, es);
3194 EDIT_UpdateText(wnd, NULL, TRUE);
3195 EDIT_EM_ScrollCaret(wnd, es);
3196 /* force scroll info update */
3197 EDIT_UpdateScrollInfo(wnd, es);
3201 /*********************************************************************
3203 * EM_SETLIMITTEXT
3205 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3206 * However, the windows version is not complied to yet in all of edit.c
3209 static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit)
3211 if (es->style & ES_MULTILINE) {
3212 if (limit)
3213 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
3214 else
3215 es->buffer_limit = BUFLIMIT_MULTI;
3216 } else {
3217 if (limit)
3218 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
3219 else
3220 es->buffer_limit = BUFLIMIT_SINGLE;
3225 /*********************************************************************
3227 * EM_SETMARGINS
3229 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3230 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3231 * of the char's width, according to the new docs.
3234 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
3235 INT left, INT right)
3237 if (action & EC_LEFTMARGIN) {
3238 if (left != EC_USEFONTINFO)
3239 es->left_margin = left;
3240 else
3241 es->left_margin = es->char_width / 3;
3244 if (action & EC_RIGHTMARGIN) {
3245 if (right != EC_USEFONTINFO)
3246 es->right_margin = right;
3247 else
3248 es->right_margin = es->char_width / 3;
3250 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3254 /*********************************************************************
3256 * EM_SETPASSWORDCHAR
3259 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, WCHAR c)
3261 if (es->style & ES_MULTILINE)
3262 return;
3264 if (es->password_char == c)
3265 return;
3267 es->password_char = c;
3268 if (c) {
3269 wnd->dwStyle |= ES_PASSWORD;
3270 es->style |= ES_PASSWORD;
3271 } else {
3272 wnd->dwStyle &= ~ES_PASSWORD;
3273 es->style &= ~ES_PASSWORD;
3275 EDIT_UpdateText(wnd, NULL, TRUE);
3279 /*********************************************************************
3281 * EDIT_EM_SetSel
3283 * note: unlike the specs say: the order of start and end
3284 * _is_ preserved in Windows. (i.e. start can be > end)
3285 * In other words: this handler is OK
3288 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
3290 UINT old_start = es->selection_start;
3291 UINT old_end = es->selection_end;
3292 UINT len = strlenW(es->text);
3294 if (start == (UINT)-1) {
3295 start = es->selection_end;
3296 end = es->selection_end;
3297 } else {
3298 start = min(start, len);
3299 end = min(end, len);
3301 es->selection_start = start;
3302 es->selection_end = end;
3303 if (after_wrap)
3304 es->flags |= EF_AFTER_WRAP;
3305 else
3306 es->flags &= ~EF_AFTER_WRAP;
3307 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3308 ORDER_UINT(start, end);
3309 ORDER_UINT(end, old_end);
3310 ORDER_UINT(start, old_start);
3311 ORDER_UINT(old_start, old_end);
3312 if (end != old_start)
3315 * One can also do
3316 * ORDER_UINT32(end, old_start);
3317 * EDIT_InvalidateText(wnd, es, start, end);
3318 * EDIT_InvalidateText(wnd, es, old_start, old_end);
3319 * in place of the following if statement.
3321 if (old_start > end )
3323 EDIT_InvalidateText(wnd, es, start, end);
3324 EDIT_InvalidateText(wnd, es, old_start, old_end);
3326 else
3328 EDIT_InvalidateText(wnd, es, start, old_start);
3329 EDIT_InvalidateText(wnd, es, end, old_end);
3332 else EDIT_InvalidateText(wnd, es, start, old_end);
3336 /*********************************************************************
3338 * EM_SETTABSTOPS
3341 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs)
3343 if (!(es->style & ES_MULTILINE))
3344 return FALSE;
3345 if (es->tabs)
3346 HeapFree(GetProcessHeap(), 0, es->tabs);
3347 es->tabs_count = count;
3348 if (!count)
3349 es->tabs = NULL;
3350 else {
3351 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3352 memcpy(es->tabs, tabs, count * sizeof(INT));
3354 return TRUE;
3358 /*********************************************************************
3360 * EM_SETTABSTOPS16
3363 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs)
3365 if (!(es->style & ES_MULTILINE))
3366 return FALSE;
3367 if (es->tabs)
3368 HeapFree(GetProcessHeap(), 0, es->tabs);
3369 es->tabs_count = count;
3370 if (!count)
3371 es->tabs = NULL;
3372 else {
3373 INT i;
3374 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3375 for (i = 0 ; i < count ; i++)
3376 es->tabs[i] = *tabs++;
3378 return TRUE;
3382 /*********************************************************************
3384 * EM_SETWORDBREAKPROC
3387 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, LPARAM lParam)
3389 if (es->word_break_proc == (void *)lParam)
3390 return;
3392 es->word_break_proc = (void *)lParam;
3393 es->word_break_proc16 = NULL;
3395 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3396 EDIT_BuildLineDefs_ML(wnd, es);
3397 EDIT_UpdateText(wnd, NULL, TRUE);
3402 /*********************************************************************
3404 * EM_SETWORDBREAKPROC16
3407 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
3409 if (es->word_break_proc16 == wbp)
3410 return;
3412 es->word_break_proc = NULL;
3413 es->word_break_proc16 = wbp;
3414 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3415 EDIT_BuildLineDefs_ML(wnd, es);
3416 EDIT_UpdateText(wnd, NULL, TRUE);
3421 /*********************************************************************
3423 * EM_UNDO / WM_UNDO
3426 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
3428 INT ulength;
3429 LPWSTR utext;
3431 /* Protect read-only edit control from modification */
3432 if(es->style & ES_READONLY)
3433 return FALSE;
3435 ulength = strlenW(es->undo_text);
3436 utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3438 strcpyW(utext, es->undo_text);
3440 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3441 es->undo_insert_count, debugstr_w(utext));
3443 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3444 EDIT_EM_EmptyUndoBuffer(es);
3445 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
3446 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3447 EDIT_EM_ScrollCaret(wnd, es);
3448 HeapFree(GetProcessHeap(), 0, utext);
3450 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3451 es->undo_insert_count, debugstr_w(es->undo_text));
3453 if (es->flags & EF_UPDATE) {
3454 es->flags &= ~EF_UPDATE;
3455 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3458 return TRUE;
3462 /*********************************************************************
3464 * WM_CHAR
3467 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, WCHAR c)
3469 BOOL control;
3471 /* Protect read-only edit control from modification */
3472 if(es->style & ES_READONLY)
3473 return;
3475 control = GetKeyState(VK_CONTROL) & 0x8000;
3477 switch (c) {
3478 case '\r':
3479 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3480 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3481 break;
3482 case '\n':
3483 if (es->style & ES_MULTILINE) {
3484 if (es->style & ES_READONLY) {
3485 EDIT_MoveHome(wnd, es, FALSE);
3486 EDIT_MoveDown_ML(wnd, es, FALSE);
3487 } else {
3488 static const WCHAR cr_lfW[] = {'\r','\n',0};
3489 EDIT_EM_ReplaceSel(wnd, es, TRUE, cr_lfW, TRUE);
3490 if (es->flags & EF_UPDATE) {
3491 es->flags &= ~EF_UPDATE;
3492 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3496 break;
3497 case '\t':
3498 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3500 static const WCHAR tabW[] = {'\t',0};
3501 EDIT_EM_ReplaceSel(wnd, es, TRUE, tabW, TRUE);
3502 if (es->flags & EF_UPDATE) {
3503 es->flags &= ~EF_UPDATE;
3504 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3507 break;
3508 case VK_BACK:
3509 if (!(es->style & ES_READONLY) && !control) {
3510 if (es->selection_start != es->selection_end)
3511 EDIT_WM_Clear(wnd, es);
3512 else {
3513 /* delete character left of caret */
3514 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
3515 EDIT_MoveBackward(wnd, es, TRUE);
3516 EDIT_WM_Clear(wnd, es);
3519 break;
3520 case 0x03: /* ^C */
3521 SendMessageW(wnd->hwndSelf, WM_COPY, 0, 0);
3522 break;
3523 case 0x16: /* ^V */
3524 SendMessageW(wnd->hwndSelf, WM_PASTE, 0, 0);
3525 break;
3526 case 0x18: /* ^X */
3527 SendMessageW(wnd->hwndSelf, WM_CUT, 0, 0);
3528 break;
3530 default:
3531 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3532 WCHAR str[2];
3533 str[0] = c;
3534 str[1] = '\0';
3535 EDIT_EM_ReplaceSel(wnd, es, TRUE, str, TRUE);
3536 if (es->flags & EF_UPDATE) {
3537 es->flags &= ~EF_UPDATE;
3538 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3541 break;
3546 /*********************************************************************
3548 * WM_COMMAND
3551 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
3553 if (code || control)
3554 return;
3556 switch (id) {
3557 case EM_UNDO:
3558 EDIT_EM_Undo(wnd, es);
3559 break;
3560 case WM_CUT:
3561 EDIT_WM_Cut(wnd, es);
3562 break;
3563 case WM_COPY:
3564 EDIT_WM_Copy(wnd, es);
3565 break;
3566 case WM_PASTE:
3567 EDIT_WM_Paste(wnd, es);
3568 break;
3569 case WM_CLEAR:
3570 EDIT_WM_Clear(wnd, es);
3571 break;
3572 case EM_SETSEL:
3573 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
3574 EDIT_EM_ScrollCaret(wnd, es);
3575 break;
3576 default:
3577 ERR("unknown menu item, please report\n");
3578 break;
3583 /*********************************************************************
3585 * WM_CONTEXTMENU
3587 * Note: the resource files resource/sysres_??.rc cannot define a
3588 * single popup menu. Hence we use a (dummy) menubar
3589 * containing the single popup menu as its first item.
3591 * FIXME: the message identifiers have been chosen arbitrarily,
3592 * hence we use MF_BYPOSITION.
3593 * We might as well use the "real" values (anybody knows ?)
3594 * The menu definition is in resources/sysres_??.rc.
3595 * Once these are OK, we better use MF_BYCOMMAND here
3596 * (as we do in EDIT_WM_Command()).
3599 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, INT x, INT y)
3601 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3602 HMENU popup = GetSubMenu(menu, 0);
3603 UINT start = es->selection_start;
3604 UINT end = es->selection_end;
3606 ORDER_UINT(start, end);
3608 /* undo */
3609 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3610 /* cut */
3611 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3612 /* copy */
3613 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3614 /* paste */
3615 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3616 /* delete */
3617 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3618 /* select all */
3619 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlenW(es->text)) ? MF_ENABLED : MF_GRAYED));
3621 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3622 DestroyMenu(menu);
3626 /*********************************************************************
3628 * WM_COPY
3631 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3633 INT s = es->selection_start;
3634 INT e = es->selection_end;
3635 HGLOBAL hdst;
3636 LPWSTR dst;
3638 if (e == s)
3639 return;
3640 ORDER_INT(s, e);
3641 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (DWORD)(e - s + 1) * sizeof(WCHAR));
3642 dst = GlobalLock(hdst);
3643 strncpyW(dst, es->text + s, e - s);
3644 dst[e - s] = 0; /* ensure 0 termination */
3645 TRACE("%s\n", debugstr_w(dst));
3646 GlobalUnlock(hdst);
3647 OpenClipboard(wnd->hwndSelf);
3648 EmptyClipboard();
3649 SetClipboardData(CF_UNICODETEXT, hdst);
3650 CloseClipboard();
3654 /*********************************************************************
3656 * WM_CREATE
3659 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCWSTR name)
3661 TRACE("%s\n", debugstr_w(name));
3663 * To initialize some final structure members, we call some helper
3664 * functions. However, since the EDITSTATE is not consistent (i.e.
3665 * not fully initialized), we should be very careful which
3666 * functions can be called, and in what order.
3668 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3669 EDIT_EM_EmptyUndoBuffer(es);
3671 if (name && *name) {
3672 EDIT_EM_ReplaceSel(wnd, es, FALSE, name, TRUE);
3673 /* if we insert text to the editline, the text scrolls out
3674 * of the window, as the caret is placed after the insert
3675 * pos normally; thus we reset es->selection... to 0 and
3676 * update caret
3678 es->selection_start = es->selection_end = 0;
3679 EDIT_EM_ScrollCaret(wnd, es);
3680 if (es->flags & EF_UPDATE) {
3681 es->flags &= ~EF_UPDATE;
3682 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3685 /* force scroll info update */
3686 EDIT_UpdateScrollInfo(wnd, es);
3687 return 0;
3691 /*********************************************************************
3693 * WM_DESTROY
3696 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3698 if (es->hloc32W) {
3699 while (LocalUnlock(es->hloc32W)) ;
3700 LocalFree(es->hloc32W);
3702 if (es->hloc32A) {
3703 while (LocalUnlock(es->hloc32A)) ;
3704 LocalFree(es->hloc32A);
3706 if (es->hloc16) {
3707 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3708 LOCAL_Free(wnd->hInstance, es->hloc16);
3710 HeapFree(GetProcessHeap(), 0, es);
3711 *(EDITSTATE **)wnd->wExtra = NULL;
3715 /*********************************************************************
3717 * WM_ERASEBKGND
3720 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3722 HBRUSH brush;
3723 RECT rc;
3725 if ( get_app_version() >= 0x40000 &&(
3726 !es->bEnableState || (es->style & ES_READONLY)))
3727 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3728 else
3729 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3731 if (!brush)
3732 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3734 GetClientRect(wnd->hwndSelf, &rc);
3735 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3736 GetClipBox(dc, &rc);
3738 * FIXME: specs say that we should UnrealizeObject() the brush,
3739 * but the specs of UnrealizeObject() say that we shouldn't
3740 * unrealize a stock object. The default brush that
3741 * DefWndProc() returns is ... a stock object.
3743 FillRect(dc, &rc, brush);
3744 return -1;
3748 /*********************************************************************
3750 * WM_GETTEXT
3753 static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode)
3755 if(!count) return 0;
3757 if(unicode)
3759 LPWSTR textW = (LPWSTR)lParam;
3760 strncpyW(textW, es->text, count);
3761 textW[count - 1] = 0; /* ensure 0 termination */
3762 return strlenW(textW);
3764 else
3766 LPSTR textA = (LPSTR)lParam;
3767 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL);
3768 textA[count - 1] = 0; /* ensure 0 termination */
3769 return strlen(textA);
3773 /*********************************************************************
3775 * WM_HSCROLL
3778 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
3780 INT dx;
3781 INT fw;
3783 if (!(es->style & ES_MULTILINE))
3784 return 0;
3786 if (!(es->style & ES_AUTOHSCROLL))
3787 return 0;
3789 dx = 0;
3790 fw = es->format_rect.right - es->format_rect.left;
3791 switch (action) {
3792 case SB_LINELEFT:
3793 TRACE("SB_LINELEFT\n");
3794 if (es->x_offset)
3795 dx = -es->char_width;
3796 break;
3797 case SB_LINERIGHT:
3798 TRACE("SB_LINERIGHT\n");
3799 if (es->x_offset < es->text_width)
3800 dx = es->char_width;
3801 break;
3802 case SB_PAGELEFT:
3803 TRACE("SB_PAGELEFT\n");
3804 if (es->x_offset)
3805 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3806 break;
3807 case SB_PAGERIGHT:
3808 TRACE("SB_PAGERIGHT\n");
3809 if (es->x_offset < es->text_width)
3810 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3811 break;
3812 case SB_LEFT:
3813 TRACE("SB_LEFT\n");
3814 if (es->x_offset)
3815 dx = -es->x_offset;
3816 break;
3817 case SB_RIGHT:
3818 TRACE("SB_RIGHT\n");
3819 if (es->x_offset < es->text_width)
3820 dx = es->text_width - es->x_offset;
3821 break;
3822 case SB_THUMBTRACK:
3823 TRACE("SB_THUMBTRACK %d\n", pos);
3824 es->flags |= EF_HSCROLL_TRACK;
3825 if(es->style & WS_HSCROLL)
3826 dx = pos - es->x_offset;
3827 else
3829 INT fw, new_x;
3830 /* Sanity check */
3831 if(pos < 0 || pos > 100) return 0;
3832 /* Assume default scroll range 0-100 */
3833 fw = es->format_rect.right - es->format_rect.left;
3834 new_x = pos * (es->text_width - fw) / 100;
3835 dx = es->text_width ? (new_x - es->x_offset) : 0;
3837 break;
3838 case SB_THUMBPOSITION:
3839 TRACE("SB_THUMBPOSITION %d\n", pos);
3840 es->flags &= ~EF_HSCROLL_TRACK;
3841 if(wnd->dwStyle & WS_HSCROLL)
3842 dx = pos - es->x_offset;
3843 else
3845 INT fw, new_x;
3846 /* Sanity check */
3847 if(pos < 0 || pos > 100) return 0;
3848 /* Assume default scroll range 0-100 */
3849 fw = es->format_rect.right - es->format_rect.left;
3850 new_x = pos * (es->text_width - fw) / 100;
3851 dx = es->text_width ? (new_x - es->x_offset) : 0;
3853 if (!dx) {
3854 /* force scroll info update */
3855 EDIT_UpdateScrollInfo(wnd, es);
3856 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3858 break;
3859 case SB_ENDSCROLL:
3860 TRACE("SB_ENDSCROLL\n");
3861 break;
3863 * FIXME : the next two are undocumented !
3864 * Are we doing the right thing ?
3865 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3866 * although it's also a regular control message.
3868 case EM_GETTHUMB: /* this one is used by NT notepad */
3869 case EM_GETTHUMB16:
3871 LRESULT ret;
3872 if(wnd->dwStyle & WS_HSCROLL)
3873 ret = GetScrollPos(wnd->hwndSelf, SB_HORZ);
3874 else
3876 /* Assume default scroll range 0-100 */
3877 INT fw = es->format_rect.right - es->format_rect.left;
3878 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
3880 TRACE("EM_GETTHUMB: returning %ld\n", ret);
3881 return ret;
3883 case EM_LINESCROLL16:
3884 TRACE("EM_LINESCROLL16\n");
3885 dx = pos;
3886 break;
3888 default:
3889 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
3890 action, action);
3891 return 0;
3893 if (dx)
3895 INT fw = es->format_rect.right - es->format_rect.left;
3896 /* check if we are going to move too far */
3897 if(es->x_offset + dx + fw > es->text_width)
3898 dx = es->text_width - fw - es->x_offset;
3899 if(dx)
3900 EDIT_EM_LineScroll_internal(wnd, es, dx, 0);
3902 return 0;
3906 /*********************************************************************
3908 * EDIT_CheckCombo
3911 static BOOL EDIT_CheckCombo(WND *wnd, EDITSTATE *es, UINT msg, INT key)
3913 HWND hLBox = es->hwndListBox;
3914 HWND hCombo;
3915 BOOL bDropped;
3916 int nEUI;
3918 if (!hLBox)
3919 return FALSE;
3921 hCombo = wnd->parent->hwndSelf;
3922 bDropped = TRUE;
3923 nEUI = 0;
3925 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3926 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3928 if (key == VK_UP || key == VK_DOWN)
3930 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
3931 nEUI = 1;
3933 if (msg == WM_KEYDOWN || nEUI)
3934 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3937 switch (msg)
3939 case WM_KEYDOWN:
3940 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3942 /* make sure ComboLBox pops up */
3943 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3944 key = VK_F4;
3945 nEUI = 2;
3948 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3949 break;
3951 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3952 if (nEUI)
3953 SendMessageW(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
3954 else
3955 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
3956 break;
3959 if(nEUI == 2)
3960 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3962 return TRUE;
3966 /*********************************************************************
3968 * WM_KEYDOWN
3970 * Handling of special keys that don't produce a WM_CHAR
3971 * (i.e. non-printable keys) & Backspace & Delete
3974 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key)
3976 BOOL shift;
3977 BOOL control;
3979 if (GetKeyState(VK_MENU) & 0x8000)
3980 return 0;
3982 shift = GetKeyState(VK_SHIFT) & 0x8000;
3983 control = GetKeyState(VK_CONTROL) & 0x8000;
3985 switch (key) {
3986 case VK_F4:
3987 case VK_UP:
3988 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key) || key == VK_F4)
3989 break;
3991 /* fall through */
3992 case VK_LEFT:
3993 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3994 EDIT_MoveUp_ML(wnd, es, shift);
3995 else
3996 if (control)
3997 EDIT_MoveWordBackward(wnd, es, shift);
3998 else
3999 EDIT_MoveBackward(wnd, es, shift);
4000 break;
4001 case VK_DOWN:
4002 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key))
4003 break;
4004 /* fall through */
4005 case VK_RIGHT:
4006 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
4007 EDIT_MoveDown_ML(wnd, es, shift);
4008 else if (control)
4009 EDIT_MoveWordForward(wnd, es, shift);
4010 else
4011 EDIT_MoveForward(wnd, es, shift);
4012 break;
4013 case VK_HOME:
4014 EDIT_MoveHome(wnd, es, shift);
4015 break;
4016 case VK_END:
4017 EDIT_MoveEnd(wnd, es, shift);
4018 break;
4019 case VK_PRIOR:
4020 if (es->style & ES_MULTILINE)
4021 EDIT_MovePageUp_ML(wnd, es, shift);
4022 else
4023 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
4024 break;
4025 case VK_NEXT:
4026 if (es->style & ES_MULTILINE)
4027 EDIT_MovePageDown_ML(wnd, es, shift);
4028 else
4029 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key);
4030 break;
4031 case VK_DELETE:
4032 if (!(es->style & ES_READONLY) && !(shift && control)) {
4033 if (es->selection_start != es->selection_end) {
4034 if (shift)
4035 EDIT_WM_Cut(wnd, es);
4036 else
4037 EDIT_WM_Clear(wnd, es);
4038 } else {
4039 if (shift) {
4040 /* delete character left of caret */
4041 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4042 EDIT_MoveBackward(wnd, es, TRUE);
4043 EDIT_WM_Clear(wnd, es);
4044 } else if (control) {
4045 /* delete to end of line */
4046 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4047 EDIT_MoveEnd(wnd, es, TRUE);
4048 EDIT_WM_Clear(wnd, es);
4049 } else {
4050 /* delete character right of caret */
4051 EDIT_EM_SetSel(wnd, es, (UINT)-1, 0, FALSE);
4052 EDIT_MoveForward(wnd, es, TRUE);
4053 EDIT_WM_Clear(wnd, es);
4057 break;
4058 case VK_INSERT:
4059 if (shift) {
4060 if (!(es->style & ES_READONLY))
4061 EDIT_WM_Paste(wnd, es);
4062 } else if (control)
4063 EDIT_WM_Copy(wnd, es);
4064 break;
4065 case VK_RETURN:
4066 /* If the edit doesn't want the return send a message to the default object */
4067 if(!(es->style & ES_WANTRETURN))
4069 HWND hwndParent = GetParent(wnd->hwndSelf);
4070 DWORD dw = SendMessageW( hwndParent, DM_GETDEFID, 0, 0 );
4071 if (HIWORD(dw) == DC_HASDEFID)
4073 SendMessageW( hwndParent, WM_COMMAND,
4074 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
4075 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
4078 break;
4080 return 0;
4084 /*********************************************************************
4086 * WM_KILLFOCUS
4089 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es)
4091 es->flags &= ~EF_FOCUSED;
4092 DestroyCaret();
4093 if(!(es->style & ES_NOHIDESEL))
4094 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
4095 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
4096 return 0;
4100 /*********************************************************************
4102 * WM_LBUTTONDBLCLK
4104 * The caret position has been set on the WM_LBUTTONDOWN message
4107 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es)
4109 INT s;
4110 INT e = es->selection_end;
4111 INT l;
4112 INT li;
4113 INT ll;
4115 if (!(es->flags & EF_FOCUSED))
4116 return 0;
4118 l = EDIT_EM_LineFromChar(es, e);
4119 li = EDIT_EM_LineIndex(es, l);
4120 ll = EDIT_EM_LineLength(es, e);
4121 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
4122 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
4123 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
4124 EDIT_EM_ScrollCaret(wnd, es);
4125 return 0;
4129 /*********************************************************************
4131 * WM_LBUTTONDOWN
4134 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
4136 INT e;
4137 BOOL after_wrap;
4139 if (!(es->flags & EF_FOCUSED))
4140 return 0;
4142 es->bCaptureState = TRUE;
4143 SetCapture(wnd->hwndSelf);
4144 EDIT_ConfinePoint(es, &x, &y);
4145 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
4146 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
4147 EDIT_EM_ScrollCaret(wnd, es);
4148 es->region_posx = es->region_posy = 0;
4149 SetTimer(wnd->hwndSelf, 0, 100, NULL);
4150 return 0;
4154 /*********************************************************************
4156 * WM_LBUTTONUP
4159 static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es)
4161 if (es->bCaptureState && GetCapture() == hwndSelf) {
4162 KillTimer(hwndSelf, 0);
4163 ReleaseCapture();
4165 es->bCaptureState = FALSE;
4166 return 0;
4170 /*********************************************************************
4172 * WM_MBUTTONDOWN
4175 static LRESULT EDIT_WM_MButtonDown(WND *wnd)
4177 SendMessageW(wnd->hwndSelf,WM_PASTE,0,0);
4178 return 0;
4182 /*********************************************************************
4184 * WM_MOUSEMOVE
4187 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, INT x, INT y)
4189 INT e;
4190 BOOL after_wrap;
4191 INT prex, prey;
4193 if (GetCapture() != wnd->hwndSelf)
4194 return 0;
4197 * FIXME: gotta do some scrolling if outside client
4198 * area. Maybe reset the timer ?
4200 prex = x; prey = y;
4201 EDIT_ConfinePoint(es, &x, &y);
4202 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
4203 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
4204 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
4205 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
4206 return 0;
4210 /*********************************************************************
4212 * WM_NCCREATE
4215 static LRESULT EDIT_WM_NCCreate(WND *wnd, DWORD style, HWND hwndParent, BOOL unicode)
4217 EDITSTATE *es;
4218 UINT alloc_size;
4220 TRACE("Creating %s edit control\n", unicode ? "Unicode" : "ANSI");
4222 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
4223 return FALSE;
4224 *(EDITSTATE **)wnd->wExtra = es;
4227 * Note: since the EDITSTATE has not been fully initialized yet,
4228 * we can't use any API calls that may send
4229 * WM_XXX messages before WM_NCCREATE is completed.
4232 es->is_unicode = unicode;
4233 es->style = style;
4235 es->bEnableState = !(style & WS_DISABLED);
4238 * In Win95 look and feel, the WS_BORDER style is replaced by the
4239 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4240 * control a non client area.
4242 if (TWEAK_WineLook != WIN31_LOOK)
4244 if (es->style & WS_BORDER)
4246 es->style &= ~WS_BORDER;
4247 wnd->dwStyle &= ~WS_BORDER;
4248 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
4251 else
4253 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
4254 wnd->dwStyle &= ~WS_BORDER;
4257 if (es->style & ES_COMBO)
4258 es->hwndListBox = GetDlgItem(hwndParent, ID_CB_LISTBOX);
4260 if (es->style & ES_MULTILINE) {
4261 es->buffer_limit = BUFLIMIT_MULTI;
4262 if (es->style & WS_VSCROLL)
4263 es->style |= ES_AUTOVSCROLL;
4264 if (es->style & WS_HSCROLL)
4265 es->style |= ES_AUTOHSCROLL;
4266 es->style &= ~ES_PASSWORD;
4267 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4268 if (es->style & ES_RIGHT)
4269 es->style &= ~ES_CENTER;
4270 es->style &= ~WS_HSCROLL;
4271 es->style &= ~ES_AUTOHSCROLL;
4274 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4275 es->style |= ES_AUTOVSCROLL;
4276 } else {
4277 es->buffer_limit = BUFLIMIT_SINGLE;
4278 es->style &= ~ES_CENTER;
4279 es->style &= ~ES_RIGHT;
4280 es->style &= ~WS_HSCROLL;
4281 es->style &= ~WS_VSCROLL;
4282 es->style &= ~ES_AUTOVSCROLL;
4283 es->style &= ~ES_WANTRETURN;
4284 if (es->style & ES_UPPERCASE) {
4285 es->style &= ~ES_LOWERCASE;
4286 es->style &= ~ES_NUMBER;
4287 } else if (es->style & ES_LOWERCASE)
4288 es->style &= ~ES_NUMBER;
4289 if (es->style & ES_PASSWORD)
4290 es->password_char = '*';
4292 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4293 es->style |= ES_AUTOHSCROLL;
4296 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4297 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4298 return FALSE;
4299 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4301 if (!(es->undo_text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (es->buffer_size + 1) * sizeof(WCHAR))))
4302 return FALSE;
4303 es->undo_buffer_size = es->buffer_size;
4305 if (es->style & ES_MULTILINE)
4306 if (!(es->first_line_def = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
4307 return FALSE;
4308 es->line_count = 1;
4310 return TRUE;
4313 /*********************************************************************
4315 * WM_PAINT
4318 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
4320 PAINTSTRUCT ps;
4321 INT i;
4322 HDC dc;
4323 HFONT old_font = 0;
4324 RECT rc;
4325 RECT rcLine;
4326 RECT rcRgn;
4327 BOOL rev = es->bEnableState &&
4328 ((es->flags & EF_FOCUSED) ||
4329 (es->style & ES_NOHIDESEL));
4330 if (!wParam)
4331 dc = BeginPaint(wnd->hwndSelf, &ps);
4332 else
4333 dc = (HDC) wParam;
4334 if(es->style & WS_BORDER) {
4335 GetClientRect(wnd->hwndSelf, &rc);
4336 if(es->style & ES_MULTILINE) {
4337 if(es->style & WS_HSCROLL) rc.bottom++;
4338 if(es->style & WS_VSCROLL) rc.right++;
4340 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
4342 IntersectClipRect(dc, es->format_rect.left,
4343 es->format_rect.top,
4344 es->format_rect.right,
4345 es->format_rect.bottom);
4346 if (es->style & ES_MULTILINE) {
4347 GetClientRect(wnd->hwndSelf, &rc);
4348 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
4350 if (es->font)
4351 old_font = SelectObject(dc, es->font);
4352 if ( get_app_version() >= 0x40000 &&(
4353 !es->bEnableState || (es->style & ES_READONLY)))
4354 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
4355 else
4356 EDIT_SEND_CTLCOLOR(wnd, dc);
4358 if (!es->bEnableState)
4359 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
4360 GetClipBox(dc, &rcRgn);
4361 if (es->style & ES_MULTILINE) {
4362 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4363 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
4364 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
4365 if (IntersectRect(&rc, &rcRgn, &rcLine))
4366 EDIT_PaintLine(wnd, es, dc, i, rev);
4368 } else {
4369 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
4370 if (IntersectRect(&rc, &rcRgn, &rcLine))
4371 EDIT_PaintLine(wnd, es, dc, 0, rev);
4373 if (es->font)
4374 SelectObject(dc, old_font);
4376 if (!wParam)
4377 EndPaint(wnd->hwndSelf, &ps);
4381 /*********************************************************************
4383 * WM_PASTE
4386 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
4388 HGLOBAL hsrc;
4389 LPWSTR src;
4391 /* Protect read-only edit control from modification */
4392 if(es->style & ES_READONLY)
4393 return;
4395 OpenClipboard(wnd->hwndSelf);
4396 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
4397 src = (LPWSTR)GlobalLock(hsrc);
4398 EDIT_EM_ReplaceSel(wnd, es, TRUE, src, TRUE);
4399 GlobalUnlock(hsrc);
4401 if (es->flags & EF_UPDATE) {
4402 es->flags &= ~EF_UPDATE;
4403 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
4406 CloseClipboard();
4410 /*********************************************************************
4412 * WM_SETFOCUS
4415 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es)
4417 es->flags |= EF_FOCUSED;
4418 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4419 EDIT_SetCaretPos(wnd, es, es->selection_end,
4420 es->flags & EF_AFTER_WRAP);
4421 if(!(es->style & ES_NOHIDESEL))
4422 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
4423 ShowCaret(wnd->hwndSelf);
4424 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
4428 /*********************************************************************
4430 * WM_SETFONT
4432 * With Win95 look the margins are set to default font value unless
4433 * the system font (font == 0) is being set, in which case they are left
4434 * unchanged.
4437 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
4439 TEXTMETRICW tm;
4440 HDC dc;
4441 HFONT old_font = 0;
4442 RECT r;
4444 es->font = font;
4445 dc = GetDC(wnd->hwndSelf);
4446 if (font)
4447 old_font = SelectObject(dc, font);
4448 GetTextMetricsW(dc, &tm);
4449 es->line_height = tm.tmHeight;
4450 es->char_width = tm.tmAveCharWidth;
4451 if (font)
4452 SelectObject(dc, old_font);
4453 ReleaseDC(wnd->hwndSelf, dc);
4454 if (font && (TWEAK_WineLook > WIN31_LOOK))
4455 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
4456 EC_USEFONTINFO, EC_USEFONTINFO);
4458 /* Force the recalculation of the format rect for each font change */
4459 GetClientRect(wnd->hwndSelf, &r);
4460 EDIT_SetRectNP(wnd, es, &r);
4462 if (es->style & ES_MULTILINE)
4463 EDIT_BuildLineDefs_ML(wnd, es);
4464 else
4465 EDIT_CalcLineWidth_SL(wnd, es);
4467 if (redraw)
4468 EDIT_UpdateText(wnd, NULL, TRUE);
4469 if (es->flags & EF_FOCUSED) {
4470 DestroyCaret();
4471 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4472 EDIT_SetCaretPos(wnd, es, es->selection_end,
4473 es->flags & EF_AFTER_WRAP);
4474 ShowCaret(wnd->hwndSelf);
4479 /*********************************************************************
4481 * WM_SETTEXT
4483 * NOTES
4484 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4485 * The modified flag is reset. No notifications are sent.
4487 * For single-line controls, reception of WM_SETTEXT triggers:
4488 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4491 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPARAM lParam, BOOL unicode)
4493 LPWSTR text = NULL;
4495 if(unicode)
4496 text = (LPWSTR)lParam;
4497 else if (lParam)
4499 LPCSTR textA = (LPCSTR)lParam;
4500 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
4501 if((text = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
4502 MultiByteToWideChar(CP_ACP, 0, textA, -1, text, countW);
4505 EDIT_EM_SetSel(wnd, es, 0, (UINT)-1, FALSE);
4506 if (text) {
4507 TRACE("%s\n", debugstr_w(text));
4508 EDIT_EM_ReplaceSel(wnd, es, FALSE, text, !(es->style & (ES_MULTILINE | ES_COMBO)));
4509 if(!unicode)
4510 HeapFree(GetProcessHeap(), 0, text);
4511 } else {
4512 static const WCHAR empty_stringW[] = {0};
4513 TRACE("<NULL>\n");
4514 EDIT_EM_ReplaceSel(wnd, es, FALSE, empty_stringW, !(es->style & (ES_MULTILINE | ES_COMBO)));
4516 es->x_offset = 0;
4517 es->flags &= ~EF_MODIFIED;
4518 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
4519 EDIT_EM_ScrollCaret(wnd, es);
4521 if (es->flags & EF_UPDATE) {
4522 es->flags &= ~EF_UPDATE;
4523 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
4528 /*********************************************************************
4530 * WM_SIZE
4533 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
4535 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4536 RECT rc;
4537 TRACE("width = %d, height = %d\n", width, height);
4538 SetRect(&rc, 0, 0, width, height);
4539 EDIT_SetRectNP(wnd, es, &rc);
4540 EDIT_UpdateText(wnd, NULL, TRUE);
4545 /*********************************************************************
4547 * WM_SYSKEYDOWN
4550 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
4552 if ((key == VK_BACK) && (key_data & 0x2000)) {
4553 if (EDIT_EM_CanUndo(es))
4554 EDIT_EM_Undo(wnd, es);
4555 return 0;
4556 } else if (key == VK_UP || key == VK_DOWN) {
4557 if (EDIT_CheckCombo(wnd, es, WM_SYSKEYDOWN, key))
4558 return 0;
4560 return DefWindowProcW(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4564 /*********************************************************************
4566 * WM_TIMER
4569 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es)
4571 if (es->region_posx < 0) {
4572 EDIT_MoveBackward(wnd, es, TRUE);
4573 } else if (es->region_posx > 0) {
4574 EDIT_MoveForward(wnd, es, TRUE);
4577 * FIXME: gotta do some vertical scrolling here, like
4578 * EDIT_EM_LineScroll(wnd, 0, 1);
4582 /*********************************************************************
4584 * WM_VSCROLL
4587 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos)
4589 INT dy;
4591 if (!(es->style & ES_MULTILINE))
4592 return 0;
4594 if (!(es->style & ES_AUTOVSCROLL))
4595 return 0;
4597 dy = 0;
4598 switch (action) {
4599 case SB_LINEUP:
4600 case SB_LINEDOWN:
4601 case SB_PAGEUP:
4602 case SB_PAGEDOWN:
4603 TRACE("action %d\n", action);
4604 EDIT_EM_Scroll(wnd, es, action);
4605 return 0;
4606 case SB_TOP:
4607 TRACE("SB_TOP\n");
4608 dy = -es->y_offset;
4609 break;
4610 case SB_BOTTOM:
4611 TRACE("SB_BOTTOM\n");
4612 dy = es->line_count - 1 - es->y_offset;
4613 break;
4614 case SB_THUMBTRACK:
4615 TRACE("SB_THUMBTRACK %d\n", pos);
4616 es->flags |= EF_VSCROLL_TRACK;
4617 if(es->style & WS_VSCROLL)
4618 dy = pos - es->y_offset;
4619 else
4621 /* Assume default scroll range 0-100 */
4622 INT vlc, new_y;
4623 /* Sanity check */
4624 if(pos < 0 || pos > 100) return 0;
4625 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4626 new_y = pos * (es->line_count - vlc) / 100;
4627 dy = es->line_count ? (new_y - es->y_offset) : 0;
4628 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4629 es->line_count, es->y_offset, pos, dy);
4631 break;
4632 case SB_THUMBPOSITION:
4633 TRACE("SB_THUMBPOSITION %d\n", pos);
4634 es->flags &= ~EF_VSCROLL_TRACK;
4635 if(es->style & WS_VSCROLL)
4636 dy = pos - es->y_offset;
4637 else
4639 /* Assume default scroll range 0-100 */
4640 INT vlc, new_y;
4641 /* Sanity check */
4642 if(pos < 0 || pos > 100) return 0;
4643 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4644 new_y = pos * (es->line_count - vlc) / 100;
4645 dy = es->line_count ? (new_y - es->y_offset) : 0;
4646 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4647 es->line_count, es->y_offset, pos, dy);
4649 if (!dy)
4651 /* force scroll info update */
4652 EDIT_UpdateScrollInfo(wnd, es);
4653 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4655 break;
4656 case SB_ENDSCROLL:
4657 TRACE("SB_ENDSCROLL\n");
4658 break;
4660 * FIXME : the next two are undocumented !
4661 * Are we doing the right thing ?
4662 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4663 * although it's also a regular control message.
4665 case EM_GETTHUMB: /* this one is used by NT notepad */
4666 case EM_GETTHUMB16:
4668 LRESULT ret;
4669 if(wnd->dwStyle & WS_VSCROLL)
4670 ret = GetScrollPos(wnd->hwndSelf, SB_VERT);
4671 else
4673 /* Assume default scroll range 0-100 */
4674 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4675 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
4677 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4678 return ret;
4680 case EM_LINESCROLL16:
4681 TRACE("EM_LINESCROLL16 %d\n", pos);
4682 dy = pos;
4683 break;
4685 default:
4686 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4687 action, action);
4688 return 0;
4690 if (dy)
4691 EDIT_EM_LineScroll(wnd, es, 0, dy);
4692 return 0;
4696 /*********************************************************************
4698 * EDIT_UpdateText
4701 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase)
4703 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4705 /* EF_UPDATE will be turned off in paint */
4706 if (es->flags & EF_UPDATE)
4707 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
4709 InvalidateRect(wnd->hwndSelf, rc, bErase);