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

Last change on this file since 9524 was 9524, checked in by sandervl, 23 years ago

Merged our changes/fixes

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