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
)
2209 BkColor
= GetBkColor(dc
);
2210 TextColor
= GetTextColor(dc
);
2212 SetBkColor(dc
, GetSysColor(COLOR_HIGHLIGHT
));
2213 SetTextColor(dc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
2215 li
= EDIT_EM_LineIndex(es
, line
);
2216 if (es
->style
& ES_MULTILINE
) {
2217 ret
= (INT
)LOWORD(TabbedTextOutW(dc
, x
, y
, es
->text
+ li
+ col
, count
,
2218 es
->tabs_count
, es
->tabs
, es
->format_rect
.left
- es
->x_offset
));
2220 LPWSTR text
= EDIT_GetPasswordPointer_SL(es
);
2221 TextOutW(dc
, x
, y
, text
+ li
+ col
, count
);
2222 GetTextExtentPoint32W(dc
, text
+ li
+ col
, count
, &size
);
2224 if (es
->style
& ES_PASSWORD
)
2225 HeapFree(GetProcessHeap(), 0, text
);
2228 SetBkColor(dc
, BkColor
);
2229 SetTextColor(dc
, TextColor
);
2235 /*********************************************************************
2240 static void EDIT_SetCaretPos(WND
*wnd
, EDITSTATE
*es
, INT pos
,
2243 LRESULT res
= EDIT_EM_PosFromChar(wnd
, es
, pos
, after_wrap
);
2244 SetCaretPos(SLOWORD(res
), SHIWORD(res
));
2248 /*********************************************************************
2252 * note: this is not (exactly) the handler called on EM_SETRECTNP
2253 * it is also used to set the rect of a single line control
2256 static void EDIT_SetRectNP(WND
*wnd
, EDITSTATE
*es
, LPRECT rc
)
2258 CopyRect(&es
->format_rect
, rc
);
2259 if (es
->style
& WS_BORDER
) {
2260 INT bw
= GetSystemMetrics(SM_CXBORDER
) + 1;
2261 if(TWEAK_WineLook
== WIN31_LOOK
)
2263 es
->format_rect
.left
+= bw
;
2264 es
->format_rect
.top
+= bw
;
2265 es
->format_rect
.right
-= bw
;
2266 es
->format_rect
.bottom
-= bw
;
2268 es
->format_rect
.left
+= es
->left_margin
;
2269 es
->format_rect
.right
-= es
->right_margin
;
2270 es
->format_rect
.right
= max(es
->format_rect
.right
, es
->format_rect
.left
+ es
->char_width
);
2271 if (es
->style
& ES_MULTILINE
)
2273 INT fw
, vlc
, max_x_offset
, max_y_offset
;
2275 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
2276 es
->format_rect
.bottom
= es
->format_rect
.top
+ max(1, vlc
) * es
->line_height
;
2278 /* correct es->x_offset */
2279 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
2280 max_x_offset
= es
->text_width
- fw
;
2281 if(max_x_offset
< 0) max_x_offset
= 0;
2282 if(es
->x_offset
> max_x_offset
)
2283 es
->x_offset
= max_x_offset
;
2285 /* correct es->y_offset */
2286 max_y_offset
= es
->line_count
- vlc
;
2287 if(max_y_offset
< 0) max_y_offset
= 0;
2288 if(es
->y_offset
> max_y_offset
)
2289 es
->y_offset
= max_y_offset
;
2291 /* force scroll info update */
2292 EDIT_UpdateScrollInfo(wnd
, es
);
2295 /* Windows doesn't care to fix text placement for SL controls */
2296 es
->format_rect
.bottom
= es
->format_rect
.top
+ es
->line_height
;
2298 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
))
2299 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
2303 /*********************************************************************
2308 static void EDIT_UnlockBuffer(WND
*wnd
, EDITSTATE
*es
, BOOL force
)
2310 /* Edit window might be already destroyed */
2311 if(!IsWindow(wnd
->hwndSelf
))
2313 WARN("edit wnd %04x already destroyed\n", wnd
->hwndSelf
);
2318 ERR("no EDITSTATE ... please report\n");
2321 if (!es
->lock_count
) {
2322 ERR("lock_count == 0 ... please report\n");
2326 ERR("es->text == 0 ... please report\n");
2330 if (force
|| (es
->lock_count
== 1)) {
2333 BOOL _16bit
= FALSE
;
2335 UINT countW
= strlenW(es
->text
) + 1;
2339 UINT countA_new
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, NULL
, 0, NULL
, NULL
);
2340 TRACE("Synchronizing with 32-bit ANSI buffer\n");
2341 TRACE("%d WCHARs translated to %d bytes\n", countW
, countA_new
);
2342 countA
= LocalSize(es
->hloc32A
);
2343 if(countA_new
> countA
)
2346 UINT alloc_size
= ROUND_TO_GROW(countA_new
);
2347 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA
, alloc_size
);
2348 hloc32A_new
= LocalReAlloc(es
->hloc32A
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
);
2351 es
->hloc32A
= hloc32A_new
;
2352 countA
= LocalSize(hloc32A_new
);
2353 TRACE("Real new size %d bytes\n", countA
);
2356 WARN("FAILED! Will synchronize partially\n");
2358 textA
= LocalLock(es
->hloc32A
);
2362 UINT countA_new
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, NULL
, 0, NULL
, NULL
);
2363 TRACE("Synchronizing with 16-bit ANSI buffer\n");
2364 TRACE("%d WCHARs translated to %d bytes\n", countW
, countA_new
);
2365 countA
= LOCAL_Size(wnd
->hInstance
, es
->hloc16
);
2366 if(countA_new
> countA
)
2368 HLOCAL16 hloc16_new
;
2369 UINT alloc_size
= ROUND_TO_GROW(countA_new
);
2370 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA
, alloc_size
);
2371 hloc16_new
= LOCAL_ReAlloc(wnd
->hInstance
, es
->hloc16
, alloc_size
, LMEM_MOVEABLE
| LMEM_ZEROINIT
);
2374 es
->hloc16
= hloc16_new
;
2375 countA
= LOCAL_Size(wnd
->hInstance
, hloc16_new
);
2376 TRACE("Real new size %d bytes\n", countA
);
2379 WARN("FAILED! Will synchronize partially\n");
2381 textA
= LOCAL_Lock(wnd
->hInstance
, es
->hloc16
);
2387 WideCharToMultiByte(CP_ACP
, 0, es
->text
, countW
, textA
, countA
, NULL
, NULL
);
2389 LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
);
2391 LocalUnlock(es
->hloc32A
);
2394 LocalUnlock(es
->hloc32W
);
2398 ERR("no buffer ... please report\n");
2406 /*********************************************************************
2408 * EDIT_UpdateScrollInfo
2411 static void EDIT_UpdateScrollInfo(WND
*wnd
, EDITSTATE
*es
)
2413 if ((es
->style
& WS_VSCROLL
) && !(es
->flags
& EF_VSCROLL_TRACK
))
2416 si
.cbSize
= sizeof(SCROLLINFO
);
2417 si
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
2419 si
.nMax
= es
->line_count
- 1;
2420 si
.nPage
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
2421 si
.nPos
= es
->y_offset
;
2422 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2423 si
.nMin
, si
.nMax
, si
.nPage
, si
.nPos
);
2424 SetScrollInfo(wnd
->hwndSelf
, SB_VERT
, &si
, TRUE
);
2427 if ((es
->style
& WS_HSCROLL
) && !(es
->flags
& EF_HSCROLL_TRACK
))
2430 si
.cbSize
= sizeof(SCROLLINFO
);
2431 si
.fMask
= SIF_PAGE
| SIF_POS
| SIF_RANGE
| SIF_DISABLENOSCROLL
;
2433 si
.nMax
= es
->text_width
- 1;
2434 si
.nPage
= es
->format_rect
.right
- es
->format_rect
.left
;
2435 si
.nPos
= es
->x_offset
;
2436 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2437 si
.nMin
, si
.nMax
, si
.nPage
, si
.nPos
);
2438 SetScrollInfo(wnd
->hwndSelf
, SB_HORZ
, &si
, TRUE
);
2442 /*********************************************************************
2444 * EDIT_WordBreakProc
2446 * Find the beginning of words.
2447 * Note: unlike the specs for a WordBreakProc, this function only
2448 * allows to be called without linebreaks between s[0] upto
2449 * s[count - 1]. Remember it is only called
2450 * internally, so we can decide this for ourselves.
2453 static INT CALLBACK
EDIT_WordBreakProc(LPWSTR s
, INT index
, INT count
, INT action
)
2457 TRACE("s=%p, index=%d, count=%d, action=%d\n", s
, index
, count
, action
);
2467 if (s
[index
] == ' ') {
2468 while (index
&& (s
[index
] == ' '))
2471 while (index
&& (s
[index
] != ' '))
2473 if (s
[index
] == ' ')
2477 while (index
&& (s
[index
] != ' '))
2479 if (s
[index
] == ' ')
2489 if (s
[index
] == ' ')
2490 while ((index
< count
) && (s
[index
] == ' ')) index
++;
2492 while (s
[index
] && (s
[index
] != ' ') && (index
< count
))
2494 while ((s
[index
] == ' ') && (index
< count
)) index
++;
2498 case WB_ISDELIMITER
:
2499 ret
= (s
[index
] == ' ');
2502 ERR("unknown action code, please report !\n");
2509 /*********************************************************************
2513 * returns line number (not index) in high-order word of result.
2514 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2515 * to Richedit, not to the edit control. Original documentation is valid.
2516 * FIXME: do the specs mean to return -1 if outside client area or
2517 * if outside formatting rectangle ???
2520 static LRESULT
EDIT_EM_CharFromPos(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
2528 GetClientRect(wnd
->hwndSelf
, &rc
);
2529 if (!PtInRect(&rc
, pt
))
2532 index
= EDIT_CharFromPos(wnd
, es
, x
, y
, NULL
);
2533 return MAKELONG(index
, EDIT_EM_LineFromChar(es
, index
));
2537 /*********************************************************************
2541 * Enable or disable soft breaks.
2543 static BOOL
EDIT_EM_FmtLines(EDITSTATE
*es
, BOOL add_eol
)
2545 es
->flags
&= ~EF_USE_SOFTBRK
;
2547 es
->flags
|= EF_USE_SOFTBRK
;
2548 FIXME("soft break enabled, not implemented\n");
2554 /*********************************************************************
2558 * Hopefully this won't fire back at us.
2559 * We always start with a fixed buffer in the local heap.
2560 * Despite of the documentation says that the local heap is used
2561 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2562 * buffer on the local heap.
2565 static HLOCAL
EDIT_EM_GetHandle(EDITSTATE
*es
)
2569 if (!(es
->style
& ES_MULTILINE
))
2573 hLocal
= es
->hloc32W
;
2579 UINT countA
, alloc_size
;
2580 TRACE("Allocating 32-bit ANSI alias buffer\n");
2581 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, NULL
, 0, NULL
, NULL
);
2582 alloc_size
= ROUND_TO_GROW(countA
);
2583 if(!(es
->hloc32A
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
)))
2585 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size
);
2588 textA
= LocalLock(es
->hloc32A
);
2589 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, countA
, NULL
, NULL
);
2590 LocalUnlock(es
->hloc32A
);
2592 hLocal
= es
->hloc32A
;
2595 TRACE("Returning %04X, LocalSize() = %d\n", hLocal
, LocalSize(hLocal
));
2600 /*********************************************************************
2604 * Hopefully this won't fire back at us.
2605 * We always start with a buffer in 32 bit linear memory.
2606 * However, with this message a 16 bit application requests
2607 * a handle of 16 bit local heap memory, where it expects to find
2609 * It's a pitty that from this moment on we have to use this
2610 * local heap, because applications may rely on the handle
2613 * In this function we'll try to switch to local heap.
2615 static HLOCAL16
EDIT_EM_GetHandle16(WND
*wnd
, EDITSTATE
*es
)
2618 UINT countA
, alloc_size
;
2620 if (!(es
->style
& ES_MULTILINE
))
2626 if (!LOCAL_HeapSize(wnd
->hInstance
)) {
2627 if (!LocalInit16(wnd
->hInstance
, 0,
2628 GlobalSize16(wnd
->hInstance
))) {
2629 ERR("could not initialize local heap\n");
2632 TRACE("local heap initialized\n");
2635 countA
= WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, NULL
, 0, NULL
, NULL
);
2636 alloc_size
= ROUND_TO_GROW(countA
);
2638 TRACE("Allocating 16-bit ANSI alias buffer\n");
2639 if (!(es
->hloc16
= LOCAL_Alloc(wnd
->hInstance
, LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
))) {
2640 ERR("could not allocate new 16 bit buffer\n");
2644 if (!(textA
= (LPSTR
)LOCAL_Lock(wnd
->hInstance
, es
->hloc16
))) {
2645 ERR("could not lock new 16 bit buffer\n");
2646 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
2651 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, countA
, NULL
, NULL
);
2652 LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
);
2654 TRACE("Returning %04X, LocalSize() = %d\n", es
->hloc16
, LOCAL_Size(wnd
->hInstance
, es
->hloc16
));
2659 /*********************************************************************
2664 static INT
EDIT_EM_GetLine(EDITSTATE
*es
, INT line
, LPARAM lParam
, BOOL unicode
)
2667 INT line_len
, dst_len
;
2670 if (es
->style
& ES_MULTILINE
) {
2671 if (line
>= es
->line_count
)
2675 i
= EDIT_EM_LineIndex(es
, line
);
2677 line_len
= EDIT_EM_LineLength(es
, i
);
2678 dst_len
= *(WORD
*)lParam
;
2681 LPWSTR dst
= (LPWSTR
)lParam
;
2682 if(dst_len
<= line_len
)
2684 memcpy(dst
, src
, dst_len
* sizeof(WCHAR
));
2687 else /* Append 0 if enough space */
2689 memcpy(dst
, src
, line_len
* sizeof(WCHAR
));
2696 LPSTR dst
= (LPSTR
)lParam
;
2698 ret
= WideCharToMultiByte(CP_ACP
, 0, src
, line_len
, dst
, dst_len
, NULL
, NULL
);
2699 if(!ret
) /* Insufficient buffer size */
2701 if(ret
< dst_len
) /* Append 0 if enough space */
2708 /*********************************************************************
2713 static LRESULT
EDIT_EM_GetSel(EDITSTATE
*es
, LPUINT start
, LPUINT end
)
2715 UINT s
= es
->selection_start
;
2716 UINT e
= es
->selection_end
;
2723 return MAKELONG(s
, e
);
2727 /*********************************************************************
2731 * FIXME: is this right ? (or should it be only VSCROLL)
2732 * (and maybe only for edit controls that really have their
2733 * own scrollbars) (and maybe only for multiline controls ?)
2734 * All in all: very poorly documented
2737 static LRESULT
EDIT_EM_GetThumb(WND
*wnd
, EDITSTATE
*es
)
2739 return MAKELONG(EDIT_WM_VScroll(wnd
, es
, EM_GETTHUMB16
, 0),
2740 EDIT_WM_HScroll(wnd
, es
, EM_GETTHUMB16
, 0));
2744 /*********************************************************************
2749 static INT
EDIT_EM_LineFromChar(EDITSTATE
*es
, INT index
)
2754 if (!(es
->style
& ES_MULTILINE
))
2756 if (index
> (INT
)strlenW(es
->text
))
2757 return es
->line_count
- 1;
2759 index
= min(es
->selection_start
, es
->selection_end
);
2762 line_def
= es
->first_line_def
;
2763 index
-= line_def
->length
;
2764 while ((index
>= 0) && line_def
->next
) {
2766 line_def
= line_def
->next
;
2767 index
-= line_def
->length
;
2773 /*********************************************************************
2778 static INT
EDIT_EM_LineIndex(EDITSTATE
*es
, INT line
)
2783 if (!(es
->style
& ES_MULTILINE
))
2785 if (line
>= es
->line_count
)
2789 line_def
= es
->first_line_def
;
2791 INT index
= es
->selection_end
- line_def
->length
;
2792 while ((index
>= 0) && line_def
->next
) {
2793 line_index
+= line_def
->length
;
2794 line_def
= line_def
->next
;
2795 index
-= line_def
->length
;
2799 line_index
+= line_def
->length
;
2800 line_def
= line_def
->next
;
2808 /*********************************************************************
2813 static INT
EDIT_EM_LineLength(EDITSTATE
*es
, INT index
)
2817 if (!(es
->style
& ES_MULTILINE
))
2818 return strlenW(es
->text
);
2821 /* get the number of remaining non-selected chars of selected lines */
2824 li
= EDIT_EM_LineFromChar(es
, es
->selection_start
);
2825 /* # chars before start of selection area */
2826 count
= es
->selection_start
- EDIT_EM_LineIndex(es
, li
);
2827 li
= EDIT_EM_LineFromChar(es
, es
->selection_end
);
2828 /* # chars after end of selection */
2829 count
+= EDIT_EM_LineIndex(es
, li
) +
2830 EDIT_EM_LineLength(es
, li
) - es
->selection_end
;
2833 line_def
= es
->first_line_def
;
2834 index
-= line_def
->length
;
2835 while ((index
>= 0) && line_def
->next
) {
2836 line_def
= line_def
->next
;
2837 index
-= line_def
->length
;
2839 return line_def
->net_length
;
2843 /*********************************************************************
2847 * NOTE: dx is in average character widths, dy - in lines;
2850 static BOOL
EDIT_EM_LineScroll(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
)
2852 if (!(es
->style
& ES_MULTILINE
))
2855 dx
*= es
->char_width
;
2856 return EDIT_EM_LineScroll_internal(wnd
, es
, dx
, dy
);
2859 /*********************************************************************
2861 * EDIT_EM_LineScroll_internal
2863 * Version of EDIT_EM_LineScroll for internal use.
2864 * It doesn't refuse if ES_MULTILINE is set and assumes that
2865 * dx is in pixels, dy - in lines.
2868 static BOOL
EDIT_EM_LineScroll_internal(WND
*wnd
, EDITSTATE
*es
, INT dx
, INT dy
)
2871 INT x_offset_in_pixels
;
2873 if (es
->style
& ES_MULTILINE
)
2875 x_offset_in_pixels
= es
->x_offset
;
2880 x_offset_in_pixels
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->x_offset
, FALSE
));
2883 if (-dx
> x_offset_in_pixels
)
2884 dx
= -x_offset_in_pixels
;
2885 if (dx
> es
->text_width
- x_offset_in_pixels
)
2886 dx
= es
->text_width
- x_offset_in_pixels
;
2887 nyoff
= max(0, es
->y_offset
+ dy
);
2888 if (nyoff
>= es
->line_count
)
2889 nyoff
= es
->line_count
- 1;
2890 dy
= (es
->y_offset
- nyoff
) * es
->line_height
;
2895 es
->y_offset
= nyoff
;
2896 if(es
->style
& ES_MULTILINE
)
2899 es
->x_offset
+= dx
/ es
->char_width
;
2901 GetClientRect(wnd
->hwndSelf
, &rc1
);
2902 IntersectRect(&rc
, &rc1
, &es
->format_rect
);
2903 ScrollWindowEx(wnd
->hwndSelf
, -dx
, dy
,
2904 NULL
, &rc
, (HRGN
)NULL
, NULL
, SW_INVALIDATE
);
2905 /* force scroll info update */
2906 EDIT_UpdateScrollInfo(wnd
, es
);
2908 if (dx
&& !(es
->flags
& EF_HSCROLL_TRACK
))
2909 EDIT_NOTIFY_PARENT(es
, EN_HSCROLL
, "EN_HSCROLL");
2910 if (dy
&& !(es
->flags
& EF_VSCROLL_TRACK
))
2911 EDIT_NOTIFY_PARENT(es
, EN_VSCROLL
, "EN_VSCROLL");
2916 /*********************************************************************
2921 static LRESULT
EDIT_EM_PosFromChar(WND
*wnd
, EDITSTATE
*es
, INT index
, BOOL after_wrap
)
2923 INT len
= strlenW(es
->text
);
2932 index
= min(index
, len
);
2933 dc
= GetDC(wnd
->hwndSelf
);
2935 old_font
= SelectObject(dc
, es
->font
);
2936 if (es
->style
& ES_MULTILINE
) {
2937 l
= EDIT_EM_LineFromChar(es
, index
);
2938 y
= (l
- es
->y_offset
) * es
->line_height
;
2939 li
= EDIT_EM_LineIndex(es
, l
);
2940 if (after_wrap
&& (li
== index
) && l
) {
2942 LINEDEF
*line_def
= es
->first_line_def
;
2944 line_def
= line_def
->next
;
2947 if (line_def
->ending
== END_WRAP
) {
2949 y
-= es
->line_height
;
2950 li
= EDIT_EM_LineIndex(es
, l
);
2953 x
= LOWORD(GetTabbedTextExtentW(dc
, es
->text
+ li
, index
- li
,
2954 es
->tabs_count
, es
->tabs
)) - es
->x_offset
;
2956 LPWSTR text
= EDIT_GetPasswordPointer_SL(es
);
2957 if (index
< es
->x_offset
) {
2958 GetTextExtentPoint32W(dc
, text
+ index
,
2959 es
->x_offset
- index
, &size
);
2962 GetTextExtentPoint32W(dc
, text
+ es
->x_offset
,
2963 index
- es
->x_offset
, &size
);
2967 if (es
->style
& ES_PASSWORD
)
2968 HeapFree(GetProcessHeap(), 0, text
);
2970 x
+= es
->format_rect
.left
;
2971 y
+= es
->format_rect
.top
;
2973 SelectObject(dc
, old_font
);
2974 ReleaseDC(wnd
->hwndSelf
, dc
);
2975 return MAKELONG((INT16
)x
, (INT16
)y
);
2979 /*********************************************************************
2983 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2986 static void EDIT_EM_ReplaceSel(WND
*wnd
, EDITSTATE
*es
, BOOL can_undo
, LPCWSTR lpsz_replace
, BOOL send_update
)
2988 UINT strl
= strlenW(lpsz_replace
);
2989 UINT tl
= strlenW(es
->text
);
2997 TRACE("%s, can_undo %d, send_update %d\n",
2998 debugstr_w(lpsz_replace
), can_undo
, send_update
);
3000 s
= es
->selection_start
;
3001 e
= es
->selection_end
;
3003 if ((s
== e
) && !strl
)
3008 if (!EDIT_MakeFit(wnd
, es
, tl
- (e
- s
) + strl
))
3012 /* there is something to be deleted */
3014 utl
= strlenW(es
->undo_text
);
3015 if (!es
->undo_insert_count
&& (*es
->undo_text
&& (s
== es
->undo_position
))) {
3016 /* undo-buffer is extended to the right */
3017 EDIT_MakeUndoFit(es
, utl
+ e
- s
);
3018 strncpyW(es
->undo_text
+ utl
, es
->text
+ s
, e
- s
+ 1);
3019 (es
->undo_text
+ utl
)[e
- s
] = 0; /* ensure 0 termination */
3020 } else if (!es
->undo_insert_count
&& (*es
->undo_text
&& (e
== es
->undo_position
))) {
3021 /* undo-buffer is extended to the left */
3022 EDIT_MakeUndoFit(es
, utl
+ e
- s
);
3023 for (p
= es
->undo_text
+ utl
; p
>= es
->undo_text
; p
--)
3025 for (i
= 0 , p
= es
->undo_text
; i
< e
- s
; i
++)
3026 p
[i
] = (es
->text
+ s
)[i
];
3027 es
->undo_position
= s
;
3029 /* new undo-buffer */
3030 EDIT_MakeUndoFit(es
, e
- s
);
3031 strncpyW(es
->undo_text
, es
->text
+ s
, e
- s
+ 1);
3032 es
->undo_text
[e
- s
] = 0; /* ensure 0 termination */
3033 es
->undo_position
= s
;
3035 /* any deletion makes the old insertion-undo invalid */
3036 es
->undo_insert_count
= 0;
3038 EDIT_EM_EmptyUndoBuffer(es
);
3041 strcpyW(es
->text
+ s
, es
->text
+ e
);
3044 /* there is an insertion */
3046 if ((s
== es
->undo_position
) ||
3047 ((es
->undo_insert_count
) &&
3048 (s
== es
->undo_position
+ es
->undo_insert_count
)))
3050 * insertion is new and at delete position or
3051 * an extension to either left or right
3053 es
->undo_insert_count
+= strl
;
3055 /* new insertion undo */
3056 es
->undo_position
= s
;
3057 es
->undo_insert_count
= strl
;
3058 /* new insertion makes old delete-buffer invalid */
3059 *es
->undo_text
= '\0';
3062 EDIT_EM_EmptyUndoBuffer(es
);
3065 tl
= strlenW(es
->text
);
3066 for (p
= es
->text
+ tl
; p
>= es
->text
+ s
; p
--)
3068 for (i
= 0 , p
= es
->text
+ s
; i
< strl
; i
++)
3069 p
[i
] = lpsz_replace
[i
];
3070 if(es
->style
& ES_UPPERCASE
)
3071 CharUpperBuffW(p
, strl
);
3072 else if(es
->style
& ES_LOWERCASE
)
3073 CharLowerBuffW(p
, strl
);
3076 if (es
->style
& ES_MULTILINE
)
3078 INT s
= min(es
->selection_start
, es
->selection_end
);
3080 hrgn
= CreateRectRgn(0, 0, 0, 0);
3081 EDIT_BuildLineDefs_ML(wnd
, es
, s
, s
+ strl
,
3082 strl
- (es
->selection_end
- es
->selection_start
), hrgn
);
3085 EDIT_CalcLineWidth_SL(wnd
, es
);
3087 EDIT_EM_SetSel(wnd
, es
, s
, s
, FALSE
);
3088 es
->flags
|= EF_MODIFIED
;
3089 if (send_update
) es
->flags
|= EF_UPDATE
;
3090 EDIT_EM_ScrollCaret(wnd
, es
);
3092 /* force scroll info update */
3093 EDIT_UpdateScrollInfo(wnd
, es
);
3097 EDIT_UpdateTextRegion(wnd
, hrgn
, TRUE
);
3101 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3103 if(es
->flags
& EF_UPDATE
)
3105 es
->flags
&= ~EF_UPDATE
;
3106 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3111 /*********************************************************************
3116 static LRESULT
EDIT_EM_Scroll(WND
*wnd
, EDITSTATE
*es
, INT action
)
3120 if (!(es
->style
& ES_MULTILINE
))
3121 return (LRESULT
)FALSE
;
3131 if (es
->y_offset
< es
->line_count
- 1)
3136 dy
= -(es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3139 if (es
->y_offset
< es
->line_count
- 1)
3140 dy
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3143 return (LRESULT
)FALSE
;
3146 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3147 /* check if we are going to move too far */
3148 if(es
->y_offset
+ dy
> es
->line_count
- vlc
)
3149 dy
= es
->line_count
- vlc
- es
->y_offset
;
3151 /* Notification is done in EDIT_EM_LineScroll */
3153 EDIT_EM_LineScroll(wnd
, es
, 0, dy
);
3155 return MAKELONG((INT16
)dy
, (BOOL16
)TRUE
);
3159 /*********************************************************************
3164 static void EDIT_EM_ScrollCaret(WND
*wnd
, EDITSTATE
*es
)
3166 if (es
->style
& ES_MULTILINE
) {
3171 INT cw
= es
->char_width
;
3176 l
= EDIT_EM_LineFromChar(es
, es
->selection_end
);
3177 li
= EDIT_EM_LineIndex(es
, l
);
3178 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
));
3179 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
3180 if (l
>= es
->y_offset
+ vlc
)
3181 dy
= l
- vlc
+ 1 - es
->y_offset
;
3182 if (l
< es
->y_offset
)
3183 dy
= l
- es
->y_offset
;
3184 ww
= es
->format_rect
.right
- es
->format_rect
.left
;
3185 if (x
< es
->format_rect
.left
)
3186 dx
= x
- es
->format_rect
.left
- ww
/ HSCROLL_FRACTION
/ cw
* cw
;
3187 if (x
> es
->format_rect
.right
)
3188 dx
= x
- es
->format_rect
.left
- (HSCROLL_FRACTION
- 1) * ww
/ HSCROLL_FRACTION
/ cw
* cw
;
3191 /* check if we are going to move too far */
3192 if(es
->x_offset
+ dx
+ ww
> es
->text_width
)
3193 dx
= es
->text_width
- ww
- es
->x_offset
;
3195 EDIT_EM_LineScroll_internal(wnd
, es
, dx
, dy
);
3202 if (!(es
->style
& ES_AUTOHSCROLL
))
3205 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3206 format_width
= es
->format_rect
.right
- es
->format_rect
.left
;
3207 if (x
< es
->format_rect
.left
) {
3208 goal
= es
->format_rect
.left
+ format_width
/ HSCROLL_FRACTION
;
3211 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3212 } while ((x
< goal
) && es
->x_offset
);
3213 /* FIXME: use ScrollWindow() somehow to improve performance */
3214 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3215 } else if (x
> es
->format_rect
.right
) {
3217 INT len
= strlenW(es
->text
);
3218 goal
= es
->format_rect
.right
- format_width
/ HSCROLL_FRACTION
;
3221 x
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, es
->selection_end
, FALSE
));
3222 x_last
= SLOWORD(EDIT_EM_PosFromChar(wnd
, es
, len
, FALSE
));
3223 } while ((x
> goal
) && (x_last
> es
->format_rect
.right
));
3224 /* FIXME: use ScrollWindow() somehow to improve performance */
3225 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3229 if(es
->flags
& EF_FOCUSED
)
3230 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
, es
->flags
& EF_AFTER_WRAP
);
3234 /*********************************************************************
3238 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3241 static void EDIT_EM_SetHandle(WND
*wnd
, EDITSTATE
*es
, HLOCAL hloc
)
3243 if (!(es
->style
& ES_MULTILINE
))
3247 WARN("called with NULL handle\n");
3251 EDIT_UnlockBuffer(wnd
, es
, TRUE
);
3255 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
3256 es
->hloc16
= (HLOCAL16
)NULL
;
3263 LocalFree(es
->hloc32A
);
3264 es
->hloc32A
= (HLOCAL
)NULL
;
3275 countA
= LocalSize(hloc
);
3276 textA
= LocalLock(hloc
);
3277 countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, NULL
, 0);
3278 if(!(hloc32W_new
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, countW
* sizeof(WCHAR
))))
3280 ERR("Could not allocate new unicode buffer\n");
3283 textW
= LocalLock(hloc32W_new
);
3284 MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, textW
, countW
);
3285 LocalUnlock(hloc32W_new
);
3289 LocalFree(es
->hloc32W
);
3291 es
->hloc32W
= hloc32W_new
;
3295 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
3297 EDIT_LockBuffer(wnd
, es
);
3299 es
->x_offset
= es
->y_offset
= 0;
3300 es
->selection_start
= es
->selection_end
= 0;
3301 EDIT_EM_EmptyUndoBuffer(es
);
3302 es
->flags
&= ~EF_MODIFIED
;
3303 es
->flags
&= ~EF_UPDATE
;
3304 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3305 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3306 EDIT_EM_ScrollCaret(wnd
, es
);
3307 /* force scroll info update */
3308 EDIT_UpdateScrollInfo(wnd
, es
);
3312 /*********************************************************************
3316 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3319 static void EDIT_EM_SetHandle16(WND
*wnd
, EDITSTATE
*es
, HLOCAL16 hloc
)
3326 if (!(es
->style
& ES_MULTILINE
))
3330 WARN("called with NULL handle\n");
3334 EDIT_UnlockBuffer(wnd
, es
, TRUE
);
3338 LocalFree(es
->hloc32A
);
3339 es
->hloc32A
= (HLOCAL
)NULL
;
3342 countA
= LOCAL_Size(wnd
->hInstance
, hloc
);
3343 textA
= LOCAL_Lock(wnd
->hInstance
, hloc
);
3344 countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, NULL
, 0);
3345 if(!(hloc32W_new
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, countW
* sizeof(WCHAR
))))
3347 ERR("Could not allocate new unicode buffer\n");
3350 textW
= LocalLock(hloc32W_new
);
3351 MultiByteToWideChar(CP_ACP
, 0, textA
, countA
, textW
, countW
);
3352 LocalUnlock(hloc32W_new
);
3353 LOCAL_Unlock(wnd
->hInstance
, hloc
);
3356 LocalFree(es
->hloc32W
);
3358 es
->hloc32W
= hloc32W_new
;
3361 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
3363 EDIT_LockBuffer(wnd
, es
);
3365 es
->x_offset
= es
->y_offset
= 0;
3366 es
->selection_start
= es
->selection_end
= 0;
3367 EDIT_EM_EmptyUndoBuffer(es
);
3368 es
->flags
&= ~EF_MODIFIED
;
3369 es
->flags
&= ~EF_UPDATE
;
3370 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3371 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3372 EDIT_EM_ScrollCaret(wnd
, es
);
3373 /* force scroll info update */
3374 EDIT_UpdateScrollInfo(wnd
, es
);
3378 /*********************************************************************
3382 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3383 * However, the windows version is not complied to yet in all of edit.c
3386 static void EDIT_EM_SetLimitText(EDITSTATE
*es
, INT limit
)
3388 if (es
->style
& ES_MULTILINE
) {
3390 es
->buffer_limit
= min(limit
, BUFLIMIT_MULTI
);
3392 es
->buffer_limit
= BUFLIMIT_MULTI
;
3395 es
->buffer_limit
= min(limit
, BUFLIMIT_SINGLE
);
3397 es
->buffer_limit
= BUFLIMIT_SINGLE
;
3402 /*********************************************************************
3406 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3407 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3408 * of the char's width, according to the new docs.
3411 static void EDIT_EM_SetMargins(EDITSTATE
*es
, INT action
,
3412 INT left
, INT right
)
3414 if (action
& EC_LEFTMARGIN
) {
3415 if (left
!= EC_USEFONTINFO
)
3416 es
->left_margin
= left
;
3418 es
->left_margin
= es
->char_width
/ 3;
3421 if (action
& EC_RIGHTMARGIN
) {
3422 if (right
!= EC_USEFONTINFO
)
3423 es
->right_margin
= right
;
3425 es
->right_margin
= es
->char_width
/ 3;
3427 TRACE("left=%d, right=%d\n", es
->left_margin
, es
->right_margin
);
3431 /*********************************************************************
3433 * EM_SETPASSWORDCHAR
3436 static void EDIT_EM_SetPasswordChar(WND
*wnd
, EDITSTATE
*es
, WCHAR c
)
3438 if (es
->style
& ES_MULTILINE
)
3441 if (es
->password_char
== c
)
3444 es
->password_char
= c
;
3446 wnd
->dwStyle
|= ES_PASSWORD
;
3447 es
->style
|= ES_PASSWORD
;
3449 wnd
->dwStyle
&= ~ES_PASSWORD
;
3450 es
->style
&= ~ES_PASSWORD
;
3452 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3456 /*********************************************************************
3460 * note: unlike the specs say: the order of start and end
3461 * _is_ preserved in Windows. (i.e. start can be > end)
3462 * In other words: this handler is OK
3465 static void EDIT_EM_SetSel(WND
*wnd
, EDITSTATE
*es
, UINT start
, UINT end
, BOOL after_wrap
)
3467 UINT old_start
= es
->selection_start
;
3468 UINT old_end
= es
->selection_end
;
3469 UINT len
= strlenW(es
->text
);
3471 if (start
== (UINT
)-1) {
3472 start
= es
->selection_end
;
3473 end
= es
->selection_end
;
3475 start
= min(start
, len
);
3476 end
= min(end
, len
);
3478 es
->selection_start
= start
;
3479 es
->selection_end
= end
;
3481 es
->flags
|= EF_AFTER_WRAP
;
3483 es
->flags
&= ~EF_AFTER_WRAP
;
3484 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3485 ORDER_UINT(start
, end
);
3486 ORDER_UINT(end
, old_end
);
3487 ORDER_UINT(start
, old_start
);
3488 ORDER_UINT(old_start
, old_end
);
3489 if (end
!= old_start
)
3493 * ORDER_UINT32(end, old_start);
3494 * EDIT_InvalidateText(wnd, es, start, end);
3495 * EDIT_InvalidateText(wnd, es, old_start, old_end);
3496 * in place of the following if statement.
3498 if (old_start
> end
)
3500 EDIT_InvalidateText(wnd
, es
, start
, end
);
3501 EDIT_InvalidateText(wnd
, es
, old_start
, old_end
);
3505 EDIT_InvalidateText(wnd
, es
, start
, old_start
);
3506 EDIT_InvalidateText(wnd
, es
, end
, old_end
);
3509 else EDIT_InvalidateText(wnd
, es
, start
, old_end
);
3513 /*********************************************************************
3518 static BOOL
EDIT_EM_SetTabStops(EDITSTATE
*es
, INT count
, LPINT tabs
)
3520 if (!(es
->style
& ES_MULTILINE
))
3523 HeapFree(GetProcessHeap(), 0, es
->tabs
);
3524 es
->tabs_count
= count
;
3528 es
->tabs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(INT
));
3529 memcpy(es
->tabs
, tabs
, count
* sizeof(INT
));
3535 /*********************************************************************
3540 static BOOL
EDIT_EM_SetTabStops16(EDITSTATE
*es
, INT count
, LPINT16 tabs
)
3542 if (!(es
->style
& ES_MULTILINE
))
3545 HeapFree(GetProcessHeap(), 0, es
->tabs
);
3546 es
->tabs_count
= count
;
3551 es
->tabs
= HeapAlloc(GetProcessHeap(), 0, count
* sizeof(INT
));
3552 for (i
= 0 ; i
< count
; i
++)
3553 es
->tabs
[i
] = *tabs
++;
3559 /*********************************************************************
3561 * EM_SETWORDBREAKPROC
3564 static void EDIT_EM_SetWordBreakProc(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
)
3566 if (es
->word_break_proc
== (void *)lParam
)
3569 es
->word_break_proc
= (void *)lParam
;
3570 es
->word_break_proc16
= NULL
;
3572 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
)) {
3573 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3574 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3579 /*********************************************************************
3581 * EM_SETWORDBREAKPROC16
3584 static void EDIT_EM_SetWordBreakProc16(WND
*wnd
, EDITSTATE
*es
, EDITWORDBREAKPROC16 wbp
)
3586 if (es
->word_break_proc16
== wbp
)
3589 es
->word_break_proc
= NULL
;
3590 es
->word_break_proc16
= wbp
;
3591 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_AUTOHSCROLL
)) {
3592 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
3593 EDIT_UpdateText(wnd
, NULL
, TRUE
);
3598 /*********************************************************************
3603 static BOOL
EDIT_EM_Undo(WND
*wnd
, EDITSTATE
*es
)
3608 /* Protect read-only edit control from modification */
3609 if(es
->style
& ES_READONLY
)
3612 ulength
= strlenW(es
->undo_text
);
3613 utext
= HeapAlloc(GetProcessHeap(), 0, (ulength
+ 1) * sizeof(WCHAR
));
3615 strcpyW(utext
, es
->undo_text
);
3617 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3618 es
->undo_insert_count
, debugstr_w(utext
));
3620 EDIT_EM_SetSel(wnd
, es
, es
->undo_position
, es
->undo_position
+ es
->undo_insert_count
, FALSE
);
3621 EDIT_EM_EmptyUndoBuffer(es
);
3622 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, utext
, FALSE
);
3623 EDIT_EM_SetSel(wnd
, es
, es
->undo_position
, es
->undo_position
+ es
->undo_insert_count
, FALSE
);
3624 /* send the notification after the selection start and end are set */
3625 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3626 EDIT_EM_ScrollCaret(wnd
, es
);
3627 HeapFree(GetProcessHeap(), 0, utext
);
3629 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3630 es
->undo_insert_count
, debugstr_w(es
->undo_text
));
3635 /*********************************************************************
3640 static void EDIT_WM_Char(WND
*wnd
, EDITSTATE
*es
, WCHAR c
)
3644 /* Protect read-only edit control from modification */
3645 if(es
->style
& ES_READONLY
)
3648 control
= GetKeyState(VK_CONTROL
) & 0x8000;
3652 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3653 if(!(es
->style
& ES_MULTILINE
) && !(es
->style
& ES_WANTRETURN
))
3656 if (es
->style
& ES_MULTILINE
) {
3657 if (es
->style
& ES_READONLY
) {
3658 EDIT_MoveHome(wnd
, es
, FALSE
);
3659 EDIT_MoveDown_ML(wnd
, es
, FALSE
);
3661 static const WCHAR cr_lfW
[] = {'\r','\n',0};
3662 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, cr_lfW
, TRUE
);
3667 if ((es
->style
& ES_MULTILINE
) && !(es
->style
& ES_READONLY
))
3669 static const WCHAR tabW
[] = {'\t',0};
3670 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, tabW
, TRUE
);
3674 if (!(es
->style
& ES_READONLY
) && !control
) {
3675 if (es
->selection_start
!= es
->selection_end
)
3676 EDIT_WM_Clear(wnd
, es
);
3678 /* delete character left of caret */
3679 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
3680 EDIT_MoveBackward(wnd
, es
, TRUE
);
3681 EDIT_WM_Clear(wnd
, es
);
3686 SendMessageW(wnd
->hwndSelf
, WM_COPY
, 0, 0);
3689 SendMessageW(wnd
->hwndSelf
, WM_PASTE
, 0, 0);
3692 SendMessageW(wnd
->hwndSelf
, WM_CUT
, 0, 0);
3696 if (!(es
->style
& ES_READONLY
) && (c
>= ' ') && (c
!= 127)) {
3700 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, str
, TRUE
);
3707 /*********************************************************************
3712 static void EDIT_WM_Command(WND
*wnd
, EDITSTATE
*es
, INT code
, INT id
, HWND control
)
3714 if (code
|| control
)
3719 EDIT_EM_Undo(wnd
, es
);
3722 EDIT_WM_Cut(wnd
, es
);
3725 EDIT_WM_Copy(wnd
, es
);
3728 EDIT_WM_Paste(wnd
, es
);
3731 EDIT_WM_Clear(wnd
, es
);
3734 EDIT_EM_SetSel(wnd
, es
, 0, (UINT
)-1, FALSE
);
3735 EDIT_EM_ScrollCaret(wnd
, es
);
3738 ERR("unknown menu item, please report\n");
3744 /*********************************************************************
3748 * Note: the resource files resource/sysres_??.rc cannot define a
3749 * single popup menu. Hence we use a (dummy) menubar
3750 * containing the single popup menu as its first item.
3752 * FIXME: the message identifiers have been chosen arbitrarily,
3753 * hence we use MF_BYPOSITION.
3754 * We might as well use the "real" values (anybody knows ?)
3755 * The menu definition is in resources/sysres_??.rc.
3756 * Once these are OK, we better use MF_BYCOMMAND here
3757 * (as we do in EDIT_WM_Command()).
3760 static void EDIT_WM_ContextMenu(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
3762 HMENU menu
= LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3763 HMENU popup
= GetSubMenu(menu
, 0);
3764 UINT start
= es
->selection_start
;
3765 UINT end
= es
->selection_end
;
3767 ORDER_UINT(start
, end
);
3770 EnableMenuItem(popup
, 0, MF_BYPOSITION
| (EDIT_EM_CanUndo(es
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3772 EnableMenuItem(popup
, 2, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_PASSWORD
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3774 EnableMenuItem(popup
, 3, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_PASSWORD
) ? MF_ENABLED
: MF_GRAYED
));
3776 EnableMenuItem(popup
, 4, MF_BYPOSITION
| (IsClipboardFormatAvailable(CF_UNICODETEXT
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3778 EnableMenuItem(popup
, 5, MF_BYPOSITION
| ((end
- start
) && !(es
->style
& ES_READONLY
) ? MF_ENABLED
: MF_GRAYED
));
3780 EnableMenuItem(popup
, 7, MF_BYPOSITION
| (start
|| (end
!= strlenW(es
->text
)) ? MF_ENABLED
: MF_GRAYED
));
3782 TrackPopupMenu(popup
, TPM_LEFTALIGN
| TPM_RIGHTBUTTON
, x
, y
, 0, wnd
->hwndSelf
, NULL
);
3787 /*********************************************************************
3792 static void EDIT_WM_Copy(WND
*wnd
, EDITSTATE
*es
)
3794 INT s
= es
->selection_start
;
3795 INT e
= es
->selection_end
;
3802 hdst
= GlobalAlloc(GMEM_MOVEABLE
| GMEM_DDESHARE
, (DWORD
)(e
- s
+ 1) * sizeof(WCHAR
));
3803 dst
= GlobalLock(hdst
);
3804 strncpyW(dst
, es
->text
+ s
, e
- s
);
3805 dst
[e
- s
] = 0; /* ensure 0 termination */
3806 TRACE("%s\n", debugstr_w(dst
));
3808 OpenClipboard(wnd
->hwndSelf
);
3810 SetClipboardData(CF_UNICODETEXT
, hdst
);
3815 /*********************************************************************
3820 static LRESULT
EDIT_WM_Create(WND
*wnd
, EDITSTATE
*es
, LPCWSTR name
)
3822 TRACE("%s\n", debugstr_w(name
));
3824 * To initialize some final structure members, we call some helper
3825 * functions. However, since the EDITSTATE is not consistent (i.e.
3826 * not fully initialized), we should be very careful which
3827 * functions can be called, and in what order.
3829 EDIT_WM_SetFont(wnd
, es
, 0, FALSE
);
3830 EDIT_EM_EmptyUndoBuffer(es
);
3832 if (name
&& *name
) {
3833 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, name
, FALSE
);
3834 /* if we insert text to the editline, the text scrolls out
3835 * of the window, as the caret is placed after the insert
3836 * pos normally; thus we reset es->selection... to 0 and
3839 es
->selection_start
= es
->selection_end
= 0;
3840 /* send the notification after the selection start and end are set */
3841 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
3842 EDIT_EM_ScrollCaret(wnd
, es
);
3844 /* force scroll info update */
3845 EDIT_UpdateScrollInfo(wnd
, es
);
3850 /*********************************************************************
3855 static void EDIT_WM_Destroy(WND
*wnd
, EDITSTATE
*es
)
3860 while (LocalUnlock(es
->hloc32W
)) ;
3861 LocalFree(es
->hloc32W
);
3864 while (LocalUnlock(es
->hloc32A
)) ;
3865 LocalFree(es
->hloc32A
);
3868 while (LOCAL_Unlock(wnd
->hInstance
, es
->hloc16
)) ;
3869 LOCAL_Free(wnd
->hInstance
, es
->hloc16
);
3872 pc
= es
->first_line_def
;
3876 HeapFree(GetProcessHeap(), 0, pc
);
3880 HeapFree(GetProcessHeap(), 0, es
);
3881 *(EDITSTATE
**)wnd
->wExtra
= NULL
;
3885 /*********************************************************************
3890 static LRESULT
EDIT_WM_EraseBkGnd(WND
*wnd
, EDITSTATE
*es
, HDC dc
)
3895 if ( get_app_version() >= 0x40000 &&(
3896 !es
->bEnableState
|| (es
->style
& ES_READONLY
)))
3897 brush
= (HBRUSH
)EDIT_SEND_CTLCOLORSTATIC(wnd
, dc
);
3899 brush
= (HBRUSH
)EDIT_SEND_CTLCOLOR(wnd
, dc
);
3902 brush
= (HBRUSH
)GetStockObject(WHITE_BRUSH
);
3904 GetClientRect(wnd
->hwndSelf
, &rc
);
3905 IntersectClipRect(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
3906 GetClipBox(dc
, &rc
);
3908 * FIXME: specs say that we should UnrealizeObject() the brush,
3909 * but the specs of UnrealizeObject() say that we shouldn't
3910 * unrealize a stock object. The default brush that
3911 * DefWndProc() returns is ... a stock object.
3913 FillRect(dc
, &rc
, brush
);
3918 /*********************************************************************
3923 static INT
EDIT_WM_GetText(EDITSTATE
*es
, INT count
, LPARAM lParam
, BOOL unicode
)
3925 if(!count
) return 0;
3929 LPWSTR textW
= (LPWSTR
)lParam
;
3930 strncpyW(textW
, es
->text
, count
);
3931 textW
[count
- 1] = 0; /* ensure 0 termination */
3932 return strlenW(textW
);
3936 LPSTR textA
= (LPSTR
)lParam
;
3937 WideCharToMultiByte(CP_ACP
, 0, es
->text
, -1, textA
, count
, NULL
, NULL
);
3938 textA
[count
- 1] = 0; /* ensure 0 termination */
3939 return strlen(textA
);
3943 /*********************************************************************
3948 static LRESULT
EDIT_WM_HScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
)
3953 if (!(es
->style
& ES_MULTILINE
))
3956 if (!(es
->style
& ES_AUTOHSCROLL
))
3960 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
3963 TRACE("SB_LINELEFT\n");
3965 dx
= -es
->char_width
;
3968 TRACE("SB_LINERIGHT\n");
3969 if (es
->x_offset
< es
->text_width
)
3970 dx
= es
->char_width
;
3973 TRACE("SB_PAGELEFT\n");
3975 dx
= -fw
/ HSCROLL_FRACTION
/ es
->char_width
* es
->char_width
;
3978 TRACE("SB_PAGERIGHT\n");
3979 if (es
->x_offset
< es
->text_width
)
3980 dx
= fw
/ HSCROLL_FRACTION
/ es
->char_width
* es
->char_width
;
3988 TRACE("SB_RIGHT\n");
3989 if (es
->x_offset
< es
->text_width
)
3990 dx
= es
->text_width
- es
->x_offset
;
3993 TRACE("SB_THUMBTRACK %d\n", pos
);
3994 es
->flags
|= EF_HSCROLL_TRACK
;
3995 if(es
->style
& WS_HSCROLL
)
3996 dx
= pos
- es
->x_offset
;
4001 if(pos
< 0 || pos
> 100) return 0;
4002 /* Assume default scroll range 0-100 */
4003 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4004 new_x
= pos
* (es
->text_width
- fw
) / 100;
4005 dx
= es
->text_width
? (new_x
- es
->x_offset
) : 0;
4008 case SB_THUMBPOSITION
:
4009 TRACE("SB_THUMBPOSITION %d\n", pos
);
4010 es
->flags
&= ~EF_HSCROLL_TRACK
;
4011 if(wnd
->dwStyle
& WS_HSCROLL
)
4012 dx
= pos
- es
->x_offset
;
4017 if(pos
< 0 || pos
> 100) return 0;
4018 /* Assume default scroll range 0-100 */
4019 fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4020 new_x
= pos
* (es
->text_width
- fw
) / 100;
4021 dx
= es
->text_width
? (new_x
- es
->x_offset
) : 0;
4024 /* force scroll info update */
4025 EDIT_UpdateScrollInfo(wnd
, es
);
4026 EDIT_NOTIFY_PARENT(es
, EN_HSCROLL
, "EN_HSCROLL");
4030 TRACE("SB_ENDSCROLL\n");
4033 * FIXME : the next two are undocumented !
4034 * Are we doing the right thing ?
4035 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4036 * although it's also a regular control message.
4038 case EM_GETTHUMB
: /* this one is used by NT notepad */
4042 if(wnd
->dwStyle
& WS_HSCROLL
)
4043 ret
= GetScrollPos(wnd
->hwndSelf
, SB_HORZ
);
4046 /* Assume default scroll range 0-100 */
4047 INT fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4048 ret
= es
->text_width
? es
->x_offset
* 100 / (es
->text_width
- fw
) : 0;
4050 TRACE("EM_GETTHUMB: returning %ld\n", ret
);
4053 case EM_LINESCROLL16
:
4054 TRACE("EM_LINESCROLL16\n");
4059 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4065 INT fw
= es
->format_rect
.right
- es
->format_rect
.left
;
4066 /* check if we are going to move too far */
4067 if(es
->x_offset
+ dx
+ fw
> es
->text_width
)
4068 dx
= es
->text_width
- fw
- es
->x_offset
;
4070 EDIT_EM_LineScroll_internal(wnd
, es
, dx
, 0);
4076 /*********************************************************************
4081 static BOOL
EDIT_CheckCombo(WND
*wnd
, EDITSTATE
*es
, UINT msg
, INT key
)
4083 HWND hLBox
= es
->hwndListBox
;
4091 hCombo
= wnd
->parent
->hwndSelf
;
4095 TRACE_(combo
)("[%04x]: handling msg %04x (%04x)\n",
4096 wnd
->hwndSelf
, (UINT16
)msg
, (UINT16
)key
);
4098 if (key
== VK_UP
|| key
== VK_DOWN
)
4100 if (SendMessageW(hCombo
, CB_GETEXTENDEDUI
, 0, 0))
4103 if (msg
== WM_KEYDOWN
|| nEUI
)
4104 bDropped
= (BOOL
)SendMessageW(hCombo
, CB_GETDROPPEDSTATE
, 0, 0);
4110 if (!bDropped
&& nEUI
&& (key
== VK_UP
|| key
== VK_DOWN
))
4112 /* make sure ComboLBox pops up */
4113 SendMessageW(hCombo
, CB_SETEXTENDEDUI
, FALSE
, 0);
4118 SendMessageW(hLBox
, WM_KEYDOWN
, (WPARAM
)key
, 0);
4121 case WM_SYSKEYDOWN
: /* Handle Alt+up/down arrows */
4123 SendMessageW(hCombo
, CB_SHOWDROPDOWN
, bDropped
? FALSE
: TRUE
, 0);
4125 SendMessageW(hLBox
, WM_KEYDOWN
, (WPARAM
)VK_F4
, 0);
4130 SendMessageW(hCombo
, CB_SETEXTENDEDUI
, TRUE
, 0);
4136 /*********************************************************************
4140 * Handling of special keys that don't produce a WM_CHAR
4141 * (i.e. non-printable keys) & Backspace & Delete
4144 static LRESULT
EDIT_WM_KeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
)
4149 if (GetKeyState(VK_MENU
) & 0x8000)
4152 shift
= GetKeyState(VK_SHIFT
) & 0x8000;
4153 control
= GetKeyState(VK_CONTROL
) & 0x8000;
4158 if (EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
) || key
== VK_F4
)
4163 if ((es
->style
& ES_MULTILINE
) && (key
== VK_UP
))
4164 EDIT_MoveUp_ML(wnd
, es
, shift
);
4167 EDIT_MoveWordBackward(wnd
, es
, shift
);
4169 EDIT_MoveBackward(wnd
, es
, shift
);
4172 if (EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
))
4176 if ((es
->style
& ES_MULTILINE
) && (key
== VK_DOWN
))
4177 EDIT_MoveDown_ML(wnd
, es
, shift
);
4179 EDIT_MoveWordForward(wnd
, es
, shift
);
4181 EDIT_MoveForward(wnd
, es
, shift
);
4184 EDIT_MoveHome(wnd
, es
, shift
);
4187 EDIT_MoveEnd(wnd
, es
, shift
);
4190 if (es
->style
& ES_MULTILINE
)
4191 EDIT_MovePageUp_ML(wnd
, es
, shift
);
4193 EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
);
4196 if (es
->style
& ES_MULTILINE
)
4197 EDIT_MovePageDown_ML(wnd
, es
, shift
);
4199 EDIT_CheckCombo(wnd
, es
, WM_KEYDOWN
, key
);
4202 if (!(es
->style
& ES_READONLY
) && !(shift
&& control
)) {
4203 if (es
->selection_start
!= es
->selection_end
) {
4205 EDIT_WM_Cut(wnd
, es
);
4207 EDIT_WM_Clear(wnd
, es
);
4210 /* delete character left of caret */
4211 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4212 EDIT_MoveBackward(wnd
, es
, TRUE
);
4213 EDIT_WM_Clear(wnd
, es
);
4214 } else if (control
) {
4215 /* delete to end of line */
4216 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4217 EDIT_MoveEnd(wnd
, es
, TRUE
);
4218 EDIT_WM_Clear(wnd
, es
);
4220 /* delete character right of caret */
4221 EDIT_EM_SetSel(wnd
, es
, (UINT
)-1, 0, FALSE
);
4222 EDIT_MoveForward(wnd
, es
, TRUE
);
4223 EDIT_WM_Clear(wnd
, es
);
4230 if (!(es
->style
& ES_READONLY
))
4231 EDIT_WM_Paste(wnd
, es
);
4233 EDIT_WM_Copy(wnd
, es
);
4236 /* If the edit doesn't want the return send a message to the default object */
4237 if(!(es
->style
& ES_WANTRETURN
))
4239 HWND hwndParent
= GetParent(wnd
->hwndSelf
);
4240 DWORD dw
= SendMessageW( hwndParent
, DM_GETDEFID
, 0, 0 );
4241 if (HIWORD(dw
) == DC_HASDEFID
)
4243 SendMessageW( hwndParent
, WM_COMMAND
,
4244 MAKEWPARAM( LOWORD(dw
), BN_CLICKED
),
4245 (LPARAM
)GetDlgItem( hwndParent
, LOWORD(dw
) ) );
4254 /*********************************************************************
4259 static LRESULT
EDIT_WM_KillFocus(WND
*wnd
, EDITSTATE
*es
)
4261 es
->flags
&= ~EF_FOCUSED
;
4263 if(!(es
->style
& ES_NOHIDESEL
))
4264 EDIT_InvalidateText(wnd
, es
, es
->selection_start
, es
->selection_end
);
4265 EDIT_NOTIFY_PARENT(es
, EN_KILLFOCUS
, "EN_KILLFOCUS");
4270 /*********************************************************************
4274 * The caret position has been set on the WM_LBUTTONDOWN message
4277 static LRESULT
EDIT_WM_LButtonDblClk(WND
*wnd
, EDITSTATE
*es
)
4280 INT e
= es
->selection_end
;
4285 if (!(es
->flags
& EF_FOCUSED
))
4288 l
= EDIT_EM_LineFromChar(es
, e
);
4289 li
= EDIT_EM_LineIndex(es
, l
);
4290 ll
= EDIT_EM_LineLength(es
, e
);
4291 s
= li
+ EDIT_CallWordBreakProc(es
, li
, e
- li
, ll
, WB_LEFT
);
4292 e
= li
+ EDIT_CallWordBreakProc(es
, li
, e
- li
, ll
, WB_RIGHT
);
4293 EDIT_EM_SetSel(wnd
, es
, s
, e
, FALSE
);
4294 EDIT_EM_ScrollCaret(wnd
, es
);
4299 /*********************************************************************
4304 static LRESULT
EDIT_WM_LButtonDown(WND
*wnd
, EDITSTATE
*es
, DWORD keys
, INT x
, INT y
)
4309 if (!(es
->flags
& EF_FOCUSED
))
4312 es
->bCaptureState
= TRUE
;
4313 SetCapture(wnd
->hwndSelf
);
4314 EDIT_ConfinePoint(es
, &x
, &y
);
4315 e
= EDIT_CharFromPos(wnd
, es
, x
, y
, &after_wrap
);
4316 EDIT_EM_SetSel(wnd
, es
, (keys
& MK_SHIFT
) ? es
->selection_start
: e
, e
, after_wrap
);
4317 EDIT_EM_ScrollCaret(wnd
, es
);
4318 es
->region_posx
= es
->region_posy
= 0;
4319 SetTimer(wnd
->hwndSelf
, 0, 100, NULL
);
4324 /*********************************************************************
4329 static LRESULT
EDIT_WM_LButtonUp(HWND hwndSelf
, EDITSTATE
*es
)
4331 if (es
->bCaptureState
&& GetCapture() == hwndSelf
) {
4332 KillTimer(hwndSelf
, 0);
4335 es
->bCaptureState
= FALSE
;
4340 /*********************************************************************
4345 static LRESULT
EDIT_WM_MButtonDown(WND
*wnd
)
4347 SendMessageW(wnd
->hwndSelf
,WM_PASTE
,0,0);
4352 /*********************************************************************
4357 static LRESULT
EDIT_WM_MouseMove(WND
*wnd
, EDITSTATE
*es
, INT x
, INT y
)
4363 if (GetCapture() != wnd
->hwndSelf
)
4367 * FIXME: gotta do some scrolling if outside client
4368 * area. Maybe reset the timer ?
4371 EDIT_ConfinePoint(es
, &x
, &y
);
4372 es
->region_posx
= (prex
< x
) ? -1 : ((prex
> x
) ? 1 : 0);
4373 es
->region_posy
= (prey
< y
) ? -1 : ((prey
> y
) ? 1 : 0);
4374 e
= EDIT_CharFromPos(wnd
, es
, x
, y
, &after_wrap
);
4375 EDIT_EM_SetSel(wnd
, es
, es
->selection_start
, e
, after_wrap
);
4380 /*********************************************************************
4385 static LRESULT
EDIT_WM_NCCreate(WND
*wnd
, DWORD style
, HWND hwndParent
, BOOL unicode
)
4390 TRACE("Creating %s edit control, style = %08lx\n",
4391 unicode
? "Unicode" : "ANSI", style
);
4393 if (!(es
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*es
))))
4395 *(EDITSTATE
**)wnd
->wExtra
= es
;
4398 * Note: since the EDITSTATE has not been fully initialized yet,
4399 * we can't use any API calls that may send
4400 * WM_XXX messages before WM_NCCREATE is completed.
4403 es
->is_unicode
= unicode
;
4406 es
->bEnableState
= !(style
& WS_DISABLED
);
4409 * In Win95 look and feel, the WS_BORDER style is replaced by the
4410 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4411 * control a non client area.
4413 if (TWEAK_WineLook
!= WIN31_LOOK
)
4415 if (es
->style
& WS_BORDER
)
4417 es
->style
&= ~WS_BORDER
;
4418 wnd
->dwStyle
&= ~WS_BORDER
;
4419 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
4424 if ((es
->style
& WS_BORDER
) && !(es
->style
& WS_DLGFRAME
))
4425 wnd
->dwStyle
&= ~WS_BORDER
;
4428 /* Save parent, which will be notified by EN_* messages */
4429 es
->hwndParent
= hwndParent
;
4431 if (es
->style
& ES_COMBO
)
4432 es
->hwndListBox
= GetDlgItem(hwndParent
, ID_CB_LISTBOX
);
4434 if (es
->style
& ES_MULTILINE
) {
4435 es
->buffer_limit
= BUFLIMIT_MULTI
;
4436 if (es
->style
& WS_VSCROLL
)
4437 es
->style
|= ES_AUTOVSCROLL
;
4438 if (es
->style
& WS_HSCROLL
)
4439 es
->style
|= ES_AUTOHSCROLL
;
4440 es
->style
&= ~ES_PASSWORD
;
4441 if ((es
->style
& ES_CENTER
) || (es
->style
& ES_RIGHT
)) {
4442 if (es
->style
& ES_RIGHT
)
4443 es
->style
&= ~ES_CENTER
;
4444 es
->style
&= ~WS_HSCROLL
;
4445 es
->style
&= ~ES_AUTOHSCROLL
;
4448 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4449 es
->style
|= ES_AUTOVSCROLL
;
4451 es
->buffer_limit
= BUFLIMIT_SINGLE
;
4452 es
->style
&= ~ES_CENTER
;
4453 es
->style
&= ~ES_RIGHT
;
4454 es
->style
&= ~WS_HSCROLL
;
4455 es
->style
&= ~WS_VSCROLL
;
4456 es
->style
&= ~ES_AUTOVSCROLL
;
4457 es
->style
&= ~ES_WANTRETURN
;
4458 if (es
->style
& ES_UPPERCASE
) {
4459 es
->style
&= ~ES_LOWERCASE
;
4460 es
->style
&= ~ES_NUMBER
;
4461 } else if (es
->style
& ES_LOWERCASE
)
4462 es
->style
&= ~ES_NUMBER
;
4463 if (es
->style
& ES_PASSWORD
)
4464 es
->password_char
= '*';
4466 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4467 es
->style
|= ES_AUTOHSCROLL
;
4470 alloc_size
= ROUND_TO_GROW((es
->buffer_size
+ 1) * sizeof(WCHAR
));
4471 if(!(es
->hloc32W
= LocalAlloc(LMEM_MOVEABLE
| LMEM_ZEROINIT
, alloc_size
)))
4473 es
->buffer_size
= LocalSize(es
->hloc32W
)/sizeof(WCHAR
) - 1;
4475 if (!(es
->undo_text
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, (es
->buffer_size
+ 1) * sizeof(WCHAR
))))
4477 es
->undo_buffer_size
= es
->buffer_size
;
4479 if (es
->style
& ES_MULTILINE
)
4480 if (!(es
->first_line_def
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LINEDEF
))))
4487 /*********************************************************************
4492 static void EDIT_WM_Paint(WND
*wnd
, EDITSTATE
*es
, WPARAM wParam
)
4501 BOOL rev
= es
->bEnableState
&&
4502 ((es
->flags
& EF_FOCUSED
) ||
4503 (es
->style
& ES_NOHIDESEL
));
4505 dc
= BeginPaint(wnd
->hwndSelf
, &ps
);
4508 if(es
->style
& WS_BORDER
) {
4509 GetClientRect(wnd
->hwndSelf
, &rc
);
4510 if(es
->style
& ES_MULTILINE
) {
4511 if(es
->style
& WS_HSCROLL
) rc
.bottom
++;
4512 if(es
->style
& WS_VSCROLL
) rc
.right
++;
4514 Rectangle(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
4516 IntersectClipRect(dc
, es
->format_rect
.left
,
4517 es
->format_rect
.top
,
4518 es
->format_rect
.right
,
4519 es
->format_rect
.bottom
);
4520 if (es
->style
& ES_MULTILINE
) {
4521 GetClientRect(wnd
->hwndSelf
, &rc
);
4522 IntersectClipRect(dc
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
);
4525 old_font
= SelectObject(dc
, es
->font
);
4526 if ( get_app_version() >= 0x40000 &&(
4527 !es
->bEnableState
|| (es
->style
& ES_READONLY
)))
4528 EDIT_SEND_CTLCOLORSTATIC(wnd
, dc
);
4530 EDIT_SEND_CTLCOLOR(wnd
, dc
);
4532 if (!es
->bEnableState
)
4533 SetTextColor(dc
, GetSysColor(COLOR_GRAYTEXT
));
4534 GetClipBox(dc
, &rcRgn
);
4535 if (es
->style
& ES_MULTILINE
) {
4536 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4537 for (i
= es
->y_offset
; i
<= min(es
->y_offset
+ vlc
, es
->y_offset
+ es
->line_count
- 1) ; i
++) {
4538 EDIT_GetLineRect(wnd
, es
, i
, 0, -1, &rcLine
);
4539 if (IntersectRect(&rc
, &rcRgn
, &rcLine
))
4540 EDIT_PaintLine(wnd
, es
, dc
, i
, rev
);
4543 EDIT_GetLineRect(wnd
, es
, 0, 0, -1, &rcLine
);
4544 if (IntersectRect(&rc
, &rcRgn
, &rcLine
))
4545 EDIT_PaintLine(wnd
, es
, dc
, 0, rev
);
4548 SelectObject(dc
, old_font
);
4551 EndPaint(wnd
->hwndSelf
, &ps
);
4555 /*********************************************************************
4560 static void EDIT_WM_Paste(WND
*wnd
, EDITSTATE
*es
)
4565 /* Protect read-only edit control from modification */
4566 if(es
->style
& ES_READONLY
)
4569 OpenClipboard(wnd
->hwndSelf
);
4570 if ((hsrc
= GetClipboardData(CF_UNICODETEXT
))) {
4571 src
= (LPWSTR
)GlobalLock(hsrc
);
4572 EDIT_EM_ReplaceSel(wnd
, es
, TRUE
, src
, TRUE
);
4579 /*********************************************************************
4584 static void EDIT_WM_SetFocus(WND
*wnd
, EDITSTATE
*es
)
4586 es
->flags
|= EF_FOCUSED
;
4587 CreateCaret(wnd
->hwndSelf
, 0, 2, es
->line_height
);
4588 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
,
4589 es
->flags
& EF_AFTER_WRAP
);
4590 if(!(es
->style
& ES_NOHIDESEL
))
4591 EDIT_InvalidateText(wnd
, es
, es
->selection_start
, es
->selection_end
);
4592 ShowCaret(wnd
->hwndSelf
);
4593 EDIT_NOTIFY_PARENT(es
, EN_SETFOCUS
, "EN_SETFOCUS");
4597 /*********************************************************************
4601 * With Win95 look the margins are set to default font value unless
4602 * the system font (font == 0) is being set, in which case they are left
4606 static void EDIT_WM_SetFont(WND
*wnd
, EDITSTATE
*es
, HFONT font
, BOOL redraw
)
4614 dc
= GetDC(wnd
->hwndSelf
);
4616 old_font
= SelectObject(dc
, font
);
4617 GetTextMetricsW(dc
, &tm
);
4618 es
->line_height
= tm
.tmHeight
;
4619 es
->char_width
= tm
.tmAveCharWidth
;
4621 SelectObject(dc
, old_font
);
4622 ReleaseDC(wnd
->hwndSelf
, dc
);
4623 if (font
&& (TWEAK_WineLook
> WIN31_LOOK
))
4624 EDIT_EM_SetMargins(es
, EC_LEFTMARGIN
| EC_RIGHTMARGIN
,
4625 EC_USEFONTINFO
, EC_USEFONTINFO
);
4627 /* Force the recalculation of the format rect for each font change */
4628 GetClientRect(wnd
->hwndSelf
, &r
);
4629 EDIT_SetRectNP(wnd
, es
, &r
);
4631 if (es
->style
& ES_MULTILINE
)
4632 EDIT_BuildLineDefs_ML(wnd
, es
, 0, strlenW(es
->text
), 0, (HRGN
)0);
4634 EDIT_CalcLineWidth_SL(wnd
, es
);
4637 EDIT_UpdateText(wnd
, NULL
, TRUE
);
4638 if (es
->flags
& EF_FOCUSED
) {
4640 CreateCaret(wnd
->hwndSelf
, 0, 2, es
->line_height
);
4641 EDIT_SetCaretPos(wnd
, es
, es
->selection_end
,
4642 es
->flags
& EF_AFTER_WRAP
);
4643 ShowCaret(wnd
->hwndSelf
);
4648 /*********************************************************************
4653 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4654 * The modified flag is reset. No notifications are sent.
4656 * For single-line controls, reception of WM_SETTEXT triggers:
4657 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4660 static void EDIT_WM_SetText(WND
*wnd
, EDITSTATE
*es
, LPARAM lParam
, BOOL unicode
)
4665 text
= (LPWSTR
)lParam
;
4668 LPCSTR textA
= (LPCSTR
)lParam
;
4669 INT countW
= MultiByteToWideChar(CP_ACP
, 0, textA
, -1, NULL
, 0);
4670 if((text
= HeapAlloc(GetProcessHeap(), 0, countW
* sizeof(WCHAR
))))
4671 MultiByteToWideChar(CP_ACP
, 0, textA
, -1, text
, countW
);
4674 EDIT_EM_SetSel(wnd
, es
, 0, (UINT
)-1, FALSE
);
4676 TRACE("%s\n", debugstr_w(text
));
4677 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, text
, FALSE
);
4679 HeapFree(GetProcessHeap(), 0, text
);
4681 static const WCHAR empty_stringW
[] = {0};
4683 EDIT_EM_ReplaceSel(wnd
, es
, FALSE
, empty_stringW
, FALSE
);
4686 es
->flags
&= ~EF_MODIFIED
;
4687 EDIT_EM_SetSel(wnd
, es
, 0, 0, FALSE
);
4688 /* Send the notification after the selection start and end have been set
4689 * edit control doesn't send notification on WM_SETTEXT
4690 * if it is multiline, or it is part of combobox
4692 if( !((es
->style
& ES_MULTILINE
) || es
->hwndListBox
))
4693 EDIT_NOTIFY_PARENT(es
, EN_CHANGE
, "EN_CHANGE");
4694 EDIT_EM_ScrollCaret(wnd
, es
);
4698 /*********************************************************************
4703 static void EDIT_WM_Size(WND
*wnd
, EDITSTATE
*es
, UINT action
, INT width
, INT height
)
4705 if ((action
== SIZE_MAXIMIZED
) || (action
== SIZE_RESTORED
)) {
4707 TRACE("width = %d, height = %d\n", width
, height
);
4708 SetRect(&rc
, 0, 0, width
, height
);
4709 EDIT_SetRectNP(wnd
, es
, &rc
);
4710 EDIT_UpdateText(wnd
, NULL
, TRUE
);
4715 /*********************************************************************
4720 static LRESULT
EDIT_WM_SysKeyDown(WND
*wnd
, EDITSTATE
*es
, INT key
, DWORD key_data
)
4722 if ((key
== VK_BACK
) && (key_data
& 0x2000)) {
4723 if (EDIT_EM_CanUndo(es
))
4724 EDIT_EM_Undo(wnd
, es
);
4726 } else if (key
== VK_UP
|| key
== VK_DOWN
) {
4727 if (EDIT_CheckCombo(wnd
, es
, WM_SYSKEYDOWN
, key
))
4730 return DefWindowProcW(wnd
->hwndSelf
, WM_SYSKEYDOWN
, (WPARAM
)key
, (LPARAM
)key_data
);
4734 /*********************************************************************
4739 static void EDIT_WM_Timer(WND
*wnd
, EDITSTATE
*es
)
4741 if (es
->region_posx
< 0) {
4742 EDIT_MoveBackward(wnd
, es
, TRUE
);
4743 } else if (es
->region_posx
> 0) {
4744 EDIT_MoveForward(wnd
, es
, TRUE
);
4747 * FIXME: gotta do some vertical scrolling here, like
4748 * EDIT_EM_LineScroll(wnd, 0, 1);
4752 /*********************************************************************
4757 static LRESULT
EDIT_WM_VScroll(WND
*wnd
, EDITSTATE
*es
, INT action
, INT pos
)
4761 if (!(es
->style
& ES_MULTILINE
))
4764 if (!(es
->style
& ES_AUTOVSCROLL
))
4773 TRACE("action %d\n", action
);
4774 EDIT_EM_Scroll(wnd
, es
, action
);
4781 TRACE("SB_BOTTOM\n");
4782 dy
= es
->line_count
- 1 - es
->y_offset
;
4785 TRACE("SB_THUMBTRACK %d\n", pos
);
4786 es
->flags
|= EF_VSCROLL_TRACK
;
4787 if(es
->style
& WS_VSCROLL
)
4788 dy
= pos
- es
->y_offset
;
4791 /* Assume default scroll range 0-100 */
4794 if(pos
< 0 || pos
> 100) return 0;
4795 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4796 new_y
= pos
* (es
->line_count
- vlc
) / 100;
4797 dy
= es
->line_count
? (new_y
- es
->y_offset
) : 0;
4798 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4799 es
->line_count
, es
->y_offset
, pos
, dy
);
4802 case SB_THUMBPOSITION
:
4803 TRACE("SB_THUMBPOSITION %d\n", pos
);
4804 es
->flags
&= ~EF_VSCROLL_TRACK
;
4805 if(es
->style
& WS_VSCROLL
)
4806 dy
= pos
- es
->y_offset
;
4809 /* Assume default scroll range 0-100 */
4812 if(pos
< 0 || pos
> 100) return 0;
4813 vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4814 new_y
= pos
* (es
->line_count
- vlc
) / 100;
4815 dy
= es
->line_count
? (new_y
- es
->y_offset
) : 0;
4816 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4817 es
->line_count
, es
->y_offset
, pos
, dy
);
4821 /* force scroll info update */
4822 EDIT_UpdateScrollInfo(wnd
, es
);
4823 EDIT_NOTIFY_PARENT(es
, EN_VSCROLL
, "EN_VSCROLL");
4827 TRACE("SB_ENDSCROLL\n");
4830 * FIXME : the next two are undocumented !
4831 * Are we doing the right thing ?
4832 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4833 * although it's also a regular control message.
4835 case EM_GETTHUMB
: /* this one is used by NT notepad */
4839 if(wnd
->dwStyle
& WS_VSCROLL
)
4840 ret
= GetScrollPos(wnd
->hwndSelf
, SB_VERT
);
4843 /* Assume default scroll range 0-100 */
4844 INT vlc
= (es
->format_rect
.bottom
- es
->format_rect
.top
) / es
->line_height
;
4845 ret
= es
->line_count
? es
->y_offset
* 100 / (es
->line_count
- vlc
) : 0;
4847 TRACE("EM_GETTHUMB: returning %ld\n", ret
);
4850 case EM_LINESCROLL16
:
4851 TRACE("EM_LINESCROLL16 %d\n", pos
);
4856 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4861 EDIT_EM_LineScroll(wnd
, es
, 0, dy
);
4865 /*********************************************************************
4870 static void EDIT_UpdateTextRegion(WND
*wnd
, HRGN hrgn
, BOOL bErase
)
4872 EDITSTATE
*es
= *(EDITSTATE
**)((wnd
)->wExtra
);
4874 if (es
->flags
& EF_UPDATE
)
4875 EDIT_NOTIFY_PARENT(es
, EN_UPDATE
, "EN_UPDATE");
4877 InvalidateRgn(wnd
->hwndSelf
, hrgn
, bErase
);
4881 /*********************************************************************
4886 static void EDIT_UpdateText(WND
*wnd
, LPRECT rc
, BOOL bErase
)
4888 EDITSTATE
*es
= *(EDITSTATE
**)((wnd
)->wExtra
);
4890 if (es
->flags
& EF_UPDATE
)
4891 EDIT_NOTIFY_PARENT(es
, EN_UPDATE
, "EN_UPDATE");
4893 InvalidateRect(wnd
->hwndSelf
, rc
, bErase
);