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

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

* empty log message *

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