Release 980601
[wine.git] / controls / edit.c
blobd8407bc044697edb25873553e8f9352c2fbff23b
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 "windows.h"
15 #include "winnt.h"
16 #include "win.h"
17 #include "combo.h"
18 #include "local.h"
19 #include "resource.h"
20 #include "debug.h"
21 #include "callback.h"
23 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
24 FIXME: BTW, new specs say 65535 (do you dare ???) */
25 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
26 #define BUFSTART_MULTI 1024 /* starting size */
27 #define BUFSTART_SINGLE 256 /* starting size */
28 #define GROWLENGTH 64 /* buffers grow by this much */
29 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
32 * extra flags for EDITSTATE.flags field
34 #define EF_MODIFIED 0x0001 /* text has been modified */
35 #define EF_FOCUSED 0x0002 /* we have input focus */
36 #define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
37 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
38 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
39 #define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
40 #define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
41 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
42 wrapped line, instead of in front of the next character */
43 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
45 typedef BOOL32 *LPBOOL32;
47 typedef enum
49 END_0 = 0, /* line ends with terminating '\0' character */
50 END_WRAP, /* line is wrapped */
51 END_HARD, /* line ends with a hard return '\r\n' */
52 END_SOFT /* line ends with a soft return '\r\r\n' */
53 } LINE_END;
55 typedef struct tagLINEDEF {
56 INT32 length; /* bruto length of a line in bytes */
57 INT32 net_length; /* netto length of a line in visible characters */
58 LINE_END ending;
59 INT32 width; /* width of the line in pixels */
60 struct tagLINEDEF *next;
61 } LINEDEF;
63 typedef struct
65 HANDLE32 heap; /* our own heap */
66 LPSTR text; /* the actual contents of the control */
67 INT32 buffer_size; /* the size of the buffer */
68 INT32 buffer_limit; /* the maximum size to which the buffer may grow */
69 HFONT32 font; /* NULL means standard system font */
70 INT32 x_offset; /* scroll offset for multi lines this is in pixels
71 for single lines it's in characters */
72 INT32 line_height; /* height of a screen line in pixels */
73 INT32 char_width; /* average character width in pixels */
74 DWORD style; /* sane version of wnd->dwStyle */
75 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
76 INT32 undo_insert_count; /* number of characters inserted in sequence */
77 INT32 undo_position; /* character index of the insertion and deletion */
78 LPSTR undo_text; /* deleted text */
79 INT32 undo_buffer_size; /* size of the deleted text buffer */
80 INT32 selection_start; /* == selection_end if no selection */
81 INT32 selection_end; /* == current caret position */
82 CHAR password_char; /* == 0 if no password char, and for multi line controls */
83 INT32 left_margin; /* in pixels */
84 INT32 right_margin; /* in pixels */
85 RECT32 format_rect;
86 INT32 region_posx; /* Position of cursor relative to region: */
87 INT32 region_posy; /* -1: to left, 0: within, 1: to right */
88 EDITWORDBREAKPROC16 word_break_proc16;
89 EDITWORDBREAKPROC32A word_break_proc32A;
90 INT32 line_count; /* number of lines */
91 INT32 y_offset; /* scroll offset in number of lines */
93 * only for multi line controls
95 INT32 lock_count; /* amount of re-entries in the EditWndProc */
96 INT32 tabs_count;
97 LPINT32 tabs;
98 INT32 text_width; /* width of the widest line in pixels */
99 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
100 HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
101 HLOCAL32 hloc32; /* for controls receiving EM_GETHANDLE */
102 } EDITSTATE;
105 #define SWAP_INT32(x,y) do { INT32 temp = (INT32)(x); (x) = (INT32)(y); (y) = temp; } while(0)
106 #define ORDER_INT32(x,y) do { if ((INT32)(y) < (INT32)(x)) SWAP_INT32((x),(y)); } while(0)
108 #define SWAP_UINT32(x,y) do { UINT32 temp = (UINT32)(x); (x) = (UINT32)(y); (y) = temp; } while(0)
109 #define ORDER_UINT32(x,y) do { if ((UINT32)(y) < (UINT32)(x)) SWAP_UINT32((x),(y)); } while(0)
111 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
112 ({TRACE(edit, "notification " str " sent to hwnd=%08x\n", \
113 (UINT32)(hwnd));})
115 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
116 (SendMessage32A((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
117 (WPARAM32)(hdc), (LPARAM)(wnd)->hwndSelf))
118 #define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
119 (DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str), \
120 SendMessage32A((wnd)->parent->hwndSelf, WM_COMMAND, \
121 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
122 (LPARAM)(wnd)->hwndSelf))
123 #define DPRINTF_EDIT_MSG16(str) \
124 TRACE(edit, \
125 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
126 (UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
127 #define DPRINTF_EDIT_MSG32(str) \
128 TRACE(edit, \
129 "32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
130 (UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
132 /*********************************************************************
134 * Declarations
139 * These functions have trivial implementations
140 * We still like to call them internally
141 * "static __inline__" makes them more like macro's
143 static __inline__ BOOL32 EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es);
144 static __inline__ void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es);
145 static __inline__ void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
146 static __inline__ void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
148 * This is the only exported function
150 LRESULT WINAPI EditWndProc( HWND32 hwnd, UINT32 msg,
151 WPARAM32 wParam, LPARAM lParam );
153 * Helper functions only valid for one type of control
155 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
156 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es);
157 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
158 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
159 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
160 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
162 * Helper functions valid for both single line _and_ multi line controls
164 static INT32 EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT32 start, INT32 index, INT32 count, INT32 action);
165 static INT32 EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y, LPBOOL32 after_wrap);
166 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT32 x, LPINT32 y);
167 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT32 line, INT32 scol, INT32 ecol, LPRECT32 rc);
168 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end);
169 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
170 static BOOL32 EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT32 size);
171 static BOOL32 EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT32 size);
172 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL32 extend);
173 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL32 extend);
174 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL32 extend);
175 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL32 extend);
176 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL32 extend);
177 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL32 extend);
178 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC32 hdc, INT32 line, BOOL32 rev);
179 static INT32 EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC32 hdc, INT32 x, INT32 y, INT32 line, INT32 col, INT32 count, BOOL32 rev);
180 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT32 lprc);
181 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL32 force);
182 static INT32 EDIT_WordBreakProc(LPSTR s, INT32 index, INT32 count, INT32 action);
184 * EM_XXX message handlers
186 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y);
187 static BOOL32 EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL32 add_eol);
188 static HLOCAL32 EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es);
189 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
190 static INT32 EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT32 line, LPSTR lpch);
191 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT32 start, LPUINT32 end);
192 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
193 static INT32 EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT32 index);
194 static INT32 EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT32 line);
195 static INT32 EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT32 index);
196 static BOOL32 EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT32 dx, INT32 dy);
197 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT32 index, BOOL32 after_wrap);
198 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL32 can_undo, LPCSTR lpsz_replace);
199 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT32 action);
200 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
201 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL32 hloc);
202 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
203 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT32 limit);
204 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT32 action, INT32 left, INT32 right);
205 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c);
206 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT32 start, UINT32 end, BOOL32 after_wrap);
207 static BOOL32 EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT32 count, LPINT32 tabs);
208 static BOOL32 EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT32 count, LPINT16 tabs);
209 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC32A wbp);
210 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
211 static BOOL32 EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
213 * WM_XXX message handlers
215 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data);
216 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT32 code, INT32 id, HWND32 conrtol);
217 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND32 hwnd, INT32 x, INT32 y);
218 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
219 static LRESULT EDIT_WM_Create(WND *wnd, LPCREATESTRUCT32A cs);
220 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
221 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC32 dc);
222 static INT32 EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT32 count, LPSTR text);
223 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar);
224 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data);
225 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND32 window_getting_focus);
226 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
227 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
228 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
229 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
230 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es);
231 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
232 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND32 window_losing_focus);
233 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT32 font, BOOL32 redraw);
234 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
235 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT32 action, INT32 width, INT32 height);
236 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data);
237 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT32 id, TIMERPROC32 timer_proc);
238 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar);
241 /*********************************************************************
243 * EM_CANUNDO
246 static __inline__ BOOL32 EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es)
248 return (es->undo_insert_count || lstrlen32A(es->undo_text));
252 /*********************************************************************
254 * EM_EMPTYUNDOBUFFER
257 static __inline__ void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es)
259 es->undo_insert_count = 0;
260 *es->undo_text = '\0';
264 /*********************************************************************
266 * WM_CLEAR
269 static __inline__ void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
271 EDIT_EM_ReplaceSel(wnd, es, TRUE, "");
275 /*********************************************************************
277 * WM_CUT
280 static __inline__ void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
282 EDIT_WM_Copy(wnd, es);
283 EDIT_WM_Clear(wnd, es);
287 /*********************************************************************
289 * EditWndProc()
291 * The messages are in the order of the actual integer values
292 * (which can be found in include/windows.h)
293 * Whereever possible the 16 bit versions are converted to
294 * the 32 bit ones, so that we can 'fall through' to the
295 * helper functions. These are mostly 32 bit (with a few
296 * exceptions, clearly indicated by a '16' extension to their
297 * names).
300 LRESULT WINAPI EditWndProc( HWND32 hwnd, UINT32 msg,
301 WPARAM32 wParam, LPARAM lParam )
303 WND *wnd = WIN_FindWndPtr(hwnd);
304 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
305 LRESULT result = 0;
307 switch (msg) {
308 case WM_CREATE:
309 DPRINTF_EDIT_MSG32("WM_CREATE");
310 return EDIT_WM_Create(wnd, (LPCREATESTRUCT32A)lParam);
312 case WM_DESTROY:
313 DPRINTF_EDIT_MSG32("WM_DESTROY");
314 EDIT_WM_Destroy(wnd, es);
315 return 0;
318 if (!es)
319 return DefWindowProc32A(hwnd, msg, wParam, lParam);
321 EDIT_LockBuffer(wnd, es);
322 switch (msg) {
323 case EM_GETSEL16:
324 DPRINTF_EDIT_MSG16("EM_GETSEL");
325 wParam = 0;
326 lParam = 0;
327 /* fall through */
328 case EM_GETSEL32:
329 DPRINTF_EDIT_MSG32("EM_GETSEL");
330 result = EDIT_EM_GetSel(wnd, es, (LPUINT32)wParam, (LPUINT32)lParam);
331 break;
333 case EM_SETSEL16:
334 DPRINTF_EDIT_MSG16("EM_SETSEL");
335 if (SLOWORD(lParam) == -1)
336 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
337 else
338 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
339 if (!wParam)
340 EDIT_EM_ScrollCaret(wnd, es);
341 result = 1;
342 break;
343 case EM_SETSEL32:
344 DPRINTF_EDIT_MSG32("EM_SETSEL");
345 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
346 result = 1;
347 break;
349 case EM_GETRECT16:
350 DPRINTF_EDIT_MSG16("EM_GETRECT");
351 if (lParam)
352 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
353 break;
354 case EM_GETRECT32:
355 DPRINTF_EDIT_MSG32("EM_GETRECT");
356 if (lParam)
357 CopyRect32((LPRECT32)lParam, &es->format_rect);
358 break;
360 case EM_SETRECT16:
361 DPRINTF_EDIT_MSG16("EM_SETRECT");
362 if ((es->style & ES_MULTILINE) && lParam) {
363 RECT32 rc;
364 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
365 EDIT_SetRectNP(wnd, es, &rc);
366 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
368 break;
369 case EM_SETRECT32:
370 DPRINTF_EDIT_MSG32("EM_SETRECT");
371 if ((es->style & ES_MULTILINE) && lParam) {
372 EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
373 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
375 break;
377 case EM_SETRECTNP16:
378 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
379 if ((es->style & ES_MULTILINE) && lParam) {
380 RECT32 rc;
381 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
382 EDIT_SetRectNP(wnd, es, &rc);
384 break;
385 case EM_SETRECTNP32:
386 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
387 if ((es->style & ES_MULTILINE) && lParam)
388 EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
389 break;
391 case EM_SCROLL16:
392 DPRINTF_EDIT_MSG16("EM_SCROLL");
393 /* fall through */
394 case EM_SCROLL32:
395 DPRINTF_EDIT_MSG32("EM_SCROLL");
396 result = EDIT_EM_Scroll(wnd, es, (INT32)wParam);
397 break;
399 case EM_LINESCROLL16:
400 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
401 wParam = (WPARAM32)(INT32)SHIWORD(lParam);
402 lParam = (LPARAM)(INT32)SLOWORD(lParam);
403 /* fall through */
404 case EM_LINESCROLL32:
405 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
406 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT32)wParam, (INT32)lParam);
407 break;
409 case EM_SCROLLCARET16:
410 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
411 /* fall through */
412 case EM_SCROLLCARET32:
413 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
414 EDIT_EM_ScrollCaret(wnd, es);
415 result = 1;
416 break;
418 case EM_GETMODIFY16:
419 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
420 /* fall through */
421 case EM_GETMODIFY32:
422 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
423 return ((es->flags & EF_MODIFIED) != 0);
424 break;
426 case EM_SETMODIFY16:
427 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
428 /* fall through */
429 case EM_SETMODIFY32:
430 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
431 if (wParam)
432 es->flags |= EF_MODIFIED;
433 else
434 es->flags &= ~EF_MODIFIED;
435 break;
437 case EM_GETLINECOUNT16:
438 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
439 /* fall through */
440 case EM_GETLINECOUNT32:
441 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
442 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
443 break;
445 case EM_LINEINDEX16:
446 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
447 if ((INT16)wParam == -1)
448 wParam = (WPARAM32)-1;
449 /* fall through */
450 case EM_LINEINDEX32:
451 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
452 result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT32)wParam);
453 break;
455 case EM_SETHANDLE16:
456 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
457 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
458 break;
459 case EM_SETHANDLE32:
460 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
461 EDIT_EM_SetHandle(wnd, es, (HLOCAL32)wParam);
462 break;
464 case EM_GETHANDLE16:
465 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
466 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
467 break;
468 case EM_GETHANDLE32:
469 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
470 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
471 break;
473 case EM_GETTHUMB16:
474 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
475 /* fall through */
476 case EM_GETTHUMB32:
477 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
478 result = EDIT_EM_GetThumb(wnd, es);
479 break;
481 /* messages 0x00bf and 0x00c0 missing from specs */
483 case WM_USER+15:
484 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
485 /* fall through */
486 case 0x00bf:
487 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
488 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
489 break;
491 case WM_USER+16:
492 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
493 /* fall through */
494 case 0x00c0:
495 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
496 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
497 break;
499 case EM_LINELENGTH16:
500 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
501 /* fall through */
502 case EM_LINELENGTH32:
503 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
504 result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT32)wParam);
505 break;
507 case EM_REPLACESEL16:
508 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
509 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
510 /* fall through */
511 case EM_REPLACESEL32:
512 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
513 EDIT_EM_ReplaceSel(wnd, es, (BOOL32)wParam, (LPCSTR)lParam);
514 break;
516 /* message 0x00c3 missing from specs */
518 case WM_USER+19:
519 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
520 /* fall through */
521 case 0x00c3:
522 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
523 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
524 break;
526 case EM_GETLINE16:
527 DPRINTF_EDIT_MSG16("EM_GETLINE");
528 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
529 /* fall through */
530 case EM_GETLINE32:
531 DPRINTF_EDIT_MSG32("EM_GETLINE");
532 result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT32)wParam, (LPSTR)lParam);
533 break;
535 case EM_LIMITTEXT16:
536 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
537 /* fall through */
538 case EM_SETLIMITTEXT32:
539 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
540 EDIT_EM_SetLimitText(wnd, es, (INT32)wParam);
541 break;
543 case EM_CANUNDO16:
544 DPRINTF_EDIT_MSG16("EM_CANUNDO");
545 /* fall through */
546 case EM_CANUNDO32:
547 DPRINTF_EDIT_MSG32("EM_CANUNDO");
548 result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
549 break;
551 case EM_UNDO16:
552 DPRINTF_EDIT_MSG16("EM_UNDO");
553 /* fall through */
554 case EM_UNDO32:
555 /* fall through */
556 case WM_UNDO:
557 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
558 result = (LRESULT)EDIT_EM_Undo(wnd, es);
559 break;
561 case EM_FMTLINES16:
562 DPRINTF_EDIT_MSG16("EM_FMTLINES");
563 /* fall through */
564 case EM_FMTLINES32:
565 DPRINTF_EDIT_MSG32("EM_FMTLINES");
566 result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL32)wParam);
567 break;
569 case EM_LINEFROMCHAR16:
570 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
571 /* fall through */
572 case EM_LINEFROMCHAR32:
573 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
574 result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT32)wParam);
575 break;
577 /* message 0x00ca missing from specs */
579 case WM_USER+26:
580 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
581 /* fall through */
582 case 0x00ca:
583 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
584 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
585 break;
587 case EM_SETTABSTOPS16:
588 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
589 result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT32)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
590 break;
591 case EM_SETTABSTOPS32:
592 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
593 result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT32)wParam, (LPINT32)lParam);
594 break;
596 case EM_SETPASSWORDCHAR16:
597 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
598 /* fall through */
599 case EM_SETPASSWORDCHAR32:
600 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
601 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
602 break;
604 case EM_EMPTYUNDOBUFFER16:
605 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
606 /* fall through */
607 case EM_EMPTYUNDOBUFFER32:
608 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
609 EDIT_EM_EmptyUndoBuffer(wnd, es);
610 break;
612 case EM_GETFIRSTVISIBLELINE16:
613 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
614 result = es->y_offset;
615 break;
616 case EM_GETFIRSTVISIBLELINE32:
617 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
618 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
619 break;
621 case EM_SETREADONLY16:
622 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
623 /* fall through */
624 case EM_SETREADONLY32:
625 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
626 if (wParam) {
627 wnd->dwStyle |= ES_READONLY;
628 es->style |= ES_READONLY;
629 } else {
630 wnd->dwStyle &= ~ES_READONLY;
631 es->style &= ~ES_READONLY;
633 return 1;
634 break;
636 case EM_SETWORDBREAKPROC16:
637 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
638 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
639 break;
640 case EM_SETWORDBREAKPROC32:
641 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
642 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROC32A)lParam);
643 break;
645 case EM_GETWORDBREAKPROC16:
646 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
647 result = (LRESULT)es->word_break_proc16;
648 break;
649 case EM_GETWORDBREAKPROC32:
650 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
651 result = (LRESULT)es->word_break_proc32A;
652 break;
654 case EM_GETPASSWORDCHAR16:
655 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
656 /* fall through */
657 case EM_GETPASSWORDCHAR32:
658 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
659 result = es->password_char;
660 break;
662 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
664 case EM_SETMARGINS32:
665 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
666 EDIT_EM_SetMargins(wnd, es, (INT32)wParam, SLOWORD(lParam), SHIWORD(lParam));
667 break;
669 case EM_GETMARGINS32:
670 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
671 result = MAKELONG(es->left_margin, es->right_margin);
672 break;
674 case EM_GETLIMITTEXT32:
675 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
676 result = es->buffer_limit;
677 break;
679 case EM_POSFROMCHAR32:
680 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
681 result = EDIT_EM_PosFromChar(wnd, es, (INT32)wParam, FALSE);
682 break;
684 case EM_CHARFROMPOS32:
685 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
686 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
687 break;
689 case WM_GETDLGCODE:
690 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
691 result = (es->style & ES_MULTILINE) ?
692 DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
693 DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
694 break;
696 case WM_CHAR:
697 DPRINTF_EDIT_MSG32("WM_CHAR");
698 EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
699 break;
701 case WM_CLEAR:
702 DPRINTF_EDIT_MSG32("WM_CLEAR");
703 EDIT_WM_Clear(wnd, es);
704 break;
706 case WM_COMMAND:
707 DPRINTF_EDIT_MSG32("WM_COMMAND");
708 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND32)lParam);
709 break;
711 case WM_CONTEXTMENU:
712 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
713 EDIT_WM_ContextMenu(wnd, es, (HWND32)wParam, SLOWORD(lParam), SHIWORD(lParam));
714 break;
716 case WM_COPY:
717 DPRINTF_EDIT_MSG32("WM_COPY");
718 EDIT_WM_Copy(wnd, es);
719 break;
721 case WM_CUT:
722 DPRINTF_EDIT_MSG32("WM_CUT");
723 EDIT_WM_Cut(wnd, es);
724 break;
726 case WM_ENABLE:
727 DPRINTF_EDIT_MSG32("WM_ENABLE");
728 InvalidateRect32(hwnd, NULL, TRUE);
729 break;
731 case WM_ERASEBKGND:
732 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
733 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC32)wParam);
734 break;
736 case WM_GETFONT:
737 DPRINTF_EDIT_MSG32("WM_GETFONT");
738 result = (LRESULT)es->font;
739 break;
741 case WM_GETTEXT:
742 DPRINTF_EDIT_MSG32("WM_GETTEXT");
743 result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT32)wParam, (LPSTR)lParam);
744 break;
746 case WM_GETTEXTLENGTH:
747 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
748 result = lstrlen32A(es->text);
749 break;
751 case WM_HSCROLL:
752 DPRINTF_EDIT_MSG32("WM_HSCROLL");
753 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)lParam);
754 break;
756 case WM_KEYDOWN:
757 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
758 result = EDIT_WM_KeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
759 break;
761 case WM_KILLFOCUS:
762 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
763 result = EDIT_WM_KillFocus(wnd, es, (HWND32)wParam);
764 break;
766 case WM_LBUTTONDBLCLK:
767 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
768 result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
769 break;
771 case WM_LBUTTONDOWN:
772 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
773 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
774 break;
776 case WM_LBUTTONUP:
777 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
778 result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
779 break;
781 case WM_MOUSEACTIVATE:
783 * FIXME: maybe DefWindowProc() screws up, but it seems that
784 * modalless dialog boxes need this. If we don't do this, the focus
785 * will _not_ be set by DefWindowProc() for edit controls in a
786 * modalless dialog box ???
788 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
789 SetFocus32(wnd->hwndSelf);
790 result = MA_ACTIVATE;
791 break;
793 case WM_MOUSEMOVE:
795 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
797 result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
798 break;
800 case WM_PAINT:
801 DPRINTF_EDIT_MSG32("WM_PAINT");
802 EDIT_WM_Paint(wnd, es);
803 break;
805 case WM_PASTE:
806 DPRINTF_EDIT_MSG32("WM_PASTE");
807 EDIT_WM_Paste(wnd, es);
808 break;
810 case WM_SETFOCUS:
811 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
812 EDIT_WM_SetFocus(wnd, es, (HWND32)wParam);
813 break;
815 case WM_SETFONT:
816 DPRINTF_EDIT_MSG32("WM_SETFONT");
817 EDIT_WM_SetFont(wnd, es, (HFONT32)wParam, LOWORD(lParam) != 0);
818 break;
820 case WM_SETTEXT:
821 DPRINTF_EDIT_MSG32("WM_SETTEXT");
822 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
823 result = TRUE;
824 break;
826 case WM_SIZE:
827 DPRINTF_EDIT_MSG32("WM_SIZE");
828 EDIT_WM_Size(wnd, es, (UINT32)wParam, LOWORD(lParam), HIWORD(lParam));
829 break;
831 case WM_SYSKEYDOWN:
832 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
833 result = EDIT_WM_SysKeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
834 break;
836 case WM_TIMER:
837 DPRINTF_EDIT_MSG32("WM_TIMER");
838 EDIT_WM_Timer(wnd, es, (INT32)wParam, (TIMERPROC32)lParam);
839 break;
841 case WM_VSCROLL:
842 DPRINTF_EDIT_MSG32("WM_VSCROLL");
843 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)(lParam));
844 break;
846 default:
847 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
848 break;
850 EDIT_UnlockBuffer(wnd, es, FALSE);
851 return result;
855 /*********************************************************************
857 * EDIT_BuildLineDefs_ML
859 * Build linked list of text lines.
860 * Lines can end with '\0' (last line), a character (if it is wrapped),
861 * a soft return '\r\r\n' or a hard return '\r\n'
864 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
866 HDC32 dc;
867 HFONT32 old_font = 0;
868 LPSTR start, cp;
869 INT32 fw;
870 LINEDEF *current_def;
871 LINEDEF **previous_next;
873 current_def = es->first_line_def;
874 do {
875 LINEDEF *next_def = current_def->next;
876 HeapFree(es->heap, 0, current_def);
877 current_def = next_def;
878 } while (current_def);
879 es->line_count = 0;
880 es->text_width = 0;
882 dc = GetDC32(wnd->hwndSelf);
883 if (es->font)
884 old_font = SelectObject32(dc, es->font);
886 fw = es->format_rect.right - es->format_rect.left;
887 start = es->text;
888 previous_next = &es->first_line_def;
889 do {
890 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
891 current_def->next = NULL;
892 cp = start;
893 while (*cp) {
894 if ((*cp == '\r') && (*(cp + 1) == '\n'))
895 break;
896 cp++;
898 if (!(*cp)) {
899 current_def->ending = END_0;
900 current_def->net_length = lstrlen32A(start);
901 } else if ((cp > start) && (*(cp - 1) == '\r')) {
902 current_def->ending = END_SOFT;
903 current_def->net_length = cp - start - 1;
904 } else {
905 current_def->ending = END_HARD;
906 current_def->net_length = cp - start;
908 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
909 start, current_def->net_length,
910 es->tabs_count, es->tabs));
911 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
912 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
913 INT32 next = 0;
914 INT32 prev;
915 do {
916 prev = next;
917 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
918 prev + 1, current_def->net_length, WB_RIGHT);
919 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
920 start, next, es->tabs_count, es->tabs));
921 } while (current_def->width <= fw);
922 if (!prev) {
923 next = 0;
924 do {
925 prev = next;
926 next++;
927 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
928 start, next, es->tabs_count, es->tabs));
929 } while (current_def->width <= fw);
930 if (!prev)
931 prev = 1;
933 current_def->net_length = prev;
934 current_def->ending = END_WRAP;
935 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc, start,
936 current_def->net_length, es->tabs_count, es->tabs));
938 switch (current_def->ending) {
939 case END_SOFT:
940 current_def->length = current_def->net_length + 3;
941 break;
942 case END_HARD:
943 current_def->length = current_def->net_length + 2;
944 break;
945 case END_WRAP:
946 case END_0:
947 current_def->length = current_def->net_length;
948 break;
950 es->text_width = MAX(es->text_width, current_def->width);
951 start += current_def->length;
952 *previous_next = current_def;
953 previous_next = &current_def->next;
954 es->line_count++;
955 } while (current_def->ending != END_0);
956 if (es->font)
957 SelectObject32(dc, old_font);
958 ReleaseDC32(wnd->hwndSelf, dc);
962 /*********************************************************************
964 * EDIT_CallWordBreakProc
966 * Call appropriate WordBreakProc (internal or external).
968 * Note: The "start" argument should always be an index refering
969 * to es->text. The actual wordbreak proc might be
970 * 16 bit, so we can't always pass any 32 bit LPSTR.
971 * Hence we assume that es->text is the buffer that holds
972 * the string under examination (we can decide this for ourselves).
975 static INT32 EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT32 start, INT32 index, INT32 count, INT32 action)
977 if (es->word_break_proc16) {
978 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
979 SEGPTR segptr = LocalLock16(hloc16);
980 INT32 ret = (INT32)Callbacks->CallWordBreakProc(es->word_break_proc16,
981 segptr + start, index, count, action);
982 LocalUnlock16(hloc16);
983 return ret;
985 else if (es->word_break_proc32A)
987 TRACE(relay, "(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
988 es->word_break_proc32A, es->text + start, index,
989 count, action );
990 return (INT32)es->word_break_proc32A( es->text + start, index,
991 count, action );
993 else
994 return EDIT_WordBreakProc(es->text + start, index, count, action);
998 /*********************************************************************
1000 * EDIT_CharFromPos
1002 * Beware: This is not the function called on EM_CHARFROMPOS
1003 * The position _can_ be outside the formatting / client
1004 * rectangle
1005 * The return value is only the character index
1008 static INT32 EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y, LPBOOL32 after_wrap)
1010 INT32 index;
1011 HDC32 dc;
1012 HFONT32 old_font = 0;
1014 if (es->style & ES_MULTILINE) {
1015 INT32 line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1016 INT32 line_index = 0;
1017 LINEDEF *line_def = es->first_line_def;
1018 INT32 low, high;
1019 while ((line > 0) && line_def->next) {
1020 line_index += line_def->length;
1021 line_def = line_def->next;
1022 line--;
1024 x += es->x_offset - es->format_rect.left;
1025 if (x >= line_def->width) {
1026 if (after_wrap)
1027 *after_wrap = (line_def->ending == END_WRAP);
1028 return line_index + line_def->net_length;
1030 if (x <= 0) {
1031 if (after_wrap)
1032 *after_wrap = FALSE;
1033 return line_index;
1035 dc = GetDC32(wnd->hwndSelf);
1036 if (es->font)
1037 old_font = SelectObject32(dc, es->font);
1038 low = line_index + 1;
1039 high = line_index + line_def->net_length + 1;
1040 while (low < high - 1)
1042 INT32 mid = (low + high) / 2;
1043 if (LOWORD(GetTabbedTextExtent32A(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1044 else low = mid;
1046 index = low;
1048 if (after_wrap)
1049 *after_wrap = ((index == line_index + line_def->net_length) &&
1050 (line_def->ending == END_WRAP));
1051 } else {
1052 LPSTR text;
1053 SIZE32 size;
1054 if (after_wrap)
1055 *after_wrap = FALSE;
1056 x -= es->format_rect.left;
1057 if (!x)
1058 return es->x_offset;
1059 text = EDIT_GetPasswordPointer_SL(wnd, es);
1060 dc = GetDC32(wnd->hwndSelf);
1061 if (es->font)
1062 old_font = SelectObject32(dc, es->font);
1063 if (x < 0)
1065 INT32 low = 0;
1066 INT32 high = es->x_offset;
1067 while (low < high - 1)
1069 INT32 mid = (low + high) / 2;
1070 GetTextExtentPoint32A( dc, text + mid,
1071 es->x_offset - mid, &size );
1072 if (size.cx > -x) low = mid;
1073 else high = mid;
1075 index = low;
1077 else
1079 INT32 low = es->x_offset;
1080 INT32 high = lstrlen32A(es->text) + 1;
1081 while (low < high - 1)
1083 INT32 mid = (low + high) / 2;
1084 GetTextExtentPoint32A( dc, text + es->x_offset,
1085 mid - es->x_offset, &size );
1086 if (size.cx > x) high = mid;
1087 else low = mid;
1089 index = low;
1091 if (es->style & ES_PASSWORD)
1092 HeapFree(es->heap, 0 ,text);
1094 if (es->font)
1095 SelectObject32(dc, old_font);
1096 ReleaseDC32(wnd->hwndSelf, dc);
1097 return index;
1101 /*********************************************************************
1103 * EDIT_ConfinePoint
1105 * adjusts the point to be within the formatting rectangle
1106 * (so CharFromPos returns the nearest _visible_ character)
1109 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT32 x, LPINT32 y)
1111 *x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
1112 *y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
1116 /*********************************************************************
1118 * EDIT_GetLineRect
1120 * Calculates the bounding rectangle for a line from a starting
1121 * column to an ending column.
1124 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT32 line, INT32 scol, INT32 ecol, LPRECT32 rc)
1126 INT32 line_index = EDIT_EM_LineIndex(wnd, es, line);
1128 if (es->style & ES_MULTILINE)
1129 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1130 else
1131 rc->top = es->format_rect.top;
1132 rc->bottom = rc->top + es->line_height;
1133 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1134 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1138 /*********************************************************************
1140 * EDIT_GetPasswordPointer_SL
1142 * note: caller should free the (optionally) allocated buffer
1145 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
1147 if (es->style & ES_PASSWORD) {
1148 INT32 len = lstrlen32A(es->text);
1149 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1150 RtlFillMemory(text, len, es->password_char);
1151 text[len] = '\0';
1152 return text;
1153 } else
1154 return es->text;
1158 /*********************************************************************
1160 * EDIT_LockBuffer
1162 * This acts as a LOCAL_Lock(), but it locks only once. This way
1163 * you can call it whenever you like, without unlocking.
1166 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1168 if (!es) {
1169 ERR(edit, "no EDITSTATE ... please report\n");
1170 return;
1172 if (!(es->style & ES_MULTILINE))
1173 return;
1174 if (!es->text) {
1175 if (es->hloc32)
1176 es->text = LocalLock32(es->hloc32);
1177 else if (es->hloc16)
1178 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1179 else {
1180 ERR(edit, "no buffer ... please report\n");
1181 return;
1184 es->lock_count++;
1188 /*********************************************************************
1190 * EDIT_SL_InvalidateText
1192 * Called from EDIT_InvalidateText().
1193 * Does the job for single-line controls only.
1196 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1198 RECT32 line_rect;
1199 RECT32 rc;
1201 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1202 if (IntersectRect32(&rc, &line_rect, &es->format_rect))
1203 InvalidateRect32(wnd->hwndSelf, &rc, FALSE);
1207 /*********************************************************************
1209 * EDIT_ML_InvalidateText
1211 * Called from EDIT_InvalidateText().
1212 * Does the job for multi-line controls only.
1215 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1217 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1218 INT32 sl = EDIT_EM_LineFromChar(wnd, es, start);
1219 INT32 el = EDIT_EM_LineFromChar(wnd, es, end);
1220 INT32 sc;
1221 INT32 ec;
1222 RECT32 rc1;
1223 RECT32 rcWnd;
1224 RECT32 rcLine;
1225 RECT32 rcUpdate;
1226 INT32 l;
1228 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1229 return;
1231 sc = start - EDIT_EM_LineIndex(wnd, es, sl);
1232 ec = end - EDIT_EM_LineIndex(wnd, es, el);
1233 if (sl < es->y_offset) {
1234 sl = es->y_offset;
1235 sc = 0;
1237 if (el > es->y_offset + vlc) {
1238 el = es->y_offset + vlc;
1239 ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
1241 GetClientRect32(wnd->hwndSelf, &rc1);
1242 IntersectRect32(&rcWnd, &rc1, &es->format_rect);
1243 if (sl == el) {
1244 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1245 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1246 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1247 } else {
1248 EDIT_GetLineRect(wnd, es, sl, sc,
1249 EDIT_EM_LineLength(wnd, es,
1250 EDIT_EM_LineIndex(wnd, es, sl)),
1251 &rcLine);
1252 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1253 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1254 for (l = sl + 1 ; l < el ; l++) {
1255 EDIT_GetLineRect(wnd, es, l, 0,
1256 EDIT_EM_LineLength(wnd, es,
1257 EDIT_EM_LineIndex(wnd, es, l)),
1258 &rcLine);
1259 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1260 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1262 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1263 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1264 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1269 /*********************************************************************
1271 * EDIT_InvalidateText
1273 * Invalidate the text from offset start upto, but not including,
1274 * offset end. Useful for (re)painting the selection.
1275 * Regions outside the linewidth are not invalidated.
1276 * end == -1 means end == TextLength.
1277 * start and end need not be ordered.
1280 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1282 if (end == start)
1283 return;
1285 if (end == -1)
1286 end = lstrlen32A(es->text);
1288 ORDER_INT32(start, end);
1290 if (es->style & ES_MULTILINE)
1291 EDIT_ML_InvalidateText(wnd, es, start, end);
1292 else
1293 EDIT_SL_InvalidateText(wnd, es, start, end);
1297 /*********************************************************************
1299 * EDIT_MakeFit
1301 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1304 static BOOL32 EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT32 size)
1306 HLOCAL32 hNew32;
1307 HLOCAL16 hNew16;
1309 if (size <= es->buffer_size)
1310 return TRUE;
1311 if (size > es->buffer_limit) {
1312 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1313 return FALSE;
1315 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1316 if (size > es->buffer_limit)
1317 size = es->buffer_limit;
1319 TRACE(edit, "trying to ReAlloc to %d+1\n", size);
1321 EDIT_UnlockBuffer(wnd, es, TRUE);
1322 if (es->text) {
1323 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1324 es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1325 else
1326 es->buffer_size = 0;
1327 } else if (es->hloc32) {
1328 if ((hNew32 = LocalReAlloc32(es->hloc32, size + 1, 0))) {
1329 TRACE(edit, "Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1330 es->hloc32 = hNew32;
1331 es->buffer_size = MIN(LocalSize32(es->hloc32) - 1, es->buffer_limit);
1333 } else if (es->hloc16) {
1334 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1335 TRACE(edit, "Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1336 es->hloc16 = hNew16;
1337 es->buffer_size = MIN(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1340 if (es->buffer_size < size) {
1341 EDIT_LockBuffer(wnd, es);
1342 WARN(edit, "FAILED ! We now have %d+1\n", es->buffer_size);
1343 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1344 return FALSE;
1345 } else {
1346 EDIT_LockBuffer(wnd, es);
1347 TRACE(edit, "We now have %d+1\n", es->buffer_size);
1348 return TRUE;
1353 /*********************************************************************
1355 * EDIT_MakeUndoFit
1357 * Try to fit size + 1 bytes in the undo buffer.
1360 static BOOL32 EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT32 size)
1362 if (size <= es->undo_buffer_size)
1363 return TRUE;
1364 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1366 TRACE(edit, "trying to ReAlloc to %d+1\n", size);
1368 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1369 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1370 if (es->undo_buffer_size < size) {
1371 WARN(edit, "FAILED ! We now have %d+1\n", es->undo_buffer_size);
1372 return FALSE;
1374 return TRUE;
1376 return FALSE;
1380 /*********************************************************************
1382 * EDIT_MoveBackward
1385 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1387 INT32 e = es->selection_end;
1389 if (e) {
1390 e--;
1391 if ((es->style & ES_MULTILINE) && e &&
1392 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1393 e--;
1394 if (e && (es->text[e - 1] == '\r'))
1395 e--;
1398 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1399 EDIT_EM_ScrollCaret(wnd, es);
1403 /*********************************************************************
1405 * EDIT_MoveDown_ML
1407 * Only for multi line controls
1408 * Move the caret one line down, on a column with the nearest
1409 * x coordinate on the screen (might be a different column).
1412 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1414 INT32 s = es->selection_start;
1415 INT32 e = es->selection_end;
1416 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1417 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1418 INT32 x = SLOWORD(pos);
1419 INT32 y = SHIWORD(pos);
1421 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1422 if (!extend)
1423 s = e;
1424 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1425 EDIT_EM_ScrollCaret(wnd, es);
1429 /*********************************************************************
1431 * EDIT_MoveEnd
1434 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL32 extend)
1436 BOOL32 after_wrap = FALSE;
1437 INT32 e;
1439 if (es->style & ES_MULTILINE)
1440 e = EDIT_CharFromPos(wnd, es, 0x7fffffff,
1441 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1442 else
1443 e = lstrlen32A(es->text);
1444 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1445 EDIT_EM_ScrollCaret(wnd, es);
1449 /*********************************************************************
1451 * EDIT_MoveForward
1454 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1456 INT32 e = es->selection_end;
1458 if (es->text[e]) {
1459 e++;
1460 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1461 if (es->text[e] == '\n')
1462 e++;
1463 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1464 e += 2;
1467 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1468 EDIT_EM_ScrollCaret(wnd, es);
1472 /*********************************************************************
1474 * EDIT_MoveHome
1476 * Home key: move to beginning of line.
1479 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL32 extend)
1481 INT32 e;
1483 if (es->style & ES_MULTILINE)
1484 e = EDIT_CharFromPos(wnd, es, 0x80000000,
1485 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1486 else
1487 e = 0;
1488 EDIT_EM_SetSel(wnd, es, e, extend ? es->selection_start : e, FALSE);
1489 EDIT_EM_ScrollCaret(wnd, es);
1493 /*********************************************************************
1495 * EDIT_MovePageDown_ML
1497 * Only for multi line controls
1498 * Move the caret one page down, on a column with the nearest
1499 * x coordinate on the screen (might be a different column).
1502 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1504 INT32 s = es->selection_start;
1505 INT32 e = es->selection_end;
1506 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1507 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1508 INT32 x = SLOWORD(pos);
1509 INT32 y = SHIWORD(pos);
1511 e = EDIT_CharFromPos(wnd, es, x,
1512 y + (es->format_rect.bottom - es->format_rect.top),
1513 &after_wrap);
1514 if (!extend)
1515 s = e;
1516 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1517 EDIT_EM_ScrollCaret(wnd, es);
1521 /*********************************************************************
1523 * EDIT_MovePageUp_ML
1525 * Only for multi line controls
1526 * Move the caret one page up, on a column with the nearest
1527 * x coordinate on the screen (might be a different column).
1530 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1532 INT32 s = es->selection_start;
1533 INT32 e = es->selection_end;
1534 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1535 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1536 INT32 x = SLOWORD(pos);
1537 INT32 y = SHIWORD(pos);
1539 e = EDIT_CharFromPos(wnd, es, x,
1540 y - (es->format_rect.bottom - es->format_rect.top),
1541 &after_wrap);
1542 if (!extend)
1543 s = e;
1544 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1545 EDIT_EM_ScrollCaret(wnd, es);
1549 /*********************************************************************
1551 * EDIT_MoveUp_ML
1553 * Only for multi line controls
1554 * Move the caret one line up, on a column with the nearest
1555 * x coordinate on the screen (might be a different column).
1558 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1560 INT32 s = es->selection_start;
1561 INT32 e = es->selection_end;
1562 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1563 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1564 INT32 x = SLOWORD(pos);
1565 INT32 y = SHIWORD(pos);
1567 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1568 if (!extend)
1569 s = e;
1570 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1571 EDIT_EM_ScrollCaret(wnd, es);
1575 /*********************************************************************
1577 * EDIT_MoveWordBackward
1580 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1582 INT32 s = es->selection_start;
1583 INT32 e = es->selection_end;
1584 INT32 l;
1585 INT32 ll;
1586 INT32 li;
1588 l = EDIT_EM_LineFromChar(wnd, es, e);
1589 ll = EDIT_EM_LineLength(wnd, es, e);
1590 li = EDIT_EM_LineIndex(wnd, es, l);
1591 if (e - li == 0) {
1592 if (l) {
1593 li = EDIT_EM_LineIndex(wnd, es, l - 1);
1594 e = li + EDIT_EM_LineLength(wnd, es, li);
1596 } else {
1597 e = li + (INT32)EDIT_CallWordBreakProc(wnd, es,
1598 li, e - li, ll, WB_LEFT);
1600 if (!extend)
1601 s = e;
1602 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1603 EDIT_EM_ScrollCaret(wnd, es);
1607 /*********************************************************************
1609 * EDIT_MoveWordForward
1612 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1614 INT32 s = es->selection_start;
1615 INT32 e = es->selection_end;
1616 INT32 l;
1617 INT32 ll;
1618 INT32 li;
1620 l = EDIT_EM_LineFromChar(wnd, es, e);
1621 ll = EDIT_EM_LineLength(wnd, es, e);
1622 li = EDIT_EM_LineIndex(wnd, es, l);
1623 if (e - li == ll) {
1624 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1625 e = EDIT_EM_LineIndex(wnd, es, l + 1);
1626 } else {
1627 e = li + EDIT_CallWordBreakProc(wnd, es,
1628 li, e - li + 1, ll, WB_RIGHT);
1630 if (!extend)
1631 s = e;
1632 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1633 EDIT_EM_ScrollCaret(wnd, es);
1637 /*********************************************************************
1639 * EDIT_PaintLine
1642 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 line, BOOL32 rev)
1644 INT32 s = es->selection_start;
1645 INT32 e = es->selection_end;
1646 INT32 li;
1647 INT32 ll;
1648 INT32 x;
1649 INT32 y;
1650 LRESULT pos;
1652 if (es->style & ES_MULTILINE) {
1653 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1654 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1655 return;
1656 } else if (line)
1657 return;
1659 TRACE(edit, "line=%d\n", line);
1661 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
1662 x = SLOWORD(pos);
1663 y = SHIWORD(pos);
1664 li = EDIT_EM_LineIndex(wnd, es, line);
1665 ll = EDIT_EM_LineLength(wnd, es, li);
1666 s = es->selection_start;
1667 e = es->selection_end;
1668 ORDER_INT32(s, e);
1669 s = MIN(li + ll, MAX(li, s));
1670 e = MIN(li + ll, MAX(li, e));
1671 if (rev && (s != e) &&
1672 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1673 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
1674 x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
1675 x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
1676 } else
1677 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
1681 /*********************************************************************
1683 * EDIT_PaintText
1686 static INT32 EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 x, INT32 y, INT32 line, INT32 col, INT32 count, BOOL32 rev)
1688 COLORREF BkColor;
1689 COLORREF TextColor;
1690 INT32 ret;
1691 INT32 li;
1692 SIZE32 size;
1694 if (!count)
1695 return 0;
1696 BkColor = GetBkColor32(dc);
1697 TextColor = GetTextColor32(dc);
1698 if (rev) {
1699 SetBkColor32(dc, GetSysColor32(COLOR_HIGHLIGHT));
1700 SetTextColor32(dc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
1702 li = EDIT_EM_LineIndex(wnd, es, line);
1703 if (es->style & ES_MULTILINE) {
1704 ret = (INT32)LOWORD(TabbedTextOut32A(dc, x, y, es->text + li + col, count,
1705 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1706 } else {
1707 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
1708 TextOut32A(dc, x, y, text + li + col, count);
1709 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1710 ret = size.cx;
1711 if (es->style & ES_PASSWORD)
1712 HeapFree(es->heap, 0, text);
1714 if (rev) {
1715 SetBkColor32(dc, BkColor);
1716 SetTextColor32(dc, TextColor);
1718 return ret;
1722 /*********************************************************************
1724 * EM_SCROLLCARET
1727 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
1729 if (es->style & ES_MULTILINE) {
1730 INT32 l;
1731 INT32 li;
1732 INT32 vlc;
1733 INT32 ww;
1734 INT32 cw = es->char_width;
1735 INT32 x;
1736 INT32 dy = 0;
1737 INT32 dx = 0;
1739 l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
1740 li = EDIT_EM_LineIndex(wnd, es, l);
1741 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
1742 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1743 if (l >= es->y_offset + vlc)
1744 dy = l - vlc + 1 - es->y_offset;
1745 if (l < es->y_offset)
1746 dy = l - es->y_offset;
1747 ww = es->format_rect.right - es->format_rect.left;
1748 if (x < es->format_rect.left)
1749 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1750 if (x > es->format_rect.right)
1751 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1752 if (dy || dx)
1753 EDIT_EM_LineScroll(wnd, es, dx, dy);
1754 } else {
1755 INT32 x;
1756 INT32 goal;
1757 INT32 format_width;
1759 if (!(es->style & ES_AUTOHSCROLL))
1760 return;
1762 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1763 format_width = es->format_rect.right - es->format_rect.left;
1764 if (x < es->format_rect.left) {
1765 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1766 do {
1767 es->x_offset--;
1768 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1769 } while ((x < goal) && es->x_offset);
1770 /* FIXME: use ScrollWindow() somehow to improve performance */
1771 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
1772 } else if (x > es->format_rect.right) {
1773 INT32 x_last;
1774 INT32 len = lstrlen32A(es->text);
1775 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1776 do {
1777 es->x_offset++;
1778 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1779 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
1780 } while ((x > goal) && (x_last > es->format_rect.right));
1781 /* FIXME: use ScrollWindow() somehow to improve performance */
1782 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
1788 /*********************************************************************
1790 * EDIT_SetRectNP
1792 * note: this is not (exactly) the handler called on EM_SETRECTNP
1793 * it is also used to set the rect of a single line control
1796 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT32 rc)
1798 CopyRect32(&es->format_rect, rc);
1799 if (es->style & WS_BORDER) {
1800 INT32 bw = GetSystemMetrics32(SM_CXBORDER) + 1;
1801 es->format_rect.left += bw;
1802 es->format_rect.top += bw;
1803 es->format_rect.right -= bw;
1804 es->format_rect.bottom -= bw;
1806 es->format_rect.left += es->left_margin;
1807 es->format_rect.right -= es->right_margin;
1808 es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
1809 if (es->style & ES_MULTILINE)
1810 es->format_rect.bottom = es->format_rect.top +
1811 MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1812 else
1813 es->format_rect.bottom = es->format_rect.top + es->line_height;
1814 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1815 EDIT_BuildLineDefs_ML(wnd, es);
1819 /*********************************************************************
1821 * EDIT_UnlockBuffer
1824 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL32 force)
1826 if (!es) {
1827 ERR(edit, "no EDITSTATE ... please report\n");
1828 return;
1830 if (!(es->style & ES_MULTILINE))
1831 return;
1832 if (!es->lock_count) {
1833 ERR(edit, "lock_count == 0 ... please report\n");
1834 return;
1836 if (!es->text) {
1837 ERR(edit, "es->text == 0 ... please report\n");
1838 return;
1840 if (force || (es->lock_count == 1)) {
1841 if (es->hloc32) {
1842 LocalUnlock32(es->hloc32);
1843 es->text = NULL;
1844 } else if (es->hloc16) {
1845 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1846 es->text = NULL;
1849 es->lock_count--;
1853 /*********************************************************************
1855 * EDIT_WordBreakProc
1857 * Find the beginning of words.
1858 * Note: unlike the specs for a WordBreakProc, this function only
1859 * allows to be called without linebreaks between s[0] upto
1860 * s[count - 1]. Remember it is only called
1861 * internally, so we can decide this for ourselves.
1864 static INT32 EDIT_WordBreakProc(LPSTR s, INT32 index, INT32 count, INT32 action)
1866 INT32 ret = 0;
1868 TRACE(edit, "s=%p, index=%u, count=%u, action=%d\n",
1869 s, index, count, action);
1871 switch (action) {
1872 case WB_LEFT:
1873 if (!count)
1874 break;
1875 if (index)
1876 index--;
1877 if (s[index] == ' ') {
1878 while (index && (s[index] == ' '))
1879 index--;
1880 if (index) {
1881 while (index && (s[index] != ' '))
1882 index--;
1883 if (s[index] == ' ')
1884 index++;
1886 } else {
1887 while (index && (s[index] != ' '))
1888 index--;
1889 if (s[index] == ' ')
1890 index++;
1892 ret = index;
1893 break;
1894 case WB_RIGHT:
1895 if (!count)
1896 break;
1897 if (index)
1898 index--;
1899 if (s[index] == ' ')
1900 while ((index < count) && (s[index] == ' ')) index++;
1901 else {
1902 while (s[index] && (s[index] != ' ') && (index < count))
1903 index++;
1904 while ((s[index] == ' ') && (index < count)) index++;
1906 ret = index;
1907 break;
1908 case WB_ISDELIMITER:
1909 ret = (s[index] == ' ');
1910 break;
1911 default:
1912 ERR(edit, "unknown action code, please report !\n");
1913 break;
1915 return ret;
1919 /*********************************************************************
1921 * EM_CHARFROMPOS
1923 * FIXME: do the specs mean to return LineIndex or LineNumber ???
1924 * Let's assume LineIndex is meant
1925 * FIXME: do the specs mean to return -1 if outside client area or
1926 * if outside formatting rectangle ???
1929 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y)
1931 POINT32 pt;
1932 RECT32 rc;
1933 INT32 index;
1935 pt.x = x;
1936 pt.y = y;
1937 GetClientRect32(wnd->hwndSelf, &rc);
1938 if (!PtInRect32(&rc, pt))
1939 return -1;
1941 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
1942 return MAKELONG(index, EDIT_EM_LineIndex(wnd, es,
1943 EDIT_EM_LineFromChar(wnd, es, index)));
1947 /*********************************************************************
1949 * EM_FMTLINES
1951 * Enable or disable soft breaks.
1953 static BOOL32 EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL32 add_eol)
1955 es->flags &= ~EF_USE_SOFTBRK;
1956 if (add_eol) {
1957 es->flags |= EF_USE_SOFTBRK;
1958 FIXME(edit, "soft break enabled, not implemented\n");
1960 return add_eol;
1964 /*********************************************************************
1966 * EM_GETHANDLE
1968 * Hopefully this won't fire back at us.
1969 * We always start with a fixed buffer in our own heap.
1970 * However, with this message a 32 bit application requests
1971 * a handle to 32 bit moveable local heap memory, where it expects
1972 * to find the text.
1973 * It's a pity that from this moment on we have to use this
1974 * local heap, because applications may rely on the handle
1975 * in the future.
1977 * In this function we'll try to switch to local heap.
1980 static HLOCAL32 EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
1982 HLOCAL32 newBuf;
1983 LPSTR newText;
1984 INT32 newSize;
1986 if (!(es->style & ES_MULTILINE))
1987 return 0;
1989 if (es->hloc32)
1990 return es->hloc32;
1991 else if (es->hloc16)
1992 return (HLOCAL32)es->hloc16;
1994 if (!(newBuf = LocalAlloc32(LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
1995 ERR(edit, "could not allocate new 32 bit buffer\n");
1996 return 0;
1998 newSize = MIN(LocalSize32(newBuf) - 1, es->buffer_limit);
1999 if (!(newText = LocalLock32(newBuf))) {
2000 ERR(edit, "could not lock new 32 bit buffer\n");
2001 LocalFree32(newBuf);
2002 return 0;
2004 lstrcpy32A(newText, es->text);
2005 EDIT_UnlockBuffer(wnd, es, TRUE);
2006 if (es->text)
2007 HeapFree(es->heap, 0, es->text);
2008 es->hloc32 = newBuf;
2009 es->hloc16 = (HLOCAL16)NULL;
2010 es->buffer_size = newSize;
2011 es->text = newText;
2012 EDIT_LockBuffer(wnd, es);
2013 TRACE(edit, "switched to 32 bit local heap\n");
2015 return es->hloc32;
2019 /*********************************************************************
2021 * EM_GETHANDLE16
2023 * Hopefully this won't fire back at us.
2024 * We always start with a buffer in 32 bit linear memory.
2025 * However, with this message a 16 bit application requests
2026 * a handle of 16 bit local heap memory, where it expects to find
2027 * the text.
2028 * It's a pitty that from this moment on we have to use this
2029 * local heap, because applications may rely on the handle
2030 * in the future.
2032 * In this function we'll try to switch to local heap.
2034 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2036 HLOCAL16 newBuf;
2037 LPSTR newText;
2038 INT32 newSize;
2040 if (!(es->style & ES_MULTILINE))
2041 return 0;
2043 if (es->hloc16)
2044 return es->hloc16;
2046 if (!LOCAL_HeapSize(wnd->hInstance)) {
2047 if (!LocalInit(wnd->hInstance, 0,
2048 GlobalSize16(wnd->hInstance))) {
2049 ERR(edit, "could not initialize local heap\n");
2050 return 0;
2052 TRACE(edit, "local heap initialized\n");
2054 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
2055 ERR(edit, "could not allocate new 16 bit buffer\n");
2056 return 0;
2058 newSize = MIN(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2059 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2060 ERR(edit, "could not lock new 16 bit buffer\n");
2061 LOCAL_Free(wnd->hInstance, newBuf);
2062 return 0;
2064 lstrcpy32A(newText, es->text);
2065 EDIT_UnlockBuffer(wnd, es, TRUE);
2066 if (es->text)
2067 HeapFree(es->heap, 0, es->text);
2068 else if (es->hloc32) {
2069 while (LocalFree32(es->hloc32)) ;
2070 LocalFree32(es->hloc32);
2072 es->hloc32 = (HLOCAL32)NULL;
2073 es->hloc16 = newBuf;
2074 es->buffer_size = newSize;
2075 es->text = newText;
2076 EDIT_LockBuffer(wnd, es);
2077 TRACE(edit, "switched to 16 bit buffer\n");
2079 return es->hloc16;
2083 /*********************************************************************
2085 * EM_GETLINE
2088 static INT32 EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT32 line, LPSTR lpch)
2090 LPSTR src;
2091 INT32 len;
2092 INT32 i;
2094 if (es->style & ES_MULTILINE) {
2095 if (line >= es->line_count)
2096 return 0;
2097 } else
2098 line = 0;
2099 src = es->text + EDIT_EM_LineIndex(wnd, es, line);
2100 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, line));
2101 for (i = 0 ; i < len ; i++) {
2102 *lpch = *src;
2103 src++;
2104 lpch++;
2106 return (LRESULT)len;
2110 /*********************************************************************
2112 * EM_GETSEL
2115 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT32 start, LPUINT32 end)
2117 UINT32 s = es->selection_start;
2118 UINT32 e = es->selection_end;
2120 ORDER_UINT32(s, e);
2121 if (start)
2122 *start = s;
2123 if (end)
2124 *end = e;
2125 return MAKELONG(s, e);
2129 /*********************************************************************
2131 * EM_GETTHUMB
2133 * FIXME: is this right ? (or should it be only VSCROLL)
2134 * (and maybe only for edit controls that really have their
2135 * own scrollbars) (and maybe only for multiline controls ?)
2136 * All in all: very poorly documented
2138 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2139 * WM_VSCROLL handlers
2142 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2144 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
2145 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
2149 /*********************************************************************
2151 * EM_LINEFROMCHAR
2154 static INT32 EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT32 index)
2156 INT32 line;
2157 LINEDEF *line_def;
2159 if (!(es->style & ES_MULTILINE))
2160 return 0;
2161 if (index > lstrlen32A(es->text))
2162 return es->line_count - 1;
2163 if (index == -1)
2164 index = MIN(es->selection_start, es->selection_end);
2166 line = 0;
2167 line_def = es->first_line_def;
2168 index -= line_def->length;
2169 while ((index >= 0) && line_def->next) {
2170 line++;
2171 line_def = line_def->next;
2172 index -= line_def->length;
2174 return line;
2178 /*********************************************************************
2180 * EM_LINEINDEX
2183 static INT32 EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT32 line)
2185 INT32 line_index;
2186 LINEDEF *line_def;
2188 if (!(es->style & ES_MULTILINE))
2189 return 0;
2190 if (line >= es->line_count)
2191 return -1;
2193 line_index = 0;
2194 line_def = es->first_line_def;
2195 if (line == -1) {
2196 INT32 index = es->selection_end - line_def->length;
2197 while ((index >= 0) && line_def->next) {
2198 line_index += line_def->length;
2199 line_def = line_def->next;
2200 index -= line_def->length;
2202 } else {
2203 while (line > 0) {
2204 line_index += line_def->length;
2205 line_def = line_def->next;
2206 line--;
2209 return line_index;
2213 /*********************************************************************
2215 * EM_LINELENGTH
2218 static INT32 EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT32 index)
2220 LINEDEF *line_def;
2222 if (!(es->style & ES_MULTILINE))
2223 return lstrlen32A(es->text);
2225 if (index == -1) {
2226 /* FIXME: broken
2227 INT32 sl = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
2228 INT32 el = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2229 return es->selection_start - es->line_defs[sl].offset +
2230 es->line_defs[el].offset +
2231 es->line_defs[el].length - es->selection_end;
2233 return 0;
2235 line_def = es->first_line_def;
2236 index -= line_def->length;
2237 while ((index >= 0) && line_def->next) {
2238 line_def = line_def->next;
2239 index -= line_def->length;
2241 return line_def->net_length;
2245 /*********************************************************************
2247 * EM_LINESCROLL
2249 * FIXME: dx is in average character widths
2250 * However, we assume it is in pixels when we use this
2251 * function internally
2254 static BOOL32 EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT32 dx, INT32 dy)
2256 INT32 nyoff;
2258 if (!(es->style & ES_MULTILINE))
2259 return FALSE;
2261 if (-dx > es->x_offset)
2262 dx = -es->x_offset;
2263 if (dx > es->text_width - es->x_offset)
2264 dx = es->text_width - es->x_offset;
2265 nyoff = MAX(0, es->y_offset + dy);
2266 if (nyoff >= es->line_count)
2267 nyoff = es->line_count - 1;
2268 dy = (es->y_offset - nyoff) * es->line_height;
2269 if (dx || dy) {
2270 RECT32 rc1;
2271 RECT32 rc;
2272 GetClientRect32(wnd->hwndSelf, &rc1);
2273 IntersectRect32(&rc, &rc1, &es->format_rect);
2274 ScrollWindowEx32(wnd->hwndSelf, -dx, dy,
2275 NULL, &rc, (HRGN32)NULL, NULL, SW_INVALIDATE);
2276 es->y_offset = nyoff;
2277 es->x_offset += dx;
2279 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2280 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2281 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2282 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2283 return TRUE;
2287 /*********************************************************************
2289 * EM_POSFROMCHAR
2292 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT32 index, BOOL32 after_wrap)
2294 INT32 len = lstrlen32A(es->text);
2295 INT32 l;
2296 INT32 li;
2297 INT32 x;
2298 INT32 y = 0;
2299 HDC32 dc;
2300 HFONT32 old_font = 0;
2301 SIZE32 size;
2303 index = MIN(index, len);
2304 dc = GetDC32(wnd->hwndSelf);
2305 if (es->font)
2306 old_font = SelectObject32(dc, es->font);
2307 if (es->style & ES_MULTILINE) {
2308 l = EDIT_EM_LineFromChar(wnd, es, index);
2309 y = (l - es->y_offset) * es->line_height;
2310 li = EDIT_EM_LineIndex(wnd, es, l);
2311 if (after_wrap && (li == index) && l) {
2312 INT32 l2 = l - 1;
2313 LINEDEF *line_def = es->first_line_def;
2314 while (l2) {
2315 line_def = line_def->next;
2316 l2--;
2318 if (line_def->ending == END_WRAP) {
2319 l--;
2320 y -= es->line_height;
2321 li = EDIT_EM_LineIndex(wnd, es, l);
2324 x = LOWORD(GetTabbedTextExtent32A(dc, es->text + li, index - li,
2325 es->tabs_count, es->tabs)) - es->x_offset;
2326 } else {
2327 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
2328 if (index < es->x_offset) {
2329 GetTextExtentPoint32A(dc, text + index,
2330 es->x_offset - index, &size);
2331 x = -size.cx;
2332 } else {
2333 GetTextExtentPoint32A(dc, text + es->x_offset,
2334 index - es->x_offset, &size);
2335 x = size.cx;
2337 y = 0;
2338 if (es->style & ES_PASSWORD)
2339 HeapFree(es->heap, 0 ,text);
2341 x += es->format_rect.left;
2342 y += es->format_rect.top;
2343 if (es->font)
2344 SelectObject32(dc, old_font);
2345 ReleaseDC32(wnd->hwndSelf, dc);
2346 return MAKELONG((INT16)x, (INT16)y);
2350 /*********************************************************************
2352 * EM_REPLACESEL
2354 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2357 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL32 can_undo, LPCSTR lpsz_replace)
2359 INT32 strl = lstrlen32A(lpsz_replace);
2360 INT32 tl = lstrlen32A(es->text);
2361 INT32 utl;
2362 UINT32 s;
2363 UINT32 e;
2364 INT32 i;
2365 LPSTR p;
2367 s = es->selection_start;
2368 e = es->selection_end;
2370 if ((s == e) && !strl)
2371 return;
2373 ORDER_UINT32(s, e);
2375 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2376 return;
2378 if (e != s) {
2379 /* there is something to be deleted */
2380 if (can_undo) {
2381 utl = lstrlen32A(es->undo_text);
2382 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2383 /* undo-buffer is extended to the right */
2384 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2385 lstrcpyn32A(es->undo_text + utl, es->text + s, e - s + 1);
2386 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2387 /* undo-buffer is extended to the left */
2388 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2389 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2390 p[e - s] = p[0];
2391 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2392 p[i] = (es->text + s)[i];
2393 es->undo_position = s;
2394 } else {
2395 /* new undo-buffer */
2396 EDIT_MakeUndoFit(wnd, es, e - s);
2397 lstrcpyn32A(es->undo_text, es->text + s, e - s + 1);
2398 es->undo_position = s;
2400 /* any deletion makes the old insertion-undo invalid */
2401 es->undo_insert_count = 0;
2402 } else
2403 EDIT_EM_EmptyUndoBuffer(wnd, es);
2405 /* now delete */
2406 lstrcpy32A(es->text + s, es->text + e);
2408 if (strl) {
2409 /* there is an insertion */
2410 if (can_undo) {
2411 if ((s == es->undo_position) ||
2412 ((es->undo_insert_count) &&
2413 (s == es->undo_position + es->undo_insert_count)))
2415 * insertion is new and at delete position or
2416 * an extension to either left or right
2418 es->undo_insert_count += strl;
2419 else {
2420 /* new insertion undo */
2421 es->undo_position = s;
2422 es->undo_insert_count = strl;
2423 /* new insertion makes old delete-buffer invalid */
2424 *es->undo_text = '\0';
2426 } else
2427 EDIT_EM_EmptyUndoBuffer(wnd, es);
2429 /* now insert */
2430 tl = lstrlen32A(es->text);
2431 for (p = es->text + tl ; p >= es->text + s ; p--)
2432 p[strl] = p[0];
2433 for (i = 0 , p = es->text + s ; i < strl ; i++)
2434 p[i] = lpsz_replace[i];
2435 if(es->style & ES_UPPERCASE)
2436 CharUpperBuff32A(p, strl);
2437 else if(es->style & ES_LOWERCASE)
2438 CharLowerBuff32A(p, strl);
2439 s += strl;
2441 /* FIXME: really inefficient */
2442 if (es->style & ES_MULTILINE)
2443 EDIT_BuildLineDefs_ML(wnd, es);
2445 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2446 es->flags |= EF_MODIFIED;
2447 es->flags |= EF_UPDATE;
2448 EDIT_EM_ScrollCaret(wnd, es);
2450 /* FIXME: really inefficient */
2451 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2455 /*********************************************************************
2457 * EM_SCROLL
2460 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT32 action)
2462 INT32 dy;
2464 if (!(es->style & ES_MULTILINE))
2465 return (LRESULT)FALSE;
2467 dy = 0;
2469 switch (action) {
2470 case SB_LINEUP:
2471 if (es->y_offset)
2472 dy = -1;
2473 break;
2474 case SB_LINEDOWN:
2475 if (es->y_offset < es->line_count - 1)
2476 dy = 1;
2477 break;
2478 case SB_PAGEUP:
2479 if (es->y_offset)
2480 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2481 break;
2482 case SB_PAGEDOWN:
2483 if (es->y_offset < es->line_count - 1)
2484 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2485 break;
2486 default:
2487 return (LRESULT)FALSE;
2489 if (dy) {
2490 EDIT_EM_LineScroll(wnd, es, 0, dy);
2491 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2493 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2497 /*********************************************************************
2499 * EM_SETHANDLE
2501 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2504 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL32 hloc)
2506 if (!(es->style & ES_MULTILINE))
2507 return;
2509 if (!hloc) {
2510 WARN(edit, "called with NULL handle\n");
2511 return;
2514 EDIT_UnlockBuffer(wnd, es, TRUE);
2516 * old buffer is freed by caller, unless
2517 * it is still in our own heap. (in that case
2518 * we free it, correcting the buggy caller.)
2520 if (es->text)
2521 HeapFree(es->heap, 0, es->text);
2523 es->hloc16 = (HLOCAL16)NULL;
2524 es->hloc32 = hloc;
2525 es->text = NULL;
2526 es->buffer_size = LocalSize32(es->hloc32) - 1;
2527 EDIT_LockBuffer(wnd, es);
2529 es->x_offset = es->y_offset = 0;
2530 es->selection_start = es->selection_end = 0;
2531 EDIT_EM_EmptyUndoBuffer(wnd, es);
2532 es->flags &= ~EF_MODIFIED;
2533 es->flags &= ~EF_UPDATE;
2534 EDIT_BuildLineDefs_ML(wnd, es);
2535 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2536 EDIT_EM_ScrollCaret(wnd, es);
2540 /*********************************************************************
2542 * EM_SETHANDLE16
2544 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2547 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2549 if (!(es->style & ES_MULTILINE))
2550 return;
2552 if (!hloc) {
2553 WARN(edit, "called with NULL handle\n");
2554 return;
2557 EDIT_UnlockBuffer(wnd, es, TRUE);
2559 * old buffer is freed by caller, unless
2560 * it is still in our own heap. (in that case
2561 * we free it, correcting the buggy caller.)
2563 if (es->text)
2564 HeapFree(es->heap, 0, es->text);
2566 es->hloc16 = hloc;
2567 es->hloc32 = (HLOCAL32)NULL;
2568 es->text = NULL;
2569 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2570 EDIT_LockBuffer(wnd, es);
2572 es->x_offset = es->y_offset = 0;
2573 es->selection_start = es->selection_end = 0;
2574 EDIT_EM_EmptyUndoBuffer(wnd, es);
2575 es->flags &= ~EF_MODIFIED;
2576 es->flags &= ~EF_UPDATE;
2577 EDIT_BuildLineDefs_ML(wnd, es);
2578 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2579 EDIT_EM_ScrollCaret(wnd, es);
2583 /*********************************************************************
2585 * EM_SETLIMITTEXT
2587 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2588 * However, the windows version is not complied to yet in all of edit.c
2591 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT32 limit)
2593 if (es->style & ES_MULTILINE) {
2594 if (limit)
2595 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2596 else
2597 es->buffer_limit = BUFLIMIT_MULTI;
2598 } else {
2599 if (limit)
2600 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2601 else
2602 es->buffer_limit = BUFLIMIT_SINGLE;
2607 /*********************************************************************
2609 * EM_SETMARGINS
2612 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT32 action, INT32 left, INT32 right)
2614 if (action & EC_USEFONTINFO) {
2615 if (es->style & ES_MULTILINE) {
2617 * FIXME: do some GetABCCharWidth, or so
2618 * This is just preliminary
2620 es->left_margin = es->right_margin = es->char_width/4;
2621 } else
2622 es->left_margin = es->right_margin = es->char_width/4;
2623 } else {
2624 if (action & EC_LEFTMARGIN)
2625 es->left_margin = left;
2626 if (action & EC_RIGHTMARGIN)
2627 es->right_margin = right;
2629 TRACE(edit, "left=%d, right=%d\n", es->left_margin, es->right_margin);
2633 /*********************************************************************
2635 * EM_SETPASSWORDCHAR
2638 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2640 if (es->style & ES_MULTILINE)
2641 return;
2643 if (es->password_char == c)
2644 return;
2646 es->password_char = c;
2647 if (c) {
2648 wnd->dwStyle |= ES_PASSWORD;
2649 es->style |= ES_PASSWORD;
2650 } else {
2651 wnd->dwStyle &= ~ES_PASSWORD;
2652 es->style &= ~ES_PASSWORD;
2654 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2658 /*********************************************************************
2660 * EDIT_EM_SetSel
2662 * note: unlike the specs say: the order of start and end
2663 * _is_ preserved in Windows. (i.e. start can be > end)
2664 * In other words: this handler is OK
2667 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT32 start, UINT32 end, BOOL32 after_wrap)
2669 UINT32 old_start = es->selection_start;
2670 UINT32 old_end = es->selection_end;
2671 UINT32 len = lstrlen32A(es->text);
2673 if (start == -1) {
2674 start = es->selection_end;
2675 end = es->selection_end;
2676 } else {
2677 start = MIN(start, len);
2678 end = MIN(end, len);
2680 es->selection_start = start;
2681 es->selection_end = end;
2682 if (after_wrap)
2683 es->flags |= EF_AFTER_WRAP;
2684 else
2685 es->flags &= ~EF_AFTER_WRAP;
2686 if (es->flags & EF_FOCUSED) {
2687 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, end, after_wrap);
2688 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
2690 /* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2691 ORDER_UINT32(start, end);
2692 ORDER_UINT32(end, old_end);
2693 ORDER_UINT32(start, old_start);
2694 ORDER_UINT32(old_start, old_end);
2695 if (end != old_start)
2698 * One can also do
2699 * ORDER_UINT32(end, old_start);
2700 * EDIT_InvalidateText(wnd, es, start, end);
2701 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2702 * in place of the following if statement.
2704 if (old_start > end )
2706 EDIT_InvalidateText(wnd, es, start, end);
2707 EDIT_InvalidateText(wnd, es, old_start, old_end);
2709 else
2711 EDIT_InvalidateText(wnd, es, start, old_start);
2712 EDIT_InvalidateText(wnd, es, end, old_end);
2715 else EDIT_InvalidateText(wnd, es, start, old_end);
2719 /*********************************************************************
2721 * EM_SETTABSTOPS
2724 static BOOL32 EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT32 count, LPINT32 tabs)
2726 if (!(es->style & ES_MULTILINE))
2727 return FALSE;
2728 if (es->tabs)
2729 HeapFree(es->heap, 0, es->tabs);
2730 es->tabs_count = count;
2731 if (!count)
2732 es->tabs = NULL;
2733 else {
2734 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
2735 memcpy(es->tabs, tabs, count * sizeof(INT32));
2737 return TRUE;
2741 /*********************************************************************
2743 * EM_SETTABSTOPS16
2746 static BOOL32 EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT32 count, LPINT16 tabs)
2748 if (!(es->style & ES_MULTILINE))
2749 return FALSE;
2750 if (es->tabs)
2751 HeapFree(es->heap, 0, es->tabs);
2752 es->tabs_count = count;
2753 if (!count)
2754 es->tabs = NULL;
2755 else {
2756 INT32 i;
2757 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
2758 for (i = 0 ; i < count ; i++)
2759 es->tabs[i] = *tabs++;
2761 return TRUE;
2765 /*********************************************************************
2767 * EM_SETWORDBREAKPROC
2770 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC32A wbp)
2772 if (es->word_break_proc32A == wbp)
2773 return;
2775 es->word_break_proc32A = wbp;
2776 es->word_break_proc16 = NULL;
2777 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2778 EDIT_BuildLineDefs_ML(wnd, es);
2779 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2784 /*********************************************************************
2786 * EM_SETWORDBREAKPROC16
2789 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2791 if (es->word_break_proc16 == wbp)
2792 return;
2794 es->word_break_proc32A = NULL;
2795 es->word_break_proc16 = wbp;
2796 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2797 EDIT_BuildLineDefs_ML(wnd, es);
2798 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2803 /*********************************************************************
2805 * EM_UNDO / WM_UNDO
2808 static BOOL32 EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2810 INT32 ulength = lstrlen32A(es->undo_text);
2811 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2813 lstrcpy32A(utext, es->undo_text);
2815 TRACE(edit, "before UNDO:insertion length = %d, deletion buffer = %s\n",
2816 es->undo_insert_count, utext);
2818 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2819 EDIT_EM_EmptyUndoBuffer(wnd, es);
2820 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext);
2821 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2822 HeapFree(es->heap, 0, utext);
2824 TRACE(edit, "after UNDO:insertion length = %d, deletion buffer = %s\n",
2825 es->undo_insert_count, es->undo_text);
2827 return TRUE;
2831 /*********************************************************************
2833 * WM_CHAR
2836 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
2838 switch (c) {
2839 case '\r':
2840 case '\n':
2841 if (es->style & ES_MULTILINE) {
2842 if (es->style & ES_READONLY) {
2843 EDIT_MoveHome(wnd, es, FALSE);
2844 EDIT_MoveDown_ML(wnd, es, FALSE);
2845 } else
2846 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n");
2848 break;
2849 case '\t':
2850 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2851 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t");
2852 break;
2853 default:
2854 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
2855 char str[2];
2856 str[0] = c;
2857 str[1] = '\0';
2858 EDIT_EM_ReplaceSel(wnd, es, TRUE, str);
2860 break;
2865 /*********************************************************************
2867 * WM_COMMAND
2870 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT32 code, INT32 id, HWND32 control)
2872 if (code || control)
2873 return;
2875 switch (id) {
2876 case EM_UNDO32:
2877 EDIT_EM_Undo(wnd, es);
2878 break;
2879 case WM_CUT:
2880 EDIT_WM_Cut(wnd, es);
2881 break;
2882 case WM_COPY:
2883 EDIT_WM_Copy(wnd, es);
2884 break;
2885 case WM_PASTE:
2886 EDIT_WM_Paste(wnd, es);
2887 break;
2888 case WM_CLEAR:
2889 EDIT_WM_Clear(wnd, es);
2890 break;
2891 case EM_SETSEL32:
2892 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
2893 EDIT_EM_ScrollCaret(wnd, es);
2894 break;
2895 default:
2896 ERR(edit, "unknown menu item, please report\n");
2897 break;
2902 /*********************************************************************
2904 * WM_CONTEXTMENU
2906 * Note: the resource files resource/sysres_??.rc cannot define a
2907 * single popup menu. Hence we use a (dummy) menubar
2908 * containing the single popup menu as its first item.
2910 * FIXME: the message identifiers have been chosen arbitrarily,
2911 * hence we use MF_BYPOSITION.
2912 * We might as well use the "real" values (anybody knows ?)
2913 * The menu definition is in resources/sysres_??.rc.
2914 * Once these are OK, we better use MF_BYCOMMAND here
2915 * (as we do in EDIT_WM_Command()).
2918 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND32 hwnd, INT32 x, INT32 y)
2920 HMENU32 menu = LoadMenuIndirect32A(SYSRES_GetResPtr(SYSRES_MENU_EDITMENU));
2921 HMENU32 popup = GetSubMenu32(menu, 0);
2922 UINT32 start = es->selection_start;
2923 UINT32 end = es->selection_end;
2925 ORDER_UINT32(start, end);
2927 /* undo */
2928 EnableMenuItem32(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
2929 /* cut */
2930 EnableMenuItem32(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2931 /* copy */
2932 EnableMenuItem32(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2933 /* paste */
2934 EnableMenuItem32(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable32(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
2935 /* delete */
2936 EnableMenuItem32(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
2937 /* select all */
2938 EnableMenuItem32(popup, 7, MF_BYPOSITION | (start || (end != lstrlen32A(es->text)) ? MF_ENABLED : MF_GRAYED));
2940 TrackPopupMenu32(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
2941 DestroyMenu32(menu);
2945 /*********************************************************************
2947 * WM_COPY
2949 * FIXME: replace with 32 bit calls as soon as they are implemented
2950 * in the clipboard code
2953 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
2955 INT32 s = es->selection_start;
2956 INT32 e = es->selection_end;
2957 HGLOBAL16 hdst;
2958 LPSTR dst;
2960 if (e == s)
2961 return;
2962 ORDER_INT32(s, e);
2963 hdst = GlobalAlloc16(GMEM_MOVEABLE, (DWORD)(e - s + 1));
2964 dst = GlobalLock16(hdst);
2965 lstrcpyn32A(dst, es->text + s, e - s + 1);
2966 GlobalUnlock16(hdst);
2967 OpenClipboard32(wnd->hwndSelf);
2968 EmptyClipboard32();
2969 SetClipboardData16(CF_TEXT, hdst);
2970 CloseClipboard32();
2974 /*********************************************************************
2976 * WM_CREATE
2979 static LRESULT EDIT_WM_Create(WND *wnd, LPCREATESTRUCT32A cs)
2981 EDITSTATE *es;
2983 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
2984 return -1;
2985 *(EDITSTATE **)wnd->wExtra = es;
2986 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
2987 return -1;
2988 es->style = cs->style;
2990 /* remove the WS_CAPTION style if it has been set - this is really a */
2991 /* pseudo option made from a combination of WS_BORDER and WS_DLGFRAME */
2992 if ((es->style & WS_BORDER) && (es->style & WS_DLGFRAME))
2993 es->style ^= WS_DLGFRAME;
2995 if (es->style & ES_MULTILINE) {
2996 es->buffer_size = BUFSTART_MULTI;
2997 es->buffer_limit = BUFLIMIT_MULTI;
2998 if (es->style & WS_VSCROLL)
2999 es->style |= ES_AUTOVSCROLL;
3000 if (es->style & WS_HSCROLL)
3001 es->style |= ES_AUTOHSCROLL;
3002 es->style &= ~ES_PASSWORD;
3003 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3004 if (es->style & ES_RIGHT)
3005 es->style &= ~ES_CENTER;
3006 es->style &= ~WS_HSCROLL;
3007 es->style &= ~ES_AUTOHSCROLL;
3010 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3011 es->style |= ES_AUTOVSCROLL;
3012 } else {
3013 es->buffer_size = BUFSTART_SINGLE;
3014 es->buffer_limit = BUFLIMIT_SINGLE;
3015 es->style &= ~ES_CENTER;
3016 es->style &= ~ES_RIGHT;
3017 es->style &= ~WS_HSCROLL;
3018 es->style &= ~WS_VSCROLL;
3019 es->style &= ~ES_AUTOVSCROLL;
3020 es->style &= ~ES_WANTRETURN;
3021 if (es->style & ES_UPPERCASE) {
3022 es->style &= ~ES_LOWERCASE;
3023 es->style &= ~ES_NUMBER;
3024 } else if (es->style & ES_LOWERCASE)
3025 es->style &= ~ES_NUMBER;
3026 if (es->style & ES_PASSWORD)
3027 es->password_char = '*';
3029 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3030 es->style |= ES_AUTOHSCROLL;
3032 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3033 return -1;
3034 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3035 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3036 return -1;
3037 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3038 *es->text = '\0';
3039 if (es->style & ES_MULTILINE)
3040 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3041 return -1;
3042 es->line_count = 1;
3044 * To initialize some final structure members, we call some helper
3045 * functions. However, since the EDITSTATE is not consistent (i.e.
3046 * not fully initialized), we should be very careful which
3047 * functions can be called, and in what order.
3049 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3050 if (cs->lpszName && *(cs->lpszName) != '\0') {
3051 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName);
3052 /* if we insert text to the editline, the text scrolls out of the window, as the caret is placed after the insert pos normally; thus we reset es->selection... to 0 and update caret */
3053 es->selection_start = es->selection_end = 0;
3054 EDIT_EM_ScrollCaret(wnd, es);
3056 return 0;
3060 /*********************************************************************
3062 * WM_DESTROY
3065 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3067 if (es->hloc32) {
3068 while (LocalUnlock32(es->hloc32)) ;
3069 LocalFree32(es->hloc32);
3071 if (es->hloc16) {
3072 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3073 LOCAL_Free(wnd->hInstance, es->hloc16);
3075 HeapDestroy(es->heap);
3076 HeapFree(GetProcessHeap(), 0, es);
3077 *(EDITSTATE **)wnd->wExtra = NULL;
3081 /*********************************************************************
3083 * WM_ERASEBKGND
3086 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC32 dc)
3088 HBRUSH32 brush;
3089 RECT32 rc;
3091 if (!(brush = (HBRUSH32)EDIT_SEND_CTLCOLOR(wnd, dc)))
3092 brush = (HBRUSH32)GetStockObject32(WHITE_BRUSH);
3094 GetClientRect32(wnd->hwndSelf, &rc);
3095 IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
3096 GetClipBox32(dc, &rc);
3098 * FIXME: specs say that we should UnrealizeObject() the brush,
3099 * but the specs of UnrealizeObject() say that we shouldn't
3100 * unrealize a stock object. The default brush that
3101 * DefWndProc() returns is ... a stock object.
3103 FillRect32(dc, &rc, brush);
3104 return -1;
3108 /*********************************************************************
3110 * WM_GETTEXT
3113 static INT32 EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT32 count, LPSTR text)
3115 INT32 len = lstrlen32A(es->text);
3117 if (count > len) {
3118 lstrcpy32A(text, es->text);
3119 return len;
3120 } else
3121 return -1;
3125 /*********************************************************************
3127 * EDIT_HScroll_Hack
3129 * 16 bit notepad needs this. Actually it is not _our_ hack,
3130 * it is notepad's. Notepad is sending us scrollbar messages with
3131 * undocumented parameters without us even having a scrollbar ... !?!?
3134 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3136 INT32 dx = 0;
3137 INT32 fw = es->format_rect.right - es->format_rect.left;
3138 LRESULT ret = 0;
3140 if (!(es->flags & EF_HSCROLL_HACK)) {
3141 ERR(edit, "hacked WM_HSCROLL handler invoked\n");
3142 ERR(edit, " if you are _not_ running 16 bit notepad, please report\n");
3143 ERR(edit, " (this message is only displayed once per edit control)\n");
3144 es->flags |= EF_HSCROLL_HACK;
3147 switch (action) {
3148 case SB_LINELEFT:
3149 if (es->x_offset)
3150 dx = -es->char_width;
3151 break;
3152 case SB_LINERIGHT:
3153 if (es->x_offset < es->text_width)
3154 dx = es->char_width;
3155 break;
3156 case SB_PAGELEFT:
3157 if (es->x_offset)
3158 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3159 break;
3160 case SB_PAGERIGHT:
3161 if (es->x_offset < es->text_width)
3162 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3163 break;
3164 case SB_LEFT:
3165 if (es->x_offset)
3166 dx = -es->x_offset;
3167 break;
3168 case SB_RIGHT:
3169 if (es->x_offset < es->text_width)
3170 dx = es->text_width - es->x_offset;
3171 break;
3172 case SB_THUMBTRACK:
3173 es->flags |= EF_HSCROLL_TRACK;
3174 dx = pos * es->text_width / 100 - es->x_offset;
3175 break;
3176 case SB_THUMBPOSITION:
3177 es->flags &= ~EF_HSCROLL_TRACK;
3178 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3179 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3180 break;
3181 case SB_ENDSCROLL:
3182 break;
3185 * FIXME : the next two are undocumented !
3186 * Are we doing the right thing ?
3187 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3188 * although it's also a regular control message.
3190 case EM_GETTHUMB16:
3191 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3192 break;
3193 case EM_LINESCROLL16:
3194 dx = pos;
3195 break;
3197 default:
3198 ERR(edit, "undocumented (hacked) WM_HSCROLL parameter, please report\n");
3199 return 0;
3201 if (dx)
3202 EDIT_EM_LineScroll(wnd, es, dx, 0);
3203 return ret;
3207 /*********************************************************************
3209 * WM_HSCROLL
3212 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3214 INT32 dx;
3215 INT32 fw;
3217 if (!(es->style & ES_MULTILINE))
3218 return 0;
3220 if (!(es->style & ES_AUTOHSCROLL))
3221 return 0;
3223 if (!(es->style & WS_HSCROLL))
3224 return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
3226 dx = 0;
3227 fw = es->format_rect.right - es->format_rect.left;
3228 switch (action) {
3229 case SB_LINELEFT:
3230 if (es->x_offset)
3231 dx = -es->char_width;
3232 break;
3233 case SB_LINERIGHT:
3234 if (es->x_offset < es->text_width)
3235 dx = es->char_width;
3236 break;
3237 case SB_PAGELEFT:
3238 if (es->x_offset)
3239 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3240 break;
3241 case SB_PAGERIGHT:
3242 if (es->x_offset < es->text_width)
3243 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3244 break;
3245 case SB_LEFT:
3246 if (es->x_offset)
3247 dx = -es->x_offset;
3248 break;
3249 case SB_RIGHT:
3250 if (es->x_offset < es->text_width)
3251 dx = es->text_width - es->x_offset;
3252 break;
3253 case SB_THUMBTRACK:
3254 es->flags |= EF_HSCROLL_TRACK;
3255 dx = pos - es->x_offset;
3256 break;
3257 case SB_THUMBPOSITION:
3258 es->flags &= ~EF_HSCROLL_TRACK;
3259 if (!(dx = pos - es->x_offset)) {
3260 SetScrollPos32(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3261 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3263 break;
3264 case SB_ENDSCROLL:
3265 break;
3267 default:
3268 ERR(edit, "undocumented WM_HSCROLL parameter, please report\n");
3269 return 0;
3271 if (dx)
3272 EDIT_EM_LineScroll(wnd, es, dx, 0);
3273 return 0;
3277 /*********************************************************************
3279 * EDIT_CheckCombo
3282 static BOOL32 EDIT_CheckCombo(WND *wnd, UINT32 msg, INT32 key, DWORD key_data)
3284 HWND32 hLBox;
3286 if (WIDGETS_IsControl32(wnd->parent, BIC32_COMBO) &&
3287 (hLBox = COMBO_GetLBWindow(wnd->parent))) {
3288 HWND32 hCombo = wnd->parent->hwndSelf;
3289 BOOL32 bUIFlip = TRUE;
3291 TRACE(combo, "[%04x]: handling msg %04x (%04x)\n",
3292 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3294 switch (msg) {
3295 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3296 if (key != VK_F4) {
3297 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
3298 if (SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0))
3299 bUIFlip = FALSE;
3301 if (!bUIFlip)
3302 SendMessage32A(hLBox, WM_KEYDOWN, (WPARAM32)key, 0);
3303 else {
3304 /* make sure ComboLBox pops up */
3305 SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 0, 0);
3306 SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
3307 SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 1, 0);
3309 break;
3310 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3311 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
3312 if (bUIFlip) {
3313 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0);
3314 SendMessage32A(hCombo, CB_SHOWDROPDOWN32, (bUIFlip) ? FALSE : TRUE, 0);
3315 } else
3316 SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
3317 break;
3319 return TRUE;
3321 return FALSE;
3325 /*********************************************************************
3327 * WM_KEYDOWN
3329 * Handling of special keys that don't produce a WM_CHAR
3330 * (i.e. non-printable keys) & Backspace & Delete
3333 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
3335 BOOL32 shift;
3336 BOOL32 control;
3338 if (GetKeyState32(VK_MENU) & 0x8000)
3339 return 0;
3341 shift = GetKeyState32(VK_SHIFT) & 0x8000;
3342 control = GetKeyState32(VK_CONTROL) & 0x8000;
3344 switch (key) {
3345 case VK_F4:
3346 case VK_UP:
3347 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3348 break;
3349 if (key == VK_F4)
3350 break;
3351 /* fall through */
3352 case VK_LEFT:
3353 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3354 EDIT_MoveUp_ML(wnd, es, shift);
3355 else
3356 if (control)
3357 EDIT_MoveWordBackward(wnd, es, shift);
3358 else
3359 EDIT_MoveBackward(wnd, es, shift);
3360 break;
3361 case VK_DOWN:
3362 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3363 break;
3364 /* fall through */
3365 case VK_RIGHT:
3366 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3367 EDIT_MoveDown_ML(wnd, es, shift);
3368 else if (control)
3369 EDIT_MoveWordForward(wnd, es, shift);
3370 else
3371 EDIT_MoveForward(wnd, es, shift);
3372 break;
3373 case VK_HOME:
3374 EDIT_MoveHome(wnd, es, shift);
3375 break;
3376 case VK_END:
3377 EDIT_MoveEnd(wnd, es, shift);
3378 break;
3379 case VK_PRIOR:
3380 if (es->style & ES_MULTILINE)
3381 EDIT_MovePageUp_ML(wnd, es, shift);
3382 break;
3383 case VK_NEXT:
3384 if (es->style & ES_MULTILINE)
3385 EDIT_MovePageDown_ML(wnd, es, shift);
3386 break;
3387 case VK_BACK:
3388 if (!(es->style & ES_READONLY) && !control)
3389 if (es->selection_start != es->selection_end)
3390 EDIT_WM_Clear(wnd, es);
3391 else {
3392 /* delete character left of caret */
3393 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3394 EDIT_MoveBackward(wnd, es, TRUE);
3395 EDIT_WM_Clear(wnd, es);
3397 break;
3398 case VK_DELETE:
3399 if (!(es->style & ES_READONLY) && !(shift && control))
3400 if (es->selection_start != es->selection_end) {
3401 if (shift)
3402 EDIT_WM_Cut(wnd, es);
3403 else
3404 EDIT_WM_Clear(wnd, es);
3405 } else {
3406 if (shift) {
3407 /* delete character left of caret */
3408 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3409 EDIT_MoveBackward(wnd, es, TRUE);
3410 EDIT_WM_Clear(wnd, es);
3411 } else if (control) {
3412 /* delete to end of line */
3413 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3414 EDIT_MoveEnd(wnd, es, TRUE);
3415 EDIT_WM_Clear(wnd, es);
3416 } else {
3417 /* delete character right of caret */
3418 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3419 EDIT_MoveForward(wnd, es, TRUE);
3420 EDIT_WM_Clear(wnd, es);
3423 break;
3424 case VK_INSERT:
3425 if (shift) {
3426 if (!(es->style & ES_READONLY))
3427 EDIT_WM_Paste(wnd, es);
3428 } else if (control)
3429 EDIT_WM_Copy(wnd, es);
3430 break;
3432 return 0;
3436 /*********************************************************************
3438 * WM_KILLFOCUS
3441 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND32 window_getting_focus)
3443 es->flags &= ~EF_FOCUSED;
3444 DestroyCaret32();
3445 if(!(es->style & ES_NOHIDESEL))
3446 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3447 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3448 return 0;
3452 /*********************************************************************
3454 * WM_LBUTTONDBLCLK
3456 * The caret position has been set on the WM_LBUTTONDOWN message
3459 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3461 INT32 s;
3462 INT32 e = es->selection_end;
3463 INT32 l;
3464 INT32 li;
3465 INT32 ll;
3467 if (!(es->flags & EF_FOCUSED))
3468 return 0;
3470 l = EDIT_EM_LineFromChar(wnd, es, e);
3471 li = EDIT_EM_LineIndex(wnd, es, l);
3472 ll = EDIT_EM_LineLength(wnd, es, e);
3473 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3474 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3475 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3476 EDIT_EM_ScrollCaret(wnd, es);
3477 return 0;
3481 /*********************************************************************
3483 * WM_LBUTTONDOWN
3486 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3488 INT32 e;
3489 BOOL32 after_wrap;
3491 if (!(es->flags & EF_FOCUSED))
3492 return 0;
3494 SetCapture32(wnd->hwndSelf);
3495 EDIT_ConfinePoint(wnd, es, &x, &y);
3496 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3497 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3498 EDIT_EM_ScrollCaret(wnd, es);
3499 es->region_posx = es->region_posy = 0;
3500 SetTimer32(wnd->hwndSelf, 0, 100, NULL);
3501 return 0;
3505 /*********************************************************************
3507 * WM_LBUTTONUP
3510 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3512 if (GetCapture32() == wnd->hwndSelf) {
3513 KillTimer32(wnd->hwndSelf, 0);
3514 ReleaseCapture();
3516 return 0;
3520 /*********************************************************************
3522 * WM_MOUSEMOVE
3525 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3527 INT32 e;
3528 BOOL32 after_wrap;
3529 INT32 prex, prey;
3531 if (GetCapture32() != wnd->hwndSelf)
3532 return 0;
3535 * FIXME: gotta do some scrolling if outside client
3536 * area. Maybe reset the timer ?
3538 prex = x; prey = y;
3539 EDIT_ConfinePoint(wnd, es, &x, &y);
3540 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3541 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3542 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3543 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3544 return 0;
3548 /*********************************************************************
3550 * WM_PAINT
3553 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es)
3555 PAINTSTRUCT32 ps;
3556 INT32 i;
3557 HDC32 dc;
3558 HFONT32 old_font = 0;
3559 RECT32 rc;
3560 RECT32 rcLine;
3561 RECT32 rcRgn;
3562 LRESULT pos;
3563 BOOL32 rev = IsWindowEnabled32(wnd->hwndSelf) &&
3564 ((es->flags & EF_FOCUSED) ||
3565 (es->style & ES_NOHIDESEL));
3567 if (es->flags & EF_UPDATE)
3568 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
3570 dc = BeginPaint32(wnd->hwndSelf, &ps);
3571 IntersectClipRect32(dc, es->format_rect.left,
3572 es->format_rect.top,
3573 es->format_rect.right,
3574 es->format_rect.bottom);
3575 if (es->style & ES_MULTILINE) {
3576 GetClientRect32(wnd->hwndSelf, &rc);
3577 IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
3579 if (es->font)
3580 old_font = SelectObject32(dc, es->font);
3581 EDIT_SEND_CTLCOLOR(wnd, dc);
3582 if (!IsWindowEnabled32(wnd->hwndSelf))
3583 SetTextColor32(dc, GetSysColor32(COLOR_GRAYTEXT));
3584 GetClipBox32(dc, &rcRgn);
3585 if (es->style & ES_MULTILINE) {
3586 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3587 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3588 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3589 if (IntersectRect32(&rc, &rcRgn, &rcLine))
3590 EDIT_PaintLine(wnd, es, dc, i, rev);
3592 } else {
3593 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3594 if (IntersectRect32(&rc, &rcRgn, &rcLine))
3595 EDIT_PaintLine(wnd, es, dc, 0, rev);
3597 if (es->font)
3598 SelectObject32(dc, old_font);
3599 if (es->flags & EF_FOCUSED) {
3600 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3601 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3603 EndPaint32(wnd->hwndSelf, &ps);
3604 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3605 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3606 SCROLLINFO si;
3607 si.cbSize = sizeof(SCROLLINFO);
3608 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3609 si.nMin = 0;
3610 si.nMax = es->line_count + vlc - 2;
3611 si.nPage = vlc;
3612 si.nPos = es->y_offset;
3613 SetScrollInfo32(wnd->hwndSelf, SB_VERT, &si, TRUE);
3615 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3616 SCROLLINFO si;
3617 INT32 fw = es->format_rect.right - es->format_rect.left;
3618 si.cbSize = sizeof(SCROLLINFO);
3619 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3620 si.nMin = 0;
3621 si.nMax = es->text_width + fw - 1;
3622 si.nPage = fw;
3623 si.nPos = es->x_offset;
3624 SetScrollInfo32(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3627 if (es->flags & EF_UPDATE) {
3628 es->flags &= ~EF_UPDATE;
3629 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3634 /*********************************************************************
3636 * WM_PASTE
3638 * FIXME: replace with 32 bit handler once GetClipboardData32() is
3639 * implemented in misc/clipboard.c
3642 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3644 HGLOBAL16 hsrc;
3645 LPSTR src;
3647 OpenClipboard32(wnd->hwndSelf);
3648 if ((hsrc = GetClipboardData16(CF_TEXT))) {
3649 src = (LPSTR)GlobalLock16(hsrc);
3650 EDIT_EM_ReplaceSel(wnd, es, TRUE, src);
3651 GlobalUnlock16(hsrc);
3653 CloseClipboard32();
3657 /*********************************************************************
3659 * WM_SETFOCUS
3662 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND32 window_losing_focus)
3664 LRESULT pos;
3666 es->flags |= EF_FOCUSED;
3667 CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
3668 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3669 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3670 if(!(es->style & ES_NOHIDESEL))
3671 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3672 ShowCaret32(wnd->hwndSelf);
3673 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3677 /*********************************************************************
3679 * WM_SETFONT
3682 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT32 font, BOOL32 redraw)
3684 TEXTMETRIC32A tm;
3685 HDC32 dc;
3686 HFONT32 old_font = 0;
3688 es->font = font;
3689 dc = GetDC32(wnd->hwndSelf);
3690 if (font)
3691 old_font = SelectObject32(dc, font);
3692 GetTextMetrics32A(dc, &tm);
3693 es->line_height = tm.tmHeight;
3694 es->char_width = tm.tmAveCharWidth;
3695 if (font)
3696 SelectObject32(dc, old_font);
3697 ReleaseDC32(wnd->hwndSelf, dc);
3698 if (wnd->flags & WIN_ISWIN32)
3699 EDIT_EM_SetMargins(wnd, es, EC_USEFONTINFO, 0, 0);
3700 if (es->style & ES_MULTILINE)
3701 EDIT_BuildLineDefs_ML(wnd, es);
3702 if (redraw)
3703 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
3704 if (es->flags & EF_FOCUSED) {
3705 LRESULT pos;
3706 DestroyCaret32();
3707 CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
3708 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3709 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3710 ShowCaret32(wnd->hwndSelf);
3715 /*********************************************************************
3717 * WM_SETTEXT
3720 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
3722 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3723 if (text) {
3724 TRACE(edit, "\t'%s'\n", text);
3725 EDIT_EM_ReplaceSel(wnd, es, FALSE, text);
3726 es->x_offset = 0;
3728 es->flags |= EF_MODIFIED;
3729 es->flags |= EF_UPDATE;
3730 EDIT_EM_ScrollCaret(wnd, es);
3734 /*********************************************************************
3736 * WM_SIZE
3739 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT32 action, INT32 width, INT32 height)
3741 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3742 RECT32 rc;
3743 SetRect32(&rc, 0, 0, width, height);
3744 EDIT_SetRectNP(wnd, es, &rc);
3745 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
3750 /*********************************************************************
3752 * WM_SYSKEYDOWN
3755 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
3757 if ((key == VK_BACK) && (key_data & 0x2000)) {
3758 if (EDIT_EM_CanUndo(wnd, es))
3759 EDIT_EM_Undo(wnd, es);
3760 return 0;
3761 } else if (key == VK_UP || key == VK_DOWN)
3762 if (EDIT_CheckCombo(wnd, WM_SYSKEYDOWN, key, key_data))
3763 return 0;
3764 return DefWindowProc32A(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM32)key, (LPARAM)key_data);
3768 /*********************************************************************
3770 * WM_TIMER
3773 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT32 id, TIMERPROC32 timer_proc)
3775 if (es->region_posx < 0) {
3776 EDIT_MoveBackward(wnd, es, TRUE);
3777 } else if (es->region_posx > 0) {
3778 EDIT_MoveForward(wnd, es, TRUE);
3781 * FIXME: gotta do some vertical scrolling here, like
3782 * EDIT_EM_LineScroll(wnd, 0, 1);
3787 /*********************************************************************
3789 * EDIT_VScroll_Hack
3791 * 16 bit notepad needs this. Actually it is not _our_ hack,
3792 * it is notepad's. Notepad is sending us scrollbar messages with
3793 * undocumented parameters without us even having a scrollbar ... !?!?
3796 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3798 INT32 dy = 0;
3799 LRESULT ret = 0;
3801 if (!(es->flags & EF_VSCROLL_HACK)) {
3802 ERR(edit, "hacked WM_VSCROLL handler invoked\n");
3803 ERR(edit, " if you are _not_ running 16 bit notepad, please report\n");
3804 ERR(edit, " (this message is only displayed once per edit control)\n");
3805 es->flags |= EF_VSCROLL_HACK;
3808 switch (action) {
3809 case SB_LINEUP:
3810 case SB_LINEDOWN:
3811 case SB_PAGEUP:
3812 case SB_PAGEDOWN:
3813 EDIT_EM_Scroll(wnd, es, action);
3814 return 0;
3815 case SB_TOP:
3816 dy = -es->y_offset;
3817 break;
3818 case SB_BOTTOM:
3819 dy = es->line_count - 1 - es->y_offset;
3820 break;
3821 case SB_THUMBTRACK:
3822 es->flags |= EF_VSCROLL_TRACK;
3823 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
3824 break;
3825 case SB_THUMBPOSITION:
3826 es->flags &= ~EF_VSCROLL_TRACK;
3827 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
3828 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
3829 break;
3830 case SB_ENDSCROLL:
3831 break;
3834 * FIXME : the next two are undocumented !
3835 * Are we doing the right thing ?
3836 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3837 * although it's also a regular control message.
3839 case EM_GETTHUMB16:
3840 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
3841 break;
3842 case EM_LINESCROLL16:
3843 dy = pos;
3844 break;
3846 default:
3847 ERR(edit, "undocumented (hacked) WM_VSCROLL parameter, please report\n");
3848 return 0;
3850 if (dy)
3851 EDIT_EM_LineScroll(wnd, es, 0, dy);
3852 return ret;
3856 /*********************************************************************
3858 * WM_VSCROLL
3861 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3863 INT32 dy;
3865 if (!(es->style & ES_MULTILINE))
3866 return 0;
3868 if (!(es->style & ES_AUTOVSCROLL))
3869 return 0;
3871 if (!(es->style & WS_VSCROLL))
3872 return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
3874 dy = 0;
3875 switch (action) {
3876 case SB_LINEUP:
3877 case SB_LINEDOWN:
3878 case SB_PAGEUP:
3879 case SB_PAGEDOWN:
3880 EDIT_EM_Scroll(wnd, es, action);
3881 return 0;
3883 case SB_TOP:
3884 dy = -es->y_offset;
3885 break;
3886 case SB_BOTTOM:
3887 dy = es->line_count - 1 - es->y_offset;
3888 break;
3889 case SB_THUMBTRACK:
3890 es->flags |= EF_VSCROLL_TRACK;
3891 dy = pos - es->y_offset;
3892 break;
3893 case SB_THUMBPOSITION:
3894 es->flags &= ~EF_VSCROLL_TRACK;
3895 if (!(dy = pos - es->y_offset)) {
3896 SetScrollPos32(wnd->hwndSelf, SB_VERT, pos, TRUE);
3897 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
3899 break;
3900 case SB_ENDSCROLL:
3901 break;
3903 default:
3904 ERR(edit, "undocumented WM_VSCROLL action %d, please report\n",
3905 action);
3906 return 0;
3908 if (dy)
3909 EDIT_EM_LineScroll(wnd, es, 0, dy);
3910 return 0;