source: trunk/src/user32/edit.cpp@ 2666

Last change on this file since 2666 was 2666, checked in by cbratschi, 26 years ago

several fixes -> changelog

File size: 138.2 KB
Line 
1/* $Id: edit.cpp,v 1.32 2000-02-05 19:45:15 cbratschi Exp $ */
2/*
3 * Edit control
4 *
5 * Copyright David W. Metcalfe, 1994
6 * Copyright William Magro, 1995, 1996
7 * Copyright Frans van Dorsselaer, 1996, 1997
8 *
9 * Copyright 1999 Christoph Bratschi
10 *
11 * WINE version: 991212
12 *
13 * Status: complete
14 * Version: 5.00
15 */
16
17/* CB: todo
18 - EN_ALIGN_LTR_EC, EN_ALIGN_RTL_EC -> undocumented notifications
19 - text alignment for single and multi line (ES_LEFT, ES_RIGHT, ES_CENTER)
20 new in Win98, Win2k: for single line too
21 - WinNT/Win2k: higher size limits (single: 0x7FFFFFFE, multi: none)
22 - too many redraws and recalculations!
23*/
24
25#include <os2win.h>
26#include <string.h>
27#include "controls.h"
28#include "combo.h"
29
30#ifdef DEBUG
31char *GetMsgText(int Msg);
32#endif
33
34#define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
35 FIXME: BTW, Win95 specs say 65535 (do you dare ???) */
36#define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
37
38//#define BUFLIMIT_MULTI 0xFFFFFFFE
39//#define BUFLIMIT_SINGLE 0x7FFFFFFF
40
41#define BUFSTART_MULTI 1024 /* starting size */
42#define BUFSTART_SINGLE 256 /* starting size */
43#define GROWLENGTH 64 /* buffers grow by this much */
44#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
45
46/*
47 * extra flags for EDITSTATE.flags field
48 */
49#define EF_MODIFIED 0x0001 /* text has been modified */
50#define EF_FOCUSED 0x0002 /* we have input focus */
51#define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
52#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
53#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
54#define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
55#define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
56#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
57 wrapped line, instead of in front of the next character */
58#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
59
60typedef enum
61{
62 END_0 = 0, /* line ends with terminating '\0' character */
63 END_WRAP, /* line is wrapped */
64 END_HARD, /* line ends with a hard return '\r\n' */
65 END_SOFT /* line ends with a soft return '\r\r\n' */
66} LINE_END;
67
68typedef struct tagLINEDEF {
69 INT length; /* bruto length of a line in bytes */
70 INT net_length; /* netto length of a line in visible characters */
71 LINE_END ending;
72 INT width; /* width of the line in pixels */
73 struct tagLINEDEF *next;
74} LINEDEF;
75
76typedef struct
77{
78 HANDLE heap; /* our own heap */
79 LPSTR text; /* the actual contents of the control */
80 INT buffer_size; /* the size of the buffer */
81 INT buffer_limit; /* the maximum size to which the buffer may grow */
82 HFONT font; /* NULL means standard system font */
83 INT x_offset; /* scroll offset for multi lines this is in pixels
84 for single lines it's in characters */
85 INT line_height; /* height of a screen line in pixels */
86 INT char_width; /* average character width in pixels */
87 DWORD style; /* sane version of wnd->dwStyle */
88 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
89 INT undo_insert_count; /* number of characters inserted in sequence */
90 INT undo_position; /* character index of the insertion and deletion */
91 LPSTR undo_text; /* deleted text */
92 INT undo_buffer_size; /* size of the deleted text buffer */
93 INT selection_start; /* == selection_end if no selection */
94 INT selection_end; /* == current caret position */
95 CHAR password_char; /* == 0 if no password char, and for multi line controls */
96 INT left_margin; /* in pixels */
97 INT right_margin; /* in pixels */
98 RECT format_rect;
99 INT region_posx; /* Position of cursor relative to region: */
100 INT region_posy; /* -1: to left, 0: within, 1: to right */
101 EDITWORDBREAKPROCA word_break_procA;
102 INT line_count; /* number of lines */
103 INT y_offset; /* scroll offset in number of lines */
104 BOOL bCaptureState; /* flag indicating whether mouse was captured */
105 BOOL bEnableState; /* flag keeping the enable state */
106 /*
107 * only for multi line controls
108 */
109 INT lock_count; /* amount of re-entries in the EditWndProc */
110 INT tabs_count;
111 LPINT tabs;
112 INT text_width; /* width of the widest line in pixels */
113 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
114 HLOCAL hloc; /* for controls receiving EM_GETHANDLE */
115} EDITSTATE;
116
117
118#define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
119#define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
120
121#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
122#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
123
124/* used for disabled or read-only edit control */
125#define EDIT_SEND_CTLCOLORSTATIC(hwnd,hdc) \
126 (SendMessageA(GetParent(hwnd), WM_CTLCOLORSTATIC, \
127 (WPARAM)(hdc), (LPARAM)hwnd))
128#define EDIT_SEND_CTLCOLOR(hwnd,hdc) \
129 (SendMessageA(GetParent(hwnd), WM_CTLCOLOREDIT, \
130 (WPARAM)(hdc), (LPARAM)hwnd))
131#define EDIT_NOTIFY_PARENT(hwnd, wNotifyCode) \
132 (SendMessageA(GetParent(hwnd), WM_COMMAND, \
133 MAKEWPARAM(GetWindowLongA(hwnd,GWL_ID), wNotifyCode), (LPARAM)hwnd))
134
135/*********************************************************************
136 *
137 * Declarations
138 *
139 */
140
141/*
142 * These functions have trivial implementations
143 * We still like to call them internally
144 * "static inline" makes them more like macro's
145 */
146static inline BOOL EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es);
147static inline void EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es);
148static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es);
149static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es);
150
151/*
152 * Helper functions only valid for one type of control
153 */
154static void EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es);
155static LPSTR EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es);
156static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
157static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
158static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
159static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
160static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert);
161/*
162 * Helper functions valid for both single line _and_ multi line controls
163 */
164static INT EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
165static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
166static void EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y);
167static void EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
168static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end);
169static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es);
170static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size);
171static BOOL EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size);
172static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
173static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend);
174static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend);
175static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend);
176static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
177static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend);
178static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
179static VOID EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
180static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos, BOOL after_wrap);
181static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT lprc);
182static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force);
183static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
184static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc);
185static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache);
186
187/*
188 * EM_XXX message handlers
189 */
190static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y);
191static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol);
192static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es);
193static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es);
194static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es);
195static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch);
196static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es);
197static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es);
198static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es);
199static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es);
200static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
201static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end);
202static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es);
203static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es);
204static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index);
205static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line);
206static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index);
207static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy);
208static LRESULT EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap);
209static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace);
210static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action);
211static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es);
212static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc);
213static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit);
214static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action, INT left, INT right);
215static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified);
216static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c);
217static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly);
218static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
219static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc);
220static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
221static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs);
222static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
223static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es);
224static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
225static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
226/*
227 * WM_XXX message handlers
228 */
229static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data);
230static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
231static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y);
232static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es);
233static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs);
234static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es);
235static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc);
236static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text);
237static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
238static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
239static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus);
240static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
241static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
242static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
243static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es);
244static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
245static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs);
246static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam);
247static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es);
248static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus);
249static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw);
250static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text);
251static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height);
252static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
253static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
254static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
255
256
257/*********************************************************************
258 *
259 * EM_CANUNDO
260 *
261 */
262static inline BOOL EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es)
263{
264 return (es->undo_insert_count || lstrlenA(es->undo_text));
265}
266
267
268/*********************************************************************
269 *
270 * EM_EMPTYUNDOBUFFER
271 *
272 */
273static inline void EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es)
274{
275 es->undo_insert_count = 0;
276 *es->undo_text = '\0';
277}
278
279
280/*********************************************************************
281 *
282 * WM_CLEAR
283 *
284 */
285static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es)
286{
287 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "");
288}
289
290
291/*********************************************************************
292 *
293 * WM_CUT
294 *
295 */
296static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es)
297{
298 EDIT_WM_Copy(hwnd, es);
299 EDIT_WM_Clear(hwnd, es);
300}
301
302
303/*********************************************************************
304 *
305 * EditWndProc()
306 *
307 * The messages are in the order of the actual integer values
308 * (which can be found in include/windows.h)
309 * Whereever possible the 16 bit versions are converted to
310 * the 32 bit ones, so that we can 'fall through' to the
311 * helper functions. These are mostly 32 bit (with a few
312 * exceptions, clearly indicated by a '16' extension to their
313 * names).
314 *
315 */
316LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
317 WPARAM wParam, LPARAM lParam )
318{
319 EDITSTATE *es = (EDITSTATE*)GetInfoPtr(hwnd);
320 LRESULT result = 0;
321
322// dprintf(("EditWndProc hwnd: %04x, msg %s, wp %08x lp %08lx\n",
323// hwnd, GetMsgText(msg), wParam, lParam));
324
325 switch (msg) {
326 case WM_DESTROY:
327 //DPRINTF_EDIT_MSG32("WM_DESTROY");
328 EDIT_WM_Destroy(hwnd, es);
329 result = 0;
330 goto END;
331
332 case WM_NCCREATE:
333 //DPRINTF_EDIT_MSG32("WM_NCCREATE");
334 result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTA)lParam);
335 goto END;
336 }
337
338 if (!es)
339 {
340 result = DefWindowProcA(hwnd, msg, wParam, lParam);
341 goto END;
342 }
343
344
345 EDIT_LockBuffer(hwnd, es);
346 switch (msg) {
347 case EM_GETSEL:
348 //DPRINTF_EDIT_MSG32("EM_GETSEL");
349 result = EDIT_EM_GetSel(hwnd, es, (LPUINT)wParam, (LPUINT)lParam);
350 break;
351
352 case EM_SETSEL:
353 //DPRINTF_EDIT_MSG32("EM_SETSEL");
354 EDIT_EM_SetSel(hwnd, es, wParam, lParam, FALSE);
355 EDIT_EM_ScrollCaret(hwnd,es);
356 result = 1;
357 break;
358
359 case EM_GETRECT:
360 //DPRINTF_EDIT_MSG32("EM_GETRECT");
361 EDIT_EM_GetRect(hwnd,es,(LPRECT)lParam);
362 break;
363
364 case EM_SETRECT:
365 //DPRINTF_EDIT_MSG32("EM_SETRECT");
366 EDIT_EM_SetRect(hwnd,es,(LPRECT)lParam);
367 break;
368
369 case EM_SETRECTNP:
370 //DPRINTF_EDIT_MSG32("EM_SETRECTNP");
371 EDIT_EM_SetRectNP(hwnd,es,(LPRECT)lParam);
372 break;
373
374 case EM_SCROLL:
375 //DPRINTF_EDIT_MSG32("EM_SCROLL");
376 result = EDIT_EM_Scroll(hwnd, es, (INT)wParam);
377 break;
378
379 case EM_LINESCROLL:
380 //DPRINTF_EDIT_MSG32("EM_LINESCROLL");
381 result = (LRESULT)EDIT_EM_LineScroll(hwnd, es, (INT)wParam, (INT)lParam);
382 break;
383
384 case EM_SCROLLCARET:
385 //DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
386 EDIT_EM_ScrollCaret(hwnd, es);
387 result = 1;
388 break;
389
390 case EM_GETMODIFY:
391 //DPRINTF_EDIT_MSG32("EM_GETMODIFY");
392 result = (LRESULT)EDIT_EM_GetModify(hwnd,es);
393 break;
394
395 case EM_SETMODIFY:
396 //DPRINTF_EDIT_MSG32("EM_SETMODIFY");
397 EDIT_EM_SetModify(hwnd,es,(BOOL)wParam);
398 break;
399
400 case EM_GETLINECOUNT:
401 //DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
402 result = (LRESULT)EDIT_EM_GetLineCount(hwnd,es);
403 break;
404
405 case EM_LINEINDEX:
406 //DPRINTF_EDIT_MSG32("EM_LINEINDEX");
407 result = (LRESULT)EDIT_EM_LineIndex(hwnd, es, (INT)wParam);
408 break;
409
410 case EM_SETHANDLE:
411 //DPRINTF_EDIT_MSG32("EM_SETHANDLE");
412 EDIT_EM_SetHandle(hwnd, es, (HLOCAL)wParam);
413 break;
414
415 case EM_GETHANDLE:
416 //DPRINTF_EDIT_MSG32("EM_GETHANDLE");
417 result = (LRESULT)EDIT_EM_GetHandle(hwnd, es);
418 break;
419
420 case EM_GETTHUMB:
421 //DPRINTF_EDIT_MSG32("EM_GETTHUMB");
422 result = EDIT_EM_GetThumb(hwnd, es);
423 break;
424
425 /* messages 0x00bf and 0x00c0 missing from specs */
426
427 case WM_USER+15:
428 //DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
429 /* fall through */
430 case 0x00bf:
431 //DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
432 result = DefWindowProcA(hwnd, msg, wParam, lParam);
433 break;
434
435 case WM_USER+16:
436 //DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
437 /* fall through */
438 case 0x00c0:
439 //DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
440 result = DefWindowProcA(hwnd, msg, wParam, lParam);
441 break;
442
443 case EM_LINELENGTH:
444 //DPRINTF_EDIT_MSG32("EM_LINELENGTH");
445 result = (LRESULT)EDIT_EM_LineLength(hwnd, es, (INT)wParam);
446 break;
447
448 case EM_REPLACESEL:
449 //DPRINTF_EDIT_MSG32("EM_REPLACESEL");
450 EDIT_EM_ReplaceSel(hwnd, es, (BOOL)wParam, (LPCSTR)lParam);
451 result = 1;
452 break;
453
454 /* message 0x00c3 missing from specs */
455
456 case WM_USER+19:
457 //DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
458 /* fall through */
459 case 0x00c3:
460 //DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
461 result = DefWindowProcA(hwnd, msg, wParam, lParam);
462 break;
463
464 case EM_GETLINE:
465 //DPRINTF_EDIT_MSG32("EM_GETLINE");
466 result = (LRESULT)EDIT_EM_GetLine(hwnd, es, (INT)wParam, (LPSTR)lParam);
467 break;
468
469 case EM_SETLIMITTEXT:
470 //DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
471 EDIT_EM_SetLimitText(hwnd, es, (INT)wParam);
472 break;
473
474 case EM_CANUNDO:
475 //DPRINTF_EDIT_MSG32("EM_CANUNDO");
476 result = (LRESULT)EDIT_EM_CanUndo(hwnd, es);
477 break;
478
479 case EM_UNDO:
480 /* fall through */
481 case WM_UNDO:
482 //DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
483 result = (LRESULT)EDIT_EM_Undo(hwnd, es);
484 break;
485
486 case EM_FMTLINES:
487 //DPRINTF_EDIT_MSG32("EM_FMTLINES");
488 result = (LRESULT)EDIT_EM_FmtLines(hwnd, es, (BOOL)wParam);
489 break;
490
491 case EM_LINEFROMCHAR:
492 //DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
493 result = (LRESULT)EDIT_EM_LineFromChar(hwnd, es, (INT)wParam);
494 break;
495
496 /* message 0x00ca missing from specs */
497
498 case WM_USER+26:
499 //DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
500 /* fall through */
501 case 0x00ca:
502 //DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
503 result = DefWindowProcA(hwnd, msg, wParam, lParam);
504 break;
505
506 case EM_SETTABSTOPS:
507 //DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
508 result = (LRESULT)EDIT_EM_SetTabStops(hwnd, es, (INT)wParam, (LPINT)lParam);
509 break;
510
511 case EM_SETPASSWORDCHAR:
512 //DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
513 EDIT_EM_SetPasswordChar(hwnd, es, (CHAR)wParam);
514 break;
515
516 case EM_EMPTYUNDOBUFFER:
517 //DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
518 EDIT_EM_EmptyUndoBuffer(hwnd, es);
519 break;
520
521 case EM_GETFIRSTVISIBLELINE:
522 //DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
523 result = (LRESULT)EDIT_EM_GetFirstVisibleLine(hwnd,es);
524 break;
525
526 case EM_SETREADONLY:
527 //DPRINTF_EDIT_MSG32("EM_SETREADONLY");
528 result = (LRESULT)EDIT_EM_SetReadOnly(hwnd,es,(BOOL)wParam);
529 break;
530
531 case EM_SETWORDBREAKPROC:
532 //DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
533 EDIT_EM_SetWordBreakProc(hwnd, es, (EDITWORDBREAKPROCA)lParam);
534 break;
535
536 case EM_GETWORDBREAKPROC:
537 //DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
538 result = (LRESULT)EDIT_EM_GetWordbreakProc(hwnd,es);
539 break;
540
541 case EM_GETPASSWORDCHAR:
542 //DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
543 result = (LRESULT)EDIT_EM_GetPasswordChar(hwnd,es);
544 break;
545
546 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
547
548 case EM_SETMARGINS:
549 //DPRINTF_EDIT_MSG32("EM_SETMARGINS");
550 EDIT_EM_SetMargins(hwnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
551 break;
552
553 case EM_GETMARGINS:
554 //DPRINTF_EDIT_MSG32("EM_GETMARGINS");
555 result = EDIT_EM_GetMargins(hwnd,es);
556 break;
557
558 case EM_GETLIMITTEXT:
559 //DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
560 result = (LRESULT)EDIT_EM_GetLimitText(hwnd,es);
561 break;
562
563 case EM_POSFROMCHAR:
564 //DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
565 result = EDIT_EM_PosFromChar(hwnd,0, es, (INT)wParam, FALSE);
566 break;
567
568 case EM_CHARFROMPOS:
569 //DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
570 result = EDIT_EM_CharFromPos(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
571 break;
572
573 case EM_SETIMESTATUS:
574 result = EDIT_EM_SetIMEStatus(hwnd,es,wParam,lParam);
575 break;
576
577 case EM_GETIMESTATUS:
578 result = EDIT_EM_GetIMEStatus(hwnd,es,wParam,lParam);
579 break;
580
581 case WM_GETDLGCODE:
582 //DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
583 result = (es->style & ES_MULTILINE) ?
584 DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
585 DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
586 break;
587
588 case WM_CHAR:
589 //DPRINTF_EDIT_MSG32("WM_CHAR");
590 EDIT_WM_Char(hwnd, es, (CHAR)wParam, (DWORD)lParam);
591 break;
592
593 case WM_CLEAR:
594 //DPRINTF_EDIT_MSG32("WM_CLEAR");
595 EDIT_WM_Clear(hwnd, es);
596 break;
597
598 case WM_COMMAND:
599 //DPRINTF_EDIT_MSG32("WM_COMMAND");
600 EDIT_WM_Command(hwnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
601 break;
602
603 case WM_CONTEXTMENU:
604 //DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
605 EDIT_WM_ContextMenu(hwnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
606 break;
607
608 case WM_COPY:
609 //DPRINTF_EDIT_MSG32("WM_COPY");
610 EDIT_WM_Copy(hwnd, es);
611 break;
612
613 case WM_CREATE:
614 //DPRINTF_EDIT_MSG32("WM_CREATE");
615 result = EDIT_WM_Create(hwnd, es, (LPCREATESTRUCTA)lParam);
616 break;
617
618 case WM_CUT:
619 //DPRINTF_EDIT_MSG32("WM_CUT");
620 EDIT_WM_Cut(hwnd, es);
621 break;
622
623 case WM_ENABLE:
624 //DPRINTF_EDIT_MSG32("WM_ENABLE");
625 es->bEnableState = (BOOL)wParam;
626 EDIT_Refresh(hwnd,es,FALSE);
627 break;
628
629 case WM_ERASEBKGND:
630 //DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
631 result = EDIT_WM_EraseBkGnd(hwnd, es, (HDC)wParam);
632 break;
633
634 case WM_GETFONT:
635 //DPRINTF_EDIT_MSG32("WM_GETFONT");
636 result = (LRESULT)es->font;
637 break;
638
639 case WM_GETTEXT:
640 //DPRINTF_EDIT_MSG32("WM_GETTEXT");
641 result = (LRESULT)EDIT_WM_GetText(hwnd, es, (INT)wParam, (LPSTR)lParam);
642 break;
643
644 case WM_GETTEXTLENGTH:
645 //DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
646 result = lstrlenA(es->text);
647 break;
648
649 case WM_HSCROLL:
650 //DPRINTF_EDIT_MSG32("WM_HSCROLL");
651 result = EDIT_WM_HScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
652 break;
653
654 case WM_KEYDOWN:
655 //DPRINTF_EDIT_MSG32("WM_KEYDOWN");
656 result = EDIT_WM_KeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
657 break;
658
659 case WM_KILLFOCUS:
660 //DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
661 result = EDIT_WM_KillFocus(hwnd, es, (HWND)wParam);
662 break;
663
664 case WM_LBUTTONDBLCLK:
665 //DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
666 result = EDIT_WM_LButtonDblClk(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
667 break;
668
669 case WM_LBUTTONDOWN:
670 //DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
671 result = EDIT_WM_LButtonDown(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
672 break;
673
674 case WM_LBUTTONUP:
675 //DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
676 result = EDIT_WM_LButtonUp(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
677 break;
678
679 case WM_CAPTURECHANGED:
680 result = EDIT_WM_CaptureChanged(hwnd,es);
681 break;
682
683 case WM_MOUSEACTIVATE:
684 /*
685 * FIXME: maybe DefWindowProc() screws up, but it seems that
686 * modalless dialog boxes need this. If we don't do this, the focus
687 * will _not_ be set by DefWindowProc() for edit controls in a
688 * modalless dialog box ???
689 */
690 //DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
691 SetFocus(hwnd);
692 result = MA_ACTIVATE;
693 break;
694
695 case WM_MOUSEMOVE:
696 /*
697 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
698 */
699 result = EDIT_WM_MouseMove(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
700 break;
701
702 case WM_PAINT:
703 //DPRINTF_EDIT_MSG32("WM_PAINT");
704 EDIT_WM_Paint(hwnd, es,wParam);
705 break;
706
707 case WM_PASTE:
708 //DPRINTF_EDIT_MSG32("WM_PASTE");
709 EDIT_WM_Paste(hwnd, es);
710 break;
711
712 case WM_SETFOCUS:
713 //DPRINTF_EDIT_MSG32("WM_SETFOCUS");
714 EDIT_WM_SetFocus(hwnd, es, (HWND)wParam);
715 break;
716
717 case WM_SETFONT:
718 //DPRINTF_EDIT_MSG32("WM_SETFONT");
719 EDIT_WM_SetFont(hwnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
720 break;
721
722 case WM_SETTEXT:
723 //DPRINTF_EDIT_MSG32("WM_SETTEXT");
724 EDIT_WM_SetText(hwnd, es, (LPCSTR)lParam);
725 result = TRUE;
726 break;
727
728 case WM_SIZE:
729 //DPRINTF_EDIT_MSG32("WM_SIZE");
730 EDIT_WM_Size(hwnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
731 break;
732
733 case WM_SYSKEYDOWN:
734 //DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
735 result = EDIT_WM_SysKeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
736 break;
737
738 case WM_TIMER:
739 //DPRINTF_EDIT_MSG32("WM_TIMER");
740 EDIT_WM_Timer(hwnd, es, (INT)wParam, (TIMERPROC)lParam);
741 break;
742
743 case WM_VSCROLL:
744 //DPRINTF_EDIT_MSG32("WM_VSCROLL");
745 result = EDIT_WM_VScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
746 break;
747
748 default:
749 result = DefWindowProcA(hwnd, msg, wParam, lParam);
750 break;
751 }
752 EDIT_UnlockBuffer(hwnd, es, FALSE);
753 END:
754 return result;
755
756}
757
758
759/*********************************************************************
760 *
761 * EDIT_BuildLineDefs_ML
762 *
763 * Build linked list of text lines.
764 * Lines can end with '\0' (last line), a character (if it is wrapped),
765 * a soft return '\r\r\n' or a hard return '\r\n'
766 *
767 */
768static void EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es)
769{
770 HDC hdc = 0;
771 HFONT old_font = 0;
772 LPSTR start, cp;
773 INT fw;
774 LINEDEF *current_def;
775 LINEDEF **previous_next;
776
777 current_def = es->first_line_def;
778 do {
779 LINEDEF *next_def = current_def->next;
780 HeapFree(es->heap, 0, current_def);
781 current_def = next_def;
782 } while (current_def);
783 es->line_count = 0;
784 es->text_width = 0;
785
786 fw = es->format_rect.right - es->format_rect.left;
787 start = es->text;
788 previous_next = &es->first_line_def;
789 do {
790 current_def = (LINEDEF*)HeapAlloc(es->heap, 0, sizeof(LINEDEF));
791 current_def->next = NULL;
792 cp = start;
793 while (*cp) {
794 if ((*cp == '\r') && (*(cp + 1) == '\n'))
795 break;
796 cp++;
797 }
798 if (!(*cp)) {
799 current_def->ending = END_0;
800 current_def->net_length = lstrlenA(start);
801 } else if ((cp > start) && (*(cp - 1) == '\r')) {
802 current_def->ending = END_SOFT;
803 current_def->net_length = cp - start - 1;
804 } else {
805 current_def->ending = END_HARD;
806 current_def->net_length = cp - start;
807 }
808
809 if (!hdc)
810 {
811 hdc = GetDC(hwnd);
812 if (es->font)
813 old_font = SelectObject(hdc, es->font);
814 }
815
816 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
817 start, current_def->net_length,
818 es->tabs_count, es->tabs));
819 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
820 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
821 INT next = 0;
822 INT prev;
823 do {
824 prev = next;
825 next = EDIT_CallWordBreakProc(hwnd, es, start - es->text,
826 prev + 1, current_def->net_length, WB_RIGHT);
827 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
828 start, next, es->tabs_count, es->tabs));
829 } while (current_def->width <= fw);
830 if (!prev) {
831 next = 0;
832 do {
833 prev = next;
834 next++;
835 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
836 start, next, es->tabs_count, es->tabs));
837 } while (current_def->width <= fw);
838 if (!prev)
839 prev = 1;
840 }
841 current_def->net_length = prev;
842 current_def->ending = END_WRAP;
843 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc, start,
844 current_def->net_length, es->tabs_count, es->tabs));
845 }
846 switch (current_def->ending) {
847 case END_SOFT:
848 current_def->length = current_def->net_length + 3;
849 break;
850 case END_HARD:
851 current_def->length = current_def->net_length + 2;
852 break;
853 case END_WRAP:
854 case END_0:
855 current_def->length = current_def->net_length;
856 break;
857 }
858 es->text_width = MAX(es->text_width, current_def->width);
859 start += current_def->length;
860 *previous_next = current_def;
861 previous_next = &current_def->next;
862 es->line_count++;
863 } while (current_def->ending != END_0);
864 if (hdc)
865 {
866 if (es->font)
867 SelectObject(hdc, old_font);
868 ReleaseDC(hwnd, hdc);
869 }
870 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
871}
872
873
874/*********************************************************************
875 *
876 * EDIT_CallWordBreakProc
877 *
878 * Call appropriate WordBreakProc (internal or external).
879 *
880 * Note: The "start" argument should always be an index refering
881 * to es->text. The actual wordbreak proc might be
882 * 16 bit, so we can't always pass any 32 bit LPSTR.
883 * Hence we assume that es->text is the buffer that holds
884 * the string under examination (we can decide this for ourselves).
885 *
886 */
887static INT EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
888{
889 if (es->word_break_procA)
890 {
891 //TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
892 // es->word_break_proc32A, es->text + start, index,
893 // count, action );
894 return (INT)es->word_break_procA( es->text + start, index,
895 count, action );
896 }
897 else
898 return EDIT_WordBreakProc(es->text + start, index, count, action);
899}
900
901
902/*********************************************************************
903 *
904 * EDIT_CharFromPos
905 *
906 * Beware: This is not the function called on EM_CHARFROMPOS
907 * The position _can_ be outside the formatting / client
908 * rectangle
909 * The return value is only the character index
910 *
911 */
912static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
913{
914 INT index;
915 HDC dc = 0;
916 HFONT old_font = 0;
917
918 if (es->style & ES_MULTILINE) {
919 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
920 INT line_index = 0;
921 LINEDEF *line_def = es->first_line_def;
922 INT low, high;
923 while ((line > 0) && line_def->next) {
924 line_index += line_def->length;
925 line_def = line_def->next;
926 line--;
927 }
928 x += es->x_offset - es->format_rect.left;
929 if (x >= line_def->width) {
930 if (after_wrap)
931 *after_wrap = (line_def->ending == END_WRAP);
932 return line_index + line_def->net_length;
933 }
934 if (x <= 0) {
935 if (after_wrap)
936 *after_wrap = FALSE;
937 return line_index;
938 }
939 low = line_index + 1;
940 high = line_index + line_def->net_length + 1;
941 if (low < high-1)
942 {
943 if (!dc)
944 {
945 dc = GetDC(hwnd);
946 if (es->font)
947 old_font = SelectObject(dc, es->font);
948 }
949
950 while (low < high-1)
951 {
952 INT mid = (low + high) / 2;
953 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
954 else low = mid;
955 }
956 }
957 index = low;
958
959 if (after_wrap)
960 *after_wrap = ((index == line_index + line_def->net_length) &&
961 (line_def->ending == END_WRAP));
962 } else {
963 LPSTR text;
964 SIZE size;
965 if (after_wrap)
966 *after_wrap = FALSE;
967 x -= es->format_rect.left;
968 if (!x)
969 return es->x_offset;
970 text = EDIT_GetPasswordPointer_SL(hwnd, es);
971 if (x < 0)
972 {
973 INT low = 0;
974 INT high = es->x_offset;
975
976 if (low < high-1)
977 {
978 if (!dc)
979 {
980 dc = GetDC(hwnd);
981 if (es->font)
982 old_font = SelectObject(dc, es->font);
983 }
984
985 while (low < high-1)
986 {
987 INT mid = (low + high) / 2;
988
989 GetTextExtentPoint32A( dc, text + mid,
990 es->x_offset - mid, &size );
991 if (size.cx > -x) low = mid;
992 else high = mid;
993 }
994 }
995 index = low;
996 } else
997 {
998 INT low = es->x_offset;
999 INT high = lstrlenA(es->text) + 1;
1000
1001 if (low < high-1)
1002 {
1003 if (!dc)
1004 {
1005 dc = GetDC(hwnd);
1006 if (es->font)
1007 old_font = SelectObject(dc, es->font);
1008 }
1009
1010 while (low < high-1)
1011 {
1012 INT mid = (low + high) / 2;
1013
1014 GetTextExtentPoint32A( dc, text + es->x_offset,
1015 mid - es->x_offset, &size );
1016 if (size.cx > x) high = mid;
1017 else low = mid;
1018 }
1019 }
1020 index = low;
1021 }
1022 if (es->style & ES_PASSWORD)
1023 HeapFree(es->heap, 0 ,text);
1024 }
1025 if (dc)
1026 {
1027 if (es->font)
1028 SelectObject(dc, old_font);
1029 ReleaseDC(hwnd, dc);
1030 }
1031 return index;
1032}
1033
1034
1035/*********************************************************************
1036 *
1037 * EDIT_ConfinePoint
1038 *
1039 * adjusts the point to be within the formatting rectangle
1040 * (so CharFromPos returns the nearest _visible_ character)
1041 *
1042 */
1043static void EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y)
1044{
1045 *x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
1046 *y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
1047}
1048
1049
1050/*********************************************************************
1051 *
1052 * EDIT_GetLineRect
1053 *
1054 * Calculates the bounding rectangle for a line from a starting
1055 * column to an ending column.
1056 *
1057 */
1058static void EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1059{
1060 INT line_index = EDIT_EM_LineIndex(hwnd, es, line);
1061
1062 if (es->style & ES_MULTILINE)
1063 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1064 else
1065 rc->top = es->format_rect.top;
1066 rc->bottom = rc->top + es->line_height;
1067
1068 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + scol, TRUE));
1069 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + ecol, TRUE))+1;
1070}
1071
1072
1073/*********************************************************************
1074 *
1075 * EDIT_GetPasswordPointer_SL
1076 *
1077 * note: caller should free the (optionally) allocated buffer
1078 *
1079 */
1080static LPSTR EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es)
1081{
1082 if (es->style & ES_PASSWORD) {
1083 INT len = lstrlenA(es->text);
1084 LPSTR text = (LPSTR)HeapAlloc(es->heap, 0, len + 1);
1085 RtlFillMemory(text, len, es->password_char);
1086 text[len] = '\0';
1087 return text;
1088 } else
1089 return es->text;
1090}
1091
1092
1093/*********************************************************************
1094 *
1095 * EDIT_LockBuffer
1096 *
1097 * This acts as a LOCAL_Lock(), but it locks only once. This way
1098 * you can call it whenever you like, without unlocking.
1099 *
1100 */
1101static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es)
1102{
1103 if (!es) {
1104 //ERR_(edit)("no EDITSTATE ... please report\n");
1105 return;
1106 }
1107 if (!(es->style & ES_MULTILINE))
1108 return;
1109 if (!es->text) {
1110 if (es->hloc)
1111 es->text = (char*)LocalLock(es->hloc);
1112 else {
1113 //ERR_(edit)("no buffer ... please report\n");
1114 return;
1115 }
1116 }
1117 es->lock_count++;
1118}
1119
1120
1121/*********************************************************************
1122 *
1123 * EDIT_SL_InvalidateText
1124 *
1125 * Called from EDIT_InvalidateText().
1126 * Does the job for single-line controls only.
1127 *
1128 */
1129static void EDIT_SL_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1130{
1131 RECT line_rect;
1132 RECT rc;
1133
1134 EDIT_GetLineRect(hwnd,0, es, 0, start, end, &line_rect);
1135
1136 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1137 {
1138 HideCaret(hwnd);
1139 InvalidateRect(hwnd, &rc, TRUE);
1140 ShowCaret(hwnd);
1141 }
1142}
1143
1144
1145/*********************************************************************
1146 *
1147 * EDIT_ML_InvalidateText
1148 *
1149 * Called from EDIT_InvalidateText().
1150 * Does the job for multi-line controls only.
1151 *
1152 */
1153static void EDIT_ML_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1154{
1155 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1156 INT sl = EDIT_EM_LineFromChar(hwnd, es, start);
1157 INT el = EDIT_EM_LineFromChar(hwnd, es, end);
1158 INT sc;
1159 INT ec;
1160 RECT rc1;
1161 RECT rcWnd;
1162 RECT rcLine;
1163 RECT rcUpdate;
1164 INT l;
1165
1166 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1167 return;
1168
1169 sc = start - EDIT_EM_LineIndex(hwnd, es, sl);
1170 ec = end - EDIT_EM_LineIndex(hwnd, es, el);
1171 if (sl < es->y_offset) {
1172 sl = es->y_offset;
1173 sc = 0;
1174 }
1175 if (el > es->y_offset + vlc) {
1176 el = es->y_offset + vlc;
1177 ec = EDIT_EM_LineLength(hwnd, es, EDIT_EM_LineIndex(hwnd, es, el));
1178 }
1179 GetClientRect(hwnd, &rc1);
1180 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1181 HideCaret(hwnd);
1182 if (sl == el) {
1183 EDIT_GetLineRect(hwnd,0, es, sl, sc, ec, &rcLine);
1184
1185 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1186 InvalidateRect(hwnd, &rcUpdate, TRUE);
1187 } else {
1188 EDIT_GetLineRect(hwnd,0, es, sl, sc,
1189 EDIT_EM_LineLength(hwnd, es,
1190 EDIT_EM_LineIndex(hwnd, es, sl)),
1191 &rcLine);
1192 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1193 InvalidateRect(hwnd, &rcUpdate, TRUE);
1194 for (l = sl + 1 ; l < el ; l++) {
1195 EDIT_GetLineRect(hwnd,0, es, l, 0,
1196 EDIT_EM_LineLength(hwnd, es,
1197 EDIT_EM_LineIndex(hwnd, es, l)),
1198 &rcLine);
1199 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1200 InvalidateRect(hwnd, &rcUpdate, TRUE);
1201 }
1202 EDIT_GetLineRect(hwnd,0, es, el, 0, ec, &rcLine);
1203 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1204 InvalidateRect(hwnd, &rcUpdate, TRUE);
1205 }
1206 ShowCaret(hwnd);
1207}
1208
1209
1210/*********************************************************************
1211 *
1212 * EDIT_InvalidateText
1213 *
1214 * Invalidate the text from offset start upto, but not including,
1215 * offset end. Useful for (re)painting the selection.
1216 * Regions outside the linewidth are not invalidated.
1217 * end == -1 means end == TextLength.
1218 * start and end need not be ordered.
1219 *
1220 */
1221static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1222{
1223 if (end == start)
1224 return;
1225
1226 if (end == -1)
1227 end = lstrlenA(es->text);
1228
1229 ORDER_INT(start, end);
1230
1231 if (es->style & ES_MULTILINE)
1232 EDIT_ML_InvalidateText(hwnd, es, start, end);
1233 else
1234 EDIT_SL_InvalidateText(hwnd, es, start, end);
1235}
1236
1237
1238/*********************************************************************
1239 *
1240 * EDIT_MakeFit
1241 *
1242 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1243 *
1244 */
1245static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size)
1246{
1247 HLOCAL hNew32;
1248
1249 if (size <= es->buffer_size)
1250 return TRUE;
1251 if (size > es->buffer_limit) {
1252 EDIT_NOTIFY_PARENT(hwnd, EN_MAXTEXT);
1253 return FALSE;
1254 }
1255 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1256 if (size > es->buffer_limit)
1257 size = es->buffer_limit;
1258
1259 //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
1260
1261 EDIT_UnlockBuffer(hwnd, es, TRUE);
1262 if (es->text) {
1263 es->text = (char*)HeapReAlloc(es->heap, 0, es->text, size + 1);
1264 if (es->text)
1265 es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1266 else
1267 es->buffer_size = 0;
1268 } else if (es->hloc) {
1269 hNew32 = LocalReAlloc(es->hloc, size + 1, 0);
1270 if (hNew32) {
1271 //TRACE_(edit)("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1272 es->hloc = hNew32;
1273 es->buffer_size = MIN(LocalSize(es->hloc) - 1, es->buffer_limit);
1274 }
1275 }
1276 if (es->buffer_size < size) {
1277 EDIT_LockBuffer(hwnd, es);
1278 //WARN_(edit)("FAILED ! We now have %d+1\n", es->buffer_size);
1279 EDIT_NOTIFY_PARENT(hwnd, EN_ERRSPACE);
1280 return FALSE;
1281 } else {
1282 EDIT_LockBuffer(hwnd, es);
1283 //TRACE_(edit)("We now have %d+1\n", es->buffer_size);
1284 return TRUE;
1285 }
1286}
1287
1288
1289/*********************************************************************
1290 *
1291 * EDIT_MakeUndoFit
1292 *
1293 * Try to fit size + 1 bytes in the undo buffer.
1294 *
1295 */
1296static BOOL EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size)
1297{
1298 if (size <= es->undo_buffer_size)
1299 return TRUE;
1300 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1301
1302 //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
1303 es->undo_text = (char*)HeapReAlloc(es->heap, 0, es->undo_text, size + 1);
1304 if (es->undo_text) {
1305 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1306 if (es->undo_buffer_size < size) {
1307 //WARN_(edit)("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1308 return FALSE;
1309 }
1310 return TRUE;
1311 }
1312 return FALSE;
1313}
1314
1315
1316/*********************************************************************
1317 *
1318 * EDIT_MoveBackward
1319 *
1320 */
1321static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
1322{
1323 INT e = es->selection_end;
1324
1325 if (e) {
1326 e--;
1327 if ((es->style & ES_MULTILINE) && e &&
1328 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1329 e--;
1330 if (e && (es->text[e - 1] == '\r'))
1331 e--;
1332 }
1333 }
1334 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1335 EDIT_EM_ScrollCaret(hwnd, es);
1336}
1337
1338
1339/*********************************************************************
1340 *
1341 * EDIT_MoveDown_ML
1342 *
1343 * Only for multi line controls
1344 * Move the caret one line down, on a column with the nearest
1345 * x coordinate on the screen (might be a different column).
1346 *
1347 */
1348static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1349{
1350 INT s = es->selection_start;
1351 INT e = es->selection_end;
1352 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1353 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1354 INT x = SLOWORD(pos);
1355 INT y = SHIWORD(pos);
1356
1357 e = EDIT_CharFromPos(hwnd, es, x, y + es->line_height, &after_wrap);
1358 if (!extend)
1359 s = e;
1360 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1361 EDIT_EM_ScrollCaret(hwnd, es);
1362}
1363
1364
1365/*********************************************************************
1366 *
1367 * EDIT_MoveEnd
1368 *
1369 */
1370static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend)
1371{
1372 BOOL after_wrap = FALSE;
1373 INT e;
1374
1375 /* Pass a high value in x to make sure of receiving the en of the line */
1376 if (es->style & ES_MULTILINE)
1377 e = EDIT_CharFromPos(hwnd, es, 0x3fffffff,
1378 HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1379 else
1380 e = lstrlenA(es->text);
1381 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, after_wrap);
1382 EDIT_EM_ScrollCaret(hwnd, es);
1383}
1384
1385
1386/*********************************************************************
1387 *
1388 * EDIT_MoveForward
1389 *
1390 */
1391static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend)
1392{
1393 INT e = es->selection_end;
1394
1395 if (es->text[e]) {
1396 e++;
1397 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1398 if (es->text[e] == '\n')
1399 e++;
1400 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1401 e += 2;
1402 }
1403 }
1404 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1405 EDIT_EM_ScrollCaret(hwnd, es);
1406}
1407
1408
1409/*********************************************************************
1410 *
1411 * EDIT_MoveHome
1412 *
1413 * Home key: move to beginning of line.
1414 *
1415 */
1416static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend)
1417{
1418 INT e;
1419
1420 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1421 if (es->style & ES_MULTILINE)
1422 e = EDIT_CharFromPos(hwnd, es, -es->x_offset,
1423 HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1424 else
1425 e = 0;
1426 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1427 EDIT_EM_ScrollCaret(hwnd, es);
1428}
1429
1430
1431/*********************************************************************
1432 *
1433 * EDIT_MovePageDown_ML
1434 *
1435 * Only for multi line controls
1436 * Move the caret one page down, on a column with the nearest
1437 * x coordinate on the screen (might be a different column).
1438 *
1439 */
1440static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1441{
1442 INT s = es->selection_start;
1443 INT e = es->selection_end;
1444 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1445 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1446 INT x = SLOWORD(pos);
1447 INT y = SHIWORD(pos);
1448
1449 e = EDIT_CharFromPos(hwnd, es, x,
1450 y + (es->format_rect.bottom - es->format_rect.top),
1451 &after_wrap);
1452 if (!extend)
1453 s = e;
1454 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1455 EDIT_EM_ScrollCaret(hwnd, es);
1456}
1457
1458
1459/*********************************************************************
1460 *
1461 * EDIT_MovePageUp_ML
1462 *
1463 * Only for multi line controls
1464 * Move the caret one page up, on a column with the nearest
1465 * x coordinate on the screen (might be a different column).
1466 *
1467 */
1468static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1469{
1470 INT s = es->selection_start;
1471 INT e = es->selection_end;
1472 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1473 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1474 INT x = SLOWORD(pos);
1475 INT y = SHIWORD(pos);
1476
1477 e = EDIT_CharFromPos(hwnd, es, x,
1478 y - (es->format_rect.bottom - es->format_rect.top),
1479 &after_wrap);
1480 if (!extend)
1481 s = e;
1482 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1483 EDIT_EM_ScrollCaret(hwnd, es);
1484}
1485
1486
1487/*********************************************************************
1488 *
1489 * EDIT_MoveUp_ML
1490 *
1491 * Only for multi line controls
1492 * Move the caret one line up, on a column with the nearest
1493 * x coordinate on the screen (might be a different column).
1494 *
1495 */
1496static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1497{
1498 INT s = es->selection_start;
1499 INT e = es->selection_end;
1500 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1501 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1502 INT x = SLOWORD(pos);
1503 INT y = SHIWORD(pos);
1504
1505 e = EDIT_CharFromPos(hwnd, es, x, y - es->line_height, &after_wrap);
1506 if (!extend)
1507 s = e;
1508 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1509 EDIT_EM_ScrollCaret(hwnd, es);
1510}
1511
1512
1513/*********************************************************************
1514 *
1515 * EDIT_MoveWordBackward
1516 *
1517 */
1518static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
1519{
1520 INT s = es->selection_start;
1521 INT e = es->selection_end;
1522 INT l;
1523 INT ll;
1524 INT li;
1525
1526 l = EDIT_EM_LineFromChar(hwnd, es, e);
1527 ll = EDIT_EM_LineLength(hwnd, es, e);
1528 li = EDIT_EM_LineIndex(hwnd, es, l);
1529 if (e - li == 0) {
1530 if (l) {
1531 li = EDIT_EM_LineIndex(hwnd, es, l - 1);
1532 e = li + EDIT_EM_LineLength(hwnd, es, li);
1533 }
1534 } else {
1535 e = li + (INT)EDIT_CallWordBreakProc(hwnd, es,
1536 li, e - li, ll, WB_LEFT);
1537 }
1538 if (!extend)
1539 s = e;
1540 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
1541 EDIT_EM_ScrollCaret(hwnd, es);
1542}
1543
1544
1545/*********************************************************************
1546 *
1547 * EDIT_MoveWordForward
1548 *
1549 */
1550static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend)
1551{
1552 INT s = es->selection_start;
1553 INT e = es->selection_end;
1554 INT l;
1555 INT ll;
1556 INT li;
1557
1558 l = EDIT_EM_LineFromChar(hwnd, es, e);
1559 ll = EDIT_EM_LineLength(hwnd, es, e);
1560 li = EDIT_EM_LineIndex(hwnd, es, l);
1561 if (e - li == ll) {
1562 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1563 e = EDIT_EM_LineIndex(hwnd, es, l + 1);
1564 } else {
1565 e = li + EDIT_CallWordBreakProc(hwnd, es,
1566 li, e - li + 1, ll, WB_RIGHT);
1567 }
1568 if (!extend)
1569 s = e;
1570 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
1571 EDIT_EM_ScrollCaret(hwnd, es);
1572}
1573
1574
1575/*********************************************************************
1576 *
1577 * EDIT_PaintLine
1578 *
1579 */
1580static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1581{
1582 INT s = es->selection_start;
1583 INT e = es->selection_end;
1584 INT li;
1585 INT ll;
1586 INT x;
1587 INT y;
1588 LRESULT pos;
1589
1590 if (es->style & ES_MULTILINE) {
1591 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1592 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1593 return;
1594 } else if (line)
1595 return;
1596
1597 //TRACE_(edit)("line=%d\n", line);
1598
1599 pos = EDIT_EM_PosFromChar(hwnd,dc, es, EDIT_EM_LineIndex(hwnd, es, line), FALSE);
1600 x = SLOWORD(pos);
1601 y = SHIWORD(pos);
1602 li = EDIT_EM_LineIndex(hwnd, es, line);
1603 ll = EDIT_EM_LineLength(hwnd, es, li);
1604 s = es->selection_start;
1605 e = es->selection_end;
1606 ORDER_INT(s, e);
1607 s = MIN(li + ll, MAX(li, s));
1608 e = MIN(li + ll, MAX(li, e));
1609
1610 if (rev && (s != e) && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL)))
1611 {
1612 HRGN oldRgn,newRgn,combRgn;
1613 RECT rect;
1614 //CB: OS/2 has problems with relative string positions (i.e. Communicator)
1615 // fix: always calculate string from starting point, tab bugs fixed too
1616 // otherwise we have 'dancing characters'
1617
1618 if (!(es->style & ES_MULTILINE))
1619 {
1620 SIZE size;
1621
1622 rect.top = y;
1623 rect.bottom = y+es->line_height;
1624 GetTextExtentPoint32A(dc,es->text+li,s-li,&size);
1625 rect.left = x+size.cx;
1626 GetTextExtentPoint32A(dc,es->text+li,e-li,&size);
1627 rect.right = x+size.cx;
1628
1629 oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1630 GetClipRgn(dc,oldRgn);
1631 newRgn = CreateRectRgnIndirect(&rect);
1632 combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1633 CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
1634 SelectClipRgn(dc,combRgn);
1635 EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
1636 CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
1637 SelectClipRgn(dc,combRgn);
1638 EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
1639 DeleteObject(oldRgn);
1640 DeleteObject(newRgn);
1641 DeleteObject(combRgn);
1642 } else
1643 {
1644 rect.top = y;
1645 rect.bottom = y+es->line_height;
1646 rect.left = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,s-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
1647 rect.right = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,e-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
1648
1649 oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1650 GetClipRgn(dc,oldRgn);
1651 newRgn = CreateRectRgnIndirect(&rect);
1652 combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1653 CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
1654 SelectClipRgn(dc,combRgn);
1655 EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
1656 CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
1657 SelectClipRgn(dc,combRgn);
1658 EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
1659 SelectClipRgn(dc,oldRgn);
1660 DeleteObject(oldRgn);
1661 DeleteObject(newRgn);
1662 DeleteObject(combRgn);
1663 }
1664 } else EDIT_PaintText(hwnd, es, dc, x, y, line, 0, ll, FALSE);
1665}
1666
1667
1668/*********************************************************************
1669 *
1670 * EDIT_PaintText
1671 *
1672 */
1673static VOID EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1674{
1675 COLORREF BkColor;
1676 COLORREF TextColor;
1677 INT li;
1678
1679 if (!count)
1680 return;
1681 BkColor = GetBkColor(dc);
1682 TextColor = GetTextColor(dc);
1683 if (rev)
1684 {
1685 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1686 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1687 }
1688 li = EDIT_EM_LineIndex(hwnd, es, line);
1689 if (es->style & ES_MULTILINE)
1690 {
1691 TabbedTextOutA(dc, x, y, es->text + li + col, count,
1692 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset);
1693 } else
1694 {
1695 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
1696 POINT pt;
1697
1698 TextOutA(dc,x,y,text+li+col,count);
1699 if (es->style & ES_PASSWORD)
1700 HeapFree(es->heap, 0, text);
1701 }
1702 if (rev)
1703 {
1704 SetBkColor(dc, BkColor);
1705 SetTextColor(dc, TextColor);
1706 }
1707}
1708
1709
1710/*********************************************************************
1711 *
1712 * EDIT_SetCaretPos
1713 *
1714 */
1715static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos,
1716 BOOL after_wrap)
1717{
1718 LRESULT res = EDIT_EM_PosFromChar(hwnd,0, es, pos, after_wrap);
1719 INT x = SLOWORD(res);
1720 INT y = SHIWORD(res);
1721
1722 if(x < es->format_rect.left)
1723 x = es->format_rect.left;
1724 if(x > es->format_rect.right - 2)
1725 x = es->format_rect.right - 2;
1726 if(y > es->format_rect.bottom)
1727 y = es->format_rect.bottom;
1728 if(y < es->format_rect.top)
1729 y = es->format_rect.top;
1730 SetCaretPos(x, y);
1731 return;
1732}
1733
1734
1735/*********************************************************************
1736 *
1737 * EDIT_SetRectNP
1738 *
1739 * note: this is not (exactly) the handler called on EM_SETRECTNP
1740 * it is also used to set the rect of a single line control
1741 *
1742 */
1743static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT rc)
1744{
1745 CopyRect(&es->format_rect, rc);
1746 if (es->style & WS_BORDER)
1747 {
1748 INT bw = GetSystemMetrics(SM_CXBORDER)+1,bh = GetSystemMetrics(SM_CYBORDER)+1;
1749
1750 es->format_rect.left += bw;
1751 es->format_rect.top += bh;
1752 es->format_rect.right -= bw;
1753 es->format_rect.bottom -= bh;
1754 }
1755 es->format_rect.left += es->left_margin;
1756 es->format_rect.right -= es->right_margin;
1757 es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
1758 if (es->style & ES_MULTILINE)
1759 es->format_rect.bottom = es->format_rect.top +
1760 MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1761 else
1762 es->format_rect.bottom = es->format_rect.top + es->line_height;
1763 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1764 EDIT_BuildLineDefs_ML(hwnd,es);
1765 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
1766}
1767
1768
1769/*********************************************************************
1770 *
1771 * EDIT_UnlockBuffer
1772 *
1773 */
1774static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force)
1775{
1776 if (!es) {
1777 //ERR_(edit)("no EDITSTATE ... please report\n");
1778 return;
1779 }
1780 if (!(es->style & ES_MULTILINE))
1781 return;
1782 if (!es->lock_count) {
1783 //ERR_(edit)("lock_count == 0 ... please report\n");
1784 return;
1785 }
1786 if (!es->text) {
1787 //ERR_(edit)("es->text == 0 ... please report\n");
1788 return;
1789 }
1790 if (force || (es->lock_count == 1)) {
1791 if (es->hloc) {
1792 LocalUnlock(es->hloc);
1793 es->text = NULL;
1794 }
1795 }
1796 es->lock_count--;
1797}
1798
1799
1800/*********************************************************************
1801 *
1802 * EDIT_WordBreakProc
1803 *
1804 * Find the beginning of words.
1805 * Note: unlike the specs for a WordBreakProc, this function only
1806 * allows to be called without linebreaks between s[0] upto
1807 * s[count - 1]. Remember it is only called
1808 * internally, so we can decide this for ourselves.
1809 *
1810 */
1811static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1812{
1813 INT ret = 0;
1814
1815 //TRACE_(edit)("s=%p, index=%u, count=%u, action=%d\n",
1816 // s, index, count, action);
1817
1818 switch (action) {
1819 case WB_LEFT:
1820 if (!count)
1821 break;
1822 if (index)
1823 index--;
1824 if (s[index] == ' ') {
1825 while (index && (s[index] == ' '))
1826 index--;
1827 if (index) {
1828 while (index && (s[index] != ' '))
1829 index--;
1830 if (s[index] == ' ')
1831 index++;
1832 }
1833 } else {
1834 while (index && (s[index] != ' '))
1835 index--;
1836 if (s[index] == ' ')
1837 index++;
1838 }
1839 ret = index;
1840 break;
1841 case WB_RIGHT:
1842 if (!count)
1843 break;
1844 if (index)
1845 index--;
1846 if (s[index] == ' ')
1847 while ((index < count) && (s[index] == ' ')) index++;
1848 else {
1849 while (s[index] && (s[index] != ' ') && (index < count))
1850 index++;
1851 while ((s[index] == ' ') && (index < count)) index++;
1852 }
1853 ret = index;
1854 break;
1855 case WB_ISDELIMITER:
1856 ret = (s[index] == ' ');
1857 break;
1858 default:
1859 //ERR_(edit)("unknown action code, please report !\n");
1860 break;
1861 }
1862 return ret;
1863}
1864
1865
1866/*********************************************************************
1867 *
1868 * EM_CHARFROMPOS
1869 *
1870 * returns line number (not index) in high-order word of result.
1871 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
1872 * to Richedit, not to the edit control. Original documentation is valid.
1873 *
1874 */
1875static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y)
1876{
1877 POINT pt;
1878 INT index;
1879
1880 pt.x = x;
1881 pt.y = y;
1882
1883 if (!PtInRect(&es->format_rect,pt)) return -1;
1884
1885 index = EDIT_CharFromPos(hwnd, es, x, y, NULL);
1886 return MAKELONG(index, EDIT_EM_LineFromChar(hwnd, es, index));
1887}
1888
1889
1890/*********************************************************************
1891 *
1892 * EM_FMTLINES
1893 *
1894 * Enable or disable soft breaks.
1895 */
1896static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol)
1897{
1898 es->flags &= ~EF_USE_SOFTBRK;
1899 if (add_eol) {
1900 es->flags |= EF_USE_SOFTBRK;
1901 dprintf(("EDIT: EM_FMTLINES: soft break enabled, not implemented\n"));
1902 }
1903 return add_eol;
1904}
1905
1906static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es)
1907{
1908 return (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
1909}
1910
1911/*********************************************************************
1912 *
1913 * EM_GETHANDLE
1914 *
1915 * Hopefully this won't fire back at us.
1916 * We always start with a fixed buffer in our own heap.
1917 * However, with this message a 32 bit application requests
1918 * a handle to 32 bit moveable local heap memory, where it expects
1919 * to find the text.
1920 * It's a pity that from this moment on we have to use this
1921 * local heap, because applications may rely on the handle
1922 * in the future.
1923 *
1924 * In this function we'll try to switch to local heap.
1925 *
1926 */
1927static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es)
1928{
1929 HLOCAL newBuf;
1930 LPSTR newText;
1931 INT newSize;
1932
1933 if (!(es->style & ES_MULTILINE))
1934 return 0;
1935
1936 if (es->hloc)
1937 return es->hloc;
1938
1939 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
1940 //ERR_(edit)("could not allocate new 32 bit buffer\n");
1941 return 0;
1942 }
1943 newSize = MIN(LocalSize(newBuf) - 1, es->buffer_limit);
1944 if (!(newText = (char*)LocalLock(newBuf))) {
1945 //ERR_(edit)("could not lock new 32 bit buffer\n");
1946 LocalFree(newBuf);
1947 return 0;
1948 }
1949 lstrcpyA(newText, es->text);
1950 EDIT_UnlockBuffer(hwnd, es, TRUE);
1951 if (es->text)
1952 HeapFree(es->heap, 0, es->text);
1953 es->hloc = newBuf;
1954 es->buffer_size = newSize;
1955 es->text = newText;
1956 EDIT_LockBuffer(hwnd, es);
1957 //TRACE_(edit)("switched to 32 bit local heap\n");
1958
1959 return es->hloc;
1960}
1961
1962static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es)
1963{
1964 return es->buffer_limit;
1965}
1966
1967/*********************************************************************
1968 *
1969 * EM_GETLINE
1970 *
1971 */
1972static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch)
1973{
1974 LPSTR src;
1975 INT len;
1976 INT i;
1977
1978 if (!lpch || (*(WORD*)lpch == 0)) return 0;
1979
1980 if (es->style & ES_MULTILINE) {
1981 if (line >= es->line_count)
1982 return 0;
1983 } else
1984 line = 0;
1985 i = EDIT_EM_LineIndex(hwnd, es, line);
1986 src = es->text + i;
1987 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(hwnd, es, i));
1988 for (i = 0 ; i < len ; i++) {
1989 *lpch = *src;
1990 src++;
1991 lpch++;
1992 }
1993 //SvL: Terminate string
1994 *lpch = 0;
1995 return (LRESULT)len;
1996}
1997
1998static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es)
1999{
2000 return (es->style & ES_MULTILINE) ? es->line_count : 1;
2001}
2002
2003static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es)
2004{
2005 return MAKELONG(es->left_margin, es->right_margin);
2006}
2007
2008static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es)
2009{
2010 return ((es->flags & EF_MODIFIED) != 0);
2011}
2012
2013static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es)
2014{
2015 return es->password_char;
2016}
2017
2018static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2019{
2020 if (lprc) CopyRect(lprc,&es->format_rect);
2021}
2022
2023/*********************************************************************
2024 *
2025 * EM_GETSEL
2026 *
2027 */
2028static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end)
2029{
2030 UINT s = es->selection_start;
2031 UINT e = es->selection_end;
2032
2033 ORDER_UINT(s, e);
2034
2035 if (start)
2036 *start = s;
2037 if (end)
2038 *end = e;
2039 return MAKELONG(s, e);
2040}
2041
2042/*********************************************************************
2043 *
2044 * EM_GETTHUMB
2045 */
2046static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
2047{
2048 SCROLLINFO si;
2049
2050 if (!(es->style & ES_MULTILINE)) return 0;
2051
2052 si.cbSize = sizeof(si);
2053 si.fMask = SIF_TRACKPOS;
2054 return GetScrollInfo(hwnd,SB_VERT,&si) ? si.nTrackPos:0;
2055}
2056
2057static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es)
2058{
2059 return es->word_break_procA;
2060}
2061
2062/*********************************************************************
2063 *
2064 * EM_LINEFROMCHAR
2065 *
2066 */
2067static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index)
2068{
2069 INT line;
2070 LINEDEF *line_def;
2071
2072 if (!(es->style & ES_MULTILINE))
2073 return 0;
2074 if (index > lstrlenA(es->text))
2075 return es->line_count - 1;
2076 if (index == -1)
2077 index = MIN(es->selection_start, es->selection_end); //selection_end == caret pos
2078
2079 line = 0;
2080 line_def = es->first_line_def;
2081 index -= line_def->length;
2082 while ((index >= 0) && line_def->next) {
2083 line++;
2084 line_def = line_def->next;
2085 index -= line_def->length;
2086 }
2087 return line;
2088}
2089
2090
2091/*********************************************************************
2092 *
2093 * EM_LINEINDEX
2094 *
2095 */
2096static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line)
2097{
2098 INT line_index;
2099 LINEDEF *line_def;
2100
2101 if (!(es->style & ES_MULTILINE))
2102 return 0;
2103 if (line >= es->line_count)
2104 return -1;
2105
2106 line_index = 0;
2107 line_def = es->first_line_def;
2108 if (line == -1) {
2109 INT index = es->selection_end - line_def->length;
2110 while ((index >= 0) && line_def->next) {
2111 line_index += line_def->length;
2112 line_def = line_def->next;
2113 index -= line_def->length;
2114 }
2115 } else {
2116 while (line > 0) {
2117 line_index += line_def->length;
2118 line_def = line_def->next;
2119 line--;
2120 }
2121 }
2122 return line_index;
2123}
2124
2125
2126/*********************************************************************
2127 *
2128 * EM_LINELENGTH
2129 *
2130 */
2131static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index)
2132{
2133 LINEDEF *line_def;
2134
2135 if (!(es->style & ES_MULTILINE))
2136 return lstrlenA(es->text);
2137
2138 if (index == -1)
2139 {
2140 INT sl = EDIT_EM_LineFromChar(hwnd,es,MIN(es->selection_start,es->selection_end));
2141 INT el = EDIT_EM_LineFromChar(hwnd,es,MAX(es->selection_start,es->selection_end));
2142
2143 if (sl == el)
2144 return EDIT_EM_LineLength(hwnd,es,sl)+es->selection_start-es->selection_end;
2145 else
2146 return es->selection_start+EDIT_EM_LineLength(hwnd,es,el)-es->selection_end;
2147 }
2148 line_def = es->first_line_def;
2149 index -= line_def->length;
2150 while ((index >= 0) && line_def->next) {
2151 line_def = line_def->next;
2152 index -= line_def->length;
2153 }
2154 return line_def->net_length;
2155}
2156
2157static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert)
2158{
2159 if (updateHorz && (es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2160 {
2161 SCROLLINFO si;
2162 INT fw = es->format_rect.right - es->format_rect.left;
2163
2164 si.cbSize = sizeof(SCROLLINFO);
2165 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2166 si.nMin = 0;
2167 si.nMax = es->text_width + fw - 1;
2168 si.nPage = fw;
2169 si.nPos = es->x_offset;
2170 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2171 }
2172
2173 if (updateVert && (es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2174 {
2175 INT vlc = (es->format_rect.bottom-es->format_rect.top)/es->line_height;
2176 SCROLLINFO si;
2177
2178 si.cbSize = sizeof(SCROLLINFO);
2179 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2180 si.nMin = 0;
2181 si.nMax = es->line_count + vlc - 2;
2182 si.nPage = vlc;
2183 si.nPos = es->y_offset;
2184 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2185 }
2186}
2187
2188
2189/*********************************************************************
2190 *
2191 * EM_LINESCROLL
2192 *
2193 * FIXME: dx is in average character widths
2194 * However, we assume it is in pixels when we use this
2195 * function internally
2196 *
2197 */
2198static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2199{
2200 INT nyoff;
2201
2202 if (!(es->style & ES_MULTILINE))
2203 return FALSE;
2204
2205 if (-dx > es->x_offset)
2206 dx = -es->x_offset;
2207 if (dx > es->text_width - es->x_offset)
2208 dx = es->text_width - es->x_offset;
2209 nyoff = MAX(0, es->y_offset + dy);
2210 if (nyoff >= es->line_count)
2211 nyoff = es->line_count - 1;
2212 dy = (es->y_offset - nyoff) * es->line_height;
2213 if (dx || dy)
2214 {
2215 RECT rc1;
2216 RECT rc;
2217
2218 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2219 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
2220 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2221 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2222
2223 GetClientRect(hwnd, &rc1);
2224 IntersectRect(&rc, &rc1, &es->format_rect);
2225
2226 ScrollWindowEx(hwnd,-dx,dy,NULL,&rc,(HRGN)NULL,NULL,SW_INVALIDATE);
2227 es->y_offset = nyoff;
2228 es->x_offset += dx;
2229 EDIT_UpdateScrollBars(hwnd,es,dx,dy);
2230 }
2231
2232 return TRUE;
2233}
2234
2235
2236/*********************************************************************
2237 *
2238 * EM_POSFROMCHAR
2239 *
2240 */
2241static LRESULT EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap)
2242{
2243 INT len = lstrlenA(es->text);
2244 INT l;
2245 INT li;
2246 INT x;
2247 INT y = 0;
2248 BOOL createdDC = FALSE;
2249 HFONT old_font = 0;
2250 SIZE size;
2251
2252 index = MIN(index, len);
2253 if (!dc)
2254 {
2255 dc = GetDC(hwnd);
2256 if (es->font)
2257 old_font = SelectObject(dc, es->font);
2258 createdDC = TRUE;
2259 }
2260 if (es->style & ES_MULTILINE) {
2261 l = EDIT_EM_LineFromChar(hwnd, es, index);
2262 y = (l - es->y_offset) * es->line_height;
2263 li = EDIT_EM_LineIndex(hwnd, es, l);
2264 if (after_wrap && (li == index) && l) {
2265 INT l2 = l - 1;
2266 LINEDEF *line_def = es->first_line_def;
2267 while (l2) {
2268 line_def = line_def->next;
2269 l2--;
2270 }
2271 if (line_def->ending == END_WRAP) {
2272 l--;
2273 y -= es->line_height;
2274 li = EDIT_EM_LineIndex(hwnd, es, l);
2275 }
2276 }
2277 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2278 es->tabs_count, es->tabs)) - es->x_offset;
2279 } else
2280 {
2281 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
2282
2283 GetTextExtentPoint32A(dc,text,index,&size);
2284 x = size.cx;
2285 if (es->x_offset)
2286 {
2287 GetTextExtentPoint32A(dc,text,es->x_offset,&size);
2288 x -= size.cx;
2289 }
2290 y = 0;
2291 if (es->style & ES_PASSWORD)
2292 HeapFree(es->heap, 0 ,text);
2293 }
2294 x += es->format_rect.left;
2295 y += es->format_rect.top;
2296 if (createdDC)
2297 {
2298 if (es->font)
2299 SelectObject(dc, old_font);
2300 ReleaseDC(hwnd, dc);
2301 }
2302 return MAKELONG((INT16)x, (INT16)y);
2303}
2304
2305BOOL EDIT_CheckNumber(CHAR *text)
2306{
2307 if (!text) return TRUE;
2308
2309 while (text[0] != 0)
2310 {
2311 if ((BYTE)text[0] < '0' || (BYTE)text[0] > '9') return FALSE;
2312 text++;
2313 }
2314
2315 return TRUE;
2316}
2317
2318/*********************************************************************
2319 *
2320 * EM_REPLACESEL
2321 *
2322 */
2323static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
2324{
2325 INT strl = lstrlenA(lpsz_replace);
2326 INT tl = lstrlenA(es->text);
2327 INT utl;
2328 UINT s;
2329 UINT e;
2330 INT i;
2331 LPSTR p;
2332
2333 s = es->selection_start;
2334 e = es->selection_end;
2335
2336 if ((s == e) && !strl)
2337 return;
2338
2339 ORDER_UINT(s, e);
2340
2341 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
2342 return;
2343
2344 if (e != s) {
2345 /* there is something to be deleted */
2346 if (can_undo) {
2347 utl = lstrlenA(es->undo_text);
2348 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2349 /* undo-buffer is extended to the right */
2350 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2351 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2352 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2353 /* undo-buffer is extended to the left */
2354 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2355 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2356 p[e - s] = p[0];
2357 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2358 p[i] = (es->text + s)[i];
2359 es->undo_position = s;
2360 } else {
2361 /* new undo-buffer */
2362 EDIT_MakeUndoFit(hwnd, es, e - s);
2363 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2364 es->undo_position = s;
2365 }
2366 /* any deletion makes the old insertion-undo invalid */
2367 es->undo_insert_count = 0;
2368 } else
2369 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2370
2371 /* now delete */
2372 lstrcpyA(es->text + s, es->text + e);
2373 }
2374 if (strl)
2375 {
2376 if ((es->style & ES_NUMBER) && !EDIT_CheckNumber((CHAR*)lpsz_replace))
2377 MessageBeep(MB_ICONEXCLAMATION);
2378 else
2379 {
2380 /* there is an insertion */
2381 if (can_undo) {
2382 if ((s == es->undo_position) ||
2383 ((es->undo_insert_count) &&
2384 (s == es->undo_position + es->undo_insert_count)))
2385 /*
2386 * insertion is new and at delete position or
2387 * an extension to either left or right
2388 */
2389 es->undo_insert_count += strl;
2390 else {
2391 /* new insertion undo */
2392 es->undo_position = s;
2393 es->undo_insert_count = strl;
2394 /* new insertion makes old delete-buffer invalid */
2395 *es->undo_text = '\0';
2396 }
2397 } else
2398 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2399
2400
2401 /* now insert */
2402 tl = lstrlenA(es->text);
2403 for (p = es->text + tl ; p >= es->text + s ; p--)
2404 p[strl] = p[0];
2405 for (i = 0 , p = es->text + s ; i < strl ; i++)
2406 p[i] = lpsz_replace[i];
2407
2408 if (es->style & ES_OEMCONVERT)
2409 {
2410 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,strl);
2411
2412 CharToOemBuffA(lpsz_replace,text,strl);
2413 OemToCharBuffA(text,p,strl);
2414 HeapFree(es->heap,0,text);
2415 }
2416
2417 if(es->style & ES_UPPERCASE)
2418 CharUpperBuffA(p, strl);
2419 else if(es->style & ES_LOWERCASE)
2420 CharLowerBuffA(p, strl);
2421
2422 s += strl;
2423 }
2424 }
2425 /* FIXME: really inefficient */
2426 if (es->style & ES_MULTILINE)
2427 EDIT_BuildLineDefs_ML(hwnd,es);
2428
2429 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
2430
2431 es->flags |= EF_MODIFIED;
2432 es->flags |= EF_UPDATE;
2433 EDIT_EM_ScrollCaret(hwnd, es);
2434 EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
2435 /* FIXME: really inefficient */
2436 EDIT_Refresh(hwnd,es,TRUE);
2437}
2438
2439
2440/*********************************************************************
2441 *
2442 * EM_SCROLL
2443 *
2444 */
2445static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
2446{
2447 INT dy;
2448
2449 if (!(es->style & ES_MULTILINE))
2450 return (LRESULT)FALSE;
2451
2452 dy = 0;
2453
2454 switch (action) {
2455 case SB_LINEUP:
2456 if (es->y_offset)
2457 dy = -1;
2458 break;
2459 case SB_LINEDOWN:
2460 if (es->y_offset < es->line_count - 1)
2461 dy = 1;
2462 break;
2463 case SB_PAGEUP:
2464 if (es->y_offset)
2465 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2466 break;
2467 case SB_PAGEDOWN:
2468 if (es->y_offset < es->line_count - 1)
2469 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2470 break;
2471 default:
2472 return (LRESULT)FALSE;
2473 }
2474 if (dy) {
2475 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2476 EDIT_EM_LineScroll(hwnd, es, 0, dy);
2477 }
2478 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2479}
2480
2481
2482/*********************************************************************
2483 *
2484 * EM_SCROLLCARET
2485 *
2486 */
2487static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
2488{
2489 if (es->style & ES_MULTILINE) {
2490 INT l;
2491 INT li;
2492 INT vlc;
2493 INT ww;
2494 INT cw = es->char_width;
2495 INT x;
2496 INT dy = 0;
2497 INT dx = 0;
2498
2499 l = EDIT_EM_LineFromChar(hwnd, es, es->selection_end);
2500 li = EDIT_EM_LineIndex(hwnd, es, l);
2501 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2502 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2503 if (l >= es->y_offset + vlc)
2504 dy = l - vlc + 1 - es->y_offset;
2505 if (l < es->y_offset)
2506 dy = l - es->y_offset;
2507 ww = es->format_rect.right - es->format_rect.left;
2508 if (x < es->format_rect.left)
2509 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2510 if (x > es->format_rect.right)
2511 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2512 if (dy || dx)
2513 EDIT_EM_LineScroll(hwnd, es, dx, dy);
2514 } else {
2515 INT x;
2516 INT goal;
2517 INT format_width;
2518
2519 if (!(es->style & ES_AUTOHSCROLL))
2520 return;
2521
2522 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2523 format_width = es->format_rect.right - es->format_rect.left;
2524 if (x < es->format_rect.left)
2525 {
2526 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2527 do {
2528 es->x_offset--;
2529 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2530 } while ((x < goal) && es->x_offset);
2531 /* FIXME: use ScrollWindow() somehow to improve performance */
2532 EDIT_Refresh(hwnd,es,TRUE);
2533 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2534 } else if (x > es->format_rect.right)
2535 {
2536 INT x_last;
2537 INT len = lstrlenA(es->text);
2538 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2539 do {
2540 es->x_offset++;
2541 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2542 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, len, FALSE));
2543 } while ((x > goal) && (x_last > es->format_rect.right));
2544 /* FIXME: use ScrollWindow() somehow to improve performance */
2545 EDIT_Refresh(hwnd,es,TRUE);
2546 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2547 }
2548 }
2549}
2550
2551
2552/*********************************************************************
2553 *
2554 * EM_SETHANDLE
2555 *
2556 */
2557static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
2558{
2559 if (!(es->style & ES_MULTILINE))
2560 return;
2561
2562 if (!hloc) {
2563 //WARN_(edit)("called with NULL handle\n");
2564 return;
2565 }
2566
2567 EDIT_UnlockBuffer(hwnd, es, TRUE);
2568 /*
2569 * old buffer is freed by caller, unless
2570 * it is still in our own heap. (in that case
2571 * we free it, correcting the buggy caller.)
2572 */
2573 if (es->text)
2574 HeapFree(es->heap, 0, es->text);
2575
2576 es->hloc = hloc;
2577 es->text = NULL;
2578 es->buffer_size = LocalSize(es->hloc) - 1;
2579 EDIT_LockBuffer(hwnd, es);
2580
2581 if (es->text && (es->text[0] != 0))
2582 {
2583 if (es->style & ES_NUMBER)
2584 {
2585 //CB: todo
2586 }
2587
2588 if (es->style & ES_OEMCONVERT)
2589 {
2590 INT len = lstrlenA(es->text);
2591 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,len);
2592
2593 CharToOemBuffA(es->text,text,len);
2594 OemToCharBuffA(text,es->text,len);
2595 HeapFree(es->heap,0,text);
2596 }
2597
2598 if(es->style & ES_UPPERCASE)
2599 CharUpperA(es->text);
2600 else if(es->style & ES_LOWERCASE)
2601 CharLowerA(es->text);
2602 }
2603
2604 es->x_offset = es->y_offset = 0;
2605 es->selection_start = es->selection_end = 0;
2606 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2607 es->flags &= ~EF_MODIFIED;
2608 es->flags &= ~EF_UPDATE;
2609 EDIT_BuildLineDefs_ML(hwnd,es);
2610 EDIT_Refresh(hwnd,es,FALSE);
2611 EDIT_EM_ScrollCaret(hwnd, es);
2612 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
2613}
2614
2615/*********************************************************************
2616 *
2617 * EM_SETLIMITTEXT
2618 *
2619 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2620 * However, the windows version is not complied to yet in all of edit.c
2621 *
2622 */
2623static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit)
2624{
2625 if (es->style & ES_MULTILINE) {
2626 if (limit)
2627 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2628 else
2629 es->buffer_limit = BUFLIMIT_MULTI;
2630 } else {
2631 if (limit)
2632 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2633 else
2634 es->buffer_limit = BUFLIMIT_SINGLE;
2635 }
2636}
2637
2638
2639/*********************************************************************
2640 *
2641 * EM_SETMARGINS
2642 *
2643 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2644 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2645 * of the char's width, according to the new docs.
2646 *
2647 */
2648static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action,
2649 INT left, INT right)
2650{
2651 RECT r;
2652 INT oldLeft = es->left_margin,oldRight = es->right_margin;
2653
2654 if (action & EC_LEFTMARGIN) {
2655 if (left != EC_USEFONTINFO)
2656 es->left_margin = left;
2657 else
2658 es->left_margin = es->char_width / 3;
2659 }
2660
2661 if (action & EC_RIGHTMARGIN) {
2662 if (right != EC_USEFONTINFO)
2663 es->right_margin = right;
2664 else
2665 es->right_margin = es->char_width / 3;
2666 }
2667 //TRACE_(edit)("left=%d, right=%d\n", es->left_margin, es->right_margin);
2668
2669 if ((oldLeft != es->left_margin) || (oldRight != es->right_margin))
2670 {
2671 GetClientRect(hwnd, &r);
2672 EDIT_SetRectNP(hwnd, es, &r);
2673 if (es->style & ES_MULTILINE)
2674 EDIT_BuildLineDefs_ML(hwnd,es);
2675
2676 EDIT_Refresh(hwnd,es,FALSE);
2677 if (es->flags & EF_FOCUSED) {
2678 DestroyCaret();
2679 CreateCaret(hwnd, 0, 2, es->line_height);
2680 EDIT_SetCaretPos(hwnd, es, es->selection_end,
2681 es->flags & EF_AFTER_WRAP);
2682 ShowCaret(hwnd);
2683 }
2684 }
2685}
2686
2687static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified)
2688{
2689 if (fModified)
2690 es->flags |= EF_MODIFIED;
2691 else
2692 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
2693}
2694
2695/*********************************************************************
2696 *
2697 * EM_SETPASSWORDCHAR
2698 *
2699 */
2700static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c)
2701{
2702 if (es->style & ES_MULTILINE)
2703 return;
2704
2705 if (es->password_char == c)
2706 return;
2707
2708 es->password_char = c;
2709 if (c) {
2710 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_PASSWORD);
2711 es->style |= ES_PASSWORD;
2712 } else {
2713 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_PASSWORD);
2714 es->style &= ~ES_PASSWORD;
2715 }
2716 EDIT_Refresh(hwnd,es,FALSE);
2717}
2718
2719static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly)
2720{
2721 if (fReadOnly)
2722 {
2723 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_READONLY);
2724 es->style |= ES_READONLY;
2725 } else
2726 {
2727 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_READONLY);
2728 es->style &= ~ES_READONLY;
2729 }
2730
2731 return TRUE;
2732}
2733
2734static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2735{
2736 if ((es->style & ES_MULTILINE) && lprc)
2737 {
2738 EDIT_SetRectNP(hwnd,es,lprc);
2739 EDIT_Refresh(hwnd,es,FALSE);
2740 }
2741}
2742
2743static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2744{
2745 if ((es->style & ES_MULTILINE) && lprc)
2746 EDIT_SetRectNP(hwnd,es,lprc);
2747}
2748
2749/*********************************************************************
2750 *
2751 * EDIT_EM_SetSel
2752 *
2753 * note: unlike the specs say: the order of start and end
2754 * _is_ preserved in Windows. (i.e. start can be > end)
2755 * In other words: this handler is OK
2756 *
2757 */
2758static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2759{
2760 UINT old_start = es->selection_start;
2761 UINT old_end = es->selection_end;
2762 UINT len = lstrlenA(es->text);
2763
2764 if (start == -1) {
2765 start = es->selection_end;
2766 end = es->selection_end;
2767 } else {
2768 start = MIN(start, len);
2769 end = MIN(end, len);
2770 }
2771 es->selection_start = start;
2772 es->selection_end = end;
2773 if (after_wrap)
2774 es->flags |= EF_AFTER_WRAP;
2775 else
2776 es->flags &= ~EF_AFTER_WRAP;
2777 if (es->flags & EF_FOCUSED)
2778 EDIT_SetCaretPos(hwnd, es, end, after_wrap);
2779/* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2780 ORDER_UINT(start, end);
2781 ORDER_UINT(end, old_end);
2782 ORDER_UINT(start, old_start);
2783 ORDER_UINT(old_start, old_end);
2784 if ((start == old_start) && (end == old_end)) return;
2785 if (end != old_start)
2786 {
2787/*
2788 * One can also do
2789 * ORDER_UINT32(end, old_start);
2790 * EDIT_InvalidateText(wnd, es, start, end);
2791 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2792 * in place of the following if statement.
2793 */
2794 if (old_start > end )
2795 {
2796 EDIT_InvalidateText(hwnd, es, start, end);
2797 EDIT_InvalidateText(hwnd, es, old_start, old_end);
2798 }
2799 else
2800 {
2801 EDIT_InvalidateText(hwnd, es, start, old_start);
2802 EDIT_InvalidateText(hwnd, es, end, old_end);
2803 }
2804 }
2805 else EDIT_InvalidateText(hwnd, es, start, old_end);
2806}
2807
2808
2809/*********************************************************************
2810 *
2811 * EM_SETTABSTOPS
2812 *
2813 */
2814static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs)
2815{
2816 if (!(es->style & ES_MULTILINE))
2817 return FALSE;
2818 if (es->tabs)
2819 HeapFree(es->heap, 0, es->tabs);
2820 es->tabs_count = count;
2821 if (!count)
2822 es->tabs = NULL;
2823 else {
2824 es->tabs = (INT*)HeapAlloc(es->heap, 0, count * sizeof(INT));
2825 memcpy(es->tabs, tabs, count * sizeof(INT));
2826 }
2827 return TRUE;
2828}
2829
2830
2831/*********************************************************************
2832 *
2833 * EM_SETWORDBREAKPROC
2834 *
2835 */
2836static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2837{
2838 if (es->word_break_procA == wbp)
2839 return;
2840
2841 es->word_break_procA = wbp;
2842 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2843 EDIT_BuildLineDefs_ML(hwnd,es);
2844 EDIT_Refresh(hwnd,es,FALSE);
2845 }
2846}
2847
2848
2849/*********************************************************************
2850 *
2851 * EM_UNDO / WM_UNDO
2852 *
2853 */
2854static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
2855{
2856 INT ulength = lstrlenA(es->undo_text);
2857 LPSTR utext = (LPSTR)HeapAlloc(es->heap, 0, ulength + 1);
2858
2859 lstrcpyA(utext, es->undo_text);
2860
2861 //TRACE_(edit)("before UNDO:insertion length = %d, deletion buffer = %s\n",
2862 // es->undo_insert_count, utext);
2863
2864 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2865 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2866 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext);
2867 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2868 HeapFree(es->heap, 0, utext);
2869
2870 //TRACE_(edit)("after UNDO:insertion length = %d, deletion buffer = %s\n",
2871 // es->undo_insert_count, es->undo_text);
2872
2873 return TRUE;
2874}
2875
2876static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2877{
2878 if (wParam == EMSIS_COMPOSITIONSTRING)
2879 {
2880 //CB: todo
2881 }
2882
2883 return 0;
2884}
2885
2886static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2887{
2888 //CB: todo
2889
2890 return 0; //default
2891}
2892
2893/*********************************************************************
2894 *
2895 * WM_CHAR
2896 *
2897 */
2898static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data)
2899{
2900 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
2901 switch (c) {
2902 case '\r':
2903 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
2904 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
2905 {
2906 MessageBeep(MB_ICONEXCLAMATION);
2907 break;
2908 }
2909 case '\n':
2910 if (es->style & ES_MULTILINE) {
2911 if (es->style & ES_READONLY) {
2912 EDIT_MoveHome(hwnd, es, FALSE);
2913 EDIT_MoveDown_ML(hwnd, es, FALSE);
2914 } else
2915 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\r\n");
2916 }
2917 break;
2918 case '\t':
2919 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2920 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\t");
2921 break;
2922 case VK_BACK:
2923 if (!(es->style & ES_READONLY) && !control) {
2924 if (es->selection_start != es->selection_end)
2925 EDIT_WM_Clear(hwnd, es);
2926 else {
2927 /* delete character left of caret */
2928 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
2929 EDIT_MoveBackward(hwnd, es, TRUE);
2930 EDIT_WM_Clear(hwnd, es);
2931 }
2932 }
2933 break;
2934 default:
2935 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127))
2936 {
2937 char str[2];
2938
2939 if (es->style & ES_NUMBER)
2940 {
2941 if (((BYTE)c < '0') || ((BYTE)c > '9')) MessageBeep(MB_ICONEXCLAMATION);
2942 return;
2943 }
2944 str[0] = c;
2945 str[1] = '\0';
2946 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str);
2947 } else MessageBeep(MB_ICONEXCLAMATION);
2948 break;
2949 }
2950}
2951
2952
2953/*********************************************************************
2954 *
2955 * WM_COMMAND
2956 *
2957 */
2958static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
2959{
2960 if (code || control)
2961 return;
2962
2963 switch (id) {
2964 case EM_UNDO:
2965 EDIT_EM_Undo(hwnd, es);
2966 break;
2967 case WM_CUT:
2968 EDIT_WM_Cut(hwnd, es);
2969 break;
2970 case WM_COPY:
2971 EDIT_WM_Copy(hwnd, es);
2972 break;
2973 case WM_PASTE:
2974 EDIT_WM_Paste(hwnd, es);
2975 break;
2976 case WM_CLEAR:
2977 EDIT_WM_Clear(hwnd, es);
2978 break;
2979 case EM_SETSEL:
2980 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
2981 EDIT_EM_ScrollCaret(hwnd, es);
2982 break;
2983 default:
2984 //ERR_(edit)("unknown menu item, please report\n");
2985 break;
2986 }
2987}
2988
2989
2990/*********************************************************************
2991 *
2992 * WM_CONTEXTMENU
2993 *
2994 * Note: the resource files resource/sysres_??.rc cannot define a
2995 * single popup menu. Hence we use a (dummy) menubar
2996 * containing the single popup menu as its first item.
2997 *
2998 * FIXME: the message identifiers have been chosen arbitrarily,
2999 * hence we use MF_BYPOSITION.
3000 * We might as well use the "real" values (anybody knows ?)
3001 * The menu definition is in resources/sysres_??.rc.
3002 * Once these are OK, we better use MF_BYCOMMAND here
3003 * (as we do in EDIT_WM_Command()).
3004 *
3005 */
3006static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y)
3007{
3008 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3009 HMENU popup = GetSubMenu(menu, 0);
3010 UINT start = es->selection_start;
3011 UINT end = es->selection_end;
3012
3013 ORDER_UINT(start, end);
3014
3015 /* undo */
3016 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(hwnd, es) ? MF_ENABLED : MF_GRAYED));
3017 /* cut */
3018 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & (ES_PASSWORD | ES_READONLY)) ? MF_ENABLED : MF_GRAYED));
3019 /* copy */
3020 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3021 /* paste */
3022 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3023 /* delete */
3024 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3025 /* select all */
3026 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
3027
3028 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
3029 DestroyMenu(menu);
3030}
3031
3032
3033/*********************************************************************
3034 *
3035 * WM_COPY
3036 *
3037 */
3038static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
3039{
3040 INT s = es->selection_start;
3041 INT e = es->selection_end;
3042 HGLOBAL hdst;
3043 LPSTR dst;
3044
3045 if (e == s)
3046 return;
3047 ORDER_INT(s, e);
3048 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3049 dst = (LPSTR)GlobalLock(hdst);
3050 lstrcpynA(dst, es->text + s, e - s + 1);
3051 GlobalUnlock(hdst);
3052 OpenClipboard(hwnd);
3053 EmptyClipboard();
3054 SetClipboardData(CF_TEXT, hdst);
3055 CloseClipboard();
3056}
3057
3058
3059/*********************************************************************
3060 *
3061 * WM_CREATE
3062 *
3063 */
3064static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3065{
3066 /*
3067 * To initialize some final structure members, we call some helper
3068 * functions. However, since the EDITSTATE is not consistent (i.e.
3069 * not fully initialized), we should be very careful which
3070 * functions can be called, and in what order.
3071 */
3072 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
3073 EDIT_EM_EmptyUndoBuffer(hwnd, es);
3074 if (cs->lpszName && *(cs->lpszName) != '\0') {
3075 EDIT_EM_ReplaceSel(hwnd, es, FALSE, cs->lpszName);
3076 /* if we insert text to the editline, the text scrolls out
3077 * of the window, as the caret is placed after the insert
3078 * pos normally; thus we reset es->selection... to 0 and
3079 * update caret
3080 */
3081 es->selection_start = es->selection_end = 0;
3082 EDIT_EM_ScrollCaret(hwnd, es);
3083 }
3084 return 0;
3085}
3086
3087
3088/*********************************************************************
3089 *
3090 * WM_DESTROY
3091 *
3092 */
3093static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
3094{
3095 if (es->hloc) {
3096 while (LocalUnlock(es->hloc)) ;
3097 LocalFree(es->hloc);
3098 }
3099
3100 HeapDestroy(es->heap);
3101 HeapFree(GetProcessHeap(), 0, es);
3102 SetInfoPtr(hwnd,0);
3103}
3104
3105
3106/*********************************************************************
3107 *
3108 * WM_ERASEBKGND
3109 *
3110 */
3111static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
3112{
3113 HBRUSH brush;
3114 RECT rc;
3115
3116 HideCaret(hwnd);
3117
3118 if (!es->bEnableState || (es->style & ES_READONLY))
3119 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
3120 else
3121 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
3122
3123 if (!brush)
3124 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3125
3126 GetClientRect(hwnd, &rc);
3127 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3128 GetClipBox(dc, &rc);
3129 /*
3130 * FIXME: specs say that we should UnrealizeObject() the brush,
3131 * but the specs of UnrealizeObject() say that we shouldn't
3132 * unrealize a stock object. The default brush that
3133 * DefWndProc() returns is ... a stock object.
3134 */
3135 FillRect(dc, &rc, brush);
3136
3137 ShowCaret(hwnd);
3138
3139 return -1;
3140}
3141
3142
3143/*********************************************************************
3144 *
3145 * WM_GETTEXT
3146 *
3147 */
3148static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text)
3149{
3150 INT len;
3151
3152 if (es->text == NULL) // the only case of failure i can imagine
3153 return 0;
3154
3155 len = min(count, lstrlenA(es->text)+1); // determine length
3156 lstrcpynA(text, es->text, len); // copy as much as possible
3157 return len;
3158}
3159
3160
3161/*********************************************************************
3162 *
3163 * EDIT_HScroll_Hack
3164 *
3165 * 16 bit notepad needs this. Actually it is not _our_ hack,
3166 * it is notepad's. Notepad is sending us scrollbar messages with
3167 * undocumented parameters without us even having a scrollbar ... !?!?
3168 *
3169 */
3170static LRESULT EDIT_HScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3171{
3172 INT dx = 0;
3173 INT fw = es->format_rect.right - es->format_rect.left;
3174 LRESULT ret = 0;
3175
3176 if (!(es->flags & EF_HSCROLL_HACK)) {
3177 //ERR_(edit)("hacked WM_HSCROLL handler invoked\n");
3178 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
3179 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
3180 es->flags |= EF_HSCROLL_HACK;
3181 }
3182
3183 switch (action) {
3184 case SB_LINELEFT:
3185 if (es->x_offset)
3186 dx = -es->char_width;
3187 break;
3188 case SB_LINERIGHT:
3189 if (es->x_offset < es->text_width)
3190 dx = es->char_width;
3191 break;
3192 case SB_PAGELEFT:
3193 if (es->x_offset)
3194 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3195 break;
3196 case SB_PAGERIGHT:
3197 if (es->x_offset < es->text_width)
3198 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3199 break;
3200 case SB_LEFT:
3201 if (es->x_offset)
3202 dx = -es->x_offset;
3203 break;
3204 case SB_RIGHT:
3205 if (es->x_offset < es->text_width)
3206 dx = es->text_width - es->x_offset;
3207 break;
3208 case SB_THUMBTRACK:
3209 es->flags |= EF_HSCROLL_TRACK;
3210 dx = pos * es->text_width / 100 - es->x_offset;
3211 break;
3212 case SB_THUMBPOSITION:
3213 es->flags &= ~EF_HSCROLL_TRACK;
3214 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3215 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3216 break;
3217 case SB_ENDSCROLL:
3218 break;
3219
3220 default:
3221 //ERR_(edit)("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3222 return 0;
3223 }
3224 if (dx)
3225 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3226 return ret;
3227}
3228
3229
3230/*********************************************************************
3231 *
3232 * WM_HSCROLL
3233 *
3234 */
3235static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3236{
3237 INT dx;
3238 INT fw;
3239
3240 if (!(es->style & ES_MULTILINE))
3241 return 0;
3242
3243 if (!(es->style & ES_AUTOHSCROLL))
3244 return 0;
3245
3246 if (!(es->style & WS_HSCROLL))
3247 return EDIT_HScroll_Hack(hwnd, es, action, pos, scroll_bar);
3248
3249 dx = 0;
3250 fw = es->format_rect.right - es->format_rect.left;
3251 switch (action) {
3252 case SB_LINELEFT:
3253 if (es->x_offset)
3254 dx = -es->char_width;
3255 break;
3256 case SB_LINERIGHT:
3257 if (es->x_offset < es->text_width)
3258 dx = es->char_width;
3259 break;
3260 case SB_PAGELEFT:
3261 if (es->x_offset)
3262 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3263 break;
3264 case SB_PAGERIGHT:
3265 if (es->x_offset < es->text_width)
3266 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3267 break;
3268 case SB_LEFT:
3269 if (es->x_offset)
3270 dx = -es->x_offset;
3271 break;
3272 case SB_RIGHT:
3273 if (es->x_offset < es->text_width)
3274 dx = es->text_width - es->x_offset;
3275 break;
3276 case SB_THUMBTRACK:
3277 es->flags |= EF_HSCROLL_TRACK;
3278 dx = pos - es->x_offset;
3279 break;
3280 case SB_THUMBPOSITION:
3281 es->flags &= ~EF_HSCROLL_TRACK;
3282 if (!(dx = pos - es->x_offset)) {
3283 SetScrollPos(hwnd, SB_HORZ, pos, TRUE);
3284 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3285 }
3286 break;
3287 case SB_ENDSCROLL:
3288 break;
3289
3290 default:
3291 //ERR_(edit)("undocumented WM_HSCROLL parameter, please report\n");
3292 return 0;
3293 }
3294 if (dx)
3295 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3296 return 0;
3297}
3298
3299
3300/*********************************************************************
3301 *
3302 * EDIT_CheckCombo
3303 *
3304 */
3305static BOOL EDIT_CheckCombo(HWND hwnd, UINT msg, INT key, DWORD key_data)
3306{
3307 HWND hLBox;
3308
3309 if (GetClassWord(GetParent(hwnd),GCW_ATOM) == GlobalFindAtomA(COMBOBOXCLASSNAME) &&
3310 (hLBox = COMBO_GetLBWindow(GetParent(hwnd)))) {
3311 HWND hCombo = GetParent(hwnd);
3312 BOOL bUIFlip = TRUE;
3313
3314 //TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3315 // wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3316
3317 switch (msg) {
3318 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3319 if (key != VK_F4) {
3320 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3321 if (SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0))
3322 bUIFlip = FALSE;
3323 }
3324 if (!bUIFlip)
3325 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3326 else {
3327 /* make sure ComboLBox pops up */
3328 SendMessageA(hCombo, CB_SETEXTENDEDUI, 0, 0);
3329 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3330 SendMessageA(hCombo, CB_SETEXTENDEDUI, 1, 0);
3331 }
3332 break;
3333 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3334 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3335 if (bUIFlip) {
3336 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3337 SendMessageA(hCombo, CB_SHOWDROPDOWN, (bUIFlip) ? FALSE : TRUE, 0);
3338 } else
3339 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3340 break;
3341 }
3342 return TRUE;
3343 }
3344 return FALSE;
3345}
3346
3347
3348/*********************************************************************
3349 *
3350 * WM_KEYDOWN
3351 *
3352 * Handling of special keys that don't produce a WM_CHAR
3353 * (i.e. non-printable keys) & Backspace & Delete
3354 *
3355 */
3356static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3357{
3358 BOOL shift;
3359 BOOL control;
3360
3361 if (GetKeyState(VK_MENU) & 0x8000)
3362 return 0;
3363
3364 shift = GetKeyState(VK_SHIFT) & 0x8000;
3365 control = GetKeyState(VK_CONTROL) & 0x8000;
3366
3367 switch (key) {
3368 case VK_F4:
3369 case VK_UP:
3370 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3371 break;
3372 if (key == VK_F4)
3373 break;
3374 /* fall through */
3375 case VK_LEFT:
3376 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3377 EDIT_MoveUp_ML(hwnd, es, shift);
3378 else
3379 if (control)
3380 EDIT_MoveWordBackward(hwnd, es, shift);
3381 else
3382 EDIT_MoveBackward(hwnd, es, shift);
3383 break;
3384 case VK_DOWN:
3385 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3386 break;
3387 /* fall through */
3388 case VK_RIGHT:
3389 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3390 EDIT_MoveDown_ML(hwnd, es, shift);
3391 else if (control)
3392 EDIT_MoveWordForward(hwnd, es, shift);
3393 else
3394 EDIT_MoveForward(hwnd, es, shift);
3395 break;
3396 case VK_HOME:
3397 EDIT_MoveHome(hwnd, es, shift);
3398 break;
3399 case VK_END:
3400 EDIT_MoveEnd(hwnd, es, shift);
3401 break;
3402 case VK_PRIOR:
3403 if (es->style & ES_MULTILINE)
3404 EDIT_MovePageUp_ML(hwnd, es, shift);
3405 break;
3406 case VK_NEXT:
3407 if (es->style & ES_MULTILINE)
3408 EDIT_MovePageDown_ML(hwnd, es, shift);
3409 break;
3410 case VK_DELETE:
3411 if (!(es->style & ES_READONLY) && !(shift && control)) {
3412 if (es->selection_start != es->selection_end) {
3413 if (shift)
3414 EDIT_WM_Cut(hwnd, es);
3415 else
3416 EDIT_WM_Clear(hwnd, es);
3417 } else {
3418 if (shift) {
3419 /* delete character left of caret */
3420 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3421 EDIT_MoveBackward(hwnd, es, TRUE);
3422 EDIT_WM_Clear(hwnd, es);
3423 } else if (control) {
3424 /* delete to end of line */
3425 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3426 EDIT_MoveEnd(hwnd, es, TRUE);
3427 EDIT_WM_Clear(hwnd, es);
3428 } else {
3429 /* delete character right of caret */
3430 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3431 EDIT_MoveForward(hwnd, es, TRUE);
3432 EDIT_WM_Clear(hwnd, es);
3433 }
3434 }
3435 }
3436 break;
3437 case VK_INSERT:
3438 if (shift) {
3439 if (!(es->style & ES_READONLY))
3440 EDIT_WM_Paste(hwnd, es);
3441 } else if (control)
3442 EDIT_WM_Copy(hwnd, es);
3443 break;
3444 case VK_RETURN:
3445 /* If the edit doesn't want the return send a message to the default object */
3446 if(!(es->style & ES_WANTRETURN))
3447 {
3448 HWND hwndParent = GetParent(hwnd);
3449 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
3450 if (HIWORD(dw) == DC_HASDEFID)
3451 {
3452 SendMessageA( hwndParent, WM_COMMAND,
3453 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3454 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3455 }
3456 }
3457 break;
3458 }
3459 return 0;
3460}
3461
3462
3463/*********************************************************************
3464 *
3465 * WM_KILLFOCUS
3466 *
3467 */
3468static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus)
3469{
3470 es->flags &= ~EF_FOCUSED;
3471 DestroyCaret();
3472 if(!(es->style & ES_NOHIDESEL))
3473 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3474 EDIT_NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
3475 return 0;
3476}
3477
3478
3479/*********************************************************************
3480 *
3481 * WM_LBUTTONDBLCLK
3482 *
3483 * The caret position has been set on the WM_LBUTTONDOWN message
3484 *
3485 */
3486static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3487{
3488 INT s;
3489 INT e = es->selection_end;
3490 INT l;
3491 INT li;
3492 INT ll;
3493
3494 if (!(es->flags & EF_FOCUSED))
3495 return 0;
3496
3497 l = EDIT_EM_LineFromChar(hwnd, es, e);
3498 li = EDIT_EM_LineIndex(hwnd, es, l);
3499 ll = EDIT_EM_LineLength(hwnd, es, e);
3500 s = li + EDIT_CallWordBreakProc (hwnd, es, li, e - li, ll, WB_LEFT);
3501 e = li + EDIT_CallWordBreakProc(hwnd, es, li, e - li, ll, WB_RIGHT);
3502 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
3503 EDIT_EM_ScrollCaret(hwnd, es);
3504 return 0;
3505}
3506
3507
3508/*********************************************************************
3509 *
3510 * WM_LBUTTONDOWN
3511 *
3512 */
3513static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3514{
3515 INT e;
3516 BOOL after_wrap;
3517
3518 if (!(es->flags & EF_FOCUSED))
3519 return 0;
3520
3521 es->bCaptureState = TRUE;
3522 SetCapture(hwnd);
3523 EDIT_ConfinePoint(hwnd, es, &x, &y);
3524 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3525 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3526 EDIT_EM_ScrollCaret(hwnd, es);
3527 es->region_posx = es->region_posy = 0;
3528 SetTimer(hwnd, 0, 100, NULL);
3529 return 0;
3530}
3531
3532
3533/*********************************************************************
3534 *
3535 * WM_LBUTTONUP
3536 *
3537 */
3538static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3539{
3540 if (es->bCaptureState)
3541 {
3542 KillTimer(hwnd,0);
3543 ReleaseCapture();
3544 }
3545 es->bCaptureState = FALSE;
3546
3547 return 0;
3548}
3549
3550static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es)
3551{
3552 if (es->bCaptureState) KillTimer(hwnd,0);
3553 es->bCaptureState = FALSE;
3554
3555 return 0;
3556}
3557
3558/*********************************************************************
3559 *
3560 * WM_MOUSEMOVE
3561 *
3562 */
3563static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3564{
3565 INT e;
3566 BOOL after_wrap;
3567 INT prex, prey;
3568
3569 if (!es->bCaptureState) return 0;
3570
3571 /*
3572 * FIXME: gotta do some scrolling if outside client
3573 * area. Maybe reset the timer ?
3574 */
3575 prex = x; prey = y;
3576 EDIT_ConfinePoint(hwnd, es, &x, &y);
3577 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3578 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3579 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3580 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
3581
3582 return 0;
3583}
3584
3585
3586/*********************************************************************
3587 *
3588 * WM_NCCREATE
3589 *
3590 */
3591static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs)
3592{
3593 EDITSTATE *es;
3594
3595 if (!(es = (EDITSTATE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3596 return FALSE;
3597 SetInfoPtr(hwnd,(DWORD)es);
3598
3599 /*
3600 * Note: since the EDITSTATE has not been fully initialized yet,
3601 * we can't use any API calls that may send
3602 * WM_XXX messages before WM_NCCREATE is completed.
3603 */
3604
3605 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3606 return FALSE;
3607 es->style = cs->style;
3608
3609 es->bEnableState = !(cs->style & WS_DISABLED);
3610
3611 /*
3612 * In Win95 look and feel, the WS_BORDER style is replaced by the
3613 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3614 * control a non client area.
3615 */
3616 if (es->style & WS_BORDER)
3617 {
3618 es->style &= ~WS_BORDER;
3619 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~WS_BORDER);
3620 SetWindowLongA(hwnd,GWL_EXSTYLE,GetWindowLongA(hwnd,GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3621 }
3622
3623 if (es->style & ES_MULTILINE) {
3624 es->buffer_size = BUFSTART_MULTI;
3625 es->buffer_limit = BUFLIMIT_MULTI;
3626 if (es->style & WS_VSCROLL)
3627 es->style |= ES_AUTOVSCROLL;
3628 if (es->style & WS_HSCROLL)
3629 es->style |= ES_AUTOHSCROLL;
3630 es->style &= ~ES_PASSWORD;
3631 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3632 if (es->style & ES_RIGHT)
3633 es->style &= ~ES_CENTER;
3634 es->style &= ~WS_HSCROLL;
3635 es->style &= ~ES_AUTOHSCROLL;
3636 }
3637
3638 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3639 es->style |= ES_AUTOVSCROLL;
3640 } else {
3641 es->buffer_size = BUFSTART_SINGLE;
3642 es->buffer_limit = BUFLIMIT_SINGLE;
3643 es->style &= ~ES_CENTER;
3644 es->style &= ~ES_RIGHT;
3645 es->style &= ~WS_HSCROLL;
3646 es->style &= ~WS_VSCROLL;
3647 es->style &= ~ES_AUTOVSCROLL;
3648 es->style &= ~ES_WANTRETURN;
3649 if (es->style & ES_UPPERCASE) {
3650 es->style &= ~ES_LOWERCASE;
3651 es->style &= ~ES_NUMBER;
3652 } else if (es->style & ES_LOWERCASE)
3653 es->style &= ~ES_NUMBER;
3654 if (es->style & ES_PASSWORD)
3655 es->password_char = '*';
3656
3657 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3658 es->style |= ES_AUTOHSCROLL;
3659 }
3660 if (!(es->text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3661 return FALSE;
3662 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3663 if (!(es->undo_text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3664 return FALSE;
3665 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3666 *es->text = '\0';
3667 if (es->style & ES_MULTILINE)
3668 if (!(es->first_line_def = (LINEDEF*)HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3669 return FALSE;
3670 es->line_count = 1;
3671
3672 return TRUE;
3673}
3674
3675static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc,BOOL eraseBkGnd)
3676{
3677 INT i;
3678 HFONT old_font = 0;
3679 RECT rc;
3680 RECT rcLine;
3681 RECT rcRgn;
3682 BOOL rev = es->bEnableState && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL));
3683
3684 HideCaret(hwnd);
3685
3686 if (eraseBkGnd)
3687 {
3688 HBRUSH brush;
3689
3690 if (!es->bEnableState || (es->style & ES_READONLY))
3691 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3692 else
3693 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, hdc);
3694
3695 if (!brush)
3696 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3697
3698 GetClientRect(hwnd, &rc);
3699 /*
3700 * FIXME: specs say that we should UnrealizeObject() the brush,
3701 * but the specs of UnrealizeObject() say that we shouldn't
3702 * unrealize a stock object. The default brush that
3703 * DefWndProc() returns is ... a stock object.
3704 */
3705 FillRect(hdc, &rc, brush);
3706 }
3707
3708 if(es->style & WS_BORDER)
3709 {
3710 GetClientRect(hwnd, &rc);
3711 if(es->style & ES_MULTILINE)
3712 {
3713 if(es->style & WS_HSCROLL) rc.bottom++;
3714 if(es->style & WS_VSCROLL) rc.right++;
3715 }
3716 Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
3717 }
3718
3719 IntersectClipRect(hdc, es->format_rect.left,
3720 es->format_rect.top,
3721 es->format_rect.right,
3722 es->format_rect.bottom);
3723 if (es->style & ES_MULTILINE)
3724 {
3725 GetClientRect(hwnd, &rc);
3726 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
3727 }
3728 if (es->font) old_font = SelectObject(hdc, es->font);
3729 if (!es->bEnableState || (es->style & ES_READONLY))
3730 EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3731 else
3732 EDIT_SEND_CTLCOLOR(hwnd, hdc);
3733 if (!es->bEnableState)
3734 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
3735 GetClipBox(hdc, &rcRgn);
3736 if (es->style & ES_MULTILINE)
3737 {
3738 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3739
3740 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++)
3741 {
3742 EDIT_GetLineRect(hwnd,hdc, es, i, 0, -1, &rcLine);
3743 if (IntersectRect(&rc, &rcRgn, &rcLine))
3744 EDIT_PaintLine(hwnd, es, hdc, i, rev);
3745 }
3746 } else
3747 {
3748 EDIT_GetLineRect(hwnd,hdc, es, 0, 0, -1, &rcLine);
3749 if (IntersectRect(&rc, &rcRgn, &rcLine))
3750 EDIT_PaintLine(hwnd, es, hdc, 0, rev);
3751 }
3752 if (es->font) SelectObject(hdc, old_font);
3753 if (es->flags & EF_FOCUSED)
3754 EDIT_SetCaretPos(hwnd, es, es->selection_end,es->flags & EF_AFTER_WRAP);
3755
3756 ShowCaret(hwnd);
3757
3758 if (es->flags & EF_UPDATE)
3759 {
3760 es->flags &= ~EF_UPDATE;
3761 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
3762 }
3763}
3764
3765static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache)
3766{
3767 HDC hdc,hdcCompatible;
3768 HBITMAP bitmap,oldbmp;
3769 RECT rect;
3770
3771 if (!IsWindowVisible(hwnd)) return;
3772
3773 if (!useCache)
3774 {
3775 InvalidateRect(hwnd,NULL,TRUE);
3776 return;
3777 }
3778
3779 //CB: original control redraws many times, cache drawing
3780 HideCaret(hwnd);
3781 GetClientRect(hwnd,&rect);
3782 hdc = GetDC(hwnd);
3783 hdcCompatible = CreateCompatibleDC(hdc);
3784 bitmap = CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
3785 oldbmp = SelectObject(hdcCompatible,bitmap);
3786 EDIT_Draw(hwnd,es,hdcCompatible,TRUE);
3787 SelectClipRgn(hdcCompatible,0);
3788 BitBlt(hdc,0,0,rect.right,rect.bottom,hdcCompatible,0,0,SRCCOPY);
3789 SelectObject(hdcCompatible,oldbmp);
3790 DeleteObject(bitmap);
3791 DeleteDC(hdcCompatible);
3792 ReleaseDC(hwnd,hdc);
3793 ShowCaret(hwnd);
3794}
3795
3796/*********************************************************************
3797 *
3798 * WM_PAINT
3799 *
3800 */
3801static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam)
3802{
3803 PAINTSTRUCT ps;
3804 HDC hdc;
3805
3806 if (!wParam) hdc = BeginPaint(hwnd, &ps);
3807 else hdc = (HDC) wParam;
3808
3809 EDIT_Draw(hwnd,es,hdc,FALSE);
3810
3811 if (!wParam) EndPaint(hwnd, &ps);
3812}
3813
3814
3815/*********************************************************************
3816 *
3817 * WM_PASTE
3818 *
3819 */
3820static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
3821{
3822 HGLOBAL hsrc;
3823 LPSTR src;
3824
3825 OpenClipboard(hwnd);
3826 hsrc = GetClipboardData(CF_TEXT);
3827 if (hsrc) {
3828 src = (LPSTR)GlobalLock(hsrc);
3829 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src);
3830 GlobalUnlock(hsrc);
3831 }
3832 CloseClipboard();
3833}
3834
3835
3836/*********************************************************************
3837 *
3838 * WM_SETFOCUS
3839 *
3840 */
3841static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus)
3842{
3843 es->flags |= EF_FOCUSED;
3844 CreateCaret(hwnd, 0, 2, es->line_height);
3845 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3846 es->flags & EF_AFTER_WRAP);
3847 if(!(es->style & ES_NOHIDESEL))
3848 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3849 ShowCaret(hwnd);
3850 EDIT_NOTIFY_PARENT(hwnd, EN_SETFOCUS);
3851}
3852
3853/*********************************************************************
3854 *
3855 * WM_SETFONT
3856 *
3857 * With Win95 look the margins are set to default font value unless
3858 * the system font (font == 0) is being set, in which case they are left
3859 * unchanged.
3860 *
3861 */
3862static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
3863{
3864 TEXTMETRICA tm;
3865 HDC dc;
3866 HFONT old_font = 0;
3867 RECT r;
3868
3869 dc = GetDC(hwnd);
3870 if (font)
3871 old_font = SelectObject(dc, font);
3872 if (!GetTextMetricsA(dc, &tm))
3873 {
3874 if (font) SelectObject(dc,old_font);
3875 ReleaseDC(hwnd,dc);
3876
3877 return;
3878 }
3879 es->font = font;
3880 es->line_height = tm.tmHeight;
3881
3882 es->char_width = tm.tmAveCharWidth;
3883 if (font)
3884 SelectObject(dc, old_font);
3885 ReleaseDC(hwnd, dc);
3886 if (font)
3887 EDIT_EM_SetMargins(hwnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3888 EC_USEFONTINFO, EC_USEFONTINFO);
3889 /* Force the recalculation of the format rect for each font change */
3890 GetClientRect(hwnd, &r);
3891 EDIT_SetRectNP(hwnd, es, &r);
3892 if (es->style & ES_MULTILINE)
3893 EDIT_BuildLineDefs_ML(hwnd,es);
3894
3895 if (redraw)
3896 EDIT_Refresh(hwnd,es,FALSE);
3897 if (es->flags & EF_FOCUSED) {
3898 DestroyCaret();
3899 CreateCaret(hwnd, 0, 2, es->line_height);
3900 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3901 es->flags & EF_AFTER_WRAP);
3902 ShowCaret(hwnd);
3903 }
3904}
3905
3906
3907/*********************************************************************
3908 *
3909 * WM_SETTEXT
3910 *
3911 * NOTES
3912 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3913 * The modified flag is reset. No notifications are sent.
3914 *
3915 * For single-line controls, reception of WM_SETTEXT triggers:
3916 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3917 *
3918 */
3919static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text)
3920{
3921 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
3922 if (text) {
3923 //TRACE_(edit)("\t'%s'\n", text);
3924 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text);
3925 } else {
3926 //TRACE_(edit)("\t<NULL>\n");
3927 EDIT_EM_ReplaceSel(hwnd, es, FALSE, "");
3928 }
3929 es->x_offset = 0;
3930 if (es->style & ES_MULTILINE) {
3931 es->flags &= ~EF_UPDATE;
3932 } else {
3933 es->flags |= EF_UPDATE;
3934 }
3935 es->flags &= ~EF_MODIFIED;
3936 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
3937 EDIT_EM_ScrollCaret(hwnd, es);
3938 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
3939 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
3940}
3941
3942
3943/*********************************************************************
3944 *
3945 * WM_SIZE
3946 *
3947 */
3948static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
3949{
3950 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3951 RECT rc;
3952 SetRect(&rc, 0, 0, width, height);
3953 EDIT_SetRectNP(hwnd, es, &rc);
3954 EDIT_Refresh(hwnd,es,FALSE);
3955 }
3956}
3957
3958
3959/*********************************************************************
3960 *
3961 * WM_SYSKEYDOWN
3962 *
3963 */
3964static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3965{
3966 if ((key == VK_BACK) && (key_data & 0x2000)) {
3967 if (EDIT_EM_CanUndo(hwnd, es))
3968 EDIT_EM_Undo(hwnd, es);
3969 return 0;
3970 } else if ((key == VK_UP) || (key == VK_DOWN))
3971 if (EDIT_CheckCombo(hwnd, WM_SYSKEYDOWN, key, key_data))
3972 return 0;
3973 return DefWindowProcA(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
3974}
3975
3976
3977/*********************************************************************
3978 *
3979 * WM_TIMER
3980 *
3981 */
3982static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
3983{
3984 if (es->region_posx < 0) {
3985 EDIT_MoveBackward(hwnd, es, TRUE);
3986 } else if (es->region_posx > 0) {
3987 EDIT_MoveForward(hwnd, es, TRUE);
3988 }
3989
3990 if (!(es->style & ES_MULTILINE)) return;
3991
3992 if (es->region_posy < 0)
3993 {
3994 EDIT_MoveUp_ML(hwnd,es,TRUE);
3995 } else if (es->region_posy > 0)
3996 {
3997 EDIT_MoveDown_ML(hwnd,es,TRUE);
3998 }
3999}
4000
4001
4002/*********************************************************************
4003 *
4004 * EDIT_VScroll_Hack
4005 *
4006 * 16 bit notepad needs this. Actually it is not _our_ hack,
4007 * it is notepad's. Notepad is sending us scrollbar messages with
4008 * undocumented parameters without us even having a scrollbar ... !?!?
4009 *
4010 */
4011static LRESULT EDIT_VScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4012{
4013 INT dy = 0;
4014 LRESULT ret = 0;
4015
4016 if (!(es->flags & EF_VSCROLL_HACK)) {
4017 //ERR_(edit)("hacked WM_VSCROLL handler invoked\n");
4018 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
4019 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
4020 es->flags |= EF_VSCROLL_HACK;
4021 }
4022
4023 switch (action) {
4024 case SB_LINEUP:
4025 case SB_LINEDOWN:
4026 case SB_PAGEUP:
4027 case SB_PAGEDOWN:
4028 EDIT_EM_Scroll(hwnd, es, action);
4029 return 0;
4030 case SB_TOP:
4031 dy = -es->y_offset;
4032 break;
4033 case SB_BOTTOM:
4034 dy = es->line_count - 1 - es->y_offset;
4035 break;
4036 case SB_THUMBTRACK:
4037 es->flags |= EF_VSCROLL_TRACK;
4038 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4039 break;
4040 case SB_THUMBPOSITION:
4041 es->flags &= ~EF_VSCROLL_TRACK;
4042 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4043 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
4044 break;
4045 case SB_ENDSCROLL:
4046 break;
4047
4048 default:
4049 //ERR_(edit)("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4050 return 0;
4051 }
4052 if (dy)
4053 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4054 return ret;
4055}
4056
4057
4058/*********************************************************************
4059 *
4060 * WM_VSCROLL
4061 *
4062 */
4063static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4064{
4065 INT dy;
4066
4067 if (!(es->style & ES_MULTILINE))
4068 return 0;
4069
4070 if (!(es->style & ES_AUTOVSCROLL))
4071 return 0;
4072
4073 if (!(es->style & WS_VSCROLL))
4074 return EDIT_VScroll_Hack(hwnd, es, action, pos, scroll_bar);
4075
4076 dy = 0;
4077 switch (action) {
4078 case SB_LINEUP:
4079 case SB_LINEDOWN:
4080 case SB_PAGEUP:
4081 case SB_PAGEDOWN:
4082 EDIT_EM_Scroll(hwnd, es, action);
4083 return 0;
4084
4085 case SB_TOP:
4086 dy = -es->y_offset;
4087 break;
4088 case SB_BOTTOM:
4089 dy = es->line_count - 1 - es->y_offset;
4090 break;
4091 case SB_THUMBTRACK:
4092 es->flags |= EF_VSCROLL_TRACK;
4093 dy = pos - es->y_offset;
4094 break;
4095 case SB_THUMBPOSITION:
4096 es->flags &= ~EF_VSCROLL_TRACK;
4097 if (!(dy = pos - es->y_offset)) {
4098 SetScrollPos(hwnd, SB_VERT, pos, TRUE);
4099 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
4100 }
4101 break;
4102 case SB_ENDSCROLL:
4103 break;
4104
4105 default:
4106 //ERR_(edit)("undocumented WM_VSCROLL action %d, please report\n",
4107 // action);
4108 return 0;
4109 }
4110 if (dy)
4111 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4112 return 0;
4113}
4114
4115BOOL EDIT_Register()
4116{
4117 WNDCLASSA wndClass;
4118
4119//SvL: Don't check this now
4120// if (GlobalFindAtomA(EDITCLASSNAME)) return FALSE;
4121
4122 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
4123 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4124 wndClass.lpfnWndProc = (WNDPROC)EditWndProc;
4125 wndClass.cbClsExtra = 0;
4126 wndClass.cbWndExtra = sizeof(VOID*);
4127 wndClass.hCursor = LoadCursorA(0,IDC_IBEAMA);
4128 wndClass.hbrBackground = (HBRUSH)0;
4129 wndClass.lpszClassName = EDITCLASSNAME;
4130
4131 return RegisterClassA(&wndClass);
4132}
4133
4134BOOL EDIT_Unregister()
4135{
4136 if (GlobalFindAtomA(EDITCLASSNAME))
4137 return UnregisterClassA(EDITCLASSNAME,(HINSTANCE)NULL);
4138 else return FALSE;
4139}
Note: See TracBrowser for help on using the repository browser.