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

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

Show/HideCaret fix, removed TextOut workaround

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