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

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

WINE 991212, WM_SETREDRAW fix, MDI work

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