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

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

text output changes, desktop WM_GETTEXT fixed, scrollbar DC changes

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