Sheri Steeves
[wine.git] / controls / edit.c
blob5f8548d605290b9715ca7ace220d5ed76d8b219c
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 "combo.h"
23 #include "local.h"
24 #include "selectors.h"
25 #include "debugtools.h"
26 #include "callback.h"
27 #include "tweak.h"
28 #include "winversion.h"
30 DEFAULT_DEBUG_CHANNEL(edit)
31 DECLARE_DEBUG_CHANNEL(combo)
32 DECLARE_DEBUG_CHANNEL(relay)
34 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
35 FIXME: BTW, new specs say 65535 (do you dare ???) */
36 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
37 #define BUFSTART_MULTI 1024 /* starting size */
38 #define BUFSTART_SINGLE 256 /* starting size */
39 #define GROWLENGTH 64 /* buffers grow by this much */
40 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
43 * extra flags for EDITSTATE.flags field
45 #define EF_MODIFIED 0x0001 /* text has been modified */
46 #define EF_FOCUSED 0x0002 /* we have input focus */
47 #define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
48 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
49 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
50 #define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
51 #define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
52 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
53 wrapped line, instead of in front of the next character */
54 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
56 typedef enum
58 END_0 = 0, /* line ends with terminating '\0' character */
59 END_WRAP, /* line is wrapped */
60 END_HARD, /* line ends with a hard return '\r\n' */
61 END_SOFT /* line ends with a soft return '\r\r\n' */
62 } LINE_END;
64 typedef struct tagLINEDEF {
65 INT length; /* bruto length of a line in bytes */
66 INT net_length; /* netto length of a line in visible characters */
67 LINE_END ending;
68 INT width; /* width of the line in pixels */
69 struct tagLINEDEF *next;
70 } LINEDEF;
72 typedef struct
74 HANDLE heap; /* our own heap */
75 LPSTR text; /* the actual contents of the control */
76 INT buffer_size; /* the size of the buffer */
77 INT buffer_limit; /* the maximum size to which the buffer may grow */
78 HFONT font; /* NULL means standard system font */
79 INT x_offset; /* scroll offset for multi lines this is in pixels
80 for single lines it's in characters */
81 INT line_height; /* height of a screen line in pixels */
82 INT char_width; /* average character width in pixels */
83 DWORD style; /* sane version of wnd->dwStyle */
84 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
85 INT undo_insert_count; /* number of characters inserted in sequence */
86 INT undo_position; /* character index of the insertion and deletion */
87 LPSTR undo_text; /* deleted text */
88 INT undo_buffer_size; /* size of the deleted text buffer */
89 INT selection_start; /* == selection_end if no selection */
90 INT selection_end; /* == current caret position */
91 CHAR password_char; /* == 0 if no password char, and for multi line controls */
92 INT left_margin; /* in pixels */
93 INT right_margin; /* in pixels */
94 RECT format_rect;
95 INT region_posx; /* Position of cursor relative to region: */
96 INT region_posy; /* -1: to left, 0: within, 1: to right */
97 EDITWORDBREAKPROC16 word_break_proc16;
98 EDITWORDBREAKPROCA word_break_proc32A;
99 INT line_count; /* number of lines */
100 INT y_offset; /* scroll offset in number of lines */
101 BOOL bCaptureState; /* flag indicating whether mouse was captured */
102 BOOL bEnableState; /* flag keeping the enable state */
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);
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_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
242 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs);
243 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
244 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
245 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus);
246 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
247 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
248 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
249 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
250 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
251 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
254 /*********************************************************************
256 * EM_CANUNDO
259 static inline BOOL EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es)
261 return (es->undo_insert_count || lstrlenA(es->undo_text));
265 /*********************************************************************
267 * EM_EMPTYUNDOBUFFER
270 static inline void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es)
272 es->undo_insert_count = 0;
273 *es->undo_text = '\0';
277 /*********************************************************************
279 * WM_CLEAR
282 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
284 EDIT_EM_ReplaceSel(wnd, es, TRUE, "");
288 /*********************************************************************
290 * WM_CUT
293 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
295 EDIT_WM_Copy(wnd, es);
296 EDIT_WM_Clear(wnd, es);
300 /*********************************************************************
302 * EditWndProc()
304 * The messages are in the order of the actual integer values
305 * (which can be found in include/windows.h)
306 * Whereever possible the 16 bit versions are converted to
307 * the 32 bit ones, so that we can 'fall through' to the
308 * helper functions. These are mostly 32 bit (with a few
309 * exceptions, clearly indicated by a '16' extension to their
310 * names).
313 LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
314 WPARAM wParam, LPARAM lParam )
316 WND *wnd = WIN_FindWndPtr(hwnd);
317 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
318 LRESULT result = 0;
320 switch (msg) {
321 case WM_DESTROY:
322 DPRINTF_EDIT_MSG32("WM_DESTROY");
323 EDIT_WM_Destroy(wnd, es);
324 result = 0;
325 goto END;
327 case WM_NCCREATE:
328 DPRINTF_EDIT_MSG32("WM_NCCREATE");
329 result = EDIT_WM_NCCreate(wnd, (LPCREATESTRUCTA)lParam);
330 goto END;
333 if (!es)
335 result = DefWindowProcA(hwnd, msg, wParam, lParam);
336 goto END;
340 EDIT_LockBuffer(wnd, es);
341 switch (msg) {
342 case EM_GETSEL16:
343 DPRINTF_EDIT_MSG16("EM_GETSEL");
344 wParam = 0;
345 lParam = 0;
346 /* fall through */
347 case EM_GETSEL:
348 DPRINTF_EDIT_MSG32("EM_GETSEL");
349 result = EDIT_EM_GetSel(wnd, es, (LPUINT)wParam, (LPUINT)lParam);
350 break;
352 case EM_SETSEL16:
353 DPRINTF_EDIT_MSG16("EM_SETSEL");
354 if (SLOWORD(lParam) == -1)
355 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
356 else
357 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
358 if (!wParam)
359 EDIT_EM_ScrollCaret(wnd, es);
360 result = 1;
361 break;
362 case EM_SETSEL:
363 DPRINTF_EDIT_MSG32("EM_SETSEL");
364 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
365 EDIT_EM_ScrollCaret(wnd, es);
366 result = 1;
367 break;
369 case EM_GETRECT16:
370 DPRINTF_EDIT_MSG16("EM_GETRECT");
371 if (lParam)
372 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
373 break;
374 case EM_GETRECT:
375 DPRINTF_EDIT_MSG32("EM_GETRECT");
376 if (lParam)
377 CopyRect((LPRECT)lParam, &es->format_rect);
378 break;
380 case EM_SETRECT16:
381 DPRINTF_EDIT_MSG16("EM_SETRECT");
382 if ((es->style & ES_MULTILINE) && lParam) {
383 RECT rc;
384 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
385 EDIT_SetRectNP(wnd, es, &rc);
386 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
388 break;
389 case EM_SETRECT:
390 DPRINTF_EDIT_MSG32("EM_SETRECT");
391 if ((es->style & ES_MULTILINE) && lParam) {
392 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
393 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
395 break;
397 case EM_SETRECTNP16:
398 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
399 if ((es->style & ES_MULTILINE) && lParam) {
400 RECT rc;
401 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
402 EDIT_SetRectNP(wnd, es, &rc);
404 break;
405 case EM_SETRECTNP:
406 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
407 if ((es->style & ES_MULTILINE) && lParam)
408 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
409 break;
411 case EM_SCROLL16:
412 DPRINTF_EDIT_MSG16("EM_SCROLL");
413 /* fall through */
414 case EM_SCROLL:
415 DPRINTF_EDIT_MSG32("EM_SCROLL");
416 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
417 break;
419 case EM_LINESCROLL16:
420 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
421 wParam = (WPARAM)(INT)SHIWORD(lParam);
422 lParam = (LPARAM)(INT)SLOWORD(lParam);
423 /* fall through */
424 case EM_LINESCROLL:
425 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
426 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
427 break;
429 case EM_SCROLLCARET16:
430 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
431 /* fall through */
432 case EM_SCROLLCARET:
433 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
434 EDIT_EM_ScrollCaret(wnd, es);
435 result = 1;
436 break;
438 case EM_GETMODIFY16:
439 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
440 /* fall through */
441 case EM_GETMODIFY:
442 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
443 result = ((es->flags & EF_MODIFIED) != 0);
444 break;
446 case EM_SETMODIFY16:
447 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
448 /* fall through */
449 case EM_SETMODIFY:
450 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
451 if (wParam)
452 es->flags |= EF_MODIFIED;
453 else
454 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
455 break;
457 case EM_GETLINECOUNT16:
458 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
459 /* fall through */
460 case EM_GETLINECOUNT:
461 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
462 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
463 break;
465 case EM_LINEINDEX16:
466 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
467 if ((INT16)wParam == -1)
468 wParam = (WPARAM)-1;
469 /* fall through */
470 case EM_LINEINDEX:
471 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
472 result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT)wParam);
473 break;
475 case EM_SETHANDLE16:
476 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
477 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
478 break;
479 case EM_SETHANDLE:
480 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
481 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
482 break;
484 case EM_GETHANDLE16:
485 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
486 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
487 break;
488 case EM_GETHANDLE:
489 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
490 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
491 break;
493 case EM_GETTHUMB16:
494 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
495 /* fall through */
496 case EM_GETTHUMB:
497 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
498 result = EDIT_EM_GetThumb(wnd, es);
499 break;
501 /* messages 0x00bf and 0x00c0 missing from specs */
503 case WM_USER+15:
504 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
505 /* fall through */
506 case 0x00bf:
507 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
508 result = DefWindowProcA(hwnd, msg, wParam, lParam);
509 break;
511 case WM_USER+16:
512 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
513 /* fall through */
514 case 0x00c0:
515 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
516 result = DefWindowProcA(hwnd, msg, wParam, lParam);
517 break;
519 case EM_LINELENGTH16:
520 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
521 /* fall through */
522 case EM_LINELENGTH:
523 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
524 result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT)wParam);
525 break;
527 case EM_REPLACESEL16:
528 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
529 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
530 /* fall through */
531 case EM_REPLACESEL:
532 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
533 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, (LPCSTR)lParam);
534 result = 1;
535 break;
537 /* message 0x00c3 missing from specs */
539 case WM_USER+19:
540 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
541 /* fall through */
542 case 0x00c3:
543 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
544 result = DefWindowProcA(hwnd, msg, wParam, lParam);
545 break;
547 case EM_GETLINE16:
548 DPRINTF_EDIT_MSG16("EM_GETLINE");
549 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
550 /* fall through */
551 case EM_GETLINE:
552 DPRINTF_EDIT_MSG32("EM_GETLINE");
553 result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT)wParam, (LPSTR)lParam);
554 break;
556 case EM_LIMITTEXT16:
557 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
558 /* fall through */
559 case EM_SETLIMITTEXT:
560 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
561 EDIT_EM_SetLimitText(wnd, es, (INT)wParam);
562 break;
564 case EM_CANUNDO16:
565 DPRINTF_EDIT_MSG16("EM_CANUNDO");
566 /* fall through */
567 case EM_CANUNDO:
568 DPRINTF_EDIT_MSG32("EM_CANUNDO");
569 result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
570 break;
572 case EM_UNDO16:
573 DPRINTF_EDIT_MSG16("EM_UNDO");
574 /* fall through */
575 case EM_UNDO:
576 /* fall through */
577 case WM_UNDO:
578 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
579 result = (LRESULT)EDIT_EM_Undo(wnd, es);
580 break;
582 case EM_FMTLINES16:
583 DPRINTF_EDIT_MSG16("EM_FMTLINES");
584 /* fall through */
585 case EM_FMTLINES:
586 DPRINTF_EDIT_MSG32("EM_FMTLINES");
587 result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL)wParam);
588 break;
590 case EM_LINEFROMCHAR16:
591 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
592 /* fall through */
593 case EM_LINEFROMCHAR:
594 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
595 result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT)wParam);
596 break;
598 /* message 0x00ca missing from specs */
600 case WM_USER+26:
601 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
602 /* fall through */
603 case 0x00ca:
604 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
605 result = DefWindowProcA(hwnd, msg, wParam, lParam);
606 break;
608 case EM_SETTABSTOPS16:
609 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
610 result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
611 break;
612 case EM_SETTABSTOPS:
613 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
614 result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT)wParam, (LPINT)lParam);
615 break;
617 case EM_SETPASSWORDCHAR16:
618 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
619 /* fall through */
620 case EM_SETPASSWORDCHAR:
621 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
622 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
623 break;
625 case EM_EMPTYUNDOBUFFER16:
626 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
627 /* fall through */
628 case EM_EMPTYUNDOBUFFER:
629 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
630 EDIT_EM_EmptyUndoBuffer(wnd, es);
631 break;
633 case EM_GETFIRSTVISIBLELINE16:
634 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
635 result = es->y_offset;
636 break;
637 case EM_GETFIRSTVISIBLELINE:
638 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
639 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
640 break;
642 case EM_SETREADONLY16:
643 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
644 /* fall through */
645 case EM_SETREADONLY:
646 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
647 if (wParam) {
648 wnd->dwStyle |= ES_READONLY;
649 es->style |= ES_READONLY;
650 } else {
651 wnd->dwStyle &= ~ES_READONLY;
652 es->style &= ~ES_READONLY;
654 result = 1;
655 break;
657 case EM_SETWORDBREAKPROC16:
658 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
659 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
660 break;
661 case EM_SETWORDBREAKPROC:
662 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
663 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROCA)lParam);
664 break;
666 case EM_GETWORDBREAKPROC16:
667 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
668 result = (LRESULT)es->word_break_proc16;
669 break;
670 case EM_GETWORDBREAKPROC:
671 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
672 result = (LRESULT)es->word_break_proc32A;
673 break;
675 case EM_GETPASSWORDCHAR16:
676 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
677 /* fall through */
678 case EM_GETPASSWORDCHAR:
679 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
680 result = es->password_char;
681 break;
683 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
685 case EM_SETMARGINS:
686 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
687 EDIT_EM_SetMargins(wnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
688 break;
690 case EM_GETMARGINS:
691 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
692 result = MAKELONG(es->left_margin, es->right_margin);
693 break;
695 case EM_GETLIMITTEXT:
696 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
697 result = es->buffer_limit;
698 break;
700 case EM_POSFROMCHAR:
701 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
702 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
703 break;
705 case EM_CHARFROMPOS:
706 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
707 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
708 break;
710 case WM_GETDLGCODE:
711 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
712 result = (es->style & ES_MULTILINE) ?
713 DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
714 DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
715 break;
717 case WM_CHAR:
718 DPRINTF_EDIT_MSG32("WM_CHAR");
719 EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
720 break;
722 case WM_CLEAR:
723 DPRINTF_EDIT_MSG32("WM_CLEAR");
724 EDIT_WM_Clear(wnd, es);
725 break;
727 case WM_COMMAND:
728 DPRINTF_EDIT_MSG32("WM_COMMAND");
729 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
730 break;
732 case WM_CONTEXTMENU:
733 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
734 EDIT_WM_ContextMenu(wnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
735 break;
737 case WM_COPY:
738 DPRINTF_EDIT_MSG32("WM_COPY");
739 EDIT_WM_Copy(wnd, es);
740 break;
742 case WM_CREATE:
743 DPRINTF_EDIT_MSG32("WM_CREATE");
744 result = EDIT_WM_Create(wnd, es, (LPCREATESTRUCTA)lParam);
745 break;
747 case WM_CUT:
748 DPRINTF_EDIT_MSG32("WM_CUT");
749 EDIT_WM_Cut(wnd, es);
750 break;
752 case WM_ENABLE:
753 DPRINTF_EDIT_MSG32("WM_ENABLE");
754 es->bEnableState = (BOOL) wParam;
755 InvalidateRect(hwnd, NULL, TRUE);
756 break;
758 case WM_ERASEBKGND:
759 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
760 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
761 break;
763 case WM_GETFONT:
764 DPRINTF_EDIT_MSG32("WM_GETFONT");
765 result = (LRESULT)es->font;
766 break;
768 case WM_GETTEXT:
769 DPRINTF_EDIT_MSG32("WM_GETTEXT");
770 result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT)wParam, (LPSTR)lParam);
771 break;
773 case WM_GETTEXTLENGTH:
774 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
775 result = lstrlenA(es->text);
776 break;
778 case WM_HSCROLL:
779 DPRINTF_EDIT_MSG32("WM_HSCROLL");
780 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
781 break;
783 case WM_KEYDOWN:
784 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
785 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
786 break;
788 case WM_KILLFOCUS:
789 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
790 result = EDIT_WM_KillFocus(wnd, es, (HWND)wParam);
791 break;
793 case WM_LBUTTONDBLCLK:
794 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
795 result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
796 break;
798 case WM_LBUTTONDOWN:
799 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
800 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
801 break;
803 case WM_LBUTTONUP:
804 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
805 result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
806 break;
808 case WM_MOUSEACTIVATE:
810 * FIXME: maybe DefWindowProc() screws up, but it seems that
811 * modalless dialog boxes need this. If we don't do this, the focus
812 * will _not_ be set by DefWindowProc() for edit controls in a
813 * modalless dialog box ???
815 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
816 SetFocus(wnd->hwndSelf);
817 result = MA_ACTIVATE;
818 break;
820 case WM_MOUSEMOVE:
822 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
824 result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
825 break;
827 case WM_PAINT:
828 DPRINTF_EDIT_MSG32("WM_PAINT");
829 EDIT_WM_Paint(wnd, es, wParam);
830 break;
832 case WM_PASTE:
833 DPRINTF_EDIT_MSG32("WM_PASTE");
834 EDIT_WM_Paste(wnd, es);
835 break;
837 case WM_SETFOCUS:
838 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
839 EDIT_WM_SetFocus(wnd, es, (HWND)wParam);
840 break;
842 case WM_SETFONT:
843 DPRINTF_EDIT_MSG32("WM_SETFONT");
844 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
845 break;
847 case WM_SETTEXT:
848 DPRINTF_EDIT_MSG32("WM_SETTEXT");
849 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
850 result = TRUE;
851 break;
853 case WM_SIZE:
854 DPRINTF_EDIT_MSG32("WM_SIZE");
855 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
856 break;
858 case WM_SYSKEYDOWN:
859 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
860 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
861 break;
863 case WM_TIMER:
864 DPRINTF_EDIT_MSG32("WM_TIMER");
865 EDIT_WM_Timer(wnd, es, (INT)wParam, (TIMERPROC)lParam);
866 break;
868 case WM_VSCROLL:
869 DPRINTF_EDIT_MSG32("WM_VSCROLL");
870 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
871 break;
873 case WM_MOUSEWHEEL:
875 short gcWheelDelta = 0;
876 UINT pulScrollLines = 3;
877 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
879 if (wParam & (MK_SHIFT | MK_CONTROL)) {
880 result = DefWindowProcA(hwnd, msg, wParam, lParam);
881 break;
883 gcWheelDelta -= (short) HIWORD(wParam);
884 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
886 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
887 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
888 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
891 break;
892 default:
893 result = DefWindowProcA(hwnd, msg, wParam, lParam);
894 break;
896 EDIT_UnlockBuffer(wnd, es, FALSE);
897 END:
898 WIN_ReleaseWndPtr(wnd);
899 return result;
904 /*********************************************************************
906 * EDIT_BuildLineDefs_ML
908 * Build linked list of text lines.
909 * Lines can end with '\0' (last line), a character (if it is wrapped),
910 * a soft return '\r\r\n' or a hard return '\r\n'
913 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
915 HDC dc;
916 HFONT old_font = 0;
917 LPSTR start, cp;
918 INT fw;
919 LINEDEF *current_def;
920 LINEDEF **previous_next;
922 current_def = es->first_line_def;
923 do {
924 LINEDEF *next_def = current_def->next;
925 HeapFree(es->heap, 0, current_def);
926 current_def = next_def;
927 } while (current_def);
928 es->line_count = 0;
929 es->text_width = 0;
931 dc = GetDC(wnd->hwndSelf);
932 if (es->font)
933 old_font = SelectObject(dc, es->font);
935 fw = es->format_rect.right - es->format_rect.left;
936 start = es->text;
937 previous_next = &es->first_line_def;
938 do {
939 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
940 current_def->next = NULL;
941 cp = start;
942 while (*cp) {
943 if ((*cp == '\r') && (*(cp + 1) == '\n'))
944 break;
945 cp++;
947 if (!(*cp)) {
948 current_def->ending = END_0;
949 current_def->net_length = lstrlenA(start);
950 } else if ((cp > start) && (*(cp - 1) == '\r')) {
951 current_def->ending = END_SOFT;
952 current_def->net_length = cp - start - 1;
953 } else {
954 current_def->ending = END_HARD;
955 current_def->net_length = cp - start;
957 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
958 start, current_def->net_length,
959 es->tabs_count, es->tabs));
960 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
961 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
962 INT next = 0;
963 INT prev;
964 do {
965 prev = next;
966 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
967 prev + 1, current_def->net_length, WB_RIGHT);
968 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
969 start, next, es->tabs_count, es->tabs));
970 } while (current_def->width <= fw);
971 if (!prev) {
972 next = 0;
973 do {
974 prev = next;
975 next++;
976 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
977 start, next, es->tabs_count, es->tabs));
978 } while (current_def->width <= fw);
979 if (!prev)
980 prev = 1;
982 current_def->net_length = prev;
983 current_def->ending = END_WRAP;
984 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc, start,
985 current_def->net_length, es->tabs_count, es->tabs));
987 switch (current_def->ending) {
988 case END_SOFT:
989 current_def->length = current_def->net_length + 3;
990 break;
991 case END_HARD:
992 current_def->length = current_def->net_length + 2;
993 break;
994 case END_WRAP:
995 case END_0:
996 current_def->length = current_def->net_length;
997 break;
999 es->text_width = max(es->text_width, current_def->width);
1000 start += current_def->length;
1001 *previous_next = current_def;
1002 previous_next = &current_def->next;
1003 es->line_count++;
1004 } while (current_def->ending != END_0);
1005 if (es->font)
1006 SelectObject(dc, old_font);
1007 ReleaseDC(wnd->hwndSelf, dc);
1011 /*********************************************************************
1013 * EDIT_CallWordBreakProc
1015 * Call appropriate WordBreakProc (internal or external).
1017 * Note: The "start" argument should always be an index refering
1018 * to es->text. The actual wordbreak proc might be
1019 * 16 bit, so we can't always pass any 32 bit LPSTR.
1020 * Hence we assume that es->text is the buffer that holds
1021 * the string under examination (we can decide this for ourselves).
1024 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
1026 if (es->word_break_proc16) {
1027 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
1028 SEGPTR segptr = LocalLock16(hloc16);
1029 INT ret = (INT)Callbacks->CallWordBreakProc(es->word_break_proc16,
1030 segptr + start, index, count, action);
1031 LocalUnlock16(hloc16);
1032 return ret;
1034 else if (es->word_break_proc32A)
1036 TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
1037 es->word_break_proc32A, es->text + start, index,
1038 count, action );
1039 return (INT)es->word_break_proc32A( es->text + start, index,
1040 count, action );
1042 else
1043 return EDIT_WordBreakProc(es->text + start, index, count, action);
1047 /*********************************************************************
1049 * EDIT_CharFromPos
1051 * Beware: This is not the function called on EM_CHARFROMPOS
1052 * The position _can_ be outside the formatting / client
1053 * rectangle
1054 * The return value is only the character index
1057 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1059 INT index;
1060 HDC dc;
1061 HFONT old_font = 0;
1063 if (es->style & ES_MULTILINE) {
1064 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1065 INT line_index = 0;
1066 LINEDEF *line_def = es->first_line_def;
1067 INT low, high;
1068 while ((line > 0) && line_def->next) {
1069 line_index += line_def->length;
1070 line_def = line_def->next;
1071 line--;
1073 x += es->x_offset - es->format_rect.left;
1074 if (x >= line_def->width) {
1075 if (after_wrap)
1076 *after_wrap = (line_def->ending == END_WRAP);
1077 return line_index + line_def->net_length;
1079 if (x <= 0) {
1080 if (after_wrap)
1081 *after_wrap = FALSE;
1082 return line_index;
1084 dc = GetDC(wnd->hwndSelf);
1085 if (es->font)
1086 old_font = SelectObject(dc, es->font);
1087 low = line_index + 1;
1088 high = line_index + line_def->net_length + 1;
1089 while (low < high - 1)
1091 INT mid = (low + high) / 2;
1092 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1093 else low = mid;
1095 index = low;
1097 if (after_wrap)
1098 *after_wrap = ((index == line_index + line_def->net_length) &&
1099 (line_def->ending == END_WRAP));
1100 } else {
1101 LPSTR text;
1102 SIZE size;
1103 if (after_wrap)
1104 *after_wrap = FALSE;
1105 x -= es->format_rect.left;
1106 if (!x)
1107 return es->x_offset;
1108 text = EDIT_GetPasswordPointer_SL(wnd, es);
1109 dc = GetDC(wnd->hwndSelf);
1110 if (es->font)
1111 old_font = SelectObject(dc, es->font);
1112 if (x < 0)
1114 INT low = 0;
1115 INT high = es->x_offset;
1116 while (low < high - 1)
1118 INT mid = (low + high) / 2;
1119 GetTextExtentPoint32A( dc, text + mid,
1120 es->x_offset - mid, &size );
1121 if (size.cx > -x) low = mid;
1122 else high = mid;
1124 index = low;
1126 else
1128 INT low = es->x_offset;
1129 INT high = lstrlenA(es->text) + 1;
1130 while (low < high - 1)
1132 INT mid = (low + high) / 2;
1133 GetTextExtentPoint32A( dc, text + es->x_offset,
1134 mid - es->x_offset, &size );
1135 if (size.cx > x) high = mid;
1136 else low = mid;
1138 index = low;
1140 if (es->style & ES_PASSWORD)
1141 HeapFree(es->heap, 0 ,text);
1143 if (es->font)
1144 SelectObject(dc, old_font);
1145 ReleaseDC(wnd->hwndSelf, dc);
1146 return index;
1150 /*********************************************************************
1152 * EDIT_ConfinePoint
1154 * adjusts the point to be within the formatting rectangle
1155 * (so CharFromPos returns the nearest _visible_ character)
1158 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT x, LPINT y)
1160 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1161 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1165 /*********************************************************************
1167 * EDIT_GetLineRect
1169 * Calculates the bounding rectangle for a line from a starting
1170 * column to an ending column.
1173 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1175 INT line_index = EDIT_EM_LineIndex(wnd, es, line);
1177 if (es->style & ES_MULTILINE)
1178 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1179 else
1180 rc->top = es->format_rect.top;
1181 rc->bottom = rc->top + es->line_height;
1182 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1183 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1187 /*********************************************************************
1189 * EDIT_GetPasswordPointer_SL
1191 * note: caller should free the (optionally) allocated buffer
1194 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
1196 if (es->style & ES_PASSWORD) {
1197 INT len = lstrlenA(es->text);
1198 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1199 RtlFillMemory(text, len, es->password_char);
1200 text[len] = '\0';
1201 return text;
1202 } else
1203 return es->text;
1207 /*********************************************************************
1209 * EDIT_LockBuffer
1211 * This acts as a LOCAL_Lock(), but it locks only once. This way
1212 * you can call it whenever you like, without unlocking.
1215 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1217 if (!es) {
1218 ERR("no EDITSTATE ... please report\n");
1219 return;
1221 if (!(es->style & ES_MULTILINE))
1222 return;
1223 if (!es->text) {
1224 if (es->hloc32)
1225 es->text = LocalLock(es->hloc32);
1226 else if (es->hloc16)
1227 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1228 else {
1229 ERR("no buffer ... please report\n");
1230 return;
1233 es->lock_count++;
1237 /*********************************************************************
1239 * EDIT_SL_InvalidateText
1241 * Called from EDIT_InvalidateText().
1242 * Does the job for single-line controls only.
1245 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1247 RECT line_rect;
1248 RECT rc;
1250 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1251 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1252 InvalidateRect(wnd->hwndSelf, &rc, FALSE);
1256 /*********************************************************************
1258 * EDIT_ML_InvalidateText
1260 * Called from EDIT_InvalidateText().
1261 * Does the job for multi-line controls only.
1264 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1266 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1267 INT sl = EDIT_EM_LineFromChar(wnd, es, start);
1268 INT el = EDIT_EM_LineFromChar(wnd, es, end);
1269 INT sc;
1270 INT ec;
1271 RECT rc1;
1272 RECT rcWnd;
1273 RECT rcLine;
1274 RECT rcUpdate;
1275 INT l;
1277 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1278 return;
1280 sc = start - EDIT_EM_LineIndex(wnd, es, sl);
1281 ec = end - EDIT_EM_LineIndex(wnd, es, el);
1282 if (sl < es->y_offset) {
1283 sl = es->y_offset;
1284 sc = 0;
1286 if (el > es->y_offset + vlc) {
1287 el = es->y_offset + vlc;
1288 ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
1290 GetClientRect(wnd->hwndSelf, &rc1);
1291 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1292 if (sl == el) {
1293 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1294 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1295 InvalidateRect(wnd->hwndSelf, &rcUpdate, FALSE);
1296 } else {
1297 EDIT_GetLineRect(wnd, es, sl, sc,
1298 EDIT_EM_LineLength(wnd, es,
1299 EDIT_EM_LineIndex(wnd, es, sl)),
1300 &rcLine);
1301 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1302 InvalidateRect(wnd->hwndSelf, &rcUpdate, FALSE);
1303 for (l = sl + 1 ; l < el ; l++) {
1304 EDIT_GetLineRect(wnd, es, l, 0,
1305 EDIT_EM_LineLength(wnd, es,
1306 EDIT_EM_LineIndex(wnd, es, l)),
1307 &rcLine);
1308 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1309 InvalidateRect(wnd->hwndSelf, &rcUpdate, FALSE);
1311 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1312 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1313 InvalidateRect(wnd->hwndSelf, &rcUpdate, FALSE);
1318 /*********************************************************************
1320 * EDIT_InvalidateText
1322 * Invalidate the text from offset start upto, but not including,
1323 * offset end. Useful for (re)painting the selection.
1324 * Regions outside the linewidth are not invalidated.
1325 * end == -1 means end == TextLength.
1326 * start and end need not be ordered.
1329 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1331 if (end == start)
1332 return;
1334 if (end == -1)
1335 end = lstrlenA(es->text);
1337 ORDER_INT(start, end);
1339 if (es->style & ES_MULTILINE)
1340 EDIT_ML_InvalidateText(wnd, es, start, end);
1341 else
1342 EDIT_SL_InvalidateText(wnd, es, start, end);
1346 /*********************************************************************
1348 * EDIT_MakeFit
1350 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1353 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT size)
1355 HLOCAL hNew32;
1356 HLOCAL16 hNew16;
1358 if (size <= es->buffer_size)
1359 return TRUE;
1360 if (size > es->buffer_limit) {
1361 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1362 return FALSE;
1364 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1365 if (size > es->buffer_limit)
1366 size = es->buffer_limit;
1368 TRACE("trying to ReAlloc to %d+1\n", size);
1370 EDIT_UnlockBuffer(wnd, es, TRUE);
1371 if (es->text) {
1372 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1373 es->buffer_size = min(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1374 else
1375 es->buffer_size = 0;
1376 } else if (es->hloc32) {
1377 if ((hNew32 = LocalReAlloc(es->hloc32, size + 1, 0))) {
1378 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1379 es->hloc32 = hNew32;
1380 es->buffer_size = min(LocalSize(es->hloc32) - 1, es->buffer_limit);
1382 } else if (es->hloc16) {
1383 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1384 TRACE("Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1385 es->hloc16 = hNew16;
1386 es->buffer_size = min(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1389 if (es->buffer_size < size) {
1390 EDIT_LockBuffer(wnd, es);
1391 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1392 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1393 return FALSE;
1394 } else {
1395 EDIT_LockBuffer(wnd, es);
1396 TRACE("We now have %d+1\n", es->buffer_size);
1397 return TRUE;
1402 /*********************************************************************
1404 * EDIT_MakeUndoFit
1406 * Try to fit size + 1 bytes in the undo buffer.
1409 static BOOL EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT size)
1411 if (size <= es->undo_buffer_size)
1412 return TRUE;
1413 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1415 TRACE("trying to ReAlloc to %d+1\n", size);
1417 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1418 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1419 if (es->undo_buffer_size < size) {
1420 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1421 return FALSE;
1423 return TRUE;
1425 return FALSE;
1429 /*********************************************************************
1431 * EDIT_MoveBackward
1434 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1436 INT e = es->selection_end;
1438 if (e) {
1439 e--;
1440 if ((es->style & ES_MULTILINE) && e &&
1441 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1442 e--;
1443 if (e && (es->text[e - 1] == '\r'))
1444 e--;
1447 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1448 EDIT_EM_ScrollCaret(wnd, es);
1452 /*********************************************************************
1454 * EDIT_MoveDown_ML
1456 * Only for multi line controls
1457 * Move the caret one line down, on a column with the nearest
1458 * x coordinate on the screen (might be a different column).
1461 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1463 INT s = es->selection_start;
1464 INT e = es->selection_end;
1465 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1466 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1467 INT x = SLOWORD(pos);
1468 INT y = SHIWORD(pos);
1470 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1471 if (!extend)
1472 s = e;
1473 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1474 EDIT_EM_ScrollCaret(wnd, es);
1478 /*********************************************************************
1480 * EDIT_MoveEnd
1483 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1485 BOOL after_wrap = FALSE;
1486 INT e;
1488 /* Pass a high value in x to make sure of receiving the en of the line */
1489 if (es->style & ES_MULTILINE)
1490 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1491 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1492 else
1493 e = lstrlenA(es->text);
1494 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1495 EDIT_EM_ScrollCaret(wnd, es);
1499 /*********************************************************************
1501 * EDIT_MoveForward
1504 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1506 INT e = es->selection_end;
1508 if (es->text[e]) {
1509 e++;
1510 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1511 if (es->text[e] == '\n')
1512 e++;
1513 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1514 e += 2;
1517 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1518 EDIT_EM_ScrollCaret(wnd, es);
1522 /*********************************************************************
1524 * EDIT_MoveHome
1526 * Home key: move to beginning of line.
1529 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1531 INT e;
1533 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1534 if (es->style & ES_MULTILINE)
1535 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1536 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1537 else
1538 e = 0;
1539 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1540 EDIT_EM_ScrollCaret(wnd, es);
1544 /*********************************************************************
1546 * EDIT_MovePageDown_ML
1548 * Only for multi line controls
1549 * Move the caret one page down, on a column with the nearest
1550 * x coordinate on the screen (might be a different column).
1553 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1555 INT s = es->selection_start;
1556 INT e = es->selection_end;
1557 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1558 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1559 INT x = SLOWORD(pos);
1560 INT y = SHIWORD(pos);
1562 e = EDIT_CharFromPos(wnd, es, x,
1563 y + (es->format_rect.bottom - es->format_rect.top),
1564 &after_wrap);
1565 if (!extend)
1566 s = e;
1567 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1568 EDIT_EM_ScrollCaret(wnd, es);
1572 /*********************************************************************
1574 * EDIT_MovePageUp_ML
1576 * Only for multi line controls
1577 * Move the caret one page up, on a column with the nearest
1578 * x coordinate on the screen (might be a different column).
1581 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1583 INT s = es->selection_start;
1584 INT e = es->selection_end;
1585 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1586 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1587 INT x = SLOWORD(pos);
1588 INT y = SHIWORD(pos);
1590 e = EDIT_CharFromPos(wnd, es, x,
1591 y - (es->format_rect.bottom - es->format_rect.top),
1592 &after_wrap);
1593 if (!extend)
1594 s = e;
1595 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1596 EDIT_EM_ScrollCaret(wnd, es);
1600 /*********************************************************************
1602 * EDIT_MoveUp_ML
1604 * Only for multi line controls
1605 * Move the caret one line up, on a column with the nearest
1606 * x coordinate on the screen (might be a different column).
1609 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1611 INT s = es->selection_start;
1612 INT e = es->selection_end;
1613 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1614 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1615 INT x = SLOWORD(pos);
1616 INT y = SHIWORD(pos);
1618 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1619 if (!extend)
1620 s = e;
1621 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1622 EDIT_EM_ScrollCaret(wnd, es);
1626 /*********************************************************************
1628 * EDIT_MoveWordBackward
1631 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1633 INT s = es->selection_start;
1634 INT e = es->selection_end;
1635 INT l;
1636 INT ll;
1637 INT li;
1639 l = EDIT_EM_LineFromChar(wnd, es, e);
1640 ll = EDIT_EM_LineLength(wnd, es, e);
1641 li = EDIT_EM_LineIndex(wnd, es, l);
1642 if (e - li == 0) {
1643 if (l) {
1644 li = EDIT_EM_LineIndex(wnd, es, l - 1);
1645 e = li + EDIT_EM_LineLength(wnd, es, li);
1647 } else {
1648 e = li + (INT)EDIT_CallWordBreakProc(wnd, es,
1649 li, e - li, ll, WB_LEFT);
1651 if (!extend)
1652 s = e;
1653 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1654 EDIT_EM_ScrollCaret(wnd, es);
1658 /*********************************************************************
1660 * EDIT_MoveWordForward
1663 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
1665 INT s = es->selection_start;
1666 INT e = es->selection_end;
1667 INT l;
1668 INT ll;
1669 INT li;
1671 l = EDIT_EM_LineFromChar(wnd, es, e);
1672 ll = EDIT_EM_LineLength(wnd, es, e);
1673 li = EDIT_EM_LineIndex(wnd, es, l);
1674 if (e - li == ll) {
1675 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1676 e = EDIT_EM_LineIndex(wnd, es, l + 1);
1677 } else {
1678 e = li + EDIT_CallWordBreakProc(wnd, es,
1679 li, e - li + 1, ll, WB_RIGHT);
1681 if (!extend)
1682 s = e;
1683 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1684 EDIT_EM_ScrollCaret(wnd, es);
1688 /*********************************************************************
1690 * EDIT_PaintLine
1693 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1695 INT s = es->selection_start;
1696 INT e = es->selection_end;
1697 INT li;
1698 INT ll;
1699 INT x;
1700 INT y;
1701 LRESULT pos;
1703 if (es->style & ES_MULTILINE) {
1704 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1705 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1706 return;
1707 } else if (line)
1708 return;
1710 TRACE("line=%d\n", line);
1712 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
1713 x = SLOWORD(pos);
1714 y = SHIWORD(pos);
1715 li = EDIT_EM_LineIndex(wnd, es, line);
1716 ll = EDIT_EM_LineLength(wnd, es, li);
1717 s = es->selection_start;
1718 e = es->selection_end;
1719 ORDER_INT(s, e);
1720 s = min(li + ll, max(li, s));
1721 e = min(li + ll, max(li, e));
1722 if (rev && (s != e) &&
1723 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1724 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
1725 x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
1726 x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
1727 } else
1728 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
1732 /*********************************************************************
1734 * EDIT_PaintText
1737 static INT EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1739 COLORREF BkColor;
1740 COLORREF TextColor;
1741 INT ret;
1742 INT li;
1743 SIZE size;
1745 if (!count)
1746 return 0;
1747 BkColor = GetBkColor(dc);
1748 TextColor = GetTextColor(dc);
1749 if (rev) {
1750 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1751 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1753 li = EDIT_EM_LineIndex(wnd, es, line);
1754 if (es->style & ES_MULTILINE) {
1755 ret = (INT)LOWORD(TabbedTextOutA(dc, x, y, es->text + li + col, count,
1756 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1757 } else {
1758 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
1759 TextOutA(dc, x, y, text + li + col, count);
1760 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1761 ret = size.cx;
1762 if (es->style & ES_PASSWORD)
1763 HeapFree(es->heap, 0, text);
1765 if (rev) {
1766 SetBkColor(dc, BkColor);
1767 SetTextColor(dc, TextColor);
1769 return ret;
1773 /*********************************************************************
1775 * EDIT_SetCaretPos
1778 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
1779 BOOL after_wrap)
1781 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
1782 INT x = SLOWORD(res);
1783 INT y = SHIWORD(res);
1785 if(x < es->format_rect.left)
1786 x = es->format_rect.left;
1787 if(x > es->format_rect.right - 2)
1788 x = es->format_rect.right - 2;
1789 if(y > es->format_rect.bottom)
1790 y = es->format_rect.bottom;
1791 if(y < es->format_rect.top)
1792 y = es->format_rect.top;
1793 SetCaretPos(x, y);
1794 return;
1798 /*********************************************************************
1800 * EDIT_SetRectNP
1802 * note: this is not (exactly) the handler called on EM_SETRECTNP
1803 * it is also used to set the rect of a single line control
1806 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
1808 CopyRect(&es->format_rect, rc);
1809 if (es->style & WS_BORDER) {
1810 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
1811 if(TWEAK_WineLook == WIN31_LOOK)
1812 bw += 2;
1813 es->format_rect.left += bw;
1814 es->format_rect.top += bw;
1815 es->format_rect.right -= bw;
1816 es->format_rect.bottom -= bw;
1818 es->format_rect.left += es->left_margin;
1819 es->format_rect.right -= es->right_margin;
1820 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
1821 if (es->style & ES_MULTILINE)
1822 es->format_rect.bottom = es->format_rect.top +
1823 max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1824 else
1825 es->format_rect.bottom = es->format_rect.top + es->line_height;
1826 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1827 EDIT_BuildLineDefs_ML(wnd, es);
1831 /*********************************************************************
1833 * EDIT_UnlockBuffer
1836 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
1838 if (!es) {
1839 ERR("no EDITSTATE ... please report\n");
1840 return;
1842 if (!(es->style & ES_MULTILINE))
1843 return;
1844 if (!es->lock_count) {
1845 ERR("lock_count == 0 ... please report\n");
1846 return;
1848 if (!es->text) {
1849 ERR("es->text == 0 ... please report\n");
1850 return;
1852 if (force || (es->lock_count == 1)) {
1853 if (es->hloc32) {
1854 LocalUnlock(es->hloc32);
1855 es->text = NULL;
1856 } else if (es->hloc16) {
1857 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1858 es->text = NULL;
1861 es->lock_count--;
1865 /*********************************************************************
1867 * EDIT_WordBreakProc
1869 * Find the beginning of words.
1870 * Note: unlike the specs for a WordBreakProc, this function only
1871 * allows to be called without linebreaks between s[0] upto
1872 * s[count - 1]. Remember it is only called
1873 * internally, so we can decide this for ourselves.
1876 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1878 INT ret = 0;
1880 TRACE("s=%p, index=%u, count=%u, action=%d\n",
1881 s, index, count, action);
1883 switch (action) {
1884 case WB_LEFT:
1885 if (!count)
1886 break;
1887 if (index)
1888 index--;
1889 if (s[index] == ' ') {
1890 while (index && (s[index] == ' '))
1891 index--;
1892 if (index) {
1893 while (index && (s[index] != ' '))
1894 index--;
1895 if (s[index] == ' ')
1896 index++;
1898 } else {
1899 while (index && (s[index] != ' '))
1900 index--;
1901 if (s[index] == ' ')
1902 index++;
1904 ret = index;
1905 break;
1906 case WB_RIGHT:
1907 if (!count)
1908 break;
1909 if (index)
1910 index--;
1911 if (s[index] == ' ')
1912 while ((index < count) && (s[index] == ' ')) index++;
1913 else {
1914 while (s[index] && (s[index] != ' ') && (index < count))
1915 index++;
1916 while ((s[index] == ' ') && (index < count)) index++;
1918 ret = index;
1919 break;
1920 case WB_ISDELIMITER:
1921 ret = (s[index] == ' ');
1922 break;
1923 default:
1924 ERR("unknown action code, please report !\n");
1925 break;
1927 return ret;
1931 /*********************************************************************
1933 * EM_CHARFROMPOS
1935 * returns line number (not index) in high-order word of result.
1936 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
1937 * to Richedit, not to the edit control. Original documentation is valid.
1938 * FIXME: do the specs mean to return -1 if outside client area or
1939 * if outside formatting rectangle ???
1942 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
1944 POINT pt;
1945 RECT rc;
1946 INT index;
1948 pt.x = x;
1949 pt.y = y;
1950 GetClientRect(wnd->hwndSelf, &rc);
1951 if (!PtInRect(&rc, pt))
1952 return -1;
1954 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
1955 return MAKELONG(index, EDIT_EM_LineFromChar(wnd, es, index));
1959 /*********************************************************************
1961 * EM_FMTLINES
1963 * Enable or disable soft breaks.
1965 static BOOL EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL add_eol)
1967 es->flags &= ~EF_USE_SOFTBRK;
1968 if (add_eol) {
1969 es->flags |= EF_USE_SOFTBRK;
1970 FIXME("soft break enabled, not implemented\n");
1972 return add_eol;
1976 /*********************************************************************
1978 * EM_GETHANDLE
1980 * Hopefully this won't fire back at us.
1981 * We always start with a fixed buffer in our own heap.
1982 * However, with this message a 32 bit application requests
1983 * a handle to 32 bit moveable local heap memory, where it expects
1984 * to find the text.
1985 * It's a pity that from this moment on we have to use this
1986 * local heap, because applications may rely on the handle
1987 * in the future.
1989 * In this function we'll try to switch to local heap.
1992 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
1994 HLOCAL newBuf;
1995 LPSTR newText;
1996 INT newSize;
1998 if (!(es->style & ES_MULTILINE))
1999 return 0;
2001 if (es->hloc32)
2002 return es->hloc32;
2003 else if (es->hloc16)
2004 return (HLOCAL)es->hloc16;
2006 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
2007 ERR("could not allocate new 32 bit buffer\n");
2008 return 0;
2010 newSize = min(LocalSize(newBuf) - 1, es->buffer_limit);
2011 if (!(newText = LocalLock(newBuf))) {
2012 ERR("could not lock new 32 bit buffer\n");
2013 LocalFree(newBuf);
2014 return 0;
2016 lstrcpyA(newText, es->text);
2017 EDIT_UnlockBuffer(wnd, es, TRUE);
2018 if (es->text)
2019 HeapFree(es->heap, 0, es->text);
2020 es->hloc32 = newBuf;
2021 es->hloc16 = (HLOCAL16)NULL;
2022 es->buffer_size = newSize;
2023 es->text = newText;
2024 EDIT_LockBuffer(wnd, es);
2025 TRACE("switched to 32 bit local heap\n");
2027 return es->hloc32;
2031 /*********************************************************************
2033 * EM_GETHANDLE16
2035 * Hopefully this won't fire back at us.
2036 * We always start with a buffer in 32 bit linear memory.
2037 * However, with this message a 16 bit application requests
2038 * a handle of 16 bit local heap memory, where it expects to find
2039 * the text.
2040 * It's a pitty that from this moment on we have to use this
2041 * local heap, because applications may rely on the handle
2042 * in the future.
2044 * In this function we'll try to switch to local heap.
2046 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2048 HLOCAL16 newBuf;
2049 LPSTR newText;
2050 INT newSize;
2052 if (!(es->style & ES_MULTILINE))
2053 return 0;
2055 if (es->hloc16)
2056 return es->hloc16;
2058 if (!LOCAL_HeapSize(wnd->hInstance)) {
2059 if (!LocalInit16(wnd->hInstance, 0,
2060 GlobalSize16(wnd->hInstance))) {
2061 ERR("could not initialize local heap\n");
2062 return 0;
2064 TRACE("local heap initialized\n");
2066 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
2067 ERR("could not allocate new 16 bit buffer\n");
2068 return 0;
2070 newSize = min(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2071 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2072 ERR("could not lock new 16 bit buffer\n");
2073 LOCAL_Free(wnd->hInstance, newBuf);
2074 return 0;
2076 lstrcpyA(newText, es->text);
2077 EDIT_UnlockBuffer(wnd, es, TRUE);
2078 if (es->text)
2079 HeapFree(es->heap, 0, es->text);
2080 else if (es->hloc32) {
2081 while (LocalFree(es->hloc32)) ;
2082 LocalFree(es->hloc32);
2084 es->hloc32 = (HLOCAL)NULL;
2085 es->hloc16 = newBuf;
2086 es->buffer_size = newSize;
2087 es->text = newText;
2088 EDIT_LockBuffer(wnd, es);
2089 TRACE("switched to 16 bit buffer\n");
2091 return es->hloc16;
2095 /*********************************************************************
2097 * EM_GETLINE
2100 static INT EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT line, LPSTR lpch)
2102 LPSTR src;
2103 INT len;
2104 INT i;
2106 if (es->style & ES_MULTILINE) {
2107 if (line >= es->line_count)
2108 return 0;
2109 } else
2110 line = 0;
2111 i = EDIT_EM_LineIndex(wnd, es, line);
2112 src = es->text + i;
2113 len = min(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, i));
2114 for (i = 0 ; i < len ; i++) {
2115 *lpch = *src;
2116 src++;
2117 lpch++;
2119 return (LRESULT)len;
2123 /*********************************************************************
2125 * EM_GETSEL
2128 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT start, LPUINT end)
2130 UINT s = es->selection_start;
2131 UINT e = es->selection_end;
2133 ORDER_UINT(s, e);
2134 if (start)
2135 *start = s;
2136 if (end)
2137 *end = e;
2138 return MAKELONG(s, e);
2142 /*********************************************************************
2144 * EM_GETTHUMB
2146 * FIXME: is this right ? (or should it be only VSCROLL)
2147 * (and maybe only for edit controls that really have their
2148 * own scrollbars) (and maybe only for multiline controls ?)
2149 * All in all: very poorly documented
2151 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2152 * WM_VSCROLL handlers
2155 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2157 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
2158 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
2162 /*********************************************************************
2164 * EM_LINEFROMCHAR
2167 static INT EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT index)
2169 INT line;
2170 LINEDEF *line_def;
2172 if (!(es->style & ES_MULTILINE))
2173 return 0;
2174 if (index > lstrlenA(es->text))
2175 return es->line_count - 1;
2176 if (index == -1)
2177 index = min(es->selection_start, es->selection_end);
2179 line = 0;
2180 line_def = es->first_line_def;
2181 index -= line_def->length;
2182 while ((index >= 0) && line_def->next) {
2183 line++;
2184 line_def = line_def->next;
2185 index -= line_def->length;
2187 return line;
2191 /*********************************************************************
2193 * EM_LINEINDEX
2196 static INT EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT line)
2198 INT line_index;
2199 LINEDEF *line_def;
2201 if (!(es->style & ES_MULTILINE))
2202 return 0;
2203 if (line >= es->line_count)
2204 return -1;
2206 line_index = 0;
2207 line_def = es->first_line_def;
2208 if (line == -1) {
2209 INT index = es->selection_end - line_def->length;
2210 while ((index >= 0) && line_def->next) {
2211 line_index += line_def->length;
2212 line_def = line_def->next;
2213 index -= line_def->length;
2215 } else {
2216 while (line > 0) {
2217 line_index += line_def->length;
2218 line_def = line_def->next;
2219 line--;
2222 return line_index;
2226 /*********************************************************************
2228 * EM_LINELENGTH
2231 static INT EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT index)
2233 LINEDEF *line_def;
2235 if (!(es->style & ES_MULTILINE))
2236 return lstrlenA(es->text);
2238 if (index == -1) {
2239 /* FIXME: broken
2240 INT32 sl = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
2241 INT32 el = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2242 return es->selection_start - es->line_defs[sl].offset +
2243 es->line_defs[el].offset +
2244 es->line_defs[el].length - es->selection_end;
2246 return 0;
2248 line_def = es->first_line_def;
2249 index -= line_def->length;
2250 while ((index >= 0) && line_def->next) {
2251 line_def = line_def->next;
2252 index -= line_def->length;
2254 return line_def->net_length;
2258 /*********************************************************************
2260 * EM_LINESCROLL
2262 * FIXME: dx is in average character widths
2263 * However, we assume it is in pixels when we use this
2264 * function internally
2267 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2269 INT nyoff;
2271 if (!(es->style & ES_MULTILINE))
2272 return FALSE;
2274 if (-dx > es->x_offset)
2275 dx = -es->x_offset;
2276 if (dx > es->text_width - es->x_offset)
2277 dx = es->text_width - es->x_offset;
2278 nyoff = max(0, es->y_offset + dy);
2279 if (nyoff >= es->line_count)
2280 nyoff = es->line_count - 1;
2281 dy = (es->y_offset - nyoff) * es->line_height;
2282 if (dx || dy) {
2283 RECT rc1;
2284 RECT rc;
2285 GetClientRect(wnd->hwndSelf, &rc1);
2286 IntersectRect(&rc, &rc1, &es->format_rect);
2287 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2288 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2289 es->y_offset = nyoff;
2290 es->x_offset += dx;
2292 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2293 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2294 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2295 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2296 return TRUE;
2300 /*********************************************************************
2302 * EM_POSFROMCHAR
2305 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2307 INT len = lstrlenA(es->text);
2308 INT l;
2309 INT li;
2310 INT x;
2311 INT y = 0;
2312 HDC dc;
2313 HFONT old_font = 0;
2314 SIZE size;
2316 index = min(index, len);
2317 dc = GetDC(wnd->hwndSelf);
2318 if (es->font)
2319 old_font = SelectObject(dc, es->font);
2320 if (es->style & ES_MULTILINE) {
2321 l = EDIT_EM_LineFromChar(wnd, es, index);
2322 y = (l - es->y_offset) * es->line_height;
2323 li = EDIT_EM_LineIndex(wnd, es, l);
2324 if (after_wrap && (li == index) && l) {
2325 INT l2 = l - 1;
2326 LINEDEF *line_def = es->first_line_def;
2327 while (l2) {
2328 line_def = line_def->next;
2329 l2--;
2331 if (line_def->ending == END_WRAP) {
2332 l--;
2333 y -= es->line_height;
2334 li = EDIT_EM_LineIndex(wnd, es, l);
2337 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2338 es->tabs_count, es->tabs)) - es->x_offset;
2339 } else {
2340 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
2341 if (index < es->x_offset) {
2342 GetTextExtentPoint32A(dc, text + index,
2343 es->x_offset - index, &size);
2344 x = -size.cx;
2345 } else {
2346 GetTextExtentPoint32A(dc, text + es->x_offset,
2347 index - es->x_offset, &size);
2348 x = size.cx;
2350 y = 0;
2351 if (es->style & ES_PASSWORD)
2352 HeapFree(es->heap, 0 ,text);
2354 x += es->format_rect.left;
2355 y += es->format_rect.top;
2356 if (es->font)
2357 SelectObject(dc, old_font);
2358 ReleaseDC(wnd->hwndSelf, dc);
2359 return MAKELONG((INT16)x, (INT16)y);
2363 /*********************************************************************
2365 * EM_REPLACESEL
2367 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2370 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
2372 INT strl = lstrlenA(lpsz_replace);
2373 INT tl = lstrlenA(es->text);
2374 INT utl;
2375 UINT s;
2376 UINT e;
2377 INT i;
2378 LPSTR p;
2380 s = es->selection_start;
2381 e = es->selection_end;
2383 if ((s == e) && !strl)
2384 return;
2386 ORDER_UINT(s, e);
2388 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2389 return;
2391 if (e != s) {
2392 /* there is something to be deleted */
2393 if (can_undo) {
2394 utl = lstrlenA(es->undo_text);
2395 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2396 /* undo-buffer is extended to the right */
2397 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2398 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2399 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2400 /* undo-buffer is extended to the left */
2401 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2402 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2403 p[e - s] = p[0];
2404 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2405 p[i] = (es->text + s)[i];
2406 es->undo_position = s;
2407 } else {
2408 /* new undo-buffer */
2409 EDIT_MakeUndoFit(wnd, es, e - s);
2410 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2411 es->undo_position = s;
2413 /* any deletion makes the old insertion-undo invalid */
2414 es->undo_insert_count = 0;
2415 } else
2416 EDIT_EM_EmptyUndoBuffer(wnd, es);
2418 /* now delete */
2419 lstrcpyA(es->text + s, es->text + e);
2421 if (strl) {
2422 /* there is an insertion */
2423 if (can_undo) {
2424 if ((s == es->undo_position) ||
2425 ((es->undo_insert_count) &&
2426 (s == es->undo_position + es->undo_insert_count)))
2428 * insertion is new and at delete position or
2429 * an extension to either left or right
2431 es->undo_insert_count += strl;
2432 else {
2433 /* new insertion undo */
2434 es->undo_position = s;
2435 es->undo_insert_count = strl;
2436 /* new insertion makes old delete-buffer invalid */
2437 *es->undo_text = '\0';
2439 } else
2440 EDIT_EM_EmptyUndoBuffer(wnd, es);
2442 /* now insert */
2443 tl = lstrlenA(es->text);
2444 for (p = es->text + tl ; p >= es->text + s ; p--)
2445 p[strl] = p[0];
2446 for (i = 0 , p = es->text + s ; i < strl ; i++)
2447 p[i] = lpsz_replace[i];
2448 if(es->style & ES_UPPERCASE)
2449 CharUpperBuffA(p, strl);
2450 else if(es->style & ES_LOWERCASE)
2451 CharLowerBuffA(p, strl);
2452 s += strl;
2454 /* FIXME: really inefficient */
2455 if (es->style & ES_MULTILINE)
2456 EDIT_BuildLineDefs_ML(wnd, es);
2458 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2459 es->flags |= EF_MODIFIED;
2460 es->flags |= EF_UPDATE;
2461 EDIT_EM_ScrollCaret(wnd, es);
2463 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
2465 /* FIXME: really inefficient */
2466 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2470 /*********************************************************************
2472 * EM_SCROLL
2475 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
2477 INT dy;
2479 if (!(es->style & ES_MULTILINE))
2480 return (LRESULT)FALSE;
2482 dy = 0;
2484 switch (action) {
2485 case SB_LINEUP:
2486 if (es->y_offset)
2487 dy = -1;
2488 break;
2489 case SB_LINEDOWN:
2490 if (es->y_offset < es->line_count - 1)
2491 dy = 1;
2492 break;
2493 case SB_PAGEUP:
2494 if (es->y_offset)
2495 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2496 break;
2497 case SB_PAGEDOWN:
2498 if (es->y_offset < es->line_count - 1)
2499 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2500 break;
2501 default:
2502 return (LRESULT)FALSE;
2504 if (dy) {
2505 EDIT_EM_LineScroll(wnd, es, 0, dy);
2506 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2508 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2512 /*********************************************************************
2514 * EM_SCROLLCARET
2517 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
2519 if (es->style & ES_MULTILINE) {
2520 INT l;
2521 INT li;
2522 INT vlc;
2523 INT ww;
2524 INT cw = es->char_width;
2525 INT x;
2526 INT dy = 0;
2527 INT dx = 0;
2529 l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2530 li = EDIT_EM_LineIndex(wnd, es, l);
2531 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2532 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2533 if (l >= es->y_offset + vlc)
2534 dy = l - vlc + 1 - es->y_offset;
2535 if (l < es->y_offset)
2536 dy = l - es->y_offset;
2537 ww = es->format_rect.right - es->format_rect.left;
2538 if (x < es->format_rect.left)
2539 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2540 if (x > es->format_rect.right)
2541 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2542 if (dy || dx)
2543 EDIT_EM_LineScroll(wnd, es, dx, dy);
2544 } else {
2545 INT x;
2546 INT goal;
2547 INT format_width;
2549 if (!(es->style & ES_AUTOHSCROLL))
2550 return;
2552 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2553 format_width = es->format_rect.right - es->format_rect.left;
2554 if (x < es->format_rect.left) {
2555 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2556 do {
2557 es->x_offset--;
2558 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2559 } while ((x < goal) && es->x_offset);
2560 /* FIXME: use ScrollWindow() somehow to improve performance */
2561 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2562 } else if (x > es->format_rect.right) {
2563 INT x_last;
2564 INT len = lstrlenA(es->text);
2565 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2566 do {
2567 es->x_offset++;
2568 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2569 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
2570 } while ((x > goal) && (x_last > es->format_rect.right));
2571 /* FIXME: use ScrollWindow() somehow to improve performance */
2572 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2578 /*********************************************************************
2580 * EM_SETHANDLE
2582 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2585 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
2587 if (!(es->style & ES_MULTILINE))
2588 return;
2590 if (!hloc) {
2591 WARN("called with NULL handle\n");
2592 return;
2595 EDIT_UnlockBuffer(wnd, es, TRUE);
2597 * old buffer is freed by caller, unless
2598 * it is still in our own heap. (in that case
2599 * we free it, correcting the buggy caller.)
2601 if (es->text)
2602 HeapFree(es->heap, 0, es->text);
2604 es->hloc16 = (HLOCAL16)NULL;
2605 es->hloc32 = hloc;
2606 es->text = NULL;
2607 es->buffer_size = LocalSize(es->hloc32) - 1;
2608 EDIT_LockBuffer(wnd, es);
2610 es->x_offset = es->y_offset = 0;
2611 es->selection_start = es->selection_end = 0;
2612 EDIT_EM_EmptyUndoBuffer(wnd, es);
2613 es->flags &= ~EF_MODIFIED;
2614 es->flags &= ~EF_UPDATE;
2615 EDIT_BuildLineDefs_ML(wnd, es);
2616 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2617 EDIT_EM_ScrollCaret(wnd, es);
2621 /*********************************************************************
2623 * EM_SETHANDLE16
2625 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2628 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2630 if (!(es->style & ES_MULTILINE))
2631 return;
2633 if (!hloc) {
2634 WARN("called with NULL handle\n");
2635 return;
2638 EDIT_UnlockBuffer(wnd, es, TRUE);
2640 * old buffer is freed by caller, unless
2641 * it is still in our own heap. (in that case
2642 * we free it, correcting the buggy caller.)
2644 if (es->text)
2645 HeapFree(es->heap, 0, es->text);
2647 es->hloc16 = hloc;
2648 es->hloc32 = (HLOCAL)NULL;
2649 es->text = NULL;
2650 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2651 EDIT_LockBuffer(wnd, es);
2653 es->x_offset = es->y_offset = 0;
2654 es->selection_start = es->selection_end = 0;
2655 EDIT_EM_EmptyUndoBuffer(wnd, es);
2656 es->flags &= ~EF_MODIFIED;
2657 es->flags &= ~EF_UPDATE;
2658 EDIT_BuildLineDefs_ML(wnd, es);
2659 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2660 EDIT_EM_ScrollCaret(wnd, es);
2664 /*********************************************************************
2666 * EM_SETLIMITTEXT
2668 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2669 * However, the windows version is not complied to yet in all of edit.c
2672 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT limit)
2674 if (es->style & ES_MULTILINE) {
2675 if (limit)
2676 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
2677 else
2678 es->buffer_limit = BUFLIMIT_MULTI;
2679 } else {
2680 if (limit)
2681 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
2682 else
2683 es->buffer_limit = BUFLIMIT_SINGLE;
2688 /*********************************************************************
2690 * EM_SETMARGINS
2692 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2693 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2694 * of the char's width, according to the new docs.
2697 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT action,
2698 INT left, INT right)
2700 if (action & EC_LEFTMARGIN) {
2701 if (left != EC_USEFONTINFO)
2702 es->left_margin = left;
2703 else
2704 es->left_margin = es->char_width / 3;
2707 if (action & EC_RIGHTMARGIN) {
2708 if (right != EC_USEFONTINFO)
2709 es->right_margin = right;
2710 else
2711 es->right_margin = es->char_width / 3;
2713 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2717 /*********************************************************************
2719 * EM_SETPASSWORDCHAR
2722 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2724 if (es->style & ES_MULTILINE)
2725 return;
2727 if (es->password_char == c)
2728 return;
2730 es->password_char = c;
2731 if (c) {
2732 wnd->dwStyle |= ES_PASSWORD;
2733 es->style |= ES_PASSWORD;
2734 } else {
2735 wnd->dwStyle &= ~ES_PASSWORD;
2736 es->style &= ~ES_PASSWORD;
2738 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2742 /*********************************************************************
2744 * EDIT_EM_SetSel
2746 * note: unlike the specs say: the order of start and end
2747 * _is_ preserved in Windows. (i.e. start can be > end)
2748 * In other words: this handler is OK
2751 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2753 UINT old_start = es->selection_start;
2754 UINT old_end = es->selection_end;
2755 UINT len = lstrlenA(es->text);
2757 if (start == -1) {
2758 start = es->selection_end;
2759 end = es->selection_end;
2760 } else {
2761 start = min(start, len);
2762 end = min(end, len);
2764 es->selection_start = start;
2765 es->selection_end = end;
2766 if (after_wrap)
2767 es->flags |= EF_AFTER_WRAP;
2768 else
2769 es->flags &= ~EF_AFTER_WRAP;
2770 if (es->flags & EF_FOCUSED)
2771 EDIT_SetCaretPos(wnd, es, end, after_wrap);
2772 /* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2773 ORDER_UINT(start, end);
2774 ORDER_UINT(end, old_end);
2775 ORDER_UINT(start, old_start);
2776 ORDER_UINT(old_start, old_end);
2777 if (end != old_start)
2780 * One can also do
2781 * ORDER_UINT32(end, old_start);
2782 * EDIT_InvalidateText(wnd, es, start, end);
2783 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2784 * in place of the following if statement.
2786 if (old_start > end )
2788 EDIT_InvalidateText(wnd, es, start, end);
2789 EDIT_InvalidateText(wnd, es, old_start, old_end);
2791 else
2793 EDIT_InvalidateText(wnd, es, start, old_start);
2794 EDIT_InvalidateText(wnd, es, end, old_end);
2797 else EDIT_InvalidateText(wnd, es, start, old_end);
2801 /*********************************************************************
2803 * EM_SETTABSTOPS
2806 static BOOL EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT count, LPINT tabs)
2808 if (!(es->style & ES_MULTILINE))
2809 return FALSE;
2810 if (es->tabs)
2811 HeapFree(es->heap, 0, es->tabs);
2812 es->tabs_count = count;
2813 if (!count)
2814 es->tabs = NULL;
2815 else {
2816 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2817 memcpy(es->tabs, tabs, count * sizeof(INT));
2819 return TRUE;
2823 /*********************************************************************
2825 * EM_SETTABSTOPS16
2828 static BOOL EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT count, LPINT16 tabs)
2830 if (!(es->style & ES_MULTILINE))
2831 return FALSE;
2832 if (es->tabs)
2833 HeapFree(es->heap, 0, es->tabs);
2834 es->tabs_count = count;
2835 if (!count)
2836 es->tabs = NULL;
2837 else {
2838 INT i;
2839 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2840 for (i = 0 ; i < count ; i++)
2841 es->tabs[i] = *tabs++;
2843 return TRUE;
2847 /*********************************************************************
2849 * EM_SETWORDBREAKPROC
2852 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2854 if (es->word_break_proc32A == wbp)
2855 return;
2857 es->word_break_proc32A = wbp;
2858 es->word_break_proc16 = NULL;
2859 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2860 EDIT_BuildLineDefs_ML(wnd, es);
2861 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2866 /*********************************************************************
2868 * EM_SETWORDBREAKPROC16
2871 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2873 if (es->word_break_proc16 == wbp)
2874 return;
2876 es->word_break_proc32A = NULL;
2877 es->word_break_proc16 = wbp;
2878 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2879 EDIT_BuildLineDefs_ML(wnd, es);
2880 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
2885 /*********************************************************************
2887 * EM_UNDO / WM_UNDO
2890 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2892 INT ulength = lstrlenA(es->undo_text);
2893 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2895 lstrcpyA(utext, es->undo_text);
2897 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2898 es->undo_insert_count, utext);
2900 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2901 EDIT_EM_EmptyUndoBuffer(wnd, es);
2902 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext);
2903 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2904 HeapFree(es->heap, 0, utext);
2906 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2907 es->undo_insert_count, es->undo_text);
2909 return TRUE;
2913 /*********************************************************************
2915 * WM_CHAR
2918 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
2920 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
2921 switch (c) {
2922 case '\r':
2923 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
2924 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
2925 break;
2926 case '\n':
2927 if (es->style & ES_MULTILINE) {
2928 if (es->style & ES_READONLY) {
2929 EDIT_MoveHome(wnd, es, FALSE);
2930 EDIT_MoveDown_ML(wnd, es, FALSE);
2931 } else
2932 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n");
2934 break;
2935 case '\t':
2936 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2937 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t");
2938 break;
2939 case VK_BACK:
2940 if (!(es->style & ES_READONLY) && !control) {
2941 if (es->selection_start != es->selection_end)
2942 EDIT_WM_Clear(wnd, es);
2943 else {
2944 /* delete character left of caret */
2945 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
2946 EDIT_MoveBackward(wnd, es, TRUE);
2947 EDIT_WM_Clear(wnd, es);
2950 break;
2951 case 0x03: /* Ctrl-C */
2952 EDIT_WM_Copy(wnd, es);
2953 break;
2954 case 0x16: /* Ctrl-V */
2955 if (!(es->style & ES_READONLY))
2956 EDIT_WM_Paste(wnd, es);
2957 break;
2958 case 0x18: /* Ctrl-X */
2959 if (!(es->style & ES_READONLY))
2960 EDIT_WM_Cut(wnd, es);
2961 break;
2963 default:
2964 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
2965 char str[2];
2966 str[0] = c;
2967 str[1] = '\0';
2968 EDIT_EM_ReplaceSel(wnd, es, TRUE, str);
2970 break;
2975 /*********************************************************************
2977 * WM_COMMAND
2980 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
2982 if (code || control)
2983 return;
2985 switch (id) {
2986 case EM_UNDO:
2987 EDIT_EM_Undo(wnd, es);
2988 break;
2989 case WM_CUT:
2990 EDIT_WM_Cut(wnd, es);
2991 break;
2992 case WM_COPY:
2993 EDIT_WM_Copy(wnd, es);
2994 break;
2995 case WM_PASTE:
2996 EDIT_WM_Paste(wnd, es);
2997 break;
2998 case WM_CLEAR:
2999 EDIT_WM_Clear(wnd, es);
3000 break;
3001 case EM_SETSEL:
3002 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3003 EDIT_EM_ScrollCaret(wnd, es);
3004 break;
3005 default:
3006 ERR("unknown menu item, please report\n");
3007 break;
3012 /*********************************************************************
3014 * WM_CONTEXTMENU
3016 * Note: the resource files resource/sysres_??.rc cannot define a
3017 * single popup menu. Hence we use a (dummy) menubar
3018 * containing the single popup menu as its first item.
3020 * FIXME: the message identifiers have been chosen arbitrarily,
3021 * hence we use MF_BYPOSITION.
3022 * We might as well use the "real" values (anybody knows ?)
3023 * The menu definition is in resources/sysres_??.rc.
3024 * Once these are OK, we better use MF_BYCOMMAND here
3025 * (as we do in EDIT_WM_Command()).
3028 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND hwnd, INT x, INT y)
3030 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3031 HMENU popup = GetSubMenu(menu, 0);
3032 UINT start = es->selection_start;
3033 UINT end = es->selection_end;
3035 ORDER_UINT(start, end);
3037 /* undo */
3038 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
3039 /* cut */
3040 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3041 /* copy */
3042 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3043 /* paste */
3044 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
3045 /* delete */
3046 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
3047 /* select all */
3048 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
3050 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3051 DestroyMenu(menu);
3055 /*********************************************************************
3057 * WM_COPY
3060 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3062 INT s = es->selection_start;
3063 INT e = es->selection_end;
3064 HGLOBAL hdst;
3065 LPSTR dst;
3067 if (e == s)
3068 return;
3069 ORDER_INT(s, e);
3070 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3071 dst = GlobalLock(hdst);
3072 lstrcpynA(dst, es->text + s, e - s + 1);
3073 GlobalUnlock(hdst);
3074 OpenClipboard(wnd->hwndSelf);
3075 EmptyClipboard();
3076 SetClipboardData(CF_TEXT, hdst);
3077 CloseClipboard();
3081 /*********************************************************************
3083 * WM_CREATE
3086 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3089 * To initialize some final structure members, we call some helper
3090 * functions. However, since the EDITSTATE is not consistent (i.e.
3091 * not fully initialized), we should be very careful which
3092 * functions can be called, and in what order.
3094 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3095 EDIT_EM_EmptyUndoBuffer(wnd, es);
3097 if (cs->lpszName && *(cs->lpszName) != '\0') {
3098 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName);
3099 /* if we insert text to the editline, the text scrolls out
3100 * of the window, as the caret is placed after the insert
3101 * pos normally; thus we reset es->selection... to 0 and
3102 * update caret
3104 es->selection_start = es->selection_end = 0;
3105 EDIT_EM_ScrollCaret(wnd, es);
3107 return 0;
3111 /*********************************************************************
3113 * WM_DESTROY
3116 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3118 if (es->hloc32) {
3119 while (LocalUnlock(es->hloc32)) ;
3120 LocalFree(es->hloc32);
3122 if (es->hloc16) {
3123 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3124 LOCAL_Free(wnd->hInstance, es->hloc16);
3126 HeapDestroy(es->heap);
3127 HeapFree(GetProcessHeap(), 0, es);
3128 *(EDITSTATE **)wnd->wExtra = NULL;
3132 /*********************************************************************
3134 * WM_ERASEBKGND
3137 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3139 HBRUSH brush;
3140 RECT rc;
3142 if ( VERSION_AppWinVer() >= 0x40000 &&(
3143 !es->bEnableState || (es->style & ES_READONLY)))
3144 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3145 else
3146 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3148 if (!brush)
3149 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3151 GetClientRect(wnd->hwndSelf, &rc);
3152 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3153 GetClipBox(dc, &rc);
3155 * FIXME: specs say that we should UnrealizeObject() the brush,
3156 * but the specs of UnrealizeObject() say that we shouldn't
3157 * unrealize a stock object. The default brush that
3158 * DefWndProc() returns is ... a stock object.
3160 FillRect(dc, &rc, brush);
3161 return -1;
3165 /*********************************************************************
3167 * WM_GETTEXT
3170 static INT EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT count, LPSTR text)
3172 lstrcpynA(text, es->text, count);
3173 return strlen(text);
3177 /*********************************************************************
3179 * EDIT_HScroll_Hack
3181 * 16 bit notepad needs this. Actually it is not _our_ hack,
3182 * it is notepad's. Notepad is sending us scrollbar messages with
3183 * undocumented parameters without us even having a scrollbar ... !?!?
3186 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3188 INT dx = 0;
3189 INT fw = es->format_rect.right - es->format_rect.left;
3190 LRESULT ret = 0;
3192 if (!(es->flags & EF_HSCROLL_HACK)) {
3193 ERR("hacked WM_HSCROLL handler invoked\n");
3194 ERR(" if you are _not_ running 16 bit notepad, please report\n");
3195 ERR(" (this message is only displayed once per edit control)\n");
3196 es->flags |= EF_HSCROLL_HACK;
3199 switch (action) {
3200 case SB_LINELEFT:
3201 if (es->x_offset)
3202 dx = -es->char_width;
3203 break;
3204 case SB_LINERIGHT:
3205 if (es->x_offset < es->text_width)
3206 dx = es->char_width;
3207 break;
3208 case SB_PAGELEFT:
3209 if (es->x_offset)
3210 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3211 break;
3212 case SB_PAGERIGHT:
3213 if (es->x_offset < es->text_width)
3214 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3215 break;
3216 case SB_LEFT:
3217 if (es->x_offset)
3218 dx = -es->x_offset;
3219 break;
3220 case SB_RIGHT:
3221 if (es->x_offset < es->text_width)
3222 dx = es->text_width - es->x_offset;
3223 break;
3224 case SB_THUMBTRACK:
3225 es->flags |= EF_HSCROLL_TRACK;
3226 dx = pos * es->text_width / 100 - es->x_offset;
3227 break;
3228 case SB_THUMBPOSITION:
3229 es->flags &= ~EF_HSCROLL_TRACK;
3230 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3231 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3232 break;
3233 case SB_ENDSCROLL:
3234 break;
3237 * FIXME : the next two are undocumented !
3238 * Are we doing the right thing ?
3239 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3240 * although it's also a regular control message.
3242 case EM_GETTHUMB16:
3243 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3244 break;
3245 case EM_LINESCROLL16:
3246 dx = pos;
3247 break;
3249 default:
3250 ERR("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3251 return 0;
3253 if (dx)
3254 EDIT_EM_LineScroll(wnd, es, dx, 0);
3255 return ret;
3259 /*********************************************************************
3261 * WM_HSCROLL
3264 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3266 INT dx;
3267 INT fw;
3269 if (!(es->style & ES_MULTILINE))
3270 return 0;
3272 if (!(es->style & ES_AUTOHSCROLL))
3273 return 0;
3275 if (!(es->style & WS_HSCROLL))
3276 return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
3278 dx = 0;
3279 fw = es->format_rect.right - es->format_rect.left;
3280 switch (action) {
3281 case SB_LINELEFT:
3282 if (es->x_offset)
3283 dx = -es->char_width;
3284 break;
3285 case SB_LINERIGHT:
3286 if (es->x_offset < es->text_width)
3287 dx = es->char_width;
3288 break;
3289 case SB_PAGELEFT:
3290 if (es->x_offset)
3291 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3292 break;
3293 case SB_PAGERIGHT:
3294 if (es->x_offset < es->text_width)
3295 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3296 break;
3297 case SB_LEFT:
3298 if (es->x_offset)
3299 dx = -es->x_offset;
3300 break;
3301 case SB_RIGHT:
3302 if (es->x_offset < es->text_width)
3303 dx = es->text_width - es->x_offset;
3304 break;
3305 case SB_THUMBTRACK:
3306 es->flags |= EF_HSCROLL_TRACK;
3307 dx = pos - es->x_offset;
3308 break;
3309 case SB_THUMBPOSITION:
3310 es->flags &= ~EF_HSCROLL_TRACK;
3311 if (!(dx = pos - es->x_offset)) {
3312 SetScrollPos(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3313 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3315 break;
3316 case SB_ENDSCROLL:
3317 break;
3319 default:
3320 ERR("undocumented WM_HSCROLL parameter, please report\n");
3321 return 0;
3323 if (dx)
3324 EDIT_EM_LineScroll(wnd, es, dx, 0);
3325 return 0;
3329 /*********************************************************************
3331 * EDIT_CheckCombo
3334 static BOOL EDIT_CheckCombo(WND *wnd, UINT msg, INT key, DWORD key_data)
3336 HWND hLBox;
3338 /********************************************************************
3339 * This if statement used to check to see if the parent of the
3340 * edit control was a 'combobox' by comparing the ATOM of the parent
3341 * to a table of internal window control ATOMs. However, this check
3342 * would fail if the parent was a superclassed combobox (Although
3343 * having the same basic functionality of a combobox, it has a
3344 * different name and ATOM, thus defeating this check.)
3346 * The safe way to determine if the parent is a combobox is to send it
3347 * a message only a combo box would understand. I send it a message
3348 * CB_GETCOUNT, if I get 0 then the parent is not a combobox -
3349 * return FALSE. If I get > 0, then the parent IS a combobox
3350 * (or sub/super classed derrivative thereof)
3351 ********************************************************************/
3352 if (
3353 ((SendMessageA(wnd->parent->hwndSelf, CB_GETCOUNT, 0, 0)) > 0) &&
3354 (hLBox = COMBO_GetLBWindow(wnd->parent))
3357 HWND hCombo = wnd->parent->hwndSelf;
3358 BOOL bUIFlip = TRUE;
3360 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3361 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3363 switch (msg) {
3364 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3365 if (key != VK_F4) {
3366 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3367 if (SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0))
3368 bUIFlip = FALSE;
3370 if (!bUIFlip)
3371 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3372 else {
3373 /* make sure ComboLBox pops up */
3374 SendMessageA(hCombo, CB_SETEXTENDEDUI, 0, 0);
3375 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3376 SendMessageA(hCombo, CB_SETEXTENDEDUI, 1, 0);
3378 break;
3379 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3380 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3381 if (bUIFlip) {
3382 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3383 SendMessageA(hCombo, CB_SHOWDROPDOWN, (bUIFlip) ? FALSE : TRUE, 0);
3384 } else
3385 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3386 break;
3388 return TRUE;
3390 return FALSE;
3394 /*********************************************************************
3396 * WM_KEYDOWN
3398 * Handling of special keys that don't produce a WM_CHAR
3399 * (i.e. non-printable keys) & Backspace & Delete
3402 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
3404 BOOL shift;
3405 BOOL control;
3407 if (GetKeyState(VK_MENU) & 0x8000)
3408 return 0;
3410 shift = GetKeyState(VK_SHIFT) & 0x8000;
3411 control = GetKeyState(VK_CONTROL) & 0x8000;
3413 switch (key) {
3414 case VK_F4:
3415 case VK_UP:
3416 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3417 break;
3418 if (key == VK_F4)
3419 break;
3420 /* fall through */
3421 case VK_LEFT:
3422 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3423 EDIT_MoveUp_ML(wnd, es, shift);
3424 else
3425 if (control)
3426 EDIT_MoveWordBackward(wnd, es, shift);
3427 else
3428 EDIT_MoveBackward(wnd, es, shift);
3429 break;
3430 case VK_DOWN:
3431 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3432 break;
3433 /* fall through */
3434 case VK_RIGHT:
3435 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3436 EDIT_MoveDown_ML(wnd, es, shift);
3437 else if (control)
3438 EDIT_MoveWordForward(wnd, es, shift);
3439 else
3440 EDIT_MoveForward(wnd, es, shift);
3441 break;
3442 case VK_HOME:
3443 EDIT_MoveHome(wnd, es, shift);
3444 break;
3445 case VK_END:
3446 EDIT_MoveEnd(wnd, es, shift);
3447 break;
3448 case VK_PRIOR:
3449 if (es->style & ES_MULTILINE)
3450 EDIT_MovePageUp_ML(wnd, es, shift);
3451 break;
3452 case VK_NEXT:
3453 if (es->style & ES_MULTILINE)
3454 EDIT_MovePageDown_ML(wnd, es, shift);
3455 break;
3456 case VK_DELETE:
3457 if (!(es->style & ES_READONLY) && !(shift && control)) {
3458 if (es->selection_start != es->selection_end) {
3459 if (shift)
3460 EDIT_WM_Cut(wnd, es);
3461 else
3462 EDIT_WM_Clear(wnd, es);
3463 } else {
3464 if (shift) {
3465 /* delete character left of caret */
3466 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3467 EDIT_MoveBackward(wnd, es, TRUE);
3468 EDIT_WM_Clear(wnd, es);
3469 } else if (control) {
3470 /* delete to end of line */
3471 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3472 EDIT_MoveEnd(wnd, es, TRUE);
3473 EDIT_WM_Clear(wnd, es);
3474 } else {
3475 /* delete character right of caret */
3476 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3477 EDIT_MoveForward(wnd, es, TRUE);
3478 EDIT_WM_Clear(wnd, es);
3482 break;
3483 case VK_INSERT:
3484 if (shift) {
3485 if (!(es->style & ES_READONLY))
3486 EDIT_WM_Paste(wnd, es);
3487 } else if (control)
3488 EDIT_WM_Copy(wnd, es);
3489 break;
3490 case VK_RETURN:
3491 /* If the edit doesn't want the return send a message to the default object */
3492 if(!(es->style & ES_WANTRETURN))
3494 HWND hwndParent = GetParent(wnd->hwndSelf);
3495 DWORD dw = SendMessage16( hwndParent, DM_GETDEFID, 0, 0 );
3496 if (HIWORD(dw) == DC_HASDEFID)
3498 SendMessageA( hwndParent, WM_COMMAND,
3499 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3500 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3503 break;
3505 return 0;
3509 /*********************************************************************
3511 * WM_KILLFOCUS
3514 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND window_getting_focus)
3516 es->flags &= ~EF_FOCUSED;
3517 DestroyCaret();
3518 if(!(es->style & ES_NOHIDESEL))
3519 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3520 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3521 return 0;
3525 /*********************************************************************
3527 * WM_LBUTTONDBLCLK
3529 * The caret position has been set on the WM_LBUTTONDOWN message
3532 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3534 INT s;
3535 INT e = es->selection_end;
3536 INT l;
3537 INT li;
3538 INT ll;
3540 if (!(es->flags & EF_FOCUSED))
3541 return 0;
3543 l = EDIT_EM_LineFromChar(wnd, es, e);
3544 li = EDIT_EM_LineIndex(wnd, es, l);
3545 ll = EDIT_EM_LineLength(wnd, es, e);
3546 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3547 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3548 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3549 EDIT_EM_ScrollCaret(wnd, es);
3550 return 0;
3554 /*********************************************************************
3556 * WM_LBUTTONDOWN
3559 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3561 INT e;
3562 BOOL after_wrap;
3564 if (!(es->flags & EF_FOCUSED))
3565 return 0;
3567 es->bCaptureState = TRUE;
3568 SetCapture(wnd->hwndSelf);
3569 EDIT_ConfinePoint(wnd, es, &x, &y);
3570 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3571 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3572 EDIT_EM_ScrollCaret(wnd, es);
3573 es->region_posx = es->region_posy = 0;
3574 SetTimer(wnd->hwndSelf, 0, 100, NULL);
3575 return 0;
3579 /*********************************************************************
3581 * WM_LBUTTONUP
3584 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3586 if (es->bCaptureState && GetCapture() == wnd->hwndSelf) {
3587 KillTimer(wnd->hwndSelf, 0);
3588 ReleaseCapture();
3590 es->bCaptureState = FALSE;
3591 return 0;
3595 /*********************************************************************
3597 * WM_MOUSEMOVE
3600 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3602 INT e;
3603 BOOL after_wrap;
3604 INT prex, prey;
3606 if (GetCapture() != wnd->hwndSelf)
3607 return 0;
3610 * FIXME: gotta do some scrolling if outside client
3611 * area. Maybe reset the timer ?
3613 prex = x; prey = y;
3614 EDIT_ConfinePoint(wnd, es, &x, &y);
3615 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3616 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3617 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3618 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3619 return 0;
3623 /*********************************************************************
3625 * WM_NCCREATE
3628 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs)
3630 EDITSTATE *es;
3632 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3633 return FALSE;
3634 *(EDITSTATE **)wnd->wExtra = es;
3637 * Note: since the EDITSTATE has not been fully initialized yet,
3638 * we can't use any API calls that may send
3639 * WM_XXX messages before WM_NCCREATE is completed.
3642 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3643 return FALSE;
3644 es->style = cs->style;
3646 es->bEnableState = !(cs->style & WS_DISABLED);
3649 * In Win95 look and feel, the WS_BORDER style is replaced by the
3650 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3651 * control a non client area.
3653 if (TWEAK_WineLook != WIN31_LOOK)
3655 if (es->style & WS_BORDER)
3657 es->style &= ~WS_BORDER;
3658 wnd->dwStyle &= ~WS_BORDER;
3659 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
3662 else
3664 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
3665 wnd->dwStyle &= ~WS_BORDER;
3668 if (es->style & ES_MULTILINE) {
3669 es->buffer_size = BUFSTART_MULTI;
3670 es->buffer_limit = BUFLIMIT_MULTI;
3671 if (es->style & WS_VSCROLL)
3672 es->style |= ES_AUTOVSCROLL;
3673 if (es->style & WS_HSCROLL)
3674 es->style |= ES_AUTOHSCROLL;
3675 es->style &= ~ES_PASSWORD;
3676 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3677 if (es->style & ES_RIGHT)
3678 es->style &= ~ES_CENTER;
3679 es->style &= ~WS_HSCROLL;
3680 es->style &= ~ES_AUTOHSCROLL;
3683 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3684 es->style |= ES_AUTOVSCROLL;
3685 } else {
3686 es->buffer_size = BUFSTART_SINGLE;
3687 es->buffer_limit = BUFLIMIT_SINGLE;
3688 es->style &= ~ES_CENTER;
3689 es->style &= ~ES_RIGHT;
3690 es->style &= ~WS_HSCROLL;
3691 es->style &= ~WS_VSCROLL;
3692 es->style &= ~ES_AUTOVSCROLL;
3693 es->style &= ~ES_WANTRETURN;
3694 if (es->style & ES_UPPERCASE) {
3695 es->style &= ~ES_LOWERCASE;
3696 es->style &= ~ES_NUMBER;
3697 } else if (es->style & ES_LOWERCASE)
3698 es->style &= ~ES_NUMBER;
3699 if (es->style & ES_PASSWORD)
3700 es->password_char = '*';
3702 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3703 es->style |= ES_AUTOHSCROLL;
3705 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3706 return FALSE;
3707 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3708 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3709 return FALSE;
3710 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3711 *es->text = '\0';
3712 if (es->style & ES_MULTILINE)
3713 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3714 return FALSE;
3715 es->line_count = 1;
3717 return TRUE;
3720 /*********************************************************************
3722 * WM_PAINT
3725 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
3727 PAINTSTRUCT ps;
3728 INT i;
3729 HDC dc;
3730 HFONT old_font = 0;
3731 RECT rc;
3732 RECT rcLine;
3733 RECT rcRgn;
3734 BOOL rev = es->bEnableState &&
3735 ((es->flags & EF_FOCUSED) ||
3736 (es->style & ES_NOHIDESEL));
3737 if (!wParam)
3738 dc = BeginPaint(wnd->hwndSelf, &ps);
3739 else
3740 dc = (HDC) wParam;
3741 if(es->style & WS_BORDER) {
3742 GetClientRect(wnd->hwndSelf, &rc);
3743 if(es->style & ES_MULTILINE) {
3744 if(es->style & WS_HSCROLL) rc.bottom++;
3745 if(es->style & WS_VSCROLL) rc.right++;
3747 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
3749 IntersectClipRect(dc, es->format_rect.left,
3750 es->format_rect.top,
3751 es->format_rect.right,
3752 es->format_rect.bottom);
3753 if (es->style & ES_MULTILINE) {
3754 GetClientRect(wnd->hwndSelf, &rc);
3755 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3757 if (es->font)
3758 old_font = SelectObject(dc, es->font);
3759 if ( VERSION_AppWinVer() >= 0x40000 &&(
3760 !es->bEnableState || (es->style & ES_READONLY)))
3761 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3762 else
3763 EDIT_SEND_CTLCOLOR(wnd, dc);
3765 if (!es->bEnableState)
3766 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3767 GetClipBox(dc, &rcRgn);
3768 if (es->style & ES_MULTILINE) {
3769 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3770 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3771 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3772 if (IntersectRect(&rc, &rcRgn, &rcLine))
3773 EDIT_PaintLine(wnd, es, dc, i, rev);
3775 } else {
3776 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3777 if (IntersectRect(&rc, &rcRgn, &rcLine))
3778 EDIT_PaintLine(wnd, es, dc, 0, rev);
3780 if (es->font)
3781 SelectObject(dc, old_font);
3782 if (es->flags & EF_FOCUSED)
3783 EDIT_SetCaretPos(wnd, es, es->selection_end,
3784 es->flags & EF_AFTER_WRAP);
3785 if (!wParam)
3786 EndPaint(wnd->hwndSelf, &ps);
3787 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3788 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3789 SCROLLINFO si;
3790 si.cbSize = sizeof(SCROLLINFO);
3791 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3792 si.nMin = 0;
3793 si.nMax = es->line_count + vlc - 2;
3794 si.nPage = vlc;
3795 si.nPos = es->y_offset;
3796 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
3798 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3799 SCROLLINFO si;
3800 INT fw = es->format_rect.right - es->format_rect.left;
3801 si.cbSize = sizeof(SCROLLINFO);
3802 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3803 si.nMin = 0;
3804 si.nMax = es->text_width + fw - 1;
3805 si.nPage = fw;
3806 si.nPos = es->x_offset;
3807 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3810 if (es->flags & EF_UPDATE) {
3811 es->flags &= ~EF_UPDATE;
3812 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3817 /*********************************************************************
3819 * WM_PASTE
3822 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3824 HGLOBAL hsrc;
3825 LPSTR src;
3827 OpenClipboard(wnd->hwndSelf);
3828 if ((hsrc = GetClipboardData(CF_TEXT))) {
3829 src = (LPSTR)GlobalLock(hsrc);
3830 EDIT_EM_ReplaceSel(wnd, es, TRUE, src);
3831 GlobalUnlock(hsrc);
3833 CloseClipboard();
3837 /*********************************************************************
3839 * WM_SETFOCUS
3842 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus)
3844 es->flags |= EF_FOCUSED;
3845 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
3846 EDIT_SetCaretPos(wnd, es, es->selection_end,
3847 es->flags & EF_AFTER_WRAP);
3848 if(!(es->style & ES_NOHIDESEL))
3849 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3850 ShowCaret(wnd->hwndSelf);
3851 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3855 /*********************************************************************
3857 * WM_SETFONT
3859 * With Win95 look the margins are set to default font value unless
3860 * the system font (font == 0) is being set, in which case they are left
3861 * unchanged.
3864 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
3866 TEXTMETRICA tm;
3867 HDC dc;
3868 HFONT old_font = 0;
3869 RECT r;
3871 es->font = font;
3872 dc = GetDC(wnd->hwndSelf);
3873 if (font)
3874 old_font = SelectObject(dc, font);
3875 GetTextMetricsA(dc, &tm);
3876 es->line_height = tm.tmHeight;
3877 es->char_width = tm.tmAveCharWidth;
3878 if (font)
3879 SelectObject(dc, old_font);
3880 ReleaseDC(wnd->hwndSelf, dc);
3881 if (font && (TWEAK_WineLook > WIN31_LOOK))
3882 EDIT_EM_SetMargins(wnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3883 EC_USEFONTINFO, EC_USEFONTINFO);
3885 /* Force the recalculation of the format rect for each font change */
3886 GetClientRect(wnd->hwndSelf, &r);
3887 EDIT_SetRectNP(wnd, es, &r);
3889 if (es->style & ES_MULTILINE)
3890 EDIT_BuildLineDefs_ML(wnd, es);
3892 if (redraw)
3893 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
3894 if (es->flags & EF_FOCUSED) {
3895 DestroyCaret();
3896 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
3897 EDIT_SetCaretPos(wnd, es, es->selection_end,
3898 es->flags & EF_AFTER_WRAP);
3899 ShowCaret(wnd->hwndSelf);
3904 /*********************************************************************
3906 * WM_SETTEXT
3908 * NOTES
3909 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3910 * The modified flag is reset. No notifications are sent.
3912 * For single-line controls, reception of WM_SETTEXT triggers:
3913 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3916 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
3918 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3919 if (text) {
3920 TRACE("\t'%s'\n", text);
3921 EDIT_EM_ReplaceSel(wnd, es, FALSE, text);
3922 } else {
3923 TRACE("\t<NULL>\n");
3924 EDIT_EM_ReplaceSel(wnd, es, FALSE, "");
3926 es->x_offset = 0;
3927 if (es->style & ES_MULTILINE) {
3928 es->flags &= ~EF_UPDATE;
3929 } else {
3930 es->flags |= EF_UPDATE;
3932 es->flags &= ~EF_MODIFIED;
3933 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
3934 EDIT_EM_ScrollCaret(wnd, es);
3936 if (es->flags & EF_UPDATE)
3937 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
3941 /*********************************************************************
3943 * WM_SIZE
3946 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
3948 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3949 RECT rc;
3950 SetRect(&rc, 0, 0, width, height);
3951 EDIT_SetRectNP(wnd, es, &rc);
3952 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
3957 /*********************************************************************
3959 * WM_SYSKEYDOWN
3962 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
3964 if ((key == VK_BACK) && (key_data & 0x2000)) {
3965 if (EDIT_EM_CanUndo(wnd, es))
3966 EDIT_EM_Undo(wnd, es);
3967 return 0;
3968 } else if (key == VK_UP || key == VK_DOWN)
3969 if (EDIT_CheckCombo(wnd, WM_SYSKEYDOWN, key, key_data))
3970 return 0;
3971 return DefWindowProcA(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
3975 /*********************************************************************
3977 * WM_TIMER
3980 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
3982 if (es->region_posx < 0) {
3983 EDIT_MoveBackward(wnd, es, TRUE);
3984 } else if (es->region_posx > 0) {
3985 EDIT_MoveForward(wnd, es, TRUE);
3988 * FIXME: gotta do some vertical scrolling here, like
3989 * EDIT_EM_LineScroll(wnd, 0, 1);
3994 /*********************************************************************
3996 * EDIT_VScroll_Hack
3998 * 16 bit notepad needs this. Actually it is not _our_ hack,
3999 * it is notepad's. Notepad is sending us scrollbar messages with
4000 * undocumented parameters without us even having a scrollbar ... !?!?
4003 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4005 INT dy = 0;
4006 LRESULT ret = 0;
4008 if (!(es->flags & EF_VSCROLL_HACK)) {
4009 ERR("hacked WM_VSCROLL handler invoked\n");
4010 ERR(" if you are _not_ running 16 bit notepad, please report\n");
4011 ERR(" (this message is only displayed once per edit control)\n");
4012 es->flags |= EF_VSCROLL_HACK;
4015 switch (action) {
4016 case SB_LINEUP:
4017 case SB_LINEDOWN:
4018 case SB_PAGEUP:
4019 case SB_PAGEDOWN:
4020 EDIT_EM_Scroll(wnd, es, action);
4021 return 0;
4022 case SB_TOP:
4023 dy = -es->y_offset;
4024 break;
4025 case SB_BOTTOM:
4026 dy = es->line_count - 1 - es->y_offset;
4027 break;
4028 case SB_THUMBTRACK:
4029 es->flags |= EF_VSCROLL_TRACK;
4030 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4031 break;
4032 case SB_THUMBPOSITION:
4033 es->flags &= ~EF_VSCROLL_TRACK;
4034 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4035 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4036 break;
4037 case SB_ENDSCROLL:
4038 break;
4041 * FIXME : the next two are undocumented !
4042 * Are we doing the right thing ?
4043 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4044 * although it's also a regular control message.
4046 case EM_GETTHUMB16:
4047 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
4048 break;
4049 case EM_LINESCROLL16:
4050 dy = pos;
4051 break;
4053 default:
4054 ERR("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4055 return 0;
4057 if (dy)
4058 EDIT_EM_LineScroll(wnd, es, 0, dy);
4059 return ret;
4063 /*********************************************************************
4065 * WM_VSCROLL
4068 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4070 INT dy;
4072 if (!(es->style & ES_MULTILINE))
4073 return 0;
4075 if (!(es->style & ES_AUTOVSCROLL))
4076 return 0;
4078 if (!(es->style & WS_VSCROLL))
4079 return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
4081 dy = 0;
4082 switch (action) {
4083 case SB_LINEUP:
4084 case SB_LINEDOWN:
4085 case SB_PAGEUP:
4086 case SB_PAGEDOWN:
4087 EDIT_EM_Scroll(wnd, es, action);
4088 return 0;
4090 case SB_TOP:
4091 dy = -es->y_offset;
4092 break;
4093 case SB_BOTTOM:
4094 dy = es->line_count - 1 - es->y_offset;
4095 break;
4096 case SB_THUMBTRACK:
4097 es->flags |= EF_VSCROLL_TRACK;
4098 dy = pos - es->y_offset;
4099 break;
4100 case SB_THUMBPOSITION:
4101 es->flags &= ~EF_VSCROLL_TRACK;
4102 if (!(dy = pos - es->y_offset)) {
4103 SetScrollPos(wnd->hwndSelf, SB_VERT, pos, TRUE);
4104 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4106 break;
4107 case SB_ENDSCROLL:
4108 break;
4110 default:
4111 ERR("undocumented WM_VSCROLL action %d, please report\n",
4112 action);
4113 return 0;
4115 if (dy)
4116 EDIT_EM_LineScroll(wnd, es, 0, dy);
4117 return 0;