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

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

KOM: Edit control: DBCS related fixes

File size: 159.7 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 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
3155 /* force scroll info update */
3156 EDIT_UpdateScrollInfo(hwnd, es);
3157 }
3158 if (dx && !(es->flags & EF_HSCROLL_TRACK))
3159 EDIT_NOTIFY_PARENT(hwnd, es, EN_HSCROLL, "EN_HSCROLL");
3160 if (dy && !(es->flags & EF_VSCROLL_TRACK))
3161 EDIT_NOTIFY_PARENT(hwnd, es, EN_VSCROLL, "EN_VSCROLL");
3162 return TRUE;
3163}
3164
3165
3166/*********************************************************************
3167 *
3168 * EM_POSFROMCHAR
3169 *
3170 */
3171static LRESULT EDIT_EM_PosFromChar(HWND hwnd, EDITSTATE *es, INT index, BOOL after_wrap)
3172{
3173 INT len = strlenW(es->text);
3174 INT l;
3175 INT li;
3176 INT x;
3177 INT y = 0;
3178 HDC dc;
3179 HFONT old_font = 0;
3180 SIZE size;
3181
3182 index = min(index, len);
3183 dc = GetDC(hwnd);
3184 if (es->font)
3185 old_font = SelectObject(dc, es->font);
3186 if (es->style & ES_MULTILINE) {
3187 l = EDIT_EM_LineFromChar(es, index);
3188 y = (l - es->y_offset) * es->line_height;
3189 li = EDIT_EM_LineIndex(es, l);
3190 if (after_wrap && (li == index) && l) {
3191 INT l2 = l - 1;
3192 LINEDEF *line_def = es->first_line_def;
3193 while (l2) {
3194 line_def = line_def->next;
3195 l2--;
3196 }
3197 if (line_def->ending == END_WRAP) {
3198 l--;
3199 y -= es->line_height;
3200 li = EDIT_EM_LineIndex(es, l);
3201 }
3202 }
3203 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
3204 es->tabs_count, es->tabs)) - es->x_offset;
3205 } else {
3206 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
3207 if (index < es->x_offset) {
3208 GetTextExtentPoint32W(dc, text + index,
3209 es->x_offset - index, &size);
3210 x = -size.cx;
3211 } else {
3212 GetTextExtentPoint32W(dc, text + es->x_offset,
3213 index - es->x_offset, &size);
3214 x = size.cx;
3215 }
3216 y = 0;
3217 if (es->style & ES_PASSWORD)
3218 HeapFree(GetProcessHeap(), 0, text);
3219 }
3220 x += es->format_rect.left;
3221 y += es->format_rect.top;
3222 if (es->font)
3223 SelectObject(dc, old_font);
3224 ReleaseDC(hwnd, dc);
3225 return MAKELONG((INT16)x, (INT16)y);
3226}
3227
3228
3229/*********************************************************************
3230 *
3231 * EM_REPLACESEL
3232 *
3233 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
3234 *
3235 */
3236static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update)
3237{
3238 UINT strl = strlenW(lpsz_replace);
3239 UINT tl = strlenW(es->text);
3240 UINT utl;
3241 UINT s;
3242 UINT e;
3243 UINT i;
3244 LPWSTR p;
3245 HRGN hrgn = 0;
3246
3247 TRACE("%s, can_undo %d, send_update %d\n",
3248 debugstr_w(lpsz_replace), can_undo, send_update);
3249
3250 s = es->selection_start;
3251 e = es->selection_end;
3252
3253 if ((s == e) && !strl)
3254 return;
3255
3256 ORDER_UINT(s, e);
3257
3258 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
3259 return;
3260
3261 if (e != s) {
3262 /* there is something to be deleted */
3263 TRACE("deleting stuff.\n");
3264 if (can_undo) {
3265 utl = strlenW(es->undo_text);
3266 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
3267 /* undo-buffer is extended to the right */
3268 EDIT_MakeUndoFit(es, utl + e - s);
3269 strncpyW(es->undo_text + utl, es->text + s, e - s + 1);
3270 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
3271 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
3272 /* undo-buffer is extended to the left */
3273 EDIT_MakeUndoFit(es, utl + e - s);
3274 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
3275 p[e - s] = p[0];
3276 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
3277 p[i] = (es->text + s)[i];
3278 es->undo_position = s;
3279 } else {
3280 /* new undo-buffer */
3281 EDIT_MakeUndoFit(es, e - s);
3282 strncpyW(es->undo_text, es->text + s, e - s + 1);
3283 es->undo_text[e - s] = 0; /* ensure 0 termination */
3284 es->undo_position = s;
3285 }
3286 /* any deletion makes the old insertion-undo invalid */
3287 es->undo_insert_count = 0;
3288 } else
3289 EDIT_EM_EmptyUndoBuffer(es);
3290
3291 /* now delete */
3292 strcpyW(es->text + s, es->text + e);
3293 }
3294 if (strl) {
3295 /* there is an insertion */
3296 if (can_undo) {
3297 if ((s == es->undo_position) ||
3298 ((es->undo_insert_count) &&
3299 (s == es->undo_position + es->undo_insert_count)))
3300 /*
3301 * insertion is new and at delete position or
3302 * an extension to either left or right
3303 */
3304 es->undo_insert_count += strl;
3305 else {
3306 /* new insertion undo */
3307 es->undo_position = s;
3308 es->undo_insert_count = strl;
3309 /* new insertion makes old delete-buffer invalid */
3310 *es->undo_text = '\0';
3311 }
3312 } else
3313 EDIT_EM_EmptyUndoBuffer(es);
3314
3315 /* now insert */
3316 tl = strlenW(es->text);
3317 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));
3318 for (p = es->text + tl ; p >= es->text + s ; p--)
3319 p[strl] = p[0];
3320 for (i = 0 , p = es->text + s ; i < strl ; i++)
3321 p[i] = lpsz_replace[i];
3322 if(es->style & ES_UPPERCASE)
3323 CharUpperBuffW(p, strl);
3324 else if(es->style & ES_LOWERCASE)
3325 CharLowerBuffW(p, strl);
3326 s += strl;
3327 }
3328 if (es->style & ES_MULTILINE)
3329 {
3330 INT s = min(es->selection_start, es->selection_end);
3331
3332 hrgn = CreateRectRgn(0, 0, 0, 0);
3333 EDIT_BuildLineDefs_ML(hwnd, es, s, s + strl,
3334 strl - abs(es->selection_end - es->selection_start), hrgn);
3335 }
3336 else
3337 EDIT_CalcLineWidth_SL(hwnd, es);
3338
3339 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
3340 es->flags |= EF_MODIFIED;
3341 if (send_update) es->flags |= EF_UPDATE;
3342 EDIT_EM_ScrollCaret(hwnd, es);
3343
3344 /* force scroll info update */
3345 EDIT_UpdateScrollInfo(hwnd, es);
3346
3347 if (hrgn)
3348 {
3349 EDIT_UpdateTextRegion(hwnd, es, hrgn, TRUE);
3350 DeleteObject(hrgn);
3351 }
3352 else
3353 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3354
3355 if(es->flags & EF_UPDATE)
3356 {
3357 es->flags &= ~EF_UPDATE;
3358 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
3359 }
3360}
3361
3362
3363/*********************************************************************
3364 *
3365 * EM_SCROLL
3366 *
3367 */
3368static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
3369{
3370 INT dy;
3371
3372 if (!(es->style & ES_MULTILINE))
3373 return (LRESULT)FALSE;
3374
3375 dy = 0;
3376
3377 switch (action) {
3378 case SB_LINEUP:
3379 if (es->y_offset)
3380 dy = -1;
3381 break;
3382 case SB_LINEDOWN:
3383 if (es->y_offset < es->line_count - 1)
3384 dy = 1;
3385 break;
3386 case SB_PAGEUP:
3387 if (es->y_offset)
3388 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
3389 break;
3390 case SB_PAGEDOWN:
3391 if (es->y_offset < es->line_count - 1)
3392 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3393 break;
3394 default:
3395 return (LRESULT)FALSE;
3396 }
3397 if (dy) {
3398 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3399 /* check if we are going to move too far */
3400 if(es->y_offset + dy > es->line_count - vlc)
3401 dy = es->line_count - vlc - es->y_offset;
3402
3403 /* Notification is done in EDIT_EM_LineScroll */
3404 if(dy)
3405 EDIT_EM_LineScroll(hwnd, es, 0, dy);
3406 }
3407 return MAKELONG((INT16)dy, (BOOL16)TRUE);
3408}
3409
3410
3411/*********************************************************************
3412 *
3413 * EM_SCROLLCARET
3414 *
3415 */
3416static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
3417{
3418 if (es->style & ES_MULTILINE) {
3419 INT l;
3420 INT li;
3421 INT vlc;
3422 INT ww;
3423 INT cw = es->char_width;
3424 INT x;
3425 INT dy = 0;
3426 INT dx = 0;
3427
3428 l = EDIT_EM_LineFromChar(es, es->selection_end);
3429 li = EDIT_EM_LineIndex(es, l);
3430 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
3431 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3432 if (l >= es->y_offset + vlc)
3433 dy = l - vlc + 1 - es->y_offset;
3434 if (l < es->y_offset)
3435 dy = l - es->y_offset;
3436 ww = es->format_rect.right - es->format_rect.left;
3437 if (x < es->format_rect.left)
3438 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
3439 if (x > es->format_rect.right)
3440 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
3441 if (dy || dx)
3442 {
3443 /* check if we are going to move too far */
3444 if(es->x_offset + dx + ww > es->text_width)
3445 dx = es->text_width - ww - es->x_offset;
3446 if(dx || dy)
3447 EDIT_EM_LineScroll_internal(hwnd, es, dx, dy);
3448 }
3449 } else {
3450 INT x;
3451 INT goal;
3452 INT format_width;
3453
3454 if (!(es->style & ES_AUTOHSCROLL))
3455 return;
3456
3457 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3458 format_width = es->format_rect.right - es->format_rect.left;
3459 if (x < es->format_rect.left) {
3460 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
3461 do {
3462 es->x_offset--;
3463 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3464 } while ((x < goal) && es->x_offset);
3465 /* FIXME: use ScrollWindow() somehow to improve performance */
3466 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3467 } else if (x > es->format_rect.right) {
3468 INT x_last;
3469 INT len = strlenW(es->text);
3470 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
3471 do {
3472 es->x_offset++;
3473 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
3474 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, len, FALSE));
3475 } while ((x > goal) && (x_last > es->format_rect.right));
3476 /* FIXME: use ScrollWindow() somehow to improve performance */
3477 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3478 }
3479 }
3480
3481 if(es->flags & EF_FOCUSED)
3482 EDIT_SetCaretPos(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
3483}
3484
3485
3486/*********************************************************************
3487 *
3488 * EM_SETHANDLE
3489 *
3490 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3491 *
3492 */
3493static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
3494{
3495 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
3496
3497 if (!(es->style & ES_MULTILINE))
3498 return;
3499
3500 if (!hloc) {
3501 WARN("called with NULL handle\n");
3502 return;
3503 }
3504
3505 EDIT_UnlockBuffer(hwnd, es, TRUE);
3506
3507#ifndef __WIN32OS2__
3508 if(es->hloc16)
3509 {
3510 LOCAL_Free(hInstance, es->hloc16);
3511 es->hloc16 = (HLOCAL16)NULL;
3512 }
3513#endif
3514 if(es->is_unicode)
3515 {
3516 if(es->hloc32A)
3517 {
3518 LocalFree(es->hloc32A);
3519 es->hloc32A = (HLOCAL)NULL;
3520 }
3521 es->hloc32W = hloc;
3522 }
3523 else
3524 {
3525 INT countW, countA;
3526 HLOCAL hloc32W_new;
3527 WCHAR *textW;
3528 CHAR *textA;
3529
3530 countA = LocalSize(hloc);
3531 textA = LocalLock(hloc);
3532 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3533 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3534 {
3535 ERR("Could not allocate new unicode buffer\n");
3536 return;
3537 }
3538 textW = LocalLock(hloc32W_new);
3539 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3540 LocalUnlock(hloc32W_new);
3541 LocalUnlock(hloc);
3542
3543 if(es->hloc32W)
3544 LocalFree(es->hloc32W);
3545
3546 es->hloc32W = hloc32W_new;
3547 es->hloc32A = hloc;
3548 }
3549
3550 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3551
3552 EDIT_LockBuffer(hwnd, es);
3553
3554 es->x_offset = es->y_offset = 0;
3555 es->selection_start = es->selection_end = 0;
3556 EDIT_EM_EmptyUndoBuffer(es);
3557 es->flags &= ~EF_MODIFIED;
3558 es->flags &= ~EF_UPDATE;
3559 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3560 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3561 EDIT_EM_ScrollCaret(hwnd, es);
3562 /* force scroll info update */
3563 EDIT_UpdateScrollInfo(hwnd, es);
3564}
3565
3566
3567#ifndef __WIN32OS2__
3568/*********************************************************************
3569 *
3570 * EM_SETHANDLE16
3571 *
3572 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
3573 *
3574 */
3575static void EDIT_EM_SetHandle16(HWND hwnd, EDITSTATE *es, HLOCAL16 hloc)
3576{
3577 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
3578 INT countW, countA;
3579 HLOCAL hloc32W_new;
3580 WCHAR *textW;
3581 CHAR *textA;
3582
3583 if (!(es->style & ES_MULTILINE))
3584 return;
3585
3586 if (!hloc) {
3587 WARN("called with NULL handle\n");
3588 return;
3589 }
3590
3591 EDIT_UnlockBuffer(hwnd, es, TRUE);
3592
3593 if(es->hloc32A)
3594 {
3595 LocalFree(es->hloc32A);
3596 es->hloc32A = (HLOCAL)NULL;
3597 }
3598
3599 countA = LOCAL_Size(hInstance, hloc);
3600 textA = LOCAL_Lock(hInstance, hloc);
3601 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
3602 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
3603 {
3604 ERR("Could not allocate new unicode buffer\n");
3605 return;
3606 }
3607 textW = LocalLock(hloc32W_new);
3608 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
3609 LocalUnlock(hloc32W_new);
3610 LOCAL_Unlock(hInstance, hloc);
3611
3612 if(es->hloc32W)
3613 LocalFree(es->hloc32W);
3614
3615 es->hloc32W = hloc32W_new;
3616 es->hloc16 = hloc;
3617
3618 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
3619
3620 EDIT_LockBuffer(hwnd, es);
3621
3622 es->x_offset = es->y_offset = 0;
3623 es->selection_start = es->selection_end = 0;
3624 EDIT_EM_EmptyUndoBuffer(es);
3625 es->flags &= ~EF_MODIFIED;
3626 es->flags &= ~EF_UPDATE;
3627 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3628 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3629 EDIT_EM_ScrollCaret(hwnd, es);
3630 /* force scroll info update */
3631 EDIT_UpdateScrollInfo(hwnd, es);
3632}
3633#endif
3634
3635/*********************************************************************
3636 *
3637 * EM_SETLIMITTEXT
3638 *
3639 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
3640 * However, the windows version is not complied to yet in all of edit.c
3641 *
3642 */
3643static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit)
3644{
3645 if (es->style & ES_MULTILINE) {
3646 if (limit)
3647 es->buffer_limit = min(limit, BUFLIMIT_MULTI);
3648 else
3649 es->buffer_limit = BUFLIMIT_MULTI;
3650 } else {
3651 if (limit)
3652 es->buffer_limit = min(limit, BUFLIMIT_SINGLE);
3653 else
3654 es->buffer_limit = BUFLIMIT_SINGLE;
3655 }
3656}
3657
3658
3659/*********************************************************************
3660 *
3661 * EM_SETMARGINS
3662 *
3663 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
3664 * action wParam despite what the docs say. EC_USEFONTINFO means one third
3665 * of the char's width, according to the new docs.
3666 *
3667 */
3668static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
3669 INT left, INT right)
3670{
3671 if (action & EC_LEFTMARGIN) {
3672 if (left != EC_USEFONTINFO)
3673 es->left_margin = left;
3674 else
3675 es->left_margin = es->char_width / 3;
3676 }
3677
3678 if (action & EC_RIGHTMARGIN) {
3679 if (right != EC_USEFONTINFO)
3680 es->right_margin = right;
3681 else
3682 es->right_margin = es->char_width / 3;
3683 }
3684 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3685}
3686
3687
3688/*********************************************************************
3689 *
3690 * EM_SETPASSWORDCHAR
3691 *
3692 */
3693static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, WCHAR c)
3694{
3695 LONG style;
3696
3697 if (es->style & ES_MULTILINE)
3698 return;
3699
3700 if (es->password_char == c)
3701 return;
3702
3703 style = GetWindowLongA( hwnd, GWL_STYLE );
3704 es->password_char = c;
3705 if (c) {
3706 SetWindowLongA( hwnd, GWL_STYLE, style | ES_PASSWORD );
3707 es->style |= ES_PASSWORD;
3708 } else {
3709 SetWindowLongA( hwnd, GWL_STYLE, style & ~ES_PASSWORD );
3710 es->style &= ~ES_PASSWORD;
3711 }
3712 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3713}
3714
3715
3716/*********************************************************************
3717 *
3718 * EDIT_EM_SetSel
3719 *
3720 * note: unlike the specs say: the order of start and end
3721 * _is_ preserved in Windows. (i.e. start can be > end)
3722 * In other words: this handler is OK
3723 *
3724 */
3725static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
3726{
3727 UINT old_start = es->selection_start;
3728 UINT old_end = es->selection_end;
3729 UINT len = strlenW(es->text);
3730
3731 if (start == (UINT)-1) {
3732 start = es->selection_end;
3733 end = es->selection_end;
3734 } else {
3735 start = min(start, len);
3736 end = min(end, len);
3737 }
3738 es->selection_start = start;
3739 es->selection_end = end;
3740 if (after_wrap)
3741 es->flags |= EF_AFTER_WRAP;
3742 else
3743 es->flags &= ~EF_AFTER_WRAP;
3744/* This is a little bit more efficient than before, not sure if it can be improved. FIXME? */
3745 ORDER_UINT(start, end);
3746 ORDER_UINT(end, old_end);
3747 ORDER_UINT(start, old_start);
3748 ORDER_UINT(old_start, old_end);
3749 if (end != old_start)
3750 {
3751/*
3752 * One can also do
3753 * ORDER_UINT32(end, old_start);
3754 * EDIT_InvalidateText(hwnd, es, start, end);
3755 * EDIT_InvalidateText(hwnd, es, old_start, old_end);
3756 * in place of the following if statement.
3757 */
3758 if (old_start > end )
3759 {
3760 EDIT_InvalidateText(hwnd, es, start, end);
3761 EDIT_InvalidateText(hwnd, es, old_start, old_end);
3762 }
3763 else
3764 {
3765 EDIT_InvalidateText(hwnd, es, start, old_start);
3766 EDIT_InvalidateText(hwnd, es, end, old_end);
3767 }
3768 }
3769 else EDIT_InvalidateText(hwnd, es, start, old_end);
3770}
3771
3772
3773/*********************************************************************
3774 *
3775 * EM_SETTABSTOPS
3776 *
3777 */
3778static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs)
3779{
3780 if (!(es->style & ES_MULTILINE))
3781 return FALSE;
3782 if (es->tabs)
3783 HeapFree(GetProcessHeap(), 0, es->tabs);
3784 es->tabs_count = count;
3785 if (!count)
3786 es->tabs = NULL;
3787 else {
3788 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3789 memcpy(es->tabs, tabs, count * sizeof(INT));
3790 }
3791 return TRUE;
3792}
3793
3794
3795/*********************************************************************
3796 *
3797 * EM_SETTABSTOPS16
3798 *
3799 */
3800static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs)
3801{
3802 if (!(es->style & ES_MULTILINE))
3803 return FALSE;
3804 if (es->tabs)
3805 HeapFree(GetProcessHeap(), 0, es->tabs);
3806 es->tabs_count = count;
3807 if (!count)
3808 es->tabs = NULL;
3809 else {
3810 INT i;
3811 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3812 for (i = 0 ; i < count ; i++)
3813 es->tabs[i] = *tabs++;
3814 }
3815 return TRUE;
3816}
3817
3818
3819/*********************************************************************
3820 *
3821 * EM_SETWORDBREAKPROC
3822 *
3823 */
3824static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, LPARAM lParam)
3825{
3826 if (es->word_break_proc == (void *)lParam)
3827 return;
3828
3829 es->word_break_proc = (void *)lParam;
3830 es->word_break_proc16 = NULL;
3831
3832 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3833 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3834 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3835 }
3836}
3837
3838
3839#ifndef __WIN32OS2__
3840/*********************************************************************
3841 *
3842 * EM_SETWORDBREAKPROC16
3843 *
3844 */
3845static void EDIT_EM_SetWordBreakProc16(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
3846{
3847 if (es->word_break_proc16 == wbp)
3848 return;
3849
3850 es->word_break_proc = NULL;
3851 es->word_break_proc16 = wbp;
3852 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3853 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
3854 EDIT_UpdateText(hwnd, es, NULL, TRUE);
3855 }
3856}
3857#endif
3858
3859/*********************************************************************
3860 *
3861 * EM_UNDO / WM_UNDO
3862 *
3863 */
3864static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
3865{
3866 INT ulength;
3867 LPWSTR utext;
3868
3869 /* Protect read-only edit control from modification */
3870 if(es->style & ES_READONLY)
3871 return FALSE;
3872
3873 ulength = strlenW(es->undo_text);
3874 utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3875
3876 strcpyW(utext, es->undo_text);
3877
3878 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3879 es->undo_insert_count, debugstr_w(utext));
3880
3881 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3882 EDIT_EM_EmptyUndoBuffer(es);
3883 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext, FALSE);
3884 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3885 /* send the notification after the selection start and end are set */
3886 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
3887 EDIT_EM_ScrollCaret(hwnd, es);
3888 HeapFree(GetProcessHeap(), 0, utext);
3889
3890 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3891 es->undo_insert_count, debugstr_w(es->undo_text));
3892 return TRUE;
3893}
3894
3895
3896/*********************************************************************
3897 *
3898 * WM_CHAR
3899 *
3900 */
3901static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, WCHAR c)
3902{
3903 BOOL control;
3904
3905 /* Protect read-only edit control from modification */
3906 if(es->style & ES_READONLY)
3907 return;
3908
3909 control = GetKeyState(VK_CONTROL) & 0x8000;
3910
3911 switch (c) {
3912 case '\r':
3913 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
3914 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3915 break;
3916 case '\n':
3917 if (es->style & ES_MULTILINE) {
3918 if (es->style & ES_READONLY) {
3919 EDIT_MoveHome(hwnd, es, FALSE);
3920 EDIT_MoveDown_ML(hwnd, es, FALSE);
3921 } else {
3922 static const WCHAR cr_lfW[] = {'\r','\n',0};
3923 EDIT_EM_ReplaceSel(hwnd, es, TRUE, cr_lfW, TRUE);
3924 }
3925 }
3926 break;
3927 case '\t':
3928 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3929 {
3930 static const WCHAR tabW[] = {'\t',0};
3931 EDIT_EM_ReplaceSel(hwnd, es, TRUE, tabW, TRUE);
3932 }
3933 break;
3934 case VK_BACK:
3935 if (!(es->style & ES_READONLY) && !control) {
3936 if (es->selection_start != es->selection_end)
3937 EDIT_WM_Clear(hwnd, es);
3938 else {
3939 /* delete character left of caret */
3940 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
3941 EDIT_MoveBackward(hwnd, es, TRUE);
3942 EDIT_WM_Clear(hwnd, es);
3943 }
3944 }
3945 break;
3946 case 0x03: /* ^C */
3947 SendMessageW(hwnd, WM_COPY, 0, 0);
3948 break;
3949 case 0x16: /* ^V */
3950 SendMessageW(hwnd, WM_PASTE, 0, 0);
3951 break;
3952 case 0x18: /* ^X */
3953 SendMessageW(hwnd, WM_CUT, 0, 0);
3954 break;
3955
3956 default:
3957 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3958 WCHAR str[2];
3959 str[0] = c;
3960 str[1] = '\0';
3961 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str, TRUE);
3962 }
3963 break;
3964 }
3965}
3966
3967
3968/*********************************************************************
3969 *
3970 * WM_COMMAND
3971 *
3972 */
3973static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
3974{
3975 if (code || control)
3976 return;
3977
3978 switch (id) {
3979 case EM_UNDO:
3980 EDIT_EM_Undo(hwnd, es);
3981 break;
3982 case WM_CUT:
3983 EDIT_WM_Cut(hwnd, es);
3984 break;
3985 case WM_COPY:
3986 EDIT_WM_Copy(hwnd, es);
3987 break;
3988 case WM_PASTE:
3989 EDIT_WM_Paste(hwnd, es);
3990 break;
3991 case WM_CLEAR:
3992 EDIT_WM_Clear(hwnd, es);
3993 break;
3994 case EM_SETSEL:
3995 EDIT_EM_SetSel(hwnd, es, 0, (UINT)-1, FALSE);
3996 EDIT_EM_ScrollCaret(hwnd, es);
3997 break;
3998 default:
3999 ERR("unknown menu item, please report\n");
4000 break;
4001 }
4002}
4003
4004
4005/*********************************************************************
4006 *
4007 * WM_CONTEXTMENU
4008 *
4009 * Note: the resource files resource/sysres_??.rc cannot define a
4010 * single popup menu. Hence we use a (dummy) menubar
4011 * containing the single popup menu as its first item.
4012 *
4013 * FIXME: the message identifiers have been chosen arbitrarily,
4014 * hence we use MF_BYPOSITION.
4015 * We might as well use the "real" values (anybody knows ?)
4016 * The menu definition is in resources/sysres_??.rc.
4017 * Once these are OK, we better use MF_BYCOMMAND here
4018 * (as we do in EDIT_WM_Command()).
4019 *
4020 */
4021static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, INT x, INT y)
4022{
4023 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
4024 HMENU popup = GetSubMenu(menu, 0);
4025 UINT start = es->selection_start;
4026 UINT end = es->selection_end;
4027
4028 ORDER_UINT(start, end);
4029
4030 /* undo */
4031 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
4032 /* cut */
4033 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
4034 /* copy */
4035 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
4036 /* paste */
4037 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
4038 /* delete */
4039 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
4040 /* select all */
4041 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != strlenW(es->text)) ? MF_ENABLED : MF_GRAYED));
4042
4043 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
4044 DestroyMenu(menu);
4045}
4046
4047
4048/*********************************************************************
4049 *
4050 * WM_COPY
4051 *
4052 */
4053static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
4054{
4055 INT s = es->selection_start;
4056 INT e = es->selection_end;
4057 HGLOBAL hdst;
4058 LPWSTR dst;
4059
4060 if (e == s)
4061 return;
4062 ORDER_INT(s, e);
4063 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (DWORD)(e - s + 1) * sizeof(WCHAR));
4064 dst = GlobalLock(hdst);
4065 strncpyW(dst, es->text + s, e - s);
4066 dst[e - s ] = 0; /* ensure 0 termination */
4067 TRACE("%s\n", debugstr_w(dst));
4068
4069 GlobalUnlock(hdst);
4070 OpenClipboard(hwnd);
4071 EmptyClipboard();
4072 SetClipboardData(CF_UNICODETEXT, hdst);
4073 CloseClipboard();
4074}
4075
4076
4077/*********************************************************************
4078 *
4079 * WM_CREATE
4080 *
4081 */
4082static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCWSTR name)
4083{
4084 TRACE("%s\n", debugstr_w(name));
4085 /*
4086 * To initialize some final structure members, we call some helper
4087 * functions. However, since the EDITSTATE is not consistent (i.e.
4088 * not fully initialized), we should be very careful which
4089 * functions can be called, and in what order.
4090 */
4091 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
4092 EDIT_EM_EmptyUndoBuffer(es);
4093
4094 if (name && *name) {
4095 EDIT_EM_ReplaceSel(hwnd, es, FALSE, name, FALSE);
4096 /* if we insert text to the editline, the text scrolls out
4097 * of the window, as the caret is placed after the insert
4098 * pos normally; thus we reset es->selection... to 0 and
4099 * update caret
4100 */
4101 es->selection_start = es->selection_end = 0;
4102 /* send the notification after the selection start and end are set */
4103 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
4104 EDIT_EM_ScrollCaret(hwnd, es);
4105 }
4106 /* force scroll info update */
4107 EDIT_UpdateScrollInfo(hwnd, es);
4108 return 0;
4109}
4110
4111
4112/*********************************************************************
4113 *
4114 * WM_DESTROY
4115 *
4116 */
4117static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
4118{
4119 HINSTANCE hInstance = GetWindowLongA( hwnd, GWL_HINSTANCE );
4120 LINEDEF *pc, *pp;
4121
4122 if (es->hloc32W) {
4123 while (LocalUnlock(es->hloc32W)) ;
4124 LocalFree(es->hloc32W);
4125 }
4126 if (es->hloc32A) {
4127 while (LocalUnlock(es->hloc32A)) ;
4128 LocalFree(es->hloc32A);
4129 }
4130#ifndef __WIN32OS2__
4131 if (es->hloc16) {
4132 while (LOCAL_Unlock(hInstance, es->hloc16)) ;
4133 LOCAL_Free(hInstance, es->hloc16);
4134 }
4135#endif
4136 pc = es->first_line_def;
4137 while (pc)
4138 {
4139 pp = pc->next;
4140 HeapFree(GetProcessHeap(), 0, pc);
4141 pc = pp;
4142 }
4143
4144 SetWindowLongA( hwnd, 0, 0 );
4145 HeapFree(GetProcessHeap(), 0, es);
4146}
4147
4148
4149/*********************************************************************
4150 *
4151 * WM_ERASEBKGND
4152 *
4153 */
4154static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
4155{
4156 HBRUSH brush;
4157 RECT rc;
4158
4159 if ( get_app_version() >= 0x40000 &&(
4160 !es->bEnableState || (es->style & ES_READONLY)))
4161 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
4162 else
4163 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
4164
4165 if (!brush)
4166 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
4167
4168 GetClientRect(hwnd, &rc);
4169 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
4170 GetClipBox(dc, &rc);
4171 /*
4172 * FIXME: specs say that we should UnrealizeObject() the brush,
4173 * but the specs of UnrealizeObject() say that we shouldn't
4174 * unrealize a stock object. The default brush that
4175 * DefWndProc() returns is ... a stock object.
4176 */
4177 FillRect(dc, &rc, brush);
4178 return -1;
4179}
4180
4181
4182/*********************************************************************
4183 *
4184 * WM_GETTEXT
4185 *
4186 */
4187static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPARAM lParam, BOOL unicode)
4188{
4189 if(!count) return 0;
4190
4191 if(unicode)
4192 {
4193 LPWSTR textW = (LPWSTR)lParam;
4194 strncpyW(textW, es->text, count);
4195 textW[count - 1] = 0; /* ensure 0 termination */
4196 return strlenW(textW);
4197 }
4198 else
4199 {
4200 LPSTR textA = (LPSTR)lParam;
4201 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL);
4202#ifdef __WIN32OS2__
4203 lstrtrunc( textA, count );
4204#else
4205 textA[count - 1] = 0; /* ensure 0 termination */
4206#endif
4207 return strlen(textA);
4208 }
4209}
4210
4211/*********************************************************************
4212 *
4213 * WM_HSCROLL
4214 *
4215 */
4216static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos)
4217{
4218 INT dx;
4219 INT fw;
4220
4221 if (!(es->style & ES_MULTILINE))
4222 return 0;
4223
4224 if (!(es->style & ES_AUTOHSCROLL))
4225 return 0;
4226
4227 dx = 0;
4228 fw = es->format_rect.right - es->format_rect.left;
4229 switch (action) {
4230 case SB_LINELEFT:
4231 TRACE("SB_LINELEFT\n");
4232 if (es->x_offset)
4233 dx = -es->char_width;
4234 break;
4235 case SB_LINERIGHT:
4236 TRACE("SB_LINERIGHT\n");
4237 if (es->x_offset < es->text_width)
4238 dx = es->char_width;
4239 break;
4240 case SB_PAGELEFT:
4241 TRACE("SB_PAGELEFT\n");
4242 if (es->x_offset)
4243 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4244 break;
4245 case SB_PAGERIGHT:
4246 TRACE("SB_PAGERIGHT\n");
4247 if (es->x_offset < es->text_width)
4248 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4249 break;
4250 case SB_LEFT:
4251 TRACE("SB_LEFT\n");
4252 if (es->x_offset)
4253 dx = -es->x_offset;
4254 break;
4255 case SB_RIGHT:
4256 TRACE("SB_RIGHT\n");
4257 if (es->x_offset < es->text_width)
4258 dx = es->text_width - es->x_offset;
4259 break;
4260 case SB_THUMBTRACK:
4261 TRACE("SB_THUMBTRACK %d\n", pos);
4262 es->flags |= EF_HSCROLL_TRACK;
4263 if(es->style & WS_HSCROLL)
4264 dx = pos - es->x_offset;
4265 else
4266 {
4267 INT fw, new_x;
4268 /* Sanity check */
4269 if(pos < 0 || pos > 100) return 0;
4270 /* Assume default scroll range 0-100 */
4271 fw = es->format_rect.right - es->format_rect.left;
4272 new_x = pos * (es->text_width - fw) / 100;
4273 dx = es->text_width ? (new_x - es->x_offset) : 0;
4274 }
4275 break;
4276 case SB_THUMBPOSITION:
4277 TRACE("SB_THUMBPOSITION %d\n", pos);
4278 es->flags &= ~EF_HSCROLL_TRACK;
4279 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_HSCROLL)
4280 dx = pos - es->x_offset;
4281 else
4282 {
4283 INT fw, new_x;
4284 /* Sanity check */
4285 if(pos < 0 || pos > 100) return 0;
4286 /* Assume default scroll range 0-100 */
4287 fw = es->format_rect.right - es->format_rect.left;
4288 new_x = pos * (es->text_width - fw) / 100;
4289 dx = es->text_width ? (new_x - es->x_offset) : 0;
4290 }
4291 if (!dx) {
4292 /* force scroll info update */
4293 EDIT_UpdateScrollInfo(hwnd, es);
4294 EDIT_NOTIFY_PARENT(hwnd, es, EN_HSCROLL, "EN_HSCROLL");
4295 }
4296 break;
4297 case SB_ENDSCROLL:
4298 TRACE("SB_ENDSCROLL\n");
4299 break;
4300 /*
4301 * FIXME : the next two are undocumented !
4302 * Are we doing the right thing ?
4303 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4304 * although it's also a regular control message.
4305 */
4306 case EM_GETTHUMB: /* this one is used by NT notepad */
4307 case EM_GETTHUMB16:
4308 {
4309 LRESULT ret;
4310 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_HSCROLL)
4311 ret = GetScrollPos(hwnd, SB_HORZ);
4312 else
4313 {
4314 /* Assume default scroll range 0-100 */
4315 INT fw = es->format_rect.right - es->format_rect.left;
4316 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
4317 }
4318 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4319 return ret;
4320 }
4321 case EM_LINESCROLL16:
4322 TRACE("EM_LINESCROLL16\n");
4323 dx = pos;
4324 break;
4325
4326 default:
4327 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4328 action, action);
4329 return 0;
4330 }
4331 if (dx)
4332 {
4333 INT fw = es->format_rect.right - es->format_rect.left;
4334 /* check if we are going to move too far */
4335 if(es->x_offset + dx + fw > es->text_width)
4336 dx = es->text_width - fw - es->x_offset;
4337 if(dx)
4338 EDIT_EM_LineScroll_internal(hwnd, es, dx, 0);
4339 }
4340 return 0;
4341}
4342
4343
4344/*********************************************************************
4345 *
4346 * EDIT_CheckCombo
4347 *
4348 */
4349static BOOL EDIT_CheckCombo(HWND hwnd, EDITSTATE *es, UINT msg, INT key)
4350{
4351 HWND hLBox = es->hwndListBox;
4352 HWND hCombo;
4353 BOOL bDropped;
4354 int nEUI;
4355
4356 if (!hLBox)
4357 return FALSE;
4358
4359 hCombo = GetParent(hwnd);
4360 bDropped = TRUE;
4361 nEUI = 0;
4362
4363 TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
4364 hwnd, (UINT16)msg, (UINT16)key);
4365
4366 if (key == VK_UP || key == VK_DOWN)
4367 {
4368 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
4369 nEUI = 1;
4370
4371 if (msg == WM_KEYDOWN || nEUI)
4372 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
4373 }
4374
4375 switch (msg)
4376 {
4377 case WM_KEYDOWN:
4378 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
4379 {
4380 /* make sure ComboLBox pops up */
4381 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
4382 key = VK_F4;
4383 nEUI = 2;
4384 }
4385
4386 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
4387 break;
4388
4389 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
4390 if (nEUI)
4391 SendMessageW(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
4392 else
4393 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
4394 break;
4395 }
4396
4397 if(nEUI == 2)
4398 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
4399
4400 return TRUE;
4401}
4402
4403
4404/*********************************************************************
4405 *
4406 * WM_KEYDOWN
4407 *
4408 * Handling of special keys that don't produce a WM_CHAR
4409 * (i.e. non-printable keys) & Backspace & Delete
4410 *
4411 */
4412static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key)
4413{
4414 BOOL shift;
4415 BOOL control;
4416
4417 if (GetKeyState(VK_MENU) & 0x8000)
4418 return 0;
4419
4420 shift = GetKeyState(VK_SHIFT) & 0x8000;
4421 control = GetKeyState(VK_CONTROL) & 0x8000;
4422
4423 switch (key) {
4424 case VK_F4:
4425 case VK_UP:
4426 if (EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key) || key == VK_F4)
4427 break;
4428
4429 /* fall through */
4430 case VK_LEFT:
4431 if ((es->style & ES_MULTILINE) && (key == VK_UP))
4432 EDIT_MoveUp_ML(hwnd, es, shift);
4433 else
4434 if (control)
4435 EDIT_MoveWordBackward(hwnd, es, shift);
4436 else
4437 EDIT_MoveBackward(hwnd, es, shift);
4438 break;
4439 case VK_DOWN:
4440 if (EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key))
4441 break;
4442 /* fall through */
4443 case VK_RIGHT:
4444 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
4445 EDIT_MoveDown_ML(hwnd, es, shift);
4446 else if (control)
4447 EDIT_MoveWordForward(hwnd, es, shift);
4448 else
4449 EDIT_MoveForward(hwnd, es, shift);
4450 break;
4451 case VK_HOME:
4452 EDIT_MoveHome(hwnd, es, shift);
4453 break;
4454 case VK_END:
4455 EDIT_MoveEnd(hwnd, es, shift);
4456 break;
4457 case VK_PRIOR:
4458 if (es->style & ES_MULTILINE)
4459 EDIT_MovePageUp_ML(hwnd, es, shift);
4460 else
4461 EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key);
4462 break;
4463 case VK_NEXT:
4464 if (es->style & ES_MULTILINE)
4465 EDIT_MovePageDown_ML(hwnd, es, shift);
4466 else
4467 EDIT_CheckCombo(hwnd, es, WM_KEYDOWN, key);
4468 break;
4469 case VK_DELETE:
4470 if (!(es->style & ES_READONLY) && !(shift && control)) {
4471 if (es->selection_start != es->selection_end) {
4472 if (shift)
4473 EDIT_WM_Cut(hwnd, es);
4474 else
4475 EDIT_WM_Clear(hwnd, es);
4476 } else {
4477 if (shift) {
4478 /* delete character left of caret */
4479 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4480 EDIT_MoveBackward(hwnd, es, TRUE);
4481 EDIT_WM_Clear(hwnd, es);
4482 } else if (control) {
4483 /* delete to end of line */
4484 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4485 EDIT_MoveEnd(hwnd, es, TRUE);
4486 EDIT_WM_Clear(hwnd, es);
4487 } else {
4488 /* delete character right of caret */
4489 EDIT_EM_SetSel(hwnd, es, (UINT)-1, 0, FALSE);
4490 EDIT_MoveForward(hwnd, es, TRUE);
4491 EDIT_WM_Clear(hwnd, es);
4492 }
4493 }
4494 }
4495 break;
4496 case VK_INSERT:
4497 if (shift) {
4498 if (!(es->style & ES_READONLY))
4499 EDIT_WM_Paste(hwnd, es);
4500 } else if (control)
4501 EDIT_WM_Copy(hwnd, es);
4502 break;
4503 case VK_RETURN:
4504 /* If the edit doesn't want the return send a message to the default object */
4505 if(!(es->style & ES_WANTRETURN))
4506 {
4507 HWND hwndParent = GetParent(hwnd);
4508 DWORD dw = SendMessageW( hwndParent, DM_GETDEFID, 0, 0 );
4509 if (HIWORD(dw) == DC_HASDEFID)
4510 {
4511 SendMessageW( hwndParent, WM_COMMAND,
4512 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
4513 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
4514 }
4515 }
4516 break;
4517 }
4518 return 0;
4519}
4520
4521
4522/*********************************************************************
4523 *
4524 * WM_KILLFOCUS
4525 *
4526 */
4527static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es)
4528{
4529 es->flags &= ~EF_FOCUSED;
4530 DestroyCaret();
4531 if(!(es->style & ES_NOHIDESEL))
4532 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
4533 EDIT_NOTIFY_PARENT(hwnd, es, EN_KILLFOCUS, "EN_KILLFOCUS");
4534 return 0;
4535}
4536
4537
4538/*********************************************************************
4539 *
4540 * WM_LBUTTONDBLCLK
4541 *
4542 * The caret position has been set on the WM_LBUTTONDOWN message
4543 *
4544 */
4545static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es)
4546{
4547 INT s;
4548 INT e = es->selection_end;
4549 INT l;
4550 INT li;
4551 INT ll;
4552
4553 if (!(es->flags & EF_FOCUSED))
4554 return 0;
4555
4556 l = EDIT_EM_LineFromChar(es, e);
4557 li = EDIT_EM_LineIndex(es, l);
4558 ll = EDIT_EM_LineLength(es, e);
4559 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
4560 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
4561 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
4562 EDIT_EM_ScrollCaret(hwnd, es);
4563 return 0;
4564}
4565
4566
4567/*********************************************************************
4568 *
4569 * WM_LBUTTONDOWN
4570 *
4571 */
4572static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
4573{
4574 INT e;
4575 BOOL after_wrap;
4576
4577 if (!(es->flags & EF_FOCUSED))
4578 return 0;
4579
4580 es->bCaptureState = TRUE;
4581 SetCapture(hwnd);
4582 EDIT_ConfinePoint(es, &x, &y);
4583 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
4584 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
4585 EDIT_EM_ScrollCaret(hwnd, es);
4586 es->region_posx = es->region_posy = 0;
4587 SetTimer(hwnd, 0, 100, NULL);
4588 return 0;
4589}
4590
4591
4592/*********************************************************************
4593 *
4594 * WM_LBUTTONUP
4595 *
4596 */
4597static LRESULT EDIT_WM_LButtonUp(HWND hwndSelf, EDITSTATE *es)
4598{
4599#ifdef __WIN32OS2__
4600 if (es->bCaptureState) {
4601 KillTimer(hwndSelf, 0);
4602 if(GetCapture() == hwndSelf) ReleaseCapture();
4603 }
4604#else
4605 if (es->bCaptureState && GetCapture() == hwndSelf) {
4606 KillTimer(hwndSelf, 0);
4607 ReleaseCapture();
4608 }
4609#endif
4610 es->bCaptureState = FALSE;
4611 return 0;
4612}
4613
4614
4615/*********************************************************************
4616 *
4617 * WM_MBUTTONDOWN
4618 *
4619 */
4620static LRESULT EDIT_WM_MButtonDown(HWND hwnd)
4621{
4622 SendMessageW(hwnd,WM_PASTE,0,0);
4623 return 0;
4624}
4625
4626
4627/*********************************************************************
4628 *
4629 * WM_MOUSEMOVE
4630 *
4631 */
4632static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, INT x, INT y)
4633{
4634 INT e;
4635 BOOL after_wrap;
4636 INT prex, prey;
4637
4638 if (GetCapture() != hwnd)
4639 return 0;
4640
4641 /*
4642 * FIXME: gotta do some scrolling if outside client
4643 * area. Maybe reset the timer ?
4644 */
4645 prex = x; prey = y;
4646 EDIT_ConfinePoint(es, &x, &y);
4647 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
4648 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
4649 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
4650 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
4651 return 0;
4652}
4653
4654
4655/*********************************************************************
4656 *
4657 * WM_NCCREATE
4658 *
4659 * See also EDIT_WM_StyleChanged
4660 */
4661static LRESULT EDIT_WM_NCCreate(HWND hwnd, DWORD style, HWND hwndParent, BOOL unicode)
4662{
4663 EDITSTATE *es;
4664 UINT alloc_size;
4665
4666 TRACE("Creating %s edit control, style = %08lx\n",
4667 unicode ? "Unicode" : "ANSI", style);
4668
4669 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
4670 return FALSE;
4671 SetWindowLongA( hwnd, 0, (LONG)es );
4672
4673 /*
4674 * Note: since the EDITSTATE has not been fully initialized yet,
4675 * we can't use any API calls that may send
4676 * WM_XXX messages before WM_NCCREATE is completed.
4677 */
4678
4679 es->is_unicode = unicode;
4680 es->style = style;
4681
4682 es->bEnableState = !(style & WS_DISABLED);
4683
4684 /* Save parent, which will be notified by EN_* messages */
4685 es->hwndParent = hwndParent;
4686
4687 if (es->style & ES_COMBO)
4688 es->hwndListBox = GetDlgItem(hwndParent, ID_CB_LISTBOX);
4689
4690 /* Number overrides lowercase overrides uppercase (at least it
4691 * does in Win95). However I'll bet that ES_NUMBER would be
4692 * invalid under Win 3.1.
4693 */
4694 if (es->style & ES_NUMBER) {
4695 ; /* do not override the ES_NUMBER */
4696 } else if (es->style & ES_LOWERCASE) {
4697 es->style &= ~ES_UPPERCASE;
4698 }
4699 if (es->style & ES_MULTILINE) {
4700 es->buffer_limit = BUFLIMIT_MULTI;
4701 if (es->style & WS_VSCROLL)
4702 es->style |= ES_AUTOVSCROLL;
4703 if (es->style & WS_HSCROLL)
4704 es->style |= ES_AUTOHSCROLL;
4705 es->style &= ~ES_PASSWORD;
4706 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4707 /* Confirmed - RIGHT overrides CENTER */
4708 if (es->style & ES_RIGHT)
4709 es->style &= ~ES_CENTER;
4710 es->style &= ~WS_HSCROLL;
4711 es->style &= ~ES_AUTOHSCROLL;
4712 }
4713
4714 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
4715 es->style |= ES_AUTOVSCROLL;
4716 } else {
4717 es->buffer_limit = BUFLIMIT_SINGLE;
4718 if (WIN31_LOOK == TWEAK_WineLook ||
4719 WIN95_LOOK == TWEAK_WineLook) {
4720 es->style &= ~ES_CENTER;
4721 es->style &= ~ES_RIGHT;
4722 } else {
4723 if (es->style & ES_RIGHT)
4724 es->style &= ~ES_CENTER;
4725 }
4726 es->style &= ~WS_HSCROLL;
4727 es->style &= ~WS_VSCROLL;
4728 es->style &= ~ES_AUTOVSCROLL;
4729 es->style &= ~ES_WANTRETURN;
4730 if (es->style & ES_PASSWORD)
4731 es->password_char = '*';
4732
4733 /* FIXME: for now, all single line controls are AUTOHSCROLL */
4734 es->style |= ES_AUTOHSCROLL;
4735 }
4736
4737 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4738 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4739 return FALSE;
4740 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4741
4742 if (!(es->undo_text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (es->buffer_size + 1) * sizeof(WCHAR))))
4743 return FALSE;
4744 es->undo_buffer_size = es->buffer_size;
4745
4746 if (es->style & ES_MULTILINE)
4747 if (!(es->first_line_def = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
4748 return FALSE;
4749 es->line_count = 1;
4750
4751 /*
4752 * In Win95 look and feel, the WS_BORDER style is replaced by the
4753 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4754 * control a non client area. Not always. This coordinates in some
4755 * way with the window creation code in dialog.c When making
4756 * modifications please ensure that the code still works for edit
4757 * controls created directly with style 0x50800000, exStyle 0 (
4758 * which should have a single pixel border)
4759 */
4760 if (TWEAK_WineLook != WIN31_LOOK)
4761 {
4762 es->style &= ~WS_BORDER;
4763 }
4764 else
4765 {
4766 if ((es->style & WS_BORDER) && !(es->style & WS_DLGFRAME))
4767 SetWindowLongA( hwnd, GWL_STYLE,
4768 GetWindowLongA( hwnd, GWL_STYLE ) & ~WS_BORDER );
4769 }
4770
4771 return TRUE;
4772}
4773
4774/*********************************************************************
4775 *
4776 * WM_PAINT
4777 *
4778 */
4779static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es, WPARAM wParam)
4780{
4781 PAINTSTRUCT ps;
4782 INT i;
4783 HDC dc;
4784 HFONT old_font = 0;
4785 RECT rc;
4786 RECT rcLine;
4787 RECT rcRgn;
4788 BOOL rev = es->bEnableState &&
4789 ((es->flags & EF_FOCUSED) ||
4790 (es->style & ES_NOHIDESEL));
4791 if (!wParam)
4792 dc = BeginPaint(hwnd, &ps);
4793 else
4794 dc = (HDC) wParam;
4795 if(es->style & WS_BORDER) {
4796 GetClientRect(hwnd, &rc);
4797 if(es->style & ES_MULTILINE) {
4798 if(es->style & WS_HSCROLL) rc.bottom++;
4799 if(es->style & WS_VSCROLL) rc.right++;
4800 }
4801 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
4802 }
4803 IntersectClipRect(dc, es->format_rect.left,
4804 es->format_rect.top,
4805 es->format_rect.right,
4806 es->format_rect.bottom);
4807 if (es->style & ES_MULTILINE) {
4808 GetClientRect(hwnd, &rc);
4809 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
4810 }
4811 if (es->font)
4812 old_font = SelectObject(dc, es->font);
4813 if ( get_app_version() >= 0x40000 &&(
4814 !es->bEnableState || (es->style & ES_READONLY)))
4815 EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
4816 else
4817 EDIT_SEND_CTLCOLOR(hwnd, dc);
4818
4819 if (!es->bEnableState)
4820 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
4821 GetClipBox(dc, &rcRgn);
4822 if (es->style & ES_MULTILINE) {
4823 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
4824 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
4825 EDIT_GetLineRect(hwnd, es, i, 0, -1, &rcLine);
4826 if (IntersectRect(&rc, &rcRgn, &rcLine))
4827 EDIT_PaintLine(hwnd, es, dc, i, rev);
4828 }
4829 } else {
4830 EDIT_GetLineRect(hwnd, es, 0, 0, -1, &rcLine);
4831 if (IntersectRect(&rc, &rcRgn, &rcLine))
4832 EDIT_PaintLine(hwnd, es, dc, 0, rev);
4833 }
4834 if (es->font)
4835 SelectObject(dc, old_font);
4836
4837 if (!wParam)
4838 EndPaint(hwnd, &ps);
4839}
4840
4841
4842/*********************************************************************
4843 *
4844 * WM_PASTE
4845 *
4846 */
4847static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
4848{
4849 HGLOBAL hsrc;
4850 LPWSTR src;
4851
4852 /* Protect read-only edit control from modification */
4853 if(es->style & ES_READONLY)
4854 return;
4855
4856 OpenClipboard(hwnd);
4857 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
4858 src = (LPWSTR)GlobalLock(hsrc);
4859 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src, TRUE);
4860 GlobalUnlock(hsrc);
4861 }
4862 CloseClipboard();
4863}
4864
4865
4866/*********************************************************************
4867 *
4868 * WM_SETFOCUS
4869 *
4870 */
4871static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es)
4872{
4873 es->flags |= EF_FOCUSED;
4874 CreateCaret(hwnd, 0, 2, es->line_height);
4875 EDIT_SetCaretPos(hwnd, es, es->selection_end,
4876 es->flags & EF_AFTER_WRAP);
4877 if(!(es->style & ES_NOHIDESEL))
4878 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
4879 ShowCaret(hwnd);
4880 EDIT_NOTIFY_PARENT(hwnd, es, EN_SETFOCUS, "EN_SETFOCUS");
4881}
4882
4883
4884/*********************************************************************
4885 *
4886 * WM_SETFONT
4887 *
4888 * With Win95 look the margins are set to default font value unless
4889 * the system font (font == 0) is being set, in which case they are left
4890 * unchanged.
4891 *
4892 */
4893static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
4894{
4895 TEXTMETRICW tm;
4896 HDC dc;
4897 HFONT old_font = 0;
4898 RECT r;
4899
4900 es->font = font;
4901 dc = GetDC(hwnd);
4902 if (font)
4903 old_font = SelectObject(dc, font);
4904 GetTextMetricsW(dc, &tm);
4905 es->line_height = tm.tmHeight;
4906 es->char_width = tm.tmAveCharWidth;
4907 if (font)
4908 SelectObject(dc, old_font);
4909 ReleaseDC(hwnd, dc);
4910 if (font && (TWEAK_WineLook > WIN31_LOOK))
4911 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
4912 EC_USEFONTINFO, EC_USEFONTINFO);
4913
4914 /* Force the recalculation of the format rect for each font change */
4915 GetClientRect(hwnd, &r);
4916 EDIT_SetRectNP(hwnd, es, &r);
4917
4918 if (es->style & ES_MULTILINE)
4919 EDIT_BuildLineDefs_ML(hwnd, es, 0, strlenW(es->text), 0, (HRGN)0);
4920 else
4921 EDIT_CalcLineWidth_SL(hwnd, es);
4922
4923 if (redraw)
4924 EDIT_UpdateText(hwnd, es, NULL, TRUE);
4925 if (es->flags & EF_FOCUSED) {
4926 DestroyCaret();
4927 CreateCaret(hwnd, 0, 2, es->line_height);
4928 EDIT_SetCaretPos(hwnd, es, es->selection_end,
4929 es->flags & EF_AFTER_WRAP);
4930 ShowCaret(hwnd);
4931 }
4932}
4933
4934
4935/*********************************************************************
4936 *
4937 * WM_SETTEXT
4938 *
4939 * NOTES
4940 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4941 * The modified flag is reset. No notifications are sent.
4942 *
4943 * For single-line controls, reception of WM_SETTEXT triggers:
4944 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4945 *
4946 */
4947static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPARAM lParam, BOOL unicode)
4948{
4949 LPWSTR text = NULL;
4950
4951 if(unicode)
4952 text = (LPWSTR)lParam;
4953 else if (lParam)
4954 {
4955 LPCSTR textA = (LPCSTR)lParam;
4956 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
4957 if((text = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
4958 MultiByteToWideChar(CP_ACP, 0, textA, -1, text, countW);
4959 }
4960
4961 EDIT_EM_SetSel(hwnd, es, 0, (UINT)-1, FALSE);
4962 if (text) {
4963 TRACE("%s\n", debugstr_w(text));
4964 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text, FALSE);
4965 if(!unicode)
4966 HeapFree(GetProcessHeap(), 0, text);
4967 } else {
4968 static const WCHAR empty_stringW[] = {0};
4969 TRACE("<NULL>\n");
4970 EDIT_EM_ReplaceSel(hwnd, es, FALSE, empty_stringW, FALSE);
4971 }
4972 es->x_offset = 0;
4973 es->flags &= ~EF_MODIFIED;
4974 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
4975 /* Send the notification after the selection start and end have been set
4976 * edit control doesn't send notification on WM_SETTEXT
4977 * if it is multiline, or it is part of combobox
4978 */
4979 if( !((es->style & ES_MULTILINE) || es->hwndListBox))
4980 {
4981 EDIT_NOTIFY_PARENT(hwnd, es, EN_CHANGE, "EN_CHANGE");
4982 EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
4983 }
4984 EDIT_EM_ScrollCaret(hwnd, es);
4985}
4986
4987
4988/*********************************************************************
4989 *
4990 * WM_SIZE
4991 *
4992 */
4993static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
4994{
4995 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4996 RECT rc;
4997 TRACE("width = %d, height = %d\n", width, height);
4998 SetRect(&rc, 0, 0, width, height);
4999 EDIT_SetRectNP(hwnd, es, &rc);
5000 EDIT_UpdateText(hwnd, es, NULL, TRUE);
5001 }
5002}
5003
5004
5005/*********************************************************************
5006 *
5007 * WM_STYLECHANGED
5008 *
5009 * This message is sent by SetWindowLong on having changed either the Style
5010 * or the extended style.
5011 *
5012 * We ensure that the window's version of the styles and the EDITSTATE's agree.
5013 *
5014 * See also EDIT_WM_NCCreate
5015 *
5016 * It appears that the Windows version of the edit control allows the style
5017 * (as retrieved by GetWindowLong) to be any value and maintains an internal
5018 * style variable which will generally be different. In this function we
5019 * update the internal style based on what changed in the externally visible
5020 * style.
5021 *
5022 * Much of this content as based upon the MSDN, especially:
5023 * Platform SDK Documentation -> User Interface Services ->
5024 * Windows User Interface -> Edit Controls -> Edit Control Reference ->
5025 * Edit Control Styles
5026 */
5027static LRESULT EDIT_WM_StyleChanged (HWND hwnd,
5028 EDITSTATE *es,
5029 WPARAM which,
5030 const STYLESTRUCT *style)
5031{
5032 if (GWL_STYLE == which) {
5033 DWORD style_change_mask;
5034 DWORD new_style;
5035 /* Only a subset of changes can be applied after the control
5036 * has been created.
5037 */
5038 style_change_mask = ES_UPPERCASE | ES_LOWERCASE |
5039 ES_NUMBER;
5040 if (es->style & ES_MULTILINE)
5041 style_change_mask |= ES_WANTRETURN;
5042
5043 new_style = style->styleNew & style_change_mask;
5044
5045 /* Number overrides lowercase overrides uppercase (at least it
5046 * does in Win95). However I'll bet that ES_NUMBER would be
5047 * invalid under Win 3.1.
5048 */
5049 if (new_style & ES_NUMBER) {
5050 ; /* do not override the ES_NUMBER */
5051 } else if (new_style & ES_LOWERCASE) {
5052 new_style &= ~ES_UPPERCASE;
5053 }
5054
5055 es->style = (es->style & ~style_change_mask) | new_style;
5056 } else if (GWL_EXSTYLE == which) {
5057 ; /* FIXME - what is needed here */
5058 } else {
5059 WARN ("Invalid style change %d\n",which);
5060 }
5061
5062 return 0;
5063}
5064
5065/*********************************************************************
5066 *
5067 * WM_SYSKEYDOWN
5068 *
5069 */
5070static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
5071{
5072 if ((key == VK_BACK) && (key_data & 0x2000)) {
5073 if (EDIT_EM_CanUndo(es))
5074 EDIT_EM_Undo(hwnd, es);
5075 return 0;
5076 } else if (key == VK_UP || key == VK_DOWN) {
5077 if (EDIT_CheckCombo(hwnd, es, WM_SYSKEYDOWN, key))
5078 return 0;
5079 }
5080 return DefWindowProcW(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
5081}
5082
5083
5084/*********************************************************************
5085 *
5086 * WM_TIMER
5087 *
5088 */
5089static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es)
5090{
5091 if (es->region_posx < 0) {
5092 EDIT_MoveBackward(hwnd, es, TRUE);
5093 } else if (es->region_posx > 0) {
5094 EDIT_MoveForward(hwnd, es, TRUE);
5095 }
5096/*
5097 * FIXME: gotta do some vertical scrolling here, like
5098 * EDIT_EM_LineScroll(hwnd, 0, 1);
5099 */
5100}
5101
5102/*********************************************************************
5103 *
5104 * WM_VSCROLL
5105 *
5106 */
5107static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos)
5108{
5109 INT dy;
5110
5111 if (!(es->style & ES_MULTILINE))
5112 return 0;
5113
5114 if (!(es->style & ES_AUTOVSCROLL))
5115 return 0;
5116
5117 dy = 0;
5118 switch (action) {
5119 case SB_LINEUP:
5120 case SB_LINEDOWN:
5121 case SB_PAGEUP:
5122 case SB_PAGEDOWN:
5123 TRACE("action %d\n", action);
5124 EDIT_EM_Scroll(hwnd, es, action);
5125 return 0;
5126 case SB_TOP:
5127 TRACE("SB_TOP\n");
5128 dy = -es->y_offset;
5129 break;
5130 case SB_BOTTOM:
5131 TRACE("SB_BOTTOM\n");
5132 dy = es->line_count - 1 - es->y_offset;
5133 break;
5134 case SB_THUMBTRACK:
5135 TRACE("SB_THUMBTRACK %d\n", pos);
5136 es->flags |= EF_VSCROLL_TRACK;
5137 if(es->style & WS_VSCROLL)
5138 dy = pos - es->y_offset;
5139 else
5140 {
5141 /* Assume default scroll range 0-100 */
5142 INT vlc, new_y;
5143 /* Sanity check */
5144 if(pos < 0 || pos > 100) return 0;
5145 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
5146 new_y = pos * (es->line_count - vlc) / 100;
5147 dy = es->line_count ? (new_y - es->y_offset) : 0;
5148 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
5149 es->line_count, es->y_offset, pos, dy);
5150 }
5151 break;
5152 case SB_THUMBPOSITION:
5153 TRACE("SB_THUMBPOSITION %d\n", pos);
5154 es->flags &= ~EF_VSCROLL_TRACK;
5155 if(es->style & WS_VSCROLL)
5156 dy = pos - es->y_offset;
5157 else
5158 {
5159 /* Assume default scroll range 0-100 */
5160 INT vlc, new_y;
5161 /* Sanity check */
5162 if(pos < 0 || pos > 100) return 0;
5163 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
5164 new_y = pos * (es->line_count - vlc) / 100;
5165 dy = es->line_count ? (new_y - es->y_offset) : 0;
5166 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
5167 es->line_count, es->y_offset, pos, dy);
5168 }
5169 if (!dy)
5170 {
5171 /* force scroll info update */
5172 EDIT_UpdateScrollInfo(hwnd, es);
5173 EDIT_NOTIFY_PARENT(hwnd, es, EN_VSCROLL, "EN_VSCROLL");
5174 }
5175 break;
5176 case SB_ENDSCROLL:
5177 TRACE("SB_ENDSCROLL\n");
5178 break;
5179 /*
5180 * FIXME : the next two are undocumented !
5181 * Are we doing the right thing ?
5182 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
5183 * although it's also a regular control message.
5184 */
5185 case EM_GETTHUMB: /* this one is used by NT notepad */
5186 case EM_GETTHUMB16:
5187 {
5188 LRESULT ret;
5189 if(GetWindowLongA( hwnd, GWL_STYLE ) & WS_VSCROLL)
5190 ret = GetScrollPos(hwnd, SB_VERT);
5191 else
5192 {
5193 /* Assume default scroll range 0-100 */
5194 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
5195 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
5196 }
5197 TRACE("EM_GETTHUMB: returning %ld\n", ret);
5198 return ret;
5199 }
5200 case EM_LINESCROLL16:
5201 TRACE("EM_LINESCROLL16 %d\n", pos);
5202 dy = pos;
5203 break;
5204
5205 default:
5206 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
5207 action, action);
5208 return 0;
5209 }
5210 if (dy)
5211 EDIT_EM_LineScroll(hwnd, es, 0, dy);
5212 return 0;
5213}
5214
5215/*********************************************************************
5216 *
5217 * EDIT_UpdateText
5218 *
5219 */
5220static void EDIT_UpdateTextRegion(HWND hwnd, EDITSTATE *es, HRGN hrgn, BOOL bErase)
5221{
5222 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
5223 InvalidateRgn(hwnd, hrgn, bErase);
5224}
5225
5226
5227/*********************************************************************
5228 *
5229 * EDIT_UpdateText
5230 *
5231 */
5232static void EDIT_UpdateText(HWND hwnd, EDITSTATE *es, LPRECT rc, BOOL bErase)
5233{
5234 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd, es, EN_UPDATE, "EN_UPDATE");
5235 InvalidateRect(hwnd, rc, bErase);
5236}
Note: See TracBrowser for help on using the repository browser.