Send HDN_ITEMCHANGING and HDN_ITEMCHANGED when resizing.
[wine/multimedia.git] / controls / edit.c
blob7a9664b91ecc9c93c2d0d06b60a68da27bc0fb8a
1 /*
2 * Edit control
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
8 */
11 * please read EDIT.TODO (and update it when you change things)
14 #include "config.h"
16 #include <string.h>
17 #include <stdlib.h>
19 #include "winbase.h"
20 #include "winnt.h"
21 #include "win.h"
22 #include "wine/winbase16.h"
23 #include "wine/winuser16.h"
24 #include "combo.h"
25 #include "local.h"
26 #include "selectors.h"
27 #include "debugtools.h"
28 #include "tweak.h"
30 DEFAULT_DEBUG_CHANNEL(edit);
31 DECLARE_DEBUG_CHANNEL(combo);
32 DECLARE_DEBUG_CHANNEL(relay);
34 #define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
35 FIXME: BTW, new specs say 65535 (do you dare ???) */
36 #define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
37 #define BUFSTART_MULTI 1024 /* starting size */
38 #define BUFSTART_SINGLE 256 /* starting size */
39 #define GROWLENGTH 64 /* buffers grow by this much */
40 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
43 * extra flags for EDITSTATE.flags field
45 #define EF_MODIFIED 0x0001 /* text has been modified */
46 #define EF_FOCUSED 0x0002 /* we have input focus */
47 #define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
48 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
49 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
50 #define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
51 #define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
52 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
53 wrapped line, instead of in front of the next character */
54 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
56 typedef enum
58 END_0 = 0, /* line ends with terminating '\0' character */
59 END_WRAP, /* line is wrapped */
60 END_HARD, /* line ends with a hard return '\r\n' */
61 END_SOFT /* line ends with a soft return '\r\r\n' */
62 } LINE_END;
64 typedef struct tagLINEDEF {
65 INT length; /* bruto length of a line in bytes */
66 INT net_length; /* netto length of a line in visible characters */
67 LINE_END ending;
68 INT width; /* width of the line in pixels */
69 struct tagLINEDEF *next;
70 } LINEDEF;
72 typedef struct
74 HANDLE heap; /* our own heap */
75 LPSTR text; /* the actual contents of the control */
76 INT buffer_size; /* the size of the buffer */
77 INT buffer_limit; /* the maximum size to which the buffer may grow */
78 HFONT font; /* NULL means standard system font */
79 INT x_offset; /* scroll offset for multi lines this is in pixels
80 for single lines it's in characters */
81 INT line_height; /* height of a screen line in pixels */
82 INT char_width; /* average character width in pixels */
83 DWORD style; /* sane version of wnd->dwStyle */
84 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
85 INT undo_insert_count; /* number of characters inserted in sequence */
86 INT undo_position; /* character index of the insertion and deletion */
87 LPSTR undo_text; /* deleted text */
88 INT undo_buffer_size; /* size of the deleted text buffer */
89 INT selection_start; /* == selection_end if no selection */
90 INT selection_end; /* == current caret position */
91 CHAR password_char; /* == 0 if no password char, and for multi line controls */
92 INT left_margin; /* in pixels */
93 INT right_margin; /* in pixels */
94 RECT format_rect;
95 INT region_posx; /* Position of cursor relative to region: */
96 INT region_posy; /* -1: to left, 0: within, 1: to right */
97 EDITWORDBREAKPROC16 word_break_proc16;
98 EDITWORDBREAKPROCA word_break_proc32A;
99 INT line_count; /* number of lines */
100 INT y_offset; /* scroll offset in number of lines */
101 BOOL bCaptureState; /* flag indicating whether mouse was captured */
102 BOOL bEnableState; /* flag keeping the enable state */
103 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
105 * only for multi line controls
107 INT lock_count; /* amount of re-entries in the EditWndProc */
108 INT tabs_count;
109 LPINT tabs;
110 INT text_width; /* width of the widest line in pixels */
111 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
112 HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
113 HLOCAL hloc32; /* for controls receiving EM_GETHANDLE */
114 } EDITSTATE;
117 #define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
118 #define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
120 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
121 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
123 #define DPRINTF_EDIT_NOTIFY(hwnd, str) \
124 do {TRACE("notification " str " sent to hwnd=%08x\n", \
125 (UINT)(hwnd));} while(0)
127 /* used for disabled or read-only edit control */
128 #define EDIT_SEND_CTLCOLORSTATIC(wnd,hdc) \
129 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLORSTATIC, \
130 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
131 #define EDIT_SEND_CTLCOLOR(wnd,hdc) \
132 (SendMessageA((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
133 (WPARAM)(hdc), (LPARAM)(wnd)->hwndSelf))
134 #define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
135 do {DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str); \
136 SendMessageA((wnd)->parent->hwndSelf, WM_COMMAND, \
137 MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
138 (LPARAM)(wnd)->hwndSelf);} while(0)
139 #define DPRINTF_EDIT_MSG16(str) \
140 TRACE(\
141 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
142 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
143 #define DPRINTF_EDIT_MSG32(str) \
144 TRACE(\
145 "32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
146 (UINT)hwnd, (UINT)wParam, (UINT)lParam)
148 /*********************************************************************
150 * Declarations
155 * These functions have trivial implementations
156 * We still like to call them internally
157 * "static inline" makes them more like macro's
159 static inline BOOL EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es);
160 static inline void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es);
161 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
162 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
165 * Helper functions only valid for one type of control
167 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
168 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es);
169 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
170 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend);
171 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
172 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend);
174 * Helper functions valid for both single line _and_ multi line controls
176 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
177 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
178 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT x, LPINT y);
179 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
180 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end);
181 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
182 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT size);
183 static BOOL EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT size);
184 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend);
185 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend);
186 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend);
187 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend);
188 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend);
189 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend);
190 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
191 static INT EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
192 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos, BOOL after_wrap);
193 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT lprc);
194 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force);
195 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
197 * EM_XXX message handlers
199 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y);
200 static BOOL EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL add_eol);
201 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es);
202 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
203 static INT EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT line, LPSTR lpch);
204 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT start, LPUINT end);
205 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
206 static INT EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT index);
207 static INT EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT line);
208 static INT EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT index);
209 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy);
210 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap);
211 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update);
212 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action);
213 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
214 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc);
215 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
216 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT limit);
217 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT action, INT left, INT right);
218 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c);
219 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
220 static BOOL EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT count, LPINT tabs);
221 static BOOL EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT count, LPINT16 tabs);
222 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
223 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
224 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
226 * WM_XXX message handlers
228 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data);
229 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
230 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND hwnd, INT x, INT y);
231 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
232 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs);
233 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
234 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc);
235 static INT EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT count, LPSTR text);
236 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
237 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
238 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND window_getting_focus);
239 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
240 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
241 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
242 static LRESULT EDIT_WM_MButtonDown(WND *wnd);
243 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y);
244 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs);
245 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam);
246 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
247 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus);
248 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw);
249 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
250 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height);
251 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data);
252 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
253 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
254 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase);
257 /*********************************************************************
259 * EM_CANUNDO
262 static inline BOOL EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es)
264 return (es->undo_insert_count || strlen(es->undo_text));
268 /*********************************************************************
270 * EM_EMPTYUNDOBUFFER
273 static inline void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es)
275 es->undo_insert_count = 0;
276 *es->undo_text = '\0';
280 /*********************************************************************
282 * WM_CLEAR
285 static inline void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
287 EDIT_EM_ReplaceSel(wnd, es, TRUE, "", TRUE);
289 if (es->flags & EF_UPDATE) {
290 es->flags &= ~EF_UPDATE;
291 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
296 /*********************************************************************
298 * WM_CUT
301 static inline void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
303 EDIT_WM_Copy(wnd, es);
304 EDIT_WM_Clear(wnd, es);
308 /**********************************************************************
309 * get_app_version
311 * Returns the window version in case Wine emulates a later version
312 * of windows then the application expects.
314 * In a number of cases when windows runs an application that was
315 * designed for an earlier windows version, windows reverts
316 * to "old" behaviour of that earlier version.
318 * An example is a disabled edit control that needs to be painted.
319 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
320 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
321 * applications with an expected version 0f 4.0 or higher.
324 static DWORD get_app_version(void)
326 static DWORD version;
327 if (!version)
329 DWORD dwEmulatedVersion;
330 OSVERSIONINFOA info;
331 DWORD dwProcVersion = GetProcessVersion(0);
333 GetVersionExA( &info );
334 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
335 /* fixme: this may not be 100% correct; see discussion on the
336 * wine developer list in Nov 1999 */
337 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
339 return version;
343 /*********************************************************************
345 * EditWndProc()
347 * The messages are in the order of the actual integer values
348 * (which can be found in include/windows.h)
349 * Whereever possible the 16 bit versions are converted to
350 * the 32 bit ones, so that we can 'fall through' to the
351 * helper functions. These are mostly 32 bit (with a few
352 * exceptions, clearly indicated by a '16' extension to their
353 * names).
356 LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
357 WPARAM wParam, LPARAM lParam )
359 WND *wnd = WIN_FindWndPtr(hwnd);
360 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
361 LRESULT result = 0;
363 switch (msg) {
364 case WM_DESTROY:
365 DPRINTF_EDIT_MSG32("WM_DESTROY");
366 EDIT_WM_Destroy(wnd, es);
367 result = 0;
368 goto END;
370 case WM_NCCREATE:
371 DPRINTF_EDIT_MSG32("WM_NCCREATE");
372 result = EDIT_WM_NCCreate(wnd, (LPCREATESTRUCTA)lParam);
373 goto END;
376 if (!es)
378 result = DefWindowProcA(hwnd, msg, wParam, lParam);
379 goto END;
383 EDIT_LockBuffer(wnd, es);
384 switch (msg) {
385 case EM_GETSEL16:
386 DPRINTF_EDIT_MSG16("EM_GETSEL");
387 wParam = 0;
388 lParam = 0;
389 /* fall through */
390 case EM_GETSEL:
391 DPRINTF_EDIT_MSG32("EM_GETSEL");
392 result = EDIT_EM_GetSel(wnd, es, (LPUINT)wParam, (LPUINT)lParam);
393 break;
395 case EM_SETSEL16:
396 DPRINTF_EDIT_MSG16("EM_SETSEL");
397 if (SLOWORD(lParam) == -1)
398 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
399 else
400 EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
401 if (!wParam)
402 EDIT_EM_ScrollCaret(wnd, es);
403 result = 1;
404 break;
405 case EM_SETSEL:
406 DPRINTF_EDIT_MSG32("EM_SETSEL");
407 EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
408 EDIT_EM_ScrollCaret(wnd, es);
409 result = 1;
410 break;
412 case EM_GETRECT16:
413 DPRINTF_EDIT_MSG16("EM_GETRECT");
414 if (lParam)
415 CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
416 break;
417 case EM_GETRECT:
418 DPRINTF_EDIT_MSG32("EM_GETRECT");
419 if (lParam)
420 CopyRect((LPRECT)lParam, &es->format_rect);
421 break;
423 case EM_SETRECT16:
424 DPRINTF_EDIT_MSG16("EM_SETRECT");
425 if ((es->style & ES_MULTILINE) && lParam) {
426 RECT rc;
427 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
428 EDIT_SetRectNP(wnd, es, &rc);
429 EDIT_UpdateText(wnd, NULL, TRUE);
431 break;
432 case EM_SETRECT:
433 DPRINTF_EDIT_MSG32("EM_SETRECT");
434 if ((es->style & ES_MULTILINE) && lParam) {
435 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
436 EDIT_UpdateText(wnd, NULL, TRUE);
438 break;
440 case EM_SETRECTNP16:
441 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
442 if ((es->style & ES_MULTILINE) && lParam) {
443 RECT rc;
444 CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
445 EDIT_SetRectNP(wnd, es, &rc);
447 break;
448 case EM_SETRECTNP:
449 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
450 if ((es->style & ES_MULTILINE) && lParam)
451 EDIT_SetRectNP(wnd, es, (LPRECT)lParam);
452 break;
454 case EM_SCROLL16:
455 DPRINTF_EDIT_MSG16("EM_SCROLL");
456 /* fall through */
457 case EM_SCROLL:
458 DPRINTF_EDIT_MSG32("EM_SCROLL");
459 result = EDIT_EM_Scroll(wnd, es, (INT)wParam);
460 break;
462 case EM_LINESCROLL16:
463 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
464 wParam = (WPARAM)(INT)SHIWORD(lParam);
465 lParam = (LPARAM)(INT)SLOWORD(lParam);
466 /* fall through */
467 case EM_LINESCROLL:
468 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
469 result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT)wParam, (INT)lParam);
470 break;
472 case EM_SCROLLCARET16:
473 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
474 /* fall through */
475 case EM_SCROLLCARET:
476 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
477 EDIT_EM_ScrollCaret(wnd, es);
478 result = 1;
479 break;
481 case EM_GETMODIFY16:
482 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
483 /* fall through */
484 case EM_GETMODIFY:
485 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
486 result = ((es->flags & EF_MODIFIED) != 0);
487 break;
489 case EM_SETMODIFY16:
490 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
491 /* fall through */
492 case EM_SETMODIFY:
493 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
494 if (wParam)
495 es->flags |= EF_MODIFIED;
496 else
497 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
498 break;
500 case EM_GETLINECOUNT16:
501 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
502 /* fall through */
503 case EM_GETLINECOUNT:
504 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
505 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
506 break;
508 case EM_LINEINDEX16:
509 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
510 if ((INT16)wParam == -1)
511 wParam = (WPARAM)-1;
512 /* fall through */
513 case EM_LINEINDEX:
514 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
515 result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT)wParam);
516 break;
518 case EM_SETHANDLE16:
519 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
520 EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
521 break;
522 case EM_SETHANDLE:
523 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
524 EDIT_EM_SetHandle(wnd, es, (HLOCAL)wParam);
525 break;
527 case EM_GETHANDLE16:
528 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
529 result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
530 break;
531 case EM_GETHANDLE:
532 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
533 result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
534 break;
536 case EM_GETTHUMB16:
537 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
538 /* fall through */
539 case EM_GETTHUMB:
540 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
541 result = EDIT_EM_GetThumb(wnd, es);
542 break;
544 /* messages 0x00bf and 0x00c0 missing from specs */
546 case WM_USER+15:
547 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
548 /* fall through */
549 case 0x00bf:
550 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
551 result = DefWindowProcA(hwnd, msg, wParam, lParam);
552 break;
554 case WM_USER+16:
555 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
556 /* fall through */
557 case 0x00c0:
558 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
559 result = DefWindowProcA(hwnd, msg, wParam, lParam);
560 break;
562 case EM_LINELENGTH16:
563 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
564 /* fall through */
565 case EM_LINELENGTH:
566 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
567 result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT)wParam);
568 break;
570 case EM_REPLACESEL16:
571 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
572 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
573 /* fall through */
574 case EM_REPLACESEL:
575 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
576 EDIT_EM_ReplaceSel(wnd, es, (BOOL)wParam, (LPCSTR)lParam, TRUE);
577 if (es->flags & EF_UPDATE) {
578 es->flags &= ~EF_UPDATE;
579 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
581 result = 1;
582 break;
584 /* message 0x00c3 missing from specs */
586 case WM_USER+19:
587 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
588 /* fall through */
589 case 0x00c3:
590 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
591 result = DefWindowProcA(hwnd, msg, wParam, lParam);
592 break;
594 case EM_GETLINE16:
595 DPRINTF_EDIT_MSG16("EM_GETLINE");
596 lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
597 /* fall through */
598 case EM_GETLINE:
599 DPRINTF_EDIT_MSG32("EM_GETLINE");
600 result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT)wParam, (LPSTR)lParam);
601 break;
603 case EM_LIMITTEXT16:
604 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
605 /* fall through */
606 case EM_SETLIMITTEXT:
607 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
608 EDIT_EM_SetLimitText(wnd, es, (INT)wParam);
609 break;
611 case EM_CANUNDO16:
612 DPRINTF_EDIT_MSG16("EM_CANUNDO");
613 /* fall through */
614 case EM_CANUNDO:
615 DPRINTF_EDIT_MSG32("EM_CANUNDO");
616 result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
617 break;
619 case EM_UNDO16:
620 DPRINTF_EDIT_MSG16("EM_UNDO");
621 /* fall through */
622 case EM_UNDO:
623 /* fall through */
624 case WM_UNDO:
625 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
626 result = (LRESULT)EDIT_EM_Undo(wnd, es);
627 break;
629 case EM_FMTLINES16:
630 DPRINTF_EDIT_MSG16("EM_FMTLINES");
631 /* fall through */
632 case EM_FMTLINES:
633 DPRINTF_EDIT_MSG32("EM_FMTLINES");
634 result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL)wParam);
635 break;
637 case EM_LINEFROMCHAR16:
638 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
639 /* fall through */
640 case EM_LINEFROMCHAR:
641 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
642 result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT)wParam);
643 break;
645 /* message 0x00ca missing from specs */
647 case WM_USER+26:
648 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
649 /* fall through */
650 case 0x00ca:
651 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
652 result = DefWindowProcA(hwnd, msg, wParam, lParam);
653 break;
655 case EM_SETTABSTOPS16:
656 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
657 result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
658 break;
659 case EM_SETTABSTOPS:
660 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
661 result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT)wParam, (LPINT)lParam);
662 break;
664 case EM_SETPASSWORDCHAR16:
665 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
666 /* fall through */
667 case EM_SETPASSWORDCHAR:
668 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
669 EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
670 break;
672 case EM_EMPTYUNDOBUFFER16:
673 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
674 /* fall through */
675 case EM_EMPTYUNDOBUFFER:
676 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
677 EDIT_EM_EmptyUndoBuffer(wnd, es);
678 break;
680 case EM_GETFIRSTVISIBLELINE16:
681 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
682 result = es->y_offset;
683 break;
684 case EM_GETFIRSTVISIBLELINE:
685 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
686 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
687 break;
689 case EM_SETREADONLY16:
690 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
691 /* fall through */
692 case EM_SETREADONLY:
693 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
694 if (wParam) {
695 wnd->dwStyle |= ES_READONLY;
696 es->style |= ES_READONLY;
697 } else {
698 wnd->dwStyle &= ~ES_READONLY;
699 es->style &= ~ES_READONLY;
701 result = 1;
702 break;
704 case EM_SETWORDBREAKPROC16:
705 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
706 EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
707 break;
708 case EM_SETWORDBREAKPROC:
709 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
710 EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROCA)lParam);
711 break;
713 case EM_GETWORDBREAKPROC16:
714 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
715 result = (LRESULT)es->word_break_proc16;
716 break;
717 case EM_GETWORDBREAKPROC:
718 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
719 result = (LRESULT)es->word_break_proc32A;
720 break;
722 case EM_GETPASSWORDCHAR16:
723 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
724 /* fall through */
725 case EM_GETPASSWORDCHAR:
726 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
727 result = es->password_char;
728 break;
730 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
732 case EM_SETMARGINS:
733 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
734 EDIT_EM_SetMargins(wnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
735 break;
737 case EM_GETMARGINS:
738 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
739 result = MAKELONG(es->left_margin, es->right_margin);
740 break;
742 case EM_GETLIMITTEXT:
743 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
744 result = es->buffer_limit;
745 break;
747 case EM_POSFROMCHAR:
748 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
749 result = EDIT_EM_PosFromChar(wnd, es, (INT)wParam, FALSE);
750 break;
752 case EM_CHARFROMPOS:
753 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
754 result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
755 break;
757 case WM_GETDLGCODE:
758 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
759 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
761 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
763 int vk = (int)((LPMSG)lParam)->wParam;
765 if ((wnd->dwStyle & ES_WANTRETURN) && vk == VK_RETURN)
767 result |= DLGC_WANTMESSAGE;
769 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
771 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
772 result |= DLGC_WANTMESSAGE;
775 break;
777 case WM_CHAR:
778 DPRINTF_EDIT_MSG32("WM_CHAR");
779 if (((CHAR)wParam == VK_RETURN || (CHAR)wParam == VK_ESCAPE) && es->hwndListBox)
781 if (SendMessageA(wnd->parent->hwndSelf, CB_GETDROPPEDSTATE, 0, 0))
782 SendMessageA(wnd->parent->hwndSelf, WM_KEYDOWN, wParam, 0);
783 break;
785 EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
786 break;
788 case WM_CLEAR:
789 DPRINTF_EDIT_MSG32("WM_CLEAR");
790 EDIT_WM_Clear(wnd, es);
791 break;
793 case WM_COMMAND:
794 DPRINTF_EDIT_MSG32("WM_COMMAND");
795 EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
796 break;
798 case WM_CONTEXTMENU:
799 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
800 EDIT_WM_ContextMenu(wnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
801 break;
803 case WM_COPY:
804 DPRINTF_EDIT_MSG32("WM_COPY");
805 EDIT_WM_Copy(wnd, es);
806 break;
808 case WM_CREATE:
809 DPRINTF_EDIT_MSG32("WM_CREATE");
810 result = EDIT_WM_Create(wnd, es, (LPCREATESTRUCTA)lParam);
811 break;
813 case WM_CUT:
814 DPRINTF_EDIT_MSG32("WM_CUT");
815 EDIT_WM_Cut(wnd, es);
816 break;
818 case WM_ENABLE:
819 DPRINTF_EDIT_MSG32("WM_ENABLE");
820 es->bEnableState = (BOOL) wParam;
821 EDIT_UpdateText(wnd, NULL, TRUE);
822 break;
824 case WM_ERASEBKGND:
825 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
826 result = EDIT_WM_EraseBkGnd(wnd, es, (HDC)wParam);
827 break;
829 case WM_GETFONT:
830 DPRINTF_EDIT_MSG32("WM_GETFONT");
831 result = (LRESULT)es->font;
832 break;
834 case WM_GETTEXT:
835 DPRINTF_EDIT_MSG32("WM_GETTEXT");
836 result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT)wParam, (LPSTR)lParam);
837 break;
839 case WM_GETTEXTLENGTH:
840 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
841 result = strlen(es->text);
842 break;
844 case WM_HSCROLL:
845 DPRINTF_EDIT_MSG32("WM_HSCROLL");
846 result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
847 break;
849 case WM_KEYDOWN:
850 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
851 result = EDIT_WM_KeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
852 break;
854 case WM_KILLFOCUS:
855 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
856 result = EDIT_WM_KillFocus(wnd, es, (HWND)wParam);
857 break;
859 case WM_LBUTTONDBLCLK:
860 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
861 result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
862 break;
864 case WM_LBUTTONDOWN:
865 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
866 result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
867 break;
869 case WM_LBUTTONUP:
870 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
871 result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
872 break;
874 case WM_MBUTTONDOWN:
875 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
876 result = EDIT_WM_MButtonDown(wnd);
877 break;
879 case WM_MOUSEACTIVATE:
881 * FIXME: maybe DefWindowProc() screws up, but it seems that
882 * modeless dialog boxes need this. If we don't do this, the focus
883 * will _not_ be set by DefWindowProc() for edit controls in a
884 * modeless dialog box ???
886 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
887 SetFocus(wnd->hwndSelf);
888 result = MA_ACTIVATE;
889 break;
891 case WM_MOUSEMOVE:
893 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
895 result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
896 break;
898 case WM_PAINT:
899 DPRINTF_EDIT_MSG32("WM_PAINT");
900 EDIT_WM_Paint(wnd, es, wParam);
901 break;
903 case WM_PASTE:
904 DPRINTF_EDIT_MSG32("WM_PASTE");
905 EDIT_WM_Paste(wnd, es);
906 break;
908 case WM_SETFOCUS:
909 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
910 EDIT_WM_SetFocus(wnd, es, (HWND)wParam);
911 break;
913 case WM_SETFONT:
914 DPRINTF_EDIT_MSG32("WM_SETFONT");
915 EDIT_WM_SetFont(wnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
916 break;
918 case WM_SETTEXT:
919 DPRINTF_EDIT_MSG32("WM_SETTEXT");
920 EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
921 result = TRUE;
922 break;
924 case WM_SIZE:
925 DPRINTF_EDIT_MSG32("WM_SIZE");
926 EDIT_WM_Size(wnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
927 break;
929 case WM_SYSKEYDOWN:
930 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
931 result = EDIT_WM_SysKeyDown(wnd, es, (INT)wParam, (DWORD)lParam);
932 break;
934 case WM_TIMER:
935 DPRINTF_EDIT_MSG32("WM_TIMER");
936 EDIT_WM_Timer(wnd, es, (INT)wParam, (TIMERPROC)lParam);
937 break;
939 case WM_VSCROLL:
940 DPRINTF_EDIT_MSG32("WM_VSCROLL");
941 result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
942 break;
944 case WM_MOUSEWHEEL:
946 short gcWheelDelta = 0;
947 UINT pulScrollLines = 3;
948 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
950 if (wParam & (MK_SHIFT | MK_CONTROL)) {
951 result = DefWindowProcA(hwnd, msg, wParam, lParam);
952 break;
954 gcWheelDelta -= (short) HIWORD(wParam);
955 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
957 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
958 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
959 result = EDIT_EM_LineScroll(wnd, es, 0, cLineScroll);
962 break;
963 default:
964 result = DefWindowProcA(hwnd, msg, wParam, lParam);
965 break;
967 EDIT_UnlockBuffer(wnd, es, FALSE);
968 END:
969 WIN_ReleaseWndPtr(wnd);
970 return result;
975 /*********************************************************************
977 * EDIT_BuildLineDefs_ML
979 * Build linked list of text lines.
980 * Lines can end with '\0' (last line), a character (if it is wrapped),
981 * a soft return '\r\r\n' or a hard return '\r\n'
984 static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
986 HDC dc;
987 HFONT old_font = 0;
988 LPSTR start, cp;
989 INT fw;
990 LINEDEF *current_def;
991 LINEDEF **previous_next;
993 current_def = es->first_line_def;
994 do {
995 LINEDEF *next_def = current_def->next;
996 HeapFree(es->heap, 0, current_def);
997 current_def = next_def;
998 } while (current_def);
999 es->line_count = 0;
1000 es->text_width = 0;
1002 dc = GetDC(wnd->hwndSelf);
1003 if (es->font)
1004 old_font = SelectObject(dc, es->font);
1006 fw = es->format_rect.right - es->format_rect.left;
1007 start = es->text;
1008 previous_next = &es->first_line_def;
1009 do {
1010 current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
1011 current_def->next = NULL;
1012 cp = start;
1013 while (*cp) {
1014 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1015 break;
1016 cp++;
1018 if (!(*cp)) {
1019 current_def->ending = END_0;
1020 current_def->net_length = strlen(start);
1021 } else if ((cp > start) && (*(cp - 1) == '\r')) {
1022 current_def->ending = END_SOFT;
1023 current_def->net_length = cp - start - 1;
1024 } else {
1025 current_def->ending = END_HARD;
1026 current_def->net_length = cp - start;
1028 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1029 start, current_def->net_length,
1030 es->tabs_count, es->tabs));
1031 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1032 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
1033 INT next = 0;
1034 INT prev;
1035 do {
1036 prev = next;
1037 next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
1038 prev + 1, current_def->net_length, WB_RIGHT);
1039 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1040 start, next, es->tabs_count, es->tabs));
1041 } while (current_def->width <= fw);
1042 if (!prev) {
1043 next = 0;
1044 do {
1045 prev = next;
1046 next++;
1047 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc,
1048 start, next, es->tabs_count, es->tabs));
1049 } while (current_def->width <= fw);
1050 if (!prev)
1051 prev = 1;
1053 current_def->net_length = prev;
1054 current_def->ending = END_WRAP;
1055 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(dc, start,
1056 current_def->net_length, es->tabs_count, es->tabs));
1058 switch (current_def->ending) {
1059 case END_SOFT:
1060 current_def->length = current_def->net_length + 3;
1061 break;
1062 case END_HARD:
1063 current_def->length = current_def->net_length + 2;
1064 break;
1065 case END_WRAP:
1066 case END_0:
1067 current_def->length = current_def->net_length;
1068 break;
1070 es->text_width = max(es->text_width, current_def->width);
1071 start += current_def->length;
1072 *previous_next = current_def;
1073 previous_next = &current_def->next;
1074 es->line_count++;
1075 } while (current_def->ending != END_0);
1076 if (es->font)
1077 SelectObject(dc, old_font);
1078 ReleaseDC(wnd->hwndSelf, dc);
1082 /*********************************************************************
1084 * EDIT_CallWordBreakProc
1086 * Call appropriate WordBreakProc (internal or external).
1088 * Note: The "start" argument should always be an index refering
1089 * to es->text. The actual wordbreak proc might be
1090 * 16 bit, so we can't always pass any 32 bit LPSTR.
1091 * Hence we assume that es->text is the buffer that holds
1092 * the string under examination (we can decide this for ourselves).
1095 /* ### start build ### */
1096 extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1097 /* ### stop build ### */
1098 static INT EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
1100 if (es->word_break_proc16) {
1101 HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
1102 SEGPTR segptr = LocalLock16(hloc16);
1103 INT ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1104 segptr + start, index, count, action);
1105 LocalUnlock16(hloc16);
1106 return ret;
1108 else if (es->word_break_proc32A)
1110 TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
1111 es->word_break_proc32A, es->text + start, index,
1112 count, action );
1113 return (INT)es->word_break_proc32A( es->text + start, index,
1114 count, action );
1116 else
1117 return EDIT_WordBreakProc(es->text + start, index, count, action);
1121 /*********************************************************************
1123 * EDIT_CharFromPos
1125 * Beware: This is not the function called on EM_CHARFROMPOS
1126 * The position _can_ be outside the formatting / client
1127 * rectangle
1128 * The return value is only the character index
1131 static INT EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1133 INT index;
1134 HDC dc;
1135 HFONT old_font = 0;
1137 if (es->style & ES_MULTILINE) {
1138 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1139 INT line_index = 0;
1140 LINEDEF *line_def = es->first_line_def;
1141 INT low, high;
1142 while ((line > 0) && line_def->next) {
1143 line_index += line_def->length;
1144 line_def = line_def->next;
1145 line--;
1147 x += es->x_offset - es->format_rect.left;
1148 if (x >= line_def->width) {
1149 if (after_wrap)
1150 *after_wrap = (line_def->ending == END_WRAP);
1151 return line_index + line_def->net_length;
1153 if (x <= 0) {
1154 if (after_wrap)
1155 *after_wrap = FALSE;
1156 return line_index;
1158 dc = GetDC(wnd->hwndSelf);
1159 if (es->font)
1160 old_font = SelectObject(dc, es->font);
1161 low = line_index + 1;
1162 high = line_index + line_def->net_length + 1;
1163 while (low < high - 1)
1165 INT mid = (low + high) / 2;
1166 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1167 else low = mid;
1169 index = low;
1171 if (after_wrap)
1172 *after_wrap = ((index == line_index + line_def->net_length) &&
1173 (line_def->ending == END_WRAP));
1174 } else {
1175 LPSTR text;
1176 SIZE size;
1177 if (after_wrap)
1178 *after_wrap = FALSE;
1179 x -= es->format_rect.left;
1180 if (!x)
1181 return es->x_offset;
1182 text = EDIT_GetPasswordPointer_SL(wnd, es);
1183 dc = GetDC(wnd->hwndSelf);
1184 if (es->font)
1185 old_font = SelectObject(dc, es->font);
1186 if (x < 0)
1188 INT low = 0;
1189 INT high = es->x_offset;
1190 while (low < high - 1)
1192 INT mid = (low + high) / 2;
1193 GetTextExtentPoint32A( dc, text + mid,
1194 es->x_offset - mid, &size );
1195 if (size.cx > -x) low = mid;
1196 else high = mid;
1198 index = low;
1200 else
1202 INT low = es->x_offset;
1203 INT high = strlen(es->text) + 1;
1204 while (low < high - 1)
1206 INT mid = (low + high) / 2;
1207 GetTextExtentPoint32A( dc, text + es->x_offset,
1208 mid - es->x_offset, &size );
1209 if (size.cx > x) high = mid;
1210 else low = mid;
1212 index = low;
1214 if (es->style & ES_PASSWORD)
1215 HeapFree(es->heap, 0 ,text);
1217 if (es->font)
1218 SelectObject(dc, old_font);
1219 ReleaseDC(wnd->hwndSelf, dc);
1220 return index;
1224 /*********************************************************************
1226 * EDIT_ConfinePoint
1228 * adjusts the point to be within the formatting rectangle
1229 * (so CharFromPos returns the nearest _visible_ character)
1232 static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT x, LPINT y)
1234 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1235 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1239 /*********************************************************************
1241 * EDIT_GetLineRect
1243 * Calculates the bounding rectangle for a line from a starting
1244 * column to an ending column.
1247 static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1249 INT line_index = EDIT_EM_LineIndex(wnd, es, line);
1251 if (es->style & ES_MULTILINE)
1252 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1253 else
1254 rc->top = es->format_rect.top;
1255 rc->bottom = rc->top + es->line_height;
1256 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
1257 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
1261 /*********************************************************************
1263 * EDIT_GetPasswordPointer_SL
1265 * note: caller should free the (optionally) allocated buffer
1268 static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
1270 if (es->style & ES_PASSWORD) {
1271 INT len = strlen(es->text);
1272 LPSTR text = HeapAlloc(es->heap, 0, len + 1);
1273 RtlFillMemory(text, len, es->password_char);
1274 text[len] = '\0';
1275 return text;
1276 } else
1277 return es->text;
1281 /*********************************************************************
1283 * EDIT_LockBuffer
1285 * This acts as a LOCAL_Lock(), but it locks only once. This way
1286 * you can call it whenever you like, without unlocking.
1289 static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
1291 if (!es) {
1292 ERR("no EDITSTATE ... please report\n");
1293 return;
1295 if (!(es->style & ES_MULTILINE))
1296 return;
1297 if (!es->text) {
1298 if (es->hloc32)
1299 es->text = LocalLock(es->hloc32);
1300 else if (es->hloc16)
1301 es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
1302 else {
1303 ERR("no buffer ... please report\n");
1304 return;
1307 es->lock_count++;
1311 /*********************************************************************
1313 * EDIT_SL_InvalidateText
1315 * Called from EDIT_InvalidateText().
1316 * Does the job for single-line controls only.
1319 static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1321 RECT line_rect;
1322 RECT rc;
1324 EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
1325 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1326 EDIT_UpdateText(wnd, &rc, FALSE);
1330 /*********************************************************************
1332 * EDIT_ML_InvalidateText
1334 * Called from EDIT_InvalidateText().
1335 * Does the job for multi-line controls only.
1338 static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1340 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1341 INT sl = EDIT_EM_LineFromChar(wnd, es, start);
1342 INT el = EDIT_EM_LineFromChar(wnd, es, end);
1343 INT sc;
1344 INT ec;
1345 RECT rc1;
1346 RECT rcWnd;
1347 RECT rcLine;
1348 RECT rcUpdate;
1349 INT l;
1351 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1352 return;
1354 sc = start - EDIT_EM_LineIndex(wnd, es, sl);
1355 ec = end - EDIT_EM_LineIndex(wnd, es, el);
1356 if (sl < es->y_offset) {
1357 sl = es->y_offset;
1358 sc = 0;
1360 if (el > es->y_offset + vlc) {
1361 el = es->y_offset + vlc;
1362 ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
1364 GetClientRect(wnd->hwndSelf, &rc1);
1365 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1366 if (sl == el) {
1367 EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
1368 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1369 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1370 } else {
1371 EDIT_GetLineRect(wnd, es, sl, sc,
1372 EDIT_EM_LineLength(wnd, es,
1373 EDIT_EM_LineIndex(wnd, es, sl)),
1374 &rcLine);
1375 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1376 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1377 for (l = sl + 1 ; l < el ; l++) {
1378 EDIT_GetLineRect(wnd, es, l, 0,
1379 EDIT_EM_LineLength(wnd, es,
1380 EDIT_EM_LineIndex(wnd, es, l)),
1381 &rcLine);
1382 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1383 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1385 EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
1386 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1387 EDIT_UpdateText(wnd, &rcUpdate, FALSE);
1392 /*********************************************************************
1394 * EDIT_InvalidateText
1396 * Invalidate the text from offset start upto, but not including,
1397 * offset end. Useful for (re)painting the selection.
1398 * Regions outside the linewidth are not invalidated.
1399 * end == -1 means end == TextLength.
1400 * start and end need not be ordered.
1403 static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT start, INT end)
1405 if (end == start)
1406 return;
1408 if (end == -1)
1409 end = strlen(es->text);
1411 ORDER_INT(start, end);
1413 if (es->style & ES_MULTILINE)
1414 EDIT_ML_InvalidateText(wnd, es, start, end);
1415 else
1416 EDIT_SL_InvalidateText(wnd, es, start, end);
1420 /*********************************************************************
1422 * EDIT_MakeFit
1424 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1427 static BOOL EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT size)
1429 HLOCAL hNew32;
1430 HLOCAL16 hNew16;
1432 if (size <= es->buffer_size)
1433 return TRUE;
1434 if (size > es->buffer_limit) {
1435 EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
1436 return FALSE;
1438 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1439 if (size > es->buffer_limit)
1440 size = es->buffer_limit;
1442 TRACE("trying to ReAlloc to %d+1\n", size);
1444 EDIT_UnlockBuffer(wnd, es, TRUE);
1445 if (es->text) {
1446 if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
1447 es->buffer_size = min(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1448 else
1449 es->buffer_size = 0;
1450 } else if (es->hloc32) {
1451 if ((hNew32 = LocalReAlloc(es->hloc32, size + 1, 0))) {
1452 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1453 es->hloc32 = hNew32;
1454 es->buffer_size = min(LocalSize(es->hloc32) - 1, es->buffer_limit);
1456 } else if (es->hloc16) {
1457 if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
1458 TRACE("Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
1459 es->hloc16 = hNew16;
1460 es->buffer_size = min(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
1463 if (es->buffer_size < size) {
1464 EDIT_LockBuffer(wnd, es);
1465 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1466 EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
1467 return FALSE;
1468 } else {
1469 EDIT_LockBuffer(wnd, es);
1470 TRACE("We now have %d+1\n", es->buffer_size);
1471 return TRUE;
1476 /*********************************************************************
1478 * EDIT_MakeUndoFit
1480 * Try to fit size + 1 bytes in the undo buffer.
1483 static BOOL EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT size)
1485 if (size <= es->undo_buffer_size)
1486 return TRUE;
1487 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1489 TRACE("trying to ReAlloc to %d+1\n", size);
1491 if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
1492 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1493 if (es->undo_buffer_size < size) {
1494 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1495 return FALSE;
1497 return TRUE;
1499 return FALSE;
1503 /*********************************************************************
1505 * EDIT_MoveBackward
1508 static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1510 INT e = es->selection_end;
1512 if (e) {
1513 e--;
1514 if ((es->style & ES_MULTILINE) && e &&
1515 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1516 e--;
1517 if (e && (es->text[e - 1] == '\r'))
1518 e--;
1521 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1522 EDIT_EM_ScrollCaret(wnd, es);
1526 /*********************************************************************
1528 * EDIT_MoveDown_ML
1530 * Only for multi line controls
1531 * Move the caret one line down, on a column with the nearest
1532 * x coordinate on the screen (might be a different column).
1535 static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1537 INT s = es->selection_start;
1538 INT e = es->selection_end;
1539 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1540 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1541 INT x = SLOWORD(pos);
1542 INT y = SHIWORD(pos);
1544 e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
1545 if (!extend)
1546 s = e;
1547 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1548 EDIT_EM_ScrollCaret(wnd, es);
1552 /*********************************************************************
1554 * EDIT_MoveEnd
1557 static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL extend)
1559 BOOL after_wrap = FALSE;
1560 INT e;
1562 /* Pass a high value in x to make sure of receiving the en of the line */
1563 if (es->style & ES_MULTILINE)
1564 e = EDIT_CharFromPos(wnd, es, 0x3fffffff,
1565 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1566 else
1567 e = strlen(es->text);
1568 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
1569 EDIT_EM_ScrollCaret(wnd, es);
1573 /*********************************************************************
1575 * EDIT_MoveForward
1578 static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL extend)
1580 INT e = es->selection_end;
1582 if (es->text[e]) {
1583 e++;
1584 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1585 if (es->text[e] == '\n')
1586 e++;
1587 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1588 e += 2;
1591 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1592 EDIT_EM_ScrollCaret(wnd, es);
1596 /*********************************************************************
1598 * EDIT_MoveHome
1600 * Home key: move to beginning of line.
1603 static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL extend)
1605 INT e;
1607 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1608 if (es->style & ES_MULTILINE)
1609 e = EDIT_CharFromPos(wnd, es, -es->x_offset,
1610 HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1611 else
1612 e = 0;
1613 EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
1614 EDIT_EM_ScrollCaret(wnd, es);
1618 /*********************************************************************
1620 * EDIT_MovePageDown_ML
1622 * Only for multi line controls
1623 * Move the caret one page down, on a column with the nearest
1624 * x coordinate on the screen (might be a different column).
1627 static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1629 INT s = es->selection_start;
1630 INT e = es->selection_end;
1631 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1632 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1633 INT x = SLOWORD(pos);
1634 INT y = SHIWORD(pos);
1636 e = EDIT_CharFromPos(wnd, es, x,
1637 y + (es->format_rect.bottom - es->format_rect.top),
1638 &after_wrap);
1639 if (!extend)
1640 s = e;
1641 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1642 EDIT_EM_ScrollCaret(wnd, es);
1646 /*********************************************************************
1648 * EDIT_MovePageUp_ML
1650 * Only for multi line controls
1651 * Move the caret one page up, on a column with the nearest
1652 * x coordinate on the screen (might be a different column).
1655 static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1657 INT s = es->selection_start;
1658 INT e = es->selection_end;
1659 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1660 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1661 INT x = SLOWORD(pos);
1662 INT y = SHIWORD(pos);
1664 e = EDIT_CharFromPos(wnd, es, x,
1665 y - (es->format_rect.bottom - es->format_rect.top),
1666 &after_wrap);
1667 if (!extend)
1668 s = e;
1669 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1670 EDIT_EM_ScrollCaret(wnd, es);
1674 /*********************************************************************
1676 * EDIT_MoveUp_ML
1678 * Only for multi line controls
1679 * Move the caret one line up, on a column with the nearest
1680 * x coordinate on the screen (might be a different column).
1683 static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL extend)
1685 INT s = es->selection_start;
1686 INT e = es->selection_end;
1687 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1688 LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
1689 INT x = SLOWORD(pos);
1690 INT y = SHIWORD(pos);
1692 e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
1693 if (!extend)
1694 s = e;
1695 EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
1696 EDIT_EM_ScrollCaret(wnd, es);
1700 /*********************************************************************
1702 * EDIT_MoveWordBackward
1705 static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL extend)
1707 INT s = es->selection_start;
1708 INT e = es->selection_end;
1709 INT l;
1710 INT ll;
1711 INT li;
1713 l = EDIT_EM_LineFromChar(wnd, es, e);
1714 ll = EDIT_EM_LineLength(wnd, es, e);
1715 li = EDIT_EM_LineIndex(wnd, es, l);
1716 if (e - li == 0) {
1717 if (l) {
1718 li = EDIT_EM_LineIndex(wnd, es, l - 1);
1719 e = li + EDIT_EM_LineLength(wnd, es, li);
1721 } else {
1722 e = li + (INT)EDIT_CallWordBreakProc(wnd, es,
1723 li, e - li, ll, WB_LEFT);
1725 if (!extend)
1726 s = e;
1727 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1728 EDIT_EM_ScrollCaret(wnd, es);
1732 /*********************************************************************
1734 * EDIT_MoveWordForward
1737 static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL extend)
1739 INT s = es->selection_start;
1740 INT e = es->selection_end;
1741 INT l;
1742 INT ll;
1743 INT li;
1745 l = EDIT_EM_LineFromChar(wnd, es, e);
1746 ll = EDIT_EM_LineLength(wnd, es, e);
1747 li = EDIT_EM_LineIndex(wnd, es, l);
1748 if (e - li == ll) {
1749 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1750 e = EDIT_EM_LineIndex(wnd, es, l + 1);
1751 } else {
1752 e = li + EDIT_CallWordBreakProc(wnd, es,
1753 li, e - li + 1, ll, WB_RIGHT);
1755 if (!extend)
1756 s = e;
1757 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
1758 EDIT_EM_ScrollCaret(wnd, es);
1762 /*********************************************************************
1764 * EDIT_PaintLine
1767 static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1769 INT s = es->selection_start;
1770 INT e = es->selection_end;
1771 INT li;
1772 INT ll;
1773 INT x;
1774 INT y;
1775 LRESULT pos;
1777 if (es->style & ES_MULTILINE) {
1778 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1779 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1780 return;
1781 } else if (line)
1782 return;
1784 TRACE("line=%d\n", line);
1786 pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
1787 x = SLOWORD(pos);
1788 y = SHIWORD(pos);
1789 li = EDIT_EM_LineIndex(wnd, es, line);
1790 ll = EDIT_EM_LineLength(wnd, es, li);
1791 s = es->selection_start;
1792 e = es->selection_end;
1793 ORDER_INT(s, e);
1794 s = min(li + ll, max(li, s));
1795 e = min(li + ll, max(li, e));
1796 if (rev && (s != e) &&
1797 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
1798 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
1799 x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
1800 x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
1801 } else
1802 x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
1806 /*********************************************************************
1808 * EDIT_PaintText
1811 static INT EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1813 COLORREF BkColor;
1814 COLORREF TextColor;
1815 INT ret;
1816 INT li;
1817 SIZE size;
1819 if (!count)
1820 return 0;
1821 BkColor = GetBkColor(dc);
1822 TextColor = GetTextColor(dc);
1823 if (rev) {
1824 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1825 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1827 li = EDIT_EM_LineIndex(wnd, es, line);
1828 if (es->style & ES_MULTILINE) {
1829 ret = (INT)LOWORD(TabbedTextOutA(dc, x, y, es->text + li + col, count,
1830 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
1831 } else {
1832 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
1833 TextOutA(dc, x, y, text + li + col, count);
1834 GetTextExtentPoint32A(dc, text + li + col, count, &size);
1835 ret = size.cx;
1836 if (es->style & ES_PASSWORD)
1837 HeapFree(es->heap, 0, text);
1839 if (rev) {
1840 SetBkColor(dc, BkColor);
1841 SetTextColor(dc, TextColor);
1843 return ret;
1847 /*********************************************************************
1849 * EDIT_SetCaretPos
1852 static void EDIT_SetCaretPos(WND *wnd, EDITSTATE *es, INT pos,
1853 BOOL after_wrap)
1855 LRESULT res = EDIT_EM_PosFromChar(wnd, es, pos, after_wrap);
1856 INT x = SLOWORD(res);
1857 INT y = SHIWORD(res);
1859 if(x < es->format_rect.left)
1860 x = es->format_rect.left;
1861 if(x > es->format_rect.right - 2)
1862 x = es->format_rect.right - 2;
1863 if(y > es->format_rect.bottom)
1864 y = es->format_rect.bottom;
1865 if(y < es->format_rect.top)
1866 y = es->format_rect.top;
1867 SetCaretPos(x, y);
1868 return;
1872 /*********************************************************************
1874 * EDIT_SetRectNP
1876 * note: this is not (exactly) the handler called on EM_SETRECTNP
1877 * it is also used to set the rect of a single line control
1880 static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT rc)
1882 CopyRect(&es->format_rect, rc);
1883 if (es->style & WS_BORDER) {
1884 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
1885 if(TWEAK_WineLook == WIN31_LOOK)
1886 bw += 2;
1887 es->format_rect.left += bw;
1888 es->format_rect.top += bw;
1889 es->format_rect.right -= bw;
1890 es->format_rect.bottom -= bw;
1892 es->format_rect.left += es->left_margin;
1893 es->format_rect.right -= es->right_margin;
1894 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
1895 if (es->style & ES_MULTILINE)
1896 es->format_rect.bottom = es->format_rect.top +
1897 max(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1898 else
1899 es->format_rect.bottom = es->format_rect.top + es->line_height;
1900 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1901 EDIT_BuildLineDefs_ML(wnd, es);
1905 /*********************************************************************
1907 * EDIT_UnlockBuffer
1910 static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL force)
1912 if (!es) {
1913 ERR("no EDITSTATE ... please report\n");
1914 return;
1916 if (!(es->style & ES_MULTILINE))
1917 return;
1918 if (!es->lock_count) {
1919 ERR("lock_count == 0 ... please report\n");
1920 return;
1922 if (!es->text) {
1923 ERR("es->text == 0 ... please report\n");
1924 return;
1926 if (force || (es->lock_count == 1)) {
1927 if (es->hloc32) {
1928 LocalUnlock(es->hloc32);
1929 es->text = NULL;
1930 } else if (es->hloc16) {
1931 LOCAL_Unlock(wnd->hInstance, es->hloc16);
1932 es->text = NULL;
1935 es->lock_count--;
1939 /*********************************************************************
1941 * EDIT_WordBreakProc
1943 * Find the beginning of words.
1944 * Note: unlike the specs for a WordBreakProc, this function only
1945 * allows to be called without linebreaks between s[0] upto
1946 * s[count - 1]. Remember it is only called
1947 * internally, so we can decide this for ourselves.
1950 static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1952 INT ret = 0;
1954 TRACE("s=%p, index=%u, count=%u, action=%d\n",
1955 s, index, count, action);
1957 switch (action) {
1958 case WB_LEFT:
1959 if (!count)
1960 break;
1961 if (index)
1962 index--;
1963 if (s[index] == ' ') {
1964 while (index && (s[index] == ' '))
1965 index--;
1966 if (index) {
1967 while (index && (s[index] != ' '))
1968 index--;
1969 if (s[index] == ' ')
1970 index++;
1972 } else {
1973 while (index && (s[index] != ' '))
1974 index--;
1975 if (s[index] == ' ')
1976 index++;
1978 ret = index;
1979 break;
1980 case WB_RIGHT:
1981 if (!count)
1982 break;
1983 if (index)
1984 index--;
1985 if (s[index] == ' ')
1986 while ((index < count) && (s[index] == ' ')) index++;
1987 else {
1988 while (s[index] && (s[index] != ' ') && (index < count))
1989 index++;
1990 while ((s[index] == ' ') && (index < count)) index++;
1992 ret = index;
1993 break;
1994 case WB_ISDELIMITER:
1995 ret = (s[index] == ' ');
1996 break;
1997 default:
1998 ERR("unknown action code, please report !\n");
1999 break;
2001 return ret;
2005 /*********************************************************************
2007 * EM_CHARFROMPOS
2009 * returns line number (not index) in high-order word of result.
2010 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2011 * to Richedit, not to the edit control. Original documentation is valid.
2012 * FIXME: do the specs mean to return -1 if outside client area or
2013 * if outside formatting rectangle ???
2016 static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT x, INT y)
2018 POINT pt;
2019 RECT rc;
2020 INT index;
2022 pt.x = x;
2023 pt.y = y;
2024 GetClientRect(wnd->hwndSelf, &rc);
2025 if (!PtInRect(&rc, pt))
2026 return -1;
2028 index = EDIT_CharFromPos(wnd, es, x, y, NULL);
2029 return MAKELONG(index, EDIT_EM_LineFromChar(wnd, es, index));
2033 /*********************************************************************
2035 * EM_FMTLINES
2037 * Enable or disable soft breaks.
2039 static BOOL EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL add_eol)
2041 es->flags &= ~EF_USE_SOFTBRK;
2042 if (add_eol) {
2043 es->flags |= EF_USE_SOFTBRK;
2044 FIXME("soft break enabled, not implemented\n");
2046 return add_eol;
2050 /*********************************************************************
2052 * EM_GETHANDLE
2054 * Hopefully this won't fire back at us.
2055 * We always start with a fixed buffer in our own heap.
2056 * However, with this message a 32 bit application requests
2057 * a handle to 32 bit moveable local heap memory, where it expects
2058 * to find the text.
2059 * It's a pity that from this moment on we have to use this
2060 * local heap, because applications may rely on the handle
2061 * in the future.
2063 * In this function we'll try to switch to local heap.
2066 static HLOCAL EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
2068 HLOCAL newBuf;
2069 LPSTR newText;
2070 INT newSize;
2072 if (!(es->style & ES_MULTILINE))
2073 return 0;
2075 if (es->hloc32)
2076 return es->hloc32;
2077 else if (es->hloc16)
2078 return (HLOCAL)es->hloc16;
2080 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, strlen(es->text) + 1))) {
2081 ERR("could not allocate new 32 bit buffer\n");
2082 return 0;
2084 newSize = min(LocalSize(newBuf) - 1, es->buffer_limit);
2085 if (!(newText = LocalLock(newBuf))) {
2086 ERR("could not lock new 32 bit buffer\n");
2087 LocalFree(newBuf);
2088 return 0;
2090 strcpy(newText, es->text);
2091 EDIT_UnlockBuffer(wnd, es, TRUE);
2092 if (es->text)
2093 HeapFree(es->heap, 0, es->text);
2094 es->hloc32 = newBuf;
2095 es->hloc16 = (HLOCAL16)NULL;
2096 es->buffer_size = newSize;
2097 es->text = newText;
2098 EDIT_LockBuffer(wnd, es);
2099 TRACE("switched to 32 bit local heap\n");
2101 return es->hloc32;
2105 /*********************************************************************
2107 * EM_GETHANDLE16
2109 * Hopefully this won't fire back at us.
2110 * We always start with a buffer in 32 bit linear memory.
2111 * However, with this message a 16 bit application requests
2112 * a handle of 16 bit local heap memory, where it expects to find
2113 * the text.
2114 * It's a pitty that from this moment on we have to use this
2115 * local heap, because applications may rely on the handle
2116 * in the future.
2118 * In this function we'll try to switch to local heap.
2120 static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
2122 HLOCAL16 newBuf;
2123 LPSTR newText;
2124 INT newSize;
2126 if (!(es->style & ES_MULTILINE))
2127 return 0;
2129 if (es->hloc16)
2130 return es->hloc16;
2132 if (!LOCAL_HeapSize(wnd->hInstance)) {
2133 if (!LocalInit16(wnd->hInstance, 0,
2134 GlobalSize16(wnd->hInstance))) {
2135 ERR("could not initialize local heap\n");
2136 return 0;
2138 TRACE("local heap initialized\n");
2140 if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, strlen(es->text) + 1))) {
2141 ERR("could not allocate new 16 bit buffer\n");
2142 return 0;
2144 newSize = min(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
2145 if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
2146 ERR("could not lock new 16 bit buffer\n");
2147 LOCAL_Free(wnd->hInstance, newBuf);
2148 return 0;
2150 strcpy(newText, es->text);
2151 EDIT_UnlockBuffer(wnd, es, TRUE);
2152 if (es->text)
2153 HeapFree(es->heap, 0, es->text);
2154 else if (es->hloc32) {
2155 while (LocalFree(es->hloc32)) ;
2156 LocalFree(es->hloc32);
2158 es->hloc32 = (HLOCAL)NULL;
2159 es->hloc16 = newBuf;
2160 es->buffer_size = newSize;
2161 es->text = newText;
2162 EDIT_LockBuffer(wnd, es);
2163 TRACE("switched to 16 bit buffer\n");
2165 return es->hloc16;
2169 /*********************************************************************
2171 * EM_GETLINE
2174 static INT EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT line, LPSTR lpch)
2176 LPSTR src;
2177 INT len;
2178 INT i;
2180 if (es->style & ES_MULTILINE) {
2181 if (line >= es->line_count)
2182 return 0;
2183 } else
2184 line = 0;
2185 i = EDIT_EM_LineIndex(wnd, es, line);
2186 src = es->text + i;
2187 len = min(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, i));
2188 for (i = 0 ; i < len ; i++) {
2189 *lpch = *src;
2190 src++;
2191 lpch++;
2193 return (LRESULT)len;
2197 /*********************************************************************
2199 * EM_GETSEL
2202 static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT start, LPUINT end)
2204 UINT s = es->selection_start;
2205 UINT e = es->selection_end;
2207 ORDER_UINT(s, e);
2208 if (start)
2209 *start = s;
2210 if (end)
2211 *end = e;
2212 return MAKELONG(s, e);
2216 /*********************************************************************
2218 * EM_GETTHUMB
2220 * FIXME: is this right ? (or should it be only VSCROLL)
2221 * (and maybe only for edit controls that really have their
2222 * own scrollbars) (and maybe only for multiline controls ?)
2223 * All in all: very poorly documented
2225 * FIXME: now it's also broken, because of the new WM_HSCROLL /
2226 * WM_VSCROLL handlers
2229 static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
2231 return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
2232 EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
2236 /*********************************************************************
2238 * EM_LINEFROMCHAR
2241 static INT EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT index)
2243 INT line;
2244 LINEDEF *line_def;
2246 if (!(es->style & ES_MULTILINE))
2247 return 0;
2248 if (index > strlen(es->text))
2249 return es->line_count - 1;
2250 if (index == -1)
2251 index = min(es->selection_start, es->selection_end);
2253 line = 0;
2254 line_def = es->first_line_def;
2255 index -= line_def->length;
2256 while ((index >= 0) && line_def->next) {
2257 line++;
2258 line_def = line_def->next;
2259 index -= line_def->length;
2261 return line;
2265 /*********************************************************************
2267 * EM_LINEINDEX
2270 static INT EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT line)
2272 INT line_index;
2273 LINEDEF *line_def;
2275 if (!(es->style & ES_MULTILINE))
2276 return 0;
2277 if (line >= es->line_count)
2278 return -1;
2280 line_index = 0;
2281 line_def = es->first_line_def;
2282 if (line == -1) {
2283 INT index = es->selection_end - line_def->length;
2284 while ((index >= 0) && line_def->next) {
2285 line_index += line_def->length;
2286 line_def = line_def->next;
2287 index -= line_def->length;
2289 } else {
2290 while (line > 0) {
2291 line_index += line_def->length;
2292 line_def = line_def->next;
2293 line--;
2296 return line_index;
2300 /*********************************************************************
2302 * EM_LINELENGTH
2305 static INT EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT index)
2307 LINEDEF *line_def;
2309 if (!(es->style & ES_MULTILINE))
2310 return strlen(es->text);
2312 if (index == -1) {
2313 /* get the number of remaining non-selected chars of selected lines */
2314 INT32 li;
2315 INT32 count;
2316 li = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
2317 /* # chars before start of selection area */
2318 count = es->selection_start - EDIT_EM_LineIndex(wnd, es, li);
2319 li = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2320 /* # chars after end of selection */
2321 count += EDIT_EM_LineIndex(wnd, es, li) +
2322 EDIT_EM_LineLength(wnd, es, li) - es->selection_end;
2323 return count;
2325 line_def = es->first_line_def;
2326 index -= line_def->length;
2327 while ((index >= 0) && line_def->next) {
2328 line_def = line_def->next;
2329 index -= line_def->length;
2331 return line_def->net_length;
2335 /*********************************************************************
2337 * EM_LINESCROLL
2339 * FIXME: dx is in average character widths
2340 * However, we assume it is in pixels when we use this
2341 * function internally
2344 static BOOL EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT dx, INT dy)
2346 INT nyoff;
2348 if (!(es->style & ES_MULTILINE))
2349 return FALSE;
2351 if (-dx > es->x_offset)
2352 dx = -es->x_offset;
2353 if (dx > es->text_width - es->x_offset)
2354 dx = es->text_width - es->x_offset;
2355 nyoff = max(0, es->y_offset + dy);
2356 if (nyoff >= es->line_count)
2357 nyoff = es->line_count - 1;
2358 dy = (es->y_offset - nyoff) * es->line_height;
2359 if (dx || dy) {
2360 RECT rc1;
2361 RECT rc;
2362 GetClientRect(wnd->hwndSelf, &rc1);
2363 IntersectRect(&rc, &rc1, &es->format_rect);
2364 ScrollWindowEx(wnd->hwndSelf, -dx, dy,
2365 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2366 es->y_offset = nyoff;
2367 es->x_offset += dx;
2369 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2370 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
2371 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2372 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2373 return TRUE;
2377 /*********************************************************************
2379 * EM_POSFROMCHAR
2382 static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT index, BOOL after_wrap)
2384 INT len = strlen(es->text);
2385 INT l;
2386 INT li;
2387 INT x;
2388 INT y = 0;
2389 HDC dc;
2390 HFONT old_font = 0;
2391 SIZE size;
2393 index = min(index, len);
2394 dc = GetDC(wnd->hwndSelf);
2395 if (es->font)
2396 old_font = SelectObject(dc, es->font);
2397 if (es->style & ES_MULTILINE) {
2398 l = EDIT_EM_LineFromChar(wnd, es, index);
2399 y = (l - es->y_offset) * es->line_height;
2400 li = EDIT_EM_LineIndex(wnd, es, l);
2401 if (after_wrap && (li == index) && l) {
2402 INT l2 = l - 1;
2403 LINEDEF *line_def = es->first_line_def;
2404 while (l2) {
2405 line_def = line_def->next;
2406 l2--;
2408 if (line_def->ending == END_WRAP) {
2409 l--;
2410 y -= es->line_height;
2411 li = EDIT_EM_LineIndex(wnd, es, l);
2414 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2415 es->tabs_count, es->tabs)) - es->x_offset;
2416 } else {
2417 LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
2418 if (index < es->x_offset) {
2419 GetTextExtentPoint32A(dc, text + index,
2420 es->x_offset - index, &size);
2421 x = -size.cx;
2422 } else {
2423 GetTextExtentPoint32A(dc, text + es->x_offset,
2424 index - es->x_offset, &size);
2425 x = size.cx;
2427 y = 0;
2428 if (es->style & ES_PASSWORD)
2429 HeapFree(es->heap, 0 ,text);
2431 x += es->format_rect.left;
2432 y += es->format_rect.top;
2433 if (es->font)
2434 SelectObject(dc, old_font);
2435 ReleaseDC(wnd->hwndSelf, dc);
2436 return MAKELONG((INT16)x, (INT16)y);
2440 /*********************************************************************
2442 * EM_REPLACESEL
2444 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2447 static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace, BOOL send_update)
2449 INT strl = strlen(lpsz_replace);
2450 INT tl = strlen(es->text);
2451 INT utl;
2452 UINT s;
2453 UINT e;
2454 INT i;
2455 LPSTR p;
2457 s = es->selection_start;
2458 e = es->selection_end;
2460 if ((s == e) && !strl)
2461 return;
2463 ORDER_UINT(s, e);
2465 if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
2466 return;
2468 if (e != s) {
2469 /* there is something to be deleted */
2470 if (can_undo) {
2471 utl = strlen(es->undo_text);
2472 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2473 /* undo-buffer is extended to the right */
2474 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2475 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2476 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2477 /* undo-buffer is extended to the left */
2478 EDIT_MakeUndoFit(wnd, es, utl + e - s);
2479 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2480 p[e - s] = p[0];
2481 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2482 p[i] = (es->text + s)[i];
2483 es->undo_position = s;
2484 } else {
2485 /* new undo-buffer */
2486 EDIT_MakeUndoFit(wnd, es, e - s);
2487 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2488 es->undo_position = s;
2490 /* any deletion makes the old insertion-undo invalid */
2491 es->undo_insert_count = 0;
2492 } else
2493 EDIT_EM_EmptyUndoBuffer(wnd, es);
2495 /* now delete */
2496 strcpy(es->text + s, es->text + e);
2498 if (strl) {
2499 /* there is an insertion */
2500 if (can_undo) {
2501 if ((s == es->undo_position) ||
2502 ((es->undo_insert_count) &&
2503 (s == es->undo_position + es->undo_insert_count)))
2505 * insertion is new and at delete position or
2506 * an extension to either left or right
2508 es->undo_insert_count += strl;
2509 else {
2510 /* new insertion undo */
2511 es->undo_position = s;
2512 es->undo_insert_count = strl;
2513 /* new insertion makes old delete-buffer invalid */
2514 *es->undo_text = '\0';
2516 } else
2517 EDIT_EM_EmptyUndoBuffer(wnd, es);
2519 /* now insert */
2520 tl = strlen(es->text);
2521 for (p = es->text + tl ; p >= es->text + s ; p--)
2522 p[strl] = p[0];
2523 for (i = 0 , p = es->text + s ; i < strl ; i++)
2524 p[i] = lpsz_replace[i];
2525 if(es->style & ES_UPPERCASE)
2526 CharUpperBuffA(p, strl);
2527 else if(es->style & ES_LOWERCASE)
2528 CharLowerBuffA(p, strl);
2529 s += strl;
2531 /* FIXME: really inefficient */
2532 if (es->style & ES_MULTILINE)
2533 EDIT_BuildLineDefs_ML(wnd, es);
2535 EDIT_EM_SetSel(wnd, es, s, s, FALSE);
2536 es->flags |= EF_MODIFIED;
2537 if (send_update) es->flags |= EF_UPDATE;
2538 EDIT_EM_ScrollCaret(wnd, es);
2540 /* FIXME: really inefficient */
2541 EDIT_UpdateText(wnd, NULL, TRUE);
2545 /*********************************************************************
2547 * EM_SCROLL
2550 static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT action)
2552 INT dy;
2554 if (!(es->style & ES_MULTILINE))
2555 return (LRESULT)FALSE;
2557 dy = 0;
2559 switch (action) {
2560 case SB_LINEUP:
2561 if (es->y_offset)
2562 dy = -1;
2563 break;
2564 case SB_LINEDOWN:
2565 if (es->y_offset < es->line_count - 1)
2566 dy = 1;
2567 break;
2568 case SB_PAGEUP:
2569 if (es->y_offset)
2570 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2571 break;
2572 case SB_PAGEDOWN:
2573 if (es->y_offset < es->line_count - 1)
2574 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2575 break;
2576 default:
2577 return (LRESULT)FALSE;
2579 if (dy) {
2580 EDIT_EM_LineScroll(wnd, es, 0, dy);
2581 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
2583 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2587 /*********************************************************************
2589 * EM_SCROLLCARET
2592 static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
2594 if (es->style & ES_MULTILINE) {
2595 INT l;
2596 INT li;
2597 INT vlc;
2598 INT ww;
2599 INT cw = es->char_width;
2600 INT x;
2601 INT dy = 0;
2602 INT dx = 0;
2604 l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
2605 li = EDIT_EM_LineIndex(wnd, es, l);
2606 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2607 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2608 if (l >= es->y_offset + vlc)
2609 dy = l - vlc + 1 - es->y_offset;
2610 if (l < es->y_offset)
2611 dy = l - es->y_offset;
2612 ww = es->format_rect.right - es->format_rect.left;
2613 if (x < es->format_rect.left)
2614 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2615 if (x > es->format_rect.right)
2616 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2617 if (dy || dx)
2618 EDIT_EM_LineScroll(wnd, es, dx, dy);
2619 } else {
2620 INT x;
2621 INT goal;
2622 INT format_width;
2624 if (!(es->style & ES_AUTOHSCROLL))
2625 return;
2627 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2628 format_width = es->format_rect.right - es->format_rect.left;
2629 if (x < es->format_rect.left) {
2630 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2631 do {
2632 es->x_offset--;
2633 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2634 } while ((x < goal) && es->x_offset);
2635 /* FIXME: use ScrollWindow() somehow to improve performance */
2636 EDIT_UpdateText(wnd, NULL, TRUE);
2637 } else if (x > es->format_rect.right) {
2638 INT x_last;
2639 INT len = strlen(es->text);
2640 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2641 do {
2642 es->x_offset++;
2643 x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
2644 x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
2645 } while ((x > goal) && (x_last > es->format_rect.right));
2646 /* FIXME: use ScrollWindow() somehow to improve performance */
2647 EDIT_UpdateText(wnd, NULL, TRUE);
2653 /*********************************************************************
2655 * EM_SETHANDLE
2657 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2660 static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL hloc)
2662 if (!(es->style & ES_MULTILINE))
2663 return;
2665 if (!hloc) {
2666 WARN("called with NULL handle\n");
2667 return;
2670 EDIT_UnlockBuffer(wnd, es, TRUE);
2672 * old buffer is freed by caller, unless
2673 * it is still in our own heap. (in that case
2674 * we free it, correcting the buggy caller.)
2676 if (es->text)
2677 HeapFree(es->heap, 0, es->text);
2679 es->hloc16 = (HLOCAL16)NULL;
2680 es->hloc32 = hloc;
2681 es->text = NULL;
2682 es->buffer_size = LocalSize(es->hloc32) - 1;
2683 EDIT_LockBuffer(wnd, es);
2685 es->x_offset = es->y_offset = 0;
2686 es->selection_start = es->selection_end = 0;
2687 EDIT_EM_EmptyUndoBuffer(wnd, es);
2688 es->flags &= ~EF_MODIFIED;
2689 es->flags &= ~EF_UPDATE;
2690 EDIT_BuildLineDefs_ML(wnd, es);
2691 EDIT_UpdateText(wnd, NULL, TRUE);
2692 EDIT_EM_ScrollCaret(wnd, es);
2696 /*********************************************************************
2698 * EM_SETHANDLE16
2700 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2703 static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
2705 if (!(es->style & ES_MULTILINE))
2706 return;
2708 if (!hloc) {
2709 WARN("called with NULL handle\n");
2710 return;
2713 EDIT_UnlockBuffer(wnd, es, TRUE);
2715 * old buffer is freed by caller, unless
2716 * it is still in our own heap. (in that case
2717 * we free it, correcting the buggy caller.)
2719 if (es->text)
2720 HeapFree(es->heap, 0, es->text);
2722 es->hloc16 = hloc;
2723 es->hloc32 = (HLOCAL)NULL;
2724 es->text = NULL;
2725 es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
2726 EDIT_LockBuffer(wnd, es);
2728 es->x_offset = es->y_offset = 0;
2729 es->selection_start = es->selection_end = 0;
2730 EDIT_EM_EmptyUndoBuffer(wnd, es);
2731 es->flags &= ~EF_MODIFIED;
2732 es->flags &= ~EF_UPDATE;
2733 EDIT_BuildLineDefs_ML(wnd, es);
2734 EDIT_UpdateText(wnd, NULL, TRUE);
2735 EDIT_EM_ScrollCaret(wnd, es);
2739 /*********************************************************************
2741 * EM_SETLIMITTEXT
2743 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2744 * However, the windows version is not complied to yet in all of edit.c
2747 static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT limit)
2749 if (es->style & ES_MULTILINE) {
2750 if (limit)
2751 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
2752 else
2753 es->buffer_limit = BUFLIMIT_MULTI;
2754 } else {
2755 if (limit)
2756 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
2757 else
2758 es->buffer_limit = BUFLIMIT_SINGLE;
2763 /*********************************************************************
2765 * EM_SETMARGINS
2767 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2768 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2769 * of the char's width, according to the new docs.
2772 static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT action,
2773 INT left, INT right)
2775 if (action & EC_LEFTMARGIN) {
2776 if (left != EC_USEFONTINFO)
2777 es->left_margin = left;
2778 else
2779 es->left_margin = es->char_width / 3;
2782 if (action & EC_RIGHTMARGIN) {
2783 if (right != EC_USEFONTINFO)
2784 es->right_margin = right;
2785 else
2786 es->right_margin = es->char_width / 3;
2788 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2792 /*********************************************************************
2794 * EM_SETPASSWORDCHAR
2797 static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
2799 if (es->style & ES_MULTILINE)
2800 return;
2802 if (es->password_char == c)
2803 return;
2805 es->password_char = c;
2806 if (c) {
2807 wnd->dwStyle |= ES_PASSWORD;
2808 es->style |= ES_PASSWORD;
2809 } else {
2810 wnd->dwStyle &= ~ES_PASSWORD;
2811 es->style &= ~ES_PASSWORD;
2813 EDIT_UpdateText(wnd, NULL, TRUE);
2817 /*********************************************************************
2819 * EDIT_EM_SetSel
2821 * note: unlike the specs say: the order of start and end
2822 * _is_ preserved in Windows. (i.e. start can be > end)
2823 * In other words: this handler is OK
2826 static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2828 UINT old_start = es->selection_start;
2829 UINT old_end = es->selection_end;
2830 UINT len = strlen(es->text);
2832 if (start == -1) {
2833 start = es->selection_end;
2834 end = es->selection_end;
2835 } else {
2836 start = min(start, len);
2837 end = min(end, len);
2839 es->selection_start = start;
2840 es->selection_end = end;
2841 if (after_wrap)
2842 es->flags |= EF_AFTER_WRAP;
2843 else
2844 es->flags &= ~EF_AFTER_WRAP;
2845 if (es->flags & EF_FOCUSED)
2846 EDIT_SetCaretPos(wnd, es, end, after_wrap);
2847 /* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
2848 ORDER_UINT(start, end);
2849 ORDER_UINT(end, old_end);
2850 ORDER_UINT(start, old_start);
2851 ORDER_UINT(old_start, old_end);
2852 if (end != old_start)
2855 * One can also do
2856 * ORDER_UINT32(end, old_start);
2857 * EDIT_InvalidateText(wnd, es, start, end);
2858 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2859 * in place of the following if statement.
2861 if (old_start > end )
2863 EDIT_InvalidateText(wnd, es, start, end);
2864 EDIT_InvalidateText(wnd, es, old_start, old_end);
2866 else
2868 EDIT_InvalidateText(wnd, es, start, old_start);
2869 EDIT_InvalidateText(wnd, es, end, old_end);
2872 else EDIT_InvalidateText(wnd, es, start, old_end);
2876 /*********************************************************************
2878 * EM_SETTABSTOPS
2881 static BOOL EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT count, LPINT tabs)
2883 if (!(es->style & ES_MULTILINE))
2884 return FALSE;
2885 if (es->tabs)
2886 HeapFree(es->heap, 0, es->tabs);
2887 es->tabs_count = count;
2888 if (!count)
2889 es->tabs = NULL;
2890 else {
2891 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2892 memcpy(es->tabs, tabs, count * sizeof(INT));
2894 return TRUE;
2898 /*********************************************************************
2900 * EM_SETTABSTOPS16
2903 static BOOL EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT count, LPINT16 tabs)
2905 if (!(es->style & ES_MULTILINE))
2906 return FALSE;
2907 if (es->tabs)
2908 HeapFree(es->heap, 0, es->tabs);
2909 es->tabs_count = count;
2910 if (!count)
2911 es->tabs = NULL;
2912 else {
2913 INT i;
2914 es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT));
2915 for (i = 0 ; i < count ; i++)
2916 es->tabs[i] = *tabs++;
2918 return TRUE;
2922 /*********************************************************************
2924 * EM_SETWORDBREAKPROC
2927 static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2929 if (es->word_break_proc32A == wbp)
2930 return;
2932 es->word_break_proc32A = wbp;
2933 es->word_break_proc16 = NULL;
2934 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2935 EDIT_BuildLineDefs_ML(wnd, es);
2936 EDIT_UpdateText(wnd, NULL, TRUE);
2941 /*********************************************************************
2943 * EM_SETWORDBREAKPROC16
2946 static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
2948 if (es->word_break_proc16 == wbp)
2949 return;
2951 es->word_break_proc32A = NULL;
2952 es->word_break_proc16 = wbp;
2953 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2954 EDIT_BuildLineDefs_ML(wnd, es);
2955 EDIT_UpdateText(wnd, NULL, TRUE);
2960 /*********************************************************************
2962 * EM_UNDO / WM_UNDO
2965 static BOOL EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
2967 INT ulength = strlen(es->undo_text);
2968 LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
2970 strcpy(utext, es->undo_text);
2972 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2973 es->undo_insert_count, utext);
2975 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2976 EDIT_EM_EmptyUndoBuffer(wnd, es);
2977 EDIT_EM_ReplaceSel(wnd, es, TRUE, utext, TRUE);
2978 EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2979 HeapFree(es->heap, 0, utext);
2981 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2982 es->undo_insert_count, es->undo_text);
2984 if (es->flags & EF_UPDATE) {
2985 es->flags &= ~EF_UPDATE;
2986 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
2989 return TRUE;
2993 /*********************************************************************
2995 * WM_CHAR
2998 static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
3000 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
3001 switch (c) {
3002 case '\r':
3003 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3004 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3005 break;
3006 case '\n':
3007 if (es->style & ES_MULTILINE) {
3008 if (es->style & ES_READONLY) {
3009 EDIT_MoveHome(wnd, es, FALSE);
3010 EDIT_MoveDown_ML(wnd, es, FALSE);
3011 } else {
3012 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n", TRUE);
3013 if (es->flags & EF_UPDATE) {
3014 es->flags &= ~EF_UPDATE;
3015 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3019 break;
3020 case '\t':
3021 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3023 EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t", TRUE);
3024 if (es->flags & EF_UPDATE) {
3025 es->flags &= ~EF_UPDATE;
3026 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3029 break;
3030 case VK_BACK:
3031 if (!(es->style & ES_READONLY) && !control) {
3032 if (es->selection_start != es->selection_end)
3033 EDIT_WM_Clear(wnd, es);
3034 else {
3035 /* delete character left of caret */
3036 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3037 EDIT_MoveBackward(wnd, es, TRUE);
3038 EDIT_WM_Clear(wnd, es);
3041 break;
3042 case 0x03: /* ^C */
3043 SendMessageA(wnd->hwndSelf, WM_COPY, 0, 0);
3044 break;
3045 case 0x16: /* ^V */
3046 SendMessageA(wnd->hwndSelf, WM_PASTE, 0, 0);
3047 break;
3048 case 0x18: /* ^X */
3049 SendMessageA(wnd->hwndSelf, WM_CUT, 0, 0);
3050 break;
3052 default:
3053 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
3054 char str[2];
3055 str[0] = c;
3056 str[1] = '\0';
3057 EDIT_EM_ReplaceSel(wnd, es, TRUE, str, TRUE);
3058 if (es->flags & EF_UPDATE) {
3059 es->flags &= ~EF_UPDATE;
3060 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3063 break;
3068 /*********************************************************************
3070 * WM_COMMAND
3073 static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT code, INT id, HWND control)
3075 if (code || control)
3076 return;
3078 switch (id) {
3079 case EM_UNDO:
3080 EDIT_EM_Undo(wnd, es);
3081 break;
3082 case WM_CUT:
3083 EDIT_WM_Cut(wnd, es);
3084 break;
3085 case WM_COPY:
3086 EDIT_WM_Copy(wnd, es);
3087 break;
3088 case WM_PASTE:
3089 EDIT_WM_Paste(wnd, es);
3090 break;
3091 case WM_CLEAR:
3092 EDIT_WM_Clear(wnd, es);
3093 break;
3094 case EM_SETSEL:
3095 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
3096 EDIT_EM_ScrollCaret(wnd, es);
3097 break;
3098 default:
3099 ERR("unknown menu item, please report\n");
3100 break;
3105 /*********************************************************************
3107 * WM_CONTEXTMENU
3109 * Note: the resource files resource/sysres_??.rc cannot define a
3110 * single popup menu. Hence we use a (dummy) menubar
3111 * containing the single popup menu as its first item.
3113 * FIXME: the message identifiers have been chosen arbitrarily,
3114 * hence we use MF_BYPOSITION.
3115 * We might as well use the "real" values (anybody knows ?)
3116 * The menu definition is in resources/sysres_??.rc.
3117 * Once these are OK, we better use MF_BYCOMMAND here
3118 * (as we do in EDIT_WM_Command()).
3121 static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND hwnd, INT x, INT y)
3123 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3124 HMENU popup = GetSubMenu(menu, 0);
3125 UINT start = es->selection_start;
3126 UINT end = es->selection_end;
3128 ORDER_UINT(start, end);
3130 /* undo */
3131 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
3132 /* cut */
3133 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3134 /* copy */
3135 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3136 /* paste */
3137 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
3138 /* delete */
3139 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
3140 /* select all */
3141 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlen(es->text)) ? MF_ENABLED : MF_GRAYED));
3143 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
3144 DestroyMenu(menu);
3148 /*********************************************************************
3150 * WM_COPY
3153 static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
3155 INT s = es->selection_start;
3156 INT e = es->selection_end;
3157 HGLOBAL hdst;
3158 LPSTR dst;
3160 if (e == s)
3161 return;
3162 ORDER_INT(s, e);
3163 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3164 dst = GlobalLock(hdst);
3165 lstrcpynA(dst, es->text + s, e - s + 1);
3166 GlobalUnlock(hdst);
3167 OpenClipboard(wnd->hwndSelf);
3168 EmptyClipboard();
3169 SetClipboardData(CF_TEXT, hdst);
3170 CloseClipboard();
3174 /*********************************************************************
3176 * WM_CREATE
3179 static LRESULT EDIT_WM_Create(WND *wnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3182 * To initialize some final structure members, we call some helper
3183 * functions. However, since the EDITSTATE is not consistent (i.e.
3184 * not fully initialized), we should be very careful which
3185 * functions can be called, and in what order.
3187 EDIT_WM_SetFont(wnd, es, 0, FALSE);
3188 EDIT_EM_EmptyUndoBuffer(wnd, es);
3190 if (cs->lpszName && *(cs->lpszName) != '\0') {
3191 EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName, TRUE);
3192 /* if we insert text to the editline, the text scrolls out
3193 * of the window, as the caret is placed after the insert
3194 * pos normally; thus we reset es->selection... to 0 and
3195 * update caret
3197 es->selection_start = es->selection_end = 0;
3198 EDIT_EM_ScrollCaret(wnd, es);
3199 if (es->flags & EF_UPDATE) {
3200 es->flags &= ~EF_UPDATE;
3201 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3204 return 0;
3208 /*********************************************************************
3210 * WM_DESTROY
3213 static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
3215 if (es->hloc32) {
3216 while (LocalUnlock(es->hloc32)) ;
3217 LocalFree(es->hloc32);
3219 if (es->hloc16) {
3220 while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
3221 LOCAL_Free(wnd->hInstance, es->hloc16);
3223 HeapDestroy(es->heap);
3224 HeapFree(GetProcessHeap(), 0, es);
3225 *(EDITSTATE **)wnd->wExtra = NULL;
3229 /*********************************************************************
3231 * WM_ERASEBKGND
3234 static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC dc)
3236 HBRUSH brush;
3237 RECT rc;
3239 if ( get_app_version() >= 0x40000 &&(
3240 !es->bEnableState || (es->style & ES_READONLY)))
3241 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3242 else
3243 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(wnd, dc);
3245 if (!brush)
3246 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3248 GetClientRect(wnd->hwndSelf, &rc);
3249 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3250 GetClipBox(dc, &rc);
3252 * FIXME: specs say that we should UnrealizeObject() the brush,
3253 * but the specs of UnrealizeObject() say that we shouldn't
3254 * unrealize a stock object. The default brush that
3255 * DefWndProc() returns is ... a stock object.
3257 FillRect(dc, &rc, brush);
3258 return -1;
3262 /*********************************************************************
3264 * WM_GETTEXT
3267 static INT EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT count, LPSTR text)
3269 lstrcpynA(text, es->text, count);
3270 return strlen(text);
3274 /*********************************************************************
3276 * EDIT_HScroll_Hack
3278 * 16 bit notepad needs this. Actually it is not _our_ hack,
3279 * it is notepad's. Notepad is sending us scrollbar messages with
3280 * undocumented parameters without us even having a scrollbar ... !?!?
3283 static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3285 INT dx = 0;
3286 INT fw = es->format_rect.right - es->format_rect.left;
3287 LRESULT ret = 0;
3289 if (!(es->flags & EF_HSCROLL_HACK)) {
3290 ERR("hacked WM_HSCROLL handler invoked\n");
3291 ERR(" if you are _not_ running 16 bit notepad, please report\n");
3292 ERR(" (this message is only displayed once per edit control)\n");
3293 es->flags |= EF_HSCROLL_HACK;
3296 switch (action) {
3297 case SB_LINELEFT:
3298 if (es->x_offset)
3299 dx = -es->char_width;
3300 break;
3301 case SB_LINERIGHT:
3302 if (es->x_offset < es->text_width)
3303 dx = es->char_width;
3304 break;
3305 case SB_PAGELEFT:
3306 if (es->x_offset)
3307 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3308 break;
3309 case SB_PAGERIGHT:
3310 if (es->x_offset < es->text_width)
3311 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3312 break;
3313 case SB_LEFT:
3314 if (es->x_offset)
3315 dx = -es->x_offset;
3316 break;
3317 case SB_RIGHT:
3318 if (es->x_offset < es->text_width)
3319 dx = es->text_width - es->x_offset;
3320 break;
3321 case SB_THUMBTRACK:
3322 es->flags |= EF_HSCROLL_TRACK;
3323 dx = pos * es->text_width / 100 - es->x_offset;
3324 break;
3325 case SB_THUMBPOSITION:
3326 es->flags &= ~EF_HSCROLL_TRACK;
3327 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3328 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3329 break;
3330 case SB_ENDSCROLL:
3331 break;
3334 * FIXME : the next two are undocumented !
3335 * Are we doing the right thing ?
3336 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
3337 * although it's also a regular control message.
3339 case EM_GETTHUMB16:
3340 ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
3341 break;
3342 case EM_LINESCROLL16:
3343 dx = pos;
3344 break;
3346 default:
3347 ERR("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3348 return 0;
3350 if (dx)
3351 EDIT_EM_LineScroll(wnd, es, dx, 0);
3352 return ret;
3356 /*********************************************************************
3358 * WM_HSCROLL
3361 static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3363 INT dx;
3364 INT fw;
3366 if (!(es->style & ES_MULTILINE))
3367 return 0;
3369 if (!(es->style & ES_AUTOHSCROLL))
3370 return 0;
3372 if (!(es->style & WS_HSCROLL))
3373 return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
3375 dx = 0;
3376 fw = es->format_rect.right - es->format_rect.left;
3377 switch (action) {
3378 case SB_LINELEFT:
3379 if (es->x_offset)
3380 dx = -es->char_width;
3381 break;
3382 case SB_LINERIGHT:
3383 if (es->x_offset < es->text_width)
3384 dx = es->char_width;
3385 break;
3386 case SB_PAGELEFT:
3387 if (es->x_offset)
3388 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3389 break;
3390 case SB_PAGERIGHT:
3391 if (es->x_offset < es->text_width)
3392 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3393 break;
3394 case SB_LEFT:
3395 if (es->x_offset)
3396 dx = -es->x_offset;
3397 break;
3398 case SB_RIGHT:
3399 if (es->x_offset < es->text_width)
3400 dx = es->text_width - es->x_offset;
3401 break;
3402 case SB_THUMBTRACK:
3403 es->flags |= EF_HSCROLL_TRACK;
3404 dx = pos - es->x_offset;
3405 break;
3406 case SB_THUMBPOSITION:
3407 es->flags &= ~EF_HSCROLL_TRACK;
3408 if (!(dx = pos - es->x_offset)) {
3409 SetScrollPos(wnd->hwndSelf, SB_HORZ, pos, TRUE);
3410 EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
3412 break;
3413 case SB_ENDSCROLL:
3414 break;
3416 default:
3417 ERR("undocumented WM_HSCROLL parameter, please report\n");
3418 return 0;
3420 if (dx)
3421 EDIT_EM_LineScroll(wnd, es, dx, 0);
3422 return 0;
3426 /*********************************************************************
3428 * EDIT_CheckCombo
3431 static BOOL EDIT_CheckCombo(WND *wnd, EDITSTATE *es, UINT msg, INT key, DWORD key_data)
3433 HWND hLBox = es->hwndListBox;
3434 HWND hCombo;
3435 BOOL bDropped;
3436 int nEUI;
3438 if (!hLBox)
3439 return FALSE;
3441 hCombo = wnd->parent->hwndSelf;
3442 bDropped = TRUE;
3443 nEUI = 0;
3445 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3446 wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3448 if (key == VK_UP || key == VK_DOWN)
3450 if (SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0))
3451 nEUI = 1;
3453 if (msg == WM_KEYDOWN || nEUI)
3454 bDropped = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3457 switch (msg)
3459 case WM_KEYDOWN:
3460 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3462 /* make sure ComboLBox pops up */
3463 SendMessageA(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3464 key = VK_F4;
3465 nEUI = 2;
3468 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3469 break;
3471 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3472 if (nEUI)
3473 SendMessageA(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
3474 else
3475 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
3476 break;
3479 if(nEUI == 2)
3480 SendMessageA(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3482 return TRUE;
3486 /*********************************************************************
3488 * WM_KEYDOWN
3490 * Handling of special keys that don't produce a WM_CHAR
3491 * (i.e. non-printable keys) & Backspace & Delete
3494 static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
3496 BOOL shift;
3497 BOOL control;
3499 if (GetKeyState(VK_MENU) & 0x8000)
3500 return 0;
3502 shift = GetKeyState(VK_SHIFT) & 0x8000;
3503 control = GetKeyState(VK_CONTROL) & 0x8000;
3505 switch (key) {
3506 case VK_F4:
3507 case VK_UP:
3508 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data) || key == VK_F4)
3509 break;
3511 /* fall through */
3512 case VK_LEFT:
3513 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3514 EDIT_MoveUp_ML(wnd, es, shift);
3515 else
3516 if (control)
3517 EDIT_MoveWordBackward(wnd, es, shift);
3518 else
3519 EDIT_MoveBackward(wnd, es, shift);
3520 break;
3521 case VK_DOWN:
3522 if (EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data))
3523 break;
3524 /* fall through */
3525 case VK_RIGHT:
3526 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3527 EDIT_MoveDown_ML(wnd, es, shift);
3528 else if (control)
3529 EDIT_MoveWordForward(wnd, es, shift);
3530 else
3531 EDIT_MoveForward(wnd, es, shift);
3532 break;
3533 case VK_HOME:
3534 EDIT_MoveHome(wnd, es, shift);
3535 break;
3536 case VK_END:
3537 EDIT_MoveEnd(wnd, es, shift);
3538 break;
3539 case VK_PRIOR:
3540 if (es->style & ES_MULTILINE)
3541 EDIT_MovePageUp_ML(wnd, es, shift);
3542 else
3543 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data);
3544 break;
3545 case VK_NEXT:
3546 if (es->style & ES_MULTILINE)
3547 EDIT_MovePageDown_ML(wnd, es, shift);
3548 else
3549 EDIT_CheckCombo(wnd, es, WM_KEYDOWN, key, key_data);
3550 break;
3551 case VK_DELETE:
3552 if (!(es->style & ES_READONLY) && !(shift && control)) {
3553 if (es->selection_start != es->selection_end) {
3554 if (shift)
3555 EDIT_WM_Cut(wnd, es);
3556 else
3557 EDIT_WM_Clear(wnd, es);
3558 } else {
3559 if (shift) {
3560 /* delete character left of caret */
3561 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3562 EDIT_MoveBackward(wnd, es, TRUE);
3563 EDIT_WM_Clear(wnd, es);
3564 } else if (control) {
3565 /* delete to end of line */
3566 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3567 EDIT_MoveEnd(wnd, es, TRUE);
3568 EDIT_WM_Clear(wnd, es);
3569 } else {
3570 /* delete character right of caret */
3571 EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
3572 EDIT_MoveForward(wnd, es, TRUE);
3573 EDIT_WM_Clear(wnd, es);
3577 break;
3578 case VK_INSERT:
3579 if (shift) {
3580 if (!(es->style & ES_READONLY))
3581 EDIT_WM_Paste(wnd, es);
3582 } else if (control)
3583 EDIT_WM_Copy(wnd, es);
3584 break;
3585 case VK_RETURN:
3586 /* If the edit doesn't want the return send a message to the default object */
3587 if(!(es->style & ES_WANTRETURN))
3589 HWND hwndParent = GetParent(wnd->hwndSelf);
3590 DWORD dw = SendMessage16( hwndParent, DM_GETDEFID, 0, 0 );
3591 if (HIWORD(dw) == DC_HASDEFID)
3593 SendMessageA( hwndParent, WM_COMMAND,
3594 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3595 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3598 break;
3600 return 0;
3604 /*********************************************************************
3606 * WM_KILLFOCUS
3609 static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND window_getting_focus)
3611 es->flags &= ~EF_FOCUSED;
3612 DestroyCaret();
3613 if(!(es->style & ES_NOHIDESEL))
3614 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3615 EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3616 return 0;
3620 /*********************************************************************
3622 * WM_LBUTTONDBLCLK
3624 * The caret position has been set on the WM_LBUTTONDOWN message
3627 static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3629 INT s;
3630 INT e = es->selection_end;
3631 INT l;
3632 INT li;
3633 INT ll;
3635 if (!(es->flags & EF_FOCUSED))
3636 return 0;
3638 l = EDIT_EM_LineFromChar(wnd, es, e);
3639 li = EDIT_EM_LineIndex(wnd, es, l);
3640 ll = EDIT_EM_LineLength(wnd, es, e);
3641 s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
3642 e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
3643 EDIT_EM_SetSel(wnd, es, s, e, FALSE);
3644 EDIT_EM_ScrollCaret(wnd, es);
3645 return 0;
3649 /*********************************************************************
3651 * WM_LBUTTONDOWN
3654 static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3656 INT e;
3657 BOOL after_wrap;
3659 if (!(es->flags & EF_FOCUSED))
3660 return 0;
3662 es->bCaptureState = TRUE;
3663 SetCapture(wnd->hwndSelf);
3664 EDIT_ConfinePoint(wnd, es, &x, &y);
3665 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3666 EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3667 EDIT_EM_ScrollCaret(wnd, es);
3668 es->region_posx = es->region_posy = 0;
3669 SetTimer(wnd->hwndSelf, 0, 100, NULL);
3670 return 0;
3674 /*********************************************************************
3676 * WM_LBUTTONUP
3679 static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3681 if (es->bCaptureState && GetCapture() == wnd->hwndSelf) {
3682 KillTimer(wnd->hwndSelf, 0);
3683 ReleaseCapture();
3685 es->bCaptureState = FALSE;
3686 return 0;
3690 /*********************************************************************
3692 * WM_MBUTTONDOWN
3695 static LRESULT EDIT_WM_MButtonDown(WND *wnd)
3697 SendMessageA(wnd->hwndSelf,WM_PASTE,0,0);
3698 return 0;
3702 /*********************************************************************
3704 * WM_MOUSEMOVE
3707 static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3709 INT e;
3710 BOOL after_wrap;
3711 INT prex, prey;
3713 if (GetCapture() != wnd->hwndSelf)
3714 return 0;
3717 * FIXME: gotta do some scrolling if outside client
3718 * area. Maybe reset the timer ?
3720 prex = x; prey = y;
3721 EDIT_ConfinePoint(wnd, es, &x, &y);
3722 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3723 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3724 e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
3725 EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
3726 return 0;
3730 /*********************************************************************
3732 * WM_NCCREATE
3735 static LRESULT EDIT_WM_NCCreate(WND *wnd, LPCREATESTRUCTA cs)
3737 EDITSTATE *es;
3739 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3740 return FALSE;
3741 *(EDITSTATE **)wnd->wExtra = es;
3744 * Note: since the EDITSTATE has not been fully initialized yet,
3745 * we can't use any API calls that may send
3746 * WM_XXX messages before WM_NCCREATE is completed.
3749 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3750 return FALSE;
3751 es->style = cs->style;
3753 es->bEnableState = !(cs->style & WS_DISABLED);
3756 * In Win95 look and feel, the WS_BORDER style is replaced by the
3757 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3758 * control a non client area.
3760 if (TWEAK_WineLook != WIN31_LOOK)
3762 if (es->style & WS_BORDER)
3764 es->style &= ~WS_BORDER;
3765 wnd->dwStyle &= ~WS_BORDER;
3766 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
3769 else
3771 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
3772 wnd->dwStyle &= ~WS_BORDER;
3775 if (es->style & ES_COMBO)
3776 es->hwndListBox = GetDlgItem(cs->hwndParent, ID_CB_LISTBOX);
3778 if (es->style & ES_MULTILINE) {
3779 es->buffer_size = BUFSTART_MULTI;
3780 es->buffer_limit = BUFLIMIT_MULTI;
3781 if (es->style & WS_VSCROLL)
3782 es->style |= ES_AUTOVSCROLL;
3783 if (es->style & WS_HSCROLL)
3784 es->style |= ES_AUTOHSCROLL;
3785 es->style &= ~ES_PASSWORD;
3786 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3787 if (es->style & ES_RIGHT)
3788 es->style &= ~ES_CENTER;
3789 es->style &= ~WS_HSCROLL;
3790 es->style &= ~ES_AUTOHSCROLL;
3793 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3794 es->style |= ES_AUTOVSCROLL;
3795 } else {
3796 es->buffer_size = BUFSTART_SINGLE;
3797 es->buffer_limit = BUFLIMIT_SINGLE;
3798 es->style &= ~ES_CENTER;
3799 es->style &= ~ES_RIGHT;
3800 es->style &= ~WS_HSCROLL;
3801 es->style &= ~WS_VSCROLL;
3802 es->style &= ~ES_AUTOVSCROLL;
3803 es->style &= ~ES_WANTRETURN;
3804 if (es->style & ES_UPPERCASE) {
3805 es->style &= ~ES_LOWERCASE;
3806 es->style &= ~ES_NUMBER;
3807 } else if (es->style & ES_LOWERCASE)
3808 es->style &= ~ES_NUMBER;
3809 if (es->style & ES_PASSWORD)
3810 es->password_char = '*';
3812 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3813 es->style |= ES_AUTOHSCROLL;
3815 if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3816 return FALSE;
3817 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3818 if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3819 return FALSE;
3820 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3821 *es->text = '\0';
3822 if (es->style & ES_MULTILINE)
3823 if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3824 return FALSE;
3825 es->line_count = 1;
3827 return TRUE;
3830 /*********************************************************************
3832 * WM_PAINT
3835 static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es, WPARAM wParam)
3837 PAINTSTRUCT ps;
3838 INT i;
3839 HDC dc;
3840 HFONT old_font = 0;
3841 RECT rc;
3842 RECT rcLine;
3843 RECT rcRgn;
3844 BOOL rev = es->bEnableState &&
3845 ((es->flags & EF_FOCUSED) ||
3846 (es->style & ES_NOHIDESEL));
3847 if (!wParam)
3848 dc = BeginPaint(wnd->hwndSelf, &ps);
3849 else
3850 dc = (HDC) wParam;
3851 if(es->style & WS_BORDER) {
3852 GetClientRect(wnd->hwndSelf, &rc);
3853 if(es->style & ES_MULTILINE) {
3854 if(es->style & WS_HSCROLL) rc.bottom++;
3855 if(es->style & WS_VSCROLL) rc.right++;
3857 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
3859 IntersectClipRect(dc, es->format_rect.left,
3860 es->format_rect.top,
3861 es->format_rect.right,
3862 es->format_rect.bottom);
3863 if (es->style & ES_MULTILINE) {
3864 GetClientRect(wnd->hwndSelf, &rc);
3865 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3867 if (es->font)
3868 old_font = SelectObject(dc, es->font);
3869 if ( get_app_version() >= 0x40000 &&(
3870 !es->bEnableState || (es->style & ES_READONLY)))
3871 EDIT_SEND_CTLCOLORSTATIC(wnd, dc);
3872 else
3873 EDIT_SEND_CTLCOLOR(wnd, dc);
3875 if (!es->bEnableState)
3876 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3877 GetClipBox(dc, &rcRgn);
3878 if (es->style & ES_MULTILINE) {
3879 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3880 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3881 EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
3882 if (IntersectRect(&rc, &rcRgn, &rcLine))
3883 EDIT_PaintLine(wnd, es, dc, i, rev);
3885 } else {
3886 EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
3887 if (IntersectRect(&rc, &rcRgn, &rcLine))
3888 EDIT_PaintLine(wnd, es, dc, 0, rev);
3890 if (es->font)
3891 SelectObject(dc, old_font);
3892 if (es->flags & EF_FOCUSED)
3893 EDIT_SetCaretPos(wnd, es, es->selection_end,
3894 es->flags & EF_AFTER_WRAP);
3895 if (!wParam)
3896 EndPaint(wnd->hwndSelf, &ps);
3897 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3898 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3899 SCROLLINFO si;
3900 si.cbSize = sizeof(SCROLLINFO);
3901 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3902 si.nMin = 0;
3903 si.nMax = es->line_count + vlc - 2;
3904 si.nPage = vlc;
3905 si.nPos = es->y_offset;
3906 SetScrollInfo(wnd->hwndSelf, SB_VERT, &si, TRUE);
3908 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3909 SCROLLINFO si;
3910 INT fw = es->format_rect.right - es->format_rect.left;
3911 si.cbSize = sizeof(SCROLLINFO);
3912 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3913 si.nMin = 0;
3914 si.nMax = es->text_width + fw - 1;
3915 si.nPage = fw;
3916 si.nPos = es->x_offset;
3917 SetScrollInfo(wnd->hwndSelf, SB_HORZ, &si, TRUE);
3922 /*********************************************************************
3924 * WM_PASTE
3927 static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
3929 HGLOBAL hsrc;
3930 LPSTR src;
3932 OpenClipboard(wnd->hwndSelf);
3933 if ((hsrc = GetClipboardData(CF_TEXT))) {
3934 src = (LPSTR)GlobalLock(hsrc);
3935 EDIT_EM_ReplaceSel(wnd, es, TRUE, src, TRUE);
3936 GlobalUnlock(hsrc);
3938 if (es->flags & EF_UPDATE) {
3939 es->flags &= ~EF_UPDATE;
3940 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
3943 CloseClipboard();
3947 /*********************************************************************
3949 * WM_SETFOCUS
3952 static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND window_losing_focus)
3954 es->flags |= EF_FOCUSED;
3955 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
3956 EDIT_SetCaretPos(wnd, es, es->selection_end,
3957 es->flags & EF_AFTER_WRAP);
3958 if(!(es->style & ES_NOHIDESEL))
3959 EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
3960 ShowCaret(wnd->hwndSelf);
3961 EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
3965 /*********************************************************************
3967 * WM_SETFONT
3969 * With Win95 look the margins are set to default font value unless
3970 * the system font (font == 0) is being set, in which case they are left
3971 * unchanged.
3974 static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT font, BOOL redraw)
3976 TEXTMETRICA tm;
3977 HDC dc;
3978 HFONT old_font = 0;
3979 RECT r;
3981 es->font = font;
3982 dc = GetDC(wnd->hwndSelf);
3983 if (font)
3984 old_font = SelectObject(dc, font);
3985 GetTextMetricsA(dc, &tm);
3986 es->line_height = tm.tmHeight;
3987 es->char_width = tm.tmAveCharWidth;
3988 if (font)
3989 SelectObject(dc, old_font);
3990 ReleaseDC(wnd->hwndSelf, dc);
3991 if (font && (TWEAK_WineLook > WIN31_LOOK))
3992 EDIT_EM_SetMargins(wnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3993 EC_USEFONTINFO, EC_USEFONTINFO);
3995 /* Force the recalculation of the format rect for each font change */
3996 GetClientRect(wnd->hwndSelf, &r);
3997 EDIT_SetRectNP(wnd, es, &r);
3999 if (es->style & ES_MULTILINE)
4000 EDIT_BuildLineDefs_ML(wnd, es);
4002 if (redraw)
4003 EDIT_UpdateText(wnd, NULL, TRUE);
4004 if (es->flags & EF_FOCUSED) {
4005 DestroyCaret();
4006 CreateCaret(wnd->hwndSelf, 0, 2, es->line_height);
4007 EDIT_SetCaretPos(wnd, es, es->selection_end,
4008 es->flags & EF_AFTER_WRAP);
4009 ShowCaret(wnd->hwndSelf);
4014 /*********************************************************************
4016 * WM_SETTEXT
4018 * NOTES
4019 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4020 * The modified flag is reset. No notifications are sent.
4022 * For single-line controls, reception of WM_SETTEXT triggers:
4023 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4026 static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
4028 EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
4029 if (text) {
4030 TRACE("\t'%p'\n", text);
4031 EDIT_EM_ReplaceSel(wnd, es, FALSE, text, !(es->style & ES_MULTILINE));
4032 } else {
4033 TRACE("\t<NULL>\n");
4034 EDIT_EM_ReplaceSel(wnd, es, FALSE, "", !(es->style & ES_MULTILINE));
4036 es->x_offset = 0;
4037 es->flags &= ~EF_MODIFIED;
4038 EDIT_EM_SetSel(wnd, es, 0, 0, FALSE);
4039 EDIT_EM_ScrollCaret(wnd, es);
4041 if (es->flags & EF_UPDATE) {
4042 es->flags &= ~EF_UPDATE;
4043 EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
4048 /*********************************************************************
4050 * WM_SIZE
4053 static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT action, INT width, INT height)
4055 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4056 RECT rc;
4057 SetRect(&rc, 0, 0, width, height);
4058 EDIT_SetRectNP(wnd, es, &rc);
4059 EDIT_UpdateText(wnd, NULL, TRUE);
4064 /*********************************************************************
4066 * WM_SYSKEYDOWN
4069 static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT key, DWORD key_data)
4071 if ((key == VK_BACK) && (key_data & 0x2000)) {
4072 if (EDIT_EM_CanUndo(wnd, es))
4073 EDIT_EM_Undo(wnd, es);
4074 return 0;
4075 } else if (key == VK_UP || key == VK_DOWN) {
4076 if (EDIT_CheckCombo(wnd, es, WM_SYSKEYDOWN, key, key_data))
4077 return 0;
4079 return DefWindowProcA(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4083 /*********************************************************************
4085 * WM_TIMER
4088 static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
4090 if (es->region_posx < 0) {
4091 EDIT_MoveBackward(wnd, es, TRUE);
4092 } else if (es->region_posx > 0) {
4093 EDIT_MoveForward(wnd, es, TRUE);
4096 * FIXME: gotta do some vertical scrolling here, like
4097 * EDIT_EM_LineScroll(wnd, 0, 1);
4102 /*********************************************************************
4104 * EDIT_VScroll_Hack
4106 * 16 bit notepad needs this. Actually it is not _our_ hack,
4107 * it is notepad's. Notepad is sending us scrollbar messages with
4108 * undocumented parameters without us even having a scrollbar ... !?!?
4111 static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4113 INT dy = 0;
4114 LRESULT ret = 0;
4116 if (!(es->flags & EF_VSCROLL_HACK)) {
4117 ERR("hacked WM_VSCROLL handler invoked\n");
4118 ERR(" if you are _not_ running 16 bit notepad, please report\n");
4119 ERR(" (this message is only displayed once per edit control)\n");
4120 es->flags |= EF_VSCROLL_HACK;
4123 switch (action) {
4124 case SB_LINEUP:
4125 case SB_LINEDOWN:
4126 case SB_PAGEUP:
4127 case SB_PAGEDOWN:
4128 EDIT_EM_Scroll(wnd, es, action);
4129 return 0;
4130 case SB_TOP:
4131 dy = -es->y_offset;
4132 break;
4133 case SB_BOTTOM:
4134 dy = es->line_count - 1 - es->y_offset;
4135 break;
4136 case SB_THUMBTRACK:
4137 es->flags |= EF_VSCROLL_TRACK;
4138 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4139 break;
4140 case SB_THUMBPOSITION:
4141 es->flags &= ~EF_VSCROLL_TRACK;
4142 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4143 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4144 break;
4145 case SB_ENDSCROLL:
4146 break;
4149 * FIXME : the next two are undocumented !
4150 * Are we doing the right thing ?
4151 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4152 * although it's also a regular control message.
4154 case EM_GETTHUMB16:
4155 ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
4156 break;
4157 case EM_LINESCROLL16:
4158 dy = pos;
4159 break;
4161 default:
4162 ERR("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4163 return 0;
4165 if (dy)
4166 EDIT_EM_LineScroll(wnd, es, 0, dy);
4167 return ret;
4171 /*********************************************************************
4173 * WM_VSCROLL
4176 static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4178 INT dy;
4180 if (!(es->style & ES_MULTILINE))
4181 return 0;
4183 if (!(es->style & ES_AUTOVSCROLL))
4184 return 0;
4186 if (!(es->style & WS_VSCROLL))
4187 return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
4189 dy = 0;
4190 switch (action) {
4191 case SB_LINEUP:
4192 case SB_LINEDOWN:
4193 case SB_PAGEUP:
4194 case SB_PAGEDOWN:
4195 EDIT_EM_Scroll(wnd, es, action);
4196 return 0;
4198 case SB_TOP:
4199 dy = -es->y_offset;
4200 break;
4201 case SB_BOTTOM:
4202 dy = es->line_count - 1 - es->y_offset;
4203 break;
4204 case SB_THUMBTRACK:
4205 es->flags |= EF_VSCROLL_TRACK;
4206 dy = pos - es->y_offset;
4207 break;
4208 case SB_THUMBPOSITION:
4209 es->flags &= ~EF_VSCROLL_TRACK;
4210 if (!(dy = pos - es->y_offset)) {
4211 SetScrollPos(wnd->hwndSelf, SB_VERT, pos, TRUE);
4212 EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
4214 break;
4215 case SB_ENDSCROLL:
4216 break;
4218 default:
4219 ERR("undocumented WM_VSCROLL action %d, please report\n",
4220 action);
4221 return 0;
4223 if (dy)
4224 EDIT_EM_LineScroll(wnd, es, 0, dy);
4225 return 0;
4229 /*********************************************************************
4231 * EDIT_UpdateText
4234 static void EDIT_UpdateText(WND *wnd, LPRECT rc, BOOL bErase)
4236 EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
4238 /* EF_UPDATE will be turned off in paint */
4239 if (es->flags & EF_UPDATE)
4240 EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
4242 InvalidateRect(wnd->hwndSelf, rc, bErase);