4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
11 * please read EDIT.TODO (and update it when you change things)
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
24 #include "wine/unicode.h"
28 #include "debugtools.h"
30 DEFAULT_DEBUG_CHANNEL(edit
);
31 DECLARE_DEBUG_CHANNEL(combo
);
32 DECLARE_DEBUG_CHANNEL(relay
);
34 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
35 FIXME: BTW, new specs say 65535 (do you dare ???) */
36 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
37 #define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
38 #define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
39 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
42 * extra flags for EDITSTATE.flags field
44 #define EF_MODIFIED 0x0001 /* text has been modified */
45 #define EF_FOCUSED 0x0002 /* we have input focus */
46 #define EF_UPDATE 0x0004 /* notify parent of changed state */
47 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
48 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
49 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
50 wrapped line, instead of in front of the next character */
51 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
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' */
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 */
65 INT width
; /* width of the line in pixels */
66 INT index
; /* line index into the buffer */
67 struct tagLINEDEF
*next
;
72 BOOL is_unicode
; /* how the control was created */
73 LPWSTR text
; /* the actual contents of the control */
74 UINT buffer_size
; /* the size of the buffer in characters */
75 UINT buffer_limit
; /* the maximum size to which the buffer may grow in characters */
76 HFONT font
; /* NULL means standard system font */
77 INT x_offset
; /* scroll offset for multi lines this is in pixels
78 for single lines it's in characters */
79 INT line_height
; /* height of a screen line in pixels */
80 INT char_width
; /* average character width in pixels */
81 DWORD style
; /* sane version of wnd->dwStyle */
82 WORD flags
; /* flags that are not in es->style or wnd->flags (EF_XXX) */
83 INT undo_insert_count
; /* number of characters inserted in sequence */
84 UINT undo_position
; /* character index of the insertion and deletion */
85 LPWSTR undo_text
; /* deleted text */
86 UINT undo_buffer_size
; /* size of the deleted text buffer */
87 INT selection_start
; /* == selection_end if no selection */
88 INT selection_end
; /* == current caret position */
89 WCHAR password_char
; /* == 0 if no password char, and for multi line controls */
90 INT left_margin
; /* in pixels */
91 INT right_margin
; /* in pixels */
93 INT text_width
; /* width of the widest line in pixels for multi line controls
94 and just line width for single line controls */
95 INT region_posx
; /* Position of cursor relative to region: */
96 INT region_posy
; /* -1: to left, 0: within, 1: to right */
97 EDITWORDBREAKPROC16 word_break_proc16
;
98 void *word_break_proc
; /* 32-bit word break proc: ANSI or Unicode */
99 INT line_count
; /* number of lines */
100 INT y_offset
; /* scroll offset in number of lines */
101 BOOL bCaptureState
; /* flag indicating whether mouse was captured */
102 BOOL bEnableState
; /* flag keeping the enable state */
103 HWND hwndParent
; /* Handle of parent for sending EN_* messages.
104 Even if parent will change, EN_* messages
105 should be sent to the first parent. */
106 HWND hwndListBox
; /* handle of ComboBox's listbox or NULL */
108 * only for multi line controls
110 INT lock_count
; /* amount of re-entries in the EditWndProc */
113 LINEDEF
*first_line_def
; /* linked list of (soft) linebreaks */
114 HLOCAL hloc32W
; /* our unicode local memory block */
115 HLOCAL16 hloc16
; /* alias for 16-bit control receiving EM_GETHANDLE16
117 HLOCAL hloc32A
; /* alias for ANSI control receiving EM_GETHANDLE
122 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
123 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
125 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
126 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
128 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
129 do {TRACE("notification " str " sent to hwnd=%08x\n", \
130 (UINT)(hwnd));} while(0)
132 /* used for disabled or read-only edit control */
133 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
134 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
135 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
136 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
137 (SendMessageW((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
138 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
139 #define EDIT_NOTIFY_PARENT(es, wNotifyCode, str) \
141 { /* Notify parent which has created this edit control */ \
142 DPRINTF_EDIT_NOTIFY((es)->hwndParent, str); \
143 SendMessageW((es)->hwndParent, WM_COMMAND, \
144 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
145 (LPARAM)(wnd)->hwndSelf); \
147 #define DPRINTF_EDIT_MSG16(str) \
149 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
150 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
151 #define DPRINTF_EDIT_MSG32(str) \
153 "32 bit %c : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
154 unicode ? 'W' : 'A', \
155 (UINT)wnd->hwndSelf, (UINT)wParam, (UINT)lParam)
157 /*********************************************************************
164 * These functions have trivial implementations
165 * We still like to call them internally
166 * "static inline" makes them more like macro's
168 static inline BOOL
EDIT_EM_CanUndo(EDITSTATE
*es
);
169 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE
*es
);
170 static inline void EDIT_WM_Clear(WND
*wnd
, EDITSTATE
*es
);
171 static inline void EDIT_WM_Cut(WND
*wnd
, EDITSTATE
*es
);
174 * Helper functions only valid for one type of control
176 static void EDIT_BuildLineDefs_ML(WND
*wnd
, EDITSTATE
*es
, INT iStart
, INT iEnd
, INT delta
, HRGN hrgn
);
177 static void EDIT_CalcLineWidth_SL(WND
*wnd
, EDITSTATE
*es
);
178 static LPWSTR
EDIT_GetPasswordPointer_SL(EDITSTATE
*es
);
179 static void EDIT_MoveDown_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
180 static void EDIT_MovePageDown_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
181 static void EDIT_MovePageUp_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
182 static void EDIT_MoveUp_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
184 * Helper functions valid for both single line _and_ multi line controls
186 static INT
EDIT_CallWordBreakProc(EDITSTATE
*es
, INT start
, INT index
, INT count
, INT action
);
187 static INT
EDIT_CharFromPos(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
, LPBOOL after_wrap
);
188 static void EDIT_ConfinePoint(EDITSTATE
*es
, LPINT x
, LPINT y
);
189 static void EDIT_GetLineRect(WND
*wnd
, EDITSTATE
*es
, INT line
, INT scol
, INT ecol
, LPRECT rc
);
190 static void EDIT_InvalidateText(WND
*wnd
, EDITSTATE
*es
, INT start
, INT end
);
191 static void EDIT_LockBuffer(WND
*wnd
, EDITSTATE
*es
);
192 static BOOL
EDIT_MakeFit(WND
*wnd
, EDITSTATE
*es
, UINT size
);
193 static BOOL
EDIT_MakeUndoFit(EDITSTATE
*es
, UINT size
);
194 static void EDIT_MoveBackward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
195 static void EDIT_MoveEnd(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
196 static void EDIT_MoveForward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
197 static void EDIT_MoveHome(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
198 static void EDIT_MoveWordBackward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
199 static void EDIT_MoveWordForward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
);
200 static void EDIT_PaintLine(WND
*wnd
, EDITSTATE
*es
, HDC hdc
, INT line
, BOOL rev
);
201 static INT
EDIT_PaintText(EDITSTATE
*es
, HDC hdc
, INT x
, INT y
, INT line
, INT col
, INT count
, BOOL rev
);
202 static void EDIT_SetCaretPos(WND
*wnd
, EDITSTATE
*es
, INT pos
, BOOL after_wrap
);
203 static void EDIT_SetRectNP(WND
*wnd
, EDITSTATE
*es
, LPRECT lprc
);
204 static void EDIT_UnlockBuffer(WND
*wnd
, EDITSTATE
*es
, BOOL force
);
205 static void EDIT_UpdateScrollInfo(WND
*wnd
, EDITSTATE
*es
);
206 static INT CALLBACK
EDIT_WordBreakProc(LPWSTR s
, INT index
, INT count
, INT action
);
208 * EM_XXX message handlers
210 static LRESULT
EDIT_EM_CharFromPos(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
);
211 static BOOL
EDIT_EM_FmtLines(EDITSTATE
*es
, BOOL add_eol
);
212 static HLOCAL
EDIT_EM_GetHandle(EDITSTATE
*es
);
213 static HLOCAL16
EDIT_EM_GetHandle16(WND
*wnd
, EDITSTATE
*es
);
214 static INT
EDIT_EM_GetLine(EDITSTATE
*es
, INT line
, LPARAM lParam
, BOOL unicode
);
215 static LRESULT
EDIT_EM_GetSel(EDITSTATE
*es
, LPUINT start
, LPUINT end
);
216 static LRESULT
EDIT_EM_GetThumb(WND
*wnd
, EDITSTATE
*es
);
217 static INT
EDIT_EM_LineFromChar(EDITSTATE
*es
, INT index
);
218 static INT
EDIT_EM_LineIndex(EDITSTATE
*es
, INT line
);
219 static INT
EDIT_EM_LineLength(EDITSTATE
*es
, INT index
);
220 static BOOL
EDIT_EM_LineScroll(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
);
221 static BOOL
EDIT_EM_LineScroll_internal(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
);
222 static LRESULT
EDIT_EM_PosFromChar(WND
*wnd
, EDITSTATE
*es
, INT index
, BOOL after_wrap
);
223 static void EDIT_EM_ReplaceSel(WND
*wnd
, EDITSTATE
*es
, BOOL can_undo
, LPCWSTR lpsz_replace
, BOOL send_update
);
224 static LRESULT
EDIT_EM_Scroll(WND
*wnd
, EDITSTATE
*es
, INT action
);
225 static void EDIT_EM_ScrollCaret(WND
*wnd
, EDITSTATE
*es
);
226 static void EDIT_EM_SetHandle(WND
*wnd
, EDITSTATE
*es
, HLOCAL hloc
);
227 static void EDIT_EM_SetHandle16(WND
*wnd
, EDITSTATE
*es
, HLOCAL16 hloc
);
228 static void EDIT_EM_SetLimitText(EDITSTATE
*es
, INT limit
);
229 static void EDIT_EM_SetMargins(EDITSTATE
*es
, INT action
, INT left
, INT right
);
230 static void EDIT_EM_SetPasswordChar(WND
*wnd
, EDITSTATE
*es
, WCHAR c
);
231 static void EDIT_EM_SetSel(WND
*wnd
, EDITSTATE
*es
, UINT start
, UINT end
, BOOL after_wrap
);
232 static BOOL
EDIT_EM_SetTabStops(EDITSTATE
*es
, INT count
, LPINT tabs
);
233 static BOOL
EDIT_EM_SetTabStops16(EDITSTATE
*es
, INT count
, LPINT16 tabs
);
234 static void EDIT_EM_SetWordBreakProc(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
);
235 static void EDIT_EM_SetWordBreakProc16(WND
*wnd
, EDITSTATE
*es
, EDITWORDBREAKPROC16 wbp
);
236 static BOOL
EDIT_EM_Undo(WND
*wnd
, EDITSTATE
*es
);
238 * WM_XXX message handlers
240 static void EDIT_WM_Char(WND
*wnd
, EDITSTATE
*es
, WCHAR c
);
241 static void EDIT_WM_Command(WND
*wnd
, EDITSTATE
*es
, INT code
, INT id
, HWND conrtol
);
242 static void EDIT_WM_ContextMenu(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
);
243 static void EDIT_WM_Copy(WND
*wnd
, EDITSTATE
*es
);
244 static LRESULT
EDIT_WM_Create(WND
*wnd
, EDITSTATE
*es
, LPCWSTR name
);
245 static void EDIT_WM_Destroy(WND
*wnd
, EDITSTATE
*es
);
246 static LRESULT
EDIT_WM_EraseBkGnd(WND
*wnd
, EDITSTATE
*es
, HDC dc
);
247 static INT
EDIT_WM_GetText(EDITSTATE
*es
, INT count
, LPARAM lParam
, BOOL unicode
);
248 static LRESULT
EDIT_WM_HScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
);
249 static LRESULT
EDIT_WM_KeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
);
250 static LRESULT
EDIT_WM_KillFocus(WND
*wnd
, EDITSTATE
*es
);
251 static LRESULT
EDIT_WM_LButtonDblClk(WND
*wnd
, EDITSTATE
*es
);
252 static LRESULT
EDIT_WM_LButtonDown(WND
*wnd
, EDITSTATE
*es
, DWORD keys
, INT x
, INT y
);
253 static LRESULT
EDIT_WM_LButtonUp(HWND hwndSelf
, EDITSTATE
*es
);
254 static LRESULT
EDIT_WM_MButtonDown(WND
*wnd
);
255 static LRESULT
EDIT_WM_MouseMove(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
);
256 static LRESULT
EDIT_WM_NCCreate(WND
*wnd
, DWORD style
, HWND hwndParent
, BOOL unicode
);
257 static void EDIT_WM_Paint(WND
*wnd
, EDITSTATE
*es
, WPARAM wParam
);
258 static void EDIT_WM_Paste(WND
*wnd
, EDITSTATE
*es
);
259 static void EDIT_WM_SetFocus(WND
*wnd
, EDITSTATE
*es
);
260 static void EDIT_WM_SetFont(WND
*wnd
, EDITSTATE
*es
, HFONT font
, BOOL redraw
);
261 static void EDIT_WM_SetText(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
, BOOL unicode
);
262 static void EDIT_WM_Size(WND
*wnd
, EDITSTATE
*es
, UINT action
, INT width
, INT height
);
263 static LRESULT
EDIT_WM_SysKeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
, DWORD key_data
);
264 static void EDIT_WM_Timer(WND
*wnd
, EDITSTATE
*es
);
265 static LRESULT
EDIT_WM_VScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
);
266 static void EDIT_UpdateText(WND
*wnd
, LPRECT rc
, BOOL bErase
);
267 static void EDIT_UpdateTextRegion(WND
*wnd
, HRGN hrgn
, BOOL bErase
);
269 LRESULT WINAPI
EditWndProcA(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
270 LRESULT WINAPI
EditWndProcW(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
272 /*********************************************************************
273 * edit class descriptor
275 const struct builtin_class_descr EDIT_builtin_class
=
278 CS_GLOBALCLASS
| CS_DBLCLKS
/*| CS_PARENTDC*/, /* style */
279 EditWndProcA
, /* procA */
280 EditWndProcW
, /* procW */
281 sizeof(EDITSTATE
*), /* extra */
282 IDC_IBEAMA
, /* cursor */
287 /*********************************************************************
292 static inline BOOL
EDIT_EM_CanUndo(EDITSTATE
*es
)
294 return (es
->undo_insert_count
|| strlenW(es
->undo_text
));
298 /*********************************************************************
303 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE
*es
)
305 es
->undo_insert_count
= 0;
306 *es
->undo_text
= '\0';
310 /*********************************************************************
315 static inline void EDIT_WM_Clear(WND
*wnd
, EDITSTATE
*es
)
317 static const WCHAR empty_stringW
[] = {0};
319 /* Protect read-only edit control from modification */
320 if(es
->style
& ES_READONLY
)
323 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, empty_stringW
, TRUE
);
327 /*********************************************************************
332 static inline void EDIT_WM_Cut(WND
*wnd
, EDITSTATE
*es
)
334 EDIT_WM_Copy(wnd
, es
);
335 EDIT_WM_Clear(wnd
, es
);
339 /**********************************************************************
342 * Returns the window version in case Wine emulates a later version
343 * of windows then the application expects.
345 * In a number of cases when windows runs an application that was
346 * designed for an earlier windows version, windows reverts
347 * to "old" behaviour of that earlier version.
349 * An example is a disabled edit control that needs to be painted.
350 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
351 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
352 * applications with an expected version 0f 4.0 or higher.
355 static DWORD
get_app_version(void)
357 static DWORD version
;
360 DWORD dwEmulatedVersion
;
362 DWORD dwProcVersion
= GetProcessVersion(0);
364 GetVersionExW( &info
);
365 dwEmulatedVersion
= MAKELONG( info
.dwMinorVersion
, info
.dwMajorVersion
);
366 /* fixme: this may not be 100% correct; see discussion on the
367 * wine developer list in Nov 1999 */
368 version
= dwProcVersion
< dwEmulatedVersion
? dwProcVersion
: dwEmulatedVersion
;
374 /*********************************************************************
378 * The messages are in the order of the actual integer values
379 * (which can be found in include/windows.h)
380 * Whereever possible the 16 bit versions are converted to
381 * the 32 bit ones, so that we can 'fall through' to the
382 * helper functions. These are mostly 32 bit (with a few
383 * exceptions, clearly indicated by a '16' extension to their
387 static LRESULT WINAPI
EditWndProc_locked( WND
*wnd
, UINT msg
,
388 WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
390 EDITSTATE
*es
= *(EDITSTATE
**)((wnd
)->wExtra
);
395 DPRINTF_EDIT_MSG32("WM_DESTROY");
396 EDIT_WM_Destroy(wnd
, es
);
401 DPRINTF_EDIT_MSG32("WM_NCCREATE");
404 LPCREATESTRUCTW cs
= (LPCREATESTRUCTW
)lParam
;
405 result
= EDIT_WM_NCCreate(wnd
, cs
->style
, cs
->hwndParent
, TRUE
);
409 LPCREATESTRUCTA cs
= (LPCREATESTRUCTA
)lParam
;
410 result
= EDIT_WM_NCCreate(wnd
, cs
->style
, cs
->hwndParent
, FALSE
);
418 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
420 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
425 EDIT_LockBuffer(wnd
, es
);
428 DPRINTF_EDIT_MSG16("EM_GETSEL");
433 DPRINTF_EDIT_MSG32("EM_GETSEL");
434 result
= EDIT_EM_GetSel(es
, (LPUINT
)wParam
, (LPUINT
)lParam
);
438 DPRINTF_EDIT_MSG16("EM_SETSEL");
439 if (SLOWORD(lParam
) == -1)
440 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
442 EDIT_EM_SetSel(wnd
, es
, LOWORD(lParam
), HIWORD(lParam
), FALSE
);
444 EDIT_EM_ScrollCaret(wnd
, es
);
448 DPRINTF_EDIT_MSG32("EM_SETSEL");
449 EDIT_EM_SetSel(wnd
, es
, wParam
, lParam
, FALSE
);
450 EDIT_EM_ScrollCaret(wnd
, es
);
455 DPRINTF_EDIT_MSG16("EM_GETRECT");
457 CONV_RECT32TO16(&es
->format_rect
, MapSL(lParam
));
460 DPRINTF_EDIT_MSG32("EM_GETRECT");
462 CopyRect((LPRECT
)lParam
, &es
->format_rect
);
466 DPRINTF_EDIT_MSG16("EM_SETRECT");
467 if ((es
->style
& ES_MULTILINE
) && lParam
) {
469 CONV_RECT16TO32(MapSL(lParam
), &rc
);
470 EDIT_SetRectNP(wnd
, es
, &rc
);
471 EDIT_UpdateText(wnd
, NULL
, TRUE
);
475 DPRINTF_EDIT_MSG32("EM_SETRECT");
476 if ((es
->style
& ES_MULTILINE
) && lParam
) {
477 EDIT_SetRectNP(wnd
, es
, (LPRECT
)lParam
);
478 EDIT_UpdateText(wnd
, NULL
, TRUE
);
483 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
484 if ((es
->style
& ES_MULTILINE
) && lParam
) {
486 CONV_RECT16TO32(MapSL(lParam
), &rc
);
487 EDIT_SetRectNP(wnd
, es
, &rc
);
491 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
492 if ((es
->style
& ES_MULTILINE
) && lParam
)
493 EDIT_SetRectNP(wnd
, es
, (LPRECT
)lParam
);
497 DPRINTF_EDIT_MSG16("EM_SCROLL");
500 DPRINTF_EDIT_MSG32("EM_SCROLL");
501 result
= EDIT_EM_Scroll(wnd
, es
, (INT
)wParam
);
504 case EM_LINESCROLL16
:
505 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
506 wParam
= (WPARAM
)(INT
)SHIWORD(lParam
);
507 lParam
= (LPARAM
)(INT
)SLOWORD(lParam
);
510 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
511 result
= (LRESULT
)EDIT_EM_LineScroll(wnd
, es
, (INT
)wParam
, (INT
)lParam
);
514 case EM_SCROLLCARET16
:
515 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
518 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
519 EDIT_EM_ScrollCaret(wnd
, es
);
524 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
527 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
528 result
= ((es
->flags
& EF_MODIFIED
) != 0);
532 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
535 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
537 es
->flags
|= EF_MODIFIED
;
539 es
->flags
&= ~(EF_MODIFIED
| EF_UPDATE
); /* reset pending updates */
542 case EM_GETLINECOUNT16
:
543 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
545 case EM_GETLINECOUNT
:
546 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
547 result
= (es
->style
& ES_MULTILINE
) ? es
->line_count
: 1;
551 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
552 if ((INT16
)wParam
== -1)
556 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
557 result
= (LRESULT
)EDIT_EM_LineIndex(es
, (INT
)wParam
);
561 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
562 EDIT_EM_SetHandle16(wnd
, es
, (HLOCAL16
)wParam
);
565 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
566 EDIT_EM_SetHandle(wnd
, es
, (HLOCAL
)wParam
);
570 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
571 result
= (LRESULT
)EDIT_EM_GetHandle16(wnd
, es
);
574 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
575 result
= (LRESULT
)EDIT_EM_GetHandle(es
);
579 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
582 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
583 result
= EDIT_EM_GetThumb(wnd
, es
);
586 /* messages 0x00bf and 0x00c0 missing from specs */
589 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
592 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
594 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
596 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
600 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
603 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
605 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
607 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
610 case EM_LINELENGTH16
:
611 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
614 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
615 result
= (LRESULT
)EDIT_EM_LineLength(es
, (INT
)wParam
);
618 case EM_REPLACESEL16
:
619 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
620 lParam
= (LPARAM
)MapSL(lParam
);
621 unicode
= FALSE
; /* 16-bit message is always ascii */
626 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
629 textW
= (LPWSTR
)lParam
;
632 LPSTR textA
= (LPSTR
)lParam
;
633 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
634 if((textW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
635 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, textW
, countW
);
638 EDIT_EM_ReplaceSel(wnd
, es
, (BOOL
)wParam
, textW
, TRUE
);
642 HeapFree(GetProcessHeap(), 0, textW
);
645 /* message 0x00c3 missing from specs */
648 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
651 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
653 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
655 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
659 DPRINTF_EDIT_MSG16("EM_GETLINE");
660 lParam
= (LPARAM
)MapSL(lParam
);
661 unicode
= FALSE
; /* 16-bit message is always ascii */
664 DPRINTF_EDIT_MSG32("EM_GETLINE");
665 result
= (LRESULT
)EDIT_EM_GetLine(es
, (INT
)wParam
, lParam
, unicode
);
669 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
671 case EM_SETLIMITTEXT
:
672 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
673 EDIT_EM_SetLimitText(es
, (INT
)wParam
);
677 DPRINTF_EDIT_MSG16("EM_CANUNDO");
680 DPRINTF_EDIT_MSG32("EM_CANUNDO");
681 result
= (LRESULT
)EDIT_EM_CanUndo(es
);
685 DPRINTF_EDIT_MSG16("EM_UNDO");
690 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
691 result
= (LRESULT
)EDIT_EM_Undo(wnd
, es
);
695 DPRINTF_EDIT_MSG16("EM_FMTLINES");
698 DPRINTF_EDIT_MSG32("EM_FMTLINES");
699 result
= (LRESULT
)EDIT_EM_FmtLines(es
, (BOOL
)wParam
);
702 case EM_LINEFROMCHAR16
:
703 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
705 case EM_LINEFROMCHAR
:
706 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
707 result
= (LRESULT
)EDIT_EM_LineFromChar(es
, (INT
)wParam
);
710 /* message 0x00ca missing from specs */
713 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
716 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
718 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
720 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
723 case EM_SETTABSTOPS16
:
724 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
725 result
= (LRESULT
)EDIT_EM_SetTabStops16(es
, (INT
)wParam
, MapSL(lParam
));
728 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
729 result
= (LRESULT
)EDIT_EM_SetTabStops(es
, (INT
)wParam
, (LPINT
)lParam
);
732 case EM_SETPASSWORDCHAR16
:
733 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
734 unicode
= FALSE
; /* 16-bit message is always ascii */
736 case EM_SETPASSWORDCHAR
:
739 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
742 charW
= (WCHAR
)wParam
;
746 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
749 EDIT_EM_SetPasswordChar(wnd
, es
, charW
);
753 case EM_EMPTYUNDOBUFFER16
:
754 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
756 case EM_EMPTYUNDOBUFFER
:
757 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
758 EDIT_EM_EmptyUndoBuffer(es
);
761 case EM_GETFIRSTVISIBLELINE16
:
762 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
763 result
= es
->y_offset
;
765 case EM_GETFIRSTVISIBLELINE
:
766 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
767 result
= (es
->style
& ES_MULTILINE
) ? es
->y_offset
: es
->x_offset
;
770 case EM_SETREADONLY16
:
771 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
774 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
776 wnd
->dwStyle
|= ES_READONLY
;
777 es
->style
|= ES_READONLY
;
779 wnd
->dwStyle
&= ~ES_READONLY
;
780 es
->style
&= ~ES_READONLY
;
785 case EM_SETWORDBREAKPROC16
:
786 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
787 EDIT_EM_SetWordBreakProc16(wnd
, es
, (EDITWORDBREAKPROC16
)lParam
);
789 case EM_SETWORDBREAKPROC
:
790 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
791 EDIT_EM_SetWordBreakProc(wnd
, es
, lParam
);
794 case EM_GETWORDBREAKPROC16
:
795 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
796 result
= (LRESULT
)es
->word_break_proc16
;
798 case EM_GETWORDBREAKPROC
:
799 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
800 result
= (LRESULT
)es
->word_break_proc
;
803 case EM_GETPASSWORDCHAR16
:
804 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
805 unicode
= FALSE
; /* 16-bit message is always ascii */
807 case EM_GETPASSWORDCHAR
:
809 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
812 result
= es
->password_char
;
815 WCHAR charW
= es
->password_char
;
817 WideCharToMultiByte(CP_ACP
, 0, &charW
, 1, &charA
, 1, NULL
, NULL
);
823 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
826 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
827 EDIT_EM_SetMargins(es
, (INT
)wParam
, SLOWORD(lParam
), SHIWORD(lParam
));
831 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
832 result
= MAKELONG(es
->left_margin
, es
->right_margin
);
835 case EM_GETLIMITTEXT
:
836 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
837 result
= es
->buffer_limit
;
841 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
842 result
= EDIT_EM_PosFromChar(wnd
, es
, (INT
)wParam
, FALSE
);
846 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
847 result
= EDIT_EM_CharFromPos(wnd
, es
, SLOWORD(lParam
), SHIWORD(lParam
));
851 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
852 result
= DLGC_HASSETSEL
| DLGC_WANTCHARS
| DLGC_WANTARROWS
;
854 if (lParam
&& (((LPMSG
)lParam
)->message
== WM_KEYDOWN
))
856 int vk
= (int)((LPMSG
)lParam
)->wParam
;
858 if ((wnd
->dwStyle
& ES_WANTRETURN
) && vk
== VK_RETURN
)
860 result
|= DLGC_WANTMESSAGE
;
862 else if (es
->hwndListBox
&& (vk
== VK_RETURN
|| vk
== VK_ESCAPE
))
864 if (SendMessageW(wnd
->parent
->hwndSelf
, CB_GETDROPPEDSTATE
, 0, 0))
865 result
|= DLGC_WANTMESSAGE
;
873 DPRINTF_EDIT_MSG32("WM_CHAR");
880 MultiByteToWideChar(CP_ACP
, 0, &charA
, 1, &charW
, 1);
883 if ((charW
== VK_RETURN
|| charW
== VK_ESCAPE
) && es
->hwndListBox
)
885 if (SendMessageW(wnd
->parent
->hwndSelf
, CB_GETDROPPEDSTATE
, 0, 0))
886 SendMessageW(wnd
->parent
->hwndSelf
, WM_KEYDOWN
, charW
, 0);
889 EDIT_WM_Char(wnd
, es
, charW
);
894 DPRINTF_EDIT_MSG32("WM_CLEAR");
895 EDIT_WM_Clear(wnd
, es
);
899 DPRINTF_EDIT_MSG32("WM_COMMAND");
900 EDIT_WM_Command(wnd
, es
, HIWORD(wParam
), LOWORD(wParam
), (HWND
)lParam
);
904 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
905 EDIT_WM_ContextMenu(wnd
, es
, SLOWORD(lParam
), SHIWORD(lParam
));
909 DPRINTF_EDIT_MSG32("WM_COPY");
910 EDIT_WM_Copy(wnd
, es
);
914 DPRINTF_EDIT_MSG32("WM_CREATE");
916 result
= EDIT_WM_Create(wnd
, es
, ((LPCREATESTRUCTW
)lParam
)->lpszName
);
919 LPCSTR nameA
= ((LPCREATESTRUCTA
)lParam
)->lpszName
;
923 INT countW
= MultiByteToWideChar(CP_ACP
, 0, nameA
, -1, NULL
, 0);
924 if((nameW
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
925 MultiByteToWideChar(CP_ACP
, 0, nameA
, -1, nameW
, countW
);
927 result
= EDIT_WM_Create(wnd
, es
, nameW
);
929 HeapFree(GetProcessHeap(), 0, nameW
);
934 DPRINTF_EDIT_MSG32("WM_CUT");
935 EDIT_WM_Cut(wnd
, es
);
939 DPRINTF_EDIT_MSG32("WM_ENABLE");
940 es
->bEnableState
= (BOOL
) wParam
;
941 EDIT_UpdateText(wnd
, NULL
, TRUE
);
945 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
946 result
= EDIT_WM_EraseBkGnd(wnd
, es
, (HDC
)wParam
);
950 DPRINTF_EDIT_MSG32("WM_GETFONT");
951 result
= (LRESULT
)es
->font
;
955 DPRINTF_EDIT_MSG32("WM_GETTEXT");
956 result
= (LRESULT
)EDIT_WM_GetText(es
, (INT
)wParam
, lParam
, unicode
);
959 case WM_GETTEXTLENGTH
:
960 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
961 result
= strlenW(es
->text
);
965 DPRINTF_EDIT_MSG32("WM_HSCROLL");
966 result
= EDIT_WM_HScroll(wnd
, es
, LOWORD(wParam
), SHIWORD(wParam
));
970 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
971 result
= EDIT_WM_KeyDown(wnd
, es
, (INT
)wParam
);
975 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
976 result
= EDIT_WM_KillFocus(wnd
, es
);
979 case WM_LBUTTONDBLCLK
:
980 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
981 result
= EDIT_WM_LButtonDblClk(wnd
, es
);
985 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
986 result
= EDIT_WM_LButtonDown(wnd
, es
, (DWORD
)wParam
, SLOWORD(lParam
), SHIWORD(lParam
));
990 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
991 result
= EDIT_WM_LButtonUp(wnd
->hwndSelf
, es
);
995 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
996 result
= EDIT_WM_MButtonDown(wnd
);
999 case WM_MOUSEACTIVATE
:
1001 * FIXME: maybe DefWindowProc() screws up, but it seems that
1002 * modeless dialog boxes need this. If we don't do this, the focus
1003 * will _not_ be set by DefWindowProc() for edit controls in a
1004 * modeless dialog box ???
1006 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
1007 SetFocus(wnd
->hwndSelf
);
1008 result
= MA_ACTIVATE
;
1013 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
1015 result
= EDIT_WM_MouseMove(wnd
, es
, SLOWORD(lParam
), SHIWORD(lParam
));
1019 DPRINTF_EDIT_MSG32("WM_PAINT");
1020 EDIT_WM_Paint(wnd
, es
, wParam
);
1024 DPRINTF_EDIT_MSG32("WM_PASTE");
1025 EDIT_WM_Paste(wnd
, es
);
1029 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
1030 EDIT_WM_SetFocus(wnd
, es
);
1034 DPRINTF_EDIT_MSG32("WM_SETFONT");
1035 EDIT_WM_SetFont(wnd
, es
, (HFONT
)wParam
, LOWORD(lParam
) != 0);
1039 /* FIXME: actually set an internal flag and behave accordingly */
1043 DPRINTF_EDIT_MSG32("WM_SETTEXT");
1044 EDIT_WM_SetText(wnd
, es
, lParam
, unicode
);
1049 DPRINTF_EDIT_MSG32("WM_SIZE");
1050 EDIT_WM_Size(wnd
, es
, (UINT
)wParam
, LOWORD(lParam
), HIWORD(lParam
));
1054 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
1055 result
= EDIT_WM_SysKeyDown(wnd
, es
, (INT
)wParam
, (DWORD
)lParam
);
1059 DPRINTF_EDIT_MSG32("WM_TIMER");
1060 EDIT_WM_Timer(wnd
, es
);
1064 DPRINTF_EDIT_MSG32("WM_VSCROLL");
1065 result
= EDIT_WM_VScroll(wnd
, es
, LOWORD(wParam
), SHIWORD(wParam
));
1070 int gcWheelDelta
= 0;
1071 UINT pulScrollLines
= 3;
1072 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES
,0, &pulScrollLines
, 0);
1074 if (wParam
& (MK_SHIFT
| MK_CONTROL
)) {
1076 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
1078 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
1081 gcWheelDelta
-= SHIWORD(wParam
);
1082 if (abs(gcWheelDelta
) >= WHEEL_DELTA
&& pulScrollLines
)
1084 int cLineScroll
= (int) min((UINT
) es
->line_count
, pulScrollLines
);
1085 cLineScroll
*= (gcWheelDelta
/ WHEEL_DELTA
);
1086 result
= EDIT_EM_LineScroll(wnd
, es
, 0, cLineScroll
);
1092 result
= DefWindowProcW(wnd
->hwndSelf
, msg
, wParam
, lParam
);
1094 result
= DefWindowProcA(wnd
->hwndSelf
, msg
, wParam
, lParam
);
1097 EDIT_UnlockBuffer(wnd
, es
, FALSE
);
1102 /*********************************************************************
1104 * EditWndProcW (USER32.@)
1106 LRESULT WINAPI
EditWndProcW(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1109 WND
*wndPtr
= WIN_FindWndPtr(hWnd
);
1111 res
= EditWndProc_locked(wndPtr
, uMsg
, wParam
, lParam
, TRUE
);
1113 WIN_ReleaseWndPtr(wndPtr
);
1117 /*********************************************************************
1119 * EditWndProcA (USER32.@)
1121 LRESULT WINAPI
EditWndProcA(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
1124 WND
*wndPtr
= WIN_FindWndPtr(hWnd
);
1126 res
= EditWndProc_locked(wndPtr
, uMsg
, wParam
, lParam
, FALSE
);
1128 WIN_ReleaseWndPtr(wndPtr
);
1132 /*********************************************************************
1134 * EDIT_BuildLineDefs_ML
1136 * Build linked list of text lines.
1137 * Lines can end with '\0' (last line), a character (if it is wrapped),
1138 * a soft return '\r\r\n' or a hard return '\r\n'
1141 static void EDIT_BuildLineDefs_ML(WND
*wnd
, EDITSTATE
*es
, INT istart
, INT iend
, INT delta
, HRGN hrgn
)
1145 LPWSTR current_position
, cp
;
1147 LINEDEF
*current_line
;
1148 LINEDEF
*previous_line
;
1149 LINEDEF
*start_line
;
1150 INT line_index
= 0, nstart_line
= 0, nstart_index
= 0;
1151 INT line_count
= es
->line_count
;
1152 INT orig_net_length
;
1155 if (istart
== iend
&& delta
== 0)
1158 dc
= GetDC(wnd
->hwndSelf
);
1160 old_font
= SelectObject(dc
, es
->font
);
1162 previous_line
= NULL
;
1163 current_line
= es
->first_line_def
;
1165 /* Find starting line. istart must lie inside an existing line or
1166 * at the end of buffer */
1168 if (istart
< current_line
->index
+ current_line
->length
||
1169 current_line
->ending
== END_0
)
1172 previous_line
= current_line
;
1173 current_line
= current_line
->next
;
1175 } while (current_line
);
1177 if (!current_line
) /* Error occurred start is not inside previous buffer */
1179 FIXME(" modification occurred outside buffer\n");
1183 /* Remember start of modifications in order to calculate update region */
1184 nstart_line
= line_index
;
1185 nstart_index
= current_line
->index
;
1187 /* We must start to reformat from the previous line since the modifications
1188 * may have caused the line to wrap upwards. */
1189 if (!(es
->style
& ES_AUTOHSCROLL
) && line_index
> 0)
1192 current_line
= previous_line
;
1194 start_line
= current_line
;
1196 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
1197 current_position
= es
->text
+ current_line
->index
;
1199 if (current_line
!= start_line
)
1201 if (!current_line
|| current_line
->index
+ delta
> current_position
- es
->text
)
1203 /* The buffer has been expanded, create a new line and
1204 insert it into the link list */
1205 LINEDEF
*new_line
= HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF
));
1206 new_line
->next
= previous_line
->next
;
1207 previous_line
->next
= new_line
;
1208 current_line
= new_line
;
1211 else if (current_line
->index
+ delta
< current_position
- es
->text
)
1213 /* The previous line merged with this line so we delete this extra entry */
1214 previous_line
->next
= current_line
->next
;
1215 HeapFree(GetProcessHeap(), 0, current_line
);
1216 current_line
= previous_line
->next
;
1220 else /* current_line->index + delta == current_position */
1222 if (current_position
- es
->text
> iend
)
1223 break; /* We reached end of line modifications */
1224 /* else recalulate this line */
1228 current_line
->index
= current_position
- es
->text
;
1229 orig_net_length
= current_line
->net_length
;
1231 /* Find end of line */
1232 cp
= current_position
;
1234 if ((*cp
== '\r') && (*(cp
+ 1) == '\n'))
1239 /* Mark type of line termination */
1241 current_line
->ending
= END_0
;
1242 current_line
->net_length
= strlenW(current_position
);
1243 } else if ((cp
> current_position
) && (*(cp
- 1) == '\r')) {
1244 current_line
->ending
= END_SOFT
;
1245 current_line
->net_length
= cp
- current_position
- 1;
1247 current_line
->ending
= END_HARD
;
1248 current_line
->net_length
= cp
- current_position
;
1251 /* Calculate line width */
1252 current_line
->width
= (INT
)LOWORD(GetTabbedTextExtentW(dc
,
1253 current_position
, current_line
->net_length
,
1254 es
->tabs_count
, es
->tabs
));
1256 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1257 if ((!(es
->style
& ES_AUTOHSCROLL
)) && (current_line
->width
> fw
)) {
1262 next
= EDIT_CallWordBreakProc(es
, current_position
- es
->text
,
1263 prev
+ 1, current_line
->net_length
, WB_RIGHT
);
1264 current_line
->width
= (INT
)LOWORD(GetTabbedTextExtentW(dc
,
1265 current_position
, next
, es
->tabs_count
, es
->tabs
));
1266 } while (current_line
->width
<= fw
);
1267 if (!prev
) { /* Didn't find a line break so force a break */
1272 current_line
->width
= (INT
)LOWORD(GetTabbedTextExtentW(dc
,
1273 current_position
, next
, es
->tabs_count
, es
->tabs
));
1274 } while (current_line
->width
<= fw
);
1279 /* If the first line we are calculating, wrapped before istart, we must
1280 * adjust istart in order for this to be reflected in the update region. */
1281 if (current_line
->index
== nstart_index
&& istart
> current_line
->index
+ prev
)
1282 istart
= current_line
->index
+ prev
;
1283 /* else if we are updating the previous line before the first line we
1284 * are re-caulculating and it expanded */
1285 else if (current_line
== start_line
&&
1286 current_line
->index
!= nstart_index
&& orig_net_length
< prev
)
1288 /* Line expanded due to an upwards line wrap so we must partially include
1289 * previous line in update region */
1290 nstart_line
= line_index
;
1291 nstart_index
= current_line
->index
;
1292 istart
= current_line
->index
+ orig_net_length
;
1295 current_line
->net_length
= prev
;
1296 current_line
->ending
= END_WRAP
;
1297 current_line
->width
= (INT
)LOWORD(GetTabbedTextExtentW(dc
, current_position
,
1298 current_line
->net_length
, es
->tabs_count
, es
->tabs
));
1302 /* Adjust length to include line termination */
1303 switch (current_line
->ending
) {
1305 current_line
->length
= current_line
->net_length
+ 3;
1308 current_line
->length
= current_line
->net_length
+ 2;
1312 current_line
->length
= current_line
->net_length
;
1315 es
->text_width
= max(es
->text_width
, current_line
->width
);
1316 current_position
+= current_line
->length
;
1317 previous_line
= current_line
;
1318 current_line
= current_line
->next
;
1320 } while (previous_line
->ending
!= END_0
);
1322 /* Finish adjusting line index's by delta or remove hanging lines */
1323 if (previous_line
->ending
== END_0
)
1325 LINEDEF
*pnext
= NULL
;
1327 previous_line
->next
= NULL
;
1328 while (current_line
)
1330 pnext
= current_line
->next
;
1331 HeapFree(GetProcessHeap(), 0, current_line
);
1332 current_line
= pnext
;
1338 while (current_line
)
1340 current_line
->index
+= delta
;
1341 current_line
= current_line
->next
;
1345 /* Calculate rest of modification rectangle */
1350 * We calculate two rectangles. One for the first line which may have
1351 * an indent with respect to the format rect. The other is a format-width
1352 * rectangle that spans the rest of the lines that changed or moved.
1354 rc
.top
= es
->format_rect
.top
+ nstart_line
* es
->line_height
-
1355 (es
->y_offset
* es
->line_height
); /* Adjust for vertical scrollbar */
1356 rc
.bottom
= rc
.top
+ es
->line_height
;
1357 rc
.left
= es
->format_rect
.left
+ (INT
)LOWORD(GetTabbedTextExtentW(dc
,
1358 es
->text
+ nstart_index
, istart
- nstart_index
,
1359 es
->tabs_count
, es
->tabs
)) - es
->x_offset
; /* Adjust for horz scroll */
1360 rc
.right
= es
->format_rect
.right
;
1361 SetRectRgn(hrgn
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1364 rc
.left
= es
->format_rect
.left
;
1365 rc
.right
= es
->format_rect
.right
;
1367 * If lines were added or removed we must re-paint the remainder of the
1368 * lines since the remaining lines were either shifted up or down.
1370 if (line_count
< es
->line_count
) /* We added lines */
1371 rc
.bottom
= es
->line_count
* es
->line_height
;
1372 else if (line_count
> es
->line_count
) /* We removed lines */
1373 rc
.bottom
= line_count
* es
->line_height
;
1375 rc
.bottom
= line_index
* es
->line_height
;
1376 rc
.bottom
-= (es
->y_offset
* es
->line_height
); /* Adjust for vertical scrollbar */
1377 tmphrgn
= CreateRectRgn(rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
1378 CombineRgn(hrgn
, hrgn
, tmphrgn
, RGN_OR
);
1379 DeleteObject(tmphrgn
);
1383 SelectObject(dc
, old_font
);
1385 ReleaseDC(wnd
->hwndSelf
, dc
);
1388 /*********************************************************************
1390 * EDIT_CalcLineWidth_SL
1393 static void EDIT_CalcLineWidth_SL(WND
*wnd
, EDITSTATE
*es
)
1395 es
->text_width
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, strlenW(es
->text
), FALSE
));
1398 /*********************************************************************
1400 * EDIT_CallWordBreakProc
1402 * Call appropriate WordBreakProc (internal or external).
1404 * Note: The "start" argument should always be an index refering
1405 * to es->text. The actual wordbreak proc might be
1406 * 16 bit, so we can't always pass any 32 bit LPSTR.
1407 * Hence we assume that es->text is the buffer that holds
1408 * the string under examination (we can decide this for ourselves).
1411 /* ### start build ### */
1412 extern WORD CALLBACK
EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16
,SEGPTR
,WORD
,WORD
,WORD
);
1413 /* ### stop build ### */
1414 static INT
EDIT_CallWordBreakProc(EDITSTATE
*es
, INT start
, INT index
, INT count
, INT action
)
1416 INT ret
, iWndsLocks
;
1418 /* To avoid any deadlocks, all the locks on the windows structures
1419 must be suspended before the control is passed to the application */
1420 iWndsLocks
= WIN_SuspendWndsLock();
1422 if (es
->word_break_proc16
) {
1427 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
+ start
, count
, NULL
, 0, NULL
, NULL
);
1428 hglob16
= GlobalAlloc16(GMEM_MOVEABLE
| GMEM_ZEROINIT
, countA
);
1429 segptr
= K32WOWGlobalLock16(hglob16
);
1430 WideCharToMultiByte(CP_ACP
, 0, es
->text
+ start
, count
, MapSL(segptr
), countA
, NULL
, NULL
);
1431 ret
= (INT
)EDIT_CallTo16_word_lwww(es
->word_break_proc16
,
1432 segptr
, index
, countA
, action
);
1433 GlobalUnlock16(hglob16
);
1434 GlobalFree16(hglob16
);
1436 else if (es
->word_break_proc
)
1440 EDITWORDBREAKPROCW wbpW
= (EDITWORDBREAKPROCW
)es
->word_break_proc
;
1442 TRACE_(relay
)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1443 es
->word_break_proc
, debugstr_wn(es
->text
+ start
, count
), index
, count
, action
);
1444 ret
= wbpW(es
->text
+ start
, index
, count
, action
);
1448 EDITWORDBREAKPROCA wbpA
= (EDITWORDBREAKPROCA
)es
->word_break_proc
;
1452 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
+ start
, count
, NULL
, 0, NULL
, NULL
);
1453 textA
= HeapAlloc(GetProcessHeap(), 0, countA
);
1454 WideCharToMultiByte(CP_ACP
, 0, es
->text
+ start
, count
, textA
, countA
, NULL
, NULL
);
1455 TRACE_(relay
)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1456 es
->word_break_proc
, debugstr_an(textA
, countA
), index
, countA
, action
);
1457 ret
= wbpA(textA
, index
, countA
, action
);
1458 HeapFree(GetProcessHeap(), 0, textA
);
1462 ret
= EDIT_WordBreakProc(es
->text
+ start
, index
, count
, action
);
1464 WIN_RestoreWndsLock(iWndsLocks
);
1469 /*********************************************************************
1473 * Beware: This is not the function called on EM_CHARFROMPOS
1474 * The position _can_ be outside the formatting / client
1476 * The return value is only the character index
1479 static INT
EDIT_CharFromPos(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
, LPBOOL after_wrap
)
1485 if (es
->style
& ES_MULTILINE
) {
1486 INT line
= (y
- es
->format_rect
.top
) / es
->line_height
+ es
->y_offset
;
1488 LINEDEF
*line_def
= es
->first_line_def
;
1490 while ((line
> 0) && line_def
->next
) {
1491 line_index
+= line_def
->length
;
1492 line_def
= line_def
->next
;
1495 x
+= es
->x_offset
- es
->format_rect
.left
;
1496 if (x
>= line_def
->width
) {
1498 *after_wrap
= (line_def
->ending
== END_WRAP
);
1499 return line_index
+ line_def
->net_length
;
1503 *after_wrap
= FALSE
;
1506 dc
= GetDC(wnd
->hwndSelf
);
1508 old_font
= SelectObject(dc
, es
->font
);
1509 low
= line_index
+ 1;
1510 high
= line_index
+ line_def
->net_length
+ 1;
1511 while (low
< high
- 1)
1513 INT mid
= (low
+ high
) / 2;
1514 if (LOWORD(GetTabbedTextExtentW(dc
, es
->text
+ line_index
,mid
- line_index
, es
->tabs_count
, es
->tabs
)) > x
) high
= mid
;
1520 *after_wrap
= ((index
== line_index
+ line_def
->net_length
) &&
1521 (line_def
->ending
== END_WRAP
));
1526 *after_wrap
= FALSE
;
1527 x
-= es
->format_rect
.left
;
1529 return es
->x_offset
;
1530 text
= EDIT_GetPasswordPointer_SL(es
);
1531 dc
= GetDC(wnd
->hwndSelf
);
1533 old_font
= SelectObject(dc
, es
->font
);
1537 INT high
= es
->x_offset
;
1538 while (low
< high
- 1)
1540 INT mid
= (low
+ high
) / 2;
1541 GetTextExtentPoint32W( dc
, text
+ mid
,
1542 es
->x_offset
- mid
, &size
);
1543 if (size
.cx
> -x
) low
= mid
;
1550 INT low
= es
->x_offset
;
1551 INT high
= strlenW(es
->text
) + 1;
1552 while (low
< high
- 1)
1554 INT mid
= (low
+ high
) / 2;
1555 GetTextExtentPoint32W( dc
, text
+ es
->x_offset
,
1556 mid
- es
->x_offset
, &size
);
1557 if (size
.cx
> x
) high
= mid
;
1562 if (es
->style
& ES_PASSWORD
)
1563 HeapFree(GetProcessHeap(), 0, text
);
1566 SelectObject(dc
, old_font
);
1567 ReleaseDC(wnd
->hwndSelf
, dc
);
1572 /*********************************************************************
1576 * adjusts the point to be within the formatting rectangle
1577 * (so CharFromPos returns the nearest _visible_ character)
1580 static void EDIT_ConfinePoint(EDITSTATE
*es
, LPINT x
, LPINT y
)
1582 *x
= min(max(*x
, es
->format_rect
.left
), es
->format_rect
.right
- 1);
1583 *y
= min(max(*y
, es
->format_rect
.top
), es
->format_rect
.bottom
- 1);
1587 /*********************************************************************
1591 * Calculates the bounding rectangle for a line from a starting
1592 * column to an ending column.
1595 static void EDIT_GetLineRect(WND
*wnd
, EDITSTATE
*es
, INT line
, INT scol
, INT ecol
, LPRECT rc
)
1597 INT line_index
= EDIT_EM_LineIndex(es
, line
);
1599 if (es
->style
& ES_MULTILINE
)
1600 rc
->top
= es
->format_rect
.top
+ (line
- es
->y_offset
) * es
->line_height
;
1602 rc
->top
= es
->format_rect
.top
;
1603 rc
->bottom
= rc
->top
+ es
->line_height
;
1604 rc
->left
= (scol
== 0) ? es
->format_rect
.left
: SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, line_index
+ scol
, TRUE
));
1605 rc
->right
= (ecol
== -1) ? es
->format_rect
.right
: SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, line_index
+ ecol
, TRUE
));
1609 /*********************************************************************
1611 * EDIT_GetPasswordPointer_SL
1613 * note: caller should free the (optionally) allocated buffer
1616 static LPWSTR
EDIT_GetPasswordPointer_SL(EDITSTATE
*es
)
1618 if (es
->style
& ES_PASSWORD
) {
1619 INT len
= strlenW(es
->text
);
1620 LPWSTR text
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
1622 while(len
) text
[--len
] = es
->password_char
;
1629 /*********************************************************************
1633 * This acts as a LOCAL_Lock(), but it locks only once. This way
1634 * you can call it whenever you like, without unlocking.
1637 static void EDIT_LockBuffer(WND
*wnd
, EDITSTATE
*es
)
1640 ERR("no EDITSTATE ... please report\n");
1646 BOOL _16bit
= FALSE
;
1652 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1653 textA
= LocalLock(es
->hloc32A
);
1654 countA
= strlen(textA
) + 1;
1658 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1659 textA
= LOCAL_Lock(wnd
->hInstance
, es
->hloc16
);
1660 countA
= strlen(textA
) + 1;
1665 ERR("no buffer ... please report\n");
1672 UINT countW_new
= MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, NULL
, 0);
1673 TRACE("%d bytes translated to %d WCHARs\n", countA
, countW_new
);
1674 if(countW_new
> es
->buffer_size
+ 1)
1676 UINT alloc_size
= ROUND_TO_GROW(countW_new
* sizeof(WCHAR
));
1677 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es
->buffer_size
, countW_new
);
1678 hloc32W_new
= LocalReAlloc(es
->hloc32W
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
);
1681 es
->hloc32W
= hloc32W_new
;
1682 es
->buffer_size
= LocalSize(hloc32W_new
)/sizeof(WCHAR
) - 1;
1683 TRACE("Real new size %d+1 WCHARs\n", es
->buffer_size
);
1686 WARN("FAILED! Will synchronize partially\n");
1690 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1691 es
->text
= LocalLock(es
->hloc32W
);
1695 MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, es
->text
, es
->buffer_size
+ 1);
1697 LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
);
1699 LocalUnlock(es
->hloc32A
);
1706 /*********************************************************************
1708 * EDIT_SL_InvalidateText
1710 * Called from EDIT_InvalidateText().
1711 * Does the job for single-line controls only.
1714 static void EDIT_SL_InvalidateText(WND
*wnd
, EDITSTATE
*es
, INT start
, INT end
)
1719 EDIT_GetLineRect(wnd
, es
, 0, start
, end
, &line_rect
);
1720 if (IntersectRect(&rc
, &line_rect
, &es
->format_rect
))
1721 EDIT_UpdateText(wnd
, &rc
, FALSE
);
1725 /*********************************************************************
1727 * EDIT_ML_InvalidateText
1729 * Called from EDIT_InvalidateText().
1730 * Does the job for multi-line controls only.
1733 static void EDIT_ML_InvalidateText(WND
*wnd
, EDITSTATE
*es
, INT start
, INT end
)
1735 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
1736 INT sl
= EDIT_EM_LineFromChar(es
, start
);
1737 INT el
= EDIT_EM_LineFromChar(es
, end
);
1746 if ((el
< es
->y_offset
) || (sl
> es
->y_offset
+ vlc
))
1749 sc
= start
- EDIT_EM_LineIndex(es
, sl
);
1750 ec
= end
- EDIT_EM_LineIndex(es
, el
);
1751 if (sl
< es
->y_offset
) {
1755 if (el
> es
->y_offset
+ vlc
) {
1756 el
= es
->y_offset
+ vlc
;
1757 ec
= EDIT_EM_LineLength(es
, EDIT_EM_LineIndex(es
, el
));
1759 GetClientRect(wnd
->hwndSelf
, &rc1
);
1760 IntersectRect(&rcWnd
, &rc1
, &es
->format_rect
);
1762 EDIT_GetLineRect(wnd
, es
, sl
, sc
, ec
, &rcLine
);
1763 if (IntersectRect(&rcUpdate
, &rcWnd
, &rcLine
))
1764 EDIT_UpdateText(wnd
, &rcUpdate
, FALSE
);
1766 EDIT_GetLineRect(wnd
, es
, sl
, sc
,
1767 EDIT_EM_LineLength(es
,
1768 EDIT_EM_LineIndex(es
, sl
)),
1770 if (IntersectRect(&rcUpdate
, &rcWnd
, &rcLine
))
1771 EDIT_UpdateText(wnd
, &rcUpdate
, FALSE
);
1772 for (l
= sl
+ 1 ; l
< el
; l
++) {
1773 EDIT_GetLineRect(wnd
, es
, l
, 0,
1774 EDIT_EM_LineLength(es
,
1775 EDIT_EM_LineIndex(es
, l
)),
1777 if (IntersectRect(&rcUpdate
, &rcWnd
, &rcLine
))
1778 EDIT_UpdateText(wnd
, &rcUpdate
, FALSE
);
1780 EDIT_GetLineRect(wnd
, es
, el
, 0, ec
, &rcLine
);
1781 if (IntersectRect(&rcUpdate
, &rcWnd
, &rcLine
))
1782 EDIT_UpdateText(wnd
, &rcUpdate
, FALSE
);
1787 /*********************************************************************
1789 * EDIT_InvalidateText
1791 * Invalidate the text from offset start upto, but not including,
1792 * offset end. Useful for (re)painting the selection.
1793 * Regions outside the linewidth are not invalidated.
1794 * end == -1 means end == TextLength.
1795 * start and end need not be ordered.
1798 static void EDIT_InvalidateText(WND
*wnd
, EDITSTATE
*es
, INT start
, INT end
)
1804 end
= strlenW(es
->text
);
1806 ORDER_INT(start
, end
);
1808 if (es
->style
& ES_MULTILINE
)
1809 EDIT_ML_InvalidateText(wnd
, es
, start
, end
);
1811 EDIT_SL_InvalidateText(wnd
, es
, start
, end
);
1815 /*********************************************************************
1819 * Try to fit size + 1 characters in the buffer. Constrain to limits.
1822 static BOOL
EDIT_MakeFit(WND
*wnd
, EDITSTATE
*es
, UINT size
)
1826 if (size
<= es
->buffer_size
)
1828 if (size
> es
->buffer_limit
) {
1829 EDIT_NOTIFY_PARENT(es
, EN_MAXTEXT
, "EN_MAXTEXT");
1832 if (size
> es
->buffer_limit
)
1833 size
= es
->buffer_limit
;
1835 TRACE("trying to ReAlloc to %d+1 characters\n", size
);
1837 /* Force edit to unlock it's buffer. es->text now NULL */
1838 EDIT_UnlockBuffer(wnd
, es
, TRUE
);
1841 UINT alloc_size
= ROUND_TO_GROW((size
+ 1) * sizeof(WCHAR
));
1842 if ((hNew32W
= LocalReAlloc(es
->hloc32W
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
))) {
1843 TRACE("Old 32 bit handle %08x, new handle %08x\n", es
->hloc32W
, hNew32W
);
1844 es
->hloc32W
= hNew32W
;
1845 es
->buffer_size
= LocalSize(hNew32W
)/sizeof(WCHAR
) - 1;
1849 EDIT_LockBuffer(wnd
, es
);
1851 if (es
->buffer_size
< size
) {
1852 WARN("FAILED ! We now have %d+1\n", es
->buffer_size
);
1853 EDIT_NOTIFY_PARENT(es
, EN_ERRSPACE
, "EN_ERRSPACE");
1856 TRACE("We now have %d+1\n", es
->buffer_size
);
1862 /*********************************************************************
1866 * Try to fit size + 1 bytes in the undo buffer.
1869 static BOOL
EDIT_MakeUndoFit(EDITSTATE
*es
, UINT size
)
1873 if (size
<= es
->undo_buffer_size
)
1876 TRACE("trying to ReAlloc to %d+1\n", size
);
1878 alloc_size
= ROUND_TO_GROW((size
+ 1) * sizeof(WCHAR
));
1879 if ((es
->undo_text
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, es
->undo_text
, alloc_size
))) {
1880 es
->undo_buffer_size
= alloc_size
/sizeof(WCHAR
);
1885 WARN("FAILED ! We now have %d+1\n", es
->undo_buffer_size
);
1891 /*********************************************************************
1896 static void EDIT_MoveBackward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
1898 INT e
= es
->selection_end
;
1902 if ((es
->style
& ES_MULTILINE
) && e
&&
1903 (es
->text
[e
- 1] == '\r') && (es
->text
[e
] == '\n')) {
1905 if (e
&& (es
->text
[e
- 1] == '\r'))
1909 EDIT_EM_SetSel(wnd
, es
, extend
? es
->selection_start
: e
, e
, FALSE
);
1910 EDIT_EM_ScrollCaret(wnd
, es
);
1914 /*********************************************************************
1918 * Only for multi line controls
1919 * Move the caret one line down, on a column with the nearest
1920 * x coordinate on the screen (might be a different column).
1923 static void EDIT_MoveDown_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
1925 INT s
= es
->selection_start
;
1926 INT e
= es
->selection_end
;
1927 BOOL after_wrap
= (es
->flags
& EF_AFTER_WRAP
);
1928 LRESULT pos
= EDIT_EM_PosFromChar(wnd
, es
, e
, after_wrap
);
1929 INT x
= SLOWORD(pos
);
1930 INT y
= SHIWORD(pos
);
1932 e
= EDIT_CharFromPos(wnd
, es
, x
, y
+ es
->line_height
, &after_wrap
);
1935 EDIT_EM_SetSel(wnd
, es
, s
, e
, after_wrap
);
1936 EDIT_EM_ScrollCaret(wnd
, es
);
1940 /*********************************************************************
1945 static void EDIT_MoveEnd(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
1947 BOOL after_wrap
= FALSE
;
1950 /* Pass a high value in x to make sure of receiving the end of the line */
1951 if (es
->style
& ES_MULTILINE
)
1952 e
= EDIT_CharFromPos(wnd
, es
, 0x3fffffff,
1953 HIWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
)), &after_wrap
);
1955 e
= strlenW(es
->text
);
1956 EDIT_EM_SetSel(wnd
, es
, extend
? es
->selection_start
: e
, e
, after_wrap
);
1957 EDIT_EM_ScrollCaret(wnd
, es
);
1961 /*********************************************************************
1966 static void EDIT_MoveForward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
1968 INT e
= es
->selection_end
;
1972 if ((es
->style
& ES_MULTILINE
) && (es
->text
[e
- 1] == '\r')) {
1973 if (es
->text
[e
] == '\n')
1975 else if ((es
->text
[e
] == '\r') && (es
->text
[e
+ 1] == '\n'))
1979 EDIT_EM_SetSel(wnd
, es
, extend
? es
->selection_start
: e
, e
, FALSE
);
1980 EDIT_EM_ScrollCaret(wnd
, es
);
1984 /*********************************************************************
1988 * Home key: move to beginning of line.
1991 static void EDIT_MoveHome(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
1995 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1996 if (es
->style
& ES_MULTILINE
)
1997 e
= EDIT_CharFromPos(wnd
, es
, -es
->x_offset
,
1998 HIWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
)), NULL
);
2001 EDIT_EM_SetSel(wnd
, es
, extend
? es
->selection_start
: e
, e
, FALSE
);
2002 EDIT_EM_ScrollCaret(wnd
, es
);
2006 /*********************************************************************
2008 * EDIT_MovePageDown_ML
2010 * Only for multi line controls
2011 * Move the caret one page down, on a column with the nearest
2012 * x coordinate on the screen (might be a different column).
2015 static void EDIT_MovePageDown_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
2017 INT s
= es
->selection_start
;
2018 INT e
= es
->selection_end
;
2019 BOOL after_wrap
= (es
->flags
& EF_AFTER_WRAP
);
2020 LRESULT pos
= EDIT_EM_PosFromChar(wnd
, es
, e
, after_wrap
);
2021 INT x
= SLOWORD(pos
);
2022 INT y
= SHIWORD(pos
);
2024 e
= EDIT_CharFromPos(wnd
, es
, x
,
2025 y
+ (es
->format_rect
.bottom
- es
->format_rect
.top
),
2029 EDIT_EM_SetSel(wnd
, es
, s
, e
, after_wrap
);
2030 EDIT_EM_ScrollCaret(wnd
, es
);
2034 /*********************************************************************
2036 * EDIT_MovePageUp_ML
2038 * Only for multi line controls
2039 * Move the caret one page up, on a column with the nearest
2040 * x coordinate on the screen (might be a different column).
2043 static void EDIT_MovePageUp_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
2045 INT s
= es
->selection_start
;
2046 INT e
= es
->selection_end
;
2047 BOOL after_wrap
= (es
->flags
& EF_AFTER_WRAP
);
2048 LRESULT pos
= EDIT_EM_PosFromChar(wnd
, es
, e
, after_wrap
);
2049 INT x
= SLOWORD(pos
);
2050 INT y
= SHIWORD(pos
);
2052 e
= EDIT_CharFromPos(wnd
, es
, x
,
2053 y
- (es
->format_rect
.bottom
- es
->format_rect
.top
),
2057 EDIT_EM_SetSel(wnd
, es
, s
, e
, after_wrap
);
2058 EDIT_EM_ScrollCaret(wnd
, es
);
2062 /*********************************************************************
2066 * Only for multi line controls
2067 * Move the caret one line up, on a column with the nearest
2068 * x coordinate on the screen (might be a different column).
2071 static void EDIT_MoveUp_ML(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
2073 INT s
= es
->selection_start
;
2074 INT e
= es
->selection_end
;
2075 BOOL after_wrap
= (es
->flags
& EF_AFTER_WRAP
);
2076 LRESULT pos
= EDIT_EM_PosFromChar(wnd
, es
, e
, after_wrap
);
2077 INT x
= SLOWORD(pos
);
2078 INT y
= SHIWORD(pos
);
2080 e
= EDIT_CharFromPos(wnd
, es
, x
, y
- es
->line_height
, &after_wrap
);
2083 EDIT_EM_SetSel(wnd
, es
, s
, e
, after_wrap
);
2084 EDIT_EM_ScrollCaret(wnd
, es
);
2088 /*********************************************************************
2090 * EDIT_MoveWordBackward
2093 static void EDIT_MoveWordBackward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
2095 INT s
= es
->selection_start
;
2096 INT e
= es
->selection_end
;
2101 l
= EDIT_EM_LineFromChar(es
, e
);
2102 ll
= EDIT_EM_LineLength(es
, e
);
2103 li
= EDIT_EM_LineIndex(es
, l
);
2106 li
= EDIT_EM_LineIndex(es
, l
- 1);
2107 e
= li
+ EDIT_EM_LineLength(es
, li
);
2110 e
= li
+ (INT
)EDIT_CallWordBreakProc(es
,
2111 li
, e
- li
, ll
, WB_LEFT
);
2115 EDIT_EM_SetSel(wnd
, es
, s
, e
, FALSE
);
2116 EDIT_EM_ScrollCaret(wnd
, es
);
2120 /*********************************************************************
2122 * EDIT_MoveWordForward
2125 static void EDIT_MoveWordForward(WND
*wnd
, EDITSTATE
*es
, BOOL extend
)
2127 INT s
= es
->selection_start
;
2128 INT e
= es
->selection_end
;
2133 l
= EDIT_EM_LineFromChar(es
, e
);
2134 ll
= EDIT_EM_LineLength(es
, e
);
2135 li
= EDIT_EM_LineIndex(es
, l
);
2137 if ((es
->style
& ES_MULTILINE
) && (l
!= es
->line_count
- 1))
2138 e
= EDIT_EM_LineIndex(es
, l
+ 1);
2140 e
= li
+ EDIT_CallWordBreakProc(es
,
2141 li
, e
- li
+ 1, ll
, WB_RIGHT
);
2145 EDIT_EM_SetSel(wnd
, es
, s
, e
, FALSE
);
2146 EDIT_EM_ScrollCaret(wnd
, es
);
2150 /*********************************************************************
2155 static void EDIT_PaintLine(WND
*wnd
, EDITSTATE
*es
, HDC dc
, INT line
, BOOL rev
)
2157 INT s
= es
->selection_start
;
2158 INT e
= es
->selection_end
;
2165 if (es
->style
& ES_MULTILINE
) {
2166 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
2167 if ((line
< es
->y_offset
) || (line
> es
->y_offset
+ vlc
) || (line
>= es
->line_count
))
2172 TRACE("line=%d\n", line
);
2174 pos
= EDIT_EM_PosFromChar(wnd
, es
, EDIT_EM_LineIndex(es
, line
), FALSE
);
2177 li
= EDIT_EM_LineIndex(es
, line
);
2178 ll
= EDIT_EM_LineLength(es
, li
);
2179 s
= es
->selection_start
;
2180 e
= es
->selection_end
;
2182 s
= min(li
+ ll
, max(li
, s
));
2183 e
= min(li
+ ll
, max(li
, e
));
2184 if (rev
&& (s
!= e
) &&
2185 ((es
->flags
& EF_FOCUSED
) || (es
->style
& ES_NOHIDESEL
))) {
2186 x
+= EDIT_PaintText(es
, dc
, x
, y
, line
, 0, s
- li
, FALSE
);
2187 x
+= EDIT_PaintText(es
, dc
, x
, y
, line
, s
- li
, e
- s
, TRUE
);
2188 x
+= EDIT_PaintText(es
, dc
, x
, y
, line
, e
- li
, li
+ ll
- e
, FALSE
);
2190 x
+= EDIT_PaintText(es
, dc
, x
, y
, line
, 0, ll
, FALSE
);
2194 /*********************************************************************
2199 static INT
EDIT_PaintText(EDITSTATE
*es
, HDC dc
, INT x
, INT y
, INT line
, INT col
, INT count
, BOOL rev
)
2210 BkMode
= GetBkMode(dc
);
2211 BkColor
= GetBkColor(dc
);
2212 TextColor
= GetTextColor(dc
);
2214 SetBkColor(dc
, GetSysColor(COLOR_HIGHLIGHT
));
2215 SetTextColor(dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
2216 SetBkMode( dc
, OPAQUE
);
2218 li
= EDIT_EM_LineIndex(es
, line
);
2219 if (es
->style
& ES_MULTILINE
) {
2220 ret
= (INT
)LOWORD(TabbedTextOutW(dc
, x
, y
, es
->text
+ li
+ col
, count
,
2221 es
->tabs_count
, es
->tabs
, es
->format_rect
.left
- es
->x_offset
));
2223 LPWSTR text
= EDIT_GetPasswordPointer_SL(es
);
2224 TextOutW(dc
, x
, y
, text
+ li
+ col
, count
);
2225 GetTextExtentPoint32W(dc
, text
+ li
+ col
, count
, &size
);
2227 if (es
->style
& ES_PASSWORD
)
2228 HeapFree(GetProcessHeap(), 0, text
);
2231 SetBkColor(dc
, BkColor
);
2232 SetTextColor(dc
, TextColor
);
2233 SetBkMode( dc
, BkMode
);
2239 /*********************************************************************
2244 static void EDIT_SetCaretPos(WND
*wnd
, EDITSTATE
*es
, INT pos
,
2247 LRESULT res
= EDIT_EM_PosFromChar(wnd
, es
, pos
, after_wrap
);
2248 SetCaretPos(SLOWORD(res
), SHIWORD(res
));
2252 /*********************************************************************
2256 * note: this is not (exactly) the handler called on EM_SETRECTNP
2257 * it is also used to set the rect of a single line control
2260 static void EDIT_SetRectNP(WND
*wnd
, EDITSTATE
*es
, LPRECT rc
)
2262 CopyRect(&es
->format_rect
, rc
);
2263 if (es
->style
& WS_BORDER
) {
2264 INT bw
= GetSystemMetrics(SM_CXBORDER
) + 1;
2265 if(TWEAK_WineLook
== WIN31_LOOK
)
2267 es
->format_rect
.left
+= bw
;
2268 es
->format_rect
.top
+= bw
;
2269 es
->format_rect
.right
-= bw
;
2270 es
->format_rect
.bottom
-= bw
;
2272 es
->format_rect
.left
+= es
->left_margin
;
2273 es
->format_rect
.right
-= es
->right_margin
;
2274 es
->format_rect
.right
= max(es
->format_rect
.right
, es
->format_rect
.left
+ es
->char_width
);
2275 if (es
->style
& ES_MULTILINE
)
2277 INT fw
, vlc
, max_x_offset
, max_y_offset
;
2279 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
2280 es
->format_rect
.bottom
= es
->format_rect
.top
+ max(1, vlc
) * es
->line_height
;
2282 /* correct es->x_offset */
2283 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
2284 max_x_offset
= es
->text_width
- fw
;
2285 if(max_x_offset
< 0) max_x_offset
= 0;
2286 if(es
->x_offset
> max_x_offset
)
2287 es
->x_offset
= max_x_offset
;
2289 /* correct es->y_offset */
2290 max_y_offset
= es
->line_count
- vlc
;
2291 if(max_y_offset
< 0) max_y_offset
= 0;
2292 if(es
->y_offset
> max_y_offset
)
2293 es
->y_offset
= max_y_offset
;
2295 /* force scroll info update */
2296 EDIT_UpdateScrollInfo(wnd
, es
);
2299 /* Windows doesn't care to fix text placement for SL controls */
2300 es
->format_rect
.bottom
= es
->format_rect
.top
+ es
->line_height
;
2302 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
))
2303 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
2307 /*********************************************************************
2312 static void EDIT_UnlockBuffer(WND
*wnd
, EDITSTATE
*es
, BOOL force
)
2314 /* Edit window might be already destroyed */
2315 if(!IsWindow(wnd
->hwndSelf
))
2317 WARN("edit wnd %04x already destroyed\n", wnd
->hwndSelf
);
2322 ERR("no EDITSTATE ... please report\n");
2325 if (!es
->lock_count
) {
2326 ERR("lock_count == 0 ... please report\n");
2330 ERR("es->text == 0 ... please report\n");
2334 if (force
|| (es
->lock_count
== 1)) {
2337 BOOL _16bit
= FALSE
;
2339 UINT countW
= strlenW(es
->text
) + 1;
2343 UINT countA_new
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, NULL
, 0, NULL
, NULL
);
2344 TRACE("Synchronizing with 32-bit ANSI buffer\n");
2345 TRACE("%d WCHARs translated to %d bytes\n", countW
, countA_new
);
2346 countA
= LocalSize(es
->hloc32A
);
2347 if(countA_new
> countA
)
2350 UINT alloc_size
= ROUND_TO_GROW(countA_new
);
2351 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA
, alloc_size
);
2352 hloc32A_new
= LocalReAlloc(es
->hloc32A
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
);
2355 es
->hloc32A
= hloc32A_new
;
2356 countA
= LocalSize(hloc32A_new
);
2357 TRACE("Real new size %d bytes\n", countA
);
2360 WARN("FAILED! Will synchronize partially\n");
2362 textA
= LocalLock(es
->hloc32A
);
2366 UINT countA_new
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, NULL
, 0, NULL
, NULL
);
2367 TRACE("Synchronizing with 16-bit ANSI buffer\n");
2368 TRACE("%d WCHARs translated to %d bytes\n", countW
, countA_new
);
2369 countA
= LOCAL_Size(wnd
->hInstance
, es
->hloc16
);
2370 if(countA_new
> countA
)
2372 HLOCAL16 hloc16_new
;
2373 UINT alloc_size
= ROUND_TO_GROW(countA_new
);
2374 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA
, alloc_size
);
2375 hloc16_new
= LOCAL_ReAlloc(wnd
->hInstance
, es
->hloc16
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
);
2378 es
->hloc16
= hloc16_new
;
2379 countA
= LOCAL_Size(wnd
->hInstance
, hloc16_new
);
2380 TRACE("Real new size %d bytes\n", countA
);
2383 WARN("FAILED! Will synchronize partially\n");
2385 textA
= LOCAL_Lock(wnd
->hInstance
, es
->hloc16
);
2391 WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, textA
, countA
, NULL
, NULL
);
2393 LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
);
2395 LocalUnlock(es
->hloc32A
);
2398 LocalUnlock(es
->hloc32W
);
2402 ERR("no buffer ... please report\n");
2410 /*********************************************************************
2412 * EDIT_UpdateScrollInfo
2415 static void EDIT_UpdateScrollInfo(WND
*wnd
, EDITSTATE
*es
)
2417 if ((es
->style
& WS_VSCROLL
) && !(es
->flags
& EF_VSCROLL_TRACK
))
2420 si
.cbSize
= sizeof(SCROLLINFO
);
2421 si
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
2423 si
.nMax
= es
->line_count
- 1;
2424 si
.nPage
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
2425 si
.nPos
= es
->y_offset
;
2426 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2427 si
.nMin
, si
.nMax
, si
.nPage
, si
.nPos
);
2428 SetScrollInfo(wnd
->hwndSelf
, SB_VERT
, &si
, TRUE
);
2431 if ((es
->style
& WS_HSCROLL
) && !(es
->flags
& EF_HSCROLL_TRACK
))
2434 si
.cbSize
= sizeof(SCROLLINFO
);
2435 si
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
2437 si
.nMax
= es
->text_width
- 1;
2438 si
.nPage
= es
->format_rect
.right
- es
->format_rect
.left
;
2439 si
.nPos
= es
->x_offset
;
2440 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2441 si
.nMin
, si
.nMax
, si
.nPage
, si
.nPos
);
2442 SetScrollInfo(wnd
->hwndSelf
, SB_HORZ
, &si
, TRUE
);
2446 /*********************************************************************
2448 * EDIT_WordBreakProc
2450 * Find the beginning of words.
2451 * Note: unlike the specs for a WordBreakProc, this function only
2452 * allows to be called without linebreaks between s[0] upto
2453 * s[count - 1]. Remember it is only called
2454 * internally, so we can decide this for ourselves.
2457 static INT CALLBACK
EDIT_WordBreakProc(LPWSTR s
, INT index
, INT count
, INT action
)
2461 TRACE("s=%p, index=%d, count=%d, action=%d\n", s
, index
, count
, action
);
2471 if (s
[index
] == ' ') {
2472 while (index
&& (s
[index
] == ' '))
2475 while (index
&& (s
[index
] != ' '))
2477 if (s
[index
] == ' ')
2481 while (index
&& (s
[index
] != ' '))
2483 if (s
[index
] == ' ')
2493 if (s
[index
] == ' ')
2494 while ((index
< count
) && (s
[index
] == ' ')) index
++;
2496 while (s
[index
] && (s
[index
] != ' ') && (index
< count
))
2498 while ((s
[index
] == ' ') && (index
< count
)) index
++;
2502 case WB_ISDELIMITER
:
2503 ret
= (s
[index
] == ' ');
2506 ERR("unknown action code, please report !\n");
2513 /*********************************************************************
2517 * returns line number (not index) in high-order word of result.
2518 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2519 * to Richedit, not to the edit control. Original documentation is valid.
2520 * FIXME: do the specs mean to return -1 if outside client area or
2521 * if outside formatting rectangle ???
2524 static LRESULT
EDIT_EM_CharFromPos(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
2532 GetClientRect(wnd
->hwndSelf
, &rc
);
2533 if (!PtInRect(&rc
, pt
))
2536 index
= EDIT_CharFromPos(wnd
, es
, x
, y
, NULL
);
2537 return MAKELONG(index
, EDIT_EM_LineFromChar(es
, index
));
2541 /*********************************************************************
2545 * Enable or disable soft breaks.
2547 static BOOL
EDIT_EM_FmtLines(EDITSTATE
*es
, BOOL add_eol
)
2549 es
->flags
&= ~EF_USE_SOFTBRK
;
2551 es
->flags
|= EF_USE_SOFTBRK
;
2552 FIXME("soft break enabled, not implemented\n");
2558 /*********************************************************************
2562 * Hopefully this won't fire back at us.
2563 * We always start with a fixed buffer in the local heap.
2564 * Despite of the documentation says that the local heap is used
2565 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2566 * buffer on the local heap.
2569 static HLOCAL
EDIT_EM_GetHandle(EDITSTATE
*es
)
2573 if (!(es
->style
& ES_MULTILINE
))
2577 hLocal
= es
->hloc32W
;
2583 UINT countA
, alloc_size
;
2584 TRACE("Allocating 32-bit ANSI alias buffer\n");
2585 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, NULL
, 0, NULL
, NULL
);
2586 alloc_size
= ROUND_TO_GROW(countA
);
2587 if(!(es
->hloc32A
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
)))
2589 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size
);
2592 textA
= LocalLock(es
->hloc32A
);
2593 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, countA
, NULL
, NULL
);
2594 LocalUnlock(es
->hloc32A
);
2596 hLocal
= es
->hloc32A
;
2599 TRACE("Returning %04X, LocalSize() = %d\n", hLocal
, LocalSize(hLocal
));
2604 /*********************************************************************
2608 * Hopefully this won't fire back at us.
2609 * We always start with a buffer in 32 bit linear memory.
2610 * However, with this message a 16 bit application requests
2611 * a handle of 16 bit local heap memory, where it expects to find
2613 * It's a pitty that from this moment on we have to use this
2614 * local heap, because applications may rely on the handle
2617 * In this function we'll try to switch to local heap.
2619 static HLOCAL16
EDIT_EM_GetHandle16(WND
*wnd
, EDITSTATE
*es
)
2622 UINT countA
, alloc_size
;
2624 if (!(es
->style
& ES_MULTILINE
))
2630 if (!LOCAL_HeapSize(wnd
->hInstance
)) {
2631 if (!LocalInit16(wnd
->hInstance
, 0,
2632 GlobalSize16(wnd
->hInstance
))) {
2633 ERR("could not initialize local heap\n");
2636 TRACE("local heap initialized\n");
2639 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, NULL
, 0, NULL
, NULL
);
2640 alloc_size
= ROUND_TO_GROW(countA
);
2642 TRACE("Allocating 16-bit ANSI alias buffer\n");
2643 if (!(es
->hloc16
= LOCAL_Alloc(wnd
->hInstance
, LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
))) {
2644 ERR("could not allocate new 16 bit buffer\n");
2648 if (!(textA
= (LPSTR
)LOCAL_Lock(wnd
->hInstance
, es
->hloc16
))) {
2649 ERR("could not lock new 16 bit buffer\n");
2650 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
2655 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, countA
, NULL
, NULL
);
2656 LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
);
2658 TRACE("Returning %04X, LocalSize() = %d\n", es
->hloc16
, LOCAL_Size(wnd
->hInstance
, es
->hloc16
));
2663 /*********************************************************************
2668 static INT
EDIT_EM_GetLine(EDITSTATE
*es
, INT line
, LPARAM lParam
, BOOL unicode
)
2671 INT line_len
, dst_len
;
2674 if (es
->style
& ES_MULTILINE
) {
2675 if (line
>= es
->line_count
)
2679 i
= EDIT_EM_LineIndex(es
, line
);
2681 line_len
= EDIT_EM_LineLength(es
, i
);
2682 dst_len
= *(WORD
*)lParam
;
2685 LPWSTR dst
= (LPWSTR
)lParam
;
2686 if(dst_len
<= line_len
)
2688 memcpy(dst
, src
, dst_len
* sizeof(WCHAR
));
2691 else /* Append 0 if enough space */
2693 memcpy(dst
, src
, line_len
* sizeof(WCHAR
));
2700 LPSTR dst
= (LPSTR
)lParam
;
2702 ret
= WideCharToMultiByte(CP_ACP
, 0, src
, line_len
, dst
, dst_len
, NULL
, NULL
);
2703 if(!ret
) /* Insufficient buffer size */
2705 if(ret
< dst_len
) /* Append 0 if enough space */
2712 /*********************************************************************
2717 static LRESULT
EDIT_EM_GetSel(EDITSTATE
*es
, LPUINT start
, LPUINT end
)
2719 UINT s
= es
->selection_start
;
2720 UINT e
= es
->selection_end
;
2727 return MAKELONG(s
, e
);
2731 /*********************************************************************
2735 * FIXME: is this right ? (or should it be only VSCROLL)
2736 * (and maybe only for edit controls that really have their
2737 * own scrollbars) (and maybe only for multiline controls ?)
2738 * All in all: very poorly documented
2741 static LRESULT
EDIT_EM_GetThumb(WND
*wnd
, EDITSTATE
*es
)
2743 return MAKELONG(EDIT_WM_VScroll(wnd
, es
, EM_GETTHUMB16
, 0),
2744 EDIT_WM_HScroll(wnd
, es
, EM_GETTHUMB16
, 0));
2748 /*********************************************************************
2753 static INT
EDIT_EM_LineFromChar(EDITSTATE
*es
, INT index
)
2758 if (!(es
->style
& ES_MULTILINE
))
2760 if (index
> (INT
)strlenW(es
->text
))
2761 return es
->line_count
- 1;
2763 index
= min(es
->selection_start
, es
->selection_end
);
2766 line_def
= es
->first_line_def
;
2767 index
-= line_def
->length
;
2768 while ((index
>= 0) && line_def
->next
) {
2770 line_def
= line_def
->next
;
2771 index
-= line_def
->length
;
2777 /*********************************************************************
2782 static INT
EDIT_EM_LineIndex(EDITSTATE
*es
, INT line
)
2787 if (!(es
->style
& ES_MULTILINE
))
2789 if (line
>= es
->line_count
)
2793 line_def
= es
->first_line_def
;
2795 INT index
= es
->selection_end
- line_def
->length
;
2796 while ((index
>= 0) && line_def
->next
) {
2797 line_index
+= line_def
->length
;
2798 line_def
= line_def
->next
;
2799 index
-= line_def
->length
;
2803 line_index
+= line_def
->length
;
2804 line_def
= line_def
->next
;
2812 /*********************************************************************
2817 static INT
EDIT_EM_LineLength(EDITSTATE
*es
, INT index
)
2821 if (!(es
->style
& ES_MULTILINE
))
2822 return strlenW(es
->text
);
2825 /* get the number of remaining non-selected chars of selected lines */
2828 li
= EDIT_EM_LineFromChar(es
, es
->selection_start
);
2829 /* # chars before start of selection area */
2830 count
= es
->selection_start
- EDIT_EM_LineIndex(es
, li
);
2831 li
= EDIT_EM_LineFromChar(es
, es
->selection_end
);
2832 /* # chars after end of selection */
2833 count
+= EDIT_EM_LineIndex(es
, li
) +
2834 EDIT_EM_LineLength(es
, li
) - es
->selection_end
;
2837 line_def
= es
->first_line_def
;
2838 index
-= line_def
->length
;
2839 while ((index
>= 0) && line_def
->next
) {
2840 line_def
= line_def
->next
;
2841 index
-= line_def
->length
;
2843 return line_def
->net_length
;
2847 /*********************************************************************
2851 * NOTE: dx is in average character widths, dy - in lines;
2854 static BOOL
EDIT_EM_LineScroll(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
)
2856 if (!(es
->style
& ES_MULTILINE
))
2859 dx
*= es
->char_width
;
2860 return EDIT_EM_LineScroll_internal(wnd
, es
, dx
, dy
);
2863 /*********************************************************************
2865 * EDIT_EM_LineScroll_internal
2867 * Version of EDIT_EM_LineScroll for internal use.
2868 * It doesn't refuse if ES_MULTILINE is set and assumes that
2869 * dx is in pixels, dy - in lines.
2872 static BOOL
EDIT_EM_LineScroll_internal(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
)
2875 INT x_offset_in_pixels
;
2877 if (es
->style
& ES_MULTILINE
)
2879 x_offset_in_pixels
= es
->x_offset
;
2884 x_offset_in_pixels
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->x_offset
, FALSE
));
2887 if (-dx
> x_offset_in_pixels
)
2888 dx
= -x_offset_in_pixels
;
2889 if (dx
> es
->text_width
- x_offset_in_pixels
)
2890 dx
= es
->text_width
- x_offset_in_pixels
;
2891 nyoff
= max(0, es
->y_offset
+ dy
);
2892 if (nyoff
>= es
->line_count
)
2893 nyoff
= es
->line_count
- 1;
2894 dy
= (es
->y_offset
- nyoff
) * es
->line_height
;
2899 es
->y_offset
= nyoff
;
2900 if(es
->style
& ES_MULTILINE
)
2903 es
->x_offset
+= dx
/ es
->char_width
;
2905 GetClientRect(wnd
->hwndSelf
, &rc1
);
2906 IntersectRect(&rc
, &rc1
, &es
->format_rect
);
2907 ScrollWindowEx(wnd
->hwndSelf
, -dx
, dy
,
2908 NULL
, &rc
, (HRGN
)NULL
, NULL
, SW_INVALIDATE
);
2909 /* force scroll info update */
2910 EDIT_UpdateScrollInfo(wnd
, es
);
2912 if (dx
&& !(es
->flags
& EF_HSCROLL_TRACK
))
2913 EDIT_NOTIFY_PARENT(es
, EN_HSCROLL
, "EN_HSCROLL");
2914 if (dy
&& !(es
->flags
& EF_VSCROLL_TRACK
))
2915 EDIT_NOTIFY_PARENT(es
, EN_VSCROLL
, "EN_VSCROLL");
2920 /*********************************************************************
2925 static LRESULT
EDIT_EM_PosFromChar(WND
*wnd
, EDITSTATE
*es
, INT index
, BOOL after_wrap
)
2927 INT len
= strlenW(es
->text
);
2936 index
= min(index
, len
);
2937 dc
= GetDC(wnd
->hwndSelf
);
2939 old_font
= SelectObject(dc
, es
->font
);
2940 if (es
->style
& ES_MULTILINE
) {
2941 l
= EDIT_EM_LineFromChar(es
, index
);
2942 y
= (l
- es
->y_offset
) * es
->line_height
;
2943 li
= EDIT_EM_LineIndex(es
, l
);
2944 if (after_wrap
&& (li
== index
) && l
) {
2946 LINEDEF
*line_def
= es
->first_line_def
;
2948 line_def
= line_def
->next
;
2951 if (line_def
->ending
== END_WRAP
) {
2953 y
-= es
->line_height
;
2954 li
= EDIT_EM_LineIndex(es
, l
);
2957 x
= LOWORD(GetTabbedTextExtentW(dc
, es
->text
+ li
, index
- li
,
2958 es
->tabs_count
, es
->tabs
)) - es
->x_offset
;
2960 LPWSTR text
= EDIT_GetPasswordPointer_SL(es
);
2961 if (index
< es
->x_offset
) {
2962 GetTextExtentPoint32W(dc
, text
+ index
,
2963 es
->x_offset
- index
, &size
);
2966 GetTextExtentPoint32W(dc
, text
+ es
->x_offset
,
2967 index
- es
->x_offset
, &size
);
2971 if (es
->style
& ES_PASSWORD
)
2972 HeapFree(GetProcessHeap(), 0, text
);
2974 x
+= es
->format_rect
.left
;
2975 y
+= es
->format_rect
.top
;
2977 SelectObject(dc
, old_font
);
2978 ReleaseDC(wnd
->hwndSelf
, dc
);
2979 return MAKELONG((INT16
)x
, (INT16
)y
);
2983 /*********************************************************************
2987 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2990 static void EDIT_EM_ReplaceSel(WND
*wnd
, EDITSTATE
*es
, BOOL can_undo
, LPCWSTR lpsz_replace
, BOOL send_update
)
2992 UINT strl
= strlenW(lpsz_replace
);
2993 UINT tl
= strlenW(es
->text
);
3001 TRACE("%s, can_undo %d, send_update %d\n",
3002 debugstr_w(lpsz_replace
), can_undo
, send_update
);
3004 s
= es
->selection_start
;
3005 e
= es
->selection_end
;
3007 if ((s
== e
) && !strl
)
3012 if (!EDIT_MakeFit(wnd
, es
, tl
- (e
- s
) + strl
))
3016 /* there is something to be deleted */
3018 utl
= strlenW(es
->undo_text
);
3019 if (!es
->undo_insert_count
&& (*es
->undo_text
&& (s
== es
->undo_position
))) {
3020 /* undo-buffer is extended to the right */
3021 EDIT_MakeUndoFit(es
, utl
+ e
- s
);
3022 strncpyW(es
->undo_text
+ utl
, es
->text
+ s
, e
- s
+ 1);
3023 (es
->undo_text
+ utl
)[e
- s
] = 0; /* ensure 0 termination */
3024 } else if (!es
->undo_insert_count
&& (*es
->undo_text
&& (e
== es
->undo_position
))) {
3025 /* undo-buffer is extended to the left */
3026 EDIT_MakeUndoFit(es
, utl
+ e
- s
);
3027 for (p
= es
->undo_text
+ utl
; p
>= es
->undo_text
; p
--)
3029 for (i
= 0 , p
= es
->undo_text
; i
< e
- s
; i
++)
3030 p
[i
] = (es
->text
+ s
)[i
];
3031 es
->undo_position
= s
;
3033 /* new undo-buffer */
3034 EDIT_MakeUndoFit(es
, e
- s
);
3035 strncpyW(es
->undo_text
, es
->text
+ s
, e
- s
+ 1);
3036 es
->undo_text
[e
- s
] = 0; /* ensure 0 termination */
3037 es
->undo_position
= s
;
3039 /* any deletion makes the old insertion-undo invalid */
3040 es
->undo_insert_count
= 0;
3042 EDIT_EM_EmptyUndoBuffer(es
);
3045 strcpyW(es
->text
+ s
, es
->text
+ e
);
3048 /* there is an insertion */
3050 if ((s
== es
->undo_position
) ||
3051 ((es
->undo_insert_count
) &&
3052 (s
== es
->undo_position
+ es
->undo_insert_count
)))
3054 * insertion is new and at delete position or
3055 * an extension to either left or right
3057 es
->undo_insert_count
+= strl
;
3059 /* new insertion undo */
3060 es
->undo_position
= s
;
3061 es
->undo_insert_count
= strl
;
3062 /* new insertion makes old delete-buffer invalid */
3063 *es
->undo_text
= '\0';
3066 EDIT_EM_EmptyUndoBuffer(es
);
3069 tl
= strlenW(es
->text
);
3070 for (p
= es
->text
+ tl
; p
>= es
->text
+ s
; p
--)
3072 for (i
= 0 , p
= es
->text
+ s
; i
< strl
; i
++)
3073 p
[i
] = lpsz_replace
[i
];
3074 if(es
->style
& ES_UPPERCASE
)
3075 CharUpperBuffW(p
, strl
);
3076 else if(es
->style
& ES_LOWERCASE
)
3077 CharLowerBuffW(p
, strl
);
3080 if (es
->style
& ES_MULTILINE
)
3082 INT s
= min(es
->selection_start
, es
->selection_end
);
3084 hrgn
= CreateRectRgn(0, 0, 0, 0);
3085 EDIT_BuildLineDefs_ML(wnd
, es
, s
, s
+ strl
,
3086 strl
- (es
->selection_end
- es
->selection_start
), hrgn
);
3089 EDIT_CalcLineWidth_SL(wnd
, es
);
3091 EDIT_EM_SetSel(wnd
, es
, s
, s
, FALSE
);
3092 es
->flags
|= EF_MODIFIED
;
3093 if (send_update
) es
->flags
|= EF_UPDATE
;
3094 EDIT_EM_ScrollCaret(wnd
, es
);
3096 /* force scroll info update */
3097 EDIT_UpdateScrollInfo(wnd
, es
);
3101 EDIT_UpdateTextRegion(wnd
, hrgn
, TRUE
);
3105 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3107 if(es
->flags
& EF_UPDATE
)
3109 es
->flags
&= ~EF_UPDATE
;
3110 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3115 /*********************************************************************
3120 static LRESULT
EDIT_EM_Scroll(WND
*wnd
, EDITSTATE
*es
, INT action
)
3124 if (!(es
->style
& ES_MULTILINE
))
3125 return (LRESULT
)FALSE
;
3135 if (es
->y_offset
< es
->line_count
- 1)
3140 dy
= -(es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3143 if (es
->y_offset
< es
->line_count
- 1)
3144 dy
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3147 return (LRESULT
)FALSE
;
3150 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3151 /* check if we are going to move too far */
3152 if(es
->y_offset
+ dy
> es
->line_count
- vlc
)
3153 dy
= es
->line_count
- vlc
- es
->y_offset
;
3155 /* Notification is done in EDIT_EM_LineScroll */
3157 EDIT_EM_LineScroll(wnd
, es
, 0, dy
);
3159 return MAKELONG((INT16
)dy
, (BOOL16
)TRUE
);
3163 /*********************************************************************
3168 static void EDIT_EM_ScrollCaret(WND
*wnd
, EDITSTATE
*es
)
3170 if (es
->style
& ES_MULTILINE
) {
3175 INT cw
= es
->char_width
;
3180 l
= EDIT_EM_LineFromChar(es
, es
->selection_end
);
3181 li
= EDIT_EM_LineIndex(es
, l
);
3182 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
));
3183 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3184 if (l
>= es
->y_offset
+ vlc
)
3185 dy
= l
- vlc
+ 1 - es
->y_offset
;
3186 if (l
< es
->y_offset
)
3187 dy
= l
- es
->y_offset
;
3188 ww
= es
->format_rect
.right
- es
->format_rect
.left
;
3189 if (x
< es
->format_rect
.left
)
3190 dx
= x
- es
->format_rect
.left
- ww
/ HSCROLL_FRACTION
/ cw
* cw
;
3191 if (x
> es
->format_rect
.right
)
3192 dx
= x
- es
->format_rect
.left
- (HSCROLL_FRACTION
- 1) * ww
/ HSCROLL_FRACTION
/ cw
* cw
;
3195 /* check if we are going to move too far */
3196 if(es
->x_offset
+ dx
+ ww
> es
->text_width
)
3197 dx
= es
->text_width
- ww
- es
->x_offset
;
3199 EDIT_EM_LineScroll_internal(wnd
, es
, dx
, dy
);
3206 if (!(es
->style
& ES_AUTOHSCROLL
))
3209 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3210 format_width
= es
->format_rect
.right
- es
->format_rect
.left
;
3211 if (x
< es
->format_rect
.left
) {
3212 goal
= es
->format_rect
.left
+ format_width
/ HSCROLL_FRACTION
;
3215 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3216 } while ((x
< goal
) && es
->x_offset
);
3217 /* FIXME: use ScrollWindow() somehow to improve performance */
3218 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3219 } else if (x
> es
->format_rect
.right
) {
3221 INT len
= strlenW(es
->text
);
3222 goal
= es
->format_rect
.right
- format_width
/ HSCROLL_FRACTION
;
3225 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3226 x_last
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, len
, FALSE
));
3227 } while ((x
> goal
) && (x_last
> es
->format_rect
.right
));
3228 /* FIXME: use ScrollWindow() somehow to improve performance */
3229 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3233 if(es
->flags
& EF_FOCUSED
)
3234 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
);
3238 /*********************************************************************
3242 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3245 static void EDIT_EM_SetHandle(WND
*wnd
, EDITSTATE
*es
, HLOCAL hloc
)
3247 if (!(es
->style
& ES_MULTILINE
))
3251 WARN("called with NULL handle\n");
3255 EDIT_UnlockBuffer(wnd
, es
, TRUE
);
3259 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
3260 es
->hloc16
= (HLOCAL16
)NULL
;
3267 LocalFree(es
->hloc32A
);
3268 es
->hloc32A
= (HLOCAL
)NULL
;
3279 countA
= LocalSize(hloc
);
3280 textA
= LocalLock(hloc
);
3281 countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, NULL
, 0);
3282 if(!(hloc32W_new
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, countW
* sizeof(WCHAR
))))
3284 ERR("Could not allocate new unicode buffer\n");
3287 textW
= LocalLock(hloc32W_new
);
3288 MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, textW
, countW
);
3289 LocalUnlock(hloc32W_new
);
3293 LocalFree(es
->hloc32W
);
3295 es
->hloc32W
= hloc32W_new
;
3299 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
3301 EDIT_LockBuffer(wnd
, es
);
3303 es
->x_offset
= es
->y_offset
= 0;
3304 es
->selection_start
= es
->selection_end
= 0;
3305 EDIT_EM_EmptyUndoBuffer(es
);
3306 es
->flags
&= ~EF_MODIFIED
;
3307 es
->flags
&= ~EF_UPDATE
;
3308 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3309 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3310 EDIT_EM_ScrollCaret(wnd
, es
);
3311 /* force scroll info update */
3312 EDIT_UpdateScrollInfo(wnd
, es
);
3316 /*********************************************************************
3320 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3323 static void EDIT_EM_SetHandle16(WND
*wnd
, EDITSTATE
*es
, HLOCAL16 hloc
)
3330 if (!(es
->style
& ES_MULTILINE
))
3334 WARN("called with NULL handle\n");
3338 EDIT_UnlockBuffer(wnd
, es
, TRUE
);
3342 LocalFree(es
->hloc32A
);
3343 es
->hloc32A
= (HLOCAL
)NULL
;
3346 countA
= LOCAL_Size(wnd
->hInstance
, hloc
);
3347 textA
= LOCAL_Lock(wnd
->hInstance
, hloc
);
3348 countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, NULL
, 0);
3349 if(!(hloc32W_new
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, countW
* sizeof(WCHAR
))))
3351 ERR("Could not allocate new unicode buffer\n");
3354 textW
= LocalLock(hloc32W_new
);
3355 MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, textW
, countW
);
3356 LocalUnlock(hloc32W_new
);
3357 LOCAL_Unlock(wnd
->hInstance
, hloc
);
3360 LocalFree(es
->hloc32W
);
3362 es
->hloc32W
= hloc32W_new
;
3365 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
3367 EDIT_LockBuffer(wnd
, es
);
3369 es
->x_offset
= es
->y_offset
= 0;
3370 es
->selection_start
= es
->selection_end
= 0;
3371 EDIT_EM_EmptyUndoBuffer(es
);
3372 es
->flags
&= ~EF_MODIFIED
;
3373 es
->flags
&= ~EF_UPDATE
;
3374 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3375 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3376 EDIT_EM_ScrollCaret(wnd
, es
);
3377 /* force scroll info update */
3378 EDIT_UpdateScrollInfo(wnd
, es
);
3382 /*********************************************************************
3386 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3387 * However, the windows version is not complied to yet in all of edit.c
3390 static void EDIT_EM_SetLimitText(EDITSTATE
*es
, INT limit
)
3392 if (es
->style
& ES_MULTILINE
) {
3394 es
->buffer_limit
= min(limit
, BUFLIMIT_MULTI
);
3396 es
->buffer_limit
= BUFLIMIT_MULTI
;
3399 es
->buffer_limit
= min(limit
, BUFLIMIT_SINGLE
);
3401 es
->buffer_limit
= BUFLIMIT_SINGLE
;
3406 /*********************************************************************
3410 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3411 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3412 * of the char's width, according to the new docs.
3415 static void EDIT_EM_SetMargins(EDITSTATE
*es
, INT action
,
3416 INT left
, INT right
)
3418 if (action
& EC_LEFTMARGIN
) {
3419 if (left
!= EC_USEFONTINFO
)
3420 es
->left_margin
= left
;
3422 es
->left_margin
= es
->char_width
/ 3;
3425 if (action
& EC_RIGHTMARGIN
) {
3426 if (right
!= EC_USEFONTINFO
)
3427 es
->right_margin
= right
;
3429 es
->right_margin
= es
->char_width
/ 3;
3431 TRACE("left=%d, right=%d\n", es
->left_margin
, es
->right_margin
);
3435 /*********************************************************************
3437 * EM_SETPASSWORDCHAR
3440 static void EDIT_EM_SetPasswordChar(WND
*wnd
, EDITSTATE
*es
, WCHAR c
)
3442 if (es
->style
& ES_MULTILINE
)
3445 if (es
->password_char
== c
)
3448 es
->password_char
= c
;
3450 wnd
->dwStyle
|= ES_PASSWORD
;
3451 es
->style
|= ES_PASSWORD
;
3453 wnd
->dwStyle
&= ~ES_PASSWORD
;
3454 es
->style
&= ~ES_PASSWORD
;
3456 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3460 /*********************************************************************
3464 * note: unlike the specs say: the order of start and end
3465 * _is_ preserved in Windows. (i.e. start can be > end)
3466 * In other words: this handler is OK
3469 static void EDIT_EM_SetSel(WND
*wnd
, EDITSTATE
*es
, UINT start
, UINT end
, BOOL after_wrap
)
3471 UINT old_start
= es
->selection_start
;
3472 UINT old_end
= es
->selection_end
;
3473 UINT len
= strlenW(es
->text
);
3475 if (start
== (UINT
)-1) {
3476 start
= es
->selection_end
;
3477 end
= es
->selection_end
;
3479 start
= min(start
, len
);
3480 end
= min(end
, len
);
3482 es
->selection_start
= start
;
3483 es
->selection_end
= end
;
3485 es
->flags
|= EF_AFTER_WRAP
;
3487 es
->flags
&= ~EF_AFTER_WRAP
;
3488 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3489 ORDER_UINT(start
, end
);
3490 ORDER_UINT(end
, old_end
);
3491 ORDER_UINT(start
, old_start
);
3492 ORDER_UINT(old_start
, old_end
);
3493 if (end
!= old_start
)
3497 * ORDER_UINT32(end, old_start);
3498 * EDIT_InvalidateText(wnd, es, start, end);
3499 * EDIT_InvalidateText(wnd, es, old_start, old_end);
3500 * in place of the following if statement.
3502 if (old_start
> end
)
3504 EDIT_InvalidateText(wnd
, es
, start
, end
);
3505 EDIT_InvalidateText(wnd
, es
, old_start
, old_end
);
3509 EDIT_InvalidateText(wnd
, es
, start
, old_start
);
3510 EDIT_InvalidateText(wnd
, es
, end
, old_end
);
3513 else EDIT_InvalidateText(wnd
, es
, start
, old_end
);
3517 /*********************************************************************
3522 static BOOL
EDIT_EM_SetTabStops(EDITSTATE
*es
, INT count
, LPINT tabs
)
3524 if (!(es
->style
& ES_MULTILINE
))
3527 HeapFree(GetProcessHeap(), 0, es
->tabs
);
3528 es
->tabs_count
= count
;
3532 es
->tabs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(INT
));
3533 memcpy(es
->tabs
, tabs
, count
* sizeof(INT
));
3539 /*********************************************************************
3544 static BOOL
EDIT_EM_SetTabStops16(EDITSTATE
*es
, INT count
, LPINT16 tabs
)
3546 if (!(es
->style
& ES_MULTILINE
))
3549 HeapFree(GetProcessHeap(), 0, es
->tabs
);
3550 es
->tabs_count
= count
;
3555 es
->tabs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(INT
));
3556 for (i
= 0 ; i
< count
; i
++)
3557 es
->tabs
[i
] = *tabs
++;
3563 /*********************************************************************
3565 * EM_SETWORDBREAKPROC
3568 static void EDIT_EM_SetWordBreakProc(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
)
3570 if (es
->word_break_proc
== (void *)lParam
)
3573 es
->word_break_proc
= (void *)lParam
;
3574 es
->word_break_proc16
= NULL
;
3576 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
)) {
3577 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3578 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3583 /*********************************************************************
3585 * EM_SETWORDBREAKPROC16
3588 static void EDIT_EM_SetWordBreakProc16(WND
*wnd
, EDITSTATE
*es
, EDITWORDBREAKPROC16 wbp
)
3590 if (es
->word_break_proc16
== wbp
)
3593 es
->word_break_proc
= NULL
;
3594 es
->word_break_proc16
= wbp
;
3595 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
)) {
3596 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3597 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3602 /*********************************************************************
3607 static BOOL
EDIT_EM_Undo(WND
*wnd
, EDITSTATE
*es
)
3612 /* Protect read-only edit control from modification */
3613 if(es
->style
& ES_READONLY
)
3616 ulength
= strlenW(es
->undo_text
);
3617 utext
= HeapAlloc(GetProcessHeap(), 0, (ulength
+ 1) * sizeof(WCHAR
));
3619 strcpyW(utext
, es
->undo_text
);
3621 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3622 es
->undo_insert_count
, debugstr_w(utext
));
3624 EDIT_EM_SetSel(wnd
, es
, es
->undo_position
, es
->undo_position
+ es
->undo_insert_count
, FALSE
);
3625 EDIT_EM_EmptyUndoBuffer(es
);
3626 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, utext
, FALSE
);
3627 EDIT_EM_SetSel(wnd
, es
, es
->undo_position
, es
->undo_position
+ es
->undo_insert_count
, FALSE
);
3628 /* send the notification after the selection start and end are set */
3629 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3630 EDIT_EM_ScrollCaret(wnd
, es
);
3631 HeapFree(GetProcessHeap(), 0, utext
);
3633 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3634 es
->undo_insert_count
, debugstr_w(es
->undo_text
));
3639 /*********************************************************************
3644 static void EDIT_WM_Char(WND
*wnd
, EDITSTATE
*es
, WCHAR c
)
3648 /* Protect read-only edit control from modification */
3649 if(es
->style
& ES_READONLY
)
3652 control
= GetKeyState(VK_CONTROL
) & 0x8000;
3656 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3657 if(!(es
->style
& ES_MULTILINE
) && !(es
->style
& ES_WANTRETURN
))
3660 if (es
->style
& ES_MULTILINE
) {
3661 if (es
->style
& ES_READONLY
) {
3662 EDIT_MoveHome(wnd
, es
, FALSE
);
3663 EDIT_MoveDown_ML(wnd
, es
, FALSE
);
3665 static const WCHAR cr_lfW
[] = {'\r','\n',0};
3666 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, cr_lfW
, TRUE
);
3671 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_READONLY
))
3673 static const WCHAR tabW
[] = {'\t',0};
3674 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, tabW
, TRUE
);
3678 if (!(es
->style
& ES_READONLY
) && !control
) {
3679 if (es
->selection_start
!= es
->selection_end
)
3680 EDIT_WM_Clear(wnd
, es
);
3682 /* delete character left of caret */
3683 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
3684 EDIT_MoveBackward(wnd
, es
, TRUE
);
3685 EDIT_WM_Clear(wnd
, es
);
3690 SendMessageW(wnd
->hwndSelf
, WM_COPY
, 0, 0);
3693 SendMessageW(wnd
->hwndSelf
, WM_PASTE
, 0, 0);
3696 SendMessageW(wnd
->hwndSelf
, WM_CUT
, 0, 0);
3700 if (!(es
->style
& ES_READONLY
) && (c
>= ' ') && (c
!= 127)) {
3704 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, str
, TRUE
);
3711 /*********************************************************************
3716 static void EDIT_WM_Command(WND
*wnd
, EDITSTATE
*es
, INT code
, INT id
, HWND control
)
3718 if (code
|| control
)
3723 EDIT_EM_Undo(wnd
, es
);
3726 EDIT_WM_Cut(wnd
, es
);
3729 EDIT_WM_Copy(wnd
, es
);
3732 EDIT_WM_Paste(wnd
, es
);
3735 EDIT_WM_Clear(wnd
, es
);
3738 EDIT_EM_SetSel(wnd
, es
, 0, (UINT
)-1, FALSE
);
3739 EDIT_EM_ScrollCaret(wnd
, es
);
3742 ERR("unknown menu item, please report\n");
3748 /*********************************************************************
3752 * Note: the resource files resource/sysres_??.rc cannot define a
3753 * single popup menu. Hence we use a (dummy) menubar
3754 * containing the single popup menu as its first item.
3756 * FIXME: the message identifiers have been chosen arbitrarily,
3757 * hence we use MF_BYPOSITION.
3758 * We might as well use the "real" values (anybody knows ?)
3759 * The menu definition is in resources/sysres_??.rc.
3760 * Once these are OK, we better use MF_BYCOMMAND here
3761 * (as we do in EDIT_WM_Command()).
3764 static void EDIT_WM_ContextMenu(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
3766 HMENU menu
= LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3767 HMENU popup
= GetSubMenu(menu
, 0);
3768 UINT start
= es
->selection_start
;
3769 UINT end
= es
->selection_end
;
3771 ORDER_UINT(start
, end
);
3774 EnableMenuItem(popup
, 0, MF_BYPOSITION
| (EDIT_EM_CanUndo(es
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3776 EnableMenuItem(popup
, 2, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_PASSWORD
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3778 EnableMenuItem(popup
, 3, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_PASSWORD
) ? MF_ENABLED
: MF_GRAYED
));
3780 EnableMenuItem(popup
, 4, MF_BYPOSITION
| (IsClipboardFormatAvailable(CF_UNICODETEXT
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3782 EnableMenuItem(popup
, 5, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3784 EnableMenuItem(popup
, 7, MF_BYPOSITION
| (start
|| (end
!= strlenW(es
->text
)) ? MF_ENABLED
: MF_GRAYED
));
3786 TrackPopupMenu(popup
, TPM_LEFTALIGN
| TPM_RIGHTBUTTON
, x
, y
, 0, wnd
->hwndSelf
, NULL
);
3791 /*********************************************************************
3796 static void EDIT_WM_Copy(WND
*wnd
, EDITSTATE
*es
)
3798 INT s
= es
->selection_start
;
3799 INT e
= es
->selection_end
;
3806 hdst
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_DDESHARE
, (DWORD
)(e
- s
+ 1) * sizeof(WCHAR
));
3807 dst
= GlobalLock(hdst
);
3808 strncpyW(dst
, es
->text
+ s
, e
- s
);
3809 dst
[e
- s
] = 0; /* ensure 0 termination */
3810 TRACE("%s\n", debugstr_w(dst
));
3812 OpenClipboard(wnd
->hwndSelf
);
3814 SetClipboardData(CF_UNICODETEXT
, hdst
);
3819 /*********************************************************************
3824 static LRESULT
EDIT_WM_Create(WND
*wnd
, EDITSTATE
*es
, LPCWSTR name
)
3826 TRACE("%s\n", debugstr_w(name
));
3828 * To initialize some final structure members, we call some helper
3829 * functions. However, since the EDITSTATE is not consistent (i.e.
3830 * not fully initialized), we should be very careful which
3831 * functions can be called, and in what order.
3833 EDIT_WM_SetFont(wnd
, es
, 0, FALSE
);
3834 EDIT_EM_EmptyUndoBuffer(es
);
3836 if (name
&& *name
) {
3837 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, name
, FALSE
);
3838 /* if we insert text to the editline, the text scrolls out
3839 * of the window, as the caret is placed after the insert
3840 * pos normally; thus we reset es->selection... to 0 and
3843 es
->selection_start
= es
->selection_end
= 0;
3844 /* send the notification after the selection start and end are set */
3845 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3846 EDIT_EM_ScrollCaret(wnd
, es
);
3848 /* force scroll info update */
3849 EDIT_UpdateScrollInfo(wnd
, es
);
3854 /*********************************************************************
3859 static void EDIT_WM_Destroy(WND
*wnd
, EDITSTATE
*es
)
3864 while (LocalUnlock(es
->hloc32W
)) ;
3865 LocalFree(es
->hloc32W
);
3868 while (LocalUnlock(es
->hloc32A
)) ;
3869 LocalFree(es
->hloc32A
);
3872 while (LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
)) ;
3873 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
3876 pc
= es
->first_line_def
;
3880 HeapFree(GetProcessHeap(), 0, pc
);
3884 HeapFree(GetProcessHeap(), 0, es
);
3885 *(EDITSTATE
**)wnd
->wExtra
= NULL
;
3889 /*********************************************************************
3894 static LRESULT
EDIT_WM_EraseBkGnd(WND
*wnd
, EDITSTATE
*es
, HDC dc
)
3899 if ( get_app_version() >= 0x40000 &&(
3900 !es
->bEnableState
|| (es
->style
& ES_READONLY
)))
3901 brush
= (HBRUSH
)EDIT_SEND_CTLCOLORSTATIC(wnd
, dc
);
3903 brush
= (HBRUSH
)EDIT_SEND_CTLCOLOR(wnd
, dc
);
3906 brush
= (HBRUSH
)GetStockObject(WHITE_BRUSH
);
3908 GetClientRect(wnd
->hwndSelf
, &rc
);
3909 IntersectClipRect(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
3910 GetClipBox(dc
, &rc
);
3912 * FIXME: specs say that we should UnrealizeObject() the brush,
3913 * but the specs of UnrealizeObject() say that we shouldn't
3914 * unrealize a stock object. The default brush that
3915 * DefWndProc() returns is ... a stock object.
3917 FillRect(dc
, &rc
, brush
);
3922 /*********************************************************************
3927 static INT
EDIT_WM_GetText(EDITSTATE
*es
, INT count
, LPARAM lParam
, BOOL unicode
)
3929 if(!count
) return 0;
3933 LPWSTR textW
= (LPWSTR
)lParam
;
3934 strncpyW(textW
, es
->text
, count
);
3935 textW
[count
- 1] = 0; /* ensure 0 termination */
3936 return strlenW(textW
);
3940 LPSTR textA
= (LPSTR
)lParam
;
3941 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, count
, NULL
, NULL
);
3942 textA
[count
- 1] = 0; /* ensure 0 termination */
3943 return strlen(textA
);
3947 /*********************************************************************
3952 static LRESULT
EDIT_WM_HScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
)
3957 if (!(es
->style
& ES_MULTILINE
))
3960 if (!(es
->style
& ES_AUTOHSCROLL
))
3964 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
3967 TRACE("SB_LINELEFT\n");
3969 dx
= -es
->char_width
;
3972 TRACE("SB_LINERIGHT\n");
3973 if (es
->x_offset
< es
->text_width
)
3974 dx
= es
->char_width
;
3977 TRACE("SB_PAGELEFT\n");
3979 dx
= -fw
/ HSCROLL_FRACTION
/ es
->char_width
* es
->char_width
;
3982 TRACE("SB_PAGERIGHT\n");
3983 if (es
->x_offset
< es
->text_width
)
3984 dx
= fw
/ HSCROLL_FRACTION
/ es
->char_width
* es
->char_width
;
3992 TRACE("SB_RIGHT\n");
3993 if (es
->x_offset
< es
->text_width
)
3994 dx
= es
->text_width
- es
->x_offset
;
3997 TRACE("SB_THUMBTRACK %d\n", pos
);
3998 es
->flags
|= EF_HSCROLL_TRACK
;
3999 if(es
->style
& WS_HSCROLL
)
4000 dx
= pos
- es
->x_offset
;
4005 if(pos
< 0 || pos
> 100) return 0;
4006 /* Assume default scroll range 0-100 */
4007 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4008 new_x
= pos
* (es
->text_width
- fw
) / 100;
4009 dx
= es
->text_width
? (new_x
- es
->x_offset
) : 0;
4012 case SB_THUMBPOSITION
:
4013 TRACE("SB_THUMBPOSITION %d\n", pos
);
4014 es
->flags
&= ~EF_HSCROLL_TRACK
;
4015 if(wnd
->dwStyle
& WS_HSCROLL
)
4016 dx
= pos
- es
->x_offset
;
4021 if(pos
< 0 || pos
> 100) return 0;
4022 /* Assume default scroll range 0-100 */
4023 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4024 new_x
= pos
* (es
->text_width
- fw
) / 100;
4025 dx
= es
->text_width
? (new_x
- es
->x_offset
) : 0;
4028 /* force scroll info update */
4029 EDIT_UpdateScrollInfo(wnd
, es
);
4030 EDIT_NOTIFY_PARENT(es
, EN_HSCROLL
, "EN_HSCROLL");
4034 TRACE("SB_ENDSCROLL\n");
4037 * FIXME : the next two are undocumented !
4038 * Are we doing the right thing ?
4039 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4040 * although it's also a regular control message.
4042 case EM_GETTHUMB
: /* this one is used by NT notepad */
4046 if(wnd
->dwStyle
& WS_HSCROLL
)
4047 ret
= GetScrollPos(wnd
->hwndSelf
, SB_HORZ
);
4050 /* Assume default scroll range 0-100 */
4051 INT fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4052 ret
= es
->text_width
? es
->x_offset
* 100 / (es
->text_width
- fw
) : 0;
4054 TRACE("EM_GETTHUMB: returning %ld\n", ret
);
4057 case EM_LINESCROLL16
:
4058 TRACE("EM_LINESCROLL16\n");
4063 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4069 INT fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4070 /* check if we are going to move too far */
4071 if(es
->x_offset
+ dx
+ fw
> es
->text_width
)
4072 dx
= es
->text_width
- fw
- es
->x_offset
;
4074 EDIT_EM_LineScroll_internal(wnd
, es
, dx
, 0);
4080 /*********************************************************************
4085 static BOOL
EDIT_CheckCombo(WND
*wnd
, EDITSTATE
*es
, UINT msg
, INT key
)
4087 HWND hLBox
= es
->hwndListBox
;
4095 hCombo
= wnd
->parent
->hwndSelf
;
4099 TRACE_(combo
)("[%04x]: handling msg %04x (%04x)\n",
4100 wnd
->hwndSelf
, (UINT16
)msg
, (UINT16
)key
);
4102 if (key
== VK_UP
|| key
== VK_DOWN
)
4104 if (SendMessageW(hCombo
, CB_GETEXTENDEDUI
, 0, 0))
4107 if (msg
== WM_KEYDOWN
|| nEUI
)
4108 bDropped
= (BOOL
)SendMessageW(hCombo
, CB_GETDROPPEDSTATE
, 0, 0);
4114 if (!bDropped
&& nEUI
&& (key
== VK_UP
|| key
== VK_DOWN
))
4116 /* make sure ComboLBox pops up */
4117 SendMessageW(hCombo
, CB_SETEXTENDEDUI
, FALSE
, 0);
4122 SendMessageW(hLBox
, WM_KEYDOWN
, (WPARAM
)key
, 0);
4125 case WM_SYSKEYDOWN
: /* Handle Alt+up/down arrows */
4127 SendMessageW(hCombo
, CB_SHOWDROPDOWN
, bDropped
? FALSE
: TRUE
, 0);
4129 SendMessageW(hLBox
, WM_KEYDOWN
, (WPARAM
)VK_F4
, 0);
4134 SendMessageW(hCombo
, CB_SETEXTENDEDUI
, TRUE
, 0);
4140 /*********************************************************************
4144 * Handling of special keys that don't produce a WM_CHAR
4145 * (i.e. non-printable keys) & Backspace & Delete
4148 static LRESULT
EDIT_WM_KeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
)
4153 if (GetKeyState(VK_MENU
) & 0x8000)
4156 shift
= GetKeyState(VK_SHIFT
) & 0x8000;
4157 control
= GetKeyState(VK_CONTROL
) & 0x8000;
4162 if (EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
) || key
== VK_F4
)
4167 if ((es
->style
& ES_MULTILINE
) && (key
== VK_UP
))
4168 EDIT_MoveUp_ML(wnd
, es
, shift
);
4171 EDIT_MoveWordBackward(wnd
, es
, shift
);
4173 EDIT_MoveBackward(wnd
, es
, shift
);
4176 if (EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
))
4180 if ((es
->style
& ES_MULTILINE
) && (key
== VK_DOWN
))
4181 EDIT_MoveDown_ML(wnd
, es
, shift
);
4183 EDIT_MoveWordForward(wnd
, es
, shift
);
4185 EDIT_MoveForward(wnd
, es
, shift
);
4188 EDIT_MoveHome(wnd
, es
, shift
);
4191 EDIT_MoveEnd(wnd
, es
, shift
);
4194 if (es
->style
& ES_MULTILINE
)
4195 EDIT_MovePageUp_ML(wnd
, es
, shift
);
4197 EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
);
4200 if (es
->style
& ES_MULTILINE
)
4201 EDIT_MovePageDown_ML(wnd
, es
, shift
);
4203 EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
);
4206 if (!(es
->style
& ES_READONLY
) && !(shift
&& control
)) {
4207 if (es
->selection_start
!= es
->selection_end
) {
4209 EDIT_WM_Cut(wnd
, es
);
4211 EDIT_WM_Clear(wnd
, es
);
4214 /* delete character left of caret */
4215 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4216 EDIT_MoveBackward(wnd
, es
, TRUE
);
4217 EDIT_WM_Clear(wnd
, es
);
4218 } else if (control
) {
4219 /* delete to end of line */
4220 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4221 EDIT_MoveEnd(wnd
, es
, TRUE
);
4222 EDIT_WM_Clear(wnd
, es
);
4224 /* delete character right of caret */
4225 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4226 EDIT_MoveForward(wnd
, es
, TRUE
);
4227 EDIT_WM_Clear(wnd
, es
);
4234 if (!(es
->style
& ES_READONLY
))
4235 EDIT_WM_Paste(wnd
, es
);
4237 EDIT_WM_Copy(wnd
, es
);
4240 /* If the edit doesn't want the return send a message to the default object */
4241 if(!(es
->style
& ES_WANTRETURN
))
4243 HWND hwndParent
= GetParent(wnd
->hwndSelf
);
4244 DWORD dw
= SendMessageW( hwndParent
, DM_GETDEFID
, 0, 0 );
4245 if (HIWORD(dw
) == DC_HASDEFID
)
4247 SendMessageW( hwndParent
, WM_COMMAND
,
4248 MAKEWPARAM( LOWORD(dw
), BN_CLICKED
),
4249 (LPARAM
)GetDlgItem( hwndParent
, LOWORD(dw
) ) );
4258 /*********************************************************************
4263 static LRESULT
EDIT_WM_KillFocus(WND
*wnd
, EDITSTATE
*es
)
4265 es
->flags
&= ~EF_FOCUSED
;
4267 if(!(es
->style
& ES_NOHIDESEL
))
4268 EDIT_InvalidateText(wnd
, es
, es
->selection_start
, es
->selection_end
);
4269 EDIT_NOTIFY_PARENT(es
, EN_KILLFOCUS
, "EN_KILLFOCUS");
4274 /*********************************************************************
4278 * The caret position has been set on the WM_LBUTTONDOWN message
4281 static LRESULT
EDIT_WM_LButtonDblClk(WND
*wnd
, EDITSTATE
*es
)
4284 INT e
= es
->selection_end
;
4289 if (!(es
->flags
& EF_FOCUSED
))
4292 l
= EDIT_EM_LineFromChar(es
, e
);
4293 li
= EDIT_EM_LineIndex(es
, l
);
4294 ll
= EDIT_EM_LineLength(es
, e
);
4295 s
= li
+ EDIT_CallWordBreakProc(es
, li
, e
- li
, ll
, WB_LEFT
);
4296 e
= li
+ EDIT_CallWordBreakProc(es
, li
, e
- li
, ll
, WB_RIGHT
);
4297 EDIT_EM_SetSel(wnd
, es
, s
, e
, FALSE
);
4298 EDIT_EM_ScrollCaret(wnd
, es
);
4303 /*********************************************************************
4308 static LRESULT
EDIT_WM_LButtonDown(WND
*wnd
, EDITSTATE
*es
, DWORD keys
, INT x
, INT y
)
4313 if (!(es
->flags
& EF_FOCUSED
))
4316 es
->bCaptureState
= TRUE
;
4317 SetCapture(wnd
->hwndSelf
);
4318 EDIT_ConfinePoint(es
, &x
, &y
);
4319 e
= EDIT_CharFromPos(wnd
, es
, x
, y
, &after_wrap
);
4320 EDIT_EM_SetSel(wnd
, es
, (keys
& MK_SHIFT
) ? es
->selection_start
: e
, e
, after_wrap
);
4321 EDIT_EM_ScrollCaret(wnd
, es
);
4322 es
->region_posx
= es
->region_posy
= 0;
4323 SetTimer(wnd
->hwndSelf
, 0, 100, NULL
);
4328 /*********************************************************************
4333 static LRESULT
EDIT_WM_LButtonUp(HWND hwndSelf
, EDITSTATE
*es
)
4335 if (es
->bCaptureState
&& GetCapture() == hwndSelf
) {
4336 KillTimer(hwndSelf
, 0);
4339 es
->bCaptureState
= FALSE
;
4344 /*********************************************************************
4349 static LRESULT
EDIT_WM_MButtonDown(WND
*wnd
)
4351 SendMessageW(wnd
->hwndSelf
,WM_PASTE
,0,0);
4356 /*********************************************************************
4361 static LRESULT
EDIT_WM_MouseMove(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
4367 if (GetCapture() != wnd
->hwndSelf
)
4371 * FIXME: gotta do some scrolling if outside client
4372 * area. Maybe reset the timer ?
4375 EDIT_ConfinePoint(es
, &x
, &y
);
4376 es
->region_posx
= (prex
< x
) ? -1 : ((prex
> x
) ? 1 : 0);
4377 es
->region_posy
= (prey
< y
) ? -1 : ((prey
> y
) ? 1 : 0);
4378 e
= EDIT_CharFromPos(wnd
, es
, x
, y
, &after_wrap
);
4379 EDIT_EM_SetSel(wnd
, es
, es
->selection_start
, e
, after_wrap
);
4384 /*********************************************************************
4389 static LRESULT
EDIT_WM_NCCreate(WND
*wnd
, DWORD style
, HWND hwndParent
, BOOL unicode
)
4394 TRACE("Creating %s edit control, style = %08lx\n",
4395 unicode
? "Unicode" : "ANSI", style
);
4397 if (!(es
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*es
))))
4399 *(EDITSTATE
**)wnd
->wExtra
= es
;
4402 * Note: since the EDITSTATE has not been fully initialized yet,
4403 * we can't use any API calls that may send
4404 * WM_XXX messages before WM_NCCREATE is completed.
4407 es
->is_unicode
= unicode
;
4410 es
->bEnableState
= !(style
& WS_DISABLED
);
4413 * In Win95 look and feel, the WS_BORDER style is replaced by the
4414 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4415 * control a non client area.
4417 if (TWEAK_WineLook
!= WIN31_LOOK
)
4419 if (es
->style
& WS_BORDER
)
4421 es
->style
&= ~WS_BORDER
;
4422 wnd
->dwStyle
&= ~WS_BORDER
;
4423 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
4428 if ((es
->style
& WS_BORDER
) && !(es
->style
& WS_DLGFRAME
))
4429 wnd
->dwStyle
&= ~WS_BORDER
;
4432 /* Save parent, which will be notified by EN_* messages */
4433 es
->hwndParent
= hwndParent
;
4435 if (es
->style
& ES_COMBO
)
4436 es
->hwndListBox
= GetDlgItem(hwndParent
, ID_CB_LISTBOX
);
4438 if (es
->style
& ES_MULTILINE
) {
4439 es
->buffer_limit
= BUFLIMIT_MULTI
;
4440 if (es
->style
& WS_VSCROLL
)
4441 es
->style
|= ES_AUTOVSCROLL
;
4442 if (es
->style
& WS_HSCROLL
)
4443 es
->style
|= ES_AUTOHSCROLL
;
4444 es
->style
&= ~ES_PASSWORD
;
4445 if ((es
->style
& ES_CENTER
) || (es
->style
& ES_RIGHT
)) {
4446 if (es
->style
& ES_RIGHT
)
4447 es
->style
&= ~ES_CENTER
;
4448 es
->style
&= ~WS_HSCROLL
;
4449 es
->style
&= ~ES_AUTOHSCROLL
;
4452 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4453 es
->style
|= ES_AUTOVSCROLL
;
4455 es
->buffer_limit
= BUFLIMIT_SINGLE
;
4456 es
->style
&= ~ES_CENTER
;
4457 es
->style
&= ~ES_RIGHT
;
4458 es
->style
&= ~WS_HSCROLL
;
4459 es
->style
&= ~WS_VSCROLL
;
4460 es
->style
&= ~ES_AUTOVSCROLL
;
4461 es
->style
&= ~ES_WANTRETURN
;
4462 if (es
->style
& ES_UPPERCASE
) {
4463 es
->style
&= ~ES_LOWERCASE
;
4464 es
->style
&= ~ES_NUMBER
;
4465 } else if (es
->style
& ES_LOWERCASE
)
4466 es
->style
&= ~ES_NUMBER
;
4467 if (es
->style
& ES_PASSWORD
)
4468 es
->password_char
= '*';
4470 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4471 es
->style
|= ES_AUTOHSCROLL
;
4474 alloc_size
= ROUND_TO_GROW((es
->buffer_size
+ 1) * sizeof(WCHAR
));
4475 if(!(es
->hloc32W
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
)))
4477 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
4479 if (!(es
->undo_text
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (es
->buffer_size
+ 1) * sizeof(WCHAR
))))
4481 es
->undo_buffer_size
= es
->buffer_size
;
4483 if (es
->style
& ES_MULTILINE
)
4484 if (!(es
->first_line_def
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LINEDEF
))))
4491 /*********************************************************************
4496 static void EDIT_WM_Paint(WND
*wnd
, EDITSTATE
*es
, WPARAM wParam
)
4505 BOOL rev
= es
->bEnableState
&&
4506 ((es
->flags
& EF_FOCUSED
) ||
4507 (es
->style
& ES_NOHIDESEL
));
4509 dc
= BeginPaint(wnd
->hwndSelf
, &ps
);
4512 if(es
->style
& WS_BORDER
) {
4513 GetClientRect(wnd
->hwndSelf
, &rc
);
4514 if(es
->style
& ES_MULTILINE
) {
4515 if(es
->style
& WS_HSCROLL
) rc
.bottom
++;
4516 if(es
->style
& WS_VSCROLL
) rc
.right
++;
4518 Rectangle(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
4520 IntersectClipRect(dc
, es
->format_rect
.left
,
4521 es
->format_rect
.top
,
4522 es
->format_rect
.right
,
4523 es
->format_rect
.bottom
);
4524 if (es
->style
& ES_MULTILINE
) {
4525 GetClientRect(wnd
->hwndSelf
, &rc
);
4526 IntersectClipRect(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
4529 old_font
= SelectObject(dc
, es
->font
);
4530 if ( get_app_version() >= 0x40000 &&(
4531 !es
->bEnableState
|| (es
->style
& ES_READONLY
)))
4532 EDIT_SEND_CTLCOLORSTATIC(wnd
, dc
);
4534 EDIT_SEND_CTLCOLOR(wnd
, dc
);
4536 if (!es
->bEnableState
)
4537 SetTextColor(dc
, GetSysColor(COLOR_GRAYTEXT
));
4538 GetClipBox(dc
, &rcRgn
);
4539 if (es
->style
& ES_MULTILINE
) {
4540 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4541 for (i
= es
->y_offset
; i
<= min(es
->y_offset
+ vlc
, es
->y_offset
+ es
->line_count
- 1) ; i
++) {
4542 EDIT_GetLineRect(wnd
, es
, i
, 0, -1, &rcLine
);
4543 if (IntersectRect(&rc
, &rcRgn
, &rcLine
))
4544 EDIT_PaintLine(wnd
, es
, dc
, i
, rev
);
4547 EDIT_GetLineRect(wnd
, es
, 0, 0, -1, &rcLine
);
4548 if (IntersectRect(&rc
, &rcRgn
, &rcLine
))
4549 EDIT_PaintLine(wnd
, es
, dc
, 0, rev
);
4552 SelectObject(dc
, old_font
);
4555 EndPaint(wnd
->hwndSelf
, &ps
);
4559 /*********************************************************************
4564 static void EDIT_WM_Paste(WND
*wnd
, EDITSTATE
*es
)
4569 /* Protect read-only edit control from modification */
4570 if(es
->style
& ES_READONLY
)
4573 OpenClipboard(wnd
->hwndSelf
);
4574 if ((hsrc
= GetClipboardData(CF_UNICODETEXT
))) {
4575 src
= (LPWSTR
)GlobalLock(hsrc
);
4576 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, src
, TRUE
);
4583 /*********************************************************************
4588 static void EDIT_WM_SetFocus(WND
*wnd
, EDITSTATE
*es
)
4590 es
->flags
|= EF_FOCUSED
;
4591 CreateCaret(wnd
->hwndSelf
, 0, 2, es
->line_height
);
4592 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
,
4593 es
->flags
& EF_AFTER_WRAP
);
4594 if(!(es
->style
& ES_NOHIDESEL
))
4595 EDIT_InvalidateText(wnd
, es
, es
->selection_start
, es
->selection_end
);
4596 ShowCaret(wnd
->hwndSelf
);
4597 EDIT_NOTIFY_PARENT(es
, EN_SETFOCUS
, "EN_SETFOCUS");
4601 /*********************************************************************
4605 * With Win95 look the margins are set to default font value unless
4606 * the system font (font == 0) is being set, in which case they are left
4610 static void EDIT_WM_SetFont(WND
*wnd
, EDITSTATE
*es
, HFONT font
, BOOL redraw
)
4618 dc
= GetDC(wnd
->hwndSelf
);
4620 old_font
= SelectObject(dc
, font
);
4621 GetTextMetricsW(dc
, &tm
);
4622 es
->line_height
= tm
.tmHeight
;
4623 es
->char_width
= tm
.tmAveCharWidth
;
4625 SelectObject(dc
, old_font
);
4626 ReleaseDC(wnd
->hwndSelf
, dc
);
4627 if (font
&& (TWEAK_WineLook
> WIN31_LOOK
))
4628 EDIT_EM_SetMargins(es
, EC_LEFTMARGIN
| EC_RIGHTMARGIN
,
4629 EC_USEFONTINFO
, EC_USEFONTINFO
);
4631 /* Force the recalculation of the format rect for each font change */
4632 GetClientRect(wnd
->hwndSelf
, &r
);
4633 EDIT_SetRectNP(wnd
, es
, &r
);
4635 if (es
->style
& ES_MULTILINE
)
4636 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
4638 EDIT_CalcLineWidth_SL(wnd
, es
);
4641 EDIT_UpdateText(wnd
, NULL
, TRUE
);
4642 if (es
->flags
& EF_FOCUSED
) {
4644 CreateCaret(wnd
->hwndSelf
, 0, 2, es
->line_height
);
4645 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
,
4646 es
->flags
& EF_AFTER_WRAP
);
4647 ShowCaret(wnd
->hwndSelf
);
4652 /*********************************************************************
4657 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4658 * The modified flag is reset. No notifications are sent.
4660 * For single-line controls, reception of WM_SETTEXT triggers:
4661 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4664 static void EDIT_WM_SetText(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
, BOOL unicode
)
4669 text
= (LPWSTR
)lParam
;
4672 LPCSTR textA
= (LPCSTR
)lParam
;
4673 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
4674 if((text
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
4675 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, text
, countW
);
4678 EDIT_EM_SetSel(wnd
, es
, 0, (UINT
)-1, FALSE
);
4680 TRACE("%s\n", debugstr_w(text
));
4681 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, text
, FALSE
);
4683 HeapFree(GetProcessHeap(), 0, text
);
4685 static const WCHAR empty_stringW
[] = {0};
4687 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, empty_stringW
, FALSE
);
4690 es
->flags
&= ~EF_MODIFIED
;
4691 EDIT_EM_SetSel(wnd
, es
, 0, 0, FALSE
);
4692 /* Send the notification after the selection start and end have been set
4693 * edit control doesn't send notification on WM_SETTEXT
4694 * if it is multiline, or it is part of combobox
4696 if( !((es
->style
& ES_MULTILINE
) || es
->hwndListBox
))
4697 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
4698 EDIT_EM_ScrollCaret(wnd
, es
);
4702 /*********************************************************************
4707 static void EDIT_WM_Size(WND
*wnd
, EDITSTATE
*es
, UINT action
, INT width
, INT height
)
4709 if ((action
== SIZE_MAXIMIZED
) || (action
== SIZE_RESTORED
)) {
4711 TRACE("width = %d, height = %d\n", width
, height
);
4712 SetRect(&rc
, 0, 0, width
, height
);
4713 EDIT_SetRectNP(wnd
, es
, &rc
);
4714 EDIT_UpdateText(wnd
, NULL
, TRUE
);
4719 /*********************************************************************
4724 static LRESULT
EDIT_WM_SysKeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
, DWORD key_data
)
4726 if ((key
== VK_BACK
) && (key_data
& 0x2000)) {
4727 if (EDIT_EM_CanUndo(es
))
4728 EDIT_EM_Undo(wnd
, es
);
4730 } else if (key
== VK_UP
|| key
== VK_DOWN
) {
4731 if (EDIT_CheckCombo(wnd
, es
, WM_SYSKEYDOWN
, key
))
4734 return DefWindowProcW(wnd
->hwndSelf
, WM_SYSKEYDOWN
, (WPARAM
)key
, (LPARAM
)key_data
);
4738 /*********************************************************************
4743 static void EDIT_WM_Timer(WND
*wnd
, EDITSTATE
*es
)
4745 if (es
->region_posx
< 0) {
4746 EDIT_MoveBackward(wnd
, es
, TRUE
);
4747 } else if (es
->region_posx
> 0) {
4748 EDIT_MoveForward(wnd
, es
, TRUE
);
4751 * FIXME: gotta do some vertical scrolling here, like
4752 * EDIT_EM_LineScroll(wnd, 0, 1);
4756 /*********************************************************************
4761 static LRESULT
EDIT_WM_VScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
)
4765 if (!(es
->style
& ES_MULTILINE
))
4768 if (!(es
->style
& ES_AUTOVSCROLL
))
4777 TRACE("action %d\n", action
);
4778 EDIT_EM_Scroll(wnd
, es
, action
);
4785 TRACE("SB_BOTTOM\n");
4786 dy
= es
->line_count
- 1 - es
->y_offset
;
4789 TRACE("SB_THUMBTRACK %d\n", pos
);
4790 es
->flags
|= EF_VSCROLL_TRACK
;
4791 if(es
->style
& WS_VSCROLL
)
4792 dy
= pos
- es
->y_offset
;
4795 /* Assume default scroll range 0-100 */
4798 if(pos
< 0 || pos
> 100) return 0;
4799 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4800 new_y
= pos
* (es
->line_count
- vlc
) / 100;
4801 dy
= es
->line_count
? (new_y
- es
->y_offset
) : 0;
4802 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4803 es
->line_count
, es
->y_offset
, pos
, dy
);
4806 case SB_THUMBPOSITION
:
4807 TRACE("SB_THUMBPOSITION %d\n", pos
);
4808 es
->flags
&= ~EF_VSCROLL_TRACK
;
4809 if(es
->style
& WS_VSCROLL
)
4810 dy
= pos
- es
->y_offset
;
4813 /* Assume default scroll range 0-100 */
4816 if(pos
< 0 || pos
> 100) return 0;
4817 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4818 new_y
= pos
* (es
->line_count
- vlc
) / 100;
4819 dy
= es
->line_count
? (new_y
- es
->y_offset
) : 0;
4820 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4821 es
->line_count
, es
->y_offset
, pos
, dy
);
4825 /* force scroll info update */
4826 EDIT_UpdateScrollInfo(wnd
, es
);
4827 EDIT_NOTIFY_PARENT(es
, EN_VSCROLL
, "EN_VSCROLL");
4831 TRACE("SB_ENDSCROLL\n");
4834 * FIXME : the next two are undocumented !
4835 * Are we doing the right thing ?
4836 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4837 * although it's also a regular control message.
4839 case EM_GETTHUMB
: /* this one is used by NT notepad */
4843 if(wnd
->dwStyle
& WS_VSCROLL
)
4844 ret
= GetScrollPos(wnd
->hwndSelf
, SB_VERT
);
4847 /* Assume default scroll range 0-100 */
4848 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4849 ret
= es
->line_count
? es
->y_offset
* 100 / (es
->line_count
- vlc
) : 0;
4851 TRACE("EM_GETTHUMB: returning %ld\n", ret
);
4854 case EM_LINESCROLL16
:
4855 TRACE("EM_LINESCROLL16 %d\n", pos
);
4860 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4865 EDIT_EM_LineScroll(wnd
, es
, 0, dy
);
4869 /*********************************************************************
4874 static void EDIT_UpdateTextRegion(WND
*wnd
, HRGN hrgn
, BOOL bErase
)
4876 EDITSTATE
*es
= *(EDITSTATE
**)((wnd
)->wExtra
);
4878 if (es
->flags
& EF_UPDATE
)
4879 EDIT_NOTIFY_PARENT(es
, EN_UPDATE
, "EN_UPDATE");
4881 InvalidateRgn(wnd
->hwndSelf
, hrgn
, bErase
);
4885 /*********************************************************************
4890 static void EDIT_UpdateText(WND
*wnd
, LPRECT rc
, BOOL bErase
)
4892 EDITSTATE
*es
= *(EDITSTATE
**)((wnd
)->wExtra
);
4894 if (es
->flags
& EF_UPDATE
)
4895 EDIT_NOTIFY_PARENT(es
, EN_UPDATE
, "EN_UPDATE");
4897 InvalidateRect(wnd
->hwndSelf
, rc
, bErase
);