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

Last change on this file since 1366 was 1366, checked in by phaller, 26 years ago

Fix: EDIT_WM_GetWindowText corrected according specs.

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