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

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

non-client fixes, DefWndProc enhancements, several other bugs fixed

File size: 137.1 KB
Line 
1/* $Id: edit.cpp,v 1.25 1999-12-26 17:30:14 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 VOID 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 VOID 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 li;
1640
1641 if (!count)
1642 return;
1643 BkColor = GetBkColor(dc);
1644 TextColor = GetTextColor(dc);
1645 if (rev)
1646 {
1647 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1648 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1649 }
1650 li = EDIT_EM_LineIndex(hwnd, es, line);
1651 if (es->style & ES_MULTILINE)
1652 {
1653 TabbedTextOutA(dc, x, y, es->text + li + col, count,
1654 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset);
1655 } else
1656 {
1657 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
1658 POINT pt;
1659
1660 TextOutA(dc,x,y,text+li+col,count);
1661 if (es->style & ES_PASSWORD)
1662 HeapFree(es->heap, 0, text);
1663 }
1664 if (rev)
1665 {
1666 SetBkColor(dc, BkColor);
1667 SetTextColor(dc, TextColor);
1668 }
1669}
1670
1671
1672/*********************************************************************
1673 *
1674 * EDIT_SetCaretPos
1675 *
1676 */
1677static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos,
1678 BOOL after_wrap)
1679{
1680 LRESULT res = EDIT_EM_PosFromChar(hwnd, es, pos, after_wrap);
1681 INT x = SLOWORD(res);
1682 INT y = SHIWORD(res);
1683
1684 if(x < es->format_rect.left)
1685 x = es->format_rect.left;
1686 if(x > es->format_rect.right - 2)
1687 x = es->format_rect.right - 2;
1688 if(y > es->format_rect.bottom)
1689 y = es->format_rect.bottom;
1690 if(y < es->format_rect.top)
1691 y = es->format_rect.top;
1692 SetCaretPos(x, y);
1693 return;
1694}
1695
1696
1697/*********************************************************************
1698 *
1699 * EDIT_SetRectNP
1700 *
1701 * note: this is not (exactly) the handler called on EM_SETRECTNP
1702 * it is also used to set the rect of a single line control
1703 *
1704 */
1705static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT rc)
1706{
1707 CopyRect(&es->format_rect, rc);
1708 if (es->style & WS_BORDER)
1709 {
1710 INT bw = GetSystemMetrics(SM_CXBORDER)+1,bh = GetSystemMetrics(SM_CYBORDER)+1;
1711
1712 es->format_rect.left += bw;
1713 es->format_rect.top += bh;
1714 es->format_rect.right -= bw;
1715 es->format_rect.bottom -= bh;
1716 }
1717 es->format_rect.left += es->left_margin;
1718 es->format_rect.right -= es->right_margin;
1719 es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
1720 if (es->style & ES_MULTILINE)
1721 es->format_rect.bottom = es->format_rect.top +
1722 MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1723 else
1724 es->format_rect.bottom = es->format_rect.top + es->line_height;
1725 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1726 EDIT_BuildLineDefs_ML(hwnd, es);
1727 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
1728}
1729
1730
1731/*********************************************************************
1732 *
1733 * EDIT_UnlockBuffer
1734 *
1735 */
1736static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force)
1737{
1738 if (!es) {
1739 //ERR_(edit)("no EDITSTATE ... please report\n");
1740 return;
1741 }
1742 if (!(es->style & ES_MULTILINE))
1743 return;
1744 if (!es->lock_count) {
1745 //ERR_(edit)("lock_count == 0 ... please report\n");
1746 return;
1747 }
1748 if (!es->text) {
1749 //ERR_(edit)("es->text == 0 ... please report\n");
1750 return;
1751 }
1752 if (force || (es->lock_count == 1)) {
1753 if (es->hloc) {
1754 LocalUnlock(es->hloc);
1755 es->text = NULL;
1756 }
1757 }
1758 es->lock_count--;
1759}
1760
1761
1762/*********************************************************************
1763 *
1764 * EDIT_WordBreakProc
1765 *
1766 * Find the beginning of words.
1767 * Note: unlike the specs for a WordBreakProc, this function only
1768 * allows to be called without linebreaks between s[0] upto
1769 * s[count - 1]. Remember it is only called
1770 * internally, so we can decide this for ourselves.
1771 *
1772 */
1773static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1774{
1775 INT ret = 0;
1776
1777 //TRACE_(edit)("s=%p, index=%u, count=%u, action=%d\n",
1778 // s, index, count, action);
1779
1780 switch (action) {
1781 case WB_LEFT:
1782 if (!count)
1783 break;
1784 if (index)
1785 index--;
1786 if (s[index] == ' ') {
1787 while (index && (s[index] == ' '))
1788 index--;
1789 if (index) {
1790 while (index && (s[index] != ' '))
1791 index--;
1792 if (s[index] == ' ')
1793 index++;
1794 }
1795 } else {
1796 while (index && (s[index] != ' '))
1797 index--;
1798 if (s[index] == ' ')
1799 index++;
1800 }
1801 ret = index;
1802 break;
1803 case WB_RIGHT:
1804 if (!count)
1805 break;
1806 if (index)
1807 index--;
1808 if (s[index] == ' ')
1809 while ((index < count) && (s[index] == ' ')) index++;
1810 else {
1811 while (s[index] && (s[index] != ' ') && (index < count))
1812 index++;
1813 while ((s[index] == ' ') && (index < count)) index++;
1814 }
1815 ret = index;
1816 break;
1817 case WB_ISDELIMITER:
1818 ret = (s[index] == ' ');
1819 break;
1820 default:
1821 //ERR_(edit)("unknown action code, please report !\n");
1822 break;
1823 }
1824 return ret;
1825}
1826
1827
1828/*********************************************************************
1829 *
1830 * EM_CHARFROMPOS
1831 *
1832 * returns line number (not index) in high-order word of result.
1833 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
1834 * to Richedit, not to the edit control. Original documentation is valid.
1835 *
1836 */
1837static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y)
1838{
1839 POINT pt;
1840 INT index;
1841
1842 pt.x = x;
1843 pt.y = y;
1844
1845 if (!PtInRect(&es->format_rect,pt)) return -1;
1846
1847 index = EDIT_CharFromPos(hwnd, es, x, y, NULL);
1848 return MAKELONG(index, EDIT_EM_LineFromChar(hwnd, es, index));
1849}
1850
1851
1852/*********************************************************************
1853 *
1854 * EM_FMTLINES
1855 *
1856 * Enable or disable soft breaks.
1857 */
1858static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol)
1859{
1860 es->flags &= ~EF_USE_SOFTBRK;
1861 if (add_eol) {
1862 es->flags |= EF_USE_SOFTBRK;
1863 dprintf(("EDIT: EM_FMTLINES: soft break enabled, not implemented\n"));
1864 }
1865 return add_eol;
1866}
1867
1868static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es)
1869{
1870 return (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
1871}
1872
1873/*********************************************************************
1874 *
1875 * EM_GETHANDLE
1876 *
1877 * Hopefully this won't fire back at us.
1878 * We always start with a fixed buffer in our own heap.
1879 * However, with this message a 32 bit application requests
1880 * a handle to 32 bit moveable local heap memory, where it expects
1881 * to find the text.
1882 * It's a pity that from this moment on we have to use this
1883 * local heap, because applications may rely on the handle
1884 * in the future.
1885 *
1886 * In this function we'll try to switch to local heap.
1887 *
1888 */
1889static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es)
1890{
1891 HLOCAL newBuf;
1892 LPSTR newText;
1893 INT newSize;
1894
1895 if (!(es->style & ES_MULTILINE))
1896 return 0;
1897
1898 if (es->hloc)
1899 return es->hloc;
1900
1901 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
1902 //ERR_(edit)("could not allocate new 32 bit buffer\n");
1903 return 0;
1904 }
1905 newSize = MIN(LocalSize(newBuf) - 1, es->buffer_limit);
1906 if (!(newText = (char*)LocalLock(newBuf))) {
1907 //ERR_(edit)("could not lock new 32 bit buffer\n");
1908 LocalFree(newBuf);
1909 return 0;
1910 }
1911 lstrcpyA(newText, es->text);
1912 EDIT_UnlockBuffer(hwnd, es, TRUE);
1913 if (es->text)
1914 HeapFree(es->heap, 0, es->text);
1915 es->hloc = newBuf;
1916 es->buffer_size = newSize;
1917 es->text = newText;
1918 EDIT_LockBuffer(hwnd, es);
1919 //TRACE_(edit)("switched to 32 bit local heap\n");
1920
1921 return es->hloc;
1922}
1923
1924static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es)
1925{
1926 return es->buffer_limit;
1927}
1928
1929/*********************************************************************
1930 *
1931 * EM_GETLINE
1932 *
1933 */
1934static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch)
1935{
1936 LPSTR src;
1937 INT len;
1938 INT i;
1939
1940 if (!lpch || *(WORD*)lpch == 0) return 0;
1941
1942 if (es->style & ES_MULTILINE) {
1943 if (line >= es->line_count)
1944 return 0;
1945 } else
1946 line = 0;
1947 i = EDIT_EM_LineIndex(hwnd, es, line);
1948 src = es->text + i;
1949 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(hwnd, es, i));
1950 for (i = 0 ; i < len ; i++) {
1951 *lpch = *src;
1952 src++;
1953 lpch++;
1954 }
1955 //SvL: Terminate string
1956 *lpch = 0;
1957 return (LRESULT)len;
1958}
1959
1960static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es)
1961{
1962 return (es->style & ES_MULTILINE) ? es->line_count : 1;
1963}
1964
1965static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es)
1966{
1967 return MAKELONG(es->left_margin, es->right_margin);
1968}
1969
1970static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es)
1971{
1972 return ((es->flags & EF_MODIFIED) != 0);
1973}
1974
1975static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es)
1976{
1977 return es->password_char;
1978}
1979
1980static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
1981{
1982 if (lprc) CopyRect(lprc,&es->format_rect);
1983}
1984
1985/*********************************************************************
1986 *
1987 * EM_GETSEL
1988 *
1989 */
1990static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end)
1991{
1992 UINT s = es->selection_start;
1993 UINT e = es->selection_end;
1994
1995 ORDER_UINT(s, e);
1996
1997 if (start)
1998 *start = s;
1999 if (end)
2000 *end = e;
2001 return MAKELONG(s, e);
2002}
2003
2004/*********************************************************************
2005 *
2006 * EM_GETTHUMB
2007 */
2008static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
2009{
2010 SCROLLINFO si;
2011
2012 if (!(es->style & ES_MULTILINE)) return 0;
2013
2014 si.cbSize = sizeof(si);
2015 si.fMask = SIF_TRACKPOS;
2016 return GetScrollInfo(hwnd,SB_VERT,&si) ? si.nTrackPos:0;
2017}
2018
2019static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es)
2020{
2021 return es->word_break_procA;
2022}
2023
2024/*********************************************************************
2025 *
2026 * EM_LINEFROMCHAR
2027 *
2028 */
2029static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index)
2030{
2031 INT line;
2032 LINEDEF *line_def;
2033
2034 if (!(es->style & ES_MULTILINE))
2035 return 0;
2036 if (index > lstrlenA(es->text))
2037 return es->line_count - 1;
2038 if (index == -1)
2039 index = MIN(es->selection_start, es->selection_end); //selection_end == caret pos
2040
2041 line = 0;
2042 line_def = es->first_line_def;
2043 index -= line_def->length;
2044 while ((index >= 0) && line_def->next) {
2045 line++;
2046 line_def = line_def->next;
2047 index -= line_def->length;
2048 }
2049 return line;
2050}
2051
2052
2053/*********************************************************************
2054 *
2055 * EM_LINEINDEX
2056 *
2057 */
2058static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line)
2059{
2060 INT line_index;
2061 LINEDEF *line_def;
2062
2063 if (!(es->style & ES_MULTILINE))
2064 return 0;
2065 if (line >= es->line_count)
2066 return -1;
2067
2068 line_index = 0;
2069 line_def = es->first_line_def;
2070 if (line == -1) {
2071 INT index = es->selection_end - line_def->length;
2072 while ((index >= 0) && line_def->next) {
2073 line_index += line_def->length;
2074 line_def = line_def->next;
2075 index -= line_def->length;
2076 }
2077 } else {
2078 while (line > 0) {
2079 line_index += line_def->length;
2080 line_def = line_def->next;
2081 line--;
2082 }
2083 }
2084 return line_index;
2085}
2086
2087
2088/*********************************************************************
2089 *
2090 * EM_LINELENGTH
2091 *
2092 */
2093static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index)
2094{
2095 LINEDEF *line_def;
2096
2097 if (!(es->style & ES_MULTILINE))
2098 return lstrlenA(es->text);
2099
2100 if (index == -1)
2101 {
2102 INT sl = EDIT_EM_LineFromChar(hwnd,es,MIN(es->selection_start,es->selection_end));
2103 INT el = EDIT_EM_LineFromChar(hwnd,es,MAX(es->selection_start,es->selection_end));
2104
2105 if (sl == el)
2106 return EDIT_EM_LineLength(hwnd,es,sl)+es->selection_start-es->selection_end;
2107 else
2108 return es->selection_start+EDIT_EM_LineLength(hwnd,es,el)-es->selection_end;
2109 }
2110 line_def = es->first_line_def;
2111 index -= line_def->length;
2112 while ((index >= 0) && line_def->next) {
2113 line_def = line_def->next;
2114 index -= line_def->length;
2115 }
2116 return line_def->net_length;
2117}
2118
2119static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert)
2120{
2121 if (updateHorz && (es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2122 {
2123 SCROLLINFO si;
2124 INT fw = es->format_rect.right - es->format_rect.left;
2125
2126 si.cbSize = sizeof(SCROLLINFO);
2127 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2128 si.nMin = 0;
2129 si.nMax = es->text_width + fw - 1;
2130 si.nPage = fw;
2131 si.nPos = es->x_offset;
2132 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2133 }
2134
2135 if (updateVert && (es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2136 {
2137 INT vlc = (es->format_rect.bottom-es->format_rect.top)/es->line_height;
2138 SCROLLINFO si;
2139
2140 si.cbSize = sizeof(SCROLLINFO);
2141 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2142 si.nMin = 0;
2143 si.nMax = es->line_count + vlc - 2;
2144 si.nPage = vlc;
2145 si.nPos = es->y_offset;
2146 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2147 }
2148}
2149
2150
2151/*********************************************************************
2152 *
2153 * EM_LINESCROLL
2154 *
2155 * FIXME: dx is in average character widths
2156 * However, we assume it is in pixels when we use this
2157 * function internally
2158 *
2159 */
2160static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2161{
2162 INT nyoff;
2163
2164 if (!(es->style & ES_MULTILINE))
2165 return FALSE;
2166
2167 if (-dx > es->x_offset)
2168 dx = -es->x_offset;
2169 if (dx > es->text_width - es->x_offset)
2170 dx = es->text_width - es->x_offset;
2171 nyoff = MAX(0, es->y_offset + dy);
2172 if (nyoff >= es->line_count)
2173 nyoff = es->line_count - 1;
2174 dy = (es->y_offset - nyoff) * es->line_height;
2175 if (dx || dy)
2176 {
2177 RECT rc1;
2178 RECT rc;
2179
2180 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2181 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
2182 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2183 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2184
2185 GetClientRect(hwnd, &rc1);
2186 IntersectRect(&rc, &rc1, &es->format_rect);
2187
2188 ScrollWindowEx(hwnd, -dx, dy,
2189 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2190 es->y_offset = nyoff;
2191 es->x_offset += dx;
2192 EDIT_UpdateScrollBars(hwnd,es,dx,dy);
2193 }
2194
2195 return TRUE;
2196}
2197
2198
2199/*********************************************************************
2200 *
2201 * EM_POSFROMCHAR
2202 *
2203 */
2204static LRESULT EDIT_EM_PosFromChar(HWND hwnd, EDITSTATE *es, INT index, BOOL after_wrap)
2205{
2206 INT len = lstrlenA(es->text);
2207 INT l;
2208 INT li;
2209 INT x;
2210 INT y = 0;
2211 HDC dc;
2212 HFONT old_font = 0;
2213 SIZE size;
2214
2215 index = MIN(index, len);
2216 dc = GetDC(hwnd);
2217 if (es->font)
2218 old_font = SelectObject(dc, es->font);
2219 if (es->style & ES_MULTILINE) {
2220 l = EDIT_EM_LineFromChar(hwnd, es, index);
2221 y = (l - es->y_offset) * es->line_height;
2222 li = EDIT_EM_LineIndex(hwnd, es, l);
2223 if (after_wrap && (li == index) && l) {
2224 INT l2 = l - 1;
2225 LINEDEF *line_def = es->first_line_def;
2226 while (l2) {
2227 line_def = line_def->next;
2228 l2--;
2229 }
2230 if (line_def->ending == END_WRAP) {
2231 l--;
2232 y -= es->line_height;
2233 li = EDIT_EM_LineIndex(hwnd, es, l);
2234 }
2235 }
2236 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2237 es->tabs_count, es->tabs)) - es->x_offset;
2238 } else
2239 {
2240 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
2241
2242 GetTextExtentPoint32A(dc,text,index,&size);
2243 x = size.cx;
2244 if (es->x_offset)
2245 {
2246 GetTextExtentPoint32A(dc,text,es->x_offset,&size);
2247 x -= size.cx;
2248 }
2249 y = 0;
2250 if (es->style & ES_PASSWORD)
2251 HeapFree(es->heap, 0 ,text);
2252 }
2253 x += es->format_rect.left;
2254 y += es->format_rect.top;
2255 if (es->font)
2256 SelectObject(dc, old_font);
2257 ReleaseDC(hwnd, dc);
2258 return MAKELONG((INT16)x, (INT16)y);
2259}
2260
2261BOOL EDIT_CheckNumber(CHAR *text)
2262{
2263 if (!text) return TRUE;
2264
2265 while (text[0] != 0)
2266 {
2267 if ((BYTE)text[0] < '0' || (BYTE)text[0] > '9') return FALSE;
2268 text++;
2269 }
2270
2271 return TRUE;
2272}
2273
2274/*********************************************************************
2275 *
2276 * EM_REPLACESEL
2277 *
2278 */
2279static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
2280{
2281 INT strl = lstrlenA(lpsz_replace);
2282 INT tl = lstrlenA(es->text);
2283 INT utl;
2284 UINT s;
2285 UINT e;
2286 INT i;
2287 LPSTR p;
2288
2289 s = es->selection_start;
2290 e = es->selection_end;
2291
2292 if ((s == e) && !strl)
2293 return;
2294
2295 ORDER_UINT(s, e);
2296
2297 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
2298 return;
2299
2300 if (e != s) {
2301 /* there is something to be deleted */
2302 if (can_undo) {
2303 utl = lstrlenA(es->undo_text);
2304 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2305 /* undo-buffer is extended to the right */
2306 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2307 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2308 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2309 /* undo-buffer is extended to the left */
2310 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2311 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2312 p[e - s] = p[0];
2313 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2314 p[i] = (es->text + s)[i];
2315 es->undo_position = s;
2316 } else {
2317 /* new undo-buffer */
2318 EDIT_MakeUndoFit(hwnd, es, e - s);
2319 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2320 es->undo_position = s;
2321 }
2322 /* any deletion makes the old insertion-undo invalid */
2323 es->undo_insert_count = 0;
2324 } else
2325 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2326
2327 /* now delete */
2328 lstrcpyA(es->text + s, es->text + e);
2329 }
2330 if (strl)
2331 {
2332 if (es->style & ES_NUMBER && !EDIT_CheckNumber((CHAR*)lpsz_replace))
2333 MessageBeep(MB_ICONEXCLAMATION);
2334 else
2335 {
2336 /* there is an insertion */
2337 if (can_undo) {
2338 if ((s == es->undo_position) ||
2339 ((es->undo_insert_count) &&
2340 (s == es->undo_position + es->undo_insert_count)))
2341 /*
2342 * insertion is new and at delete position or
2343 * an extension to either left or right
2344 */
2345 es->undo_insert_count += strl;
2346 else {
2347 /* new insertion undo */
2348 es->undo_position = s;
2349 es->undo_insert_count = strl;
2350 /* new insertion makes old delete-buffer invalid */
2351 *es->undo_text = '\0';
2352 }
2353 } else
2354 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2355
2356
2357 /* now insert */
2358 tl = lstrlenA(es->text);
2359 for (p = es->text + tl ; p >= es->text + s ; p--)
2360 p[strl] = p[0];
2361 for (i = 0 , p = es->text + s ; i < strl ; i++)
2362 p[i] = lpsz_replace[i];
2363
2364 if (es->style & ES_OEMCONVERT)
2365 {
2366 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,strl);
2367
2368 CharToOemBuffA(lpsz_replace,text,strl);
2369 OemToCharBuffA(text,p,strl);
2370 HeapFree(es->heap,0,text);
2371 }
2372
2373 if(es->style & ES_UPPERCASE)
2374 CharUpperBuffA(p, strl);
2375 else if(es->style & ES_LOWERCASE)
2376 CharLowerBuffA(p, strl);
2377
2378 s += strl;
2379 }
2380 }
2381 /* FIXME: really inefficient */
2382 if (es->style & ES_MULTILINE)
2383 EDIT_BuildLineDefs_ML(hwnd, es);
2384
2385 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
2386 es->flags |= EF_MODIFIED;
2387 es->flags |= EF_UPDATE;
2388 EDIT_EM_ScrollCaret(hwnd, es);
2389
2390 EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
2391 /* FIXME: really inefficient */
2392 EDIT_Refresh(hwnd,es);
2393}
2394
2395
2396/*********************************************************************
2397 *
2398 * EM_SCROLL
2399 *
2400 */
2401static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
2402{
2403 INT dy;
2404
2405 if (!(es->style & ES_MULTILINE))
2406 return (LRESULT)FALSE;
2407
2408 dy = 0;
2409
2410 switch (action) {
2411 case SB_LINEUP:
2412 if (es->y_offset)
2413 dy = -1;
2414 break;
2415 case SB_LINEDOWN:
2416 if (es->y_offset < es->line_count - 1)
2417 dy = 1;
2418 break;
2419 case SB_PAGEUP:
2420 if (es->y_offset)
2421 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2422 break;
2423 case SB_PAGEDOWN:
2424 if (es->y_offset < es->line_count - 1)
2425 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2426 break;
2427 default:
2428 return (LRESULT)FALSE;
2429 }
2430 if (dy) {
2431 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2432 EDIT_EM_LineScroll(hwnd, es, 0, dy);
2433 }
2434 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2435}
2436
2437
2438/*********************************************************************
2439 *
2440 * EM_SCROLLCARET
2441 *
2442 */
2443static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
2444{
2445 if (es->style & ES_MULTILINE) {
2446 INT l;
2447 INT li;
2448 INT vlc;
2449 INT ww;
2450 INT cw = es->char_width;
2451 INT x;
2452 INT dy = 0;
2453 INT dx = 0;
2454
2455 l = EDIT_EM_LineFromChar(hwnd, es, es->selection_end);
2456 li = EDIT_EM_LineIndex(hwnd, es, l);
2457 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2458 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2459 if (l >= es->y_offset + vlc)
2460 dy = l - vlc + 1 - es->y_offset;
2461 if (l < es->y_offset)
2462 dy = l - es->y_offset;
2463 ww = es->format_rect.right - es->format_rect.left;
2464 if (x < es->format_rect.left)
2465 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2466 if (x > es->format_rect.right)
2467 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2468 if (dy || dx)
2469 EDIT_EM_LineScroll(hwnd, es, dx, dy);
2470 } else {
2471 INT x;
2472 INT goal;
2473 INT format_width;
2474
2475 if (!(es->style & ES_AUTOHSCROLL))
2476 return;
2477
2478 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2479 format_width = es->format_rect.right - es->format_rect.left;
2480 if (x < es->format_rect.left)
2481 {
2482 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2483 do {
2484 es->x_offset--;
2485 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2486 } while ((x < goal) && es->x_offset);
2487 /* FIXME: use ScrollWindow() somehow to improve performance */
2488 EDIT_Refresh(hwnd,es);
2489 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2490 } else if (x > es->format_rect.right)
2491 {
2492 INT x_last;
2493 INT len = lstrlenA(es->text);
2494 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2495 do {
2496 es->x_offset++;
2497 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2498 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, len, FALSE));
2499 } while ((x > goal) && (x_last > es->format_rect.right));
2500 /* FIXME: use ScrollWindow() somehow to improve performance */
2501 EDIT_Refresh(hwnd,es);
2502 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2503 }
2504 }
2505}
2506
2507
2508/*********************************************************************
2509 *
2510 * EM_SETHANDLE
2511 *
2512 */
2513static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
2514{
2515 if (!(es->style & ES_MULTILINE))
2516 return;
2517
2518 if (!hloc) {
2519 //WARN_(edit)("called with NULL handle\n");
2520 return;
2521 }
2522
2523 EDIT_UnlockBuffer(hwnd, es, TRUE);
2524 /*
2525 * old buffer is freed by caller, unless
2526 * it is still in our own heap. (in that case
2527 * we free it, correcting the buggy caller.)
2528 */
2529 if (es->text)
2530 HeapFree(es->heap, 0, es->text);
2531
2532 es->hloc = hloc;
2533 es->text = NULL;
2534 es->buffer_size = LocalSize(es->hloc) - 1;
2535 EDIT_LockBuffer(hwnd, es);
2536
2537 if (es->text && es->text[0] != 0)
2538 {
2539 if (es->style & ES_NUMBER)
2540 {
2541 //CB: todo
2542 }
2543
2544 if (es->style & ES_OEMCONVERT)
2545 {
2546 INT len = lstrlenA(es->text);
2547 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,len);
2548
2549 CharToOemBuffA(es->text,text,len);
2550 OemToCharBuffA(text,es->text,len);
2551 HeapFree(es->heap,0,text);
2552 }
2553
2554 if(es->style & ES_UPPERCASE)
2555 CharUpperA(es->text);
2556 else if(es->style & ES_LOWERCASE)
2557 CharLowerA(es->text);
2558 }
2559
2560 es->x_offset = es->y_offset = 0;
2561 es->selection_start = es->selection_end = 0;
2562 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2563 es->flags &= ~EF_MODIFIED;
2564 es->flags &= ~EF_UPDATE;
2565 EDIT_BuildLineDefs_ML(hwnd, es);
2566 EDIT_Refresh(hwnd,es);
2567 EDIT_EM_ScrollCaret(hwnd, es);
2568 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
2569}
2570
2571/*********************************************************************
2572 *
2573 * EM_SETLIMITTEXT
2574 *
2575 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2576 * However, the windows version is not complied to yet in all of edit.c
2577 *
2578 */
2579static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit)
2580{
2581 if (es->style & ES_MULTILINE) {
2582 if (limit)
2583 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2584 else
2585 es->buffer_limit = BUFLIMIT_MULTI;
2586 } else {
2587 if (limit)
2588 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2589 else
2590 es->buffer_limit = BUFLIMIT_SINGLE;
2591 }
2592}
2593
2594
2595/*********************************************************************
2596 *
2597 * EM_SETMARGINS
2598 *
2599 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2600 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2601 * of the char's width, according to the new docs.
2602 *
2603 */
2604static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action,
2605 INT left, INT right)
2606{
2607 RECT r;
2608 INT oldLeft = es->left_margin,oldRight = es->right_margin;
2609
2610 if (action & EC_LEFTMARGIN) {
2611 if (left != EC_USEFONTINFO)
2612 es->left_margin = left;
2613 else
2614 es->left_margin = es->char_width / 3;
2615 }
2616
2617 if (action & EC_RIGHTMARGIN) {
2618 if (right != EC_USEFONTINFO)
2619 es->right_margin = right;
2620 else
2621 es->right_margin = es->char_width / 3;
2622 }
2623 //TRACE_(edit)("left=%d, right=%d\n", es->left_margin, es->right_margin);
2624
2625 if (oldLeft != es->left_margin || oldRight != es->right_margin)
2626 {
2627 GetClientRect(hwnd, &r);
2628 EDIT_SetRectNP(hwnd, es, &r);
2629 if (es->style & ES_MULTILINE)
2630 EDIT_BuildLineDefs_ML(hwnd, es);
2631
2632 EDIT_Refresh(hwnd,es);
2633 if (es->flags & EF_FOCUSED) {
2634 DestroyCaret();
2635 CreateCaret(hwnd, 0, 2, es->line_height);
2636 EDIT_SetCaretPos(hwnd, es, es->selection_end,
2637 es->flags & EF_AFTER_WRAP);
2638 ShowCaret(hwnd);
2639 }
2640 }
2641}
2642
2643static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified)
2644{
2645 if (fModified)
2646 es->flags |= EF_MODIFIED;
2647 else
2648 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
2649}
2650
2651/*********************************************************************
2652 *
2653 * EM_SETPASSWORDCHAR
2654 *
2655 */
2656static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c)
2657{
2658 if (es->style & ES_MULTILINE)
2659 return;
2660
2661 if (es->password_char == c)
2662 return;
2663
2664 es->password_char = c;
2665 if (c) {
2666 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_PASSWORD);
2667 es->style |= ES_PASSWORD;
2668 } else {
2669 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_PASSWORD);
2670 es->style &= ~ES_PASSWORD;
2671 }
2672 EDIT_Refresh(hwnd,es);
2673}
2674
2675static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly)
2676{
2677 if (fReadOnly)
2678 {
2679 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_READONLY);
2680 es->style |= ES_READONLY;
2681 } else
2682 {
2683 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_READONLY);
2684 es->style &= ~ES_READONLY;
2685 }
2686
2687 return TRUE;
2688}
2689
2690static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2691{
2692 if ((es->style & ES_MULTILINE) && lprc)
2693 {
2694 EDIT_SetRectNP(hwnd,es,lprc);
2695 EDIT_Refresh(hwnd,es);
2696 }
2697}
2698
2699static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2700{
2701 if ((es->style & ES_MULTILINE) && lprc)
2702 EDIT_SetRectNP(hwnd,es,lprc);
2703}
2704
2705/*********************************************************************
2706 *
2707 * EDIT_EM_SetSel
2708 *
2709 * note: unlike the specs say: the order of start and end
2710 * _is_ preserved in Windows. (i.e. start can be > end)
2711 * In other words: this handler is OK
2712 *
2713 */
2714static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2715{
2716 UINT old_start = es->selection_start;
2717 UINT old_end = es->selection_end;
2718 UINT len = lstrlenA(es->text);
2719
2720 if (start == -1) {
2721 start = es->selection_end;
2722 end = es->selection_end;
2723 } else {
2724 start = MIN(start, len);
2725 end = MIN(end, len);
2726 }
2727 es->selection_start = start;
2728 es->selection_end = end;
2729 if (after_wrap)
2730 es->flags |= EF_AFTER_WRAP;
2731 else
2732 es->flags &= ~EF_AFTER_WRAP;
2733 if (es->flags & EF_FOCUSED)
2734 EDIT_SetCaretPos(hwnd, es, end, after_wrap);
2735/* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2736 ORDER_UINT(start, end);
2737 ORDER_UINT(end, old_end);
2738 ORDER_UINT(start, old_start);
2739 ORDER_UINT(old_start, old_end);
2740 if (start == old_start && end == old_end) return;
2741 if (end != old_start)
2742 {
2743/*
2744 * One can also do
2745 * ORDER_UINT32(end, old_start);
2746 * EDIT_InvalidateText(wnd, es, start, end);
2747 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2748 * in place of the following if statement.
2749 */
2750 if (old_start > end )
2751 {
2752 EDIT_InvalidateText(hwnd, es, start, end);
2753 EDIT_InvalidateText(hwnd, es, old_start, old_end);
2754 }
2755 else
2756 {
2757 EDIT_InvalidateText(hwnd, es, start, old_start);
2758 EDIT_InvalidateText(hwnd, es, end, old_end);
2759 }
2760 }
2761 else EDIT_InvalidateText(hwnd, es, start, old_end);
2762}
2763
2764
2765/*********************************************************************
2766 *
2767 * EM_SETTABSTOPS
2768 *
2769 */
2770static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs)
2771{
2772 if (!(es->style & ES_MULTILINE))
2773 return FALSE;
2774 if (es->tabs)
2775 HeapFree(es->heap, 0, es->tabs);
2776 es->tabs_count = count;
2777 if (!count)
2778 es->tabs = NULL;
2779 else {
2780 es->tabs = (INT*)HeapAlloc(es->heap, 0, count * sizeof(INT));
2781 memcpy(es->tabs, tabs, count * sizeof(INT));
2782 }
2783 return TRUE;
2784}
2785
2786
2787/*********************************************************************
2788 *
2789 * EM_SETWORDBREAKPROC
2790 *
2791 */
2792static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2793{
2794 if (es->word_break_procA == wbp)
2795 return;
2796
2797 es->word_break_procA = wbp;
2798 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2799 EDIT_BuildLineDefs_ML(hwnd, es);
2800 EDIT_Refresh(hwnd,es);
2801 }
2802}
2803
2804
2805/*********************************************************************
2806 *
2807 * EM_UNDO / WM_UNDO
2808 *
2809 */
2810static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
2811{
2812 INT ulength = lstrlenA(es->undo_text);
2813 LPSTR utext = (LPSTR)HeapAlloc(es->heap, 0, ulength + 1);
2814
2815 lstrcpyA(utext, es->undo_text);
2816
2817 //TRACE_(edit)("before UNDO:insertion length = %d, deletion buffer = %s\n",
2818 // es->undo_insert_count, utext);
2819
2820 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2821 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2822 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext);
2823 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2824 HeapFree(es->heap, 0, utext);
2825
2826 //TRACE_(edit)("after UNDO:insertion length = %d, deletion buffer = %s\n",
2827 // es->undo_insert_count, es->undo_text);
2828
2829 return TRUE;
2830}
2831
2832static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2833{
2834 if (wParam == EMSIS_COMPOSITIONSTRING)
2835 {
2836 //CB: todo
2837 }
2838
2839 return 0;
2840}
2841
2842static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2843{
2844 //CB: todo
2845
2846 return 0; //default
2847}
2848
2849/*********************************************************************
2850 *
2851 * WM_CHAR
2852 *
2853 */
2854static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data)
2855{
2856 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
2857 switch (c) {
2858 case '\r':
2859 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
2860 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
2861 {
2862 MessageBeep(MB_ICONEXCLAMATION);
2863 break;
2864 }
2865 case '\n':
2866 if (es->style & ES_MULTILINE) {
2867 if (es->style & ES_READONLY) {
2868 EDIT_MoveHome(hwnd, es, FALSE);
2869 EDIT_MoveDown_ML(hwnd, es, FALSE);
2870 } else
2871 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\r\n");
2872 }
2873 break;
2874 case '\t':
2875 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2876 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\t");
2877 break;
2878 case VK_BACK:
2879 if (!(es->style & ES_READONLY) && !control) {
2880 if (es->selection_start != es->selection_end)
2881 EDIT_WM_Clear(hwnd, es);
2882 else {
2883 /* delete character left of caret */
2884 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
2885 EDIT_MoveBackward(hwnd, es, TRUE);
2886 EDIT_WM_Clear(hwnd, es);
2887 }
2888 }
2889 break;
2890 default:
2891 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127))
2892 {
2893 char str[2];
2894
2895 if (es->style & ES_NUMBER)
2896 {
2897 if ((BYTE)c < '0' || (BYTE)c > '9') MessageBeep(MB_ICONEXCLAMATION);
2898 return;
2899 }
2900 str[0] = c;
2901 str[1] = '\0';
2902 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str);
2903 } else MessageBeep(MB_ICONEXCLAMATION);
2904 break;
2905 }
2906}
2907
2908
2909/*********************************************************************
2910 *
2911 * WM_COMMAND
2912 *
2913 */
2914static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
2915{
2916 if (code || control)
2917 return;
2918
2919 switch (id) {
2920 case EM_UNDO:
2921 EDIT_EM_Undo(hwnd, es);
2922 break;
2923 case WM_CUT:
2924 EDIT_WM_Cut(hwnd, es);
2925 break;
2926 case WM_COPY:
2927 EDIT_WM_Copy(hwnd, es);
2928 break;
2929 case WM_PASTE:
2930 EDIT_WM_Paste(hwnd, es);
2931 break;
2932 case WM_CLEAR:
2933 EDIT_WM_Clear(hwnd, es);
2934 break;
2935 case EM_SETSEL:
2936 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
2937 EDIT_EM_ScrollCaret(hwnd, es);
2938 break;
2939 default:
2940 //ERR_(edit)("unknown menu item, please report\n");
2941 break;
2942 }
2943}
2944
2945
2946/*********************************************************************
2947 *
2948 * WM_CONTEXTMENU
2949 *
2950 * Note: the resource files resource/sysres_??.rc cannot define a
2951 * single popup menu. Hence we use a (dummy) menubar
2952 * containing the single popup menu as its first item.
2953 *
2954 * FIXME: the message identifiers have been chosen arbitrarily,
2955 * hence we use MF_BYPOSITION.
2956 * We might as well use the "real" values (anybody knows ?)
2957 * The menu definition is in resources/sysres_??.rc.
2958 * Once these are OK, we better use MF_BYCOMMAND here
2959 * (as we do in EDIT_WM_Command()).
2960 *
2961 */
2962static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y)
2963{
2964 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
2965 HMENU popup = GetSubMenu(menu, 0);
2966 UINT start = es->selection_start;
2967 UINT end = es->selection_end;
2968
2969 ORDER_UINT(start, end);
2970
2971 /* undo */
2972 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(hwnd, es) ? MF_ENABLED : MF_GRAYED));
2973 /* cut */
2974 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & (ES_PASSWORD | ES_READONLY)) ? MF_ENABLED : MF_GRAYED));
2975 /* copy */
2976 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2977 /* paste */
2978 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
2979 /* delete */
2980 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
2981 /* select all */
2982 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
2983
2984 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
2985 DestroyMenu(menu);
2986}
2987
2988
2989/*********************************************************************
2990 *
2991 * WM_COPY
2992 *
2993 */
2994static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
2995{
2996 INT s = es->selection_start;
2997 INT e = es->selection_end;
2998 HGLOBAL hdst;
2999 LPSTR dst;
3000
3001 if (e == s)
3002 return;
3003 ORDER_INT(s, e);
3004 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3005 dst = (LPSTR)GlobalLock(hdst);
3006 lstrcpynA(dst, es->text + s, e - s + 1);
3007 GlobalUnlock(hdst);
3008 OpenClipboard(hwnd);
3009 EmptyClipboard();
3010 SetClipboardData(CF_TEXT, hdst);
3011 CloseClipboard();
3012}
3013
3014
3015/*********************************************************************
3016 *
3017 * WM_CREATE
3018 *
3019 */
3020static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3021{
3022 /*
3023 * To initialize some final structure members, we call some helper
3024 * functions. However, since the EDITSTATE is not consistent (i.e.
3025 * not fully initialized), we should be very careful which
3026 * functions can be called, and in what order.
3027 */
3028 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
3029 EDIT_EM_EmptyUndoBuffer(hwnd, es);
3030 if (cs->lpszName && *(cs->lpszName) != '\0') {
3031 EDIT_EM_ReplaceSel(hwnd, es, FALSE, cs->lpszName);
3032 /* if we insert text to the editline, the text scrolls out
3033 * of the window, as the caret is placed after the insert
3034 * pos normally; thus we reset es->selection... to 0 and
3035 * update caret
3036 */
3037 es->selection_start = es->selection_end = 0;
3038 EDIT_EM_ScrollCaret(hwnd, es);
3039 }
3040 return 0;
3041}
3042
3043
3044/*********************************************************************
3045 *
3046 * WM_DESTROY
3047 *
3048 */
3049static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
3050{
3051 if (es->hloc) {
3052 while (LocalUnlock(es->hloc)) ;
3053 LocalFree(es->hloc);
3054 }
3055
3056 HeapDestroy(es->heap);
3057 HeapFree(GetProcessHeap(), 0, es);
3058 SetInfoPtr(hwnd,0);
3059}
3060
3061
3062/*********************************************************************
3063 *
3064 * WM_ERASEBKGND
3065 *
3066 */
3067static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
3068{
3069 HBRUSH brush;
3070 RECT rc;
3071
3072 HideCaret(hwnd);
3073
3074 if (!es->bEnableState || (es->style & ES_READONLY))
3075 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
3076 else
3077 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
3078
3079 if (!brush)
3080 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3081
3082 GetClientRect(hwnd, &rc);
3083 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3084 GetClipBox(dc, &rc);
3085 /*
3086 * FIXME: specs say that we should UnrealizeObject() the brush,
3087 * but the specs of UnrealizeObject() say that we shouldn't
3088 * unrealize a stock object. The default brush that
3089 * DefWndProc() returns is ... a stock object.
3090 */
3091 FillRect(dc, &rc, brush);
3092
3093 ShowCaret(hwnd);
3094
3095 return -1;
3096}
3097
3098
3099/*********************************************************************
3100 *
3101 * WM_GETTEXT
3102 *
3103 */
3104static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text)
3105{
3106 INT len;
3107
3108 if (es->text == NULL) // the only case of failure i can imagine
3109 return 0;
3110
3111 len = min(count, lstrlenA(es->text)+1); // determine length
3112 lstrcpynA(text, es->text, len); // copy as much as possible
3113 return len;
3114}
3115
3116
3117/*********************************************************************
3118 *
3119 * EDIT_HScroll_Hack
3120 *
3121 * 16 bit notepad needs this. Actually it is not _our_ hack,
3122 * it is notepad's. Notepad is sending us scrollbar messages with
3123 * undocumented parameters without us even having a scrollbar ... !?!?
3124 *
3125 */
3126static LRESULT EDIT_HScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3127{
3128 INT dx = 0;
3129 INT fw = es->format_rect.right - es->format_rect.left;
3130 LRESULT ret = 0;
3131
3132 if (!(es->flags & EF_HSCROLL_HACK)) {
3133 //ERR_(edit)("hacked WM_HSCROLL handler invoked\n");
3134 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
3135 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
3136 es->flags |= EF_HSCROLL_HACK;
3137 }
3138
3139 switch (action) {
3140 case SB_LINELEFT:
3141 if (es->x_offset)
3142 dx = -es->char_width;
3143 break;
3144 case SB_LINERIGHT:
3145 if (es->x_offset < es->text_width)
3146 dx = es->char_width;
3147 break;
3148 case SB_PAGELEFT:
3149 if (es->x_offset)
3150 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3151 break;
3152 case SB_PAGERIGHT:
3153 if (es->x_offset < es->text_width)
3154 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3155 break;
3156 case SB_LEFT:
3157 if (es->x_offset)
3158 dx = -es->x_offset;
3159 break;
3160 case SB_RIGHT:
3161 if (es->x_offset < es->text_width)
3162 dx = es->text_width - es->x_offset;
3163 break;
3164 case SB_THUMBTRACK:
3165 es->flags |= EF_HSCROLL_TRACK;
3166 dx = pos * es->text_width / 100 - es->x_offset;
3167 break;
3168 case SB_THUMBPOSITION:
3169 es->flags &= ~EF_HSCROLL_TRACK;
3170 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3171 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3172 break;
3173 case SB_ENDSCROLL:
3174 break;
3175
3176 default:
3177 //ERR_(edit)("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3178 return 0;
3179 }
3180 if (dx)
3181 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3182 return ret;
3183}
3184
3185
3186/*********************************************************************
3187 *
3188 * WM_HSCROLL
3189 *
3190 */
3191static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3192{
3193 INT dx;
3194 INT fw;
3195
3196 if (!(es->style & ES_MULTILINE))
3197 return 0;
3198
3199 if (!(es->style & ES_AUTOHSCROLL))
3200 return 0;
3201
3202 if (!(es->style & WS_HSCROLL))
3203 return EDIT_HScroll_Hack(hwnd, es, action, pos, scroll_bar);
3204
3205 dx = 0;
3206 fw = es->format_rect.right - es->format_rect.left;
3207 switch (action) {
3208 case SB_LINELEFT:
3209 if (es->x_offset)
3210 dx = -es->char_width;
3211 break;
3212 case SB_LINERIGHT:
3213 if (es->x_offset < es->text_width)
3214 dx = es->char_width;
3215 break;
3216 case SB_PAGELEFT:
3217 if (es->x_offset)
3218 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3219 break;
3220 case SB_PAGERIGHT:
3221 if (es->x_offset < es->text_width)
3222 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3223 break;
3224 case SB_LEFT:
3225 if (es->x_offset)
3226 dx = -es->x_offset;
3227 break;
3228 case SB_RIGHT:
3229 if (es->x_offset < es->text_width)
3230 dx = es->text_width - es->x_offset;
3231 break;
3232 case SB_THUMBTRACK:
3233 es->flags |= EF_HSCROLL_TRACK;
3234 dx = pos - es->x_offset;
3235 break;
3236 case SB_THUMBPOSITION:
3237 es->flags &= ~EF_HSCROLL_TRACK;
3238 if (!(dx = pos - es->x_offset)) {
3239 SetScrollPos(hwnd, SB_HORZ, pos, TRUE);
3240 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3241 }
3242 break;
3243 case SB_ENDSCROLL:
3244 break;
3245
3246 default:
3247 //ERR_(edit)("undocumented WM_HSCROLL parameter, please report\n");
3248 return 0;
3249 }
3250 if (dx)
3251 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3252 return 0;
3253}
3254
3255
3256/*********************************************************************
3257 *
3258 * EDIT_CheckCombo
3259 *
3260 */
3261static BOOL EDIT_CheckCombo(HWND hwnd, UINT msg, INT key, DWORD key_data)
3262{
3263 HWND hLBox;
3264
3265 if (GetClassWord(GetParent(hwnd),GCW_ATOM) == GlobalFindAtomA(COMBOBOXCLASSNAME) &&
3266 (hLBox = COMBO_GetLBWindow(GetParent(hwnd)))) {
3267 HWND hCombo = GetParent(hwnd);
3268 BOOL bUIFlip = TRUE;
3269
3270 //TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3271 // wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3272
3273 switch (msg) {
3274 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3275 if (key != VK_F4) {
3276 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3277 if (SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0))
3278 bUIFlip = FALSE;
3279 }
3280 if (!bUIFlip)
3281 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3282 else {
3283 /* make sure ComboLBox pops up */
3284 SendMessageA(hCombo, CB_SETEXTENDEDUI, 0, 0);
3285 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3286 SendMessageA(hCombo, CB_SETEXTENDEDUI, 1, 0);
3287 }
3288 break;
3289 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3290 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3291 if (bUIFlip) {
3292 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3293 SendMessageA(hCombo, CB_SHOWDROPDOWN, (bUIFlip) ? FALSE : TRUE, 0);
3294 } else
3295 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3296 break;
3297 }
3298 return TRUE;
3299 }
3300 return FALSE;
3301}
3302
3303
3304/*********************************************************************
3305 *
3306 * WM_KEYDOWN
3307 *
3308 * Handling of special keys that don't produce a WM_CHAR
3309 * (i.e. non-printable keys) & Backspace & Delete
3310 *
3311 */
3312static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3313{
3314 BOOL shift;
3315 BOOL control;
3316
3317 if (GetKeyState(VK_MENU) & 0x8000)
3318 return 0;
3319
3320 shift = GetKeyState(VK_SHIFT) & 0x8000;
3321 control = GetKeyState(VK_CONTROL) & 0x8000;
3322
3323 switch (key) {
3324 case VK_F4:
3325 case VK_UP:
3326 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3327 break;
3328 if (key == VK_F4)
3329 break;
3330 /* fall through */
3331 case VK_LEFT:
3332 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3333 EDIT_MoveUp_ML(hwnd, es, shift);
3334 else
3335 if (control)
3336 EDIT_MoveWordBackward(hwnd, es, shift);
3337 else
3338 EDIT_MoveBackward(hwnd, es, shift);
3339 break;
3340 case VK_DOWN:
3341 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3342 break;
3343 /* fall through */
3344 case VK_RIGHT:
3345 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3346 EDIT_MoveDown_ML(hwnd, es, shift);
3347 else if (control)
3348 EDIT_MoveWordForward(hwnd, es, shift);
3349 else
3350 EDIT_MoveForward(hwnd, es, shift);
3351 break;
3352 case VK_HOME:
3353 EDIT_MoveHome(hwnd, es, shift);
3354 break;
3355 case VK_END:
3356 EDIT_MoveEnd(hwnd, es, shift);
3357 break;
3358 case VK_PRIOR:
3359 if (es->style & ES_MULTILINE)
3360 EDIT_MovePageUp_ML(hwnd, es, shift);
3361 break;
3362 case VK_NEXT:
3363 if (es->style & ES_MULTILINE)
3364 EDIT_MovePageDown_ML(hwnd, es, shift);
3365 break;
3366 case VK_DELETE:
3367 if (!(es->style & ES_READONLY) && !(shift && control)) {
3368 if (es->selection_start != es->selection_end) {
3369 if (shift)
3370 EDIT_WM_Cut(hwnd, es);
3371 else
3372 EDIT_WM_Clear(hwnd, es);
3373 } else {
3374 if (shift) {
3375 /* delete character left of caret */
3376 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3377 EDIT_MoveBackward(hwnd, es, TRUE);
3378 EDIT_WM_Clear(hwnd, es);
3379 } else if (control) {
3380 /* delete to end of line */
3381 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3382 EDIT_MoveEnd(hwnd, es, TRUE);
3383 EDIT_WM_Clear(hwnd, es);
3384 } else {
3385 /* delete character right of caret */
3386 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3387 EDIT_MoveForward(hwnd, es, TRUE);
3388 EDIT_WM_Clear(hwnd, es);
3389 }
3390 }
3391 }
3392 break;
3393 case VK_INSERT:
3394 if (shift) {
3395 if (!(es->style & ES_READONLY))
3396 EDIT_WM_Paste(hwnd, es);
3397 } else if (control)
3398 EDIT_WM_Copy(hwnd, es);
3399 break;
3400 case VK_RETURN:
3401 /* If the edit doesn't want the return send a message to the default object */
3402 if(!(es->style & ES_WANTRETURN))
3403 {
3404 HWND hwndParent = GetParent(hwnd);
3405 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
3406 if (HIWORD(dw) == DC_HASDEFID)
3407 {
3408 SendMessageA( hwndParent, WM_COMMAND,
3409 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3410 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3411 }
3412 }
3413 break;
3414 }
3415 return 0;
3416}
3417
3418
3419/*********************************************************************
3420 *
3421 * WM_KILLFOCUS
3422 *
3423 */
3424static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus)
3425{
3426 es->flags &= ~EF_FOCUSED;
3427 DestroyCaret();
3428 if(!(es->style & ES_NOHIDESEL))
3429 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3430 EDIT_NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
3431 return 0;
3432}
3433
3434
3435/*********************************************************************
3436 *
3437 * WM_LBUTTONDBLCLK
3438 *
3439 * The caret position has been set on the WM_LBUTTONDOWN message
3440 *
3441 */
3442static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3443{
3444 INT s;
3445 INT e = es->selection_end;
3446 INT l;
3447 INT li;
3448 INT ll;
3449
3450 if (!(es->flags & EF_FOCUSED))
3451 return 0;
3452
3453 l = EDIT_EM_LineFromChar(hwnd, es, e);
3454 li = EDIT_EM_LineIndex(hwnd, es, l);
3455 ll = EDIT_EM_LineLength(hwnd, es, e);
3456 s = li + EDIT_CallWordBreakProc (hwnd, es, li, e - li, ll, WB_LEFT);
3457 e = li + EDIT_CallWordBreakProc(hwnd, es, li, e - li, ll, WB_RIGHT);
3458 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
3459 EDIT_EM_ScrollCaret(hwnd, es);
3460 return 0;
3461}
3462
3463
3464/*********************************************************************
3465 *
3466 * WM_LBUTTONDOWN
3467 *
3468 */
3469static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3470{
3471 INT e;
3472 BOOL after_wrap;
3473
3474 if (!(es->flags & EF_FOCUSED))
3475 return 0;
3476
3477 es->bCaptureState = TRUE;
3478 SetCapture(hwnd);
3479 EDIT_ConfinePoint(hwnd, es, &x, &y);
3480 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3481 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3482 EDIT_EM_ScrollCaret(hwnd, es);
3483 es->region_posx = es->region_posy = 0;
3484 SetTimer(hwnd, 0, 100, NULL);
3485 return 0;
3486}
3487
3488
3489/*********************************************************************
3490 *
3491 * WM_LBUTTONUP
3492 *
3493 */
3494static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3495{
3496 if (es->bCaptureState)
3497 {
3498 KillTimer(hwnd,0);
3499 ReleaseCapture();
3500 }
3501 es->bCaptureState = FALSE;
3502
3503 return 0;
3504}
3505
3506static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es)
3507{
3508 if (es->bCaptureState) KillTimer(hwnd,0);
3509 es->bCaptureState = FALSE;
3510
3511 return 0;
3512}
3513
3514/*********************************************************************
3515 *
3516 * WM_MOUSEMOVE
3517 *
3518 */
3519static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3520{
3521 INT e;
3522 BOOL after_wrap;
3523 INT prex, prey;
3524
3525 if (!es->bCaptureState) return 0;
3526
3527 /*
3528 * FIXME: gotta do some scrolling if outside client
3529 * area. Maybe reset the timer ?
3530 */
3531 prex = x; prey = y;
3532 EDIT_ConfinePoint(hwnd, es, &x, &y);
3533 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3534 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3535 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3536 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
3537
3538 return 0;
3539}
3540
3541
3542/*********************************************************************
3543 *
3544 * WM_NCCREATE
3545 *
3546 */
3547static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs)
3548{
3549 EDITSTATE *es;
3550
3551 if (!(es = (EDITSTATE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3552 return FALSE;
3553 SetInfoPtr(hwnd,(DWORD)es);
3554
3555 /*
3556 * Note: since the EDITSTATE has not been fully initialized yet,
3557 * we can't use any API calls that may send
3558 * WM_XXX messages before WM_NCCREATE is completed.
3559 */
3560
3561 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3562 return FALSE;
3563 es->style = cs->style;
3564
3565 es->bEnableState = !(cs->style & WS_DISABLED);
3566
3567 /*
3568 * In Win95 look and feel, the WS_BORDER style is replaced by the
3569 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3570 * control a non client area.
3571 */
3572 if (es->style & WS_BORDER)
3573 {
3574 es->style &= ~WS_BORDER;
3575 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~WS_BORDER);
3576 SetWindowLongA(hwnd,GWL_EXSTYLE,GetWindowLongA(hwnd,GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3577 }
3578
3579 if (es->style & ES_MULTILINE) {
3580 es->buffer_size = BUFSTART_MULTI;
3581 es->buffer_limit = BUFLIMIT_MULTI;
3582 if (es->style & WS_VSCROLL)
3583 es->style |= ES_AUTOVSCROLL;
3584 if (es->style & WS_HSCROLL)
3585 es->style |= ES_AUTOHSCROLL;
3586 es->style &= ~ES_PASSWORD;
3587 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3588 if (es->style & ES_RIGHT)
3589 es->style &= ~ES_CENTER;
3590 es->style &= ~WS_HSCROLL;
3591 es->style &= ~ES_AUTOHSCROLL;
3592 }
3593
3594 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3595 es->style |= ES_AUTOVSCROLL;
3596 } else {
3597 es->buffer_size = BUFSTART_SINGLE;
3598 es->buffer_limit = BUFLIMIT_SINGLE;
3599 es->style &= ~ES_CENTER;
3600 es->style &= ~ES_RIGHT;
3601 es->style &= ~WS_HSCROLL;
3602 es->style &= ~WS_VSCROLL;
3603 es->style &= ~ES_AUTOVSCROLL;
3604 es->style &= ~ES_WANTRETURN;
3605 if (es->style & ES_UPPERCASE) {
3606 es->style &= ~ES_LOWERCASE;
3607 es->style &= ~ES_NUMBER;
3608 } else if (es->style & ES_LOWERCASE)
3609 es->style &= ~ES_NUMBER;
3610 if (es->style & ES_PASSWORD)
3611 es->password_char = '*';
3612
3613 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3614 es->style |= ES_AUTOHSCROLL;
3615 }
3616 if (!(es->text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3617 return FALSE;
3618 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3619 if (!(es->undo_text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3620 return FALSE;
3621 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3622 *es->text = '\0';
3623 if (es->style & ES_MULTILINE)
3624 if (!(es->first_line_def = (LINEDEF*)HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3625 return FALSE;
3626 es->line_count = 1;
3627
3628 return TRUE;
3629}
3630
3631static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc,BOOL eraseBkGnd)
3632{
3633 INT i;
3634 HFONT old_font = 0;
3635 RECT rc;
3636 RECT rcLine;
3637 RECT rcRgn;
3638 BOOL rev = es->bEnableState && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL));
3639
3640 HideCaret(hwnd);
3641
3642 if (eraseBkGnd)
3643 {
3644 HBRUSH brush;
3645
3646 if (!es->bEnableState || (es->style & ES_READONLY))
3647 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3648 else
3649 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, hdc);
3650
3651 if (!brush)
3652 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3653
3654 GetClientRect(hwnd, &rc);
3655 /*
3656 * FIXME: specs say that we should UnrealizeObject() the brush,
3657 * but the specs of UnrealizeObject() say that we shouldn't
3658 * unrealize a stock object. The default brush that
3659 * DefWndProc() returns is ... a stock object.
3660 */
3661 FillRect(hdc, &rc, brush);
3662 }
3663
3664 if(es->style & WS_BORDER)
3665 {
3666 GetClientRect(hwnd, &rc);
3667 if(es->style & ES_MULTILINE)
3668 {
3669 if(es->style & WS_HSCROLL) rc.bottom++;
3670 if(es->style & WS_VSCROLL) rc.right++;
3671 }
3672 Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
3673 }
3674
3675 IntersectClipRect(hdc, es->format_rect.left,
3676 es->format_rect.top,
3677 es->format_rect.right,
3678 es->format_rect.bottom);
3679 if (es->style & ES_MULTILINE)
3680 {
3681 GetClientRect(hwnd, &rc);
3682 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
3683 }
3684 if (es->font) old_font = SelectObject(hdc, es->font);
3685 if (!es->bEnableState || (es->style & ES_READONLY))
3686 EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3687 else
3688 EDIT_SEND_CTLCOLOR(hwnd, hdc);
3689 if (!es->bEnableState)
3690 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
3691 GetClipBox(hdc, &rcRgn);
3692 if (es->style & ES_MULTILINE)
3693 {
3694 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3695
3696 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++)
3697 {
3698 EDIT_GetLineRect(hwnd, es, i, 0, -1, &rcLine);
3699 if (IntersectRect(&rc, &rcRgn, &rcLine))
3700 EDIT_PaintLine(hwnd, es, hdc, i, rev);
3701 }
3702 } else
3703 {
3704 EDIT_GetLineRect(hwnd, es, 0, 0, -1, &rcLine);
3705 if (IntersectRect(&rc, &rcRgn, &rcLine))
3706 EDIT_PaintLine(hwnd, es, hdc, 0, rev);
3707 }
3708 if (es->font) SelectObject(hdc, old_font);
3709 if (es->flags & EF_FOCUSED)
3710 EDIT_SetCaretPos(hwnd, es, es->selection_end,es->flags & EF_AFTER_WRAP);
3711
3712 ShowCaret(hwnd);
3713
3714 if (es->flags & EF_UPDATE)
3715 {
3716 es->flags &= ~EF_UPDATE;
3717 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
3718 }
3719}
3720
3721static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es)
3722{
3723 HDC hdc,hdcCompatible;
3724 HBITMAP bitmap,oldbmp;
3725 RECT rect;
3726
3727 //CB: original controls redraws many times, cache drawing
3728 HideCaret(hwnd);
3729 GetClientRect(hwnd,&rect);
3730 hdc = GetDC(hwnd);
3731 hdcCompatible = CreateCompatibleDC(hdc);
3732 bitmap = CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
3733 oldbmp = SelectObject(hdcCompatible,bitmap);
3734 EDIT_Draw(hwnd,es,hdcCompatible,TRUE);
3735 SelectClipRgn(hdcCompatible,0);
3736 BitBlt(hdc,0,0,rect.right,rect.bottom,hdcCompatible,0,0,SRCCOPY);
3737 SelectObject(hdcCompatible,oldbmp);
3738 DeleteObject(bitmap);
3739 DeleteDC(hdcCompatible);
3740 ReleaseDC(hwnd,hdc);
3741 ShowCaret(hwnd);
3742}
3743
3744/*********************************************************************
3745 *
3746 * WM_PAINT
3747 *
3748 */
3749static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam)
3750{
3751 PAINTSTRUCT ps;
3752 HDC hdc;
3753
3754 if (!wParam) hdc = BeginPaint(hwnd, &ps);
3755 else hdc = (HDC) wParam;
3756
3757 EDIT_Draw(hwnd,es,hdc,FALSE);
3758
3759 if (!wParam) EndPaint(hwnd, &ps);
3760}
3761
3762
3763/*********************************************************************
3764 *
3765 * WM_PASTE
3766 *
3767 */
3768static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
3769{
3770 HGLOBAL hsrc;
3771 LPSTR src;
3772
3773 OpenClipboard(hwnd);
3774 hsrc = GetClipboardData(CF_TEXT);
3775 if (hsrc) {
3776 src = (LPSTR)GlobalLock(hsrc);
3777 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src);
3778 GlobalUnlock(hsrc);
3779 }
3780 CloseClipboard();
3781}
3782
3783
3784/*********************************************************************
3785 *
3786 * WM_SETFOCUS
3787 *
3788 */
3789static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus)
3790{
3791 es->flags |= EF_FOCUSED;
3792 CreateCaret(hwnd, 0, 2, es->line_height);
3793 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3794 es->flags & EF_AFTER_WRAP);
3795 if(!(es->style & ES_NOHIDESEL))
3796 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3797 ShowCaret(hwnd);
3798 EDIT_NOTIFY_PARENT(hwnd, EN_SETFOCUS);
3799}
3800
3801/*********************************************************************
3802 *
3803 * WM_SETFONT
3804 *
3805 * With Win95 look the margins are set to default font value unless
3806 * the system font (font == 0) is being set, in which case they are left
3807 * unchanged.
3808 *
3809 */
3810static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
3811{
3812 TEXTMETRICA tm;
3813 HDC dc;
3814 HFONT old_font = 0;
3815 RECT r;
3816
3817 dc = GetDC(hwnd);
3818 if (font)
3819 old_font = SelectObject(dc, font);
3820 if (!GetTextMetricsA(dc, &tm))
3821 {
3822 if (font) SelectObject(dc,old_font);
3823 ReleaseDC(hwnd,dc);
3824
3825 return;
3826 }
3827 es->font = font;
3828 es->line_height = tm.tmHeight;
3829
3830 es->char_width = tm.tmAveCharWidth;
3831 if (font)
3832 SelectObject(dc, old_font);
3833 ReleaseDC(hwnd, dc);
3834 if (font)
3835 EDIT_EM_SetMargins(hwnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3836 EC_USEFONTINFO, EC_USEFONTINFO);
3837 /* Force the recalculation of the format rect for each font change */
3838 GetClientRect(hwnd, &r);
3839 EDIT_SetRectNP(hwnd, es, &r);
3840 if (es->style & ES_MULTILINE)
3841 EDIT_BuildLineDefs_ML(hwnd, es);
3842
3843 if (redraw)
3844 EDIT_Refresh(hwnd,es);
3845 if (es->flags & EF_FOCUSED) {
3846 DestroyCaret();
3847 CreateCaret(hwnd, 0, 2, es->line_height);
3848 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3849 es->flags & EF_AFTER_WRAP);
3850 ShowCaret(hwnd);
3851 }
3852}
3853
3854
3855/*********************************************************************
3856 *
3857 * WM_SETTEXT
3858 *
3859 * NOTES
3860 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3861 * The modified flag is reset. No notifications are sent.
3862 *
3863 * For single-line controls, reception of WM_SETTEXT triggers:
3864 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3865 *
3866 */
3867static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text)
3868{
3869 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
3870 if (text) {
3871 //TRACE_(edit)("\t'%s'\n", text);
3872 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text);
3873 } else {
3874 //TRACE_(edit)("\t<NULL>\n");
3875 EDIT_EM_ReplaceSel(hwnd, es, FALSE, "");
3876 }
3877 es->x_offset = 0;
3878 if (es->style & ES_MULTILINE) {
3879 es->flags &= ~EF_UPDATE;
3880 } else {
3881 es->flags |= EF_UPDATE;
3882 }
3883 es->flags &= ~EF_MODIFIED;
3884 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
3885 EDIT_EM_ScrollCaret(hwnd, es);
3886 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
3887 if (es->flags & EF_UPDATE) EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
3888}
3889
3890
3891/*********************************************************************
3892 *
3893 * WM_SIZE
3894 *
3895 */
3896static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
3897{
3898 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3899 RECT rc;
3900 SetRect(&rc, 0, 0, width, height);
3901 EDIT_SetRectNP(hwnd, es, &rc);
3902 EDIT_Refresh(hwnd,es);
3903 }
3904}
3905
3906
3907/*********************************************************************
3908 *
3909 * WM_SYSKEYDOWN
3910 *
3911 */
3912static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3913{
3914 if ((key == VK_BACK) && (key_data & 0x2000)) {
3915 if (EDIT_EM_CanUndo(hwnd, es))
3916 EDIT_EM_Undo(hwnd, es);
3917 return 0;
3918 } else if (key == VK_UP || key == VK_DOWN)
3919 if (EDIT_CheckCombo(hwnd, WM_SYSKEYDOWN, key, key_data))
3920 return 0;
3921 return DefWindowProcA(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
3922}
3923
3924
3925/*********************************************************************
3926 *
3927 * WM_TIMER
3928 *
3929 */
3930static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
3931{
3932 if (es->region_posx < 0) {
3933 EDIT_MoveBackward(hwnd, es, TRUE);
3934 } else if (es->region_posx > 0) {
3935 EDIT_MoveForward(hwnd, es, TRUE);
3936 }
3937
3938 if (!(es->style & ES_MULTILINE)) return;
3939
3940 if (es->region_posy < 0)
3941 {
3942 EDIT_MoveUp_ML(hwnd,es,TRUE);
3943 } else if (es->region_posy > 0)
3944 {
3945 EDIT_MoveDown_ML(hwnd,es,TRUE);
3946 }
3947}
3948
3949
3950/*********************************************************************
3951 *
3952 * EDIT_VScroll_Hack
3953 *
3954 * 16 bit notepad needs this. Actually it is not _our_ hack,
3955 * it is notepad's. Notepad is sending us scrollbar messages with
3956 * undocumented parameters without us even having a scrollbar ... !?!?
3957 *
3958 */
3959static LRESULT EDIT_VScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3960{
3961 INT dy = 0;
3962 LRESULT ret = 0;
3963
3964 if (!(es->flags & EF_VSCROLL_HACK)) {
3965 //ERR_(edit)("hacked WM_VSCROLL handler invoked\n");
3966 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
3967 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
3968 es->flags |= EF_VSCROLL_HACK;
3969 }
3970
3971 switch (action) {
3972 case SB_LINEUP:
3973 case SB_LINEDOWN:
3974 case SB_PAGEUP:
3975 case SB_PAGEDOWN:
3976 EDIT_EM_Scroll(hwnd, es, action);
3977 return 0;
3978 case SB_TOP:
3979 dy = -es->y_offset;
3980 break;
3981 case SB_BOTTOM:
3982 dy = es->line_count - 1 - es->y_offset;
3983 break;
3984 case SB_THUMBTRACK:
3985 es->flags |= EF_VSCROLL_TRACK;
3986 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
3987 break;
3988 case SB_THUMBPOSITION:
3989 es->flags &= ~EF_VSCROLL_TRACK;
3990 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
3991 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
3992 break;
3993 case SB_ENDSCROLL:
3994 break;
3995
3996 default:
3997 //ERR_(edit)("undocumented (hacked) WM_VSCROLL parameter, please report\n");
3998 return 0;
3999 }
4000 if (dy)
4001 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4002 return ret;
4003}
4004
4005
4006/*********************************************************************
4007 *
4008 * WM_VSCROLL
4009 *
4010 */
4011static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4012{
4013 INT dy;
4014
4015 if (!(es->style & ES_MULTILINE))
4016 return 0;
4017
4018 if (!(es->style & ES_AUTOVSCROLL))
4019 return 0;
4020
4021 if (!(es->style & WS_VSCROLL))
4022 return EDIT_VScroll_Hack(hwnd, es, action, pos, scroll_bar);
4023
4024 dy = 0;
4025 switch (action) {
4026 case SB_LINEUP:
4027 case SB_LINEDOWN:
4028 case SB_PAGEUP:
4029 case SB_PAGEDOWN:
4030 EDIT_EM_Scroll(hwnd, es, action);
4031 return 0;
4032
4033 case SB_TOP:
4034 dy = -es->y_offset;
4035 break;
4036 case SB_BOTTOM:
4037 dy = es->line_count - 1 - es->y_offset;
4038 break;
4039 case SB_THUMBTRACK:
4040 es->flags |= EF_VSCROLL_TRACK;
4041 dy = pos - es->y_offset;
4042 break;
4043 case SB_THUMBPOSITION:
4044 es->flags &= ~EF_VSCROLL_TRACK;
4045 if (!(dy = pos - es->y_offset)) {
4046 SetScrollPos(hwnd, SB_VERT, pos, TRUE);
4047 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
4048 }
4049 break;
4050 case SB_ENDSCROLL:
4051 break;
4052
4053 default:
4054 //ERR_(edit)("undocumented WM_VSCROLL action %d, please report\n",
4055 // action);
4056 return 0;
4057 }
4058 if (dy)
4059 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4060 return 0;
4061}
4062
4063BOOL EDIT_Register()
4064{
4065 WNDCLASSA wndClass;
4066
4067//SvL: Don't check this now
4068// if (GlobalFindAtomA(EDITCLASSNAME)) return FALSE;
4069
4070 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
4071 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4072 wndClass.lpfnWndProc = (WNDPROC)EditWndProc;
4073 wndClass.cbClsExtra = 0;
4074 wndClass.cbWndExtra = sizeof(VOID*);
4075 wndClass.hCursor = LoadCursorA(0,IDC_IBEAMA);
4076 wndClass.hbrBackground = (HBRUSH)0;
4077 wndClass.lpszClassName = EDITCLASSNAME;
4078
4079 return RegisterClassA(&wndClass);
4080}
4081
4082BOOL EDIT_Unregister()
4083{
4084 if (GlobalFindAtomA(EDITCLASSNAME))
4085 return UnregisterClassA(EDITCLASSNAME,(HINSTANCE)NULL);
4086 else return FALSE;
4087}
Note: See TracBrowser for help on using the repository browser.