Do not send EN_UPDATE to multi-line edit controls.
[wine/multimedia.git] / controls / edit.c
blobcebb44c08db5cd79b60f1ae2a9d671291a693db1
1 /*
2 * Edit control
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
8 */
11 * please read EDIT.TODO (and update it when you change things)
14 #include "config.h"
16 #include <string.h>
18 #include "winbase.h"
19 #include "winnt.h"
20 #include "win.h"
21 #include "wine/winbase16.h"
22 #include "wine/winuser16.h"
23 #include "combo.h"
24 #include "local.h"
25 #include "selectors.h"
26 #include "debugtools.h"
27 #include "tweak.h"
29 DEFAULT_DEBUG_CHANNEL(edit);
30 DECLARE_DEBUG_CHANNEL(combo);
31 DECLARE_DEBUG_CHANNEL(relay);
33 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
34 FIXME: BTW, new specs say 65535 (do you dare ???) */
35 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
36 #define BUFSTART_MULTI 1024 /* starting size */
37 #define BUFSTART_SINGLE 256 /* starting size */
38 #define GROWLENGTH 64 /* buffers grow by this much */
39 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
42 * extra flags for EDITSTATE.flags field
44 #define EF_MODIFIED 0x0001 /* text has been modified */
45 #define EF_FOCUSED 0x0002 /* we have input focus */
46 #define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
47 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
48 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
49 #define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
50 #define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
51 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
52 wrapped line, instead of in front of the next character */
53 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
55 typedef enum
57 END_0 = 0, /* line ends with terminating '\0' character */
58 END_WRAP, /* line is wrapped */
59 END_HARD, /* line ends with a hard return '\r\n' */
60 END_SOFT /* line ends with a soft return '\r\r\n' */
61 } LINE_END;
63 typedef struct tagLINEDEF {
64 INT length; /* bruto length of a line in bytes */
65 INT net_length; /* netto length of a line in visible characters */
66 LINE_END ending;
67 INT width; /* width of the line in pixels */
68 struct tagLINEDEF *next;
69 } LINEDEF;
71 typedef struct
73 HANDLE heap; /* our own heap */
74 LPSTR text; /* the actual contents of the control */
75 INT buffer_size; /* the size of the buffer */
76 INT buffer_limit; /* the maximum size to which the buffer may grow */
77 HFONT font; /* NULL means standard system font */
78 INT x_offset; /* scroll offset for multi lines this is in pixels
79 for single lines it's in characters */
80 INT line_height; /* height of a screen line in pixels */
81 INT char_width; /* average character width in pixels */
82 DWORD style; /* sane version of wnd->dwStyle */
83 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
84 INT undo_insert_count; /* number of characters inserted in sequence */
85 INT undo_position; /* character index of the insertion and deletion */
86 LPSTR undo_text; /* deleted text */
87 INT undo_buffer_size; /* size of the deleted text buffer */
88 INT selection_start; /* == selection_end if no selection */
89 INT selection_end; /* == current caret position */
90 CHAR password_char; /* == 0 if no password char, and for multi line controls */
91 INT left_margin; /* in pixels */
92 INT right_margin; /* in pixels */
93 RECT format_rect;
94 INT region_posx; /* Position of cursor relative to region: */
95 INT region_posy; /* -1: to left, 0: within, 1: to right */
96 EDITWORDBREAKPROC16 word_break_proc16;
97 EDITWORDBREAKPROCA word_break_proc32A;
98 INT line_count; /* number of lines */
99 INT y_offset; /* scroll offset in number of lines */
100 BOOL bCaptureState; /* flag indicating whether mouse was captured */
101 BOOL bEnableState; /* flag keeping the enable state */
102 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
104 * only for multi line controls
106 INT lock_count; /* amount of re-entries in the EditWndProc */
107 INT tabs_count;
108 LPINT tabs;
109 INT text_width; /* width of the widest line in pixels */
110 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
111 HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
112 HLOCAL hloc32; /* for controls receiving EM_GETHANDLE */
113 } EDITSTATE;
116 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
117 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
119 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
120 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
122 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
123 do {TRACE("notification " str " sent to hwnd=%08x\n", \
124 (UINT)(hwnd));} while(0)
126 /* used for disabled or read-only edit control */
127 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
128 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
129 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
130 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
131 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
132 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
133 #define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
134 do {DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str); \
135 SendMessageA((wnd)->parent->hwndSelf, WM_COMMAND, \
136 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
137 (LPARAM)(wnd)->hwndSelf);} while(0)
138 #define DPRINTF_EDIT_MSG16(str) \
139 TRACE(\
140 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
141 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
142 #define DPRINTF_EDIT_MSG32(str) \
143 TRACE(\
144 "32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
145 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
147 /*********************************************************************
149 * Declarations
154 * These functions have trivial implementations
155 * We still like to call them internally
156 * "static inline" makes them more like macro's
158 static inline BOOL EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es);
159 static inline void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es);
160 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
161 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
164 * Helper functions only valid for one type of control
166 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
167 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es);
168 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
169 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
170 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
171 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
173 * Helper functions valid for both single line _and_ multi line controls
175 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
176 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
177 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT x, LPINT y);
178 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
179 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end);
180 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
181 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT size);
182 static BOOL EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT size);
183 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend);
184 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend);
185 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend);
186 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend);
187 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend);
188 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend);
189 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
190 static INT EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
191 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
192 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
193 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
194 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
196 * EM_XXX message handlers
198 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y);
199 static BOOL EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL add_eol);
200 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es);
201 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
202 static INT EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT line, LPSTR lpch);
203 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT start, LPUINT end);
204 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
205 static INT EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT index);
206 static INT EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT line);
207 static INT EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT index);
208 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
209 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
210 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update);
211 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
212 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
213 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc);
214 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
215 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT limit);
216 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT action, INT left, INT right);
217 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c);
218 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
219 static BOOL EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT count, LPINT tabs);
220 static BOOL EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT count, LPINT16 tabs);
221 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
222 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
223 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
225 * WM_XXX message handlers
227 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data);
228 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
229 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND hwnd, INT x, INT y);
230 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
231 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs);
232 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
233 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc);
234 static INT EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT count, LPSTR text);
235 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
236 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
237 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND window_getting_focus);
238 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
239 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
240 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
241 static LRESULT EDIT_WM_MButtonDown(WND *wnd);
242 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
243 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs);
244 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
245 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
246 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus);
247 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
248 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
249 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
250 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
251 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
252 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
253 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
256 /*********************************************************************
258 * EM_CANUNDO
261 static inline BOOL EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es)
263 return (es->undo_insert_count || strlen(es->undo_text));
267 /*********************************************************************
269 * EM_EMPTYUNDOBUFFER
272 static inline void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es)
274 es->undo_insert_count = 0;
275 *es->undo_text = '\0';
279 /*********************************************************************
281 * WM_CLEAR
284 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
286 EDIT_EM_ReplaceSel(wnd, es, TRUE, "", TRUE);
288 if (es->flags & EF_UPDATE) {
289 es->flags &= ~EF_UPDATE;
290 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
295 /*********************************************************************
297 * WM_CUT
300 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
302 EDIT_WM_Copy(wnd, es);
303 EDIT_WM_Clear(wnd, es);
307 /**********************************************************************
308 * get_app_version
310 * Returns the window version in case Wine emulates a later version
311 * of windows then the application expects.
313 * In a number of cases when windows runs an application that was
314 * designed for an earlier windows version, windows reverts
315 * to "old" behaviour of that earlier version.
317 * An example is a disabled edit control that needs to be painted.
318 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
319 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
320 * applications with an expected version 0f 4.0 or higher.
323 static DWORD get_app_version(void)
325 static DWORD version;
326 if (!version)
328 DWORD dwEmulatedVersion;
329 OSVERSIONINFOA info;
330 DWORD dwProcVersion = GetProcessVersion(0);
332 GetVersionExA( &info );
333 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
334 /* fixme: this may not be 100% correct; see discussion on the
335 * wine developer list in Nov 1999 */
336 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
338 return version;
342 /*********************************************************************
344 * EditWndProc()
346 * The messages are in the order of the actual integer values
347 * (which can be found in include/windows.h)
348 * Whereever possible the 16 bit versions are converted to
349 * the 32 bit ones, so that we can 'fall through' to the
350 * helper functions. These are mostly 32 bit (with a few
351 * exceptions, clearly indicated by a '16' extension to their
352 * names).
355 LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
356 WPARAM wParam, LPARAM lParam )
358 WND *wnd = WIN_FindWndPtr(hwnd);
359 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
360 LRESULT result = 0;
362 switch (msg) {
363 case WM_DESTROY:
364 DPRINTF_EDIT_MSG32("WM_DESTROY");
365 EDIT_WM_Destroy(wnd, es);
366 result = 0;
367 goto END;
369 case WM_NCCREATE:
370 DPRINTF_EDIT_MSG32("WM_NCCREATE");
371 result = EDIT_WM_NCCreate(wnd, (LPCREATESTRUCTA)lParam);
372 goto END;
375 if (!es)
377 result = DefWindowProcA(hwnd, msg, wParam, lParam);
378 goto END;
382 EDIT_LockBuffer(wnd, es);
383 switch (msg) {
384 case EM_GETSEL16:
385 DPRINTF_EDIT_MSG16("EM_GETSEL");
386 wParam = 0;
387 lParam = 0;
388 /* fall through */
389 case EM_GETSEL:
390 DPRINTF_EDIT_MSG32("EM_GETSEL");
391 result = EDIT_EM_GetSel(wnd, es, (LPUINT)wParam, (LPUINT)lParam);
392 break;
394 case EM_SETSEL16:
395 DPRINTF_EDIT_MSG16("EM_SETSEL");
396 if (SLOWORD(lParam) == -1)
397 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
398 else
399 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
400 if (!wParam)
401 EDIT_EM_ScrollCaret(wnd, es);
402 result = 1;
403 break;
404 case EM_SETSEL:
405 DPRINTF_EDIT_MSG32("EM_SETSEL");
406 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
407 EDIT_EM_ScrollCaret(wnd, es);
408 result = 1;
409 break;
411 case EM_GETRECT16:
412 DPRINTF_EDIT_MSG16("EM_GETRECT");
413 if (lParam)
414 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
415 break;
416 case EM_GETRECT:
417 DPRINTF_EDIT_MSG32("EM_GETRECT");
418 if (lParam)
419 CopyRect((LPRECT)lParam, &es->format_rect);
420 break;
422 case EM_SETRECT16:
423 DPRINTF_EDIT_MSG16("EM_SETRECT");
424 if ((es->style & ES_MULTILINE) && lParam) {
425 RECT rc;
426 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
427 EDIT_SetRectNP(wnd, es, &rc);
428 EDIT_UpdateText(wnd, NULL, TRUE);
430 break;
431 case EM_SETRECT:
432 DPRINTF_EDIT_MSG32("EM_SETRECT");
433 if ((es->style & ES_MULTILINE) && lParam) {
434 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
435 EDIT_UpdateText(wnd, NULL, TRUE);
437 break;
439 case EM_SETRECTNP16:
440 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
441 if ((es->style & ES_MULTILINE) && lParam) {
442 RECT rc;
443 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
444 EDIT_SetRectNP(wnd, es, &rc);
446 break;
447 case EM_SETRECTNP:
448 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
449 if ((es->style & ES_MULTILINE) && lParam)
450 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
451 break;
453 case EM_SCROLL16:
454 DPRINTF_EDIT_MSG16("EM_SCROLL");
455 /* fall through */
456 case EM_SCROLL:
457 DPRINTF_EDIT_MSG32("EM_SCROLL");
458 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
459 break;
461 case EM_LINESCROLL16:
462 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
463 wParam = (WPARAM)(INT)SHIWORD(lParam);
464 lParam = (LPARAM)(INT)SLOWORD(lParam);
465 /* fall through */
466 case EM_LINESCROLL:
467 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
468 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
469 break;
471 case EM_SCROLLCARET16:
472 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
473 /* fall through */
474 case EM_SCROLLCARET:
475 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
476 EDIT_EM_ScrollCaret(wnd, es);
477 result = 1;
478 break;
480 case EM_GETMODIFY16:
481 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
482 /* fall through */
483 case EM_GETMODIFY:
484 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
485 result = ((es->flags & EF_MODIFIED) != 0);
486 break;
488 case EM_SETMODIFY16:
489 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
490 /* fall through */
491 case EM_SETMODIFY:
492 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
493 if (wParam)
494 es->flags |= EF_MODIFIED;
495 else
496 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
497 break;
499 case EM_GETLINECOUNT16:
500 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
501 /* fall through */
502 case EM_GETLINECOUNT:
503 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
504 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
505 break;
507 case EM_LINEINDEX16:
508 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
509 if ((INT16)wParam == -1)
510 wParam = (WPARAM)-1;
511 /* fall through */
512 case EM_LINEINDEX:
513 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
514 result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT)wParam);
515 break;
517 case EM_SETHANDLE16:
518 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
519 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
520 break;
521 case EM_SETHANDLE:
522 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
523 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
524 break;
526 case EM_GETHANDLE16:
527 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
528 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
529 break;
530 case EM_GETHANDLE:
531 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
532 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
533 break;
535 case EM_GETTHUMB16:
536 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
537 /* fall through */
538 case EM_GETTHUMB:
539 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
540 result = EDIT_EM_GetThumb(wnd, es);
541 break;
543 /* messages 0x00bf and 0x00c0 missing from specs */
545 case WM_USER+15:
546 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
547 /* fall through */
548 case 0x00bf:
549 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
550 result = DefWindowProcA(hwnd, msg, wParam, lParam);
551 break;
553 case WM_USER+16:
554 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
555 /* fall through */
556 case 0x00c0:
557 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
558 result = DefWindowProcA(hwnd, msg, wParam, lParam);
559 break;
561 case EM_LINELENGTH16:
562 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
563 /* fall through */
564 case EM_LINELENGTH:
565 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
566 result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT)wParam);
567 break;
569 case EM_REPLACESEL16:
570 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
571 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
572 /* fall through */
573 case EM_REPLACESEL:
574 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
575 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, (LPCSTR)lParam, TRUE);
576 if (es->flags & EF_UPDATE) {
577 es->flags &= ~EF_UPDATE;
578 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
580 result = 1;
581 break;
583 /* message 0x00c3 missing from specs */
585 case WM_USER+19:
586 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
587 /* fall through */
588 case 0x00c3:
589 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
590 result = DefWindowProcA(hwnd, msg, wParam, lParam);
591 break;
593 case EM_GETLINE16:
594 DPRINTF_EDIT_MSG16("EM_GETLINE");
595 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
596 /* fall through */
597 case EM_GETLINE:
598 DPRINTF_EDIT_MSG32("EM_GETLINE");
599 result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT)wParam, (LPSTR)lParam);
600 break;
602 case EM_LIMITTEXT16:
603 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
604 /* fall through */
605 case EM_SETLIMITTEXT:
606 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
607 EDIT_EM_SetLimitText(wnd, es, (INT)wParam);
608 break;
610 case EM_CANUNDO16:
611 DPRINTF_EDIT_MSG16("EM_CANUNDO");
612 /* fall through */
613 case EM_CANUNDO:
614 DPRINTF_EDIT_MSG32("EM_CANUNDO");
615 result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
616 break;
618 case EM_UNDO16:
619 DPRINTF_EDIT_MSG16("EM_UNDO");
620 /* fall through */
621 case EM_UNDO:
622 /* fall through */
623 case WM_UNDO:
624 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
625 result = (LRESULT)EDIT_EM_Undo(wnd, es);
626 break;
628 case EM_FMTLINES16:
629 DPRINTF_EDIT_MSG16("EM_FMTLINES");
630 /* fall through */
631 case EM_FMTLINES:
632 DPRINTF_EDIT_MSG32("EM_FMTLINES");
633 result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL)wParam);
634 break;
636 case EM_LINEFROMCHAR16:
637 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
638 /* fall through */
639 case EM_LINEFROMCHAR:
640 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
641 result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT)wParam);
642 break;
644 /* message 0x00ca missing from specs */
646 case WM_USER+26:
647 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
648 /* fall through */
649 case 0x00ca:
650 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
651 result = DefWindowProcA(hwnd, msg, wParam, lParam);
652 break;
654 case EM_SETTABSTOPS16:
655 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
656 result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
657 break;
658 case EM_SETTABSTOPS:
659 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
660 result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT)wParam, (LPINT)lParam);
661 break;
663 case EM_SETPASSWORDCHAR16:
664 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
665 /* fall through */
666 case EM_SETPASSWORDCHAR:
667 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
668 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
669 break;
671 case EM_EMPTYUNDOBUFFER16:
672 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
673 /* fall through */
674 case EM_EMPTYUNDOBUFFER:
675 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
676 EDIT_EM_EmptyUndoBuffer(wnd, es);
677 break;
679 case EM_GETFIRSTVISIBLELINE16:
680 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
681 result = es->y_offset;
682 break;
683 case EM_GETFIRSTVISIBLELINE:
684 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
685 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
686 break;
688 case EM_SETREADONLY16:
689 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
690 /* fall through */
691 case EM_SETREADONLY:
692 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
693 if (wParam) {
694 wnd->dwStyle |= ES_READONLY;
695 es->style |= ES_READONLY;
696 } else {
697 wnd->dwStyle &= ~ES_READONLY;
698 es->style &= ~ES_READONLY;
700 result = 1;
701 break;
703 case EM_SETWORDBREAKPROC16:
704 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
705 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
706 break;
707 case EM_SETWORDBREAKPROC:
708 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
709 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROCA)lParam);
710 break;
712 case EM_GETWORDBREAKPROC16:
713 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
714 result = (LRESULT)es->word_break_proc16;
715 break;
716 case EM_GETWORDBREAKPROC:
717 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
718 result = (LRESULT)es->word_break_proc32A;
719 break;
721 case EM_GETPASSWORDCHAR16:
722 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
723 /* fall through */
724 case EM_GETPASSWORDCHAR:
725 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
726 result = es->password_char;
727 break;
729 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
731 case EM_SETMARGINS:
732 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
733 EDIT_EM_SetMargins(wnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
734 break;
736 case EM_GETMARGINS:
737 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
738 result = MAKELONG(es->left_margin, es->right_margin);
739 break;
741 case EM_GETLIMITTEXT:
742 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
743 result = es->buffer_limit;
744 break;
746 case EM_POSFROMCHAR:
747 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
748 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
749 break;
751 case EM_CHARFROMPOS:
752 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
753 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
754 break;
756 case WM_GETDLGCODE:
757 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
758 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
760 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
762 int vk = (int)((LPMSG)lParam)->wParam;
764 if ((wnd->dwStyle & ES_WANTRETURN) && vk == VK_RETURN)
766 result |= DLGC_WANTMESSAGE;
768 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
770 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
771 result |= DLGC_WANTMESSAGE;
774 break;
776 case WM_CHAR:
777 DPRINTF_EDIT_MSG32("WM_CHAR");
778 if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) && es->hwndListBox)
780 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
781 SendMessageA(wnd->parent->hwndSelf, WM_KEYDOWN, wParam, 0);
782 break;
784 EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
785 break;
787 case WM_CLEAR:
788 DPRINTF_EDIT_MSG32("WM_CLEAR");
789 EDIT_WM_Clear(wnd, es);
790 break;
792 case WM_COMMAND:
793 DPRINTF_EDIT_MSG32("WM_COMMAND");
794 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
795 break;
797 case WM_CONTEXTMENU:
798 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
799 EDIT_WM_ContextMenu(wnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
800 break;
802 case WM_COPY:
803 DPRINTF_EDIT_MSG32("WM_COPY");
804 EDIT_WM_Copy(wnd, es);
805 break;
807 case WM_CREATE:
808 DPRINTF_EDIT_MSG32("WM_CREATE");
809 result = EDIT_WM_Create(wnd, es, (LPCREATESTRUCTA)lParam);
810 break;
812 case WM_CUT:
813 DPRINTF_EDIT_MSG32("WM_CUT");
814 EDIT_WM_Cut(wnd, es);
815 break;
817 case WM_ENABLE:
818 DPRINTF_EDIT_MSG32("WM_ENABLE");
819 es->bEnableState = (BOOL) wParam;
820 EDIT_UpdateText(wnd, NULL, TRUE);
821 break;
823 case WM_ERASEBKGND:
824 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
825 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
826 break;
828 case WM_GETFONT:
829 DPRINTF_EDIT_MSG32("WM_GETFONT");
830 result = (LRESULT)es->font;
831 break;
833 case WM_GETTEXT:
834 DPRINTF_EDIT_MSG32("WM_GETTEXT");
835 result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT)wParam, (LPSTR)lParam);
836 break;
838 case WM_GETTEXTLENGTH:
839 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
840 result = strlen(es->text);
841 break;
843 case WM_HSCROLL:
844 DPRINTF_EDIT_MSG32("WM_HSCROLL");
845 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
846 break;
848 case WM_KEYDOWN:
849 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
850 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
851 break;
853 case WM_KILLFOCUS:
854 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
855 result = EDIT_WM_KillFocus(wnd, es, (HWND)wParam);
856 break;
858 case WM_LBUTTONDBLCLK:
859 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
860 result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
861 break;
863 case WM_LBUTTONDOWN:
864 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
865 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
866 break;
868 case WM_LBUTTONUP:
869 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
870 result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
871 break;
873 case WM_MBUTTONDOWN:
874 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
875 result = EDIT_WM_MButtonDown(wnd);
876 break;
878 case WM_MOUSEACTIVATE:
880 * FIXME: maybe DefWindowProc() screws up, but it seems that
881 * modeless dialog boxes need this. If we don't do this, the focus
882 * will _not_ be set by DefWindowProc() for edit controls in a
883 * modeless dialog box ???
885 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
886 SetFocus(wnd->hwndSelf);
887 result = MA_ACTIVATE;
888 break;
890 case WM_MOUSEMOVE:
892 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
894 result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
895 break;
897 case WM_PAINT:
898 DPRINTF_EDIT_MSG32("WM_PAINT");
899 EDIT_WM_Paint(wnd, es, wParam);
900 break;
902 case WM_PASTE:
903 DPRINTF_EDIT_MSG32("WM_PASTE");
904 EDIT_WM_Paste(wnd, es);
905 break;
907 case WM_SETFOCUS:
908 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
909 EDIT_WM_SetFocus(wnd, es, (HWND)wParam);
910 break;
912 case WM_SETFONT:
913 DPRINTF_EDIT_MSG32("WM_SETFONT");
914 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
915 break;
917 case WM_SETTEXT:
918 DPRINTF_EDIT_MSG32("WM_SETTEXT");
919 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
920 result = TRUE;
921 break;
923 case WM_SIZE:
924 DPRINTF_EDIT_MSG32("WM_SIZE");
925 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
926 break;
928 case WM_SYSKEYDOWN:
929 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
930 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
931 break;
933 case WM_TIMER:
934 DPRINTF_EDIT_MSG32("WM_TIMER");
935 EDIT_WM_Timer(wnd, es, (INT)wParam, (TIMERPROC)lParam);
936 break;
938 case WM_VSCROLL:
939 DPRINTF_EDIT_MSG32("WM_VSCROLL");
940 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
941 break;
943 case WM_MOUSEWHEEL:
945 short gcWheelDelta = 0;
946 UINT pulScrollLines = 3;
947 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
949 if (wParam & (MK_SHIFT | MK_CONTROL)) {
950 result = DefWindowProcA(hwnd, msg, wParam, lParam);
951 break;
953 gcWheelDelta -= (short) HIWORD(wParam);
954 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
956 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
957 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
958 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
961 break;
962 default:
963 result = DefWindowProcA(hwnd, msg, wParam, lParam);
964 break;
966 EDIT_UnlockBuffer(wnd, es, FALSE);
967 END:
968 WIN_ReleaseWndPtr(wnd);
969 return result;
974 /*********************************************************************
976 * EDIT_BuildLineDefs_ML
978 * Build linked list of text lines.
979 * Lines can end with '\0' (last line), a character (if it is wrapped),
980 * a soft return '\r\r\n' or a hard return '\r\n'
983 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
985 HDC dc;
986 HFONT old_font = 0;
987 LPSTR start, cp;
988 INT fw;
989 LINEDEF *current_def;
990 LINEDEF **previous_next;
992 current_def = es->first_line_def;
993 do {
994 LINEDEF *next_def = current_def->next;
995 HeapFree(es->heap, 0, current_def);
996 current_def = next_def;
997 } while (current_def);
998 es->line_count = 0;
999 es->text_width = 0;
1001 dc = GetDC(wnd->hwndSelf);
1002 if (es->font)
1003 old_font = SelectObject(dc, es->font);
1005 fw = es->format_rect.right - es->format_rect.left;
1006 start = es->text;
1007 previous_next = &es->first_line_def;
1008 do {
1009 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
1010 current_def->next = NULL;
1011 cp = start;
1012 while (*cp) {
1013 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1014 break;
1015 cp++;
1017 if (!(*cp)) {
1018 current_def->ending = END_0;
1019 current_def->net_length = strlen(start);
1020 } else if ((cp > start) && (*(cp - 1) == '\r')) {
1021 current_def->ending = END_SOFT;
1022 current_def->net_length = cp - start - 1;
1023 } else {
1024 current_def->ending = END_HARD;
1025 current_def->net_length = cp - start;
1027 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1028 start, current_def->net_length,
1029 es->tabs_count, es->tabs));
1030 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1031 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
1032 INT next = 0;
1033 INT prev;
1034 do {
1035 prev = next;
1036 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
1037 prev + 1, current_def->net_length, WB_RIGHT);
1038 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1039 start, next, es->tabs_count, es->tabs));
1040 } while (current_def->width <= fw);
1041 if (!prev) {
1042 next = 0;
1043 do {
1044 prev = next;
1045 next++;
1046 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1047 start, next, es->tabs_count, es->tabs));
1048 } while (current_def->width <= fw);
1049 if (!prev)
1050 prev = 1;
1052 current_def->net_length = prev;
1053 current_def->ending = END_WRAP;
1054 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc, start,
1055 current_def->net_length, es->tabs_count, es->tabs));
1057 switch (current_def->ending) {
1058 case END_SOFT:
1059 current_def->length = current_def->net_length + 3;
1060 break;
1061 case END_HARD:
1062 current_def->length = current_def->net_length + 2;
1063 break;
1064 case END_WRAP:
1065 case END_0:
1066 current_def->length = current_def->net_length;
1067 break;
1069 es->text_width = max(es->text_width, current_def->width);
1070 start += current_def->length;
1071 *previous_next = current_def;
1072 previous_next = &current_def->next;
1073 es->line_count++;
1074 } while (current_def->ending != END_0);
1075 if (es->font)
1076 SelectObject(dc, old_font);
1077 ReleaseDC(wnd->hwndSelf, dc);
1081 /*********************************************************************
1083 * EDIT_CallWordBreakProc
1085 * Call appropriate WordBreakProc (internal or external).
1087 * Note: The "start" argument should always be an index refering
1088 * to es->text. The actual wordbreak proc might be
1089 * 16 bit, so we can't always pass any 32 bit LPSTR.
1090 * Hence we assume that es->text is the buffer that holds
1091 * the string under examination (we can decide this for ourselves).
1094 /* ### start build ### */
1095 extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1096 /* ### stop build ### */
1097 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
1099 if (es->word_break_proc16) {
1100 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
1101 SEGPTR segptr = LocalLock16(hloc16);
1102 INT ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1103 segptr + start, index, count, action);
1104 LocalUnlock16(hloc16);
1105 return ret;
1107 else if (es->word_break_proc32A)
1109 TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
1110 es->word_break_proc32A, es->text + start, index,
1111 count, action );
1112 return (INT)es->word_break_proc32A( es->text + start, index,
1113 count, action );
1115 else
1116 return EDIT_WordBreakProc(es->text + start, index, count, action);
1120 /*********************************************************************
1122 * EDIT_CharFromPos
1124 * Beware: This is not the function called on EM_CHARFROMPOS
1125 * The position _can_ be outside the formatting / client
1126 * rectangle
1127 * The return value is only the character index
1130 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1132 INT index;
1133 HDC dc;
1134 HFONT old_font = 0;
1136 if (es->style & ES_MULTILINE) {
1137 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1138 INT line_index = 0;
1139 LINEDEF *line_def = es->first_line_def;
1140 INT low, high;
1141 while ((line > 0) && line_def->next) {
1142 line_index += line_def->length;
1143 line_def = line_def->next;
1144 line--;
1146 x += es->x_offset - es->format_rect.left;
1147 if (x >= line_def->width) {
1148 if (after_wrap)
1149 *after_wrap = (line_def->ending == END_WRAP);
1150 return line_index + line_def->net_length;
1152 if (x <= 0) {
1153 if (after_wrap)
1154 *after_wrap = FALSE;
1155 return line_index;
1157 dc = GetDC(wnd->hwndSelf);
1158 if (es->font)
1159 old_font = SelectObject(dc, es->font);
1160 low = line_index + 1;
1161 high = line_index + line_def->net_length + 1;
1162 while (low < high - 1)
1164 INT mid = (low + high) / 2;
1165 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1166 else low = mid;
1168 index = low;
1170 if (after_wrap)
1171 *after_wrap = ((index == line_index + line_def->net_length) &&
1172 (line_def->ending == END_WRAP));
1173 } else {
1174 LPSTR text;
1175 SIZE size;
1176 if (after_wrap)
1177 *after_wrap = FALSE;
1178 x -= es->format_rect.left;
1179 if (!x)
1180 return es->x_offset;
1181 text = EDIT_GetPasswordPointer_SL(wnd, es);
1182 dc = GetDC(wnd->hwndSelf);
1183 if (es->font)
1184 old_font = SelectObject(dc, es->font);
1185 if (x < 0)
1187 INT low = 0;
1188 INT high = es->x_offset;
1189 while (low < high - 1)
1191 INT mid = (low + high) / 2;
1192 GetTextExtentPoint32A( dc, text + mid,
1193 es->x_offset - mid, &size );
1194 if (size.cx > -x) low = mid;
1195 else high = mid;
1197 index = low;
1199 else
1201 INT low = es->x_offset;
1202 INT high = strlen(es->text) + 1;
1203 while (low < high - 1)
1205 INT mid = (low + high) / 2;
1206 GetTextExtentPoint32A( dc, text + es->x_offset,
1207 mid - es->x_offset, &size );
1208 if (size.cx > x) high = mid;
1209 else low = mid;
1211 index = low;
1213 if (es->style & ES_PASSWORD)
1214 HeapFree(es->heap, 0 ,text);
1216 if (es->font)
1217 SelectObject(dc, old_font);
1218 ReleaseDC(wnd->hwndSelf, dc);
1219 return index;
1223 /*********************************************************************
1225 * EDIT_ConfinePoint
1227 * adjusts the point to be within the formatting rectangle
1228 * (so CharFromPos returns the nearest _visible_ character)
1231 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT x, LPINT y)
1233 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1234 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1238 /*********************************************************************
1240 * EDIT_GetLineRect
1242 * Calculates the bounding rectangle for a line from a starting
1243 * column to an ending column.
1246 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1248 INT line_index = EDIT_EM_LineIndex(wnd, es, line);
1250 if (es->style & ES_MULTILINE)
1251 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1252 else
1253 rc->top = es->format_rect.top;
1254 rc->bottom = rc->top + es->line_height;
1255 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1256 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1260 /*********************************************************************
1262 * EDIT_GetPasswordPointer_SL
1264 * note: caller should free the (optionally) allocated buffer
1267 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
1269 if (es->style & ES_PASSWORD) {
1270 INT len = strlen(es->text);
1271 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1272 RtlFillMemory(text, len, es->password_char);
1273 text[len] = '\0';
1274 return text;
1275 } else
1276 return es->text;
1280 /*********************************************************************
1282 * EDIT_LockBuffer
1284 * This acts as a LOCAL_Lock(), but it locks only once. This way
1285 * you can call it whenever you like, without unlocking.
1288 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1290 if (!es) {
1291 ERR("no EDITSTATE ... please report\n");
1292 return;
1294 if (!(es->style & ES_MULTILINE))
1295 return;
1296 if (!es->text) {
1297 if (es->hloc32)
1298 es->text = LocalLock(es->hloc32);
1299 else if (es->hloc16)
1300 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1301 else {
1302 ERR("no buffer ... please report\n");
1303 return;
1306 es->lock_count++;
1310 /*********************************************************************
1312 * EDIT_SL_InvalidateText
1314 * Called from EDIT_InvalidateText().
1315 * Does the job for single-line controls only.
1318 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1320 RECT line_rect;
1321 RECT rc;
1323 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1324 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1325 EDIT_UpdateText(wnd, &rc, FALSE);
1329 /*********************************************************************
1331 * EDIT_ML_InvalidateText
1333 * Called from EDIT_InvalidateText().
1334 * Does the job for multi-line controls only.
1337 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1339 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1340 INT sl = EDIT_EM_LineFromChar(wnd, es, start);
1341 INT el = EDIT_EM_LineFromChar(wnd, es, end);
1342 INT sc;
1343 INT ec;
1344 RECT rc1;
1345 RECT rcWnd;
1346 RECT rcLine;
1347 RECT rcUpdate;
1348 INT l;
1350 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1351 return;
1353 sc = start - EDIT_EM_LineIndex(wnd, es, sl);
1354 ec = end - EDIT_EM_LineIndex(wnd, es, el);
1355 if (sl < es->y_offset) {
1356 sl = es->y_offset;
1357 sc = 0;
1359 if (el > es->y_offset + vlc) {
1360 el = es->y_offset + vlc;
1361 ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
1363 GetClientRect(wnd->hwndSelf, &rc1);
1364 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1365 if (sl == el) {
1366 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1367 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1368 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1369 } else {
1370 EDIT_GetLineRect(wnd, es, sl, sc,
1371 EDIT_EM_LineLength(wnd, es,
1372 EDIT_EM_LineIndex(wnd, es, sl)),
1373 &rcLine);
1374 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1375 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1376 for (l = sl + 1 ; l < el ; l++) {
1377 EDIT_GetLineRect(wnd, es, l, 0,
1378 EDIT_EM_LineLength(wnd, es,
1379 EDIT_EM_LineIndex(wnd, es, l)),
1380 &rcLine);
1381 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1382 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1384 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1385 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1386 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1391 /*********************************************************************
1393 * EDIT_InvalidateText
1395 * Invalidate the text from offset start upto, but not including,
1396 * offset end. Useful for (re)painting the selection.
1397 * Regions outside the linewidth are not invalidated.
1398 * end == -1 means end == TextLength.
1399 * start and end need not be ordered.
1402 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1404 if (end == start)
1405 return;
1407 if (end == -1)
1408 end = strlen(es->text);
1410 ORDER_INT(start, end);
1412 if (es->style & ES_MULTILINE)
1413 EDIT_ML_InvalidateText(wnd, es, start, end);
1414 else
1415 EDIT_SL_InvalidateText(wnd, es, start, end);
1419 /*********************************************************************
1421 * EDIT_MakeFit
1423 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1426 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT size)
1428 HLOCAL hNew32;
1429 HLOCAL16 hNew16;
1431 if (size <= es->buffer_size)
1432 return TRUE;
1433 if (size > es->buffer_limit) {
1434 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1435 return FALSE;
1437 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1438 if (size > es->buffer_limit)
1439 size = es->buffer_limit;
1441 TRACE("trying to ReAlloc to %d+1\n", size);
1443 EDIT_UnlockBuffer(wnd, es, TRUE);
1444 if (es->text) {
1445 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1446 es->buffer_size = min(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1447 else
1448 es->buffer_size = 0;
1449 } else if (es->hloc32) {
1450 if ((hNew32 = LocalReAlloc(es->hloc32, size + 1, 0))) {
1451 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1452 es->hloc32 = hNew32;
1453 es->buffer_size = min(LocalSize(es->hloc32) - 1, es->buffer_limit);
1455 } else if (es->hloc16) {
1456 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1457 TRACE("Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1458 es->hloc16 = hNew16;
1459 es->buffer_size = min(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1462 if (es->buffer_size < size) {
1463 EDIT_LockBuffer(wnd, es);
1464 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1465 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1466 return FALSE;
1467 } else {
1468 EDIT_LockBuffer(wnd, es);
1469 TRACE("We now have %d+1\n", es->buffer_size);
1470 return TRUE;
1475 /*********************************************************************
1477 * EDIT_MakeUndoFit
1479 * Try to fit size + 1 bytes in the undo buffer.
1482 static BOOL EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT size)
1484 if (size <= es->undo_buffer_size)
1485 return TRUE;
1486 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1488 TRACE("trying to ReAlloc to %d+1\n", size);
1490 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1491 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1492 if (es->undo_buffer_size < size) {
1493 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1494 return FALSE;
1496 return TRUE;
1498 return FALSE;
1502 /*********************************************************************
1504 * EDIT_MoveBackward
1507 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1509 INT e = es->selection_end;
1511 if (e) {
1512 e--;
1513 if ((es->style & ES_MULTILINE) && e &&
1514 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1515 e--;
1516 if (e && (es->text[e - 1] == '\r'))
1517 e--;
1520 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1521 EDIT_EM_ScrollCaret(wnd, es);
1525 /*********************************************************************
1527 * EDIT_MoveDown_ML
1529 * Only for multi line controls
1530 * Move the caret one line down, on a column with the nearest
1531 * x coordinate on the screen (might be a different column).
1534 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1536 INT s = es->selection_start;
1537 INT e = es->selection_end;
1538 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1539 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1540 INT x = SLOWORD(pos);
1541 INT y = SHIWORD(pos);
1543 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1544 if (!extend)
1545 s = e;
1546 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1547 EDIT_EM_ScrollCaret(wnd, es);
1551 /*********************************************************************
1553 * EDIT_MoveEnd
1556 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1558 BOOL after_wrap = FALSE;
1559 INT e;
1561 /* Pass a high value in x to make sure of receiving the en of the line */
1562 if (es->style & ES_MULTILINE)
1563 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1564 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1565 else
1566 e = strlen(es->text);
1567 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1568 EDIT_EM_ScrollCaret(wnd, es);
1572 /*********************************************************************
1574 * EDIT_MoveForward
1577 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1579 INT e = es->selection_end;
1581 if (es->text[e]) {
1582 e++;
1583 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1584 if (es->text[e] == '\n')
1585 e++;
1586 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1587 e += 2;
1590 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1591 EDIT_EM_ScrollCaret(wnd, es);
1595 /*********************************************************************
1597 * EDIT_MoveHome
1599 * Home key: move to beginning of line.
1602 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1604 INT e;
1606 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1607 if (es->style & ES_MULTILINE)
1608 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1609 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1610 else
1611 e = 0;
1612 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1613 EDIT_EM_ScrollCaret(wnd, es);
1617 /*********************************************************************
1619 * EDIT_MovePageDown_ML
1621 * Only for multi line controls
1622 * Move the caret one page down, on a column with the nearest
1623 * x coordinate on the screen (might be a different column).
1626 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1628 INT s = es->selection_start;
1629 INT e = es->selection_end;
1630 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1631 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1632 INT x = SLOWORD(pos);
1633 INT y = SHIWORD(pos);
1635 e = EDIT_CharFromPos(wnd, es, x,
1636 y + (es->format_rect.bottom - es->format_rect.top),
1637 &after_wrap);
1638 if (!extend)
1639 s = e;
1640 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1641 EDIT_EM_ScrollCaret(wnd, es);
1645 /*********************************************************************
1647 * EDIT_MovePageUp_ML
1649 * Only for multi line controls
1650 * Move the caret one page up, on a column with the nearest
1651 * x coordinate on the screen (might be a different column).
1654 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1656 INT s = es->selection_start;
1657 INT e = es->selection_end;
1658 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1659 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1660 INT x = SLOWORD(pos);
1661 INT y = SHIWORD(pos);
1663 e = EDIT_CharFromPos(wnd, es, x,
1664 y - (es->format_rect.bottom - es->format_rect.top),
1665 &after_wrap);
1666 if (!extend)
1667 s = e;
1668 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1669 EDIT_EM_ScrollCaret(wnd, es);
1673 /*********************************************************************
1675 * EDIT_MoveUp_ML
1677 * Only for multi line controls
1678 * Move the caret one line up, on a column with the nearest
1679 * x coordinate on the screen (might be a different column).
1682 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1684 INT s = es->selection_start;
1685 INT e = es->selection_end;
1686 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1687 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1688 INT x = SLOWORD(pos);
1689 INT y = SHIWORD(pos);
1691 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1692 if (!extend)
1693 s = e;
1694 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1695 EDIT_EM_ScrollCaret(wnd, es);
1699 /*********************************************************************
1701 * EDIT_MoveWordBackward
1704 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1706 INT s = es->selection_start;
1707 INT e = es->selection_end;
1708 INT l;
1709 INT ll;
1710 INT li;
1712 l = EDIT_EM_LineFromChar(wnd, es, e);
1713 ll = EDIT_EM_LineLength(wnd, es, e);
1714 li = EDIT_EM_LineIndex(wnd, es, l);
1715 if (e - li == 0) {
1716 if (l) {
1717 li = EDIT_EM_LineIndex(wnd, es, l - 1);
1718 e = li + EDIT_EM_LineLength(wnd, es, li);
1720 } else {
1721 e = li + (INT)EDIT_CallWordBreakProc(wnd, es,
1722 li, e - li, ll, WB_LEFT);
1724 if (!extend)
1725 s = e;
1726 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1727 EDIT_EM_ScrollCaret(wnd, es);
1731 /*********************************************************************
1733 * EDIT_MoveWordForward
1736 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
1738 INT s = es->selection_start;
1739 INT e = es->selection_end;
1740 INT l;
1741 INT ll;
1742 INT li;
1744 l = EDIT_EM_LineFromChar(wnd, es, e);
1745 ll = EDIT_EM_LineLength(wnd, es, e);
1746 li = EDIT_EM_LineIndex(wnd, es, l);
1747 if (e - li == ll) {
1748 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1749 e = EDIT_EM_LineIndex(wnd, es, l + 1);
1750 } else {
1751 e = li + EDIT_CallWordBreakProc(wnd, es,
1752 li, e - li + 1, ll, WB_RIGHT);
1754 if (!extend)
1755 s = e;
1756 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1757 EDIT_EM_ScrollCaret(wnd, es);
1761 /*********************************************************************
1763 * EDIT_PaintLine
1766 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1768 INT s = es->selection_start;
1769 INT e = es->selection_end;
1770 INT li;
1771 INT ll;
1772 INT x;
1773 INT y;
1774 LRESULT pos;
1776 if (es->style & ES_MULTILINE) {
1777 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1778 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1779 return;
1780 } else if (line)
1781 return;
1783 TRACE("line=%d\n", line);
1785 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
1786 x = SLOWORD(pos);
1787 y = SHIWORD(pos);
1788 li = EDIT_EM_LineIndex(wnd, es, line);
1789 ll = EDIT_EM_LineLength(wnd, es, li);
1790 s = es->selection_start;
1791 e = es->selection_end;
1792 ORDER_INT(s, e);
1793 s = min(li + ll, max(li, s));
1794 e = min(li + ll, max(li, e));
1795 if (rev && (s != e) &&
1796 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1797 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
1798 x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
1799 x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
1800 } else
1801 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
1805 /*********************************************************************
1807 * EDIT_PaintText
1810 static INT EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1812 COLORREF BkColor;
1813 COLORREF TextColor;
1814 INT ret;
1815 INT li;
1816 SIZE size;
1818 if (!count)
1819 return 0;
1820 BkColor = GetBkColor(dc);
1821 TextColor = GetTextColor(dc);
1822 if (rev) {
1823 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1824 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1826 li = EDIT_EM_LineIndex(wnd, es, line);
1827 if (es->style & ES_MULTILINE) {
1828 ret = (INT)LOWORD(TabbedTextOutA(dc, x, y, es->text + li + col, count,
1829 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1830 } else {
1831 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
1832 TextOutA(dc, x, y, text + li + col, count);
1833 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1834 ret = size.cx;
1835 if (es->style & ES_PASSWORD)
1836 HeapFree(es->heap, 0, text);
1838 if (rev) {
1839 SetBkColor(dc, BkColor);
1840 SetTextColor(dc, TextColor);
1842 return ret;
1846 /*********************************************************************
1848 * EDIT_SetCaretPos
1851 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
1852 BOOL after_wrap)
1854 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
1855 INT x = SLOWORD(res);
1856 INT y = SHIWORD(res);
1858 if(x < es->format_rect.left)
1859 x = es->format_rect.left;
1860 if(x > es->format_rect.right - 2)
1861 x = es->format_rect.right - 2;
1862 if(y > es->format_rect.bottom)
1863 y = es->format_rect.bottom;
1864 if(y < es->format_rect.top)
1865 y = es->format_rect.top;
1866 SetCaretPos(x, y);
1867 return;
1871 /*********************************************************************
1873 * EDIT_SetRectNP
1875 * note: this is not (exactly) the handler called on EM_SETRECTNP
1876 * it is also used to set the rect of a single line control
1879 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
1881 CopyRect(&es->format_rect, rc);
1882 if (es->style & WS_BORDER) {
1883 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
1884 if(TWEAK_WineLook == WIN31_LOOK)
1885 bw += 2;
1886 es->format_rect.left += bw;
1887 es->format_rect.top += bw;
1888 es->format_rect.right -= bw;
1889 es->format_rect.bottom -= bw;
1891 es->format_rect.left += es->left_margin;
1892 es->format_rect.right -= es->right_margin;
1893 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
1894 if (es->style & ES_MULTILINE)
1895 es->format_rect.bottom = es->format_rect.top +
1896 max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1897 else
1898 es->format_rect.bottom = es->format_rect.top + es->line_height;
1899 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1900 EDIT_BuildLineDefs_ML(wnd, es);
1904 /*********************************************************************
1906 * EDIT_UnlockBuffer
1909 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
1911 if (!es) {
1912 ERR("no EDITSTATE ... please report\n");
1913 return;
1915 if (!(es->style & ES_MULTILINE))
1916 return;
1917 if (!es->lock_count) {
1918 ERR("lock_count == 0 ... please report\n");
1919 return;
1921 if (!es->text) {
1922 ERR("es->text == 0 ... please report\n");
1923 return;
1925 if (force || (es->lock_count == 1)) {
1926 if (es->hloc32) {
1927 LocalUnlock(es->hloc32);
1928 es->text = NULL;
1929 } else if (es->hloc16) {
1930 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1931 es->text = NULL;
1934 es->lock_count--;
1938 /*********************************************************************
1940 * EDIT_WordBreakProc
1942 * Find the beginning of words.
1943 * Note: unlike the specs for a WordBreakProc, this function only
1944 * allows to be called without linebreaks between s[0] upto
1945 * s[count - 1]. Remember it is only called
1946 * internally, so we can decide this for ourselves.
1949 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1951 INT ret = 0;
1953 TRACE("s=%p, index=%u, count=%u, action=%d\n",
1954 s, index, count, action);
1956 switch (action) {
1957 case WB_LEFT:
1958 if (!count)
1959 break;
1960 if (index)
1961 index--;
1962 if (s[index] == ' ') {
1963 while (index && (s[index] == ' '))
1964 index--;
1965 if (index) {
1966 while (index && (s[index] != ' '))
1967 index--;
1968 if (s[index] == ' ')
1969 index++;
1971 } else {
1972 while (index && (s[index] != ' '))
1973 index--;
1974 if (s[index] == ' ')
1975 index++;
1977 ret = index;
1978 break;
1979 case WB_RIGHT:
1980 if (!count)
1981 break;
1982 if (index)
1983 index--;
1984 if (s[index] == ' ')
1985 while ((index < count) && (s[index] == ' ')) index++;
1986 else {
1987 while (s[index] && (s[index] != ' ') && (index < count))
1988 index++;
1989 while ((s[index] == ' ') && (index < count)) index++;
1991 ret = index;
1992 break;
1993 case WB_ISDELIMITER:
1994 ret = (s[index] == ' ');
1995 break;
1996 default:
1997 ERR("unknown action code, please report !\n");
1998 break;
2000 return ret;
2004 /*********************************************************************
2006 * EM_CHARFROMPOS
2008 * returns line number (not index) in high-order word of result.
2009 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2010 * to Richedit, not to the edit control. Original documentation is valid.
2011 * FIXME: do the specs mean to return -1 if outside client area or
2012 * if outside formatting rectangle ???
2015 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
2017 POINT pt;
2018 RECT rc;
2019 INT index;
2021 pt.x = x;
2022 pt.y = y;
2023 GetClientRect(wnd->hwndSelf, &rc);
2024 if (!PtInRect(&rc, pt))
2025 return -1;
2027 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
2028 return MAKELONG(index, EDIT_EM_LineFromChar(wnd, es, index));
2032 /*********************************************************************
2034 * EM_FMTLINES
2036 * Enable or disable soft breaks.
2038 static BOOL EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL add_eol)
2040 es->flags &= ~EF_USE_SOFTBRK;
2041 if (add_eol) {
2042 es->flags |= EF_USE_SOFTBRK;
2043 FIXME("soft break enabled, not implemented\n");
2045 return add_eol;
2049 /*********************************************************************
2051 * EM_GETHANDLE
2053 * Hopefully this won't fire back at us.
2054 * We always start with a fixed buffer in our own heap.
2055 * However, with this message a 32 bit application requests
2056 * a handle to 32 bit moveable local heap memory, where it expects
2057 * to find the text.
2058 * It's a pity that from this moment on we have to use this
2059 * local heap, because applications may rely on the handle
2060 * in the future.
2062 * In this function we'll try to switch to local heap.
2065 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
2067 HLOCAL newBuf;
2068 LPSTR newText;
2069 INT newSize;
2071 if (!(es->style & ES_MULTILINE))
2072 return 0;
2074 if (es->hloc32)
2075 return es->hloc32;
2076 else if (es->hloc16)
2077 return (HLOCAL)es->hloc16;
2079 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, strlen(es->text) + 1))) {
2080 ERR("could not allocate new 32 bit buffer\n");
2081 return 0;
2083 newSize = min(LocalSize(newBuf) - 1, es->buffer_limit);
2084 if (!(newText = LocalLock(newBuf))) {
2085 ERR("could not lock new 32 bit buffer\n");
2086 LocalFree(newBuf);
2087 return 0;
2089 strcpy(newText, es->text);
2090 EDIT_UnlockBuffer(wnd, es, TRUE);
2091 if (es->text)
2092 HeapFree(es->heap, 0, es->text);
2093 es->hloc32 = newBuf;
2094 es->hloc16 = (HLOCAL16)NULL;
2095 es->buffer_size = newSize;
2096 es->text = newText;
2097 EDIT_LockBuffer(wnd, es);
2098 TRACE("switched to 32 bit local heap\n");
2100 return es->hloc32;
2104 /*********************************************************************
2106 * EM_GETHANDLE16
2108 * Hopefully this won't fire back at us.
2109 * We always start with a buffer in 32 bit linear memory.
2110 * However, with this message a 16 bit application requests
2111 * a handle of 16 bit local heap memory, where it expects to find
2112 * the text.
2113 * It's a pitty that from this moment on we have to use this
2114 * local heap, because applications may rely on the handle
2115 * in the future.
2117 * In this function we'll try to switch to local heap.
2119 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2121 HLOCAL16 newBuf;
2122 LPSTR newText;
2123 INT newSize;
2125 if (!(es->style & ES_MULTILINE))
2126 return 0;
2128 if (es->hloc16)
2129 return es->hloc16;
2131 if (!LOCAL_HeapSize(wnd->hInstance)) {
2132 if (!LocalInit16(wnd->hInstance, 0,
2133 GlobalSize16(wnd->hInstance))) {
2134 ERR("could not initialize local heap\n");
2135 return 0;
2137 TRACE("local heap initialized\n");
2139 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, strlen(es->text) + 1))) {
2140 ERR("could not allocate new 16 bit buffer\n");
2141 return 0;
2143 newSize = min(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2144 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2145 ERR("could not lock new 16 bit buffer\n");
2146 LOCAL_Free(wnd->hInstance, newBuf);
2147 return 0;
2149 strcpy(newText, es->text);
2150 EDIT_UnlockBuffer(wnd, es, TRUE);
2151 if (es->text)
2152 HeapFree(es->heap, 0, es->text);
2153 else if (es->hloc32) {
2154 while (LocalFree(es->hloc32)) ;
2155 LocalFree(es->hloc32);
2157 es->hloc32 = (HLOCAL)NULL;
2158 es->hloc16 = newBuf;
2159 es->buffer_size = newSize;
2160 es->text = newText;
2161 EDIT_LockBuffer(wnd, es);
2162 TRACE("switched to 16 bit buffer\n");
2164 return es->hloc16;
2168 /*********************************************************************
2170 * EM_GETLINE
2173 static INT EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT line, LPSTR lpch)
2175 LPSTR src;
2176 INT len;
2177 INT i;
2179 if (es->style & ES_MULTILINE) {
2180 if (line >= es->line_count)
2181 return 0;
2182 } else
2183 line = 0;
2184 i = EDIT_EM_LineIndex(wnd, es, line);
2185 src = es->text + i;
2186 len = min(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, i));
2187 for (i = 0 ; i < len ; i++) {
2188 *lpch = *src;
2189 src++;
2190 lpch++;
2192 return (LRESULT)len;
2196 /*********************************************************************
2198 * EM_GETSEL
2201 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT start, LPUINT end)
2203 UINT s = es->selection_start;
2204 UINT e = es->selection_end;
2206 ORDER_UINT(s, e);
2207 if (start)
2208 *start = s;
2209 if (end)
2210 *end = e;
2211 return MAKELONG(s, e);
2215 /*********************************************************************
2217 * EM_GETTHUMB
2219 * FIXME: is this right ? (or should it be only VSCROLL)
2220 * (and maybe only for edit controls that really have their
2221 * own scrollbars) (and maybe only for multiline controls ?)
2222 * All in all: very poorly documented
2224 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2225 * WM_VSCROLL handlers
2228 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2230 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
2231 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
2235 /*********************************************************************
2237 * EM_LINEFROMCHAR
2240 static INT EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT index)
2242 INT line;
2243 LINEDEF *line_def;
2245 if (!(es->style & ES_MULTILINE))
2246 return 0;
2247 if (index > strlen(es->text))
2248 return es->line_count - 1;
2249 if (index == -1)
2250 index = min(es->selection_start, es->selection_end);
2252 line = 0;
2253 line_def = es->first_line_def;
2254 index -= line_def->length;
2255 while ((index >= 0) && line_def->next) {
2256 line++;
2257 line_def = line_def->next;
2258 index -= line_def->length;
2260 return line;
2264 /*********************************************************************
2266 * EM_LINEINDEX
2269 static INT EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT line)
2271 INT line_index;
2272 LINEDEF *line_def;
2274 if (!(es->style & ES_MULTILINE))
2275 return 0;
2276 if (line >= es->line_count)
2277 return -1;
2279 line_index = 0;
2280 line_def = es->first_line_def;
2281 if (line == -1) {
2282 INT index = es->selection_end - line_def->length;
2283 while ((index >= 0) && line_def->next) {
2284 line_index += line_def->length;
2285 line_def = line_def->next;
2286 index -= line_def->length;
2288 } else {
2289 while (line > 0) {
2290 line_index += line_def->length;
2291 line_def = line_def->next;
2292 line--;
2295 return line_index;
2299 /*********************************************************************
2301 * EM_LINELENGTH
2304 static INT EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT index)
2306 LINEDEF *line_def;
2308 if (!(es->style & ES_MULTILINE))
2309 return strlen(es->text);
2311 if (index == -1) {
2312 /* get the number of remaining non-selected chars of selected lines */
2313 INT32 li;
2314 INT32 count;
2315 li = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
2316 /* # chars before start of selection area */
2317 count = es->selection_start - EDIT_EM_LineIndex(wnd, es, li);
2318 li = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2319 /* # chars after end of selection */
2320 count += EDIT_EM_LineIndex(wnd, es, li) +
2321 EDIT_EM_LineLength(wnd, es, li) - es->selection_end;
2322 return count;
2324 line_def = es->first_line_def;
2325 index -= line_def->length;
2326 while ((index >= 0) && line_def->next) {
2327 line_def = line_def->next;
2328 index -= line_def->length;
2330 return line_def->net_length;
2334 /*********************************************************************
2336 * EM_LINESCROLL
2338 * FIXME: dx is in average character widths
2339 * However, we assume it is in pixels when we use this
2340 * function internally
2343 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2345 INT nyoff;
2347 if (!(es->style & ES_MULTILINE))
2348 return FALSE;
2350 if (-dx > es->x_offset)
2351 dx = -es->x_offset;
2352 if (dx > es->text_width - es->x_offset)
2353 dx = es->text_width - es->x_offset;
2354 nyoff = max(0, es->y_offset + dy);
2355 if (nyoff >= es->line_count)
2356 nyoff = es->line_count - 1;
2357 dy = (es->y_offset - nyoff) * es->line_height;
2358 if (dx || dy) {
2359 RECT rc1;
2360 RECT rc;
2361 GetClientRect(wnd->hwndSelf, &rc1);
2362 IntersectRect(&rc, &rc1, &es->format_rect);
2363 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2364 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2365 es->y_offset = nyoff;
2366 es->x_offset += dx;
2368 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2369 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2370 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2371 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2372 return TRUE;
2376 /*********************************************************************
2378 * EM_POSFROMCHAR
2381 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2383 INT len = strlen(es->text);
2384 INT l;
2385 INT li;
2386 INT x;
2387 INT y = 0;
2388 HDC dc;
2389 HFONT old_font = 0;
2390 SIZE size;
2392 index = min(index, len);
2393 dc = GetDC(wnd->hwndSelf);
2394 if (es->font)
2395 old_font = SelectObject(dc, es->font);
2396 if (es->style & ES_MULTILINE) {
2397 l = EDIT_EM_LineFromChar(wnd, es, index);
2398 y = (l - es->y_offset) * es->line_height;
2399 li = EDIT_EM_LineIndex(wnd, es, l);
2400 if (after_wrap && (li == index) && l) {
2401 INT l2 = l - 1;
2402 LINEDEF *line_def = es->first_line_def;
2403 while (l2) {
2404 line_def = line_def->next;
2405 l2--;
2407 if (line_def->ending == END_WRAP) {
2408 l--;
2409 y -= es->line_height;
2410 li = EDIT_EM_LineIndex(wnd, es, l);
2413 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2414 es->tabs_count, es->tabs)) - es->x_offset;
2415 } else {
2416 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
2417 if (index < es->x_offset) {
2418 GetTextExtentPoint32A(dc, text + index,
2419 es->x_offset - index, &size);
2420 x = -size.cx;
2421 } else {
2422 GetTextExtentPoint32A(dc, text + es->x_offset,
2423 index - es->x_offset, &size);
2424 x = size.cx;
2426 y = 0;
2427 if (es->style & ES_PASSWORD)
2428 HeapFree(es->heap, 0 ,text);
2430 x += es->format_rect.left;
2431 y += es->format_rect.top;
2432 if (es->font)
2433 SelectObject(dc, old_font);
2434 ReleaseDC(wnd->hwndSelf, dc);
2435 return MAKELONG((INT16)x, (INT16)y);
2439 /*********************************************************************
2441 * EM_REPLACESEL
2443 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2446 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update)
2448 INT strl = strlen(lpsz_replace);
2449 INT tl = strlen(es->text);
2450 INT utl;
2451 UINT s;
2452 UINT e;
2453 INT i;
2454 LPSTR p;
2456 s = es->selection_start;
2457 e = es->selection_end;
2459 if ((s == e) && !strl)
2460 return;
2462 ORDER_UINT(s, e);
2464 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2465 return;
2467 if (e != s) {
2468 /* there is something to be deleted */
2469 if (can_undo) {
2470 utl = strlen(es->undo_text);
2471 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2472 /* undo-buffer is extended to the right */
2473 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2474 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2475 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2476 /* undo-buffer is extended to the left */
2477 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2478 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2479 p[e - s] = p[0];
2480 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2481 p[i] = (es->text + s)[i];
2482 es->undo_position = s;
2483 } else {
2484 /* new undo-buffer */
2485 EDIT_MakeUndoFit(wnd, es, e - s);
2486 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2487 es->undo_position = s;
2489 /* any deletion makes the old insertion-undo invalid */
2490 es->undo_insert_count = 0;
2491 } else
2492 EDIT_EM_EmptyUndoBuffer(wnd, es);
2494 /* now delete */
2495 strcpy(es->text + s, es->text + e);
2497 if (strl) {
2498 /* there is an insertion */
2499 if (can_undo) {
2500 if ((s == es->undo_position) ||
2501 ((es->undo_insert_count) &&
2502 (s == es->undo_position + es->undo_insert_count)))
2504 * insertion is new and at delete position or
2505 * an extension to either left or right
2507 es->undo_insert_count += strl;
2508 else {
2509 /* new insertion undo */
2510 es->undo_position = s;
2511 es->undo_insert_count = strl;
2512 /* new insertion makes old delete-buffer invalid */
2513 *es->undo_text = '\0';
2515 } else
2516 EDIT_EM_EmptyUndoBuffer(wnd, es);
2518 /* now insert */
2519 tl = strlen(es->text);
2520 for (p = es->text + tl ; p >= es->text + s ; p--)
2521 p[strl] = p[0];
2522 for (i = 0 , p = es->text + s ; i < strl ; i++)
2523 p[i] = lpsz_replace[i];
2524 if(es->style & ES_UPPERCASE)
2525 CharUpperBuffA(p, strl);
2526 else if(es->style & ES_LOWERCASE)
2527 CharLowerBuffA(p, strl);
2528 s += strl;
2530 /* FIXME: really inefficient */
2531 if (es->style & ES_MULTILINE)
2532 EDIT_BuildLineDefs_ML(wnd, es);
2534 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2535 es->flags |= EF_MODIFIED;
2536 if (send_update) es->flags |= EF_UPDATE;
2537 EDIT_EM_ScrollCaret(wnd, es);
2539 /* FIXME: really inefficient */
2540 EDIT_UpdateText(wnd, NULL, TRUE);
2544 /*********************************************************************
2546 * EM_SCROLL
2549 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
2551 INT dy;
2553 if (!(es->style & ES_MULTILINE))
2554 return (LRESULT)FALSE;
2556 dy = 0;
2558 switch (action) {
2559 case SB_LINEUP:
2560 if (es->y_offset)
2561 dy = -1;
2562 break;
2563 case SB_LINEDOWN:
2564 if (es->y_offset < es->line_count - 1)
2565 dy = 1;
2566 break;
2567 case SB_PAGEUP:
2568 if (es->y_offset)
2569 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2570 break;
2571 case SB_PAGEDOWN:
2572 if (es->y_offset < es->line_count - 1)
2573 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2574 break;
2575 default:
2576 return (LRESULT)FALSE;
2578 if (dy) {
2579 EDIT_EM_LineScroll(wnd, es, 0, dy);
2580 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2582 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2586 /*********************************************************************
2588 * EM_SCROLLCARET
2591 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
2593 if (es->style & ES_MULTILINE) {
2594 INT l;
2595 INT li;
2596 INT vlc;
2597 INT ww;
2598 INT cw = es->char_width;
2599 INT x;
2600 INT dy = 0;
2601 INT dx = 0;
2603 l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2604 li = EDIT_EM_LineIndex(wnd, es, l);
2605 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2606 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2607 if (l >= es->y_offset + vlc)
2608 dy = l - vlc + 1 - es->y_offset;
2609 if (l < es->y_offset)
2610 dy = l - es->y_offset;
2611 ww = es->format_rect.right - es->format_rect.left;
2612 if (x < es->format_rect.left)
2613 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2614 if (x > es->format_rect.right)
2615 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2616 if (dy || dx)
2617 EDIT_EM_LineScroll(wnd, es, dx, dy);
2618 } else {
2619 INT x;
2620 INT goal;
2621 INT format_width;
2623 if (!(es->style & ES_AUTOHSCROLL))
2624 return;
2626 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2627 format_width = es->format_rect.right - es->format_rect.left;
2628 if (x < es->format_rect.left) {
2629 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2630 do {
2631 es->x_offset--;
2632 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2633 } while ((x < goal) && es->x_offset);
2634 /* FIXME: use ScrollWindow() somehow to improve performance */
2635 EDIT_UpdateText(wnd, NULL, TRUE);
2636 } else if (x > es->format_rect.right) {
2637 INT x_last;
2638 INT len = strlen(es->text);
2639 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2640 do {
2641 es->x_offset++;
2642 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2643 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
2644 } while ((x > goal) && (x_last > es->format_rect.right));
2645 /* FIXME: use ScrollWindow() somehow to improve performance */
2646 EDIT_UpdateText(wnd, NULL, TRUE);
2652 /*********************************************************************
2654 * EM_SETHANDLE
2656 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2659 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
2661 if (!(es->style & ES_MULTILINE))
2662 return;
2664 if (!hloc) {
2665 WARN("called with NULL handle\n");
2666 return;
2669 EDIT_UnlockBuffer(wnd, es, TRUE);
2671 * old buffer is freed by caller, unless
2672 * it is still in our own heap. (in that case
2673 * we free it, correcting the buggy caller.)
2675 if (es->text)
2676 HeapFree(es->heap, 0, es->text);
2678 es->hloc16 = (HLOCAL16)NULL;
2679 es->hloc32 = hloc;
2680 es->text = NULL;
2681 es->buffer_size = LocalSize(es->hloc32) - 1;
2682 EDIT_LockBuffer(wnd, es);
2684 es->x_offset = es->y_offset = 0;
2685 es->selection_start = es->selection_end = 0;
2686 EDIT_EM_EmptyUndoBuffer(wnd, es);
2687 es->flags &= ~EF_MODIFIED;
2688 es->flags &= ~EF_UPDATE;
2689 EDIT_BuildLineDefs_ML(wnd, es);
2690 EDIT_UpdateText(wnd, NULL, TRUE);
2691 EDIT_EM_ScrollCaret(wnd, es);
2695 /*********************************************************************
2697 * EM_SETHANDLE16
2699 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2702 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2704 if (!(es->style & ES_MULTILINE))
2705 return;
2707 if (!hloc) {
2708 WARN("called with NULL handle\n");
2709 return;
2712 EDIT_UnlockBuffer(wnd, es, TRUE);
2714 * old buffer is freed by caller, unless
2715 * it is still in our own heap. (in that case
2716 * we free it, correcting the buggy caller.)
2718 if (es->text)
2719 HeapFree(es->heap, 0, es->text);
2721 es->hloc16 = hloc;
2722 es->hloc32 = (HLOCAL)NULL;
2723 es->text = NULL;
2724 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2725 EDIT_LockBuffer(wnd, es);
2727 es->x_offset = es->y_offset = 0;
2728 es->selection_start = es->selection_end = 0;
2729 EDIT_EM_EmptyUndoBuffer(wnd, es);
2730 es->flags &= ~EF_MODIFIED;
2731 es->flags &= ~EF_UPDATE;
2732 EDIT_BuildLineDefs_ML(wnd, es);
2733 EDIT_UpdateText(wnd, NULL, TRUE);
2734 EDIT_EM_ScrollCaret(wnd, es);
2738 /*********************************************************************
2740 * EM_SETLIMITTEXT
2742 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2743 * However, the windows version is not complied to yet in all of edit.c
2746 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT limit)
2748 if (es->style & ES_MULTILINE) {
2749 if (limit)
2750 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
2751 else
2752 es->buffer_limit = BUFLIMIT_MULTI;
2753 } else {
2754 if (limit)
2755 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
2756 else
2757 es->buffer_limit = BUFLIMIT_SINGLE;
2762 /*********************************************************************
2764 * EM_SETMARGINS
2766 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2767 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2768 * of the char's width, according to the new docs.
2771 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT action,
2772 INT left, INT right)
2774 if (action & EC_LEFTMARGIN) {
2775 if (left != EC_USEFONTINFO)
2776 es->left_margin = left;
2777 else
2778 es->left_margin = es->char_width / 3;
2781 if (action & EC_RIGHTMARGIN) {
2782 if (right != EC_USEFONTINFO)
2783 es->right_margin = right;
2784 else
2785 es->right_margin = es->char_width / 3;
2787 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2791 /*********************************************************************
2793 * EM_SETPASSWORDCHAR
2796 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2798 if (es->style & ES_MULTILINE)
2799 return;
2801 if (es->password_char == c)
2802 return;
2804 es->password_char = c;
2805 if (c) {
2806 wnd->dwStyle |= ES_PASSWORD;
2807 es->style |= ES_PASSWORD;
2808 } else {
2809 wnd->dwStyle &= ~ES_PASSWORD;
2810 es->style &= ~ES_PASSWORD;
2812 EDIT_UpdateText(wnd, NULL, TRUE);
2816 /*********************************************************************
2818 * EDIT_EM_SetSel
2820 * note: unlike the specs say: the order of start and end
2821 * _is_ preserved in Windows. (i.e. start can be > end)
2822 * In other words: this handler is OK
2825 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2827 UINT old_start = es->selection_start;
2828 UINT old_end = es->selection_end;
2829 UINT len = strlen(es->text);
2831 if (start == -1) {
2832 start = es->selection_end;
2833 end = es->selection_end;
2834 } else {
2835 start = min(start, len);
2836 end = min(end, len);
2838 es->selection_start = start;
2839 es->selection_end = end;
2840 if (after_wrap)
2841 es->flags |= EF_AFTER_WRAP;
2842 else
2843 es->flags &= ~EF_AFTER_WRAP;
2844 if (es->flags & EF_FOCUSED)
2845 EDIT_SetCaretPos(wnd, es, end, after_wrap);
2846 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
2847 ORDER_UINT(start, end);
2848 ORDER_UINT(end, old_end);
2849 ORDER_UINT(start, old_start);
2850 ORDER_UINT(old_start, old_end);
2851 if (end != old_start)
2854 * One can also do
2855 * ORDER_UINT32(end, old_start);
2856 * EDIT_InvalidateText(wnd, es, start, end);
2857 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2858 * in place of the following if statement.
2860 if (old_start > end )
2862 EDIT_InvalidateText(wnd, es, start, end);
2863 EDIT_InvalidateText(wnd, es, old_start, old_end);
2865 else
2867 EDIT_InvalidateText(wnd, es, start, old_start);
2868 EDIT_InvalidateText(wnd, es, end, old_end);
2871 else EDIT_InvalidateText(wnd, es, start, old_end);
2875 /*********************************************************************
2877 * EM_SETTABSTOPS
2880 static BOOL EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT count, LPINT tabs)
2882 if (!(es->style & ES_MULTILINE))
2883 return FALSE;
2884 if (es->tabs)
2885 HeapFree(es->heap, 0, es->tabs);
2886 es->tabs_count = count;
2887 if (!count)
2888 es->tabs = NULL;
2889 else {
2890 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2891 memcpy(es->tabs, tabs, count * sizeof(INT));
2893 return TRUE;
2897 /*********************************************************************
2899 * EM_SETTABSTOPS16
2902 static BOOL EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT count, LPINT16 tabs)
2904 if (!(es->style & ES_MULTILINE))
2905 return FALSE;
2906 if (es->tabs)
2907 HeapFree(es->heap, 0, es->tabs);
2908 es->tabs_count = count;
2909 if (!count)
2910 es->tabs = NULL;
2911 else {
2912 INT i;
2913 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2914 for (i = 0 ; i < count ; i++)
2915 es->tabs[i] = *tabs++;
2917 return TRUE;
2921 /*********************************************************************
2923 * EM_SETWORDBREAKPROC
2926 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2928 if (es->word_break_proc32A == wbp)
2929 return;
2931 es->word_break_proc32A = wbp;
2932 es->word_break_proc16 = NULL;
2933 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2934 EDIT_BuildLineDefs_ML(wnd, es);
2935 EDIT_UpdateText(wnd, NULL, TRUE);
2940 /*********************************************************************
2942 * EM_SETWORDBREAKPROC16
2945 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2947 if (es->word_break_proc16 == wbp)
2948 return;
2950 es->word_break_proc32A = NULL;
2951 es->word_break_proc16 = wbp;
2952 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2953 EDIT_BuildLineDefs_ML(wnd, es);
2954 EDIT_UpdateText(wnd, NULL, TRUE);
2959 /*********************************************************************
2961 * EM_UNDO / WM_UNDO
2964 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2966 INT ulength = strlen(es->undo_text);
2967 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2969 strcpy(utext, es->undo_text);
2971 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2972 es->undo_insert_count, utext);
2974 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2975 EDIT_EM_EmptyUndoBuffer(wnd, es);
2976 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
2977 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2978 HeapFree(es->heap, 0, utext);
2980 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2981 es->undo_insert_count, es->undo_text);
2983 if (es->flags & EF_UPDATE) {
2984 es->flags &= ~EF_UPDATE;
2985 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
2988 return TRUE;
2992 /*********************************************************************
2994 * WM_CHAR
2997 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
2999 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
3000 switch (c) {
3001 case '\r':
3002 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3003 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3004 break;
3005 case '\n':
3006 if (es->style & ES_MULTILINE) {
3007 if (es->style & ES_READONLY) {
3008 EDIT_MoveHome(wnd, es, FALSE);
3009 EDIT_MoveDown_ML(wnd, es, FALSE);
3010 } else {
3011 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n", TRUE);
3012 if (es->flags & EF_UPDATE) {
3013 es->flags &= ~EF_UPDATE;
3014 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3018 break;
3019 case '\t':
3020 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3022 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t", TRUE);
3023 if (es->flags & EF_UPDATE) {
3024 es->flags &= ~EF_UPDATE;
3025 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3028 break;
3029 case VK_BACK:
3030 if (!(es->style & ES_READONLY) && !control) {
3031 if (es->selection_start != es->selection_end)
3032 EDIT_WM_Clear(wnd, es);
3033 else {
3034 /* delete character left of caret */
3035 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3036 EDIT_MoveBackward(wnd, es, TRUE);
3037 EDIT_WM_Clear(wnd, es);
3040 break;
3041 case 0x03: /* ^C */
3042 SendMessageA(wnd->hwndSelf, WM_COPY, 0, 0);
3043 break;
3044 case 0x16: /* ^V */
3045 SendMessageA(wnd->hwndSelf, WM_PASTE, 0, 0);
3046 break;
3047 case 0x18: /* ^X */
3048 SendMessageA(wnd->hwndSelf, WM_CUT, 0, 0);
3049 break;
3051 default:
3052 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
3053 char str[2];
3054 str[0] = c;
3055 str[1] = '\0';
3056 EDIT_EM_ReplaceSel(wnd, es, TRUE, str, TRUE);
3057 if (es->flags & EF_UPDATE) {
3058 es->flags &= ~EF_UPDATE;
3059 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3062 break;
3067 /*********************************************************************
3069 * WM_COMMAND
3072 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
3074 if (code || control)
3075 return;
3077 switch (id) {
3078 case EM_UNDO:
3079 EDIT_EM_Undo(wnd, es);
3080 break;
3081 case WM_CUT:
3082 EDIT_WM_Cut(wnd, es);
3083 break;
3084 case WM_COPY:
3085 EDIT_WM_Copy(wnd, es);
3086 break;
3087 case WM_PASTE:
3088 EDIT_WM_Paste(wnd, es);
3089 break;
3090 case WM_CLEAR:
3091 EDIT_WM_Clear(wnd, es);
3092 break;
3093 case EM_SETSEL:
3094 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3095 EDIT_EM_ScrollCaret(wnd, es);
3096 break;
3097 default:
3098 ERR("unknown menu item, please report\n");
3099 break;
3104 /*********************************************************************
3106 * WM_CONTEXTMENU
3108 * Note: the resource files resource/sysres_??.rc cannot define a
3109 * single popup menu. Hence we use a (dummy) menubar
3110 * containing the single popup menu as its first item.
3112 * FIXME: the message identifiers have been chosen arbitrarily,
3113 * hence we use MF_BYPOSITION.
3114 * We might as well use the "real" values (anybody knows ?)
3115 * The menu definition is in resources/sysres_??.rc.
3116 * Once these are OK, we better use MF_BYCOMMAND here
3117 * (as we do in EDIT_WM_Command()).
3120 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND hwnd, INT x, INT y)
3122 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3123 HMENU popup = GetSubMenu(menu, 0);
3124 UINT start = es->selection_start;
3125 UINT end = es->selection_end;
3127 ORDER_UINT(start, end);
3129 /* undo */
3130 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
3131 /* cut */
3132 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3133 /* copy */
3134 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3135 /* paste */
3136 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
3137 /* delete */
3138 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
3139 /* select all */
3140 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlen(es->text)) ? MF_ENABLED : MF_GRAYED));
3142 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3143 DestroyMenu(menu);
3147 /*********************************************************************
3149 * WM_COPY
3152 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3154 INT s = es->selection_start;
3155 INT e = es->selection_end;
3156 HGLOBAL hdst;
3157 LPSTR dst;
3159 if (e == s)
3160 return;
3161 ORDER_INT(s, e);
3162 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3163 dst = GlobalLock(hdst);
3164 lstrcpynA(dst, es->text + s, e - s + 1);
3165 GlobalUnlock(hdst);
3166 OpenClipboard(wnd->hwndSelf);
3167 EmptyClipboard();
3168 SetClipboardData(CF_TEXT, hdst);
3169 CloseClipboard();
3173 /*********************************************************************
3175 * WM_CREATE
3178 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3181 * To initialize some final structure members, we call some helper
3182 * functions. However, since the EDITSTATE is not consistent (i.e.
3183 * not fully initialized), we should be very careful which
3184 * functions can be called, and in what order.
3186 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3187 EDIT_EM_EmptyUndoBuffer(wnd, es);
3189 if (cs->lpszName && *(cs->lpszName) != '\0') {
3190 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName, TRUE);
3191 /* if we insert text to the editline, the text scrolls out
3192 * of the window, as the caret is placed after the insert
3193 * pos normally; thus we reset es->selection... to 0 and
3194 * update caret
3196 es->selection_start = es->selection_end = 0;
3197 EDIT_EM_ScrollCaret(wnd, es);
3198 if (es->flags & EF_UPDATE) {
3199 es->flags &= ~EF_UPDATE;
3200 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3203 return 0;
3207 /*********************************************************************
3209 * WM_DESTROY
3212 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3214 if (es->hloc32) {
3215 while (LocalUnlock(es->hloc32)) ;
3216 LocalFree(es->hloc32);
3218 if (es->hloc16) {
3219 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3220 LOCAL_Free(wnd->hInstance, es->hloc16);
3222 HeapDestroy(es->heap);
3223 HeapFree(GetProcessHeap(), 0, es);
3224 *(EDITSTATE **)wnd->wExtra = NULL;
3228 /*********************************************************************
3230 * WM_ERASEBKGND
3233 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3235 HBRUSH brush;
3236 RECT rc;
3238 if ( get_app_version() >= 0x40000 &&(
3239 !es->bEnableState || (es->style & ES_READONLY)))
3240 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3241 else
3242 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3244 if (!brush)
3245 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3247 GetClientRect(wnd->hwndSelf, &rc);
3248 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3249 GetClipBox(dc, &rc);
3251 * FIXME: specs say that we should UnrealizeObject() the brush,
3252 * but the specs of UnrealizeObject() say that we shouldn't
3253 * unrealize a stock object. The default brush that
3254 * DefWndProc() returns is ... a stock object.
3256 FillRect(dc, &rc, brush);
3257 return -1;
3261 /*********************************************************************
3263 * WM_GETTEXT
3266 static INT EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT count, LPSTR text)
3268 lstrcpynA(text, es->text, count);
3269 return strlen(text);
3273 /*********************************************************************
3275 * EDIT_HScroll_Hack
3277 * 16 bit notepad needs this. Actually it is not _our_ hack,
3278 * it is notepad's. Notepad is sending us scrollbar messages with
3279 * undocumented parameters without us even having a scrollbar ... !?!?
3282 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3284 INT dx = 0;
3285 INT fw = es->format_rect.right - es->format_rect.left;
3286 LRESULT ret = 0;
3288 if (!(es->flags & EF_HSCROLL_HACK)) {
3289 ERR("hacked WM_HSCROLL handler invoked\n");
3290 ERR(" if you are _not_ running 16 bit notepad, please report\n");
3291 ERR(" (this message is only displayed once per edit control)\n");
3292 es->flags |= EF_HSCROLL_HACK;
3295 switch (action) {
3296 case SB_LINELEFT:
3297 if (es->x_offset)
3298 dx = -es->char_width;
3299 break;
3300 case SB_LINERIGHT:
3301 if (es->x_offset < es->text_width)
3302 dx = es->char_width;
3303 break;
3304 case SB_PAGELEFT:
3305 if (es->x_offset)
3306 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3307 break;
3308 case SB_PAGERIGHT:
3309 if (es->x_offset < es->text_width)
3310 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3311 break;
3312 case SB_LEFT:
3313 if (es->x_offset)
3314 dx = -es->x_offset;
3315 break;
3316 case SB_RIGHT:
3317 if (es->x_offset < es->text_width)
3318 dx = es->text_width - es->x_offset;
3319 break;
3320 case SB_THUMBTRACK:
3321 es->flags |= EF_HSCROLL_TRACK;
3322 dx = pos * es->text_width / 100 - es->x_offset;
3323 break;
3324 case SB_THUMBPOSITION:
3325 es->flags &= ~EF_HSCROLL_TRACK;
3326 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3327 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3328 break;
3329 case SB_ENDSCROLL:
3330 break;
3333 * FIXME : the next two are undocumented !
3334 * Are we doing the right thing ?
3335 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3336 * although it's also a regular control message.
3338 case EM_GETTHUMB16:
3339 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3340 break;
3341 case EM_LINESCROLL16:
3342 dx = pos;
3343 break;
3345 default:
3346 ERR("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3347 return 0;
3349 if (dx)
3350 EDIT_EM_LineScroll(wnd, es, dx, 0);
3351 return ret;
3355 /*********************************************************************
3357 * WM_HSCROLL
3360 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3362 INT dx;
3363 INT fw;
3365 if (!(es->style & ES_MULTILINE))
3366 return 0;
3368 if (!(es->style & ES_AUTOHSCROLL))
3369 return 0;
3371 if (!(es->style & WS_HSCROLL))
3372 return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
3374 dx = 0;
3375 fw = es->format_rect.right - es->format_rect.left;
3376 switch (action) {
3377 case SB_LINELEFT:
3378 if (es->x_offset)
3379 dx = -es->char_width;
3380 break;
3381 case SB_LINERIGHT:
3382 if (es->x_offset < es->text_width)
3383 dx = es->char_width;
3384 break;
3385 case SB_PAGELEFT:
3386 if (es->x_offset)
3387 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3388 break;
3389 case SB_PAGERIGHT:
3390 if (es->x_offset < es->text_width)
3391 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3392 break;
3393 case SB_LEFT:
3394 if (es->x_offset)
3395 dx = -es->x_offset;
3396 break;
3397 case SB_RIGHT:
3398 if (es->x_offset < es->text_width)
3399 dx = es->text_width - es->x_offset;
3400 break;
3401 case SB_THUMBTRACK:
3402 es->flags |= EF_HSCROLL_TRACK;
3403 dx = pos - es->x_offset;
3404 break;
3405 case SB_THUMBPOSITION:
3406 es->flags &= ~EF_HSCROLL_TRACK;
3407 if (!(dx = pos - es->x_offset)) {
3408 SetScrollPos(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3409 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3411 break;
3412 case SB_ENDSCROLL:
3413 break;
3415 default:
3416 ERR("undocumented WM_HSCROLL parameter, please report\n");
3417 return 0;
3419 if (dx)
3420 EDIT_EM_LineScroll(wnd, es, dx, 0);
3421 return 0;
3425 /*********************************************************************
3427 * EDIT_CheckCombo
3430 static BOOL EDIT_CheckCombo(WND *wnd, EDITSTATE *es, UINT msg, INT key, DWORD key_data)
3432 HWND hLBox = es->hwndListBox;
3433 HWND hCombo;
3434 BOOL bDropped;
3435 int nEUI;
3437 if (!hLBox)
3438 return FALSE;
3440 hCombo = wnd->parent->hwndSelf;
3441 bDropped = TRUE;
3442 nEUI = 0;
3444 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3445 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3447 if (key == VK_UP || key == VK_DOWN)
3449 if (SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0))
3450 nEUI = 1;
3452 if (msg == WM_KEYDOWN || nEUI)
3453 bDropped = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3456 switch (msg)
3458 case WM_KEYDOWN:
3459 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3461 /* make sure ComboLBox pops up */
3462 SendMessageA(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3463 key = VK_F4;
3464 nEUI = 2;
3467 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3468 break;
3470 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3471 if (nEUI)
3472 SendMessageA(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
3473 else
3474 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
3475 break;
3478 if(nEUI == 2)
3479 SendMessageA(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3481 return TRUE;
3485 /*********************************************************************
3487 * WM_KEYDOWN
3489 * Handling of special keys that don't produce a WM_CHAR
3490 * (i.e. non-printable keys) & Backspace & Delete
3493 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
3495 BOOL shift;
3496 BOOL control;
3498 if (GetKeyState(VK_MENU) & 0x8000)
3499 return 0;
3501 shift = GetKeyState(VK_SHIFT) & 0x8000;
3502 control = GetKeyState(VK_CONTROL) & 0x8000;
3504 switch (key) {
3505 case VK_F4:
3506 case VK_UP:
3507 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data) || key == VK_F4)
3508 break;
3510 /* fall through */
3511 case VK_LEFT:
3512 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3513 EDIT_MoveUp_ML(wnd, es, shift);
3514 else
3515 if (control)
3516 EDIT_MoveWordBackward(wnd, es, shift);
3517 else
3518 EDIT_MoveBackward(wnd, es, shift);
3519 break;
3520 case VK_DOWN:
3521 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data))
3522 break;
3523 /* fall through */
3524 case VK_RIGHT:
3525 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3526 EDIT_MoveDown_ML(wnd, es, shift);
3527 else if (control)
3528 EDIT_MoveWordForward(wnd, es, shift);
3529 else
3530 EDIT_MoveForward(wnd, es, shift);
3531 break;
3532 case VK_HOME:
3533 EDIT_MoveHome(wnd, es, shift);
3534 break;
3535 case VK_END:
3536 EDIT_MoveEnd(wnd, es, shift);
3537 break;
3538 case VK_PRIOR:
3539 if (es->style & ES_MULTILINE)
3540 EDIT_MovePageUp_ML(wnd, es, shift);
3541 else
3542 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data);
3543 break;
3544 case VK_NEXT:
3545 if (es->style & ES_MULTILINE)
3546 EDIT_MovePageDown_ML(wnd, es, shift);
3547 else
3548 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data);
3549 break;
3550 case VK_DELETE:
3551 if (!(es->style & ES_READONLY) && !(shift && control)) {
3552 if (es->selection_start != es->selection_end) {
3553 if (shift)
3554 EDIT_WM_Cut(wnd, es);
3555 else
3556 EDIT_WM_Clear(wnd, es);
3557 } else {
3558 if (shift) {
3559 /* delete character left of caret */
3560 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3561 EDIT_MoveBackward(wnd, es, TRUE);
3562 EDIT_WM_Clear(wnd, es);
3563 } else if (control) {
3564 /* delete to end of line */
3565 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3566 EDIT_MoveEnd(wnd, es, TRUE);
3567 EDIT_WM_Clear(wnd, es);
3568 } else {
3569 /* delete character right of caret */
3570 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3571 EDIT_MoveForward(wnd, es, TRUE);
3572 EDIT_WM_Clear(wnd, es);
3576 break;
3577 case VK_INSERT:
3578 if (shift) {
3579 if (!(es->style & ES_READONLY))
3580 EDIT_WM_Paste(wnd, es);
3581 } else if (control)
3582 EDIT_WM_Copy(wnd, es);
3583 break;
3584 case VK_RETURN:
3585 /* If the edit doesn't want the return send a message to the default object */
3586 if(!(es->style & ES_WANTRETURN))
3588 HWND hwndParent = GetParent(wnd->hwndSelf);
3589 DWORD dw = SendMessage16( hwndParent, DM_GETDEFID, 0, 0 );
3590 if (HIWORD(dw) == DC_HASDEFID)
3592 SendMessageA( hwndParent, WM_COMMAND,
3593 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3594 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3597 break;
3599 return 0;
3603 /*********************************************************************
3605 * WM_KILLFOCUS
3608 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND window_getting_focus)
3610 es->flags &= ~EF_FOCUSED;
3611 DestroyCaret();
3612 if(!(es->style & ES_NOHIDESEL))
3613 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3614 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3615 return 0;
3619 /*********************************************************************
3621 * WM_LBUTTONDBLCLK
3623 * The caret position has been set on the WM_LBUTTONDOWN message
3626 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3628 INT s;
3629 INT e = es->selection_end;
3630 INT l;
3631 INT li;
3632 INT ll;
3634 if (!(es->flags & EF_FOCUSED))
3635 return 0;
3637 l = EDIT_EM_LineFromChar(wnd, es, e);
3638 li = EDIT_EM_LineIndex(wnd, es, l);
3639 ll = EDIT_EM_LineLength(wnd, es, e);
3640 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3641 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3642 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3643 EDIT_EM_ScrollCaret(wnd, es);
3644 return 0;
3648 /*********************************************************************
3650 * WM_LBUTTONDOWN
3653 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3655 INT e;
3656 BOOL after_wrap;
3658 if (!(es->flags & EF_FOCUSED))
3659 return 0;
3661 es->bCaptureState = TRUE;
3662 SetCapture(wnd->hwndSelf);
3663 EDIT_ConfinePoint(wnd, es, &x, &y);
3664 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3665 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3666 EDIT_EM_ScrollCaret(wnd, es);
3667 es->region_posx = es->region_posy = 0;
3668 SetTimer(wnd->hwndSelf, 0, 100, NULL);
3669 return 0;
3673 /*********************************************************************
3675 * WM_LBUTTONUP
3678 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3680 if (es->bCaptureState && GetCapture() == wnd->hwndSelf) {
3681 KillTimer(wnd->hwndSelf, 0);
3682 ReleaseCapture();
3684 es->bCaptureState = FALSE;
3685 return 0;
3689 /*********************************************************************
3691 * WM_MBUTTONDOWN
3694 static LRESULT EDIT_WM_MButtonDown(WND *wnd)
3696 SendMessageA(wnd->hwndSelf,WM_PASTE,0,0);
3697 return 0;
3701 /*********************************************************************
3703 * WM_MOUSEMOVE
3706 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3708 INT e;
3709 BOOL after_wrap;
3710 INT prex, prey;
3712 if (GetCapture() != wnd->hwndSelf)
3713 return 0;
3716 * FIXME: gotta do some scrolling if outside client
3717 * area. Maybe reset the timer ?
3719 prex = x; prey = y;
3720 EDIT_ConfinePoint(wnd, es, &x, &y);
3721 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3722 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3723 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3724 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3725 return 0;
3729 /*********************************************************************
3731 * WM_NCCREATE
3734 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs)
3736 EDITSTATE *es;
3738 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3739 return FALSE;
3740 *(EDITSTATE **)wnd->wExtra = es;
3743 * Note: since the EDITSTATE has not been fully initialized yet,
3744 * we can't use any API calls that may send
3745 * WM_XXX messages before WM_NCCREATE is completed.
3748 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3749 return FALSE;
3750 es->style = cs->style;
3752 es->bEnableState = !(cs->style & WS_DISABLED);
3755 * In Win95 look and feel, the WS_BORDER style is replaced by the
3756 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3757 * control a non client area.
3759 if (TWEAK_WineLook != WIN31_LOOK)
3761 if (es->style & WS_BORDER)
3763 es->style &= ~WS_BORDER;
3764 wnd->dwStyle &= ~WS_BORDER;
3765 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
3768 else
3770 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
3771 wnd->dwStyle &= ~WS_BORDER;
3774 if (es->style & ES_COMBO)
3775 es->hwndListBox = GetDlgItem(cs->hwndParent, ID_CB_LISTBOX);
3777 if (es->style & ES_MULTILINE) {
3778 es->buffer_size = BUFSTART_MULTI;
3779 es->buffer_limit = BUFLIMIT_MULTI;
3780 if (es->style & WS_VSCROLL)
3781 es->style |= ES_AUTOVSCROLL;
3782 if (es->style & WS_HSCROLL)
3783 es->style |= ES_AUTOHSCROLL;
3784 es->style &= ~ES_PASSWORD;
3785 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3786 if (es->style & ES_RIGHT)
3787 es->style &= ~ES_CENTER;
3788 es->style &= ~WS_HSCROLL;
3789 es->style &= ~ES_AUTOHSCROLL;
3792 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3793 es->style |= ES_AUTOVSCROLL;
3794 } else {
3795 es->buffer_size = BUFSTART_SINGLE;
3796 es->buffer_limit = BUFLIMIT_SINGLE;
3797 es->style &= ~ES_CENTER;
3798 es->style &= ~ES_RIGHT;
3799 es->style &= ~WS_HSCROLL;
3800 es->style &= ~WS_VSCROLL;
3801 es->style &= ~ES_AUTOVSCROLL;
3802 es->style &= ~ES_WANTRETURN;
3803 if (es->style & ES_UPPERCASE) {
3804 es->style &= ~ES_LOWERCASE;
3805 es->style &= ~ES_NUMBER;
3806 } else if (es->style & ES_LOWERCASE)
3807 es->style &= ~ES_NUMBER;
3808 if (es->style & ES_PASSWORD)
3809 es->password_char = '*';
3811 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3812 es->style |= ES_AUTOHSCROLL;
3814 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3815 return FALSE;
3816 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3817 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3818 return FALSE;
3819 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3820 *es->text = '\0';
3821 if (es->style & ES_MULTILINE)
3822 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3823 return FALSE;
3824 es->line_count = 1;
3826 return TRUE;
3829 /*********************************************************************
3831 * WM_PAINT
3834 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
3836 PAINTSTRUCT ps;
3837 INT i;
3838 HDC dc;
3839 HFONT old_font = 0;
3840 RECT rc;
3841 RECT rcLine;
3842 RECT rcRgn;
3843 BOOL rev = es->bEnableState &&
3844 ((es->flags & EF_FOCUSED) ||
3845 (es->style & ES_NOHIDESEL));
3846 if (!wParam)
3847 dc = BeginPaint(wnd->hwndSelf, &ps);
3848 else
3849 dc = (HDC) wParam;
3850 if(es->style & WS_BORDER) {
3851 GetClientRect(wnd->hwndSelf, &rc);
3852 if(es->style & ES_MULTILINE) {
3853 if(es->style & WS_HSCROLL) rc.bottom++;
3854 if(es->style & WS_VSCROLL) rc.right++;
3856 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
3858 IntersectClipRect(dc, es->format_rect.left,
3859 es->format_rect.top,
3860 es->format_rect.right,
3861 es->format_rect.bottom);
3862 if (es->style & ES_MULTILINE) {
3863 GetClientRect(wnd->hwndSelf, &rc);
3864 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3866 if (es->font)
3867 old_font = SelectObject(dc, es->font);
3868 if ( get_app_version() >= 0x40000 &&(
3869 !es->bEnableState || (es->style & ES_READONLY)))
3870 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3871 else
3872 EDIT_SEND_CTLCOLOR(wnd, dc);
3874 if (!es->bEnableState)
3875 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3876 GetClipBox(dc, &rcRgn);
3877 if (es->style & ES_MULTILINE) {
3878 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3879 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3880 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3881 if (IntersectRect(&rc, &rcRgn, &rcLine))
3882 EDIT_PaintLine(wnd, es, dc, i, rev);
3884 } else {
3885 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3886 if (IntersectRect(&rc, &rcRgn, &rcLine))
3887 EDIT_PaintLine(wnd, es, dc, 0, rev);
3889 if (es->font)
3890 SelectObject(dc, old_font);
3891 if (es->flags & EF_FOCUSED)
3892 EDIT_SetCaretPos(wnd, es, es->selection_end,
3893 es->flags & EF_AFTER_WRAP);
3894 if (!wParam)
3895 EndPaint(wnd->hwndSelf, &ps);
3896 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3897 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3898 SCROLLINFO si;
3899 si.cbSize = sizeof(SCROLLINFO);
3900 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3901 si.nMin = 0;
3902 si.nMax = es->line_count + vlc - 2;
3903 si.nPage = vlc;
3904 si.nPos = es->y_offset;
3905 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
3907 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3908 SCROLLINFO si;
3909 INT fw = es->format_rect.right - es->format_rect.left;
3910 si.cbSize = sizeof(SCROLLINFO);
3911 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3912 si.nMin = 0;
3913 si.nMax = es->text_width + fw - 1;
3914 si.nPage = fw;
3915 si.nPos = es->x_offset;
3916 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3921 /*********************************************************************
3923 * WM_PASTE
3926 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3928 HGLOBAL hsrc;
3929 LPSTR src;
3931 OpenClipboard(wnd->hwndSelf);
3932 if ((hsrc = GetClipboardData(CF_TEXT))) {
3933 src = (LPSTR)GlobalLock(hsrc);
3934 EDIT_EM_ReplaceSel(wnd, es, TRUE, src, TRUE);
3935 GlobalUnlock(hsrc);
3937 if (es->flags & EF_UPDATE) {
3938 es->flags &= ~EF_UPDATE;
3939 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3942 CloseClipboard();
3946 /*********************************************************************
3948 * WM_SETFOCUS
3951 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus)
3953 es->flags |= EF_FOCUSED;
3954 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
3955 EDIT_SetCaretPos(wnd, es, es->selection_end,
3956 es->flags & EF_AFTER_WRAP);
3957 if(!(es->style & ES_NOHIDESEL))
3958 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3959 ShowCaret(wnd->hwndSelf);
3960 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3964 /*********************************************************************
3966 * WM_SETFONT
3968 * With Win95 look the margins are set to default font value unless
3969 * the system font (font == 0) is being set, in which case they are left
3970 * unchanged.
3973 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
3975 TEXTMETRICA tm;
3976 HDC dc;
3977 HFONT old_font = 0;
3978 RECT r;
3980 es->font = font;
3981 dc = GetDC(wnd->hwndSelf);
3982 if (font)
3983 old_font = SelectObject(dc, font);
3984 GetTextMetricsA(dc, &tm);
3985 es->line_height = tm.tmHeight;
3986 es->char_width = tm.tmAveCharWidth;
3987 if (font)
3988 SelectObject(dc, old_font);
3989 ReleaseDC(wnd->hwndSelf, dc);
3990 if (font && (TWEAK_WineLook > WIN31_LOOK))
3991 EDIT_EM_SetMargins(wnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3992 EC_USEFONTINFO, EC_USEFONTINFO);
3994 /* Force the recalculation of the format rect for each font change */
3995 GetClientRect(wnd->hwndSelf, &r);
3996 EDIT_SetRectNP(wnd, es, &r);
3998 if (es->style & ES_MULTILINE)
3999 EDIT_BuildLineDefs_ML(wnd, es);
4001 if (redraw)
4002 EDIT_UpdateText(wnd, NULL, TRUE);
4003 if (es->flags & EF_FOCUSED) {
4004 DestroyCaret();
4005 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4006 EDIT_SetCaretPos(wnd, es, es->selection_end,
4007 es->flags & EF_AFTER_WRAP);
4008 ShowCaret(wnd->hwndSelf);
4013 /*********************************************************************
4015 * WM_SETTEXT
4017 * NOTES
4018 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4019 * The modified flag is reset. No notifications are sent.
4021 * For single-line controls, reception of WM_SETTEXT triggers:
4022 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4025 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
4027 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
4028 if (text) {
4029 TRACE("\t'%p'\n", text);
4030 EDIT_EM_ReplaceSel(wnd, es, FALSE, text, !(es->style & ES_MULTILINE));
4031 } else {
4032 TRACE("\t<NULL>\n");
4033 EDIT_EM_ReplaceSel(wnd, es, FALSE, "", !(es->style & ES_MULTILINE));
4035 es->x_offset = 0;
4036 es->flags &= ~EF_MODIFIED;
4037 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
4038 EDIT_EM_ScrollCaret(wnd, es);
4040 if (es->flags & EF_UPDATE) {
4041 es->flags &= ~EF_UPDATE;
4042 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
4047 /*********************************************************************
4049 * WM_SIZE
4052 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
4054 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4055 RECT rc;
4056 SetRect(&rc, 0, 0, width, height);
4057 EDIT_SetRectNP(wnd, es, &rc);
4058 EDIT_UpdateText(wnd, NULL, TRUE);
4063 /*********************************************************************
4065 * WM_SYSKEYDOWN
4068 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
4070 if ((key == VK_BACK) && (key_data & 0x2000)) {
4071 if (EDIT_EM_CanUndo(wnd, es))
4072 EDIT_EM_Undo(wnd, es);
4073 return 0;
4074 } else if (key == VK_UP || key == VK_DOWN) {
4075 if (EDIT_CheckCombo(wnd, es, WM_SYSKEYDOWN, key, key_data))
4076 return 0;
4078 return DefWindowProcA(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4082 /*********************************************************************
4084 * WM_TIMER
4087 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
4089 if (es->region_posx < 0) {
4090 EDIT_MoveBackward(wnd, es, TRUE);
4091 } else if (es->region_posx > 0) {
4092 EDIT_MoveForward(wnd, es, TRUE);
4095 * FIXME: gotta do some vertical scrolling here, like
4096 * EDIT_EM_LineScroll(wnd, 0, 1);
4101 /*********************************************************************
4103 * EDIT_VScroll_Hack
4105 * 16 bit notepad needs this. Actually it is not _our_ hack,
4106 * it is notepad's. Notepad is sending us scrollbar messages with
4107 * undocumented parameters without us even having a scrollbar ... !?!?
4110 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4112 INT dy = 0;
4113 LRESULT ret = 0;
4115 if (!(es->flags & EF_VSCROLL_HACK)) {
4116 ERR("hacked WM_VSCROLL handler invoked\n");
4117 ERR(" if you are _not_ running 16 bit notepad, please report\n");
4118 ERR(" (this message is only displayed once per edit control)\n");
4119 es->flags |= EF_VSCROLL_HACK;
4122 switch (action) {
4123 case SB_LINEUP:
4124 case SB_LINEDOWN:
4125 case SB_PAGEUP:
4126 case SB_PAGEDOWN:
4127 EDIT_EM_Scroll(wnd, es, action);
4128 return 0;
4129 case SB_TOP:
4130 dy = -es->y_offset;
4131 break;
4132 case SB_BOTTOM:
4133 dy = es->line_count - 1 - es->y_offset;
4134 break;
4135 case SB_THUMBTRACK:
4136 es->flags |= EF_VSCROLL_TRACK;
4137 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4138 break;
4139 case SB_THUMBPOSITION:
4140 es->flags &= ~EF_VSCROLL_TRACK;
4141 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4142 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4143 break;
4144 case SB_ENDSCROLL:
4145 break;
4148 * FIXME : the next two are undocumented !
4149 * Are we doing the right thing ?
4150 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4151 * although it's also a regular control message.
4153 case EM_GETTHUMB16:
4154 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
4155 break;
4156 case EM_LINESCROLL16:
4157 dy = pos;
4158 break;
4160 default:
4161 ERR("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4162 return 0;
4164 if (dy)
4165 EDIT_EM_LineScroll(wnd, es, 0, dy);
4166 return ret;
4170 /*********************************************************************
4172 * WM_VSCROLL
4175 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4177 INT dy;
4179 if (!(es->style & ES_MULTILINE))
4180 return 0;
4182 if (!(es->style & ES_AUTOVSCROLL))
4183 return 0;
4185 if (!(es->style & WS_VSCROLL))
4186 return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
4188 dy = 0;
4189 switch (action) {
4190 case SB_LINEUP:
4191 case SB_LINEDOWN:
4192 case SB_PAGEUP:
4193 case SB_PAGEDOWN:
4194 EDIT_EM_Scroll(wnd, es, action);
4195 return 0;
4197 case SB_TOP:
4198 dy = -es->y_offset;
4199 break;
4200 case SB_BOTTOM:
4201 dy = es->line_count - 1 - es->y_offset;
4202 break;
4203 case SB_THUMBTRACK:
4204 es->flags |= EF_VSCROLL_TRACK;
4205 dy = pos - es->y_offset;
4206 break;
4207 case SB_THUMBPOSITION:
4208 es->flags &= ~EF_VSCROLL_TRACK;
4209 if (!(dy = pos - es->y_offset)) {
4210 SetScrollPos(wnd->hwndSelf, SB_VERT, pos, TRUE);
4211 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4213 break;
4214 case SB_ENDSCROLL:
4215 break;
4217 default:
4218 ERR("undocumented WM_VSCROLL action %d, please report\n",
4219 action);
4220 return 0;
4222 if (dy)
4223 EDIT_EM_LineScroll(wnd, es, 0, dy);
4224 return 0;
4228 /*********************************************************************
4230 * EDIT_UpdateText
4233 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase)
4235 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4237 /* EF_UPDATE will be turned off in paint */
4238 if (es->flags & EF_UPDATE)
4239 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
4241 InvalidateRect(wnd->hwndSelf, rc, bErase);