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

Last change on this file since 21303 was 21303, checked in by ydario, 16 years ago

User32 updates.

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