source: trunk/src/user32/edit.c@ 10216

Last change on this file since 10216 was 10216, checked in by sandervl, 22 years ago

KOM: WM_IME_CHAR generation + processing added for DBCS input

File size: 153.6 KB
Line 
1/*
2 * Edit control
3 *
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
7 *
8 */
9
10/*
11 * please read EDIT.TODO (and update it when you change things)
12 */
13
14#include "config.h"
15
16#include <string.h>
17#include <stdlib.h>
18
19#include "winbase.h"
20#include "winnt.h"
21#include "win.h"
22#include "wine/winbase16.h"
23#include "wine/winuser16.h"
24#include "wine/unicode.h"
25#include "controls.h"
26#include "local.h"
27#include "user.h"
28#include "debugtools.h"
29
30#ifdef __WIN32OS2__
31#include "ctrlconf.h"
32#include <heapstring.h>
33#endif
34
35DEFAULT_DEBUG_CHANNEL(edit);
36DECLARE_DEBUG_CHANNEL(combo);
37#ifndef __WIN32OS2__
38DECLARE_DEBUG_CHANNEL(relay);
39
40#define BUFLIMIT_MULTI 0x7FFFFFF /* maximum buffer size (not including '\0')
41#define BUFLIMIT_SINGLE 0x7FFFFFF /* maximum buffer size (not including '\0') */
42#else
43#define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
44 FIXME: BTW, new specs say 65535 (do you dare ???) */
45#define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
46#endif
47
48#define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
49#define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
50#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
51
52/*
53 * extra flags for EDITSTATE.flags field
54 */
55#define EF_MODIFIED 0x0001 /* text has been modified */
56#define EF_FOCUSED 0x0002 /* we have input focus */
57#define EF_UPDATE 0x0004 /* notify parent of changed state */
58#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
59#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
60#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
61 wrapped line, instead of in front of the next character */
62#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
63
64typedef enum
65{
66 END_0 = 0, /* line ends with terminating '\0' character */
67 END_WRAP, /* line is wrapped */
68 END_HARD, /* line ends with a hard return '\r\n' */
69 END_SOFT /* line ends with a soft return '\r\r\n' */
70} LINE_END;
71
72typedef struct tagLINEDEF {
73 INT length; /* bruto length of a line in bytes */
74 INT net_length; /* netto length of a line in visible characters */
75 LINE_END ending;
76 INT width; /* width of the line in pixels */
77 INT index; /* line index into the buffer */
78 struct tagLINEDEF *next;
79} LINEDEF;
80
81typedef struct
82{
83 BOOL is_unicode; /* how the control was created */
84 LPWSTR text; /* the actual contents of the control */
85 UINT buffer_size; /* the size of the buffer in characters */
86 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
87 HFONT font; /* NULL means standard system font */
88 INT x_offset; /* scroll offset for multi lines this is in pixels
89 for single lines it's in characters */
90 INT line_height; /* height of a screen line in pixels */
91 INT char_width; /* average character width in pixels */
92 DWORD style; /* sane version of wnd->dwStyle */
93 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
94 INT undo_insert_count; /* number of characters inserted in sequence */
95 UINT undo_position; /* character index of the insertion and deletion */
96 LPWSTR undo_text; /* deleted text */
97 UINT undo_buffer_size; /* size of the deleted text buffer */
98 INT selection_start; /* == selection_end if no selection */
99 INT selection_end; /* == current caret position */
100 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
101 INT left_margin; /* in pixels */
102 INT right_margin; /* in pixels */
103 RECT format_rect;
104 INT text_width; /* width of the widest line in pixels for multi line controls
105 and just line width for single line controls */
106 INT region_posx; /* Position of cursor relative to region: */
107 INT region_posy; /* -1: to left, 0: within, 1: to right */
108 EDITWORDBREAKPROC16 word_break_proc16;
109 void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
110 INT line_count; /* number of lines */
111 INT y_offset; /* scroll offset in number of lines */
112 BOOL bCaptureState; /* flag indicating whether mouse was captured */
113 BOOL bEnableState; /* flag keeping the enable state */
114 HWND hwndParent; /* Handle of parent for sending EN_* messages.
115 Even if parent will change, EN_* messages
116 should be sent to the first parent. */
117 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
118 /*
119 * only for multi line controls
120 */
121 INT lock_count; /* amount of re-entries in the EditWndProc */
122 INT tabs_count;
123 LPINT tabs;
124 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
125 HLOCAL hloc32W; /* our unicode local memory block */
126 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
127 or EM_SETHANDLE16 */
128 HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
129 or EM_SETHANDLE */
130} EDITSTATE;
131
132
133#define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
134#define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
135
136#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
137#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
138
139#define DPRINTF_EDIT_NOTIFY(hwnd, str) \
140 do {TRACE("notification " str " sent to hwnd=%08x\n", \
141 (UINT)(hwnd));} while(0)
142
143/* used for disabled or read-only edit control */
144#define EDIT_SEND_CTLCOLORSTATIC(hwnd,hdc) \
145 (SendMessageW(GetParent(hwnd), WM_CTLCOLORSTATIC, \
146 (WPARAM)(hdc), (LPARAM)(hwnd)))
147#define EDIT_SEND_CTLCOLOR(hwnd,hdc) \
148 (SendMessageW(GetParent(hwnd), WM_CTLCOLOREDIT, \
149 (WPARAM)(hdc), (LPARAM)(hwnd)))
150#define EDIT_NOTIFY_PARENT(hwnd, es, wNotifyCode, str) \
151 do \
152 { /* Notify parent which has created this edit control */ \
153 DPRINTF_EDIT_NOTIFY((es)->hwndParent, str); \
154 SendMessageW((es)->hwndParent, WM_COMMAND, \
155 MAKEWPARAM(GetWindowLongA((hwnd),GWL_ID), wNotifyCode), \
156 (LPARAM)(hwnd)); \
157 } while(0)
158#define DPRINTF_EDIT_MSG16(str) \
159 TRACE(\
160 "16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
161 hwnd, (UINT)wParam, (UINT)lParam)
162#define DPRINTF_EDIT_MSG32(str) \
163 TRACE(\
164 "32 bit %c : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
165 unicode ? 'W' : 'A', \
166 hwnd, (UINT)wParam, (UINT)lParam)
167
168/*********************************************************************
169 *
170 * Declarations
171 *
172 */
173
174/*
175 * These functions have trivial implementations
176 * We still like to call them internally
177 * "static inline" makes them more like macro's
178 */
179static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es);
180static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es);
181static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es);
182static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es);
183
184/*
185 * Helper functions only valid for one type of control
186 */
187static void EDIT_BuildLineDefs_ML(HWND hwnd, EDITSTATE *es, INT iStart, INT iEnd, INT delta, HRGN hrgn);
188static void EDIT_CalcLineWidth_SL(HWND hwnd, EDITSTATE *es);
189static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
190static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
191static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
192static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
193static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
194/*
195 * Helper functions valid for both single line _and_ multi line controls
196 */
197static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action);
198static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
199static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y);
200static void EDIT_GetLineRect(HWND hwnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
201static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end);
202static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es);
203static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, UINT size);
204static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size);
205static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
206static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend);
207static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend);
208static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend);
209static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
210static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend);
211static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
212static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
213static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos, BOOL after_wrap);
214static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT lprc);
215static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force);
216static void EDIT_UpdateScrollInfo(HWND hwnd, EDITSTATE *es);
217static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
218/*
219 * EM_XXX message handlers
220 */
221static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y);
222static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol);
223static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es);
224#ifndef __WIN32OS2__
225static HLOCAL16 EDIT_EM_GetHandle16(HWND hwnd, EDITSTATE *es);
226#endif
227static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode);
228static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end);
229static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es);
230static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
231static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
232static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
233static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy);
234static BOOL EDIT_EM_LineScroll_internal(HWND hwnd, EDITSTATE *es, INT dx, INT dy);
235static LRESULT EDIT_EM_PosFromChar(HWND hwnd, EDITSTATE *es, INT index, BOOL after_wrap);
236static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update);
237static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action);
238static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es);
239static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc);
240#ifndef __WIN32OS2__
241static void EDIT_EM_SetHandle16(HWND hwnd, EDITSTATE *es, HLOCAL16 hloc);
242#endif
243static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit);
244static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, INT left, INT right);
245static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, WCHAR c);
246static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
247static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs);
248static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs);
249static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, LPARAM lParam);
250#ifndef __WIN32OS2__
251static void EDIT_EM_SetWordBreakProc16(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
252#endif
253static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es);
254/*
255 * WM_XXX message handlers
256 */
257static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, WCHAR c);
258static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
259static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, INT x, INT y);
260static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es);
261static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCWSTR name);
262static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es);
263static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc);
264static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode);
265static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos);
266static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key);
267static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es);
268static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es);
269static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
270static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es);
271static LRESULT EDIT_WM_MButtonDown(HWND hwnd);
272static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, INT x, INT y);
273static LRESULT EDIT_WM_NCCreate(HWND hwnd, DWORD style, HWND hwndParent, BOOL unicode);
274static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es, WPARAM wParam);
275static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es);
276static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es);
277static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw);
278static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPARAM lParam, BOOL unicode);
279static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height);
280static LRESULT EDIT_WM_StyleChanged (HWND hwnd, EDITSTATE *es, WPARAM which, const STYLESTRUCT *style);
281static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
282static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es);
283static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos);
284static void EDIT_UpdateText(HWND hwnd, EDITSTATE *es, LPRECT rc, BOOL bErase);
285static void EDIT_UpdateTextRegion(HWND hwnd, EDITSTATE *es, HRGN hrgn, BOOL bErase);
286
287LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
288LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
289
290/*********************************************************************
291 * edit class descriptor
292 */
293const struct builtin_class_descr EDIT_builtin_class =
294{
295 "Edit", /* name */
296 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
297 EditWndProcA, /* procA */
298 EditWndProcW, /* procW */
299 sizeof(EDITSTATE *), /* extra */
300 IDC_IBEAMA, /* cursor */
301 0 /* brush */
302};
303
304
305/*********************************************************************
306 *
307 * EM_CANUNDO
308 *
309 */
310static inline BOOL EDIT_EM_CanUndo(EDITSTATE *es)
311{
312 return (es->undo_insert_count || strlenW(es->undo_text));
313}
314
315
316/*********************************************************************
317 *
318 * EM_EMPTYUNDOBUFFER
319 *
320 */
321static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
322{
323 es->undo_insert_count = 0;
324 *es->undo_text = '\0';
325}
326
327
328/*********************************************************************
329 *
330 * WM_CLEAR
331 *
332 */
333static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es)
334{
335 static const WCHAR empty_stringW[] = {0};
336
337 /* Protect read-only edit control from modification */
338 if(es->style & ES_READONLY)
339 return;
340
341 EDIT_EM_ReplaceSel(hwnd, es, TRUE, empty_stringW, TRUE);
342}
343
344
345/*********************************************************************
346 *
347 * WM_CUT
348 *
349 */
350static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es)
351{
352 EDIT_WM_Copy(hwnd, es);
353 EDIT_WM_Clear(hwnd, es);
354}
355
356
357/**********************************************************************
358 * get_app_version
359 *
360 * Returns the window version in case Wine emulates a later version
361 * of windows then the application expects.
362 *
363 * In a number of cases when windows runs an application that was
364 * designed for an earlier windows version, windows reverts
365 * to "old" behaviour of that earlier version.
366 *
367 * An example is a disabled edit control that needs to be painted.
368 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
369 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
370 * applications with an expected version 0f 4.0 or higher.
371 *
372 */
373static DWORD get_app_version(void)
374{
375 static DWORD version;
376 if (!version)
377 {
378 DWORD dwEmulatedVersion;
379 OSVERSIONINFOW info;
380 DWORD dwProcVersion = GetProcessVersion(0);
381
382 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
383 GetVersionExW( &info );
384 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
385 /* FIXME: this may not be 100% correct; see discussion on the
386 * wine developer list in Nov 1999 */
387 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
388 }
389 return version;
390}
391
392
393/*********************************************************************
394 *
395 * EditWndProc_common
396 *
397 * The messages are in the order of the actual integer values
398 * (which can be found in include/windows.h)
399 * Wherever possible the 16 bit versions are converted to
400 * the 32 bit ones, so that we can 'fall through' to the
401 * helper functions. These are mostly 32 bit (with a few
402 * exceptions, clearly indicated by a '16' extension to their
403 * names).
404 *
405 */
406static LRESULT WINAPI EditWndProc_common( HWND hwnd, UINT msg,
407 WPARAM wParam, LPARAM lParam, BOOL unicode )
408{
409 EDITSTATE *es = (EDITSTATE *)GetWindowLongA( hwnd, 0 );
410 LRESULT result = 0;
411
412 switch (msg) {
413 case WM_DESTROY:
414 DPRINTF_EDIT_MSG32("WM_DESTROY");
415 if (es) EDIT_WM_Destroy(hwnd, es);
416 result = 0;
417 goto END;
418
419 case WM_NCCREATE:
420 DPRINTF_EDIT_MSG32("WM_NCCREATE");
421 if(unicode)
422 {
423 LPCREATESTRUCTW cs = (LPCREATESTRUCTW)lParam;
424 result = EDIT_WM_NCCreate(hwnd, cs->style, cs->hwndParent, TRUE);
425 }
426 else
427 {
428 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
429 result = EDIT_WM_NCCreate(hwnd, cs->style, cs->hwndParent, FALSE);
430 }
431 goto END;
432 }
433
434 if (!es)
435 {
436 if(unicode)
437 result = DefWindowProcW(hwnd, msg, wParam, lParam);
438 else
439 result = DefWindowProcA(hwnd, msg, wParam, lParam);
440 goto END;
441 }
442
443
444 EDIT_LockBuffer(hwnd, es);
445 switch (msg) {
446 case EM_GETSEL16:
447 DPRINTF_EDIT_MSG16("EM_GETSEL");
448 wParam = 0;
449 lParam = 0;
450 /* fall through */
451 case EM_GETSEL:
452 DPRINTF_EDIT_MSG32("EM_GETSEL");
453 result = EDIT_EM_GetSel(es, (LPUINT)wParam, (LPUINT)lParam);
454 break;
455
456 case EM_SETSEL16:
457 DPRINTF_EDIT_MSG16("EM_SETSEL");
458 if (SLOWORD(lParam) == -1)
459 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
460 else
461 EDIT_EM_SetSel(hwnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
462 if (!wParam)
463 EDIT_EM_ScrollCaret(hwnd, es);
464 result = 1;
465 break;
466 case EM_SETSEL:
467 DPRINTF_EDIT_MSG32("EM_SETSEL");
468 EDIT_EM_SetSel(hwnd, es, wParam, lParam, FALSE);
469 EDIT_EM_ScrollCaret(hwnd, es);
470 result = 1;
471 break;
472
473 case EM_GETRECT16:
474 DPRINTF_EDIT_MSG16("EM_GETRECT");
475 if (lParam)
476 CONV_RECT32TO16(&es->format_rect, MapSL(lParam));
477 break;
478 case EM_GETRECT:
479 DPRINTF_EDIT_MSG32("EM_GETRECT");
480 if (lParam)
481 CopyRect((LPRECT)lParam, &es->format_rect);
482 break;
483
484 case EM_SETRECT16:
485 DPRINTF_EDIT_MSG16("EM_SETRECT");
486 if ((es->style & ES_MULTILINE) && lParam) {
487 RECT rc;
488 CONV_RECT16TO32(MapSL(lParam), &rc);
489 EDIT_SetRectNP(hwnd, es, &rc);
490 EDIT_UpdateText(hwnd, es, NULL, TRUE);
491 }
492 break;
493 case EM_SETRECT:
494 DPRINTF_EDIT_MSG32("EM_SETRECT");
495 if ((es->style & ES_MULTILINE) && lParam) {
496 EDIT_SetRectNP(hwnd, es, (LPRECT)lParam);
497 EDIT_UpdateText(hwnd, es, NULL, TRUE);
498 }
499 break;
500
501 case EM_SETRECTNP16:
502 DPRINTF_EDIT_MSG16("EM_SETRECTNP");
503 if ((es->style & ES_MULTILINE) && lParam) {
504 RECT rc;
505 CONV_RECT16TO32(MapSL(lParam), &rc);
506 EDIT_SetRectNP(hwnd, es, &rc);
507 }
508 break;
509 case EM_SETRECTNP:
510 DPRINTF_EDIT_MSG32("EM_SETRECTNP");
511 if ((es->style & ES_MULTILINE) && lParam)
512 EDIT_SetRectNP(hwnd, es, (LPRECT)lParam);
513 break;
514
515 case EM_SCROLL16:
516 DPRINTF_EDIT_MSG16("EM_SCROLL");
517 /* fall through */
518 case EM_SCROLL:
519 DPRINTF_EDIT_MSG32("EM_SCROLL");
520 result = EDIT_EM_Scroll(hwnd, es, (INT)wParam);
521 break;
522
523 case EM_LINESCROLL16:
524 DPRINTF_EDIT_MSG16("EM_LINESCROLL");
525 wParam = (WPARAM)(INT)SHIWORD(lParam);
526 lParam = (LPARAM)(INT)SLOWORD(lParam);
527 /* fall through */
528 case EM_LINESCROLL:
529 DPRINTF_EDIT_MSG32("EM_LINESCROLL");
530 result = (LRESULT)EDIT_EM_LineScroll(hwnd, es, (INT)wParam, (INT)lParam);
531 break;
532
533 case EM_SCROLLCARET16:
534 DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
535 /* fall through */
536 case EM_SCROLLCARET:
537 DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
538 EDIT_EM_ScrollCaret(hwnd, es);
539 result = 1;
540 break;
541
542 case EM_GETMODIFY16:
543 DPRINTF_EDIT_MSG16("EM_GETMODIFY");
544 /* fall through */
545 case EM_GETMODIFY:
546 DPRINTF_EDIT_MSG32("EM_GETMODIFY");
547 result = ((es->flags & EF_MODIFIED) != 0);
548 break;
549
550 case EM_SETMODIFY16:
551 DPRINTF_EDIT_MSG16("EM_SETMODIFY");
552 /* fall through */
553 case EM_SETMODIFY:
554 DPRINTF_EDIT_MSG32("EM_SETMODIFY");
555 if (wParam)
556 es->flags |= EF_MODIFIED;
557 else
558 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
559 break;
560
561 case EM_GETLINECOUNT16:
562 DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
563 /* fall through */
564 case EM_GETLINECOUNT:
565 DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
566 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
567 break;
568
569 case EM_LINEINDEX16:
570 DPRINTF_EDIT_MSG16("EM_LINEINDEX");
571 if ((INT16)wParam == -1)
572 wParam = (WPARAM)-1;
573 /* fall through */
574 case EM_LINEINDEX:
575 DPRINTF_EDIT_MSG32("EM_LINEINDEX");
576 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
577 break;
578
579#ifndef __WIN32OS2__
580 case EM_SETHANDLE16:
581 DPRINTF_EDIT_MSG16("EM_SETHANDLE");
582 EDIT_EM_SetHandle16(hwnd, es, (HLOCAL16)wParam);
583 break;
584#endif
585 case EM_SETHANDLE:
586 DPRINTF_EDIT_MSG32("EM_SETHANDLE");
587 EDIT_EM_SetHandle(hwnd, es, (HLOCAL)wParam);
588 break;
589
590#ifndef __WIN32OS2__
591 case EM_GETHANDLE16:
592 DPRINTF_EDIT_MSG16("EM_GETHANDLE");
593 result = (LRESULT)EDIT_EM_GetHandle16(hwnd, es);
594 break;
595#endif
596 case EM_GETHANDLE:
597 DPRINTF_EDIT_MSG32("EM_GETHANDLE");
598 result = (LRESULT)EDIT_EM_GetHandle(es);
599 break;
600
601 case EM_GETTHUMB16:
602 DPRINTF_EDIT_MSG16("EM_GETTHUMB");
603 /* fall through */
604 case EM_GETTHUMB:
605 DPRINTF_EDIT_MSG32("EM_GETTHUMB");
606 result = EDIT_EM_GetThumb(hwnd, es);
607 break;
608
609 /* messages 0x00bf and 0x00c0 missing from specs */
610
611 case WM_USER+15:
612 DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
613 /* fall through */
614 case 0x00bf:
615 DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
616 result = DefWindowProcW(hwnd, msg, wParam, lParam);
617 break;
618
619 case WM_USER+16:
620 DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
621 /* fall through */
622 case 0x00c0:
623 DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
624 result = DefWindowProcW(hwnd, msg, wParam, lParam);
625 break;
626
627 case EM_LINELENGTH16:
628 DPRINTF_EDIT_MSG16("EM_LINELENGTH");
629 /* fall through */
630 case EM_LINELENGTH:
631 DPRINTF_EDIT_MSG32("EM_LINELENGTH");
632 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
633 break;
634
635 case EM_REPLACESEL16:
636 DPRINTF_EDIT_MSG16("EM_REPLACESEL");
637 lParam = (LPARAM)MapSL(lParam);
638 unicode = FALSE; /* 16-bit message is always ascii */
639 /* fall through */
640 case EM_REPLACESEL:
641 {
642 LPWSTR textW;
643 DPRINTF_EDIT_MSG32("EM_REPLACESEL");
644
645 if(unicode)
646 textW = (LPWSTR)lParam;
647 else
648 {
649 LPSTR textA = (LPSTR)lParam;
650 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
651 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
652 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
653 }
654
655 EDIT_EM_ReplaceSel(hwnd, es, (BOOL)wParam, textW, TRUE);
656 result = 1;
657
658 if(!unicode)
659 HeapFree(GetProcessHeap(), 0, textW);
660 break;
661 }
662 /* message 0x00c3 missing from specs */
663
664 case WM_USER+19:
665 DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
666 /* fall through */
667 case 0x00c3:
668 DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
669 result = DefWindowProcW(hwnd, msg, wParam, lParam);
670 break;
671
672 case EM_GETLINE16:
673 DPRINTF_EDIT_MSG16("EM_GETLINE");
674 lParam = (LPARAM)MapSL(lParam);
675 unicode = FALSE; /* 16-bit message is always ascii */
676 /* fall through */
677 case EM_GETLINE:
678 DPRINTF_EDIT_MSG32("EM_GETLINE");
679 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, lParam, unicode);
680 break;
681
682 case EM_LIMITTEXT16:
683 DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
684 /* fall through */
685 case EM_SETLIMITTEXT:
686 DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
687 EDIT_EM_SetLimitText(es, (INT)wParam);
688 break;
689
690 case EM_CANUNDO16:
691 DPRINTF_EDIT_MSG16("EM_CANUNDO");
692 /* fall through */
693 case EM_CANUNDO:
694 DPRINTF_EDIT_MSG32("EM_CANUNDO");
695 result = (LRESULT)EDIT_EM_CanUndo(es);
696 break;
697
698 case EM_UNDO16:
699 DPRINTF_EDIT_MSG16("EM_UNDO");
700 /* fall through */
701 case EM_UNDO:
702 /* fall through */
703 case WM_UNDO:
704 DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
705 result = (LRESULT)EDIT_EM_Undo(hwnd, es);
706 break;
707
708 case EM_FMTLINES16:
709 DPRINTF_EDIT_MSG16("EM_FMTLINES");
710 /* fall through */
711 case EM_FMTLINES:
712 DPRINTF_EDIT_MSG32("EM_FMTLINES");
713 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
714 break;
715
716 case EM_LINEFROMCHAR16:
717 DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
718 /* fall through */
719 case EM_LINEFROMCHAR:
720 DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
721 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
722 break;
723
724 /* message 0x00ca missing from specs */
725
726 case WM_USER+26:
727 DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
728 /* fall through */
729 case 0x00ca:
730 DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
731 result = DefWindowProcW(hwnd, msg, wParam, lParam);
732 break;
733
734 case EM_SETTABSTOPS16:
735 DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
736 result = (LRESULT)EDIT_EM_SetTabStops16(es, (INT)wParam, MapSL(lParam));
737 break;
738 case EM_SETTABSTOPS:
739 DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
740 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
741 break;
742
743 case EM_SETPASSWORDCHAR16:
744 DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
745 unicode = FALSE; /* 16-bit message is always ascii */
746 /* fall through */
747 case EM_SETPASSWORDCHAR:
748 {
749 WCHAR charW = 0;
750 DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
751
752 if(unicode)
753 charW = (WCHAR)wParam;
754 else
755 {
756 CHAR charA = wParam;
757 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
758 }
759
760 EDIT_EM_SetPasswordChar(hwnd, es, charW);
761 break;
762 }
763
764 case EM_EMPTYUNDOBUFFER16:
765 DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
766 /* fall through */
767 case EM_EMPTYUNDOBUFFER:
768 DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
769 EDIT_EM_EmptyUndoBuffer(es);
770 break;
771
772 case EM_GETFIRSTVISIBLELINE16:
773 DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
774 result = es->y_offset;
775 break;
776 case EM_GETFIRSTVISIBLELINE:
777 DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
778 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
779 break;
780
781 case EM_SETREADONLY16:
782 DPRINTF_EDIT_MSG16("EM_SETREADONLY");
783 /* fall through */
784 case EM_SETREADONLY:
785 DPRINTF_EDIT_MSG32("EM_SETREADONLY");
786 if (wParam) {
787 SetWindowLongA( hwnd, GWL_STYLE,
788 GetWindowLongA( hwnd, GWL_STYLE ) | ES_READONLY );
789 es->style |= ES_READONLY;
790 } else {
791 SetWindowLongA( hwnd, GWL_STYLE,
792 GetWindowLongA( hwnd, GWL_STYLE ) & ~ES_READONLY );
793 es->style &= ~ES_READONLY;
794 }
795 result = 1;
796 break;
797#ifndef __WIN32OS2__
798 case EM_SETWORDBREAKPROC16:
799 DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
800 EDIT_EM_SetWordBreakProc16(hwnd, es, (EDITWORDBREAKPROC16)lParam);
801 break;
802#endif
803 case EM_SETWORDBREAKPROC:
804 DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
805 EDIT_EM_SetWordBreakProc(hwnd, es, lParam);
806 break;
807
808 case EM_GETWORDBREAKPROC16:
809 DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
810 result = (LRESULT)es->word_break_proc16;
811 break;
812 case EM_GETWORDBREAKPROC:
813 DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
814 result = (LRESULT)es->word_break_proc;
815 break;
816
817 case EM_GETPASSWORDCHAR16:
818 DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
819 unicode = FALSE; /* 16-bit message is always ascii */
820 /* fall through */
821 case EM_GETPASSWORDCHAR:
822 {
823 DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
824
825 if(unicode)
826 result = es->password_char;
827 else
828 {
829 WCHAR charW = es->password_char;
830 CHAR charA = 0;
831 WideCharToMultiByte(CP_ACP, 0, &charW, 1, &charA, 1, NULL, NULL);
832 result = charA;
833 }
834 break;
835 }
836
837 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
838
839 case EM_SETMARGINS:
840 DPRINTF_EDIT_MSG32("EM_SETMARGINS");
841 EDIT_EM_SetMargins(es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
842 break;
843
844 case EM_GETMARGINS:
845 DPRINTF_EDIT_MSG32("EM_GETMARGINS");
846 result = MAKELONG(es->left_margin, es->right_margin);
847 break;
848
849 case EM_GETLIMITTEXT:
850 DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
851 result = es->buffer_limit;
852 break;
853
854 case EM_POSFROMCHAR:
855 DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
856 result = EDIT_EM_PosFromChar(hwnd, es, (INT)wParam, FALSE);
857 break;
858
859 case EM_CHARFROMPOS:
860 DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
861 result = EDIT_EM_CharFromPos(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
862 break;
863
864 /* End of the EM_ messages which were in numerical order; what order
865 * are these in? vaguely alphabetical?
866 */
867
868 case WM_GETDLGCODE:
869 DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
870 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
871
872 if (lParam && (((LPMSG)lParam)->message == WM_KEYDOWN))
873 {
874 int vk = (int)((LPMSG)lParam)->wParam;
875
876 if (vk == VK_RETURN && (GetWindowLongA( hwnd, GWL_STYLE ) & ES_WANTRETURN))
877 {
878 result |= DLGC_WANTMESSAGE;
879 }
880 else if (es->hwndListBox && (vk == VK_RETURN || vk == VK_ESCAPE))
881 {
882 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
883 result |= DLGC_WANTMESSAGE;
884 }
885 }
886 break;
887
888 case WM_CHAR:
889 {
890 WCHAR charW;
891 DPRINTF_EDIT_MSG32("WM_CHAR");
892
893 if(unicode)
894 charW = wParam;
895 else
896 {
897 CHAR charA = wParam;
898 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
899 }
900
901 if ((charW == VK_RETURN || charW == VK_ESCAPE) && es->hwndListBox)
902 {
903 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
904 SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0);
905 break;
906 }
907 EDIT_WM_Char(hwnd, es, charW);
908 break;
909 }
910
911 case WM_CLEAR:
912 DPRINTF_EDIT_MSG32("WM_CLEAR");
913 EDIT_WM_Clear(hwnd, es);
914 break;
915
916 case WM_COMMAND:
917 DPRINTF_EDIT_MSG32("WM_COMMAND");
918 EDIT_WM_Command(hwnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
919 break;
920
921 case WM_CONTEXTMENU:
922 DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
923 EDIT_WM_ContextMenu(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
924 break;
925
926 case WM_COPY:
927 DPRINTF_EDIT_MSG32("WM_COPY");
928 EDIT_WM_Copy(hwnd, es);
929 break;
930
931 case WM_CREATE:
932 DPRINTF_EDIT_MSG32("WM_CREATE");
933 if(unicode)
934 result = EDIT_WM_Create(hwnd, es, ((LPCREATESTRUCTW)lParam)->lpszName);
935 else
936 {
937 LPCSTR nameA = ((LPCREATESTRUCTA)lParam)->lpszName;
938 LPWSTR nameW = NULL;
939 if(nameA)
940 {
941 INT countW = MultiByteToWideChar(CP_ACP, 0, nameA, -1, NULL, 0);
942 if((nameW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
943 MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW);
944 }
945 result = EDIT_WM_Create(hwnd, es, nameW);
946 if(nameW)
947 HeapFree(GetProcessHeap(), 0, nameW);
948 }
949 break;
950
951 case WM_CUT:
952 DPRINTF_EDIT_MSG32("WM_CUT");
953 EDIT_WM_Cut(hwnd, es);
954 break;
955
956 case WM_ENABLE:
957 DPRINTF_EDIT_MSG32("WM_ENABLE");
958 es->bEnableState = (BOOL) wParam;
959 EDIT_UpdateText(hwnd, es, NULL, TRUE);
960 break;
961
962 case WM_ERASEBKGND:
963 DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
964 result = EDIT_WM_EraseBkGnd(hwnd, es, (HDC)wParam);
965 break;
966
967 case WM_GETFONT:
968 DPRINTF_EDIT_MSG32("WM_GETFONT");
969 result = (LRESULT)es->font;
970 break;
971
972 case WM_GETTEXT:
973 DPRINTF_EDIT_MSG32("WM_GETTEXT");
974 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, lParam, unicode);
975 break;
976
977 case WM_GETTEXTLENGTH:
978 DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
979 result = strlenW(es->text);
980 break;
981
982 case WM_HSCROLL:
983 DPRINTF_EDIT_MSG32("WM_HSCROLL");
984 result = EDIT_WM_HScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam));
985 break;
986
987#ifdef __WIN32OS2__
988 case WM_IME_CHAR:
989 {
990 WCHAR charW;
991 DPRINTF_EDIT_MSG32("WM_IME_CHAR");
992
993 if(unicode)
994 charW = wParam;
995 else
996 {
997 // always DBCS char
998 CHAR charA[ 2 ];
999
1000 charA[ 0 ] = ( CHAR )( wParam >> 8 );
1001 charA[ 1 ] = ( CHAR )wParam;
1002
1003 MultiByteToWideChar( CP_ACP, 0, ( LPSTR )charA, 2, ( LPWSTR )&charW, 1);
1004 }
1005
1006 EDIT_WM_Char(hwnd, es, charW);
1007 break;
1008 }
1009#endif
1010
1011 case WM_KEYDOWN:
1012 DPRINTF_EDIT_MSG32("WM_KEYDOWN");
1013 result = EDIT_WM_KeyDown(hwnd, es, (INT)wParam);
1014 break;
1015
1016 case WM_KILLFOCUS:
1017 DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
1018 result = EDIT_WM_KillFocus(hwnd, es);
1019 break;
1020
1021 case WM_LBUTTONDBLCLK:
1022 DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
1023 result = EDIT_WM_LButtonDblClk(hwnd, es);
1024 break;
1025
1026 case WM_LBUTTONDOWN:
1027 DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
1028 result = EDIT_WM_LButtonDown(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
1029 break;
1030
1031 case WM_LBUTTONUP:
1032 DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
1033 result = EDIT_WM_LButtonUp(hwnd, es);
1034 break;
1035
1036 case WM_MBUTTONDOWN:
1037 DPRINTF_EDIT_MSG32("WM_MBUTTONDOWN");
1038 result = EDIT_WM_MButtonDown(hwnd);
1039 break;
1040
1041 case WM_MOUSEACTIVATE:
1042 /*
1043 * FIXME: maybe DefWindowProc() screws up, but it seems that
1044 * modeless dialog boxes need this. If we don't do this, the focus
1045 * will _not_ be set by DefWindowProc() for edit controls in a
1046 * modeless dialog box ???
1047 */
1048 DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
1049 SetFocus(hwnd);
1050 result = MA_ACTIVATE;
1051 break;
1052
1053 case WM_MOUSEMOVE:
1054 /*
1055 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
1056 */
1057 result = EDIT_WM_MouseMove(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
1058 break;
1059
1060 case WM_PAINT:
1061 DPRINTF_EDIT_MSG32("WM_PAINT");
1062 EDIT_WM_Paint(hwnd, es, wParam);
1063 break;
1064
1065 case WM_PASTE:
1066 DPRINTF_EDIT_MSG32("WM_PASTE");
1067 EDIT_WM_Paste(hwnd, es);
1068 break;
1069
1070 case WM_SETFOCUS:
1071 DPRINTF_EDIT_MSG32("WM_SETFOCUS");
1072 EDIT_WM_SetFocus(hwnd, es);
1073 break;
1074
1075 case WM_SETFONT:
1076 DPRINTF_EDIT_MSG32("WM_SETFONT");
1077 EDIT_WM_SetFont(hwnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
1078 break;
1079
1080 case WM_SETREDRAW:
1081 /* FIXME: actually set an internal flag and behave accordingly */
1082 break;
1083
1084 case WM_SETTEXT:
1085 DPRINTF_EDIT_MSG32("WM_SETTEXT");
1086 EDIT_WM_SetText(hwnd, es, lParam, unicode);
1087 result = TRUE;
1088 break;
1089
1090 case WM_SIZE:
1091 DPRINTF_EDIT_MSG32("WM_SIZE");
1092 EDIT_WM_Size(hwnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
1093 break;
1094
1095 case WM_STYLECHANGED:
1096 DPRINTF_EDIT_MSG32("WM_STYLECHANGED");
1097 result = EDIT_WM_StyleChanged (hwnd, es, wParam, (const STYLESTRUCT *)lParam);
1098 break;
1099
1100 case WM_STYLECHANGING:
1101 DPRINTF_EDIT_MSG32("WM_STYLECHANGING");
1102 result = 0; /* See EDIT_WM_StyleChanged */
1103 break;
1104
1105 case WM_SYSKEYDOWN:
1106 DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
1107 result = EDIT_WM_SysKeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
1108 break;
1109
1110 case WM_TIMER:
1111 DPRINTF_EDIT_MSG32("WM_TIMER");
1112 EDIT_WM_Timer(hwnd, es);
1113 break;
1114
1115 case WM_VSCROLL:
1116 DPRINTF_EDIT_MSG32("WM_VSCROLL");
1117 result = EDIT_WM_VScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam));
1118 break;
1119
1120 case WM_MOUSEWHEEL:
1121 {
1122 int gcWheelDelta = 0;
1123 UINT pulScrollLines = 3;
1124 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1125
1126 if (wParam & (MK_SHIFT | MK_CONTROL)) {
1127 result = DefWindowProcW(hwnd, msg, wParam, lParam);
1128 break;
1129 }
1130 gcWheelDelta -= SHIWORD(wParam);
1131 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1132 {
1133 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
1134 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1135 result = EDIT_EM_LineScroll(hwnd, es, 0, cLineScroll);
1136 }
1137 }
1138 break;
1139 default:
1140 if(unicode)
1141 result = DefWindowProcW(hwnd, msg, wParam, lParam);
1142 else
1143 result = DefWindowProcA(hwnd, msg, wParam, lParam);
1144 break;
1145 }
1146 EDIT_UnlockBuffer(hwnd, es, FALSE);
1147 END:
1148 return result;
1149}
1150
1151/*********************************************************************
1152 *
1153 * EditWndProcW (USER32.@)
1154 */
1155LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1156{
1157 if (!IsWindow( hWnd )) return 0;
1158 return EditWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
1159}
1160
1161/*********************************************************************
1162 *
1163 * EditWndProc (USER32.@)
1164 */
1165LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1166{
1167 if (!IsWindow( hWnd )) return 0;
1168 return EditWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
1169}
1170
1171/*********************************************************************
1172 *
1173 * EDIT_BuildLineDefs_ML
1174 *
1175 * Build linked list of text lines.
1176 * Lines can end with '\0' (last line), a character (if it is wrapped),
1177 * a soft return '\r\r\n' or a hard return '\r\n'
1178 *
1179 */
1180static void EDIT_BuildLineDefs_ML(HWND hwnd, EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
1181{
1182 HDC dc;
1183 HFONT old_font = 0;
1184 LPWSTR current_position, cp;
1185 INT fw;
1186 LINEDEF *current_line;
1187 LINEDEF *previous_line;
1188 LINEDEF *start_line;
1189 INT line_index = 0, nstart_line = 0, nstart_index = 0;
1190 INT line_count = es->line_count;
1191 INT orig_net_length;
1192 RECT rc;
1193
1194 if (istart == iend && delta == 0)
1195 return;
1196
1197 dc = GetDC(hwnd);
1198 if (es->font)
1199 old_font = SelectObject(dc, es->font);
1200
1201 previous_line = NULL;
1202 current_line = es->first_line_def;
1203
1204 /* Find starting line. istart must lie inside an existing line or
1205 * at the end of buffer */
1206 do {
1207 if (istart < current_line->index + current_line->length ||
1208 current_line->ending == END_0)
1209 break;
1210
1211 previous_line = current_line;
1212 current_line = current_line->next;
1213 line_index++;
1214 } while (current_line);
1215
1216 if (!current_line) /* Error occurred start is not inside previous buffer */
1217 {
1218 FIXME(" modification occurred outside buffer\n");
1219 return;
1220 }
1221
1222 /* Remember start of modifications in order to calculate update region */
1223 nstart_line = line_index;
1224 nstart_index = current_line->index;
1225
1226 /* We must start to reformat from the previous line since the modifications
1227 * may have caused the line to wrap upwards. */
1228 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
1229 {
1230 line_index--;
1231 current_line = previous_line;
1232 }
1233 start_line = current_line;
1234
1235 fw = es->format_rect.right - es->format_rect.left;
1236 current_position = es->text + current_line->index;
1237 do {
1238 if (current_line != start_line)
1239 {
1240 if (!current_line || current_line->index + delta > current_position - es->text)
1241 {
1242 /* The buffer has been expanded, create a new line and
1243 insert it into the link list */
1244 LINEDEF *new_line = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
1245 new_line->next = previous_line->next;
1246 previous_line->next = new_line;
1247 current_line = new_line;
1248 es->line_count++;
1249 }
1250 else if (current_line->index + delta < current_position - es->text)
1251 {
1252 /* The previous line merged with this line so we delete this extra entry */
1253 previous_line->next = current_line->next;
1254 HeapFree(GetProcessHeap(), 0, current_line);
1255 current_line = previous_line->next;
1256 es->line_count--;
1257 continue;
1258 }
1259 else /* current_line->index + delta == current_position */
1260 {
1261 if (current_position - es->text > iend)
1262 break; /* We reached end of line modifications */
1263 /* else recalulate this line */
1264 }
1265 }
1266
1267 current_line->index = current_position - es->text;
1268 orig_net_length = current_line->net_length;
1269
1270 /* Find end of line */
1271 cp = current_position;
1272 while (*cp) {
1273 if ((*cp == '\r') && (*(cp + 1) == '\n'))
1274 break;
1275 cp++;
1276 }
1277
1278 /* Mark type of line termination */
1279 if (!(*cp)) {
1280 current_line->ending = END_0;
1281 current_line->net_length = strlenW(current_position);
1282 } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
1283 current_line->ending = END_SOFT;
1284 current_line->net_length = cp - current_position - 1;
1285 } else {
1286 current_line->ending = END_HARD;
1287 current_line->net_length = cp - current_position;
1288 }
1289
1290 /* Calculate line width */
1291 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1292 current_position, current_line->net_length,
1293 es->tabs_count, es->tabs));
1294
1295 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
1296 if ((!(es->style & ES_AUTOHSCROLL)) && (current_line->width > fw)) {
1297 INT next = 0;
1298 INT prev;
1299 do {
1300 prev = next;
1301 next = EDIT_CallWordBreakProc(es, current_position - es->text,
1302 prev + 1, current_line->net_length, WB_RIGHT);
1303 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1304 current_position, next, es->tabs_count, es->tabs));
1305 } while (current_line->width <= fw);
1306 if (!prev) { /* Didn't find a line break so force a break */
1307 next = 0;
1308 do {
1309 prev = next;
1310 next++;
1311 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
1312 current_position, next, es->tabs_count, es->tabs));
1313 } while (current_line->width <= fw);
1314 if (!prev)
1315 prev = 1;
1316 }
1317
1318 /* If the first line we are calculating, wrapped before istart, we must
1319 * adjust istart in order for this to be reflected in the update region. */
1320 if (current_line->index == nstart_index && istart > current_line->index + prev)
1321 istart = current_line->index + prev;
1322 /* else if we are updating the previous line before the first line we
1323 * are re-calculating and it expanded */
1324 else if (current_line == start_line &&
1325 current_line->index != nstart_index && orig_net_length < prev)
1326 {
1327 /* Line expanded due to an upwards line wrap so we must partially include
1328 * previous line in update region */
1329 nstart_line = line_index;
1330 nstart_index = current_line->index;
1331 istart = current_line->index + orig_net_length;
1332 }
1333
1334 current_line->net_length = prev;
1335 current_line->ending = END_WRAP;
1336 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc, current_position,
1337 current_line->net_length, es->tabs_count, es->tabs));
1338 }
1339
1340
1341 /* Adjust length to include line termination */
1342 switch (current_line->ending) {
1343 case END_SOFT:
1344 current_line->length = current_line->net_length + 3;
1345 break;
1346 case END_HARD:
1347 current_line->length = current_line->net_length + 2;
1348 break;
1349 case END_WRAP:
1350 case END_0:
1351 current_line->length = current_line->net_length;
1352 break;
1353 }
1354 es->text_width = max(es->text_width, current_line->width);
1355 current_position += current_line->length;
1356 previous_line = current_line;
1357 current_line = current_line->next;
1358 line_index++;
1359 } while (previous_line->ending != END_0);
1360
1361 /* Finish adjusting line indexes by delta or remove hanging lines */
1362 if (previous_line->ending == END_0)
1363 {
1364 LINEDEF *pnext = NULL;
1365
1366 previous_line->next = NULL;
1367 while (current_line)
1368 {
1369 pnext = current_line->next;
1370 HeapFree(GetProcessHeap(), 0, current_line);
1371 current_line = pnext;
1372 es->line_count--;
1373 }
1374 }
1375 else
1376 {
1377 while (current_line)
1378 {
1379 current_line->index += delta;
1380 current_line = current_line->next;
1381 }
1382 }
1383
1384 /* Calculate rest of modification rectangle */
1385 if (hrgn)
1386 {
1387 HRGN tmphrgn;
1388 /*
1389 * We calculate two rectangles. One for the first line which may have
1390 * an indent with respect to the format rect. The other is a format-width
1391 * rectangle that spans the rest of the lines that changed or moved.
1392 */
1393 rc.top = es->format_rect.top + nstart_line * es->line_height -
1394 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
1395 rc.bottom = rc.top + es->line_height;
1396 rc.left = es->format_rect.left + (INT)LOWORD(GetTabbedTextExtentW(dc,
1397 es->text + nstart_index, istart - nstart_index,
1398 es->tabs_count, es->tabs)) - es->x_offset; /* Adjust for horz scroll */
1399 rc.right = es->format_rect.right;
1400 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
1401
1402 rc.top = rc.bottom;
1403 rc.left = es->format_rect.left;
1404 rc.right = es->format_rect.right;
1405 /*
1406 * If lines were added or removed we must re-paint the remainder of the
1407 * lines since the remaining lines were either shifted up or down.
1408 */
1409 if (line_count < es->line_count) /* We added lines */
1410 rc.bottom = es->line_count * es->line_height;
1411 else if (line_count > es->line_count) /* We removed lines */
1412 rc.bottom = line_count * es->line_height;
1413 else
1414 rc.bottom = line_index * es->line_height;
1415 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
1416 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
1417 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
1418 DeleteObject(tmphrgn);
1419 }
1420
1421 if (es->font)
1422 SelectObject(dc, old_font);
1423
1424 ReleaseDC(hwnd, dc);
1425}
1426
1427/*********************************************************************
1428 *
1429 * EDIT_CalcLineWidth_SL
1430 *
1431 */
1432static void EDIT_CalcLineWidth_SL(HWND hwnd, EDITSTATE *es)
1433{
1434 es->text_width = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, strlenW(es->text), FALSE));
1435}
1436
1437/*********************************************************************
1438 *
1439 * EDIT_CallWordBreakProc
1440 *
1441 * Call appropriate WordBreakProc (internal or external).
1442 *
1443 * Note: The "start" argument should always be an index referring
1444 * to es->text. The actual wordbreak proc might be
1445 * 16 bit, so we can't always pass any 32 bit LPSTR.
1446 * Hence we assume that es->text is the buffer that holds
1447 * the string under examination (we can decide this for ourselves).
1448 *
1449 */
1450/* ### start build ### */
1451extern WORD CALLBACK EDIT_CallTo16_word_lwww(EDITWORDBREAKPROC16,SEGPTR,WORD,WORD,WORD);
1452/* ### stop build ### */
1453static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
1454{
1455 INT ret, iWndsLocks;
1456
1457 /* To avoid any deadlocks, all the locks on the window structures
1458 must be suspended before the control is passed to the application */
1459 iWndsLocks = WIN_SuspendWndsLock();
1460
1461#ifndef __WIN32OS2__
1462 if (es->word_break_proc16) {
1463 HGLOBAL16 hglob16;
1464 SEGPTR segptr;
1465 INT countA;
1466
1467 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1468 hglob16 = GlobalAlloc16(GMEM_MOVEABLE | GMEM_ZEROINIT, countA);
1469 segptr = K32WOWGlobalLock16(hglob16);
1470 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, MapSL(segptr), countA, NULL, NULL);
1471 ret = (INT)EDIT_CallTo16_word_lwww(es->word_break_proc16,
1472 segptr, index, countA, action);
1473 GlobalUnlock16(hglob16);
1474 GlobalFree16(hglob16);
1475 }
1476 else
1477#endif
1478 if (es->word_break_proc)
1479 {
1480 if(es->is_unicode)
1481 {
1482 EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
1483
1484 TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1485 es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
1486 ret = wbpW(es->text + start, index, count, action);
1487 }
1488 else
1489 {
1490 EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
1491 INT countA;
1492 CHAR *textA;
1493
1494 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
1495 textA = HeapAlloc(GetProcessHeap(), 0, countA);
1496 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
1497 TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
1498 es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
1499 ret = wbpA(textA, index, countA, action);
1500 HeapFree(GetProcessHeap(), 0, textA);
1501 }
1502 }
1503 else
1504 ret = EDIT_WordBreakProc(es->text + start, index, count, action);
1505
1506 WIN_RestoreWndsLock(iWndsLocks);
1507 return ret;
1508}
1509
1510
1511/*********************************************************************
1512 *
1513 * EDIT_CharFromPos
1514 *
1515 * Beware: This is not the function called on EM_CHARFROMPOS
1516 * The position _can_ be outside the formatting / client
1517 * rectangle
1518 * The return value is only the character index
1519 *
1520 */
1521static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
1522{
1523 INT index;
1524 HDC dc;
1525 HFONT old_font = 0;
1526
1527 if (es->style & ES_MULTILINE) {
1528 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
1529 INT line_index = 0;
1530 LINEDEF *line_def = es->first_line_def;
1531 INT low, high;
1532 while ((line > 0) && line_def->next) {
1533 line_index += line_def->length;
1534 line_def = line_def->next;
1535 line--;
1536 }
1537 x += es->x_offset - es->format_rect.left;
1538 if (x >= line_def->width) {
1539 if (after_wrap)
1540 *after_wrap = (line_def->ending == END_WRAP);
1541 return line_index + line_def->net_length;
1542 }
1543 if (x <= 0) {
1544 if (after_wrap)
1545 *after_wrap = FALSE;
1546 return line_index;
1547 }
1548 dc = GetDC(hwnd);
1549 if (es->font)
1550 old_font = SelectObject(dc, es->font);
1551 low = line_index + 1;
1552 high = line_index + line_def->net_length + 1;
1553 while (low < high - 1)
1554 {
1555 INT mid = (low + high) / 2;
1556 if (LOWORD(GetTabbedTextExtentW(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
1557 else low = mid;
1558 }
1559 index = low;
1560
1561 if (after_wrap)
1562 *after_wrap = ((index == line_index + line_def->net_length) &&
1563 (line_def->ending == END_WRAP));
1564 } else {
1565 LPWSTR text;
1566 SIZE size;
1567 if (after_wrap)
1568 *after_wrap = FALSE;
1569 x -= es->format_rect.left;
1570 if (!x)
1571 return es->x_offset;
1572 text = EDIT_GetPasswordPointer_SL(es);
1573 dc = GetDC(hwnd);
1574 if (es->font)
1575 old_font = SelectObject(dc, es->font);
1576 if (x < 0)
1577 {
1578 INT low = 0;
1579 INT high = es->x_offset;
1580 while (low < high - 1)
1581 {
1582 INT mid = (low + high) / 2;
1583 GetTextExtentPoint32W( dc, text + mid,
1584 es->x_offset - mid, &size );
1585 if (size.cx > -x) low = mid;
1586 else high = mid;
1587 }
1588 index = low;
1589 }
1590 else
1591 {
1592 INT low = es->x_offset;
1593 INT high = strlenW(es->text) + 1;
1594 while (low < high - 1)
1595 {
1596 INT mid = (low + high) / 2;
1597 GetTextExtentPoint32W( dc, text + es->x_offset,
1598 mid - es->x_offset, &size );
1599 if (size.cx > x) high = mid;
1600 else low = mid;
1601 }
1602 index = low;
1603 }
1604 if (es->style & ES_PASSWORD)
1605 HeapFree(GetProcessHeap(), 0, text);
1606 }
1607 if (es->font)
1608 SelectObject(dc, old_font);
1609 ReleaseDC(hwnd, dc);
1610 return index;
1611}
1612
1613
1614/*********************************************************************
1615 *
1616 * EDIT_ConfinePoint
1617 *
1618 * adjusts the point to be within the formatting rectangle
1619 * (so CharFromPos returns the nearest _visible_ character)
1620 *
1621 */
1622static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y)
1623{
1624 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
1625 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
1626}
1627
1628
1629/*********************************************************************
1630 *
1631 * EDIT_GetLineRect
1632 *
1633 * Calculates the bounding rectangle for a line from a starting
1634 * column to an ending column.
1635 *
1636 */
1637static void EDIT_GetLineRect(HWND hwnd, EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1638{
1639 INT line_index = EDIT_EM_LineIndex(es, line);
1640
1641 if (es->style & ES_MULTILINE)
1642 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1643 else
1644 rc->top = es->format_rect.top;
1645 rc->bottom = rc->top + es->line_height;
1646 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(hwnd, es, line_index + scol, TRUE));
1647 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(hwnd, es, line_index + ecol, TRUE));
1648}
1649
1650
1651/*********************************************************************
1652 *
1653 * EDIT_GetPasswordPointer_SL
1654 *
1655 * note: caller should free the (optionally) allocated buffer
1656 *
1657 */
1658static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
1659{
1660 if (es->style & ES_PASSWORD) {
1661 INT len = strlenW(es->text);
1662 LPWSTR text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
1663 text[len] = '\0';
1664 while(len) text[--len] = es->password_char;
1665 return text;
1666 } else
1667 return es->text;
1668}
1669
1670
1671/*********************************************************************
1672 *
1673 * EDIT_LockBuffer
1674 *
1675 * This acts as a LOCAL_Lock(), but it locks only once. This way
1676 * you can call it whenever you like, without unlocking.
1677 *
1678 */
1679static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es)
1680{
1681 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
1682 if (!es) {
1683 ERR("no EDITSTATE ... please report\n");
1684 return;
1685 }
1686 if (!es->text) {
1687 CHAR *textA = NULL;
1688 UINT countA = 0;
1689 BOOL _16bit = FALSE;
1690
1691 if(es->hloc32W)
1692 {
1693 if(es->hloc32A)
1694 {
1695 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1696 textA = LocalLock(es->hloc32A);
1697 countA = strlen(textA) + 1;
1698 }
1699 else if(es->hloc16)
1700 {
1701 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1702 textA = LOCAL_Lock(hInstance, es->hloc16);
1703 countA = strlen(textA) + 1;
1704 _16bit = TRUE;
1705 }
1706 }
1707 else {
1708 ERR("no buffer ... please report\n");
1709 return;
1710 }
1711
1712 if(textA)
1713 {
1714 HLOCAL hloc32W_new;
1715 UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
1716 TRACE("%d bytes translated to %d WCHARs\n", countA, countW_new);
1717 if(countW_new > es->buffer_size + 1)
1718 {
1719 UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1720 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1721 hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1722 if(hloc32W_new)
1723 {
1724 es->hloc32W = hloc32W_new;
1725 es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1726 TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1727 }
1728 else
1729 WARN("FAILED! Will synchronize partially\n");
1730 }
1731 }
1732
1733 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1734 es->text = LocalLock(es->hloc32W);
1735
1736 if(textA)
1737 {
1738 MultiByteToWideChar(CP_ACP, 0, textA, countA, es->text, es->buffer_size + 1);
1739 if(_16bit)
1740 LOCAL_Unlock(hInstance, es->hloc16);
1741 else
1742 LocalUnlock(es->hloc32A);
1743 }
1744 }
1745 es->lock_count++;
1746}
1747
1748
1749/*********************************************************************
1750 *
1751 * EDIT_SL_InvalidateText
1752 *
1753 * Called from EDIT_InvalidateText().
1754 * Does the job for single-line controls only.
1755 *
1756 */
1757static void EDIT_SL_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1758{
1759 RECT line_rect;
1760 RECT rc;
1761
1762 EDIT_GetLineRect(hwnd, es, 0, start, end, &line_rect);
1763 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1764 EDIT_UpdateText(hwnd, es, &rc, FALSE);
1765}
1766
1767
1768/*********************************************************************
1769 *
1770 * EDIT_ML_InvalidateText
1771 *
1772 * Called from EDIT_InvalidateText().
1773 * Does the job for multi-line controls only.
1774 *
1775 */
1776static void EDIT_ML_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1777{
1778 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1779 INT sl = EDIT_EM_LineFromChar(es, start);
1780 INT el = EDIT_EM_LineFromChar(es, end);
1781 INT sc;
1782 INT ec;
1783 RECT rc1;
1784 RECT rcWnd;
1785 RECT rcLine;
1786 RECT rcUpdate;
1787 INT l;
1788
1789 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1790 return;
1791
1792 sc = start - EDIT_EM_LineIndex(es, sl);
1793 ec = end - EDIT_EM_LineIndex(es, el);
1794 if (sl < es->y_offset) {
1795 sl = es->y_offset;
1796 sc = 0;
1797 }
1798 if (el > es->y_offset + vlc) {
1799 el = es->y_offset + vlc;
1800 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1801 }
1802 GetClientRect(hwnd, &rc1);
1803 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1804 if (sl == el) {
1805 EDIT_GetLineRect(hwnd, es, sl, sc, ec, &rcLine);
1806 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1807 EDIT_UpdateText(hwnd, es, &rcUpdate, FALSE);
1808 } else {
1809 EDIT_GetLineRect(hwnd, es, sl, sc,
1810 EDIT_EM_LineLength(es,
1811 EDIT_EM_LineIndex(es, sl)),
1812 &rcLine);
1813 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1814 EDIT_UpdateText(hwnd, es, &rcUpdate, FALSE);
1815 for (l = sl + 1 ; l < el ; l++) {
1816 EDIT_GetLineRect(hwnd, es, l, 0,
1817 EDIT_EM_LineLength(es,
1818 EDIT_EM_LineIndex(es, l)),
1819 &rcLine);
1820 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1821 EDIT_UpdateText(hwnd, es, &rcUpdate, FALSE);
1822 }
1823 EDIT_GetLineRect(hwnd, es, el, 0, ec, &rcLine);
1824 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1825 EDIT_UpdateText(hwnd, es, &rcUpdate, FALSE);
1826 }
1827}
1828
1829
1830/*********************************************************************
1831 *
1832 * EDIT_InvalidateText
1833 *
1834 * Invalidate the text from offset start upto, but not including,
1835 * offset end. Useful for (re)painting the selection.
1836 * Regions outside the linewidth are not invalidated.
1837 * end == -1 means end == TextLength.
1838 * start and end need not be ordered.
1839 *
1840 */
1841static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1842{
1843 if (end == start)
1844 return;
1845
1846 if (end == -1)
1847 end = strlenW(es->text);
1848
1849 ORDER_INT(start, end);
1850
1851 if (es->style & ES_MULTILINE)
1852 EDIT_ML_InvalidateText(hwnd, es, start, end);
1853 else
1854 EDIT_SL_InvalidateText(hwnd, es, start, end);
1855}
1856
1857
1858/*********************************************************************
1859 *
1860 * EDIT_MakeFit
1861 *
1862 * Try to fit size + 1 characters in the buffer. Constrain to limits.
1863 *
1864 */
1865static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, UINT size)
1866{
1867 HLOCAL hNew32W;
1868
1869 if (size <= es->buffer_size)
1870 return TRUE;
1871#ifndef __WIN32OS2__
1872//SvL: EM_SETTEXTLIMIT has no effect in
1873// NT4, SP6 (EM_GETTEXTLIMIT only returns that value).
1874// Limits are simply ignored, no EN_MAXTEXT notification is ever sent.
1875// (fixes license edit control in Microsoft Visual C++ 4.2 install)
1876
1877 if (size > es->buffer_limit) {
1878 EDIT_NOTIFY_PARENT(hwnd, es, EN_MAXTEXT, "EN_MAXTEXT");
1879 return FALSE;
1880 }
1881 if (size > es->buffer_limit)
1882 size = es->buffer_limit;
1883#endif
1884
1885 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1886
1887 /* Force edit to unlock it's buffer. es->text now NULL */
1888 EDIT_UnlockBuffer(hwnd, es, TRUE);
1889
1890 if (es->hloc32W) {
1891 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1892 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1893 TRACE("Old 32 bit handle %08x, new handle %08x\n", es->hloc32W, hNew32W);
1894 es->hloc32W = hNew32W;
1895 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1896 }
1897 }
1898
1899 EDIT_LockBuffer(hwnd, es);
1900
1901 if (es->buffer_size < size) {
1902 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1903 EDIT_NOTIFY_PARENT(hwnd, es, EN_ERRSPACE, "EN_ERRSPACE");
1904 return FALSE;
1905 } else {
1906 TRACE("We now have %d+1\n", es->buffer_size);
1907 return TRUE;
1908 }
1909}
1910
1911
1912/*********************************************************************
1913 *
1914 * EDIT_MakeUndoFit
1915 *
1916 * Try to fit size + 1 bytes in the undo buffer.
1917 *
1918 */
1919static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1920{
1921 UINT alloc_size;
1922
1923 if (size <= es->undo_buffer_size)
1924 return TRUE;
1925
1926 TRACE("trying to ReAlloc to %d+1\n", size);
1927
1928 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1929 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1930#ifdef __WIN32OS2__
1931 es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1932#else
1933 es->undo_buffer_size = alloc_size/sizeof(WCHAR);
1934#endif
1935 return TRUE;
1936 }
1937 else
1938 {
1939 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1940 return FALSE;
1941 }
1942}
1943
1944
1945/*********************************************************************
1946 *
1947 * EDIT_MoveBackward
1948 *
1949 */
1950static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
1951{
1952 INT e = es->selection_end;
1953
1954 if (e) {
1955 e--;
1956 if ((es->style & ES_MULTILINE) && e &&
1957 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1958 e--;
1959 if (e && (es->text[e - 1] == '\r'))
1960 e--;
1961 }
1962 }
1963 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1964 EDIT_EM_ScrollCaret(hwnd, es);
1965}
1966
1967
1968/*********************************************************************
1969 *
1970 * EDIT_MoveDown_ML
1971 *
1972 * Only for multi line controls
1973 * Move the caret one line down, on a column with the nearest
1974 * x coordinate on the screen (might be a different column).
1975 *
1976 */
1977static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1978{
1979 INT s = es->selection_start;
1980 INT e = es->selection_end;
1981 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1982 LRESULT pos = EDIT_EM_PosFromChar(hwnd, es, e, after_wrap);
1983 INT x = SLOWORD(pos);
1984 INT y = SHIWORD(pos);
1985
1986 e = EDIT_CharFromPos(hwnd, es, x, y + es->line_height, &after_wrap);
1987 if (!extend)
1988 s = e;
1989 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1990 EDIT_EM_ScrollCaret(hwnd, es);
1991}
1992
1993
1994/*********************************************************************
1995 *
1996 * EDIT_MoveEnd
1997 *
1998 */
1999static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend)
2000{
2001 BOOL after_wrap = FALSE;
2002 INT e;
2003
2004 /* Pass a high value in x to make sure of receiving the end of the line */
2005 if (es->style & ES_MULTILINE)
2006 e = EDIT_CharFromPos(hwnd, es, 0x3fffffff,
2007 HIWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
2008 else
2009 e = strlenW(es->text);
2010 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, after_wrap);
2011 EDIT_EM_ScrollCaret(hwnd, es);
2012}
2013
2014
2015/*********************************************************************
2016 *
2017 * EDIT_MoveForward
2018 *
2019 */
2020static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend)
2021{
2022 INT e = es->selection_end;
2023
2024 if (es->text[e]) {
2025 e++;
2026 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
2027 if (es->text[e] == '\n')
2028 e++;
2029 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
2030 e += 2;
2031 }
2032 }
2033 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
2034 EDIT_EM_ScrollCaret(hwnd, es);
2035}
2036
2037
2038/*********************************************************************
2039 *
2040 * EDIT_MoveHome
2041 *
2042 * Home key: move to beginning of line.
2043 *
2044 */
2045static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend)
2046{
2047 INT e;
2048
2049 /* Pass the x_offset in x to make sure of receiving the first position of the line */
2050 if (es->style & ES_MULTILINE)
2051 e = EDIT_CharFromPos(hwnd, es, -es->x_offset,
2052 HIWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
2053 else
2054 e = 0;
2055 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
2056 EDIT_EM_ScrollCaret(hwnd, es);
2057}
2058
2059
2060/*********************************************************************
2061 *
2062 * EDIT_MovePageDown_ML
2063 *
2064 * Only for multi line controls
2065 * Move the caret one page down, on a column with the nearest
2066 * x coordinate on the screen (might be a different column).
2067 *
2068 */
2069static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
2070{
2071 INT s = es->selection_start;
2072 INT e = es->selection_end;
2073 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2074 LRESULT pos = EDIT_EM_PosFromChar(hwnd, es, e, after_wrap);
2075 INT x = SLOWORD(pos);
2076 INT y = SHIWORD(pos);
2077
2078 e = EDIT_CharFromPos(hwnd, es, x,
2079 y + (es->format_rect.bottom - es->format_rect.top),
2080 &after_wrap);
2081 if (!extend)
2082 s = e;
2083 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
2084 EDIT_EM_ScrollCaret(hwnd, es);
2085}
2086
2087
2088/*********************************************************************
2089 *
2090 * EDIT_MovePageUp_ML
2091 *
2092 * Only for multi line controls
2093 * Move the caret one page up, on a column with the nearest
2094 * x coordinate on the screen (might be a different column).
2095 *
2096 */
2097static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
2098{
2099 INT s = es->selection_start;
2100 INT e = es->selection_end;
2101 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2102 LRESULT pos = EDIT_EM_PosFromChar(hwnd, es, e, after_wrap);
2103 INT x = SLOWORD(pos);
2104 INT y = SHIWORD(pos);
2105
2106 e = EDIT_CharFromPos(hwnd, es, x,
2107 y - (es->format_rect.bottom - es->format_rect.top),
2108 &after_wrap);
2109 if (!extend)
2110 s = e;
2111 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
2112 EDIT_EM_ScrollCaret(hwnd, es);
2113}
2114
2115
2116/*********************************************************************
2117 *
2118 * EDIT_MoveUp_ML
2119 *
2120 * Only for multi line controls
2121 * Move the caret one line up, on a column with the nearest
2122 * x coordinate on the screen (might be a different column).
2123 *
2124 */
2125static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
2126{
2127 INT s = es->selection_start;
2128 INT e = es->selection_end;
2129 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2130 LRESULT pos = EDIT_EM_PosFromChar(hwnd, es, e, after_wrap);
2131 INT x = SLOWORD(pos);
2132 INT y = SHIWORD(pos);
2133
2134 e = EDIT_CharFromPos(hwnd, es, x, y - es->line_height, &after_wrap);
2135 if (!extend)
2136 s = e;
2137 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
2138 EDIT_EM_ScrollCaret(hwnd, es);
2139}
2140
2141
2142/*********************************************************************
2143 *
2144 * EDIT_MoveWordBackward
2145 *
2146 */
2147static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
2148{
2149 INT s = es->selection_start;
2150 INT e = es->selection_end;
2151 INT l;
2152 INT ll;
2153 INT li;
2154
2155 l = EDIT_EM_LineFromChar(es, e);
2156 ll = EDIT_EM_LineLength(es, e);
2157 li = EDIT_EM_LineIndex(es, l);
2158 if (e - li == 0) {
2159 if (l) {
2160 li = EDIT_EM_LineIndex(es, l - 1);
2161 e = li + EDIT_EM_LineLength(es, li);
2162 }
2163 } else {
2164 e = li + (INT)EDIT_CallWordBreakProc(es,
2165 li, e - li, ll, WB_LEFT);
2166 }
2167 if (!extend)
2168 s = e;
2169 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
2170 EDIT_EM_ScrollCaret(hwnd, es);
2171}
2172
2173
2174/*********************************************************************
2175 *
2176 * EDIT_MoveWordForward
2177 *
2178 */
2179static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend)
2180{
2181 INT s = es->selection_start;
2182 INT e = es->selection_end;
2183 INT l;
2184 INT ll;
2185 INT li;
2186
2187 l = EDIT_EM_LineFromChar(es, e);
2188 ll = EDIT_EM_LineLength(es, e);
2189 li = EDIT_EM_LineIndex(es, l);
2190 if (e - li == ll) {
2191 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2192 e = EDIT_EM_LineIndex(es, l + 1);
2193 } else {
2194 e = li + EDIT_CallWordBreakProc(es,
2195 li, e - li + 1, ll, WB_RIGHT);
2196 }
2197 if (!extend)
2198 s = e;
2199 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
2200 EDIT_EM_ScrollCaret(hwnd, es);
2201}
2202
2203
2204/*********************************************************************
2205 *
2206 * EDIT_PaintLine
2207 *
2208 */
2209static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
2210{
2211 INT s = es->selection_start;
2212 INT e = es->selection_end;
2213 INT li;
2214 INT ll;
2215 INT x;
2216 INT y;
2217 LRESULT pos;
2218
2219 if (es->style & ES_MULTILINE) {
2220 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2221 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2222 return;
2223 } else if (line)
2224 return;
2225
2226 TRACE("line=%d\n", line);
2227
2228 pos = EDIT_EM_PosFromChar(hwnd, es, EDIT_EM_LineIndex(es, line), FALSE);
2229 x = SLOWORD(pos);
2230 y = SHIWORD(pos);
2231 li = EDIT_EM_LineIndex(es, line);
2232 ll = EDIT_EM_LineLength(es, li);
2233 s = es->selection_start;
2234 e = es->selection_end;
2235 ORDER_INT(s, e);
2236 s = min(li + ll, max(li, s));
2237 e = min(li + ll, max(li, e));
2238 if (rev && (s != e) &&
2239 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2240 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2241 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2242 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2243 } else
2244 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2245}
2246
2247
2248/*********************************************************************
2249 *
2250 * EDIT_PaintText
2251 *
2252 */
2253static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2254{
2255 COLORREF BkColor;
2256 COLORREF TextColor;
2257 INT ret;
2258 INT li;
2259 INT BkMode;
2260 SIZE size;
2261
2262 if (!count)
2263 return 0;
2264 BkMode = GetBkMode(dc);
2265 BkColor = GetBkColor(dc);
2266 TextColor = GetTextColor(dc);
2267 if (rev) {
2268 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2269 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2270 SetBkMode( dc, OPAQUE);
2271 }
2272 li = EDIT_EM_LineIndex(es, line);
2273 if (es->style & ES_MULTILINE) {
2274 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2275 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2276 } else {
2277 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2278 TextOutW(dc, x, y, text + li + col, count);
2279 GetTextExtentPoint32W(dc, text + li + col, count, &size);
2280 ret = size.cx;
2281 if (es->style & ES_PASSWORD)
2282 HeapFree(GetProcessHeap(), 0, text);
2283 }
2284 if (rev) {
2285 SetBkColor(dc, BkColor);
2286 SetTextColor(dc, TextColor);
2287 SetBkMode( dc, BkMode);
2288 }
2289 return ret;
2290}
2291
2292
2293/*********************************************************************
2294 *
2295 * EDIT_SetCaretPos
2296 *
2297 */
2298static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos,
2299 BOOL after_wrap)
2300{
2301 LRESULT res = EDIT_EM_PosFromChar(hwnd, es, pos, after_wrap);
2302 SetCaretPos(SLOWORD(res), SHIWORD(res));
2303}
2304
2305
2306/*********************************************************************
2307 *
2308 * EDIT_SetRectNP
2309 *
2310 * note: this is not (exactly) the handler called on EM_SETRECTNP
2311 * it is also used to set the rect of a single line control
2312 *
2313 */
2314static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT rc)
2315{
2316 CopyRect(&es->format_rect, rc);
2317 if (es->style & WS_BORDER) {
2318 INT bw = GetSystemMetrics(SM_CXBORDER) + 1;
2319 if(TWEAK_WineLook == WIN31_LOOK)
2320 bw += 2;
2321 es->format_rect.left += bw;
2322 es->format_rect.top += bw;
2323 es->format_rect.right -= bw;
2324 es->format_rect.bottom -= bw;
2325 }
2326 es->format_rect.left += es->left_margin;
2327 es->format_rect.right -= es->right_margin;
2328 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2329 if (es->style & ES_MULTILINE)
2330 {
2331 INT fw, vlc, max_x_offset, max_y_offset;
2332
2333 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2334 es->format_rect.bottom = es->format_rect.top + max(1, vlc) * es->line_height;
2335
2336 /* correct es->x_offset */
2337 fw = es->format_rect.right - es->format_rect.left;
2338 max_x_offset = es->text_width - fw;
2339 if(max_x_offset < 0) max_x_offset = 0;
2340 if(es->x_offset > max_x_offset)
2341 es->x_offset = max_x_offset;
2342
2343 /* correct es->y_offset */
2344 max_y_offset = es->line_count - vlc;
2345 if(max_y_offset < 0) max_y_offset = 0;
2346 if(es->y_offset > max_y_offset)
2347 es->y_offset = max_y_offset;
2348
2349 /* force scroll info update */
2350 EDIT_UpdateScrollInfo(hwnd, es);
2351 }
2352 else
2353 /* Windows doesn't care to fix text placement for SL controls */
2354 es->format_rect.bottom = es->format_rect.top + es->line_height;
2355
2356 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2357 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
2358}
2359
2360
2361/*********************************************************************
2362 *
2363 * EDIT_UnlockBuffer
2364 *
2365 */
2366static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force)
2367{
2368 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
2369
2370 /* Edit window might be already destroyed */
2371 if(!IsWindow(hwnd))
2372 {
2373 WARN("edit hwnd %04x already destroyed\n", hwnd);
2374 return;
2375 }
2376
2377 if (!es) {
2378 ERR("no EDITSTATE ... please report\n");
2379 return;
2380 }
2381 if (!es->lock_count) {
2382 ERR("lock_count == 0 ... please report\n");
2383 return;
2384 }
2385 if (!es->text) {
2386 ERR("es->text == 0 ... please report\n");
2387 return;
2388 }
2389
2390 if (force || (es->lock_count == 1)) {
2391 if (es->hloc32W) {
2392 CHAR *textA = NULL;
2393 BOOL _16bit = FALSE;
2394 UINT countA = 0;
2395 UINT countW = strlenW(es->text) + 1;
2396
2397 if(es->hloc32A)
2398 {
2399 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2400 TRACE("Synchronizing with 32-bit ANSI buffer\n");
2401 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2402 countA = LocalSize(es->hloc32A);
2403 if(countA_new > countA)
2404 {
2405 HLOCAL hloc32A_new;
2406 UINT alloc_size = ROUND_TO_GROW(countA_new);
2407 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2408 hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2409 if(hloc32A_new)
2410 {
2411 es->hloc32A = hloc32A_new;
2412 countA = LocalSize(hloc32A_new);
2413 TRACE("Real new size %d bytes\n", countA);
2414 }
2415 else
2416 WARN("FAILED! Will synchronize partially\n");
2417 }
2418 textA = LocalLock(es->hloc32A);
2419 }
2420 else if(es->hloc16)
2421 {
2422 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
2423 TRACE("Synchronizing with 16-bit ANSI buffer\n");
2424 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
2425 countA = LOCAL_Size(hInstance, es->hloc16);
2426 if(countA_new > countA)
2427 {
2428 HLOCAL16 hloc16_new;
2429 UINT alloc_size = ROUND_TO_GROW(countA_new);
2430 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
2431 hloc16_new = LOCAL_ReAlloc(hInstance, es->hloc16, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
2432 if(hloc16_new)
2433 {
2434 es->hloc16 = hloc16_new;
2435 countA = LOCAL_Size(hInstance, hloc16_new);
2436 TRACE("Real new size %d bytes\n", countA);
2437 }
2438 else
2439 WARN("FAILED! Will synchronize partially\n");
2440 }
2441 textA = LOCAL_Lock(hInstance, es->hloc16);
2442 _16bit = TRUE;
2443 }
2444 if(textA)
2445 {
2446 WideCharToMultiByte(CP_ACP, 0, es->text, countW, textA, countA, NULL, NULL);
2447 if(_16bit)
2448 LOCAL_Unlock(hInstance, es->hloc16);
2449 else
2450 LocalUnlock(es->hloc32A);
2451 }
2452
2453 LocalUnlock(es->hloc32W);
2454 es->text = NULL;
2455 }
2456 else {
2457 ERR("no buffer ... please report\n");
2458 return;
2459 }
2460 }
2461 es->lock_count--;
2462}
2463
2464
2465/*********************************************************************
2466 *
2467 * EDIT_UpdateScrollInfo
2468 *
2469 */
2470static void EDIT_UpdateScrollInfo(HWND hwnd, EDITSTATE *es)
2471{
2472 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2473 {
2474 SCROLLINFO si;
2475 si.cbSize = sizeof(SCROLLINFO);
2476 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2477 si.nMin = 0;
2478 si.nMax = es->line_count - 1;
2479 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2480 si.nPos = es->y_offset;
2481 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2482 si.nMin, si.nMax, si.nPage, si.nPos);
2483 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2484 }
2485
2486 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2487 {
2488 SCROLLINFO si;
2489 si.cbSize = sizeof(SCROLLINFO);
2490 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2491 si.nMin = 0;
2492 si.nMax = es->text_width - 1;
2493 si.nPage = es->format_rect.right - es->format_rect.left;
2494 si.nPos = es->x_offset;
2495 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
2496 si.nMin, si.nMax, si.nPage, si.nPos);
2497 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2498 }
2499}
2500
2501/*********************************************************************
2502 *
2503 * EDIT_WordBreakProc
2504 *
2505 * Find the beginning of words.
2506 * Note: unlike the specs for a WordBreakProc, this function only
2507 * allows to be called without linebreaks between s[0] upto
2508 * s[count - 1]. Remember it is only called
2509 * internally, so we can decide this for ourselves.
2510 *
2511 */
2512static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action)
2513{
2514 INT ret = 0;
2515
2516 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
2517
2518 if(!s) return 0;
2519
2520 switch (action) {
2521 case WB_LEFT:
2522 if (!count)
2523 break;
2524 if (index)
2525 index--;
2526 if (s[index] == ' ') {
2527 while (index && (s[index] == ' '))
2528 index--;
2529 if (index) {
2530 while (index && (s[index] != ' '))
2531 index--;
2532 if (s[index] == ' ')
2533 index++;
2534 }
2535 } else {
2536 while (index && (s[index] != ' '))
2537 index--;
2538 if (s[index] == ' ')
2539 index++;
2540 }
2541 ret = index;
2542 break;
2543 case WB_RIGHT:
2544 if (!count)
2545 break;
2546 if (index)
2547 index--;
2548 if (s[index] == ' ')
2549 while ((index < count) && (s[index] == ' ')) index++;
2550 else {
2551 while (s[index] && (s[index] != ' ') && (index < count))
2552 index++;
2553 while ((s[index] == ' ') && (index < count)) index++;
2554 }
2555 ret = index;
2556 break;
2557 case WB_ISDELIMITER:
2558 ret = (s[index] == ' ');
2559 break;
2560 default:
2561 ERR("unknown action code, please report !\n");
2562 break;
2563 }
2564 return ret;
2565}
2566
2567
2568/*********************************************************************
2569 *
2570 * EM_CHARFROMPOS
2571 *
2572 * returns line number (not index) in high-order word of result.
2573 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2574 * to Richedit, not to the edit control. Original documentation is valid.
2575 * FIXME: do the specs mean to return -1 if outside client area or
2576 * if outside formatting rectangle ???
2577 *
2578 */
2579static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y)
2580{
2581 POINT pt;
2582 RECT rc;
2583 INT index;
2584
2585 pt.x = x;
2586 pt.y = y;
2587 GetClientRect(hwnd, &rc);
2588 if (!PtInRect(&rc, pt))
2589 return -1;
2590
2591 index = EDIT_CharFromPos(hwnd, es, x, y, NULL);
2592 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2593}
2594
2595
2596/*********************************************************************
2597 *
2598 * EM_FMTLINES
2599 *
2600 * Enable or disable soft breaks.
2601 */
2602static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2603{
2604 es->flags &= ~EF_USE_SOFTBRK;
2605 if (add_eol) {
2606 es->flags |= EF_USE_SOFTBRK;
2607 FIXME("soft break enabled, not implemented\n");
2608 }
2609 return add_eol;
2610}
2611
2612
2613/*********************************************************************
2614 *
2615 * EM_GETHANDLE
2616 *
2617 * Hopefully this won't fire back at us.
2618 * We always start with a fixed buffer in the local heap.
2619 * Despite of the documentation says that the local heap is used
2620 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2621 * buffer on the local heap.
2622 *
2623 */
2624static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2625{
2626 HLOCAL hLocal;
2627
2628 if (!(es->style & ES_MULTILINE))
2629 return 0;
2630
2631 if(es->is_unicode)
2632 hLocal = es->hloc32W;
2633 else
2634 {
2635 if(!es->hloc32A)
2636 {
2637 CHAR *textA;
2638 UINT countA, alloc_size;
2639 TRACE("Allocating 32-bit ANSI alias buffer\n");
2640 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2641 alloc_size = ROUND_TO_GROW(countA);
2642 if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2643 {
2644 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2645 return 0;
2646 }
2647 textA = LocalLock(es->hloc32A);
2648 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2649 LocalUnlock(es->hloc32A);
2650 }
2651 hLocal = es->hloc32A;
2652 }
2653
2654 TRACE("Returning %04X, LocalSize() = %d\n", hLocal, LocalSize(hLocal));
2655 return hLocal;
2656}
2657
2658#ifndef __WIN32OS2__
2659/*********************************************************************
2660 *
2661 * EM_GETHANDLE16
2662 *
2663 * Hopefully this won't fire back at us.
2664 * We always start with a buffer in 32 bit linear memory.
2665 * However, with this message a 16 bit application requests
2666 * a handle of 16 bit local heap memory, where it expects to find
2667 * the text.
2668 * It's a pitty that from this moment on we have to use this
2669 * local heap, because applications may rely on the handle
2670 * in the future.
2671 *
2672 * In this function we'll try to switch to local heap.
2673 */
2674static HLOCAL16 EDIT_EM_GetHandle16(HWND hwnd, EDITSTATE *es)
2675{
2676 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
2677 CHAR *textA;
2678 UINT countA, alloc_size;
2679
2680 if (!(es->style & ES_MULTILINE))
2681 return 0;
2682
2683 if (es->hloc16)
2684 return es->hloc16;
2685
2686 if (!LOCAL_HeapSize(hInstance)) {
2687 if (!LocalInit16(hInstance, 0,
2688 GlobalSize16(hInstance))) {
2689 ERR("could not initialize local heap\n");
2690 return 0;
2691 }
2692 TRACE("local heap initialized\n");
2693 }
2694
2695 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2696 alloc_size = ROUND_TO_GROW(countA);
2697
2698 TRACE("Allocating 16-bit ANSI alias buffer\n");
2699 if (!(es->hloc16 = LOCAL_Alloc(hInstance, LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) {
2700 ERR("could not allocate new 16 bit buffer\n");
2701 return 0;
2702 }
2703
2704 if (!(textA = (LPSTR)LOCAL_Lock(hInstance, es->hloc16))) {
2705 ERR("could not lock new 16 bit buffer\n");
2706 LOCAL_Free(hInstance, es->hloc16);
2707 es->hloc16 = 0;
2708 return 0;
2709 }
2710
2711 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2712 LOCAL_Unlock(hInstance, es->hloc16);
2713
2714 TRACE("Returning %04X, LocalSize() = %d\n", es->hloc16, LOCAL_Size(hInstance, es->hloc16));
2715 return es->hloc16;
2716}
2717#endif
2718
2719/*********************************************************************
2720 *
2721 * EM_GETLINE
2722 *
2723 */
2724static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPARAM lParam, BOOL unicode)
2725{
2726 LPWSTR src;
2727 INT line_len, dst_len;
2728 INT i;
2729
2730 if (es->style & ES_MULTILINE) {
2731 if (line >= es->line_count)
2732 return 0;
2733 } else
2734 line = 0;
2735 i = EDIT_EM_LineIndex(es, line);
2736 src = es->text + i;
2737 line_len = EDIT_EM_LineLength(es, i);
2738 dst_len = *(WORD *)lParam;
2739 if(unicode)
2740 {
2741 LPWSTR dst = (LPWSTR)lParam;
2742 if(dst_len <= line_len)
2743 {
2744 memcpy(dst, src, dst_len * sizeof(WCHAR));
2745 return dst_len;
2746 }
2747 else /* Append 0 if enough space */
2748 {
2749 memcpy(dst, src, line_len * sizeof(WCHAR));
2750 dst[line_len] = 0;
2751 return line_len;
2752 }
2753 }
2754 else
2755 {
2756 LPSTR dst = (LPSTR)lParam;
2757 INT ret;
2758 ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, dst, dst_len, NULL, NULL);
2759 if(!ret) /* Insufficient buffer size */
2760 return dst_len;
2761 if(ret < dst_len) /* Append 0 if enough space */
2762 dst[ret] = 0;
2763 return ret;
2764 }
2765}
2766
2767
2768/*********************************************************************
2769 *
2770 * EM_GETSEL
2771 *
2772 */
2773static LRESULT EDIT_EM_GetSel(EDITSTATE *es, LPUINT start, LPUINT end)
2774{
2775 UINT s = es->selection_start;
2776 UINT e = es->selection_end;
2777
2778 ORDER_UINT(s, e);
2779 if (start)
2780 *start = s;
2781 if (end)
2782 *end = e;
2783 return MAKELONG(s, e);
2784}
2785
2786
2787/*********************************************************************
2788 *
2789 * EM_GETTHUMB
2790 *
2791 * FIXME: is this right ? (or should it be only VSCROLL)
2792 * (and maybe only for edit controls that really have their
2793 * own scrollbars) (and maybe only for multiline controls ?)
2794 * All in all: very poorly documented
2795 *
2796 */
2797static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
2798{
2799 return MAKELONG(EDIT_WM_VScroll(hwnd, es, EM_GETTHUMB16, 0),
2800 EDIT_WM_HScroll(hwnd, es, EM_GETTHUMB16, 0));
2801}
2802
2803
2804/*********************************************************************
2805 *
2806 * EM_LINEFROMCHAR
2807 *
2808 */
2809static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
2810{
2811 INT line;
2812 LINEDEF *line_def;
2813
2814 if (!(es->style & ES_MULTILINE))
2815 return 0;
2816 if (index > (INT)strlenW(es->text))
2817 return es->line_count - 1;
2818 if (index == -1)
2819 index = min(es->selection_start, es->selection_end);
2820
2821 line = 0;
2822 line_def = es->first_line_def;
2823 index -= line_def->length;
2824 while ((index >= 0) && line_def->next) {
2825 line++;
2826 line_def = line_def->next;
2827 index -= line_def->length;
2828 }
2829 return line;
2830}
2831
2832
2833/*********************************************************************
2834 *
2835 * EM_LINEINDEX
2836 *
2837 */
2838static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line)
2839{
2840 INT line_index;
2841 LINEDEF *line_def;
2842
2843 if (!(es->style & ES_MULTILINE))
2844 return 0;
2845 if (line >= es->line_count)
2846 return -1;
2847
2848 line_index = 0;
2849 line_def = es->first_line_def;
2850 if (line == -1) {
2851 INT index = es->selection_end - line_def->length;
2852 while ((index >= 0) && line_def->next) {
2853 line_index += line_def->length;
2854 line_def = line_def->next;
2855 index -= line_def->length;
2856 }
2857 } else {
2858 while (line > 0) {
2859 line_index += line_def->length;
2860 line_def = line_def->next;
2861 line--;
2862 }
2863 }
2864 return line_index;
2865}
2866
2867
2868/*********************************************************************
2869 *
2870 * EM_LINELENGTH
2871 *
2872 */
2873static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
2874{
2875 LINEDEF *line_def;
2876
2877 if (!(es->style & ES_MULTILINE))
2878 return strlenW(es->text);
2879
2880 if (index == -1) {
2881 /* get the number of remaining non-selected chars of selected lines */
2882 INT32 l; /* line number */
2883 INT32 li; /* index of first char in line */
2884 INT32 count;
2885 l = EDIT_EM_LineFromChar(es, es->selection_start);
2886 /* # chars before start of selection area */
2887 count = es->selection_start - EDIT_EM_LineIndex(es, l);
2888 l = EDIT_EM_LineFromChar(es, es->selection_end);
2889 /* # chars after end of selection */
2890 li = EDIT_EM_LineIndex(es, l);
2891 count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
2892 return count;
2893 }
2894 line_def = es->first_line_def;
2895 index -= line_def->length;
2896 while ((index >= 0) && line_def->next) {
2897 line_def = line_def->next;
2898 index -= line_def->length;
2899 }
2900 return line_def->net_length;
2901}
2902
2903
2904/*********************************************************************
2905 *
2906 * EM_LINESCROLL
2907 *
2908 * NOTE: dx is in average character widths, dy - in lines;
2909 *
2910 */
2911static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2912{
2913 if (!(es->style & ES_MULTILINE))
2914 return FALSE;
2915
2916 dx *= es->char_width;
2917 return EDIT_EM_LineScroll_internal(hwnd, es, dx, dy);
2918}
2919
2920/*********************************************************************
2921 *
2922 * EDIT_EM_LineScroll_internal
2923 *
2924 * Version of EDIT_EM_LineScroll for internal use.
2925 * It doesn't refuse if ES_MULTILINE is set and assumes that
2926 * dx is in pixels, dy - in lines.
2927 *
2928 */
2929static BOOL EDIT_EM_LineScroll_internal(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2930{
2931 INT nyoff;
2932 INT x_offset_in_pixels;
2933
2934 if (es->style & ES_MULTILINE)
2935 {
2936 x_offset_in_pixels = es->x_offset;
2937 }
2938 else
2939 {
2940 dy = 0;
2941 x_offset_in_pixels = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->x_offset, FALSE));
2942 }
2943
2944 if (-dx > x_offset_in_pixels)
2945 dx = -x_offset_in_pixels;
2946 if (dx > es->text_width - x_offset_in_pixels)
2947 dx = es->text_width - x_offset_in_pixels;
2948 nyoff = max(0, es->y_offset + dy);
2949 if (nyoff >= es->line_count)
2950 nyoff = es->line_count - 1;
2951 dy = (es->y_offset - nyoff) * es->line_height;
2952 if (dx || dy) {
2953 RECT rc1;
2954 RECT rc;
2955
2956 es->y_offset = nyoff;
2957 if(es->style & ES_MULTILINE)
2958 es->x_offset += dx;
2959 else
2960 es->x_offset += dx / es->char_width;
2961
2962 GetClientRect(hwnd, &rc1);
2963 IntersectRect(&rc, &rc1, &es->format_rect);
2964 ScrollWindowEx(hwnd, -dx, dy,
2965 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2966 /* force scroll info update */
2967 EDIT_UpdateScrollInfo(hwnd, es);
2968 }
2969 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2970 EDIT_NOTIFY_PARENT(hwnd, es, EN_HSCROLL, "EN_HSCROLL");
2971 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2972 EDIT_NOTIFY_PARENT(hwnd, es, EN_VSCROLL, "EN_VSCROLL");
2973 return TRUE;
2974}
2975
2976
2977/*********************************************************************
2978 *
2979 * EM_POSFROMCHAR
2980 *
2981 */
2982static LRESULT EDIT_EM_PosFromChar(HWND hwnd, EDITSTATE *es, INT index, BOOL after_wrap)
2983{
2984 INT len = strlenW(es->text);
2985 INT l;
2986 INT li;
2987 INT x;
2988 INT y = 0;
2989 HDC dc;
2990 HFONT old_font = 0;
2991 SIZE size;
2992
2993 index = min(index, len);
2994 dc = GetDC(hwnd);
2995 if (es->font)
2996 old_font = SelectObject(dc, es->font);
2997 if (es->style & ES_MULTILINE) {
2998 l = EDIT_EM_LineFromChar(es, index);
2999 y = (l - es->y_offset) * es->line_height;
3000 li = EDIT_EM_LineIndex(es, l);
3001 if (after_wrap && (li == index) && l) {
3002 INT l2 = l - 1;
3003 LINEDEF *line_def = es->first_line_def;
3004 while (l2) {
3005 line_def = line_def->next;
3006 l2--;
3007 }
3008 if (line_def->ending == END_WRAP) {
3009 l--;
3010 y -= es->line_height;
3011 li = EDIT_EM_LineIndex(es, l);
3012 }
3013 }
3014 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
3015 es->tabs_count, es->tabs)) - es->x_offset;
3016 } else {
3017 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
3018 if (index < es->x_offset) {
3019 GetTextExtentPoint32W(dc, text + index,
3020 es->x_offset - index, &size);
3021 x = -size.cx;
3022 } else {
3023 GetTextExtentPoint32W(dc, text + es->x_offset,
3024 index - es->x_offset, &size);
3025 x = size.cx;
3026 }
3027 y = 0;
3028 if (es->style & ES_PASSWORD)
3029 HeapFree(GetProcessHeap(), 0, text);
3030 }
3031 x += es->format_rect.left;
3032 y += es->format_rect.top;
3033 if (es->font)
3034 SelectObject(dc, old_font);
3035 ReleaseDC(hwnd, dc);
3036 return MAKELONG((INT16)x, (INT16)y);
3037}
3038
3039
3040/*********************************************************************
3041 *
3042 * EM_REPLACESEL
3043 *
3044 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
3045 *
3046 */
3047static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update)
3048{
3049 UINT strl = strlenW(lpsz_replace);
3050 UINT tl = strlenW(es->text);
3051 UINT utl;
3052 UINT s;
3053 UINT e;
3054 UINT i;
3055 LPWSTR p;
3056 HRGN hrgn = 0;
3057
3058 TRACE("%s, can_undo %d, send_update %d\n",
3059 debugstr_w(lpsz_replace), can_undo, send_update);
3060
3061 s = es->selection_start;
3062 e = es->selection_end;
3063
3064 if ((s == e) && !strl)
3065 return;
3066
3067 ORDER_UINT(s, e);
3068
3069 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
3070 return;
3071
3072 if (e != s) {
3073 /* there is something to be deleted */
3074 TRACE("deleting stuff.\n");
3075 if (can_undo) {
3076 utl = strlenW(es->undo_text);
3077 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
3078 /* undo-buffer is extended to the right */
3079 EDIT_MakeUndoFit(es, utl + e - s);
3080 strncpyW(es->undo_text + utl, es->text + s, e - s + 1);
3081 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
3082 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
3083 /* undo-buffer is extended to the left */
3084 EDIT_MakeUndoFit(es, utl + e - s);
3085 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
3086 p[e - s] = p[0];
3087 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
3088 p[i] = (es->text + s)[i];
3089 es->undo_position = s;
3090 } else {
3091 /* new undo-buffer */
3092 EDIT_MakeUndoFit(es, e - s);
3093 strncpyW(es->undo_text, es->text + s, e - s + 1);
3094 es->undo_text[e - s] = 0; /* ensure 0 termination */
3095 es->undo_position = s;
3096 }
3097 /* any deletion makes the old insertion-undo invalid */
3098 es->undo_insert_count = 0;
3099 } else
3100 EDIT_EM_EmptyUndoBuffer(es);
3101
3102 /* now delete */
3103 strcpyW(es->text + s, es->text + e);
3104 }
3105 if (strl) {
3106 /* there is an insertion */
3107 if (can_undo) {
3108 if ((s == es->undo_position) ||
3109 ((es->undo_insert_count) &&
3110 (s == es->undo_position + es->undo_insert_count)))
3111 /*
3112 * insertion is new and at delete position or
3113 * an extension to either left or right
3114 */
3115 es->undo_insert_count += strl;
3116 else {
3117 /* new insertion undo */
3118 es->undo_position = s;
3119 es->undo_insert_count = strl;
3120 /* new insertion makes old delete-buffer invalid */
3121 *es->undo_text = '\0';
3122 }
3123 } else
3124 EDIT_EM_EmptyUndoBuffer(es);
3125
3126 /* now insert */
3127 tl = strlenW(es->text);
3128 TRACE("inserting stuff (tl %d, strl %d, selstart %d ('%s'), text '%s')\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text));
3129 for (p = es->text + tl ; p >= es->text + s ; p--)
3130 p[strl] = p[0];
3131 for (i = 0 , p = es->text + s ; i < strl ; i++)
3132 p[i] = lpsz_replace[i];
3133 if(es->style & ES_UPPERCASE)
3134 CharUpperBuffW(p, strl);
3135 else if(es->style & ES_LOWERCASE)
3136 CharLowerBuffW(p, strl);
3137 s += strl;
3138 }
3139 if (es->style & ES_MULTILINE)
3140 {
3141 INT s = min(es->selection_start, es->selection_end);
3142
3143 hrgn = CreateRectRgn(0, 0, 0, 0);
3144 EDIT_BuildLineDefs_ML(hwnd, es, s, s + strl,
3145 strl - abs(es->selection_end - es->selection_start), hrgn);
3146 }
3147 else
3148 EDIT_CalcLineWidth_SL(hwnd, es);
3149
3150 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
3151 es->flags |= EF_MODIFIED;
3152 if (send_update) es->flags |= EF_UPDATE;
3153 EDIT_EM_ScrollCaret(hwnd, es);
3154
3155 /* force scroll info update */
3156 EDIT_UpdateScrollInfo(hwnd, es);
3157
3158 if (hrgn)
3159 {
3160 EDIT_UpdateTextRegion(hwnd, es, hrgn, TRUE);
3161 DeleteObject(hrgn);
3162 }
3163 else
3164 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3165
3166 if(es->flags & EF_UPDATE)
3167 {
3168 es->flags &= ~EF_UPDATE;
3169 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
3170 }
3171}
3172
3173
3174/*********************************************************************
3175 *
3176 * EM_SCROLL
3177 *
3178 */
3179static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
3180{
3181 INT dy;
3182
3183 if (!(es->style & ES_MULTILINE))
3184 return (LRESULT)FALSE;
3185
3186 dy = 0;
3187
3188 switch (action) {
3189 case SB_LINEUP:
3190 if (es->y_offset)
3191 dy = -1;
3192 break;
3193 case SB_LINEDOWN:
3194 if (es->y_offset < es->line_count - 1)
3195 dy = 1;
3196 break;
3197 case SB_PAGEUP:
3198 if (es->y_offset)
3199 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
3200 break;
3201 case SB_PAGEDOWN:
3202 if (es->y_offset < es->line_count - 1)
3203 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3204 break;
3205 default:
3206 return (LRESULT)FALSE;
3207 }
3208 if (dy) {
3209 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3210 /* check if we are going to move too far */
3211 if(es->y_offset + dy > es->line_count - vlc)
3212 dy = es->line_count - vlc - es->y_offset;
3213
3214 /* Notification is done in EDIT_EM_LineScroll */
3215 if(dy)
3216 EDIT_EM_LineScroll(hwnd, es, 0, dy);
3217 }
3218 return MAKELONG((INT16)dy, (BOOL16)TRUE);
3219}
3220
3221
3222/*********************************************************************
3223 *
3224 * EM_SCROLLCARET
3225 *
3226 */
3227static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
3228{
3229 if (es->style & ES_MULTILINE) {
3230 INT l;
3231 INT li;
3232 INT vlc;
3233 INT ww;
3234 INT cw = es->char_width;
3235 INT x;
3236 INT dy = 0;
3237 INT dx = 0;
3238
3239 l = EDIT_EM_LineFromChar(es, es->selection_end);
3240 li = EDIT_EM_LineIndex(es, l);
3241 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
3242 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3243 if (l >= es->y_offset + vlc)
3244 dy = l - vlc + 1 - es->y_offset;
3245 if (l < es->y_offset)
3246 dy = l - es->y_offset;
3247 ww = es->format_rect.right - es->format_rect.left;
3248 if (x < es->format_rect.left)
3249 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
3250 if (x > es->format_rect.right)
3251 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
3252 if (dy || dx)
3253 {
3254 /* check if we are going to move too far */
3255 if(es->x_offset + dx + ww > es->text_width)
3256 dx = es->text_width - ww - es->x_offset;
3257 if(dx || dy)
3258 EDIT_EM_LineScroll_internal(hwnd, es, dx, dy);
3259 }
3260 } else {
3261 INT x;
3262 INT goal;
3263 INT format_width;
3264
3265 if (!(es->style & ES_AUTOHSCROLL))
3266 return;
3267
3268 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3269 format_width = es->format_rect.right - es->format_rect.left;
3270 if (x < es->format_rect.left) {
3271 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
3272 do {
3273 es->x_offset--;
3274 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3275 } while ((x < goal) && es->x_offset);
3276 /* FIXME: use ScrollWindow() somehow to improve performance */
3277 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3278 } else if (x > es->format_rect.right) {
3279 INT x_last;
3280 INT len = strlenW(es->text);
3281 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
3282 do {
3283 es->x_offset++;
3284 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3285 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, len, FALSE));
3286 } while ((x > goal) && (x_last > es->format_rect.right));
3287 /* FIXME: use ScrollWindow() somehow to improve performance */
3288 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3289 }
3290 }
3291
3292 if(es->flags & EF_FOCUSED)
3293 EDIT_SetCaretPos(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3294}
3295
3296
3297/*********************************************************************
3298 *
3299 * EM_SETHANDLE
3300 *
3301 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3302 *
3303 */
3304static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
3305{
3306 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
3307
3308 if (!(es->style & ES_MULTILINE))
3309 return;
3310
3311 if (!hloc) {
3312 WARN("called with NULL handle\n");
3313 return;
3314 }
3315
3316 EDIT_UnlockBuffer(hwnd, es, TRUE);
3317
3318#ifndef __WIN32OS2__
3319 if(es->hloc16)
3320 {
3321 LOCAL_Free(hInstance, es->hloc16);
3322 es->hloc16 = (HLOCAL16)NULL;
3323 }
3324#endif
3325 if(es->is_unicode)
3326 {
3327 if(es->hloc32A)
3328 {
3329 LocalFree(es->hloc32A);
3330 es->hloc32A = (HLOCAL)NULL;
3331 }
3332 es->hloc32W = hloc;
3333 }
3334 else
3335 {
3336 INT countW, countA;
3337 HLOCAL hloc32W_new;
3338 WCHAR *textW;
3339 CHAR *textA;
3340
3341 countA = LocalSize(hloc);
3342 textA = LocalLock(hloc);
3343 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3344 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3345 {
3346 ERR("Could not allocate new unicode buffer\n");
3347 return;
3348 }
3349 textW = LocalLock(hloc32W_new);
3350 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3351 LocalUnlock(hloc32W_new);
3352 LocalUnlock(hloc);
3353
3354 if(es->hloc32W)
3355 LocalFree(es->hloc32W);
3356
3357 es->hloc32W = hloc32W_new;
3358 es->hloc32A = hloc;
3359 }
3360
3361 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3362
3363 EDIT_LockBuffer(hwnd, es);
3364
3365 es->x_offset = es->y_offset = 0;
3366 es->selection_start = es->selection_end = 0;
3367 EDIT_EM_EmptyUndoBuffer(es);
3368 es->flags &= ~EF_MODIFIED;
3369 es->flags &= ~EF_UPDATE;
3370 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3371 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3372 EDIT_EM_ScrollCaret(hwnd, es);
3373 /* force scroll info update */
3374 EDIT_UpdateScrollInfo(hwnd, es);
3375}
3376
3377
3378#ifndef __WIN32OS2__
3379/*********************************************************************
3380 *
3381 * EM_SETHANDLE16
3382 *
3383 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3384 *
3385 */
3386static void EDIT_EM_SetHandle16(HWND hwnd, EDITSTATE *es, HLOCAL16 hloc)
3387{
3388 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
3389 INT countW, countA;
3390 HLOCAL hloc32W_new;
3391 WCHAR *textW;
3392 CHAR *textA;
3393
3394 if (!(es->style & ES_MULTILINE))
3395 return;
3396
3397 if (!hloc) {
3398 WARN("called with NULL handle\n");
3399 return;
3400 }
3401
3402 EDIT_UnlockBuffer(hwnd, es, TRUE);
3403
3404 if(es->hloc32A)
3405 {
3406 LocalFree(es->hloc32A);
3407 es->hloc32A = (HLOCAL)NULL;
3408 }
3409
3410 countA = LOCAL_Size(hInstance, hloc);
3411 textA = LOCAL_Lock(hInstance, hloc);
3412 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3413 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3414 {
3415 ERR("Could not allocate new unicode buffer\n");
3416 return;
3417 }
3418 textW = LocalLock(hloc32W_new);
3419 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3420 LocalUnlock(hloc32W_new);
3421 LOCAL_Unlock(hInstance, hloc);
3422
3423 if(es->hloc32W)
3424 LocalFree(es->hloc32W);
3425
3426 es->hloc32W = hloc32W_new;
3427 es->hloc16 = hloc;
3428
3429 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3430
3431 EDIT_LockBuffer(hwnd, es);
3432
3433 es->x_offset = es->y_offset = 0;
3434 es->selection_start = es->selection_end = 0;
3435 EDIT_EM_EmptyUndoBuffer(es);
3436 es->flags &= ~EF_MODIFIED;
3437 es->flags &= ~EF_UPDATE;
3438 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3439 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3440 EDIT_EM_ScrollCaret(hwnd, es);
3441 /* force scroll info update */
3442 EDIT_UpdateScrollInfo(hwnd, es);
3443}
3444#endif
3445
3446/*********************************************************************
3447 *
3448 * EM_SETLIMITTEXT
3449 *
3450 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3451 * However, the windows version is not complied to yet in all of edit.c
3452 *
3453 */
3454static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit)
3455{
3456 if (es->style & ES_MULTILINE) {
3457 if (limit)
3458 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
3459 else
3460 es->buffer_limit = BUFLIMIT_MULTI;
3461 } else {
3462 if (limit)
3463 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
3464 else
3465 es->buffer_limit = BUFLIMIT_SINGLE;
3466 }
3467}
3468
3469
3470/*********************************************************************
3471 *
3472 * EM_SETMARGINS
3473 *
3474 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3475 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3476 * of the char's width, according to the new docs.
3477 *
3478 */
3479static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
3480 INT left, INT right)
3481{
3482 if (action & EC_LEFTMARGIN) {
3483 if (left != EC_USEFONTINFO)
3484 es->left_margin = left;
3485 else
3486 es->left_margin = es->char_width / 3;
3487 }
3488
3489 if (action & EC_RIGHTMARGIN) {
3490 if (right != EC_USEFONTINFO)
3491 es->right_margin = right;
3492 else
3493 es->right_margin = es->char_width / 3;
3494 }
3495 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3496}
3497
3498
3499/*********************************************************************
3500 *
3501 * EM_SETPASSWORDCHAR
3502 *
3503 */
3504static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, WCHAR c)
3505{
3506 LONG style;
3507
3508 if (es->style & ES_MULTILINE)
3509 return;
3510
3511 if (es->password_char == c)
3512 return;
3513
3514 style = GetWindowLongA( hwnd, GWL_STYLE );
3515 es->password_char = c;
3516 if (c) {
3517 SetWindowLongA( hwnd, GWL_STYLE, style | ES_PASSWORD );
3518 es->style |= ES_PASSWORD;
3519 } else {
3520 SetWindowLongA( hwnd, GWL_STYLE, style & ~ES_PASSWORD );
3521 es->style &= ~ES_PASSWORD;
3522 }
3523 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3524}
3525
3526
3527/*********************************************************************
3528 *
3529 * EDIT_EM_SetSel
3530 *
3531 * note: unlike the specs say: the order of start and end
3532 * _is_ preserved in Windows. (i.e. start can be > end)
3533 * In other words: this handler is OK
3534 *
3535 */
3536static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
3537{
3538 UINT old_start = es->selection_start;
3539 UINT old_end = es->selection_end;
3540 UINT len = strlenW(es->text);
3541
3542 if (start == (UINT)-1) {
3543 start = es->selection_end;
3544 end = es->selection_end;
3545 } else {
3546 start = min(start, len);
3547 end = min(end, len);
3548 }
3549 es->selection_start = start;
3550 es->selection_end = end;
3551 if (after_wrap)
3552 es->flags |= EF_AFTER_WRAP;
3553 else
3554 es->flags &= ~EF_AFTER_WRAP;
3555/* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3556 ORDER_UINT(start, end);
3557 ORDER_UINT(end, old_end);
3558 ORDER_UINT(start, old_start);
3559 ORDER_UINT(old_start, old_end);
3560 if (end != old_start)
3561 {
3562/*
3563 * One can also do
3564 * ORDER_UINT32(end, old_start);
3565 * EDIT_InvalidateText(hwnd, es, start, end);
3566 * EDIT_InvalidateText(hwnd, es, old_start, old_end);
3567 * in place of the following if statement.
3568 */
3569 if (old_start > end )
3570 {
3571 EDIT_InvalidateText(hwnd, es, start, end);
3572 EDIT_InvalidateText(hwnd, es, old_start, old_end);
3573 }
3574 else
3575 {
3576 EDIT_InvalidateText(hwnd, es, start, old_start);
3577 EDIT_InvalidateText(hwnd, es, end, old_end);
3578 }
3579 }
3580 else EDIT_InvalidateText(hwnd, es, start, old_end);
3581}
3582
3583
3584/*********************************************************************
3585 *
3586 * EM_SETTABSTOPS
3587 *
3588 */
3589static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs)
3590{
3591 if (!(es->style & ES_MULTILINE))
3592 return FALSE;
3593 if (es->tabs)
3594 HeapFree(GetProcessHeap(), 0, es->tabs);
3595 es->tabs_count = count;
3596 if (!count)
3597 es->tabs = NULL;
3598 else {
3599 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3600 memcpy(es->tabs, tabs, count * sizeof(INT));
3601 }
3602 return TRUE;
3603}
3604
3605
3606/*********************************************************************
3607 *
3608 * EM_SETTABSTOPS16
3609 *
3610 */
3611static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs)
3612{
3613 if (!(es->style & ES_MULTILINE))
3614 return FALSE;
3615 if (es->tabs)
3616 HeapFree(GetProcessHeap(), 0, es->tabs);
3617 es->tabs_count = count;
3618 if (!count)
3619 es->tabs = NULL;
3620 else {
3621 INT i;
3622 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3623 for (i = 0 ; i < count ; i++)
3624 es->tabs[i] = *tabs++;
3625 }
3626 return TRUE;
3627}
3628
3629
3630/*********************************************************************
3631 *
3632 * EM_SETWORDBREAKPROC
3633 *
3634 */
3635static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, LPARAM lParam)
3636{
3637 if (es->word_break_proc == (void *)lParam)
3638 return;
3639
3640 es->word_break_proc = (void *)lParam;
3641 es->word_break_proc16 = NULL;
3642
3643 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3644 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3645 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3646 }
3647}
3648
3649
3650#ifndef __WIN32OS2__
3651/*********************************************************************
3652 *
3653 * EM_SETWORDBREAKPROC16
3654 *
3655 */
3656static void EDIT_EM_SetWordBreakProc16(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
3657{
3658 if (es->word_break_proc16 == wbp)
3659 return;
3660
3661 es->word_break_proc = NULL;
3662 es->word_break_proc16 = wbp;
3663 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3664 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3665 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3666 }
3667}
3668#endif
3669
3670/*********************************************************************
3671 *
3672 * EM_UNDO / WM_UNDO
3673 *
3674 */
3675static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
3676{
3677 INT ulength;
3678 LPWSTR utext;
3679
3680 /* Protect read-only edit control from modification */
3681 if(es->style & ES_READONLY)
3682 return FALSE;
3683
3684 ulength = strlenW(es->undo_text);
3685 utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3686
3687 strcpyW(utext, es->undo_text);
3688
3689 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3690 es->undo_insert_count, debugstr_w(utext));
3691
3692 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3693 EDIT_EM_EmptyUndoBuffer(es);
3694 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext, FALSE);
3695 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3696 /* send the notification after the selection start and end are set */
3697 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
3698 EDIT_EM_ScrollCaret(hwnd, es);
3699 HeapFree(GetProcessHeap(), 0, utext);
3700
3701 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3702 es->undo_insert_count, debugstr_w(es->undo_text));
3703 return TRUE;
3704}
3705
3706
3707/*********************************************************************
3708 *
3709 * WM_CHAR
3710 *
3711 */
3712static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, WCHAR c)
3713{
3714 BOOL control;
3715
3716 /* Protect read-only edit control from modification */
3717 if(es->style & ES_READONLY)
3718 return;
3719
3720 control = GetKeyState(VK_CONTROL) & 0x8000;
3721
3722 switch (c) {
3723 case '\r':
3724 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3725 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3726 break;
3727 case '\n':
3728 if (es->style & ES_MULTILINE) {
3729 if (es->style & ES_READONLY) {
3730 EDIT_MoveHome(hwnd, es, FALSE);
3731 EDIT_MoveDown_ML(hwnd, es, FALSE);
3732 } else {
3733 static const WCHAR cr_lfW[] = {'\r','\n',0};
3734 EDIT_EM_ReplaceSel(hwnd, es, TRUE, cr_lfW, TRUE);
3735 }
3736 }
3737 break;
3738 case '\t':
3739 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3740 {
3741 static const WCHAR tabW[] = {'\t',0};
3742 EDIT_EM_ReplaceSel(hwnd, es, TRUE, tabW, TRUE);
3743 }
3744 break;
3745 case VK_BACK:
3746 if (!(es->style & ES_READONLY) && !control) {
3747 if (es->selection_start != es->selection_end)
3748 EDIT_WM_Clear(hwnd, es);
3749 else {
3750 /* delete character left of caret */
3751 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
3752 EDIT_MoveBackward(hwnd, es, TRUE);
3753 EDIT_WM_Clear(hwnd, es);
3754 }
3755 }
3756 break;
3757 case 0x03: /* ^C */
3758 SendMessageW(hwnd, WM_COPY, 0, 0);
3759 break;
3760 case 0x16: /* ^V */
3761 SendMessageW(hwnd, WM_PASTE, 0, 0);
3762 break;
3763 case 0x18: /* ^X */
3764 SendMessageW(hwnd, WM_CUT, 0, 0);
3765 break;
3766
3767 default:
3768 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3769 WCHAR str[2];
3770 str[0] = c;
3771 str[1] = '\0';
3772 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str, TRUE);
3773 }
3774 break;
3775 }
3776}
3777
3778
3779/*********************************************************************
3780 *
3781 * WM_COMMAND
3782 *
3783 */
3784static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
3785{
3786 if (code || control)
3787 return;
3788
3789 switch (id) {
3790 case EM_UNDO:
3791 EDIT_EM_Undo(hwnd, es);
3792 break;
3793 case WM_CUT:
3794 EDIT_WM_Cut(hwnd, es);
3795 break;
3796 case WM_COPY:
3797 EDIT_WM_Copy(hwnd, es);
3798 break;
3799 case WM_PASTE:
3800 EDIT_WM_Paste(hwnd, es);
3801 break;
3802 case WM_CLEAR:
3803 EDIT_WM_Clear(hwnd, es);
3804 break;
3805 case EM_SETSEL:
3806 EDIT_EM_SetSel(hwnd, es, 0, (UINT)-1, FALSE);
3807 EDIT_EM_ScrollCaret(hwnd, es);
3808 break;
3809 default:
3810 ERR("unknown menu item, please report\n");
3811 break;
3812 }
3813}
3814
3815
3816/*********************************************************************
3817 *
3818 * WM_CONTEXTMENU
3819 *
3820 * Note: the resource files resource/sysres_??.rc cannot define a
3821 * single popup menu. Hence we use a (dummy) menubar
3822 * containing the single popup menu as its first item.
3823 *
3824 * FIXME: the message identifiers have been chosen arbitrarily,
3825 * hence we use MF_BYPOSITION.
3826 * We might as well use the "real" values (anybody knows ?)
3827 * The menu definition is in resources/sysres_??.rc.
3828 * Once these are OK, we better use MF_BYCOMMAND here
3829 * (as we do in EDIT_WM_Command()).
3830 *
3831 */
3832static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, INT x, INT y)
3833{
3834 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3835 HMENU popup = GetSubMenu(menu, 0);
3836 UINT start = es->selection_start;
3837 UINT end = es->selection_end;
3838
3839 ORDER_UINT(start, end);
3840
3841 /* undo */
3842 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3843 /* cut */
3844 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3845 /* copy */
3846 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3847 /* paste */
3848 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3849 /* delete */
3850 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3851 /* select all */
3852 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlenW(es->text)) ? MF_ENABLED : MF_GRAYED));
3853
3854 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
3855 DestroyMenu(menu);
3856}
3857
3858
3859/*********************************************************************
3860 *
3861 * WM_COPY
3862 *
3863 */
3864static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
3865{
3866 INT s = es->selection_start;
3867 INT e = es->selection_end;
3868 HGLOBAL hdst;
3869 LPWSTR dst;
3870
3871 if (e == s)
3872 return;
3873 ORDER_INT(s, e);
3874 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (DWORD)(e - s + 1) * sizeof(WCHAR));
3875 dst = GlobalLock(hdst);
3876 strncpyW(dst, es->text + s, e - s);
3877 dst[e - s ] = 0; /* ensure 0 termination */
3878 TRACE("%s\n", debugstr_w(dst));
3879
3880 GlobalUnlock(hdst);
3881 OpenClipboard(hwnd);
3882 EmptyClipboard();
3883 SetClipboardData(CF_UNICODETEXT, hdst);
3884 CloseClipboard();
3885}
3886
3887
3888/*********************************************************************
3889 *
3890 * WM_CREATE
3891 *
3892 */
3893static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCWSTR name)
3894{
3895 TRACE("%s\n", debugstr_w(name));
3896 /*
3897 * To initialize some final structure members, we call some helper
3898 * functions. However, since the EDITSTATE is not consistent (i.e.
3899 * not fully initialized), we should be very careful which
3900 * functions can be called, and in what order.
3901 */
3902 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
3903 EDIT_EM_EmptyUndoBuffer(es);
3904
3905 if (name && *name) {
3906 EDIT_EM_ReplaceSel(hwnd, es, FALSE, name, FALSE);
3907 /* if we insert text to the editline, the text scrolls out
3908 * of the window, as the caret is placed after the insert
3909 * pos normally; thus we reset es->selection... to 0 and
3910 * update caret
3911 */
3912 es->selection_start = es->selection_end = 0;
3913 /* send the notification after the selection start and end are set */
3914 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
3915 EDIT_EM_ScrollCaret(hwnd, es);
3916 }
3917 /* force scroll info update */
3918 EDIT_UpdateScrollInfo(hwnd, es);
3919 return 0;
3920}
3921
3922
3923/*********************************************************************
3924 *
3925 * WM_DESTROY
3926 *
3927 */
3928static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
3929{
3930 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
3931 LINEDEF *pc, *pp;
3932
3933 if (es->hloc32W) {
3934 while (LocalUnlock(es->hloc32W)) ;
3935 LocalFree(es->hloc32W);
3936 }
3937 if (es->hloc32A) {
3938 while (LocalUnlock(es->hloc32A)) ;
3939 LocalFree(es->hloc32A);
3940 }
3941#ifndef __WIN32OS2__
3942 if (es->hloc16) {
3943 while (LOCAL_Unlock(hInstance, es->hloc16)) ;
3944 LOCAL_Free(hInstance, es->hloc16);
3945 }
3946#endif
3947 pc = es->first_line_def;
3948 while (pc)
3949 {
3950 pp = pc->next;
3951 HeapFree(GetProcessHeap(), 0, pc);
3952 pc = pp;
3953 }
3954
3955 SetWindowLongA( hwnd, 0, 0 );
3956 HeapFree(GetProcessHeap(), 0, es);
3957}
3958
3959
3960/*********************************************************************
3961 *
3962 * WM_ERASEBKGND
3963 *
3964 */
3965static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
3966{
3967 HBRUSH brush;
3968 RECT rc;
3969
3970 if ( get_app_version() >= 0x40000 &&(
3971 !es->bEnableState || (es->style & ES_READONLY)))
3972 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
3973 else
3974 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
3975
3976 if (!brush)
3977 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3978
3979 GetClientRect(hwnd, &rc);
3980 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3981 GetClipBox(dc, &rc);
3982 /*
3983 * FIXME: specs say that we should UnrealizeObject() the brush,
3984 * but the specs of UnrealizeObject() say that we shouldn't
3985 * unrealize a stock object. The default brush that
3986 * DefWndProc() returns is ... a stock object.
3987 */
3988 FillRect(dc, &rc, brush);
3989 return -1;
3990}
3991
3992
3993/*********************************************************************
3994 *
3995 * WM_GETTEXT
3996 *
3997 */
3998static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode)
3999{
4000 if(!count) return 0;
4001
4002 if(unicode)
4003 {
4004 LPWSTR textW = (LPWSTR)lParam;
4005 strncpyW(textW, es->text, count);
4006 textW[count - 1] = 0; /* ensure 0 termination */
4007 return strlenW(textW);
4008 }
4009 else
4010 {
4011 LPSTR textA = (LPSTR)lParam;
4012 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL);
4013#ifdef __WIN32OS2__
4014 lstrtrunc( textA, count );
4015#else
4016 textA[count - 1] = 0; /* ensure 0 termination */
4017#endif
4018 return strlen(textA);
4019 }
4020}
4021
4022/*********************************************************************
4023 *
4024 * WM_HSCROLL
4025 *
4026 */
4027static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos)
4028{
4029 INT dx;
4030 INT fw;
4031
4032 if (!(es->style & ES_MULTILINE))
4033 return 0;
4034
4035 if (!(es->style & ES_AUTOHSCROLL))
4036 return 0;
4037
4038 dx = 0;
4039 fw = es->format_rect.right - es->format_rect.left;
4040 switch (action) {
4041 case SB_LINELEFT:
4042 TRACE("SB_LINELEFT\n");
4043 if (es->x_offset)
4044 dx = -es->char_width;
4045 break;
4046 case SB_LINERIGHT:
4047 TRACE("SB_LINERIGHT\n");
4048 if (es->x_offset < es->text_width)
4049 dx = es->char_width;
4050 break;
4051 case SB_PAGELEFT:
4052 TRACE("SB_PAGELEFT\n");
4053 if (es->x_offset)
4054 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4055 break;
4056 case SB_PAGERIGHT:
4057 TRACE("SB_PAGERIGHT\n");
4058 if (es->x_offset < es->text_width)
4059 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4060 break;
4061 case SB_LEFT:
4062 TRACE("SB_LEFT\n");
4063 if (es->x_offset)
4064 dx = -es->x_offset;
4065 break;
4066 case SB_RIGHT:
4067 TRACE("SB_RIGHT\n");
4068 if (es->x_offset < es->text_width)
4069 dx = es->text_width - es->x_offset;
4070 break;
4071 case SB_THUMBTRACK:
4072 TRACE("SB_THUMBTRACK %d\n", pos);
4073 es->flags |= EF_HSCROLL_TRACK;
4074 if(es->style & WS_HSCROLL)
4075 dx = pos - es->x_offset;
4076 else
4077 {
4078 INT fw, new_x;
4079 /* Sanity check */
4080 if(pos < 0 || pos > 100) return 0;
4081 /* Assume default scroll range 0-100 */
4082 fw = es->format_rect.right - es->format_rect.left;
4083 new_x = pos * (es->text_width - fw) / 100;
4084 dx = es->text_width ? (new_x - es->x_offset) : 0;
4085 }
4086 break;
4087 case SB_THUMBPOSITION:
4088 TRACE("SB_THUMBPOSITION %d\n", pos);
4089 es->flags &= ~EF_HSCROLL_TRACK;
4090 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_HSCROLL)
4091 dx = pos - es->x_offset;
4092 else
4093 {
4094 INT fw, new_x;
4095 /* Sanity check */
4096 if(pos < 0 || pos > 100) return 0;
4097 /* Assume default scroll range 0-100 */
4098 fw = es->format_rect.right - es->format_rect.left;
4099 new_x = pos * (es->text_width - fw) / 100;
4100 dx = es->text_width ? (new_x - es->x_offset) : 0;
4101 }
4102 if (!dx) {
4103 /* force scroll info update */
4104 EDIT_UpdateScrollInfo(hwnd, es);
4105 EDIT_NOTIFY_PARENT(hwnd, es, EN_HSCROLL, "EN_HSCROLL");
4106 }
4107 break;
4108 case SB_ENDSCROLL:
4109 TRACE("SB_ENDSCROLL\n");
4110 break;
4111 /*
4112 * FIXME : the next two are undocumented !
4113 * Are we doing the right thing ?
4114 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4115 * although it's also a regular control message.
4116 */
4117 case EM_GETTHUMB: /* this one is used by NT notepad */
4118 case EM_GETTHUMB16:
4119 {
4120 LRESULT ret;
4121 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_HSCROLL)
4122 ret = GetScrollPos(hwnd, SB_HORZ);
4123 else
4124 {
4125 /* Assume default scroll range 0-100 */
4126 INT fw = es->format_rect.right - es->format_rect.left;
4127 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
4128 }
4129 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4130 return ret;
4131 }
4132 case EM_LINESCROLL16:
4133 TRACE("EM_LINESCROLL16\n");
4134 dx = pos;
4135 break;
4136
4137 default:
4138 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4139 action, action);
4140 return 0;
4141 }
4142 if (dx)
4143 {
4144 INT fw = es->format_rect.right - es->format_rect.left;
4145 /* check if we are going to move too far */
4146 if(es->x_offset + dx + fw > es->text_width)
4147 dx = es->text_width - fw - es->x_offset;
4148 if(dx)
4149 EDIT_EM_LineScroll_internal(hwnd, es, dx, 0);
4150 }
4151 return 0;
4152}
4153
4154
4155/*********************************************************************
4156 *
4157 * EDIT_CheckCombo
4158 *
4159 */
4160static BOOL EDIT_CheckCombo(HWND hwnd, EDITSTATE *es, UINT msg, INT key)
4161{
4162 HWND hLBox = es->hwndListBox;
4163 HWND hCombo;
4164 BOOL bDropped;
4165 int nEUI;
4166
4167 if (!hLBox)
4168 return FALSE;
4169
4170 hCombo = GetParent(hwnd);
4171 bDropped = TRUE;
4172 nEUI = 0;
4173
4174 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
4175 hwnd, (UINT16)msg, (UINT16)key);
4176
4177 if (key == VK_UP || key == VK_DOWN)
4178 {
4179 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
4180 nEUI = 1;
4181
4182 if (msg == WM_KEYDOWN || nEUI)
4183 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
4184 }
4185
4186 switch (msg)
4187 {
4188 case WM_KEYDOWN:
4189 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
4190 {
4191 /* make sure ComboLBox pops up */
4192 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
4193 key = VK_F4;
4194 nEUI = 2;
4195 }
4196
4197 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
4198 break;
4199
4200 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
4201 if (nEUI)
4202 SendMessageW(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
4203 else
4204 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
4205 break;
4206 }
4207
4208 if(nEUI == 2)
4209 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
4210
4211 return TRUE;
4212}
4213
4214
4215/*********************************************************************
4216 *
4217 * WM_KEYDOWN
4218 *
4219 * Handling of special keys that don't produce a WM_CHAR
4220 * (i.e. non-printable keys) & Backspace & Delete
4221 *
4222 */
4223static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key)
4224{
4225 BOOL shift;
4226 BOOL control;
4227
4228 if (GetKeyState(VK_MENU) & 0x8000)
4229 return 0;
4230
4231 shift = GetKeyState(VK_SHIFT) & 0x8000;
4232 control = GetKeyState(VK_CONTROL) & 0x8000;
4233
4234 switch (key) {
4235 case VK_F4:
4236 case VK_UP:
4237 if (EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key) || key == VK_F4)
4238 break;
4239
4240 /* fall through */
4241 case VK_LEFT:
4242 if ((es->style & ES_MULTILINE) && (key == VK_UP))
4243 EDIT_MoveUp_ML(hwnd, es, shift);
4244 else
4245 if (control)
4246 EDIT_MoveWordBackward(hwnd, es, shift);
4247 else
4248 EDIT_MoveBackward(hwnd, es, shift);
4249 break;
4250 case VK_DOWN:
4251 if (EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key))
4252 break;
4253 /* fall through */
4254 case VK_RIGHT:
4255 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
4256 EDIT_MoveDown_ML(hwnd, es, shift);
4257 else if (control)
4258 EDIT_MoveWordForward(hwnd, es, shift);
4259 else
4260 EDIT_MoveForward(hwnd, es, shift);
4261 break;
4262 case VK_HOME:
4263 EDIT_MoveHome(hwnd, es, shift);
4264 break;
4265 case VK_END:
4266 EDIT_MoveEnd(hwnd, es, shift);
4267 break;
4268 case VK_PRIOR:
4269 if (es->style & ES_MULTILINE)
4270 EDIT_MovePageUp_ML(hwnd, es, shift);
4271 else
4272 EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key);
4273 break;
4274 case VK_NEXT:
4275 if (es->style & ES_MULTILINE)
4276 EDIT_MovePageDown_ML(hwnd, es, shift);
4277 else
4278 EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key);
4279 break;
4280 case VK_DELETE:
4281 if (!(es->style & ES_READONLY) && !(shift && control)) {
4282 if (es->selection_start != es->selection_end) {
4283 if (shift)
4284 EDIT_WM_Cut(hwnd, es);
4285 else
4286 EDIT_WM_Clear(hwnd, es);
4287 } else {
4288 if (shift) {
4289 /* delete character left of caret */
4290 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4291 EDIT_MoveBackward(hwnd, es, TRUE);
4292 EDIT_WM_Clear(hwnd, es);
4293 } else if (control) {
4294 /* delete to end of line */
4295 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4296 EDIT_MoveEnd(hwnd, es, TRUE);
4297 EDIT_WM_Clear(hwnd, es);
4298 } else {
4299 /* delete character right of caret */
4300 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4301 EDIT_MoveForward(hwnd, es, TRUE);
4302 EDIT_WM_Clear(hwnd, es);
4303 }
4304 }
4305 }
4306 break;
4307 case VK_INSERT:
4308 if (shift) {
4309 if (!(es->style & ES_READONLY))
4310 EDIT_WM_Paste(hwnd, es);
4311 } else if (control)
4312 EDIT_WM_Copy(hwnd, es);
4313 break;
4314 case VK_RETURN:
4315 /* If the edit doesn't want the return send a message to the default object */
4316 if(!(es->style & ES_WANTRETURN))
4317 {
4318 HWND hwndParent = GetParent(hwnd);
4319 DWORD dw = SendMessageW( hwndParent, DM_GETDEFID, 0, 0 );
4320 if (HIWORD(dw) == DC_HASDEFID)
4321 {
4322 SendMessageW( hwndParent, WM_COMMAND,
4323 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
4324 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
4325 }
4326 }
4327 break;
4328 }
4329 return 0;
4330}
4331
4332
4333/*********************************************************************
4334 *
4335 * WM_KILLFOCUS
4336 *
4337 */
4338static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es)
4339{
4340 es->flags &= ~EF_FOCUSED;
4341 DestroyCaret();
4342 if(!(es->style & ES_NOHIDESEL))
4343 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
4344 EDIT_NOTIFY_PARENT(hwnd, es, EN_KILLFOCUS, "EN_KILLFOCUS");
4345 return 0;
4346}
4347
4348
4349/*********************************************************************
4350 *
4351 * WM_LBUTTONDBLCLK
4352 *
4353 * The caret position has been set on the WM_LBUTTONDOWN message
4354 *
4355 */
4356static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es)
4357{
4358 INT s;
4359 INT e = es->selection_end;
4360 INT l;
4361 INT li;
4362 INT ll;
4363
4364 if (!(es->flags & EF_FOCUSED))
4365 return 0;
4366
4367 l = EDIT_EM_LineFromChar(es, e);
4368 li = EDIT_EM_LineIndex(es, l);
4369 ll = EDIT_EM_LineLength(es, e);
4370 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
4371 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
4372 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
4373 EDIT_EM_ScrollCaret(hwnd, es);
4374 return 0;
4375}
4376
4377
4378/*********************************************************************
4379 *
4380 * WM_LBUTTONDOWN
4381 *
4382 */
4383static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
4384{
4385 INT e;
4386 BOOL after_wrap;
4387
4388 if (!(es->flags & EF_FOCUSED))
4389 return 0;
4390
4391 es->bCaptureState = TRUE;
4392 SetCapture(hwnd);
4393 EDIT_ConfinePoint(es, &x, &y);
4394 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
4395 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
4396 EDIT_EM_ScrollCaret(hwnd, es);
4397 es->region_posx = es->region_posy = 0;
4398 SetTimer(hwnd, 0, 100, NULL);
4399 return 0;
4400}
4401
4402
4403/*********************************************************************
4404 *
4405 * WM_LBUTTONUP
4406 *
4407 */
4408static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es)
4409{
4410#ifdef __WIN32OS2__
4411 if (es->bCaptureState) {
4412 KillTimer(hwndSelf, 0);
4413 if(GetCapture() == hwndSelf) ReleaseCapture();
4414 }
4415#else
4416 if (es->bCaptureState && GetCapture() == hwndSelf) {
4417 KillTimer(hwndSelf, 0);
4418 ReleaseCapture();
4419 }
4420#endif
4421 es->bCaptureState = FALSE;
4422 return 0;
4423}
4424
4425
4426/*********************************************************************
4427 *
4428 * WM_MBUTTONDOWN
4429 *
4430 */
4431static LRESULT EDIT_WM_MButtonDown(HWND hwnd)
4432{
4433 SendMessageW(hwnd,WM_PASTE,0,0);
4434 return 0;
4435}
4436
4437
4438/*********************************************************************
4439 *
4440 * WM_MOUSEMOVE
4441 *
4442 */
4443static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, INT x, INT y)
4444{
4445 INT e;
4446 BOOL after_wrap;
4447 INT prex, prey;
4448
4449 if (GetCapture() != hwnd)
4450 return 0;
4451
4452 /*
4453 * FIXME: gotta do some scrolling if outside client
4454 * area. Maybe reset the timer ?
4455 */
4456 prex = x; prey = y;
4457 EDIT_ConfinePoint(es, &x, &y);
4458 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
4459 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
4460 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
4461 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
4462 return 0;
4463}
4464
4465
4466/*********************************************************************
4467 *
4468 * WM_NCCREATE
4469 *
4470 * See also EDIT_WM_StyleChanged
4471 */
4472static LRESULT EDIT_WM_NCCreate(HWND hwnd, DWORD style, HWND hwndParent, BOOL unicode)
4473{
4474 EDITSTATE *es;
4475 UINT alloc_size;
4476
4477 TRACE("Creating %s edit control, style = %08lx\n",
4478 unicode ? "Unicode" : "ANSI", style);
4479
4480 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
4481 return FALSE;
4482 SetWindowLongA( hwnd, 0, (LONG)es );
4483
4484 /*
4485 * Note: since the EDITSTATE has not been fully initialized yet,
4486 * we can't use any API calls that may send
4487 * WM_XXX messages before WM_NCCREATE is completed.
4488 */
4489
4490 es->is_unicode = unicode;
4491 es->style = style;
4492
4493 es->bEnableState = !(style & WS_DISABLED);
4494
4495 /* Save parent, which will be notified by EN_* messages */
4496 es->hwndParent = hwndParent;
4497
4498 if (es->style & ES_COMBO)
4499 es->hwndListBox = GetDlgItem(hwndParent, ID_CB_LISTBOX);
4500
4501 /* Number overrides lowercase overrides uppercase (at least it
4502 * does in Win95). However I'll bet that ES_NUMBER would be
4503 * invalid under Win 3.1.
4504 */
4505 if (es->style & ES_NUMBER) {
4506 ; /* do not override the ES_NUMBER */
4507 } else if (es->style & ES_LOWERCASE) {
4508 es->style &= ~ES_UPPERCASE;
4509 }
4510 if (es->style & ES_MULTILINE) {
4511 es->buffer_limit = BUFLIMIT_MULTI;
4512 if (es->style & WS_VSCROLL)
4513 es->style |= ES_AUTOVSCROLL;
4514 if (es->style & WS_HSCROLL)
4515 es->style |= ES_AUTOHSCROLL;
4516 es->style &= ~ES_PASSWORD;
4517 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4518 /* Confirmed - RIGHT overrides CENTER */
4519 if (es->style & ES_RIGHT)
4520 es->style &= ~ES_CENTER;
4521 es->style &= ~WS_HSCROLL;
4522 es->style &= ~ES_AUTOHSCROLL;
4523 }
4524
4525 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4526 es->style |= ES_AUTOVSCROLL;
4527 } else {
4528 es->buffer_limit = BUFLIMIT_SINGLE;
4529 if (WIN31_LOOK == TWEAK_WineLook ||
4530 WIN95_LOOK == TWEAK_WineLook) {
4531 es->style &= ~ES_CENTER;
4532 es->style &= ~ES_RIGHT;
4533 } else {
4534 if (es->style & ES_RIGHT)
4535 es->style &= ~ES_CENTER;
4536 }
4537 es->style &= ~WS_HSCROLL;
4538 es->style &= ~WS_VSCROLL;
4539 es->style &= ~ES_AUTOVSCROLL;
4540 es->style &= ~ES_WANTRETURN;
4541 if (es->style & ES_PASSWORD)
4542 es->password_char = '*';
4543
4544 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4545 es->style |= ES_AUTOHSCROLL;
4546 }
4547
4548 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4549 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4550 return FALSE;
4551 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4552
4553 if (!(es->undo_text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (es->buffer_size + 1) * sizeof(WCHAR))))
4554 return FALSE;
4555 es->undo_buffer_size = es->buffer_size;
4556
4557 if (es->style & ES_MULTILINE)
4558 if (!(es->first_line_def = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
4559 return FALSE;
4560 es->line_count = 1;
4561
4562 /*
4563 * In Win95 look and feel, the WS_BORDER style is replaced by the
4564 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4565 * control a non client area. Not always. This coordinates in some
4566 * way with the window creation code in dialog.c When making
4567 * modifications please ensure that the code still works for edit
4568 * controls created directly with style 0x50800000, exStyle 0 (
4569 * which should have a single pixel border)
4570 */
4571 if (TWEAK_WineLook != WIN31_LOOK)
4572 {
4573 es->style &= ~WS_BORDER;
4574 }
4575 else
4576 {
4577 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
4578 SetWindowLongA( hwnd, GWL_STYLE,
4579 GetWindowLongA( hwnd, GWL_STYLE ) & ~WS_BORDER );
4580 }
4581
4582 return TRUE;
4583}
4584
4585/*********************************************************************
4586 *
4587 * WM_PAINT
4588 *
4589 */
4590static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es, WPARAM wParam)
4591{
4592 PAINTSTRUCT ps;
4593 INT i;
4594 HDC dc;
4595 HFONT old_font = 0;
4596 RECT rc;
4597 RECT rcLine;
4598 RECT rcRgn;
4599 BOOL rev = es->bEnableState &&
4600 ((es->flags & EF_FOCUSED) ||
4601 (es->style & ES_NOHIDESEL));
4602 if (!wParam)
4603 dc = BeginPaint(hwnd, &ps);
4604 else
4605 dc = (HDC) wParam;
4606 if(es->style & WS_BORDER) {
4607 GetClientRect(hwnd, &rc);
4608 if(es->style & ES_MULTILINE) {
4609 if(es->style & WS_HSCROLL) rc.bottom++;
4610 if(es->style & WS_VSCROLL) rc.right++;
4611 }
4612 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
4613 }
4614 IntersectClipRect(dc, es->format_rect.left,
4615 es->format_rect.top,
4616 es->format_rect.right,
4617 es->format_rect.bottom);
4618 if (es->style & ES_MULTILINE) {
4619 GetClientRect(hwnd, &rc);
4620 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
4621 }
4622 if (es->font)
4623 old_font = SelectObject(dc, es->font);
4624 if ( get_app_version() >= 0x40000 &&(
4625 !es->bEnableState || (es->style & ES_READONLY)))
4626 EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
4627 else
4628 EDIT_SEND_CTLCOLOR(hwnd, dc);
4629
4630 if (!es->bEnableState)
4631 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
4632 GetClipBox(dc, &rcRgn);
4633 if (es->style & ES_MULTILINE) {
4634 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4635 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
4636 EDIT_GetLineRect(hwnd, es, i, 0, -1, &rcLine);
4637 if (IntersectRect(&rc, &rcRgn, &rcLine))
4638 EDIT_PaintLine(hwnd, es, dc, i, rev);
4639 }
4640 } else {
4641 EDIT_GetLineRect(hwnd, es, 0, 0, -1, &rcLine);
4642 if (IntersectRect(&rc, &rcRgn, &rcLine))
4643 EDIT_PaintLine(hwnd, es, dc, 0, rev);
4644 }
4645 if (es->font)
4646 SelectObject(dc, old_font);
4647
4648 if (!wParam)
4649 EndPaint(hwnd, &ps);
4650}
4651
4652
4653/*********************************************************************
4654 *
4655 * WM_PASTE
4656 *
4657 */
4658static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
4659{
4660 HGLOBAL hsrc;
4661 LPWSTR src;
4662
4663 /* Protect read-only edit control from modification */
4664 if(es->style & ES_READONLY)
4665 return;
4666
4667 OpenClipboard(hwnd);
4668 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
4669 src = (LPWSTR)GlobalLock(hsrc);
4670 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src, TRUE);
4671 GlobalUnlock(hsrc);
4672 }
4673 CloseClipboard();
4674}
4675
4676
4677/*********************************************************************
4678 *
4679 * WM_SETFOCUS
4680 *
4681 */
4682static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es)
4683{
4684 es->flags |= EF_FOCUSED;
4685 CreateCaret(hwnd, 0, 2, es->line_height);
4686 EDIT_SetCaretPos(hwnd, es, es->selection_end,
4687 es->flags & EF_AFTER_WRAP);
4688 if(!(es->style & ES_NOHIDESEL))
4689 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
4690 ShowCaret(hwnd);
4691 EDIT_NOTIFY_PARENT(hwnd, es, EN_SETFOCUS, "EN_SETFOCUS");
4692}
4693
4694
4695/*********************************************************************
4696 *
4697 * WM_SETFONT
4698 *
4699 * With Win95 look the margins are set to default font value unless
4700 * the system font (font == 0) is being set, in which case they are left
4701 * unchanged.
4702 *
4703 */
4704static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
4705{
4706 TEXTMETRICW tm;
4707 HDC dc;
4708 HFONT old_font = 0;
4709 RECT r;
4710
4711 es->font = font;
4712 dc = GetDC(hwnd);
4713 if (font)
4714 old_font = SelectObject(dc, font);
4715 GetTextMetricsW(dc, &tm);
4716 es->line_height = tm.tmHeight;
4717 es->char_width = tm.tmAveCharWidth;
4718 if (font)
4719 SelectObject(dc, old_font);
4720 ReleaseDC(hwnd, dc);
4721 if (font && (TWEAK_WineLook > WIN31_LOOK))
4722 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
4723 EC_USEFONTINFO, EC_USEFONTINFO);
4724
4725 /* Force the recalculation of the format rect for each font change */
4726 GetClientRect(hwnd, &r);
4727 EDIT_SetRectNP(hwnd, es, &r);
4728
4729 if (es->style & ES_MULTILINE)
4730 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
4731 else
4732 EDIT_CalcLineWidth_SL(hwnd, es);
4733
4734 if (redraw)
4735 EDIT_UpdateText(hwnd, es, NULL, TRUE);
4736 if (es->flags & EF_FOCUSED) {
4737 DestroyCaret();
4738 CreateCaret(hwnd, 0, 2, es->line_height);
4739 EDIT_SetCaretPos(hwnd, es, es->selection_end,
4740 es->flags & EF_AFTER_WRAP);
4741 ShowCaret(hwnd);
4742 }
4743}
4744
4745
4746/*********************************************************************
4747 *
4748 * WM_SETTEXT
4749 *
4750 * NOTES
4751 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4752 * The modified flag is reset. No notifications are sent.
4753 *
4754 * For single-line controls, reception of WM_SETTEXT triggers:
4755 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4756 *
4757 */
4758static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPARAM lParam, BOOL unicode)
4759{
4760 LPWSTR text = NULL;
4761
4762 if(unicode)
4763 text = (LPWSTR)lParam;
4764 else if (lParam)
4765 {
4766 LPCSTR textA = (LPCSTR)lParam;
4767 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
4768 if((text = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
4769 MultiByteToWideChar(CP_ACP, 0, textA, -1, text, countW);
4770 }
4771
4772 EDIT_EM_SetSel(hwnd, es, 0, (UINT)-1, FALSE);
4773 if (text) {
4774 TRACE("%s\n", debugstr_w(text));
4775 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text, FALSE);
4776 if(!unicode)
4777 HeapFree(GetProcessHeap(), 0, text);
4778 } else {
4779 static const WCHAR empty_stringW[] = {0};
4780 TRACE("<NULL>\n");
4781 EDIT_EM_ReplaceSel(hwnd, es, FALSE, empty_stringW, FALSE);
4782 }
4783 es->x_offset = 0;
4784 es->flags &= ~EF_MODIFIED;
4785 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
4786 /* Send the notification after the selection start and end have been set
4787 * edit control doesn't send notification on WM_SETTEXT
4788 * if it is multiline, or it is part of combobox
4789 */
4790 if( !((es->style & ES_MULTILINE) || es->hwndListBox))
4791 {
4792 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
4793 EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
4794 }
4795 EDIT_EM_ScrollCaret(hwnd, es);
4796}
4797
4798
4799/*********************************************************************
4800 *
4801 * WM_SIZE
4802 *
4803 */
4804static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
4805{
4806 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4807 RECT rc;
4808 TRACE("width = %d, height = %d\n", width, height);
4809 SetRect(&rc, 0, 0, width, height);
4810 EDIT_SetRectNP(hwnd, es, &rc);
4811 EDIT_UpdateText(hwnd, es, NULL, TRUE);
4812 }
4813}
4814
4815
4816/*********************************************************************
4817 *
4818 * WM_STYLECHANGED
4819 *
4820 * This message is sent by SetWindowLong on having changed either the Style
4821 * or the extended style.
4822 *
4823 * We ensure that the window's version of the styles and the EDITSTATE's agree.
4824 *
4825 * See also EDIT_WM_NCCreate
4826 *
4827 * It appears that the Windows version of the edit control allows the style
4828 * (as retrieved by GetWindowLong) to be any value and maintains an internal
4829 * style variable which will generally be different. In this function we
4830 * update the internal style based on what changed in the externally visible
4831 * style.
4832 *
4833 * Much of this content as based upon the MSDN, especially:
4834 * Platform SDK Documentation -> User Interface Services ->
4835 * Windows User Interface -> Edit Controls -> Edit Control Reference ->
4836 * Edit Control Styles
4837 */
4838static LRESULT EDIT_WM_StyleChanged (HWND hwnd,
4839 EDITSTATE *es,
4840 WPARAM which,
4841 const STYLESTRUCT *style)
4842{
4843 if (GWL_STYLE == which) {
4844 DWORD style_change_mask;
4845 DWORD new_style;
4846 /* Only a subset of changes can be applied after the control
4847 * has been created.
4848 */
4849 style_change_mask = ES_UPPERCASE | ES_LOWERCASE |
4850 ES_NUMBER;
4851 if (es->style & ES_MULTILINE)
4852 style_change_mask |= ES_WANTRETURN;
4853
4854 new_style = style->styleNew & style_change_mask;
4855
4856 /* Number overrides lowercase overrides uppercase (at least it
4857 * does in Win95). However I'll bet that ES_NUMBER would be
4858 * invalid under Win 3.1.
4859 */
4860 if (new_style & ES_NUMBER) {
4861 ; /* do not override the ES_NUMBER */
4862 } else if (new_style & ES_LOWERCASE) {
4863 new_style &= ~ES_UPPERCASE;
4864 }
4865
4866 es->style = (es->style & ~style_change_mask) | new_style;
4867 } else if (GWL_EXSTYLE == which) {
4868 ; /* FIXME - what is needed here */
4869 } else {
4870 WARN ("Invalid style change %d\n",which);
4871 }
4872
4873 return 0;
4874}
4875
4876/*********************************************************************
4877 *
4878 * WM_SYSKEYDOWN
4879 *
4880 */
4881static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
4882{
4883 if ((key == VK_BACK) && (key_data & 0x2000)) {
4884 if (EDIT_EM_CanUndo(es))
4885 EDIT_EM_Undo(hwnd, es);
4886 return 0;
4887 } else if (key == VK_UP || key == VK_DOWN) {
4888 if (EDIT_CheckCombo(hwnd, es, WM_SYSKEYDOWN, key))
4889 return 0;
4890 }
4891 return DefWindowProcW(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4892}
4893
4894
4895/*********************************************************************
4896 *
4897 * WM_TIMER
4898 *
4899 */
4900static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es)
4901{
4902 if (es->region_posx < 0) {
4903 EDIT_MoveBackward(hwnd, es, TRUE);
4904 } else if (es->region_posx > 0) {
4905 EDIT_MoveForward(hwnd, es, TRUE);
4906 }
4907/*
4908 * FIXME: gotta do some vertical scrolling here, like
4909 * EDIT_EM_LineScroll(hwnd, 0, 1);
4910 */
4911}
4912
4913/*********************************************************************
4914 *
4915 * WM_VSCROLL
4916 *
4917 */
4918static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos)
4919{
4920 INT dy;
4921
4922 if (!(es->style & ES_MULTILINE))
4923 return 0;
4924
4925 if (!(es->style & ES_AUTOVSCROLL))
4926 return 0;
4927
4928 dy = 0;
4929 switch (action) {
4930 case SB_LINEUP:
4931 case SB_LINEDOWN:
4932 case SB_PAGEUP:
4933 case SB_PAGEDOWN:
4934 TRACE("action %d\n", action);
4935 EDIT_EM_Scroll(hwnd, es, action);
4936 return 0;
4937 case SB_TOP:
4938 TRACE("SB_TOP\n");
4939 dy = -es->y_offset;
4940 break;
4941 case SB_BOTTOM:
4942 TRACE("SB_BOTTOM\n");
4943 dy = es->line_count - 1 - es->y_offset;
4944 break;
4945 case SB_THUMBTRACK:
4946 TRACE("SB_THUMBTRACK %d\n", pos);
4947 es->flags |= EF_VSCROLL_TRACK;
4948 if(es->style & WS_VSCROLL)
4949 dy = pos - es->y_offset;
4950 else
4951 {
4952 /* Assume default scroll range 0-100 */
4953 INT vlc, new_y;
4954 /* Sanity check */
4955 if(pos < 0 || pos > 100) return 0;
4956 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4957 new_y = pos * (es->line_count - vlc) / 100;
4958 dy = es->line_count ? (new_y - es->y_offset) : 0;
4959 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4960 es->line_count, es->y_offset, pos, dy);
4961 }
4962 break;
4963 case SB_THUMBPOSITION:
4964 TRACE("SB_THUMBPOSITION %d\n", pos);
4965 es->flags &= ~EF_VSCROLL_TRACK;
4966 if(es->style & WS_VSCROLL)
4967 dy = pos - es->y_offset;
4968 else
4969 {
4970 /* Assume default scroll range 0-100 */
4971 INT vlc, new_y;
4972 /* Sanity check */
4973 if(pos < 0 || pos > 100) return 0;
4974 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4975 new_y = pos * (es->line_count - vlc) / 100;
4976 dy = es->line_count ? (new_y - es->y_offset) : 0;
4977 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4978 es->line_count, es->y_offset, pos, dy);
4979 }
4980 if (!dy)
4981 {
4982 /* force scroll info update */
4983 EDIT_UpdateScrollInfo(hwnd, es);
4984 EDIT_NOTIFY_PARENT(hwnd, es, EN_VSCROLL, "EN_VSCROLL");
4985 }
4986 break;
4987 case SB_ENDSCROLL:
4988 TRACE("SB_ENDSCROLL\n");
4989 break;
4990 /*
4991 * FIXME : the next two are undocumented !
4992 * Are we doing the right thing ?
4993 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4994 * although it's also a regular control message.
4995 */
4996 case EM_GETTHUMB: /* this one is used by NT notepad */
4997 case EM_GETTHUMB16:
4998 {
4999 LRESULT ret;
5000 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_VSCROLL)
5001 ret = GetScrollPos(hwnd, SB_VERT);
5002 else
5003 {
5004 /* Assume default scroll range 0-100 */
5005 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
5006 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
5007 }
5008 TRACE("EM_GETTHUMB: returning %ld\n", ret);
5009 return ret;
5010 }
5011 case EM_LINESCROLL16:
5012 TRACE("EM_LINESCROLL16 %d\n", pos);
5013 dy = pos;
5014 break;
5015
5016 default:
5017 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
5018 action, action);
5019 return 0;
5020 }
5021 if (dy)
5022 EDIT_EM_LineScroll(hwnd, es, 0, dy);
5023 return 0;
5024}
5025
5026/*********************************************************************
5027 *
5028 * EDIT_UpdateText
5029 *
5030 */
5031static void EDIT_UpdateTextRegion(HWND hwnd, EDITSTATE *es, HRGN hrgn, BOOL bErase)
5032{
5033 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
5034 InvalidateRgn(hwnd, hrgn, bErase);
5035}
5036
5037
5038/*********************************************************************
5039 *
5040 * EDIT_UpdateText
5041 *
5042 */
5043static void EDIT_UpdateText(HWND hwnd, EDITSTATE *es, LPRECT rc, BOOL bErase)
5044{
5045 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
5046 InvalidateRect(hwnd, rc, bErase);
5047}
Note: See TracBrowser for help on using the repository browser.