Release 970804
[wine/multimedia.git] / controls / edit.c
blob0a8a3d487f0087e0a00586973dd2b972c1175578
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 <stdio.h>
15 #include "windows.h"
16 #include "winnt.h"
17 #include "win.h"
18 #include "combo.h"
19 #include "local.h"
20 #include "resource.h"
21 #include "stddebug.h"
22 #include "debug.h"
23 #include "callback.h"
25 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
26 FIXME: BTW, new specs say 65535 (do you dare ???) */
27 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
28 #define BUFSTART_MULTI 1024 /* starting size */
29 #define BUFSTART_SINGLE 256 /* starting size */
30 #define GROWLENGTH 64 /* buffers grow by this much */
31 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
34 * extra flags for EDITSTATE.flags field
36 #define EF_MODIFIED 0x0001 /* text has been modified */
37 #define EF_FOCUSED 0x0002 /* we have input focus */
38 #define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
39 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
40 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
41 #define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
42 #define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
43 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
44 wrapped line, instead of in front of the next character */
46 typedef BOOL32 *LPBOOL32;
48 typedef enum
50 END_0 = 0, /* line ends with terminating '\0' character */
51 END_WRAP, /* line is wrapped */
52 END_HARD, /* line ends with a hard return '\r\n' */
53 END_SOFT, /* line ends with a soft return '\r\r\n' */
54 } LINE_END;
56 typedef struct tagLINEDEF {
57 INT32 length; /* bruto length of a line in bytes */
58 INT32 net_length; /* netto length of a line in visible characters */
59 LINE_END ending;
60 INT32 width; /* width of the line in pixels */
61 struct tagLINEDEF *next;
62 } LINEDEF;
64 typedef struct
66 HANDLE32 heap; /* our own heap */
67 LPSTR text; /* the actual contents of the control */
68 INT32 buffer_size; /* the size of the buffer */
69 INT32 buffer_limit; /* the maximum size to which the buffer may grow */
70 HFONT32 font; /* NULL means standard system font */
71 INT32 x_offset; /* scroll offset for multi lines this is in pixels
72 for single lines it's in characters */
73 INT32 line_height; /* height of a screen line in pixels */
74 INT32 char_width; /* average character width in pixels */
75 DWORD style; /* sane version of wnd->dwStyle */
76 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
77 INT32 undo_insert_count; /* number of characters inserted in sequence */
78 INT32 undo_position; /* character index of the insertion and deletion */
79 LPSTR undo_text; /* deleted text */
80 INT32 undo_buffer_size; /* size of the deleted text buffer */
81 INT32 selection_start; /* == selection_end if no selection */
82 INT32 selection_end; /* == current caret position */
83 CHAR password_char; /* == 0 if no password char, and for multi line controls */
84 INT32 left_margin; /* in pixels */
85 INT32 right_margin; /* in pixels */
86 RECT32 format_rect;
87 EDITWORDBREAKPROC16 word_break_proc16;
88 EDITWORDBREAKPROC32A word_break_proc32A;
89 INT32 line_count; /* number of lines */
90 INT32 y_offset; /* scroll offset in number of lines */
92 * only for multi line controls
94 INT32 lock_count; /* amount of re-entries in the EditWndProc */
95 INT32 tabs_count;
96 LPINT32 tabs;
97 INT32 text_width; /* width of the widest line in pixels */
98 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
99 HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
100 HLOCAL32 hloc32; /* for controls receiving EM_GETHANDLE */
101 } EDITSTATE;
104 #define SWAP_INT32(x,y) do { INT32 temp = (INT32)(x); (x) = (INT32)(y); (y) = temp; } while(0)
105 #define ORDER_INT32(x,y) do { if ((INT32)(y) < (INT32)(x)) SWAP_INT32((x),(y)); } while(0)
107 #define SWAP_UINT32(x,y) do { UINT32 temp = (UINT32)(x); (x) = (UINT32)(y); (y) = temp; } while(0)
108 #define ORDER_UINT32(x,y) do { if ((UINT32)(y) < (UINT32)(x)) SWAP_UINT32((x),(y)); } while(0)
110 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
111 ({dprintf_edit(stddeb, \
112 "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 dprintf_edit(stddeb, \
125 "edit: 16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
126 (UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
127 #define DPRINTF_EDIT_MSG32(str) \
128 dprintf_edit(stddeb, \
129 "edit: 32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
130 (UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
133 /*********************************************************************
135 * Declarations
140 * These functions have trivial implementations
141 * We still like to call them internally
142 * "static __inline__" makes them more like macro's
144 static __inline__ BOOL32 EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es);
145 static __inline__ void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es);
146 static __inline__ void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
147 static __inline__ void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
149 * This is the only exported function
151 LRESULT EditWndProc(HWND32 hwnd, UINT32 msg, 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 EditWndProc(HWND32 hwnd, UINT32 msg, WPARAM32 wParam, LPARAM lParam)
302 WND *wnd = WIN_FindWndPtr(hwnd);
303 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
304 LRESULT result = 0;
306 switch (msg) {
307 case WM_CREATE:
308 DPRINTF_EDIT_MSG32("WM_CREATE");
309 return EDIT_WM_Create(wnd, (LPCREATESTRUCT32A)lParam);
311 case WM_DESTROY:
312 DPRINTF_EDIT_MSG32("WM_DESTROY");
313 EDIT_WM_Destroy(wnd, es);
314 return 0;
317 if (!es)
318 return DefWindowProc32A(hwnd, msg, wParam, lParam);
320 EDIT_LockBuffer(wnd, es);
321 switch (msg) {
322 case EM_GETSEL16:
323 DPRINTF_EDIT_MSG16("EM_GETSEL");
324 wParam = 0;
325 lParam = 0;
326 /* fall through */
327 case EM_GETSEL32:
328 DPRINTF_EDIT_MSG32("EM_GETSEL");
329 result = EDIT_EM_GetSel(wnd, es, (LPUINT32)wParam, (LPUINT32)lParam);
330 break;
332 case EM_SETSEL16:
333 DPRINTF_EDIT_MSG16("EM_SETSEL");
334 if (SLOWORD(lParam) == -1)
335 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
336 else
337 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
338 if (!wParam)
339 EDIT_EM_ScrollCaret(wnd, es);
340 result = 1;
341 break;
342 case EM_SETSEL32:
343 DPRINTF_EDIT_MSG32("EM_SETSEL");
344 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
345 result = 1;
346 break;
348 case EM_GETRECT16:
349 DPRINTF_EDIT_MSG16("EM_GETRECT");
350 if (lParam)
351 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
352 break;
353 case EM_GETRECT32:
354 DPRINTF_EDIT_MSG32("EM_GETRECT");
355 if (lParam)
356 CopyRect32((LPRECT32)lParam, &es->format_rect);
357 break;
359 case EM_SETRECT16:
360 DPRINTF_EDIT_MSG16("EM_SETRECT");
361 if ((es->style & ES_MULTILINE) && lParam) {
362 RECT32 rc;
363 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
364 EDIT_SetRectNP(wnd, es, &rc);
365 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
367 break;
368 case EM_SETRECT32:
369 DPRINTF_EDIT_MSG32("EM_SETRECT");
370 if ((es->style & ES_MULTILINE) && lParam) {
371 EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
372 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
374 break;
376 case EM_SETRECTNP16:
377 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
378 if ((es->style & ES_MULTILINE) && lParam) {
379 RECT32 rc;
380 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
381 EDIT_SetRectNP(wnd, es, &rc);
383 break;
384 case EM_SETRECTNP32:
385 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
386 if ((es->style & ES_MULTILINE) && lParam)
387 EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
388 break;
390 case EM_SCROLL16:
391 DPRINTF_EDIT_MSG16("EM_SCROLL");
392 /* fall through */
393 case EM_SCROLL32:
394 DPRINTF_EDIT_MSG32("EM_SCROLL");
395 result = EDIT_EM_Scroll(wnd, es, (INT32)wParam);
396 break;
398 case EM_LINESCROLL16:
399 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
400 wParam = (WPARAM32)(INT32)SHIWORD(lParam);
401 lParam = (LPARAM)(INT32)SLOWORD(lParam);
402 /* fall through */
403 case EM_LINESCROLL32:
404 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
405 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT32)wParam, (INT32)lParam);
406 break;
408 case EM_SCROLLCARET16:
409 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
410 /* fall through */
411 case EM_SCROLLCARET32:
412 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
413 EDIT_EM_ScrollCaret(wnd, es);
414 result = 1;
415 break;
417 case EM_GETMODIFY16:
418 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
419 /* fall through */
420 case EM_GETMODIFY32:
421 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
422 return ((es->flags & EF_MODIFIED) != 0);
423 break;
425 case EM_SETMODIFY16:
426 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
427 /* fall through */
428 case EM_SETMODIFY32:
429 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
430 if (wParam)
431 es->flags |= EF_MODIFIED;
432 else
433 es->flags &= ~EF_MODIFIED;
434 break;
436 case EM_GETLINECOUNT16:
437 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
438 /* fall through */
439 case EM_GETLINECOUNT32:
440 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
441 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
442 break;
444 case EM_LINEINDEX16:
445 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
446 if ((INT16)wParam == -1)
447 wParam = (WPARAM32)-1;
448 /* fall through */
449 case EM_LINEINDEX32:
450 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
451 result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT32)wParam);
452 break;
454 case EM_SETHANDLE16:
455 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
456 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
457 break;
458 case EM_SETHANDLE32:
459 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
460 EDIT_EM_SetHandle(wnd, es, (HLOCAL32)wParam);
461 break;
463 case EM_GETHANDLE16:
464 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
465 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
466 break;
467 case EM_GETHANDLE32:
468 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
469 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
470 break;
472 case EM_GETTHUMB16:
473 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
474 /* fall through */
475 case EM_GETTHUMB32:
476 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
477 result = EDIT_EM_GetThumb(wnd, es);
478 break;
480 /* messages 0x00bf and 0x00c0 missing from specs */
482 case WM_USER+15:
483 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
484 /* fall through */
485 case 0x00bf:
486 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
487 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
488 break;
490 case WM_USER+16:
491 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
492 /* fall through */
493 case 0x00c0:
494 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
495 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
496 break;
498 case EM_LINELENGTH16:
499 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
500 /* fall through */
501 case EM_LINELENGTH32:
502 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
503 result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT32)wParam);
504 break;
506 case EM_REPLACESEL16:
507 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
508 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
509 /* fall through */
510 case EM_REPLACESEL32:
511 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
512 EDIT_EM_ReplaceSel(wnd, es, (BOOL32)wParam, (LPCSTR)lParam);
513 break;
515 /* message 0x00c3 missing from specs */
517 case WM_USER+19:
518 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
519 /* fall through */
520 case 0x00c3:
521 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
522 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
523 break;
525 case EM_GETLINE16:
526 DPRINTF_EDIT_MSG16("EM_GETLINE");
527 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
528 /* fall through */
529 case EM_GETLINE32:
530 DPRINTF_EDIT_MSG32("EM_GETLINE");
531 result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT32)wParam, (LPSTR)lParam);
532 break;
534 case EM_LIMITTEXT16:
535 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
536 /* fall through */
537 case EM_SETLIMITTEXT32:
538 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
539 EDIT_EM_SetLimitText(wnd, es, (INT32)wParam);
540 break;
542 case EM_CANUNDO16:
543 DPRINTF_EDIT_MSG16("EM_CANUNDO");
544 /* fall through */
545 case EM_CANUNDO32:
546 DPRINTF_EDIT_MSG32("EM_CANUNDO");
547 result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
548 break;
550 case EM_UNDO16:
551 DPRINTF_EDIT_MSG16("EM_UNDO");
552 /* fall through */
553 case EM_UNDO32:
554 /* fall through */
555 case WM_UNDO:
556 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
557 result = (LRESULT)EDIT_EM_Undo(wnd, es);
558 break;
560 case EM_FMTLINES16:
561 DPRINTF_EDIT_MSG16("EM_FMTLINES");
562 /* fall through */
563 case EM_FMTLINES32:
564 DPRINTF_EDIT_MSG32("EM_FMTLINES");
565 result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL32)wParam);
566 break;
568 case EM_LINEFROMCHAR16:
569 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
570 /* fall through */
571 case EM_LINEFROMCHAR32:
572 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
573 result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT32)wParam);
574 break;
576 /* message 0x00ca missing from specs */
578 case WM_USER+26:
579 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
580 /* fall through */
581 case 0x00ca:
582 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
583 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
584 break;
586 case EM_SETTABSTOPS16:
587 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
588 result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT32)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
589 break;
590 case EM_SETTABSTOPS32:
591 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
592 result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT32)wParam, (LPINT32)lParam);
593 break;
595 case EM_SETPASSWORDCHAR16:
596 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
597 /* fall through */
598 case EM_SETPASSWORDCHAR32:
599 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
600 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
601 break;
603 case EM_EMPTYUNDOBUFFER16:
604 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
605 /* fall through */
606 case EM_EMPTYUNDOBUFFER32:
607 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
608 EDIT_EM_EmptyUndoBuffer(wnd, es);
609 break;
611 case EM_GETFIRSTVISIBLELINE16:
612 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
613 result = es->y_offset;
614 break;
615 case EM_GETFIRSTVISIBLELINE32:
616 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
617 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
618 break;
620 case EM_SETREADONLY16:
621 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
622 /* fall through */
623 case EM_SETREADONLY32:
624 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
625 if (wParam) {
626 wnd->dwStyle |= ES_READONLY;
627 es->style |= ES_READONLY;
628 } else {
629 wnd->dwStyle &= ~ES_READONLY;
630 es->style &= ~ES_READONLY;
632 return 1;
633 break;
635 case EM_SETWORDBREAKPROC16:
636 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
637 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
638 break;
639 case EM_SETWORDBREAKPROC32:
640 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
641 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROC32A)lParam);
642 break;
644 case EM_GETWORDBREAKPROC16:
645 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
646 result = (LRESULT)es->word_break_proc16;
647 break;
648 case EM_GETWORDBREAKPROC32:
649 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
650 result = (LRESULT)es->word_break_proc32A;
651 break;
653 case EM_GETPASSWORDCHAR16:
654 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
655 /* fall through */
656 case EM_GETPASSWORDCHAR32:
657 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
658 result = es->password_char;
659 break;
661 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
663 case EM_SETMARGINS32:
664 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
665 EDIT_EM_SetMargins(wnd, es, (INT32)wParam, SLOWORD(lParam), SHIWORD(lParam));
666 break;
668 case EM_GETMARGINS32:
669 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
670 result = MAKELONG(es->left_margin, es->right_margin);
671 break;
673 case EM_GETLIMITTEXT32:
674 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
675 result = es->buffer_limit;
676 break;
678 case EM_POSFROMCHAR32:
679 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
680 result = EDIT_EM_PosFromChar(wnd, es, (INT32)wParam, FALSE);
681 break;
683 case EM_CHARFROMPOS32:
684 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
685 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
686 break;
688 case WM_GETDLGCODE:
689 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
690 result = (es->style & ES_MULTILINE) ?
691 DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
692 DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
693 break;
695 case WM_CHAR:
696 DPRINTF_EDIT_MSG32("WM_CHAR");
697 EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
698 break;
700 case WM_CLEAR:
701 DPRINTF_EDIT_MSG32("WM_CLEAR");
702 EDIT_WM_Clear(wnd, es);
703 break;
705 case WM_COMMAND:
706 DPRINTF_EDIT_MSG32("WM_COMMAND");
707 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND32)lParam);
708 break;
710 case WM_CONTEXTMENU:
711 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
712 EDIT_WM_ContextMenu(wnd, es, (HWND32)wParam, SLOWORD(lParam), SHIWORD(lParam));
713 break;
715 case WM_COPY:
716 DPRINTF_EDIT_MSG32("WM_COPY");
717 EDIT_WM_Copy(wnd, es);
718 break;
720 case WM_CUT:
721 DPRINTF_EDIT_MSG32("WM_CUT");
722 EDIT_WM_Cut(wnd, es);
723 break;
725 case WM_ENABLE:
726 DPRINTF_EDIT_MSG32("WM_ENABLE");
727 InvalidateRect32(hwnd, NULL, TRUE);
728 break;
730 case WM_ERASEBKGND:
731 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
732 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC32)wParam);
733 break;
735 case WM_GETFONT:
736 DPRINTF_EDIT_MSG32("WM_GETFONT");
737 result = (LRESULT)es->font;
738 break;
740 case WM_GETTEXT:
741 DPRINTF_EDIT_MSG32("WM_GETTEXT");
742 result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT32)wParam, (LPSTR)lParam);
743 break;
745 case WM_GETTEXTLENGTH:
746 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
747 result = lstrlen32A(es->text);
748 break;
750 case WM_HSCROLL:
751 DPRINTF_EDIT_MSG32("WM_HSCROLL");
752 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)lParam);
753 break;
755 case WM_KEYDOWN:
756 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
757 result = EDIT_WM_KeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
758 break;
760 case WM_KILLFOCUS:
761 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
762 result = EDIT_WM_KillFocus(wnd, es, (HWND32)wParam);
763 break;
765 case WM_LBUTTONDBLCLK:
766 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
767 result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
768 break;
770 case WM_LBUTTONDOWN:
771 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
772 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
773 break;
775 case WM_LBUTTONUP:
776 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
777 result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
778 break;
780 case WM_MOUSEACTIVATE:
782 * FIXME: maybe DefWindowProc() screws up, but it seems that
783 * modalless dialog boxes need this. If we don't do this, the focus
784 * will _not_ be set by DefWindowProc() for edit controls in a
785 * modalless dialog box ???
787 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
788 SetFocus32(wnd->hwndSelf);
789 result = MA_ACTIVATE;
790 break;
792 case WM_MOUSEMOVE:
794 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
796 result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
797 break;
799 case WM_PAINT:
800 DPRINTF_EDIT_MSG32("WM_PAINT");
801 EDIT_WM_Paint(wnd, es);
802 break;
804 case WM_PASTE:
805 DPRINTF_EDIT_MSG32("WM_PASTE");
806 EDIT_WM_Paste(wnd, es);
807 break;
809 case WM_SETFOCUS:
810 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
811 EDIT_WM_SetFocus(wnd, es, (HWND32)wParam);
812 break;
814 case WM_SETFONT:
815 DPRINTF_EDIT_MSG32("WM_SETFONT");
816 EDIT_WM_SetFont(wnd, es, (HFONT32)wParam, LOWORD(lParam) != 0);
817 break;
819 case WM_SETTEXT:
820 DPRINTF_EDIT_MSG32("WM_SETTEXT");
821 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
822 result = TRUE;
823 break;
825 case WM_SIZE:
826 DPRINTF_EDIT_MSG32("WM_SIZE");
827 EDIT_WM_Size(wnd, es, (UINT32)wParam, LOWORD(lParam), HIWORD(lParam));
828 break;
830 case WM_SYSKEYDOWN:
831 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
832 result = EDIT_WM_SysKeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
833 break;
835 case WM_TIMER:
836 DPRINTF_EDIT_MSG32("WM_TIMER");
837 EDIT_WM_Timer(wnd, es, (INT32)wParam, (TIMERPROC32)lParam);
838 break;
840 case WM_VSCROLL:
841 DPRINTF_EDIT_MSG32("WM_VSCROLL");
842 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)(lParam));
843 break;
845 default:
846 result = DefWindowProc32A(hwnd, msg, wParam, lParam);
847 break;
849 EDIT_UnlockBuffer(wnd, es, FALSE);
850 return result;
854 /*********************************************************************
856 * EDIT_BuildLineDefs_ML
858 * Build linked list of text lines.
859 * Lines can end with '\0' (last line), a character (if it is wrapped),
860 * a soft return '\r\r\n' or a hard return '\r\n'
863 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
865 HDC32 dc;
866 HFONT32 old_font = 0;
867 LPSTR start, cp;
868 INT32 fw;
869 LINEDEF *current_def;
870 LINEDEF **previous_next;
872 current_def = es->first_line_def;
873 do {
874 LINEDEF *next_def = current_def->next;
875 HeapFree(es->heap, 0, current_def);
876 current_def = next_def;
877 } while (current_def);
878 es->line_count = 0;
879 es->text_width = 0;
881 dc = GetDC32(wnd->hwndSelf);
882 if (es->font)
883 old_font = SelectObject32(dc, es->font);
885 fw = es->format_rect.right - es->format_rect.left;
886 start = es->text;
887 previous_next = &es->first_line_def;
888 do {
889 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
890 current_def->next = NULL;
891 cp = start;
892 while (*cp) {
893 if ((*cp == '\r') && (*(cp + 1) == '\n'))
894 break;
895 cp++;
897 if (!(*cp)) {
898 current_def->ending = END_0;
899 current_def->net_length = lstrlen32A(start);
900 } else if ((cp > start) && (*(cp - 1) == '\r')) {
901 current_def->ending = END_SOFT;
902 current_def->net_length = cp - start - 1;
903 } else {
904 current_def->ending = END_HARD;
905 current_def->net_length = cp - start;
907 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
908 start, current_def->net_length,
909 es->tabs_count, es->tabs));
910 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
911 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
912 INT32 next = 0;
913 INT32 prev;
914 do {
915 prev = next;
916 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
917 prev + 1, current_def->net_length, WB_RIGHT);
918 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
919 start, next, es->tabs_count, es->tabs));
920 } while (current_def->width <= fw);
921 if (!prev) {
922 next = 0;
923 do {
924 prev = next;
925 next++;
926 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
927 start, next, es->tabs_count, es->tabs));
928 } while (current_def->width <= fw);
929 if (!prev)
930 prev = 1;
932 current_def->net_length = prev;
933 current_def->ending = END_WRAP;
934 current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc, start,
935 current_def->net_length, es->tabs_count, es->tabs));
937 switch (current_def->ending) {
938 case END_SOFT:
939 current_def->length = current_def->net_length + 3;
940 break;
941 case END_HARD:
942 current_def->length = current_def->net_length + 2;
943 break;
944 case END_WRAP:
945 case END_0:
946 current_def->length = current_def->net_length;
947 break;
949 es->text_width = MAX(es->text_width, current_def->width);
950 start += current_def->length;
951 *previous_next = current_def;
952 previous_next = &current_def->next;
953 es->line_count++;
954 } while (current_def->ending != END_0);
955 if (es->font)
956 SelectObject32(dc, old_font);
957 ReleaseDC32(wnd->hwndSelf, dc);
961 /*********************************************************************
963 * EDIT_CallWordBreakProc
965 * Call appropriate WordBreakProc (internal or external).
967 * Note: The "start" argument should always be an index refering
968 * to es->text. The actual wordbreak proc might be
969 * 16 bit, so we can't always pass any 32 bit LPSTR.
970 * Hence we assume that es->text is the buffer that holds
971 * the string under examination (we can decide this for ourselves).
974 static INT32 EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT32 start, INT32 index, INT32 count, INT32 action)
976 if (es->word_break_proc16) {
977 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
978 SEGPTR segptr = LocalLock16(hloc16);
979 INT32 ret = (INT32)CallWordBreakProc16((FARPROC16)es->word_break_proc16,
980 segptr + start, index, count, action);
981 LocalUnlock16(hloc16);
982 return ret;
983 } else if (es->word_break_proc32A)
984 return (INT32)CallWordBreakProc32A((FARPROC32)es->word_break_proc32A,
985 es->text + start, index, count, action);
986 else
987 return EDIT_WordBreakProc(es->text + start, index, count, action);
991 /*********************************************************************
993 * EDIT_CharFromPos
995 * Beware: This is not the function called on EM_CHARFROMPOS
996 * The position _can_ be outside the formatting / client
997 * rectangle
998 * The return value is only the character index
1001 static INT32 EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y, LPBOOL32 after_wrap)
1003 INT32 index;
1004 HDC32 dc;
1005 HFONT32 old_font = 0;
1007 if (es->style & ES_MULTILINE) {
1008 INT32 line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1009 INT32 line_index = 0;
1010 LINEDEF *line_def = es->first_line_def;
1011 while ((line > 0) && line_def->next) {
1012 line_index += line_def->length;
1013 line_def = line_def->next;
1014 line--;
1016 x += es->x_offset - es->format_rect.left;
1017 if (x >= line_def->width) {
1018 if (after_wrap)
1019 *after_wrap = (line_def->ending == END_WRAP);
1020 return line_index + line_def->net_length;
1022 if (x <= 0) {
1023 if (after_wrap)
1024 *after_wrap = FALSE;
1025 return line_index;
1027 dc = GetDC32(wnd->hwndSelf);
1028 if (es->font)
1029 old_font = SelectObject32(dc, es->font);
1030 /* FIXME: inefficient algorithm */
1031 for (index = line_index + 1 ; index < line_index + line_def->net_length ; index++)
1032 if (LOWORD(GetTabbedTextExtent32A(dc, es->text + line_index,
1033 index - line_index, es->tabs_count, es->tabs)) >= x)
1034 break;
1035 if (after_wrap)
1036 *after_wrap = ((index == line_index + line_def->net_length) &&
1037 (line_def->ending == END_WRAP));
1038 } else {
1039 LPSTR text;
1040 SIZE32 size;
1041 if (after_wrap)
1042 *after_wrap = FALSE;
1043 x -= es->format_rect.left;
1044 if (!x)
1045 return es->x_offset;
1046 text = EDIT_GetPasswordPointer_SL(wnd, es);
1047 dc = GetDC32(wnd->hwndSelf);
1048 if (es->font)
1049 old_font = SelectObject32(dc, es->font);
1050 if (x < 0) {
1051 x = -x;
1052 /* FIXME: inefficient algorithm */
1053 for (index = es->x_offset ; index ; index--) {
1054 GetTextExtentPoint32A(dc, text + index,
1055 es->x_offset - index, &size);
1056 if (size.cx > x)
1057 break;
1059 } else {
1060 INT32 len = lstrlen32A(es->text);
1061 /* FIXME: inefficient algorithm */
1062 for (index = es->x_offset ; index < len ; index++) {
1063 GetTextExtentPoint32A(dc, text + es->x_offset,
1064 index - es->x_offset, &size);
1065 if (size.cx >= x)
1066 break;
1069 if (es->style & ES_PASSWORD)
1070 HeapFree(es->heap, 0 ,text);
1072 if (es->font)
1073 SelectObject32(dc, old_font);
1074 ReleaseDC32(wnd->hwndSelf, dc);
1075 return index;
1079 /*********************************************************************
1081 * EDIT_ConfinePoint
1083 * adjusts the point to be within the formatting rectangle
1084 * (so CharFromPos returns the nearest _visible_ character)
1087 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT32 x, LPINT32 y)
1089 *x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
1090 *y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
1094 /*********************************************************************
1096 * EDIT_GetLineRect
1098 * Calculates the bounding rectangle for a line from a starting
1099 * column to an ending column.
1102 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT32 line, INT32 scol, INT32 ecol, LPRECT32 rc)
1104 INT32 line_index = EDIT_EM_LineIndex(wnd, es, line);
1106 if (es->style & ES_MULTILINE)
1107 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1108 else
1109 rc->top = es->format_rect.top;
1110 rc->bottom = rc->top + es->line_height;
1111 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1112 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1116 /*********************************************************************
1118 * EDIT_GetPasswordPointer_SL
1120 * note: caller should free the (optionally) allocated buffer
1123 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
1125 if (es->style & ES_PASSWORD) {
1126 INT32 len = lstrlen32A(es->text);
1127 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1128 RtlFillMemory(text, len, es->password_char);
1129 text[len] = '\0';
1130 return text;
1131 } else
1132 return es->text;
1136 /*********************************************************************
1138 * EDIT_LockBuffer
1140 * This acts as a LOCAL_Lock(), but it locks only once. This way
1141 * you can call it whenever you like, without unlocking.
1144 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1146 if (!es) {
1147 fprintf(stderr, "edit: LockBuffer() without an EDITSTATE ... please report\n");
1148 return;
1150 if (!(es->style & ES_MULTILINE))
1151 return;
1152 if (!es->text) {
1153 if (es->hloc32)
1154 es->text = LocalLock32(es->hloc32);
1155 else if (es->hloc16)
1156 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1157 else {
1158 fprintf(stderr, "edit: LockBuffer() without a buffer ... please report\n");
1159 return;
1162 es->lock_count++;
1166 /*********************************************************************
1168 * EDIT_SL_InvalidateText
1170 * Called from EDIT_InvalidateText().
1171 * Does the job for single-line controls only.
1174 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1176 RECT32 line_rect;
1177 RECT32 rc;
1179 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1180 if (IntersectRect32(&rc, &line_rect, &es->format_rect))
1181 InvalidateRect32(wnd->hwndSelf, &rc, FALSE);
1185 /*********************************************************************
1187 * EDIT_ML_InvalidateText
1189 * Called from EDIT_InvalidateText().
1190 * Does the job for multi-line controls only.
1193 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1195 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1196 INT32 sl = EDIT_EM_LineFromChar(wnd, es, start);
1197 INT32 el = EDIT_EM_LineFromChar(wnd, es, end);
1198 INT32 sc;
1199 INT32 ec;
1200 RECT32 rc1;
1201 RECT32 rcWnd;
1202 RECT32 rcLine;
1203 RECT32 rcUpdate;
1204 INT32 l;
1206 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1207 return;
1209 sc = start - EDIT_EM_LineIndex(wnd, es, sl);
1210 ec = end - EDIT_EM_LineIndex(wnd, es, el);
1211 if (sl < es->y_offset) {
1212 sl = es->y_offset;
1213 sc = 0;
1215 if (el > es->y_offset + vlc) {
1216 el = es->y_offset + vlc;
1217 ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
1219 GetClientRect32(wnd->hwndSelf, &rc1);
1220 IntersectRect32(&rcWnd, &rc1, &es->format_rect);
1221 if (sl == el) {
1222 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1223 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1224 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1225 } else {
1226 EDIT_GetLineRect(wnd, es, sl, sc,
1227 EDIT_EM_LineLength(wnd, es,
1228 EDIT_EM_LineIndex(wnd, es, sl)),
1229 &rcLine);
1230 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1231 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1232 for (l = sl + 1 ; l < el ; l++) {
1233 EDIT_GetLineRect(wnd, es, l, 0,
1234 EDIT_EM_LineLength(wnd, es,
1235 EDIT_EM_LineIndex(wnd, es, l)),
1236 &rcLine);
1237 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1238 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1240 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1241 if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
1242 InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
1247 /*********************************************************************
1249 * EDIT_InvalidateText
1251 * Invalidate the text from offset start upto, but not including,
1252 * offset end. Useful for (re)painting the selection.
1253 * Regions outside the linewidth are not invalidated.
1254 * end == -1 means end == TextLength.
1255 * start and end need not be ordered.
1258 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
1260 if (end == start)
1261 return;
1263 if (end == -1)
1264 end = lstrlen32A(es->text);
1266 ORDER_INT32(start, end);
1268 if (es->style & ES_MULTILINE)
1269 EDIT_ML_InvalidateText(wnd, es, start, end);
1270 else
1271 EDIT_SL_InvalidateText(wnd, es, start, end);
1275 /*********************************************************************
1277 * EDIT_MakeFit
1279 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1282 static BOOL32 EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT32 size)
1284 HLOCAL32 hNew32;
1285 HLOCAL16 hNew16;
1287 if (size <= es->buffer_size)
1288 return TRUE;
1289 if (size > es->buffer_limit) {
1290 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1291 return FALSE;
1293 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1294 if (size > es->buffer_limit)
1295 size = es->buffer_limit;
1297 dprintf_edit(stddeb, "edit: EDIT_MakeFit: trying to ReAlloc to %d+1\n", size);
1299 EDIT_UnlockBuffer(wnd, es, TRUE);
1300 if (es->text) {
1301 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1302 es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1303 else
1304 es->buffer_size = 0;
1305 } else if (es->hloc32) {
1306 if ((hNew32 = LocalReAlloc32(es->hloc32, size + 1, 0))) {
1307 dprintf_edit(stddeb, "edit: EDIT_MakeFit: Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1308 es->hloc32 = hNew32;
1309 es->buffer_size = MIN(LocalSize32(es->hloc32) - 1, es->buffer_limit);
1311 } else if (es->hloc16) {
1312 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1313 dprintf_edit(stddeb, "edit: EDIT_MakeFit: Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1314 es->hloc16 = hNew16;
1315 es->buffer_size = MIN(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1318 if (es->buffer_size < size) {
1319 EDIT_LockBuffer(wnd, es);
1320 dprintf_edit(stddeb, "edit: EDIT_MakeFit: FAILED ! We now have %d+1\n", es->buffer_size);
1321 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1322 return FALSE;
1323 } else {
1324 EDIT_LockBuffer(wnd, es);
1325 dprintf_edit(stddeb, "edit: EDIT_MakeFit: We now have %d+1\n", es->buffer_size);
1326 return TRUE;
1331 /*********************************************************************
1333 * EDIT_MakeUndoFit
1335 * Try to fit size + 1 bytes in the undo buffer.
1338 static BOOL32 EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT32 size)
1340 if (size <= es->undo_buffer_size)
1341 return TRUE;
1342 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1344 dprintf_edit(stddeb, "edit: EDIT_MakeUndoFit: trying to ReAlloc to %d+1\n", size);
1346 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1347 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1348 if (es->undo_buffer_size < size) {
1349 dprintf_edit(stddeb, "edit: EDIT_MakeUndoFit: FAILED ! We now have %d+1\n", es->undo_buffer_size);
1350 return FALSE;
1352 return TRUE;
1354 return FALSE;
1358 /*********************************************************************
1360 * EDIT_MoveBackward
1363 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1365 INT32 e = es->selection_end;
1367 if (e) {
1368 e--;
1369 if ((es->style & ES_MULTILINE) && e &&
1370 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1371 e--;
1372 if (e && (es->text[e - 1] == '\r'))
1373 e--;
1376 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1377 EDIT_EM_ScrollCaret(wnd, es);
1381 /*********************************************************************
1383 * EDIT_MoveDown_ML
1385 * Only for multi line controls
1386 * Move the caret one line down, on a column with the nearest
1387 * x coordinate on the screen (might be a different column).
1390 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1392 INT32 s = es->selection_start;
1393 INT32 e = es->selection_end;
1394 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1395 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1396 INT32 x = SLOWORD(pos);
1397 INT32 y = SHIWORD(pos);
1399 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1400 if (!extend)
1401 s = e;
1402 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1403 EDIT_EM_ScrollCaret(wnd, es);
1407 /*********************************************************************
1409 * EDIT_MoveEnd
1412 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL32 extend)
1414 BOOL32 after_wrap = FALSE;
1415 INT32 e;
1417 if (es->style & ES_MULTILINE)
1418 e = EDIT_CharFromPos(wnd, es, 0x7fffffff,
1419 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1420 else
1421 e = lstrlen32A(es->text);
1422 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1423 EDIT_EM_ScrollCaret(wnd, es);
1427 /*********************************************************************
1429 * EDIT_MoveForward
1432 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1434 INT32 e = es->selection_end;
1436 if (es->text[e]) {
1437 e++;
1438 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1439 if (es->text[e] == '\n')
1440 e++;
1441 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1442 e += 2;
1445 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1446 EDIT_EM_ScrollCaret(wnd, es);
1450 /*********************************************************************
1452 * EDIT_MoveHome
1454 * Home key: move to beginning of line.
1457 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL32 extend)
1459 INT32 e;
1461 if (es->style & ES_MULTILINE)
1462 e = EDIT_CharFromPos(wnd, es, 0x80000000,
1463 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1464 else
1465 e = 0;
1466 EDIT_EM_SetSel(wnd, es, e, extend ? es->selection_start : e, FALSE);
1467 EDIT_EM_ScrollCaret(wnd, es);
1471 /*********************************************************************
1473 * EDIT_MovePageDown_ML
1475 * Only for multi line controls
1476 * Move the caret one page down, on a column with the nearest
1477 * x coordinate on the screen (might be a different column).
1480 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1482 INT32 s = es->selection_start;
1483 INT32 e = es->selection_end;
1484 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1485 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1486 INT32 x = SLOWORD(pos);
1487 INT32 y = SHIWORD(pos);
1489 e = EDIT_CharFromPos(wnd, es, x,
1490 y + (es->format_rect.bottom - es->format_rect.top),
1491 &after_wrap);
1492 if (!extend)
1493 s = e;
1494 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1495 EDIT_EM_ScrollCaret(wnd, es);
1499 /*********************************************************************
1501 * EDIT_MovePageUp_ML
1503 * Only for multi line controls
1504 * Move the caret one page up, on a column with the nearest
1505 * x coordinate on the screen (might be a different column).
1508 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1510 INT32 s = es->selection_start;
1511 INT32 e = es->selection_end;
1512 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1513 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1514 INT32 x = SLOWORD(pos);
1515 INT32 y = SHIWORD(pos);
1517 e = EDIT_CharFromPos(wnd, es, x,
1518 y - (es->format_rect.bottom - es->format_rect.top),
1519 &after_wrap);
1520 if (!extend)
1521 s = e;
1522 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1523 EDIT_EM_ScrollCaret(wnd, es);
1527 /*********************************************************************
1529 * EDIT_MoveUp_ML
1531 * Only for multi line controls
1532 * Move the caret one line up, on a column with the nearest
1533 * x coordinate on the screen (might be a different column).
1536 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
1538 INT32 s = es->selection_start;
1539 INT32 e = es->selection_end;
1540 BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
1541 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1542 INT32 x = SLOWORD(pos);
1543 INT32 y = SHIWORD(pos);
1545 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1546 if (!extend)
1547 s = e;
1548 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1549 EDIT_EM_ScrollCaret(wnd, es);
1553 /*********************************************************************
1555 * EDIT_MoveWordBackward
1558 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1560 INT32 s = es->selection_start;
1561 INT32 e = es->selection_end;
1562 INT32 l;
1563 INT32 ll;
1564 INT32 li;
1566 l = EDIT_EM_LineFromChar(wnd, es, e);
1567 ll = EDIT_EM_LineLength(wnd, es, e);
1568 li = EDIT_EM_LineIndex(wnd, es, l);
1569 if (e - li == 0) {
1570 if (l) {
1571 li = EDIT_EM_LineIndex(wnd, es, l - 1);
1572 e = li + EDIT_EM_LineLength(wnd, es, li);
1574 } else {
1575 e = li + (INT32)EDIT_CallWordBreakProc(wnd, es,
1576 li, e - li, ll, WB_LEFT);
1578 if (!extend)
1579 s = e;
1580 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1581 EDIT_EM_ScrollCaret(wnd, es);
1585 /*********************************************************************
1587 * EDIT_MoveWordForward
1590 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
1592 INT32 s = es->selection_start;
1593 INT32 e = es->selection_end;
1594 INT32 l;
1595 INT32 ll;
1596 INT32 li;
1598 l = EDIT_EM_LineFromChar(wnd, es, e);
1599 ll = EDIT_EM_LineLength(wnd, es, e);
1600 li = EDIT_EM_LineIndex(wnd, es, l);
1601 if (e - li == ll) {
1602 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1603 e = EDIT_EM_LineIndex(wnd, es, l + 1);
1604 } else {
1605 e = li + EDIT_CallWordBreakProc(wnd, es,
1606 li, e - li + 1, ll, WB_RIGHT);
1608 if (!extend)
1609 s = e;
1610 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1611 EDIT_EM_ScrollCaret(wnd, es);
1615 /*********************************************************************
1617 * EDIT_PaintLine
1620 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 line, BOOL32 rev)
1622 INT32 s = es->selection_start;
1623 INT32 e = es->selection_end;
1624 INT32 li;
1625 INT32 ll;
1626 INT32 x;
1627 INT32 y;
1628 LRESULT pos;
1630 if (es->style & ES_MULTILINE) {
1631 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1632 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1633 return;
1634 } else if (line)
1635 return;
1637 dprintf_edit(stddeb, "edit: EDIT_PaintLine: line=%d\n", line);
1639 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
1640 x = SLOWORD(pos);
1641 y = SHIWORD(pos);
1642 li = EDIT_EM_LineIndex(wnd, es, line);
1643 ll = EDIT_EM_LineLength(wnd, es, li);
1644 s = es->selection_start;
1645 e = es->selection_end;
1646 ORDER_INT32(s, e);
1647 s = MIN(li + ll, MAX(li, s));
1648 e = MIN(li + ll, MAX(li, e));
1649 if (rev && (s != e) &&
1650 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1651 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
1652 x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
1653 x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
1654 } else
1655 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
1659 /*********************************************************************
1661 * EDIT_PaintText
1664 static INT32 EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 x, INT32 y, INT32 line, INT32 col, INT32 count, BOOL32 rev)
1666 COLORREF BkColor;
1667 COLORREF TextColor;
1668 INT32 ret;
1669 INT32 li;
1670 SIZE32 size;
1672 if (!count)
1673 return 0;
1674 BkColor = GetBkColor32(dc);
1675 TextColor = GetTextColor32(dc);
1676 if (rev) {
1677 SetBkColor32(dc, GetSysColor32(COLOR_HIGHLIGHT));
1678 SetTextColor32(dc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
1680 li = EDIT_EM_LineIndex(wnd, es, line);
1681 if (es->style & ES_MULTILINE) {
1682 ret = (INT32)LOWORD(TabbedTextOut32A(dc, x, y, es->text + li + col, count,
1683 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1684 } else {
1685 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
1686 TextOut32A(dc, x, y, text + li + col, count);
1687 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1688 ret = size.cx;
1689 if (es->style & ES_PASSWORD)
1690 HeapFree(es->heap, 0, text);
1692 if (rev) {
1693 SetBkColor32(dc, BkColor);
1694 SetTextColor32(dc, TextColor);
1696 return ret;
1700 /*********************************************************************
1702 * EM_SCROLLCARET
1705 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
1707 if (es->style & ES_MULTILINE) {
1708 INT32 l;
1709 INT32 li;
1710 INT32 vlc;
1711 INT32 ww;
1712 INT32 cw = es->char_width;
1713 INT32 x;
1714 INT32 dy = 0;
1715 INT32 dx = 0;
1717 l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
1718 li = EDIT_EM_LineIndex(wnd, es, l);
1719 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
1720 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1721 if (l >= es->y_offset + vlc)
1722 dy = l - vlc + 1 - es->y_offset;
1723 if (l < es->y_offset)
1724 dy = l - es->y_offset;
1725 ww = es->format_rect.right - es->format_rect.left;
1726 if (x < es->format_rect.left)
1727 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1728 if (x > es->format_rect.right)
1729 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1730 if (dy || dx)
1731 EDIT_EM_LineScroll(wnd, es, dx, dy);
1732 } else {
1733 INT32 x;
1734 INT32 goal;
1735 INT32 format_width;
1737 if (!(es->style & ES_AUTOHSCROLL))
1738 return;
1740 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1741 format_width = es->format_rect.right - es->format_rect.left;
1742 if (x < es->format_rect.left) {
1743 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1744 do {
1745 es->x_offset--;
1746 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1747 } while ((x < goal) && es->x_offset);
1748 /* FIXME: use ScrollWindow() somehow to improve performance */
1749 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
1750 } else if (x > es->format_rect.right) {
1751 INT32 x_last;
1752 INT32 len = lstrlen32A(es->text);
1753 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1754 do {
1755 es->x_offset++;
1756 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
1757 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
1758 } while ((x > goal) && (x_last > es->format_rect.right));
1759 /* FIXME: use ScrollWindow() somehow to improve performance */
1760 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
1766 /*********************************************************************
1768 * EDIT_SetRectNP
1770 * note: this is not (exactly) the handler called on EM_SETRECTNP
1771 * it is also used to set the rect of a single line control
1774 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT32 rc)
1776 CopyRect32(&es->format_rect, rc);
1777 if (es->style & WS_BORDER) {
1778 INT32 bw = GetSystemMetrics32(SM_CXBORDER) + 1;
1779 es->format_rect.left += bw;
1780 es->format_rect.top += bw;
1781 es->format_rect.right -= bw;
1782 es->format_rect.bottom -= bw;
1784 es->format_rect.left += es->left_margin;
1785 es->format_rect.right -= es->right_margin;
1786 es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
1787 if (es->style & ES_MULTILINE)
1788 es->format_rect.bottom = es->format_rect.top +
1789 MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1790 else
1791 es->format_rect.bottom = es->format_rect.top + es->line_height;
1792 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1793 EDIT_BuildLineDefs_ML(wnd, es);
1797 /*********************************************************************
1799 * EDIT_UnlockBuffer
1802 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL32 force)
1804 if (!es) {
1805 fprintf(stderr, "edit: UnlockBuffer() without an EDITSTATE ... please report\n");
1806 return;
1808 if (!(es->style & ES_MULTILINE))
1809 return;
1810 if (!es->lock_count) {
1811 fprintf(stderr, "edit: UnlockBuffer() with lock_count == 0 ... please report\n");
1812 return;
1814 if (!es->text) {
1815 fprintf(stderr, "edit: UnlockBuffer() with es->text == 0 ... please report\n");
1816 return;
1818 if (force || (es->lock_count == 1)) {
1819 if (es->hloc32) {
1820 LocalUnlock32(es->hloc32);
1821 es->text = NULL;
1822 } else if (es->hloc16) {
1823 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1824 es->text = NULL;
1827 es->lock_count--;
1831 /*********************************************************************
1833 * EDIT_WordBreakProc
1835 * Find the beginning of words.
1836 * Note: unlike the specs for a WordBreakProc, this function only
1837 * allows to be called without linebreaks between s[0] upto
1838 * s[count - 1]. Remember it is only called
1839 * internally, so we can decide this for ourselves.
1842 static INT32 EDIT_WordBreakProc(LPSTR s, INT32 index, INT32 count, INT32 action)
1844 INT32 ret = 0;
1846 dprintf_edit(stddeb, "edit: EDIT_WordBreakProc: s=%p, index=%u"
1847 ", count=%u, action=%d\n", s, index, count, action);
1849 switch (action) {
1850 case WB_LEFT:
1851 if (!count)
1852 break;
1853 if (index)
1854 index--;
1855 if (s[index] == ' ') {
1856 while (index && (s[index] == ' '))
1857 index--;
1858 if (index) {
1859 while (index && (s[index] != ' '))
1860 index--;
1861 if (s[index] == ' ')
1862 index++;
1864 } else {
1865 while (index && (s[index] != ' '))
1866 index--;
1867 if (s[index] == ' ')
1868 index++;
1870 ret = index;
1871 break;
1872 case WB_RIGHT:
1873 if (!count)
1874 break;
1875 if (index)
1876 index--;
1877 if (s[index] == ' ')
1878 while ((index < count) && (s[index] == ' ')) index++;
1879 else {
1880 while (s[index] && (s[index] != ' ') && (index < count))
1881 index++;
1882 while ((s[index] == ' ') && (index < count)) index++;
1884 ret = index;
1885 break;
1886 case WB_ISDELIMITER:
1887 ret = (s[index] == ' ');
1888 break;
1889 default:
1890 fprintf(stderr, "edit: EDIT_WordBreakProc: unknown action code, please report !\n");
1891 break;
1893 return ret;
1897 /*********************************************************************
1899 * EM_CHARFROMPOS
1901 * FIXME: do the specs mean to return LineIndex or LineNumber ???
1902 * Let's assume LineIndex is meant
1903 * FIXME: do the specs mean to return -1 if outside client area or
1904 * if outside formatting rectangle ???
1907 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y)
1909 POINT32 pt;
1910 RECT32 rc;
1911 INT32 index;
1913 pt.x = x;
1914 pt.y = y;
1915 GetClientRect32(wnd->hwndSelf, &rc);
1916 if (!PtInRect32(&rc, pt))
1917 return -1;
1919 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
1920 return MAKELONG(index, EDIT_EM_LineIndex(wnd, es,
1921 EDIT_EM_LineFromChar(wnd, es, index)));
1925 /*********************************************************************
1927 * EM_FMTLINES
1930 static BOOL32 EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL32 add_eol)
1932 fprintf(stdnimp, "edit: EM_FMTLINES: message not implemented\n");
1933 return add_eol;
1937 /*********************************************************************
1939 * EM_GETHANDLE
1941 * Hopefully this won't fire back at us.
1942 * We always start with a fixed buffer in our own heap.
1943 * However, with this message a 32 bit application requests
1944 * a handle to 32 bit moveable local heap memory, where it expects
1945 * to find the text.
1946 * It's a pitty that from this moment on we have to use this
1947 * local heap, because applications may rely on the handle
1948 * in the future.
1950 * In this function we'll try to switch to local heap.
1953 static HLOCAL32 EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
1955 HLOCAL32 newBuf;
1956 LPSTR newText;
1957 INT32 newSize;
1959 if (!(es->style & ES_MULTILINE))
1960 return 0;
1962 if (es->hloc32)
1963 return es->hloc32;
1964 else if (es->hloc16)
1965 return (HLOCAL32)es->hloc16;
1967 if (!(newBuf = LocalAlloc32(LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
1968 fprintf(stderr, "edit: EM_GETHANDLE: could not allocate new 32 bit buffer\n");
1969 return 0;
1971 newSize = MIN(LocalSize32(newBuf) - 1, es->buffer_limit);
1972 if (!(newText = LocalLock32(newBuf))) {
1973 fprintf(stderr, "edit: EM_GETHANDLE: could not lock new 32 bit buffer\n");
1974 LocalFree32(newBuf);
1975 return 0;
1977 lstrcpy32A(newText, es->text);
1978 EDIT_UnlockBuffer(wnd, es, TRUE);
1979 if (es->text)
1980 HeapFree(es->heap, 0, es->text);
1981 es->hloc32 = newBuf;
1982 es->hloc16 = (HLOCAL16)NULL;
1983 es->buffer_size = newSize;
1984 es->text = newText;
1985 EDIT_LockBuffer(wnd, es);
1986 dprintf_edit(stddeb, "edit: EM_GETHANDLE: switched to 32 bit local heap\n");
1988 return es->hloc32;
1992 /*********************************************************************
1994 * EM_GETHANDLE16
1996 * Hopefully this won't fire back at us.
1997 * We always start with a buffer in 32 bit linear memory.
1998 * However, with this message a 16 bit application requests
1999 * a handle of 16 bit local heap memory, where it expects to find
2000 * the text.
2001 * It's a pitty that from this moment on we have to use this
2002 * local heap, because applications may rely on the handle
2003 * in the future.
2005 * In this function we'll try to switch to local heap.
2007 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2009 HLOCAL16 newBuf;
2010 LPSTR newText;
2011 INT32 newSize;
2013 if (!(es->style & ES_MULTILINE))
2014 return 0;
2016 if (es->hloc16)
2017 return es->hloc16;
2019 if (!LOCAL_HeapSize(wnd->hInstance)) {
2020 if (!LocalInit(wnd->hInstance, 0,
2021 GlobalSize16(wnd->hInstance))) {
2022 fprintf(stderr, "edit: EM_GETHANDLE: could not initialize local heap\n");
2023 return 0;
2025 dprintf_edit(stddeb, "edit: EM_GETHANDLE: local heap initialized\n");
2027 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
2028 fprintf(stderr, "edit: EM_GETHANDLE: could not allocate new 16 bit buffer\n");
2029 return 0;
2031 newSize = MIN(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2032 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2033 fprintf(stderr, "edit: EM_GETHANDLE: could not lock new 16 bit buffer\n");
2034 LOCAL_Free(wnd->hInstance, newBuf);
2035 return 0;
2037 lstrcpy32A(newText, es->text);
2038 EDIT_UnlockBuffer(wnd, es, TRUE);
2039 if (es->text)
2040 HeapFree(es->heap, 0, es->text);
2041 else if (es->hloc32) {
2042 while (LocalFree32(es->hloc32)) ;
2043 LocalFree32(es->hloc32);
2045 es->hloc32 = (HLOCAL32)NULL;
2046 es->hloc16 = newBuf;
2047 es->buffer_size = newSize;
2048 es->text = newText;
2049 EDIT_LockBuffer(wnd, es);
2050 dprintf_edit(stddeb, "edit: EM_GETHANDLE: switched to 16 bit buffer\n");
2052 return es->hloc16;
2056 /*********************************************************************
2058 * EM_GETLINE
2061 static INT32 EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT32 line, LPSTR lpch)
2063 LPSTR src;
2064 INT32 len;
2065 INT32 i;
2067 if (es->style & ES_MULTILINE) {
2068 if (line >= es->line_count)
2069 return 0;
2070 } else
2071 line = 0;
2072 src = es->text + EDIT_EM_LineIndex(wnd, es, line);
2073 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, line));
2074 for (i = 0 ; i < len ; i++) {
2075 *lpch = *src;
2076 src++;
2077 lpch++;
2079 return (LRESULT)len;
2083 /*********************************************************************
2085 * EM_GETSEL
2088 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT32 start, LPUINT32 end)
2090 UINT32 s = es->selection_start;
2091 UINT32 e = es->selection_end;
2093 ORDER_UINT32(s, e);
2094 if (start)
2095 *start = s;
2096 if (end)
2097 *end = e;
2098 return MAKELONG(s, e);
2102 /*********************************************************************
2104 * EM_GETTHUMB
2106 * FIXME: is this right ? (or should it be only VSCROLL)
2107 * (and maybe only for edit controls that really have their
2108 * own scrollbars) (and maybe only for multiline controls ?)
2109 * All in all: very poorly documented
2111 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2112 * WM_VSCROLL handlers
2115 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2117 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
2118 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
2122 /*********************************************************************
2124 * EM_LINEFROMCHAR
2127 static INT32 EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT32 index)
2129 INT32 line;
2130 LINEDEF *line_def;
2132 if (!(es->style & ES_MULTILINE))
2133 return 0;
2134 if (index > lstrlen32A(es->text))
2135 return es->line_count - 1;
2136 if (index == -1)
2137 index = MIN(es->selection_start, es->selection_end);
2139 line = 0;
2140 line_def = es->first_line_def;
2141 index -= line_def->length;
2142 while ((index >= 0) && line_def->next) {
2143 line++;
2144 line_def = line_def->next;
2145 index -= line_def->length;
2147 return line;
2151 /*********************************************************************
2153 * EM_LINEINDEX
2156 static INT32 EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT32 line)
2158 INT32 line_index;
2159 LINEDEF *line_def;
2161 if (!(es->style & ES_MULTILINE))
2162 return 0;
2163 if (line >= es->line_count)
2164 return -1;
2166 line_index = 0;
2167 line_def = es->first_line_def;
2168 if (line == -1) {
2169 INT32 index = es->selection_end - line_def->length;
2170 while ((index >= 0) && line_def->next) {
2171 line_index += line_def->length;
2172 line_def = line_def->next;
2173 index -= line_def->length;
2175 } else {
2176 while (line > 0) {
2177 line_index += line_def->length;
2178 line_def = line_def->next;
2179 line--;
2182 return line_index;
2186 /*********************************************************************
2188 * EM_LINELENGTH
2191 static INT32 EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT32 index)
2193 LINEDEF *line_def;
2195 if (!(es->style & ES_MULTILINE))
2196 return lstrlen32A(es->text);
2198 if (index == -1) {
2199 /* FIXME: broken
2200 INT32 sl = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
2201 INT32 el = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2202 return es->selection_start - es->line_defs[sl].offset +
2203 es->line_defs[el].offset +
2204 es->line_defs[el].length - es->selection_end;
2206 return 0;
2208 line_def = es->first_line_def;
2209 index -= line_def->length;
2210 while ((index >= 0) && line_def->next) {
2211 line_def = line_def->next;
2212 index -= line_def->length;
2214 return line_def->net_length;
2218 /*********************************************************************
2220 * EM_LINESCROLL
2222 * FIXME: dx is in average character widths
2223 * However, we assume it is in pixels when we use this
2224 * function internally
2227 static BOOL32 EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT32 dx, INT32 dy)
2229 INT32 nyoff;
2231 if (!(es->style & ES_MULTILINE))
2232 return FALSE;
2234 if (-dx > es->x_offset)
2235 dx = -es->x_offset;
2236 if (dx > es->text_width - es->x_offset)
2237 dx = es->text_width - es->x_offset;
2238 nyoff = MAX(0, es->y_offset + dy);
2239 if (nyoff >= es->line_count)
2240 nyoff = es->line_count - 1;
2241 dy = (es->y_offset - nyoff) * es->line_height;
2242 if (dx || dy) {
2243 if (!(wnd->flags & WIN_NO_REDRAW)) {
2244 RECT32 rc1;
2245 RECT32 rc;
2246 GetClientRect32(wnd->hwndSelf, &rc1);
2247 IntersectRect32(&rc, &rc1, &es->format_rect);
2248 ScrollWindowEx32(wnd->hwndSelf, -dx, dy,
2249 NULL, &rc, (HRGN32)NULL, NULL, SW_INVALIDATE);
2251 es->y_offset = nyoff;
2252 es->x_offset += dx;
2254 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2255 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2256 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2257 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2258 return TRUE;
2262 /*********************************************************************
2264 * EM_POSFROMCHAR
2267 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT32 index, BOOL32 after_wrap)
2269 INT32 len = lstrlen32A(es->text);
2270 INT32 l;
2271 INT32 li;
2272 INT32 x;
2273 INT32 y = 0;
2274 HDC32 dc;
2275 HFONT32 old_font = 0;
2276 SIZE32 size;
2278 index = MIN(index, len);
2279 dc = GetDC32(wnd->hwndSelf);
2280 if (es->font)
2281 old_font = SelectObject32(dc, es->font);
2282 if (es->style & ES_MULTILINE) {
2283 l = EDIT_EM_LineFromChar(wnd, es, index);
2284 y = (l - es->y_offset) * es->line_height;
2285 li = EDIT_EM_LineIndex(wnd, es, l);
2286 if (after_wrap && (li == index) && l) {
2287 INT32 l2 = l - 1;
2288 LINEDEF *line_def = es->first_line_def;
2289 while (l2) {
2290 line_def = line_def->next;
2291 l2--;
2293 if (line_def->ending == END_WRAP) {
2294 l--;
2295 y -= es->line_height;
2296 li = EDIT_EM_LineIndex(wnd, es, l);
2299 x = LOWORD(GetTabbedTextExtent32A(dc, es->text + li, index - li,
2300 es->tabs_count, es->tabs)) - es->x_offset;
2301 } else {
2302 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
2303 if (index < es->x_offset) {
2304 GetTextExtentPoint32A(dc, text + index,
2305 es->x_offset - index, &size);
2306 x = -size.cx;
2307 } else {
2308 GetTextExtentPoint32A(dc, text + es->x_offset,
2309 index - es->x_offset, &size);
2310 x = size.cx;
2312 y = 0;
2313 if (es->style & ES_PASSWORD)
2314 HeapFree(es->heap, 0 ,text);
2316 x += es->format_rect.left;
2317 y += es->format_rect.top;
2318 if (es->font)
2319 SelectObject32(dc, old_font);
2320 ReleaseDC32(wnd->hwndSelf, dc);
2321 return MAKELONG((INT16)x, (INT16)y);
2325 /*********************************************************************
2327 * EM_REPLACESEL
2329 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2332 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL32 can_undo, LPCSTR lpsz_replace)
2334 INT32 strl = lstrlen32A(lpsz_replace);
2335 INT32 tl = lstrlen32A(es->text);
2336 INT32 utl;
2337 UINT32 s;
2338 UINT32 e;
2339 INT32 i;
2340 LPSTR p;
2342 s = es->selection_start;
2343 e = es->selection_end;
2345 if ((s == e) && !strl)
2346 return;
2348 ORDER_UINT32(s, e);
2350 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2351 return;
2353 if (e != s) {
2354 /* there is something to be deleted */
2355 if (can_undo) {
2356 utl = lstrlen32A(es->undo_text);
2357 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2358 /* undo-buffer is extended to the right */
2359 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2360 lstrcpyn32A(es->undo_text + utl, es->text + s, e - s + 1);
2361 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2362 /* undo-buffer is extended to the left */
2363 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2364 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2365 p[e - s] = p[0];
2366 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2367 p[i] = (es->text + s)[i];
2368 es->undo_position = s;
2369 } else {
2370 /* new undo-buffer */
2371 EDIT_MakeUndoFit(wnd, es, e - s);
2372 lstrcpyn32A(es->undo_text, es->text + s, e - s + 1);
2373 es->undo_position = s;
2375 /* any deletion makes the old insertion-undo invalid */
2376 es->undo_insert_count = 0;
2377 } else
2378 EDIT_EM_EmptyUndoBuffer(wnd, es);
2380 /* now delete */
2381 lstrcpy32A(es->text + s, es->text + e);
2383 if (strl) {
2384 /* there is an insertion */
2385 if (can_undo) {
2386 if ((s == es->undo_position) ||
2387 ((es->undo_insert_count) &&
2388 (s == es->undo_position + es->undo_insert_count)))
2390 * insertion is new and at delete position or
2391 * an extension to either left or right
2393 es->undo_insert_count += strl;
2394 else {
2395 /* new insertion undo */
2396 es->undo_position = s;
2397 es->undo_insert_count = strl;
2398 /* new insertion makes old delete-buffer invalid */
2399 *es->undo_text = '\0';
2401 } else
2402 EDIT_EM_EmptyUndoBuffer(wnd, es);
2404 /* now insert */
2405 tl = lstrlen32A(es->text);
2406 for (p = es->text + tl ; p >= es->text + s ; p--)
2407 p[strl] = p[0];
2408 for (i = 0 , p = es->text + s ; i < strl ; i++)
2409 p[i] = lpsz_replace[i];
2410 if(es->style & ES_UPPERCASE)
2411 CharUpperBuff32A(p, strl);
2412 else if(es->style & ES_LOWERCASE)
2413 CharLowerBuff32A(p, strl);
2414 s += strl;
2416 /* FIXME: really inefficient */
2417 if (es->style & ES_MULTILINE)
2418 EDIT_BuildLineDefs_ML(wnd, es);
2420 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2421 es->flags |= EF_MODIFIED;
2422 es->flags |= EF_UPDATE;
2423 EDIT_EM_ScrollCaret(wnd, es);
2425 /* FIXME: really inefficient */
2426 if (!(wnd->flags & WIN_NO_REDRAW))
2427 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2431 /*********************************************************************
2433 * EM_SCROLL
2436 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT32 action)
2438 INT32 dy;
2440 if (!(es->style & ES_MULTILINE))
2441 return (LRESULT)FALSE;
2443 dy = 0;
2445 switch (action) {
2446 case SB_LINEUP:
2447 if (es->y_offset)
2448 dy = -1;
2449 break;
2450 case SB_LINEDOWN:
2451 if (es->y_offset < es->line_count - 1)
2452 dy = 1;
2453 break;
2454 case SB_PAGEUP:
2455 if (es->y_offset)
2456 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2457 break;
2458 case SB_PAGEDOWN:
2459 if (es->y_offset < es->line_count - 1)
2460 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2461 break;
2462 default:
2463 return (LRESULT)FALSE;
2465 if (dy) {
2466 EDIT_EM_LineScroll(wnd, es, 0, dy);
2467 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2469 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2473 /*********************************************************************
2475 * EM_SETHANDLE
2477 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2480 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL32 hloc)
2482 if (!(es->style & ES_MULTILINE))
2483 return;
2485 if (!hloc) {
2486 fprintf(stderr, "edit: EM_SETHANDLE called with NULL handle\n");
2487 return;
2490 EDIT_UnlockBuffer(wnd, es, TRUE);
2492 * old buffer is freed by caller, unless
2493 * it is still in our own heap. (in that case
2494 * we free it, correcting the buggy caller.)
2496 if (es->text)
2497 HeapFree(es->heap, 0, es->text);
2499 es->hloc16 = (HLOCAL16)NULL;
2500 es->hloc32 = hloc;
2501 es->text = NULL;
2502 es->buffer_size = LocalSize32(es->hloc32) - 1;
2503 EDIT_LockBuffer(wnd, es);
2505 es->x_offset = es->y_offset = 0;
2506 es->selection_start = es->selection_end = 0;
2507 EDIT_EM_EmptyUndoBuffer(wnd, es);
2508 es->flags &= ~EF_MODIFIED;
2509 es->flags &= ~EF_UPDATE;
2510 EDIT_BuildLineDefs_ML(wnd, es);
2511 if (!(wnd->flags & WIN_NO_REDRAW))
2512 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2513 EDIT_EM_ScrollCaret(wnd, es);
2517 /*********************************************************************
2519 * EM_SETHANDLE16
2521 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2524 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2526 if (!(es->style & ES_MULTILINE))
2527 return;
2529 if (!hloc) {
2530 fprintf(stderr, "edit: EM_SETHANDLE called with NULL handle\n");
2531 return;
2534 EDIT_UnlockBuffer(wnd, es, TRUE);
2536 * old buffer is freed by caller, unless
2537 * it is still in our own heap. (in that case
2538 * we free it, correcting the buggy caller.)
2540 if (es->text)
2541 HeapFree(es->heap, 0, es->text);
2543 es->hloc16 = hloc;
2544 es->hloc32 = (HLOCAL32)NULL;
2545 es->text = NULL;
2546 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2547 EDIT_LockBuffer(wnd, es);
2549 es->x_offset = es->y_offset = 0;
2550 es->selection_start = es->selection_end = 0;
2551 EDIT_EM_EmptyUndoBuffer(wnd, es);
2552 es->flags &= ~EF_MODIFIED;
2553 es->flags &= ~EF_UPDATE;
2554 EDIT_BuildLineDefs_ML(wnd, es);
2555 if (!(wnd->flags & WIN_NO_REDRAW))
2556 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2557 EDIT_EM_ScrollCaret(wnd, es);
2561 /*********************************************************************
2563 * EM_SETLIMITTEXT
2565 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2566 * However, the windows version is not complied to yet in all of edit.c
2569 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT32 limit)
2571 if (es->style & ES_MULTILINE) {
2572 if (limit)
2573 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2574 else
2575 es->buffer_limit = BUFLIMIT_MULTI;
2576 } else {
2577 if (limit)
2578 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2579 else
2580 es->buffer_limit = BUFLIMIT_SINGLE;
2585 /*********************************************************************
2587 * EM_SETMARGINS
2590 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT32 action, INT32 left, INT32 right)
2592 if (action & EC_USEFONTINFO) {
2593 if (es->style & ES_MULTILINE) {
2595 * FIXME: do some GetABCCharWidth, or so
2596 * This is just preliminary
2598 es->left_margin = es->right_margin = es->char_width;
2599 } else
2600 es->left_margin = es->right_margin = es->char_width;
2601 return;
2602 } else {
2603 if (action & EC_LEFTMARGIN)
2604 es->left_margin = left;
2605 if (action & EC_RIGHTMARGIN)
2606 es->right_margin = right;
2611 /*********************************************************************
2613 * EM_SETPASSWORDCHAR
2616 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2618 if (es->style & ES_MULTILINE)
2619 return;
2621 if (es->password_char == c)
2622 return;
2624 es->password_char = c;
2625 if (c) {
2626 wnd->dwStyle |= ES_PASSWORD;
2627 es->style |= ES_PASSWORD;
2628 } else {
2629 wnd->dwStyle &= ~ES_PASSWORD;
2630 es->style &= ~ES_PASSWORD;
2632 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2636 /*********************************************************************
2638 * EDIT_EM_SetSel
2640 * note: unlike the specs say: the order of start and end
2641 * _is_ preserved in Windows. (i.e. start can be > end)
2642 * In other words: this handler is OK
2645 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT32 start, UINT32 end, BOOL32 after_wrap)
2647 UINT32 old_start = es->selection_start;
2648 UINT32 old_end = es->selection_end;
2649 UINT32 len = lstrlen32A(es->text);
2651 if (start == -1) {
2652 start = es->selection_end;
2653 end = es->selection_end;
2654 } else {
2655 start = MIN(start, len);
2656 end = MIN(end, len);
2658 es->selection_start = start;
2659 es->selection_end = end;
2660 if (after_wrap)
2661 es->flags |= EF_AFTER_WRAP;
2662 else
2663 es->flags &= ~EF_AFTER_WRAP;
2664 if (!(wnd->flags & WIN_NO_REDRAW)) {
2665 if (es->flags & EF_FOCUSED) {
2666 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, end, after_wrap);
2667 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
2669 /* FIXME: little efficiency, could be better */
2670 ORDER_UINT32(start, end);
2671 ORDER_UINT32(start, old_start);
2672 ORDER_UINT32(start, old_end);
2673 ORDER_UINT32(end, old_start);
2674 ORDER_UINT32(end, old_end);
2675 ORDER_UINT32(old_start, old_end);
2676 if (end != old_start) {
2677 EDIT_InvalidateText(wnd, es, start, end);
2678 EDIT_InvalidateText(wnd, es, old_start, old_end);
2679 } else
2680 EDIT_InvalidateText(wnd, es, start, old_end);
2685 /*********************************************************************
2687 * EM_SETTABSTOPS
2690 static BOOL32 EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT32 count, LPINT32 tabs)
2692 if (!(es->style & ES_MULTILINE))
2693 return FALSE;
2694 if (es->tabs)
2695 HeapFree(es->heap, 0, es->tabs);
2696 es->tabs_count = count;
2697 if (!count)
2698 es->tabs = NULL;
2699 else {
2700 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
2701 memcpy(es->tabs, tabs, count * sizeof(INT32));
2703 return TRUE;
2707 /*********************************************************************
2709 * EM_SETTABSTOPS16
2712 static BOOL32 EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT32 count, LPINT16 tabs)
2714 if (!(es->style & ES_MULTILINE))
2715 return FALSE;
2716 if (es->tabs)
2717 HeapFree(es->heap, 0, es->tabs);
2718 es->tabs_count = count;
2719 if (!count)
2720 es->tabs = NULL;
2721 else {
2722 INT32 i;
2723 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
2724 for (i = 0 ; i < count ; i++)
2725 es->tabs[i] = *tabs++;
2727 return TRUE;
2731 /*********************************************************************
2733 * EM_SETWORDBREAKPROC
2736 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC32A wbp)
2738 if (es->word_break_proc32A == wbp)
2739 return;
2741 es->word_break_proc32A = wbp;
2742 es->word_break_proc16 = NULL;
2743 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2744 EDIT_BuildLineDefs_ML(wnd, es);
2745 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2750 /*********************************************************************
2752 * EM_SETWORDBREAKPROC16
2755 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2757 if (es->word_break_proc16 == wbp)
2758 return;
2760 es->word_break_proc32A = NULL;
2761 es->word_break_proc16 = wbp;
2762 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2763 EDIT_BuildLineDefs_ML(wnd, es);
2764 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
2769 /*********************************************************************
2771 * EM_UNDO / WM_UNDO
2774 static BOOL32 EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2776 INT32 ulength = lstrlen32A(es->undo_text);
2777 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2779 lstrcpy32A(utext, es->undo_text);
2781 dprintf_edit(stddeb, "edit: before UNDO:insertion length = %d, deletion buffer = %s\n",
2782 es->undo_insert_count, utext);
2784 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2785 EDIT_EM_EmptyUndoBuffer(wnd, es);
2786 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext);
2787 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2788 HeapFree(es->heap, 0, utext);
2790 dprintf_edit(stddeb, "edit: after UNDO: insertion length = %d, deletion buffer = %s\n",
2791 es->undo_insert_count, es->undo_text);
2793 return TRUE;
2797 /*********************************************************************
2799 * WM_CHAR
2802 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
2804 switch (c) {
2805 case '\r':
2806 case '\n':
2807 if (es->style & ES_MULTILINE) {
2808 if (es->style & ES_READONLY) {
2809 EDIT_MoveHome(wnd, es, FALSE);
2810 EDIT_MoveDown_ML(wnd, es, FALSE);
2811 } else
2812 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n");
2814 break;
2815 case '\t':
2816 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2817 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t");
2818 break;
2819 default:
2820 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
2821 char str[2];
2822 str[0] = c;
2823 str[1] = '\0';
2824 EDIT_EM_ReplaceSel(wnd, es, TRUE, str);
2826 break;
2831 /*********************************************************************
2833 * WM_COMMAND
2836 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT32 code, INT32 id, HWND32 control)
2838 if (code || control)
2839 return;
2841 switch (id) {
2842 case EM_UNDO32:
2843 EDIT_EM_Undo(wnd, es);
2844 break;
2845 case WM_CUT:
2846 EDIT_WM_Cut(wnd, es);
2847 break;
2848 case WM_COPY:
2849 EDIT_WM_Copy(wnd, es);
2850 break;
2851 case WM_PASTE:
2852 EDIT_WM_Paste(wnd, es);
2853 break;
2854 case WM_CLEAR:
2855 EDIT_WM_Clear(wnd, es);
2856 break;
2857 case EM_SETSEL32:
2858 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
2859 EDIT_EM_ScrollCaret(wnd, es);
2860 break;
2861 default:
2862 dprintf_edit(stddeb, "edit: unknown menu item, please report\n");
2863 break;
2868 /*********************************************************************
2870 * WM_CONTEXTMENU
2872 * Note: the resource files resource/sysres_??.rc cannot define a
2873 * single popup menu. Hence we use a (dummy) menubar
2874 * containing the single popup menu as its first item.
2876 * FIXME: the message identifiers have been chosen arbitrarily,
2877 * hence we use MF_BYPOSITION.
2878 * We might as well use the "real" values (anybody knows ?)
2879 * The menu definition is in resources/sysres_??.rc.
2880 * Once these are OK, we better use MF_BYCOMMAND here
2881 * (as we do in EDIT_WM_Command()).
2884 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND32 hwnd, INT32 x, INT32 y)
2886 HMENU32 menu = LoadMenuIndirect32A(SYSRES_GetResPtr(SYSRES_MENU_EDITMENU));
2887 HMENU32 popup = GetSubMenu32(menu, 0);
2888 UINT32 start = es->selection_start;
2889 UINT32 end = es->selection_end;
2891 ORDER_UINT32(start, end);
2893 /* undo */
2894 EnableMenuItem32(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
2895 /* cut */
2896 EnableMenuItem32(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2897 /* copy */
2898 EnableMenuItem32(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2899 /* paste */
2900 EnableMenuItem32(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable32(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
2901 /* delete */
2902 EnableMenuItem32(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
2903 /* select all */
2904 EnableMenuItem32(popup, 7, MF_BYPOSITION | (start || (end != lstrlen32A(es->text)) ? MF_ENABLED : MF_GRAYED));
2906 TrackPopupMenu32(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
2907 DestroyMenu32(menu);
2911 /*********************************************************************
2913 * WM_COPY
2915 * FIXME: replace with 32 bit calls as soon as they are implemented
2916 * in the clipboard code
2919 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
2921 INT32 s = es->selection_start;
2922 INT32 e = es->selection_end;
2923 HGLOBAL16 hdst;
2924 LPSTR dst;
2926 if (e == s)
2927 return;
2928 ORDER_INT32(s, e);
2929 hdst = GlobalAlloc16(GMEM_MOVEABLE, (DWORD)(e - s + 1));
2930 dst = GlobalLock16(hdst);
2931 lstrcpyn32A(dst, es->text + s, e - s + 1);
2932 GlobalUnlock16(hdst);
2933 OpenClipboard32(wnd->hwndSelf);
2934 EmptyClipboard32();
2935 SetClipboardData16(CF_TEXT, hdst);
2936 CloseClipboard32();
2940 /*********************************************************************
2942 * WM_CREATE
2945 static LRESULT EDIT_WM_Create(WND *wnd, LPCREATESTRUCT32A cs)
2947 EDITSTATE *es;
2949 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
2950 return -1;
2951 *(EDITSTATE **)wnd->wExtra = es;
2952 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
2953 return -1;
2954 es->style = cs->style;
2956 /* remove the WS_CAPTION style if it has been set - this is really a */
2957 /* pseudo option made from a combination of WS_BORDER and WS_DLGFRAME */
2958 if ((es->style & WS_BORDER) && (es->style & WS_DLGFRAME))
2959 es->style ^= WS_DLGFRAME;
2961 if (es->style & ES_MULTILINE) {
2962 es->buffer_size = BUFSTART_MULTI;
2963 es->buffer_limit = BUFLIMIT_MULTI;
2964 if (es->style & WS_VSCROLL)
2965 es->style |= ES_AUTOVSCROLL;
2966 if (es->style & WS_HSCROLL)
2967 es->style |= ES_AUTOHSCROLL;
2968 es->style &= ~ES_PASSWORD;
2969 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
2970 if (es->style & ES_RIGHT)
2971 es->style &= ~ES_CENTER;
2972 es->style &= ~WS_HSCROLL;
2973 es->style &= ~ES_AUTOHSCROLL;
2976 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
2977 es->style |= ES_AUTOVSCROLL;
2978 } else {
2979 es->buffer_size = BUFSTART_SINGLE;
2980 es->buffer_limit = BUFLIMIT_SINGLE;
2981 es->style &= ~ES_CENTER;
2982 es->style &= ~ES_RIGHT;
2983 es->style &= ~WS_HSCROLL;
2984 es->style &= ~WS_VSCROLL;
2985 es->style &= ~ES_AUTOVSCROLL;
2986 es->style &= ~ES_WANTRETURN;
2987 if (es->style & ES_UPPERCASE) {
2988 es->style &= ~ES_LOWERCASE;
2989 es->style &= ~ES_NUMBER;
2990 } else if (es->style & ES_LOWERCASE)
2991 es->style &= ~ES_NUMBER;
2992 if (es->style & ES_PASSWORD)
2993 es->password_char = '*';
2995 /* FIXME: for now, all single line controls are AUTOHSCROLL */
2996 es->style |= ES_AUTOHSCROLL;
2998 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
2999 return -1;
3000 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3001 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3002 return -1;
3003 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3004 *es->text = '\0';
3005 if (es->style & ES_MULTILINE)
3006 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3007 return -1;
3008 es->line_count = 1;
3010 * To initialize some final structure members, we call some helper
3011 * functions. However, since the EDITSTATE is not consistent (i.e.
3012 * not fully initialized), we should be very careful which
3013 * functions can be called, and in what order.
3015 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3016 if (cs->lpszName && *(cs->lpszName) != '\0') {
3017 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName);
3018 /* 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 */
3019 es->selection_start = es->selection_end = 0;
3020 EDIT_EM_ScrollCaret(wnd, es);
3022 return 0;
3026 /*********************************************************************
3028 * WM_DESTROY
3031 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3033 if (es->hloc32) {
3034 while (LocalUnlock32(es->hloc32)) ;
3035 LocalFree32(es->hloc32);
3037 if (es->hloc16) {
3038 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3039 LOCAL_Free(wnd->hInstance, es->hloc16);
3041 HeapDestroy(es->heap);
3042 HeapFree(GetProcessHeap(), 0, es);
3043 *(EDITSTATE **)wnd->wExtra = NULL;
3047 /*********************************************************************
3049 * WM_ERASEBKGND
3052 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC32 dc)
3054 HBRUSH32 brush;
3055 RECT32 rc;
3057 if (!(brush = (HBRUSH32)EDIT_SEND_CTLCOLOR(wnd, dc)))
3058 brush = (HBRUSH32)GetStockObject32(WHITE_BRUSH);
3060 GetClientRect32(wnd->hwndSelf, &rc);
3061 IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
3062 GetClipBox32(dc, &rc);
3064 * FIXME: specs say that we should UnrealizeObject() the brush,
3065 * but the specs of UnrealizeObject() say that we shouldn't
3066 * unrealize a stock object. The default brush that
3067 * DefWndProc() returns is ... a stock object.
3069 FillRect32(dc, &rc, brush);
3070 return -1;
3074 /*********************************************************************
3076 * WM_GETTEXT
3079 static INT32 EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT32 count, LPSTR text)
3081 INT32 len = lstrlen32A(es->text);
3083 if (count > len) {
3084 lstrcpy32A(text, es->text);
3085 return len;
3086 } else
3087 return -1;
3091 /*********************************************************************
3093 * EDIT_HScroll_Hack
3095 * 16 bit notepad needs this. Actually it is not _our_ hack,
3096 * it is notepad's. Notepad is sending us scrollbar messages with
3097 * undocumented parameters without us even having a scrollbar ... !?!?
3100 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3102 INT32 dx = 0;
3103 INT32 fw = es->format_rect.right - es->format_rect.left;
3104 LRESULT ret = 0;
3106 if (!(es->flags & EF_HSCROLL_HACK)) {
3107 fprintf(stderr, "edit: hacked WM_HSCROLL handler invoked\n");
3108 fprintf(stderr, " if you are _not_ running 16 bit notepad, please report\n");
3109 fprintf(stderr, " (this message is only displayed once per edit control)\n");
3110 es->flags |= EF_HSCROLL_HACK;
3113 switch (action) {
3114 case SB_LINELEFT:
3115 if (es->x_offset)
3116 dx = -es->char_width;
3117 break;
3118 case SB_LINERIGHT:
3119 if (es->x_offset < es->text_width)
3120 dx = es->char_width;
3121 break;
3122 case SB_PAGELEFT:
3123 if (es->x_offset)
3124 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3125 break;
3126 case SB_PAGERIGHT:
3127 if (es->x_offset < es->text_width)
3128 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3129 break;
3130 case SB_LEFT:
3131 if (es->x_offset)
3132 dx = -es->x_offset;
3133 break;
3134 case SB_RIGHT:
3135 if (es->x_offset < es->text_width)
3136 dx = es->text_width - es->x_offset;
3137 break;
3138 case SB_THUMBTRACK:
3139 es->flags |= EF_HSCROLL_TRACK;
3140 dx = pos * es->text_width / 100 - es->x_offset;
3141 break;
3142 case SB_THUMBPOSITION:
3143 es->flags &= ~EF_HSCROLL_TRACK;
3144 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3145 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3146 break;
3147 case SB_ENDSCROLL:
3148 break;
3151 * FIXME : the next two are undocumented !
3152 * Are we doing the right thing ?
3153 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3154 * although it's also a regular control message.
3156 case EM_GETTHUMB16:
3157 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3158 break;
3159 case EM_LINESCROLL16:
3160 dx = pos;
3161 break;
3163 default:
3164 dprintf_edit(stddeb, "edit: undocumented (hacked) WM_HSCROLL parameter, please report\n");
3165 return 0;
3167 if (dx)
3168 EDIT_EM_LineScroll(wnd, es, dx, 0);
3169 return ret;
3173 /*********************************************************************
3175 * WM_HSCROLL
3178 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3180 INT32 dx;
3181 INT32 fw;
3183 if (!(es->style & ES_MULTILINE))
3184 return 0;
3186 if (!(es->style & ES_AUTOHSCROLL))
3187 return 0;
3189 if (!(es->style & WS_HSCROLL))
3190 return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
3192 dx = 0;
3193 fw = es->format_rect.right - es->format_rect.left;
3194 switch (action) {
3195 case SB_LINELEFT:
3196 if (es->x_offset)
3197 dx = -es->char_width;
3198 break;
3199 case SB_LINERIGHT:
3200 if (es->x_offset < es->text_width)
3201 dx = es->char_width;
3202 break;
3203 case SB_PAGELEFT:
3204 if (es->x_offset)
3205 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3206 break;
3207 case SB_PAGERIGHT:
3208 if (es->x_offset < es->text_width)
3209 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3210 break;
3211 case SB_LEFT:
3212 if (es->x_offset)
3213 dx = -es->x_offset;
3214 break;
3215 case SB_RIGHT:
3216 if (es->x_offset < es->text_width)
3217 dx = es->text_width - es->x_offset;
3218 break;
3219 case SB_THUMBTRACK:
3220 es->flags |= EF_HSCROLL_TRACK;
3221 dx = pos - es->x_offset;
3222 break;
3223 case SB_THUMBPOSITION:
3224 es->flags &= ~EF_HSCROLL_TRACK;
3225 if (!(dx = pos - es->x_offset)) {
3226 SetScrollPos32(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3227 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3229 break;
3230 case SB_ENDSCROLL:
3231 break;
3233 default:
3234 fprintf(stderr, "edit: undocumented WM_HSCROLL parameter, please report\n");
3235 return 0;
3237 if (dx)
3238 EDIT_EM_LineScroll(wnd, es, dx, 0);
3239 return 0;
3243 /*********************************************************************
3245 * EDIT_CheckCombo
3248 static BOOL32 EDIT_CheckCombo(WND *wnd, UINT32 msg, INT32 key, DWORD key_data)
3250 HWND32 hLBox;
3252 if (WIDGETS_IsControl32(wnd->parent, BIC32_COMBO) &&
3253 (hLBox = COMBO_GetLBWindow(wnd->parent))) {
3254 HWND32 hCombo = wnd->parent->hwndSelf;
3255 BOOL32 bUIFlip = TRUE;
3257 dprintf_combo(stddeb, "EDIT_CheckCombo [%04x]: handling msg %04x (%04x)\n",
3258 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3260 switch (msg) {
3261 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3262 if (key != VK_F4) {
3263 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
3264 if (SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0))
3265 bUIFlip = FALSE;
3267 if (!bUIFlip)
3268 SendMessage32A(hLBox, WM_KEYDOWN, (WPARAM32)key, 0);
3269 else {
3270 /* make sure ComboLBox pops up */
3271 SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 0, 0);
3272 SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
3273 SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 1, 0);
3275 break;
3276 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3277 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
3278 if (bUIFlip) {
3279 bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0);
3280 SendMessage32A(hCombo, CB_SHOWDROPDOWN32, (bUIFlip) ? FALSE : TRUE, 0);
3281 } else
3282 SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
3283 break;
3285 return TRUE;
3287 return FALSE;
3291 /*********************************************************************
3293 * WM_KEYDOWN
3295 * Handling of special keys that don't produce a WM_CHAR
3296 * (i.e. non-printable keys) & Backspace & Delete
3299 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
3301 BOOL32 shift;
3302 BOOL32 control;
3304 if (GetKeyState32(VK_MENU) & 0x8000)
3305 return 0;
3307 shift = GetKeyState32(VK_SHIFT) & 0x8000;
3308 control = GetKeyState32(VK_CONTROL) & 0x8000;
3310 switch (key) {
3311 case VK_F4:
3312 case VK_UP:
3313 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3314 break;
3315 if (key == VK_F4)
3316 break;
3317 /* fall through */
3318 case VK_LEFT:
3319 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3320 EDIT_MoveUp_ML(wnd, es, shift);
3321 else
3322 if (control)
3323 EDIT_MoveWordBackward(wnd, es, shift);
3324 else
3325 EDIT_MoveBackward(wnd, es, shift);
3326 break;
3327 case VK_DOWN:
3328 if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
3329 break;
3330 /* fall through */
3331 case VK_RIGHT:
3332 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3333 EDIT_MoveDown_ML(wnd, es, shift);
3334 else if (control)
3335 EDIT_MoveWordForward(wnd, es, shift);
3336 else
3337 EDIT_MoveForward(wnd, es, shift);
3338 break;
3339 case VK_HOME:
3340 EDIT_MoveHome(wnd, es, shift);
3341 break;
3342 case VK_END:
3343 EDIT_MoveEnd(wnd, es, shift);
3344 break;
3345 case VK_PRIOR:
3346 if (es->style & ES_MULTILINE)
3347 EDIT_MovePageUp_ML(wnd, es, shift);
3348 break;
3349 case VK_NEXT:
3350 if (es->style & ES_MULTILINE)
3351 EDIT_MovePageDown_ML(wnd, es, shift);
3352 break;
3353 case VK_BACK:
3354 if (!(es->style & ES_READONLY) && !control)
3355 if (es->selection_start != es->selection_end)
3356 EDIT_WM_Clear(wnd, es);
3357 else {
3358 /* delete character left of caret */
3359 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3360 EDIT_MoveBackward(wnd, es, TRUE);
3361 EDIT_WM_Clear(wnd, es);
3363 break;
3364 case VK_DELETE:
3365 if (!(es->style & ES_READONLY) && !(shift && control))
3366 if (es->selection_start != es->selection_end) {
3367 if (shift)
3368 EDIT_WM_Cut(wnd, es);
3369 else
3370 EDIT_WM_Clear(wnd, es);
3371 } else {
3372 if (shift) {
3373 /* delete character left of caret */
3374 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3375 EDIT_MoveBackward(wnd, es, TRUE);
3376 EDIT_WM_Clear(wnd, es);
3377 } else if (control) {
3378 /* delete to end of line */
3379 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3380 EDIT_MoveEnd(wnd, es, TRUE);
3381 EDIT_WM_Clear(wnd, es);
3382 } else {
3383 /* delete character right of caret */
3384 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3385 EDIT_MoveForward(wnd, es, TRUE);
3386 EDIT_WM_Clear(wnd, es);
3389 break;
3390 case VK_INSERT:
3391 if (shift) {
3392 if (!(es->style & ES_READONLY))
3393 EDIT_WM_Paste(wnd, es);
3394 } else if (control)
3395 EDIT_WM_Copy(wnd, es);
3396 break;
3398 return 0;
3402 /*********************************************************************
3404 * WM_KILLFOCUS
3407 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND32 window_getting_focus)
3409 es->flags &= ~EF_FOCUSED;
3410 DestroyCaret32();
3411 if(!(es->style & ES_NOHIDESEL))
3412 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3413 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3414 return 0;
3418 /*********************************************************************
3420 * WM_LBUTTONDBLCLK
3422 * The caret position has been set on the WM_LBUTTONDOWN message
3425 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3427 INT32 s;
3428 INT32 e = es->selection_end;
3429 INT32 l;
3430 INT32 li;
3431 INT32 ll;
3433 if (!(es->flags & EF_FOCUSED))
3434 return 0;
3436 l = EDIT_EM_LineFromChar(wnd, es, e);
3437 li = EDIT_EM_LineIndex(wnd, es, l);
3438 ll = EDIT_EM_LineLength(wnd, es, e);
3439 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3440 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3441 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3442 EDIT_EM_ScrollCaret(wnd, es);
3443 return 0;
3447 /*********************************************************************
3449 * WM_LBUTTONDOWN
3452 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3454 INT32 e;
3455 BOOL32 after_wrap;
3457 if (!(es->flags & EF_FOCUSED))
3458 return 0;
3460 SetCapture32(wnd->hwndSelf);
3461 EDIT_ConfinePoint(wnd, es, &x, &y);
3462 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3463 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3464 EDIT_EM_ScrollCaret(wnd, es);
3465 SetTimer32(wnd->hwndSelf, 0, 100, NULL);
3466 return 0;
3470 /*********************************************************************
3472 * WM_LBUTTONUP
3475 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3477 if (GetCapture32() == wnd->hwndSelf) {
3478 KillTimer32(wnd->hwndSelf, 0);
3479 ReleaseCapture();
3481 return 0;
3485 /*********************************************************************
3487 * WM_MOUSEMOVE
3490 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
3492 INT32 e;
3493 BOOL32 after_wrap;
3495 if (GetCapture32() != wnd->hwndSelf)
3496 return 0;
3499 * FIXME: gotta do some scrolling if outside client
3500 * area. Maybe reset the timer ?
3502 EDIT_ConfinePoint(wnd, es, &x, &y);
3503 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3504 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3505 return 0;
3509 /*********************************************************************
3511 * WM_PAINT
3514 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es)
3516 PAINTSTRUCT32 ps;
3517 INT32 i;
3518 HDC32 dc;
3519 HFONT32 old_font = 0;
3520 RECT32 rc;
3521 RECT32 rcLine;
3522 RECT32 rcRgn;
3523 LRESULT pos;
3524 BOOL32 rev = IsWindowEnabled32(wnd->hwndSelf) &&
3525 ((es->flags & EF_FOCUSED) ||
3526 (es->style & ES_NOHIDESEL));
3528 if (es->flags & EF_UPDATE)
3529 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
3531 dc = BeginPaint32(wnd->hwndSelf, &ps);
3532 IntersectClipRect32(dc, es->format_rect.left,
3533 es->format_rect.top,
3534 es->format_rect.right,
3535 es->format_rect.bottom);
3536 if (es->style & ES_MULTILINE) {
3537 GetClientRect32(wnd->hwndSelf, &rc);
3538 IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
3540 if (es->font)
3541 old_font = SelectObject32(dc, es->font);
3542 EDIT_SEND_CTLCOLOR(wnd, dc);
3543 if (!IsWindowEnabled32(wnd->hwndSelf))
3544 SetTextColor32(dc, GetSysColor32(COLOR_GRAYTEXT));
3545 GetClipBox32(dc, &rcRgn);
3546 if (es->style & ES_MULTILINE) {
3547 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3548 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3549 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3550 if (IntersectRect32(&rc, &rcRgn, &rcLine))
3551 EDIT_PaintLine(wnd, es, dc, i, rev);
3553 } else {
3554 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3555 if (IntersectRect32(&rc, &rcRgn, &rcLine))
3556 EDIT_PaintLine(wnd, es, dc, 0, rev);
3558 if (es->font)
3559 SelectObject32(dc, old_font);
3560 if (es->flags & EF_FOCUSED) {
3561 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3562 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3564 EndPaint32(wnd->hwndSelf, &ps);
3565 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3566 INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3567 SCROLLINFO si;
3568 si.cbSize = sizeof(SCROLLINFO);
3569 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3570 si.nMin = 0;
3571 si.nMax = es->line_count + vlc - 2;
3572 si.nPage = vlc;
3573 si.nPos = es->y_offset;
3574 SetScrollInfo32(wnd->hwndSelf, SB_VERT, &si, TRUE);
3576 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3577 SCROLLINFO si;
3578 INT32 fw = es->format_rect.right - es->format_rect.left;
3579 si.cbSize = sizeof(SCROLLINFO);
3580 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3581 si.nMin = 0;
3582 si.nMax = es->text_width + fw - 1;
3583 si.nPage = fw;
3584 si.nPos = es->x_offset;
3585 SetScrollInfo32(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3588 if (es->flags & EF_UPDATE) {
3589 es->flags &= ~EF_UPDATE;
3590 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3595 /*********************************************************************
3597 * WM_PASTE
3599 * FIXME: replace with 32 bit handler once GetClipboardData32() is
3600 * implemented in misc/clipboard.c
3603 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3605 HGLOBAL16 hsrc;
3606 LPSTR src;
3608 OpenClipboard32(wnd->hwndSelf);
3609 if ((hsrc = GetClipboardData16(CF_TEXT))) {
3610 src = (LPSTR)GlobalLock16(hsrc);
3611 EDIT_EM_ReplaceSel(wnd, es, TRUE, src);
3612 GlobalUnlock16(hsrc);
3614 CloseClipboard32();
3618 /*********************************************************************
3620 * WM_SETFOCUS
3623 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND32 window_losing_focus)
3625 LRESULT pos;
3627 es->flags |= EF_FOCUSED;
3628 CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
3629 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3630 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3631 if(!(es->style & ES_NOHIDESEL))
3632 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3633 ShowCaret32(wnd->hwndSelf);
3634 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3638 /*********************************************************************
3640 * WM_SETFONT
3643 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT32 font, BOOL32 redraw)
3645 TEXTMETRIC32A tm;
3646 HDC32 dc;
3647 HFONT32 old_font = 0;
3649 es->font = font;
3650 dc = GetDC32(wnd->hwndSelf);
3651 if (font)
3652 old_font = SelectObject32(dc, font);
3653 GetTextMetrics32A(dc, &tm);
3654 es->line_height = tm.tmHeight;
3655 es->char_width = tm.tmAveCharWidth;
3656 if (font)
3657 SelectObject32(dc, old_font);
3658 ReleaseDC32(wnd->hwndSelf, dc);
3659 if (wnd->flags & WIN_ISWIN32)
3660 EDIT_EM_SetMargins(wnd, es, EC_USEFONTINFO, 0, 0);
3661 if (es->style & ES_MULTILINE)
3662 EDIT_BuildLineDefs_ML(wnd, es);
3663 if (redraw && !(wnd->flags & WIN_NO_REDRAW))
3664 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
3665 if (es->flags & EF_FOCUSED) {
3666 LRESULT pos;
3667 DestroyCaret32();
3668 CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
3669 pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3670 SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
3671 ShowCaret32(wnd->hwndSelf);
3676 /*********************************************************************
3678 * WM_SETTEXT
3681 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
3683 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3684 if (text) {
3685 dprintf_edit(stddeb, "\t'%s'\n", text);
3686 EDIT_EM_ReplaceSel(wnd, es, FALSE, text);
3688 es->flags |= EF_MODIFIED;
3689 es->flags |= EF_UPDATE;
3690 EDIT_EM_ScrollCaret(wnd, es);
3694 /*********************************************************************
3696 * WM_SIZE
3699 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT32 action, INT32 width, INT32 height)
3701 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3702 RECT32 rc;
3703 SetRect32(&rc, 0, 0, width, height);
3704 EDIT_SetRectNP(wnd, es, &rc);
3705 InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
3710 /*********************************************************************
3712 * WM_SYSKEYDOWN
3715 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
3717 if ((key == VK_BACK) && (key_data & 0x2000)) {
3718 if (EDIT_EM_CanUndo(wnd, es))
3719 EDIT_EM_Undo(wnd, es);
3720 return 0;
3721 } else if (key == VK_UP || key == VK_DOWN)
3722 if (EDIT_CheckCombo(wnd, WM_SYSKEYDOWN, key, key_data))
3723 return 0;
3724 return DefWindowProc32A(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM32)key, (LPARAM)key_data);
3728 /*********************************************************************
3730 * WM_TIMER
3733 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT32 id, TIMERPROC32 timer_proc)
3736 * FIXME: gotta do some scrolling here, like
3737 * EDIT_EM_LineScroll(wnd, 0, 1);
3742 /*********************************************************************
3744 * EDIT_VScroll_Hack
3746 * 16 bit notepad needs this. Actually it is not _our_ hack,
3747 * it is notepad's. Notepad is sending us scrollbar messages with
3748 * undocumented parameters without us even having a scrollbar ... !?!?
3751 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3753 INT32 dy = 0;
3754 LRESULT ret = 0;
3756 if (!(es->flags & EF_VSCROLL_HACK)) {
3757 fprintf(stderr, "edit: hacked WM_VSCROLL handler invoked\n");
3758 fprintf(stderr, " if you are _not_ running 16 bit notepad, please report\n");
3759 fprintf(stderr, " (this message is only displayed once per edit control)\n");
3760 es->flags |= EF_VSCROLL_HACK;
3763 switch (action) {
3764 case SB_LINEUP:
3765 case SB_LINEDOWN:
3766 case SB_PAGEUP:
3767 case SB_PAGEDOWN:
3768 EDIT_EM_Scroll(wnd, es, action);
3769 return 0;
3770 case SB_TOP:
3771 dy = -es->y_offset;
3772 break;
3773 case SB_BOTTOM:
3774 dy = es->line_count - 1 - es->y_offset;
3775 break;
3776 case SB_THUMBTRACK:
3777 es->flags |= EF_VSCROLL_TRACK;
3778 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
3779 break;
3780 case SB_THUMBPOSITION:
3781 es->flags &= ~EF_VSCROLL_TRACK;
3782 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
3783 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
3784 break;
3785 case SB_ENDSCROLL:
3786 break;
3789 * FIXME : the next two are undocumented !
3790 * Are we doing the right thing ?
3791 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3792 * although it's also a regular control message.
3794 case EM_GETTHUMB16:
3795 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
3796 break;
3797 case EM_LINESCROLL16:
3798 dy = pos;
3799 break;
3801 default:
3802 fprintf(stderr, "edit: undocumented (hacked) WM_VSCROLL parameter, please report\n");
3803 return 0;
3805 if (dy)
3806 EDIT_EM_LineScroll(wnd, es, 0, dy);
3807 return ret;
3811 /*********************************************************************
3813 * WM_VSCROLL
3816 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
3818 INT32 dy;
3820 if (!(es->style & ES_MULTILINE))
3821 return 0;
3823 if (!(es->style & ES_AUTOVSCROLL))
3824 return 0;
3826 if (!(es->style & WS_VSCROLL))
3827 return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
3829 dy = 0;
3830 switch (action) {
3831 case SB_LINEUP:
3832 case SB_LINEDOWN:
3833 case SB_PAGEUP:
3834 case SB_PAGEDOWN:
3835 EDIT_EM_Scroll(wnd, es, action);
3836 return 0;
3838 case SB_TOP:
3839 dy = -es->y_offset;
3840 break;
3841 case SB_BOTTOM:
3842 dy = es->line_count - 1 - es->y_offset;
3843 break;
3844 case SB_THUMBTRACK:
3845 es->flags |= EF_VSCROLL_TRACK;
3846 dy = pos - es->y_offset;
3847 break;
3848 case SB_THUMBPOSITION:
3849 es->flags &= ~EF_VSCROLL_TRACK;
3850 if (!(dy = pos - es->y_offset)) {
3851 SetScrollPos32(wnd->hwndSelf, SB_VERT, pos, TRUE);
3852 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
3854 break;
3855 case SB_ENDSCROLL:
3856 break;
3858 default:
3859 fprintf(stderr, "edit: undocumented WM_VSCROLL parameter, please report\n");
3860 return 0;
3862 if (dy)
3863 EDIT_EM_LineScroll(wnd, es, 0, dy);
3864 return 0;