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

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

merged with WINE, other fixes

File size: 128.7 KB
Line 
1/* $Id: edit.cpp,v 1.2 1999-10-08 21:24:07 cbratschi Exp $ */
2/*
3 * Edit control
4 *
5 * Copyright David W. Metcalfe, 1994
6 * Copyright William Magro, 1995, 1996
7 * Copyright Frans van Dorsselaer, 1996, 1997
8 *
9 * Copyright 1999 Christoph Bratschi (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 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(hwnd, es, i));
1856 for (i = 0 ; i < len ; i++) {
1857 *lpch = *src;
1858 src++;
1859 lpch++;
1860 }
1861 return (LRESULT)len;
1862}
1863
1864
1865/*********************************************************************
1866 *
1867 * EM_GETSEL
1868 *
1869 */
1870static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end)
1871{
1872 UINT s = es->selection_start;
1873 UINT e = es->selection_end;
1874
1875 ORDER_UINT(s, e);
1876 if (start)
1877 *start = s;
1878 if (end)
1879 *end = e;
1880 return MAKELONG(s, e);
1881}
1882
1883
1884/*********************************************************************
1885 *
1886 * EM_GETTHUMB
1887 *
1888 * FIXME: is this right ? (or should it be only VSCROLL)
1889 * (and maybe only for edit controls that really have their
1890 * own scrollbars) (and maybe only for multiline controls ?)
1891 * All in all: very poorly documented
1892 *
1893 * FIXME: now it's also broken, because of the new WM_HSCROLL /
1894 * WM_VSCROLL handlers
1895 *
1896 */
1897static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
1898{
1899 return MAKELONG(EDIT_WM_VScroll(hwnd, es, EM_GETTHUMB, 0, 0),
1900 EDIT_WM_HScroll(hwnd, es, EM_GETTHUMB, 0, 0));
1901}
1902
1903
1904/*********************************************************************
1905 *
1906 * EM_LINEFROMCHAR
1907 *
1908 */
1909static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index)
1910{
1911 INT line;
1912 LINEDEF *line_def;
1913
1914 if (!(es->style & ES_MULTILINE))
1915 return 0;
1916 if (index > lstrlenA(es->text))
1917 return es->line_count - 1;
1918 if (index == -1)
1919 index = MIN(es->selection_start, es->selection_end);
1920
1921 line = 0;
1922 line_def = es->first_line_def;
1923 index -= line_def->length;
1924 while ((index >= 0) && line_def->next) {
1925 line++;
1926 line_def = line_def->next;
1927 index -= line_def->length;
1928 }
1929 return line;
1930}
1931
1932
1933/*********************************************************************
1934 *
1935 * EM_LINEINDEX
1936 *
1937 */
1938static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line)
1939{
1940 INT line_index;
1941 LINEDEF *line_def;
1942
1943 if (!(es->style & ES_MULTILINE))
1944 return 0;
1945 if (line >= es->line_count)
1946 return -1;
1947
1948 line_index = 0;
1949 line_def = es->first_line_def;
1950 if (line == -1) {
1951 INT index = es->selection_end - line_def->length;
1952 while ((index >= 0) && line_def->next) {
1953 line_index += line_def->length;
1954 line_def = line_def->next;
1955 index -= line_def->length;
1956 }
1957 } else {
1958 while (line > 0) {
1959 line_index += line_def->length;
1960 line_def = line_def->next;
1961 line--;
1962 }
1963 }
1964 return line_index;
1965}
1966
1967
1968/*********************************************************************
1969 *
1970 * EM_LINELENGTH
1971 *
1972 */
1973static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index)
1974{
1975 LINEDEF *line_def;
1976
1977 if (!(es->style & ES_MULTILINE))
1978 return lstrlenA(es->text);
1979
1980 if (index == -1) {
1981 /* FIXME: broken
1982 INT32 sl = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
1983 INT32 el = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
1984 return es->selection_start - es->line_defs[sl].offset +
1985 es->line_defs[el].offset +
1986 es->line_defs[el].length - es->selection_end;
1987 */
1988 return 0;
1989 }
1990 line_def = es->first_line_def;
1991 index -= line_def->length;
1992 while ((index >= 0) && line_def->next) {
1993 line_def = line_def->next;
1994 index -= line_def->length;
1995 }
1996 return line_def->net_length;
1997}
1998
1999
2000/*********************************************************************
2001 *
2002 * EM_LINESCROLL
2003 *
2004 * FIXME: dx is in average character widths
2005 * However, we assume it is in pixels when we use this
2006 * function internally
2007 *
2008 */
2009static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2010{
2011 INT nyoff;
2012
2013 if (!(es->style & ES_MULTILINE))
2014 return FALSE;
2015
2016 if (-dx > es->x_offset)
2017 dx = -es->x_offset;
2018 if (dx > es->text_width - es->x_offset)
2019 dx = es->text_width - es->x_offset;
2020 nyoff = MAX(0, es->y_offset + dy);
2021 if (nyoff >= es->line_count)
2022 nyoff = es->line_count - 1;
2023 dy = (es->y_offset - nyoff) * es->line_height;
2024 if (dx || dy) {
2025 RECT rc1;
2026 RECT rc;
2027 GetClientRect(hwnd, &rc1);
2028 IntersectRect(&rc, &rc1, &es->format_rect);
2029 ScrollWindowEx(hwnd, -dx, dy,
2030 NULL, &rc, (HRGN)NULL, NULL, SW_INVALIDATE);
2031 es->y_offset = nyoff;
2032 es->x_offset += dx;
2033 }
2034 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2035 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL, "EN_HSCROLL");
2036 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2037 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL, "EN_VSCROLL");
2038 return TRUE;
2039}
2040
2041
2042/*********************************************************************
2043 *
2044 * EM_POSFROMCHAR
2045 *
2046 */
2047static LRESULT EDIT_EM_PosFromChar(HWND hwnd, EDITSTATE *es, INT index, BOOL after_wrap)
2048{
2049 INT len = lstrlenA(es->text);
2050 INT l;
2051 INT li;
2052 INT x;
2053 INT y = 0;
2054 HDC dc;
2055 HFONT old_font = 0;
2056 SIZE size;
2057
2058 index = MIN(index, len);
2059 dc = GetDC(hwnd);
2060 if (es->font)
2061 old_font = SelectObject(dc, es->font);
2062 if (es->style & ES_MULTILINE) {
2063 l = EDIT_EM_LineFromChar(hwnd, es, index);
2064 y = (l - es->y_offset) * es->line_height;
2065 li = EDIT_EM_LineIndex(hwnd, es, l);
2066 if (after_wrap && (li == index) && l) {
2067 INT l2 = l - 1;
2068 LINEDEF *line_def = es->first_line_def;
2069 while (l2) {
2070 line_def = line_def->next;
2071 l2--;
2072 }
2073 if (line_def->ending == END_WRAP) {
2074 l--;
2075 y -= es->line_height;
2076 li = EDIT_EM_LineIndex(hwnd, es, l);
2077 }
2078 }
2079 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2080 es->tabs_count, es->tabs)) - es->x_offset;
2081 } else {
2082 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
2083 if (index < es->x_offset) {
2084 GetTextExtentPoint32A(dc, text + index,
2085 es->x_offset - index, &size);
2086 x = -size.cx;
2087 } else {
2088 GetTextExtentPoint32A(dc, text + es->x_offset,
2089 index - es->x_offset, &size);
2090 x = size.cx;
2091 }
2092 y = 0;
2093 if (es->style & ES_PASSWORD)
2094 HeapFree(es->heap, 0 ,text);
2095 }
2096 x += es->format_rect.left;
2097 y += es->format_rect.top;
2098 if (es->font)
2099 SelectObject(dc, old_font);
2100 ReleaseDC(hwnd, dc);
2101 return MAKELONG((INT16)x, (INT16)y);
2102}
2103
2104
2105/*********************************************************************
2106 *
2107 * EM_REPLACESEL
2108 *
2109 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2110 *
2111 */
2112static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
2113{
2114 INT strl = lstrlenA(lpsz_replace);
2115 INT tl = lstrlenA(es->text);
2116 INT utl;
2117 UINT s;
2118 UINT e;
2119 INT i;
2120 LPSTR p;
2121
2122 s = es->selection_start;
2123 e = es->selection_end;
2124
2125 if ((s == e) && !strl)
2126 return;
2127
2128 ORDER_UINT(s, e);
2129
2130 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
2131 return;
2132
2133 if (e != s) {
2134 /* there is something to be deleted */
2135 if (can_undo) {
2136 utl = lstrlenA(es->undo_text);
2137 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2138 /* undo-buffer is extended to the right */
2139 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2140 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2141 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2142 /* undo-buffer is extended to the left */
2143 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2144 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2145 p[e - s] = p[0];
2146 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2147 p[i] = (es->text + s)[i];
2148 es->undo_position = s;
2149 } else {
2150 /* new undo-buffer */
2151 EDIT_MakeUndoFit(hwnd, es, e - s);
2152 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2153 es->undo_position = s;
2154 }
2155 /* any deletion makes the old insertion-undo invalid */
2156 es->undo_insert_count = 0;
2157 } else
2158 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2159
2160 /* now delete */
2161 lstrcpyA(es->text + s, es->text + e);
2162 }
2163 if (strl) {
2164 /* there is an insertion */
2165 if (can_undo) {
2166 if ((s == es->undo_position) ||
2167 ((es->undo_insert_count) &&
2168 (s == es->undo_position + es->undo_insert_count)))
2169 /*
2170 * insertion is new and at delete position or
2171 * an extension to either left or right
2172 */
2173 es->undo_insert_count += strl;
2174 else {
2175 /* new insertion undo */
2176 es->undo_position = s;
2177 es->undo_insert_count = strl;
2178 /* new insertion makes old delete-buffer invalid */
2179 *es->undo_text = '\0';
2180 }
2181 } else
2182 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2183
2184 /* now insert */
2185 tl = lstrlenA(es->text);
2186 for (p = es->text + tl ; p >= es->text + s ; p--)
2187 p[strl] = p[0];
2188 for (i = 0 , p = es->text + s ; i < strl ; i++)
2189 p[i] = lpsz_replace[i];
2190 if(es->style & ES_UPPERCASE)
2191 CharUpperBuffA(p, strl);
2192 else if(es->style & ES_LOWERCASE)
2193 CharLowerBuffA(p, strl);
2194 s += strl;
2195 }
2196 /* FIXME: really inefficient */
2197 if (es->style & ES_MULTILINE)
2198 EDIT_BuildLineDefs_ML(hwnd, es);
2199
2200 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
2201 es->flags |= EF_MODIFIED;
2202 es->flags |= EF_UPDATE;
2203 EDIT_EM_ScrollCaret(hwnd, es);
2204
2205 /* FIXME: really inefficient */
2206 InvalidateRect(hwnd, NULL, TRUE);
2207}
2208
2209
2210/*********************************************************************
2211 *
2212 * EM_SCROLL
2213 *
2214 */
2215static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
2216{
2217 INT dy;
2218
2219 if (!(es->style & ES_MULTILINE))
2220 return (LRESULT)FALSE;
2221
2222 dy = 0;
2223
2224 switch (action) {
2225 case SB_LINEUP:
2226 if (es->y_offset)
2227 dy = -1;
2228 break;
2229 case SB_LINEDOWN:
2230 if (es->y_offset < es->line_count - 1)
2231 dy = 1;
2232 break;
2233 case SB_PAGEUP:
2234 if (es->y_offset)
2235 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2236 break;
2237 case SB_PAGEDOWN:
2238 if (es->y_offset < es->line_count - 1)
2239 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2240 break;
2241 default:
2242 return (LRESULT)FALSE;
2243 }
2244 if (dy) {
2245 EDIT_EM_LineScroll(hwnd, es, 0, dy);
2246 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL, "EN_VSCROLL");
2247 }
2248 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2249}
2250
2251
2252/*********************************************************************
2253 *
2254 * EM_SCROLLCARET
2255 *
2256 */
2257static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
2258{
2259 if (es->style & ES_MULTILINE) {
2260 INT l;
2261 INT li;
2262 INT vlc;
2263 INT ww;
2264 INT cw = es->char_width;
2265 INT x;
2266 INT dy = 0;
2267 INT dx = 0;
2268
2269 l = EDIT_EM_LineFromChar(hwnd, es, es->selection_end);
2270 li = EDIT_EM_LineIndex(hwnd, es, l);
2271 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2272 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2273 if (l >= es->y_offset + vlc)
2274 dy = l - vlc + 1 - es->y_offset;
2275 if (l < es->y_offset)
2276 dy = l - es->y_offset;
2277 ww = es->format_rect.right - es->format_rect.left;
2278 if (x < es->format_rect.left)
2279 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2280 if (x > es->format_rect.right)
2281 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2282 if (dy || dx)
2283 EDIT_EM_LineScroll(hwnd, es, dx, dy);
2284 } else {
2285 INT x;
2286 INT goal;
2287 INT format_width;
2288
2289 if (!(es->style & ES_AUTOHSCROLL))
2290 return;
2291
2292 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2293 format_width = es->format_rect.right - es->format_rect.left;
2294 if (x < es->format_rect.left) {
2295 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2296 do {
2297 es->x_offset--;
2298 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2299 } while ((x < goal) && es->x_offset);
2300 /* FIXME: use ScrollWindow() somehow to improve performance */
2301 InvalidateRect(hwnd, NULL, TRUE);
2302 } else if (x > es->format_rect.right) {
2303 INT x_last;
2304 INT len = lstrlenA(es->text);
2305 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2306 do {
2307 es->x_offset++;
2308 x = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, es->selection_end, FALSE));
2309 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd, es, len, FALSE));
2310 } while ((x > goal) && (x_last > es->format_rect.right));
2311 /* FIXME: use ScrollWindow() somehow to improve performance */
2312 InvalidateRect(hwnd, NULL, TRUE);
2313 }
2314 }
2315}
2316
2317
2318/*********************************************************************
2319 *
2320 * EM_SETHANDLE
2321 *
2322 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2323 *
2324 */
2325static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
2326{
2327 if (!(es->style & ES_MULTILINE))
2328 return;
2329
2330 if (!hloc) {
2331 //WARN_(edit)("called with NULL handle\n");
2332 return;
2333 }
2334
2335 EDIT_UnlockBuffer(hwnd, es, TRUE);
2336 /*
2337 * old buffer is freed by caller, unless
2338 * it is still in our own heap. (in that case
2339 * we free it, correcting the buggy caller.)
2340 */
2341 if (es->text)
2342 HeapFree(es->heap, 0, es->text);
2343
2344 es->hloc = hloc;
2345 es->text = NULL;
2346 es->buffer_size = LocalSize(es->hloc) - 1;
2347 EDIT_LockBuffer(hwnd, es);
2348
2349 es->x_offset = es->y_offset = 0;
2350 es->selection_start = es->selection_end = 0;
2351 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2352 es->flags &= ~EF_MODIFIED;
2353 es->flags &= ~EF_UPDATE;
2354 EDIT_BuildLineDefs_ML(hwnd, es);
2355 InvalidateRect(hwnd, NULL, TRUE);
2356 EDIT_EM_ScrollCaret(hwnd, es);
2357}
2358
2359/*********************************************************************
2360 *
2361 * EM_SETLIMITTEXT
2362 *
2363 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2364 * However, the windows version is not complied to yet in all of edit.c
2365 *
2366 */
2367static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit)
2368{
2369 if (es->style & ES_MULTILINE) {
2370 if (limit)
2371 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2372 else
2373 es->buffer_limit = BUFLIMIT_MULTI;
2374 } else {
2375 if (limit)
2376 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2377 else
2378 es->buffer_limit = BUFLIMIT_SINGLE;
2379 }
2380}
2381
2382
2383/*********************************************************************
2384 *
2385 * EM_SETMARGINS
2386 *
2387 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2388 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2389 * of the char's width, according to the new docs.
2390 *
2391 */
2392static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action,
2393 INT left, INT right)
2394{
2395 if (action & EC_LEFTMARGIN) {
2396 if (left != EC_USEFONTINFO)
2397 es->left_margin = left;
2398 else
2399 es->left_margin = es->char_width / 3;
2400 }
2401
2402 if (action & EC_RIGHTMARGIN) {
2403 if (right != EC_USEFONTINFO)
2404 es->right_margin = right;
2405 else
2406 es->right_margin = es->char_width / 3;
2407 }
2408 //TRACE_(edit)("left=%d, right=%d\n", es->left_margin, es->right_margin);
2409}
2410
2411
2412/*********************************************************************
2413 *
2414 * EM_SETPASSWORDCHAR
2415 *
2416 */
2417static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c)
2418{
2419 if (es->style & ES_MULTILINE)
2420 return;
2421
2422 if (es->password_char == c)
2423 return;
2424
2425 es->password_char = c;
2426 if (c) {
2427 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_PASSWORD);
2428 es->style |= ES_PASSWORD;
2429 } else {
2430 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_PASSWORD);
2431 es->style &= ~ES_PASSWORD;
2432 }
2433 InvalidateRect(hwnd, NULL, TRUE);
2434}
2435
2436
2437/*********************************************************************
2438 *
2439 * EDIT_EM_SetSel
2440 *
2441 * note: unlike the specs say: the order of start and end
2442 * _is_ preserved in Windows. (i.e. start can be > end)
2443 * In other words: this handler is OK
2444 *
2445 */
2446static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2447{
2448 UINT old_start = es->selection_start;
2449 UINT old_end = es->selection_end;
2450 UINT len = lstrlenA(es->text);
2451
2452 if (start == -1) {
2453 start = es->selection_end;
2454 end = es->selection_end;
2455 } else {
2456 start = MIN(start, len);
2457 end = MIN(end, len);
2458 }
2459 es->selection_start = start;
2460 es->selection_end = end;
2461 if (after_wrap)
2462 es->flags |= EF_AFTER_WRAP;
2463 else
2464 es->flags &= ~EF_AFTER_WRAP;
2465 if (es->flags & EF_FOCUSED)
2466 EDIT_SetCaretPos(hwnd, es, end, after_wrap);
2467/* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2468 ORDER_UINT(start, end);
2469 ORDER_UINT(end, old_end);
2470 ORDER_UINT(start, old_start);
2471 ORDER_UINT(old_start, old_end);
2472 if (end != old_start)
2473 {
2474/*
2475 * One can also do
2476 * ORDER_UINT32(end, old_start);
2477 * EDIT_InvalidateText(wnd, es, start, end);
2478 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2479 * in place of the following if statement.
2480 */
2481 if (old_start > end )
2482 {
2483 EDIT_InvalidateText(hwnd, es, start, end);
2484 EDIT_InvalidateText(hwnd, es, old_start, old_end);
2485 }
2486 else
2487 {
2488 EDIT_InvalidateText(hwnd, es, start, old_start);
2489 EDIT_InvalidateText(hwnd, es, end, old_end);
2490 }
2491 }
2492 else EDIT_InvalidateText(hwnd, es, start, old_end);
2493}
2494
2495
2496/*********************************************************************
2497 *
2498 * EM_SETTABSTOPS
2499 *
2500 */
2501static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs)
2502{
2503 if (!(es->style & ES_MULTILINE))
2504 return FALSE;
2505 if (es->tabs)
2506 HeapFree(es->heap, 0, es->tabs);
2507 es->tabs_count = count;
2508 if (!count)
2509 es->tabs = NULL;
2510 else {
2511 es->tabs = (INT*)HeapAlloc(es->heap, 0, count * sizeof(INT));
2512 memcpy(es->tabs, tabs, count * sizeof(INT));
2513 }
2514 return TRUE;
2515}
2516
2517
2518/*********************************************************************
2519 *
2520 * EM_SETWORDBREAKPROC
2521 *
2522 */
2523static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2524{
2525 if (es->word_break_procA == wbp)
2526 return;
2527
2528 es->word_break_procA = wbp;
2529 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2530 EDIT_BuildLineDefs_ML(hwnd, es);
2531 InvalidateRect(hwnd, NULL, TRUE);
2532 }
2533}
2534
2535
2536/*********************************************************************
2537 *
2538 * EM_UNDO / WM_UNDO
2539 *
2540 */
2541static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
2542{
2543 INT ulength = lstrlenA(es->undo_text);
2544 LPSTR utext = (LPSTR)HeapAlloc(es->heap, 0, ulength + 1);
2545
2546 lstrcpyA(utext, es->undo_text);
2547
2548 //TRACE_(edit)("before UNDO:insertion length = %d, deletion buffer = %s\n",
2549 // es->undo_insert_count, utext);
2550
2551 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2552 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2553 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext);
2554 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2555 HeapFree(es->heap, 0, utext);
2556
2557 //TRACE_(edit)("after UNDO:insertion length = %d, deletion buffer = %s\n",
2558 // es->undo_insert_count, es->undo_text);
2559
2560 return TRUE;
2561}
2562
2563
2564/*********************************************************************
2565 *
2566 * WM_CHAR
2567 *
2568 */
2569static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data)
2570{
2571 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
2572 switch (c) {
2573 case '\r':
2574 /* If the edit doesn't want the return, do nothing */
2575 if(!(es->style & ES_WANTRETURN))
2576 break;
2577 case '\n':
2578 if (es->style & ES_MULTILINE) {
2579 if (es->style & ES_READONLY) {
2580 EDIT_MoveHome(hwnd, es, FALSE);
2581 EDIT_MoveDown_ML(hwnd, es, FALSE);
2582 } else
2583 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\r\n");
2584 }
2585 break;
2586 case '\t':
2587 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2588 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\t");
2589 break;
2590 case VK_BACK:
2591 if (!(es->style & ES_READONLY) && !control) {
2592 if (es->selection_start != es->selection_end)
2593 EDIT_WM_Clear(hwnd, es);
2594 else {
2595 /* delete character left of caret */
2596 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
2597 EDIT_MoveBackward(hwnd, es, TRUE);
2598 EDIT_WM_Clear(hwnd, es);
2599 }
2600 }
2601 break;
2602 default:
2603 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
2604 char str[2];
2605 str[0] = c;
2606 str[1] = '\0';
2607 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str);
2608 }
2609 break;
2610 }
2611}
2612
2613
2614/*********************************************************************
2615 *
2616 * WM_COMMAND
2617 *
2618 */
2619static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
2620{
2621 if (code || control)
2622 return;
2623
2624 switch (id) {
2625 case EM_UNDO:
2626 EDIT_EM_Undo(hwnd, es);
2627 break;
2628 case WM_CUT:
2629 EDIT_WM_Cut(hwnd, es);
2630 break;
2631 case WM_COPY:
2632 EDIT_WM_Copy(hwnd, es);
2633 break;
2634 case WM_PASTE:
2635 EDIT_WM_Paste(hwnd, es);
2636 break;
2637 case WM_CLEAR:
2638 EDIT_WM_Clear(hwnd, es);
2639 break;
2640 case EM_SETSEL:
2641 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
2642 EDIT_EM_ScrollCaret(hwnd, es);
2643 break;
2644 default:
2645 //ERR_(edit)("unknown menu item, please report\n");
2646 break;
2647 }
2648}
2649
2650
2651/*********************************************************************
2652 *
2653 * WM_CONTEXTMENU
2654 *
2655 * Note: the resource files resource/sysres_??.rc cannot define a
2656 * single popup menu. Hence we use a (dummy) menubar
2657 * containing the single popup menu as its first item.
2658 *
2659 * FIXME: the message identifiers have been chosen arbitrarily,
2660 * hence we use MF_BYPOSITION.
2661 * We might as well use the "real" values (anybody knows ?)
2662 * The menu definition is in resources/sysres_??.rc.
2663 * Once these are OK, we better use MF_BYCOMMAND here
2664 * (as we do in EDIT_WM_Command()).
2665 *
2666 */
2667static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y)
2668{
2669//CB: implement native menu!
2670 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
2671 HMENU popup = GetSubMenu(menu, 0);
2672 UINT start = es->selection_start;
2673 UINT end = es->selection_end;
2674
2675 ORDER_UINT(start, end);
2676
2677 /* undo */
2678 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(hwnd, es) ? MF_ENABLED : MF_GRAYED));
2679 /* cut */
2680 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2681 /* copy */
2682 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
2683 /* paste */
2684 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
2685 /* delete */
2686 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
2687 /* select all */
2688 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
2689
2690 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
2691 DestroyMenu(menu);
2692}
2693
2694
2695/*********************************************************************
2696 *
2697 * WM_COPY
2698 *
2699 */
2700static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
2701{
2702 INT s = es->selection_start;
2703 INT e = es->selection_end;
2704 HGLOBAL hdst;
2705 LPSTR dst;
2706
2707 if (e == s)
2708 return;
2709 ORDER_INT(s, e);
2710 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
2711 dst = (LPSTR)GlobalLock(hdst);
2712 lstrcpynA(dst, es->text + s, e - s + 1);
2713 GlobalUnlock(hdst);
2714 OpenClipboard(hwnd);
2715 EmptyClipboard();
2716 SetClipboardData(CF_TEXT, hdst);
2717 CloseClipboard();
2718}
2719
2720
2721/*********************************************************************
2722 *
2723 * WM_CREATE
2724 *
2725 */
2726static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs)
2727{
2728 /*
2729 * To initialize some final structure members, we call some helper
2730 * functions. However, since the EDITSTATE is not consistent (i.e.
2731 * not fully initialized), we should be very careful which
2732 * functions can be called, and in what order.
2733 */
2734 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
2735 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2736 if (cs->lpszName && *(cs->lpszName) != '\0') {
2737 EDIT_EM_ReplaceSel(hwnd, es, FALSE, cs->lpszName);
2738 /* if we insert text to the editline, the text scrolls out
2739 * of the window, as the caret is placed after the insert
2740 * pos normally; thus we reset es->selection... to 0 and
2741 * update caret
2742 */
2743 es->selection_start = es->selection_end = 0;
2744 EDIT_EM_ScrollCaret(hwnd, es);
2745 }
2746 return 0;
2747}
2748
2749
2750/*********************************************************************
2751 *
2752 * WM_DESTROY
2753 *
2754 */
2755static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
2756{
2757 if (es->hloc) {
2758 while (LocalUnlock(es->hloc)) ;
2759 LocalFree(es->hloc);
2760 }
2761
2762 HeapDestroy(es->heap);
2763 HeapFree(GetProcessHeap(), 0, es);
2764 SetInfoPtr(hwnd,0);
2765}
2766
2767
2768/*********************************************************************
2769 *
2770 * WM_ERASEBKGND
2771 *
2772 */
2773static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
2774{
2775 HBRUSH brush;
2776 RECT rc;
2777
2778 if (!es->bEnableState || (es->style & ES_READONLY))
2779 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
2780 else
2781 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
2782
2783 if (!brush)
2784 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
2785
2786 GetClientRect(hwnd, &rc);
2787 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
2788 GetClipBox(dc, &rc);
2789 /*
2790 * FIXME: specs say that we should UnrealizeObject() the brush,
2791 * but the specs of UnrealizeObject() say that we shouldn't
2792 * unrealize a stock object. The default brush that
2793 * DefWndProc() returns is ... a stock object.
2794 */
2795 FillRect(dc, &rc, brush);
2796 return -1;
2797}
2798
2799
2800/*********************************************************************
2801 *
2802 * WM_GETTEXT
2803 *
2804 */
2805static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text)
2806{
2807 INT len = lstrlenA(es->text);
2808
2809 if (count > len) {
2810 lstrcpyA(text, es->text);
2811 return len;
2812 } else
2813 return -1;
2814}
2815
2816
2817/*********************************************************************
2818 *
2819 * EDIT_HScroll_Hack
2820 *
2821 * 16 bit notepad needs this. Actually it is not _our_ hack,
2822 * it is notepad's. Notepad is sending us scrollbar messages with
2823 * undocumented parameters without us even having a scrollbar ... !?!?
2824 *
2825 */
2826static LRESULT EDIT_HScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
2827{
2828 INT dx = 0;
2829 INT fw = es->format_rect.right - es->format_rect.left;
2830 LRESULT ret = 0;
2831
2832 if (!(es->flags & EF_HSCROLL_HACK)) {
2833 //ERR_(edit)("hacked WM_HSCROLL handler invoked\n");
2834 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
2835 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
2836 es->flags |= EF_HSCROLL_HACK;
2837 }
2838
2839 switch (action) {
2840 case SB_LINELEFT:
2841 if (es->x_offset)
2842 dx = -es->char_width;
2843 break;
2844 case SB_LINERIGHT:
2845 if (es->x_offset < es->text_width)
2846 dx = es->char_width;
2847 break;
2848 case SB_PAGELEFT:
2849 if (es->x_offset)
2850 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
2851 break;
2852 case SB_PAGERIGHT:
2853 if (es->x_offset < es->text_width)
2854 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
2855 break;
2856 case SB_LEFT:
2857 if (es->x_offset)
2858 dx = -es->x_offset;
2859 break;
2860 case SB_RIGHT:
2861 if (es->x_offset < es->text_width)
2862 dx = es->text_width - es->x_offset;
2863 break;
2864 case SB_THUMBTRACK:
2865 es->flags |= EF_HSCROLL_TRACK;
2866 dx = pos * es->text_width / 100 - es->x_offset;
2867 break;
2868 case SB_THUMBPOSITION:
2869 es->flags &= ~EF_HSCROLL_TRACK;
2870 if (!(dx = pos * es->text_width / 100 - es->x_offset))
2871 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL, "EN_HSCROLL");
2872 break;
2873 case SB_ENDSCROLL:
2874 break;
2875
2876 default:
2877 //ERR_(edit)("undocumented (hacked) WM_HSCROLL parameter, please report\n");
2878 return 0;
2879 }
2880 if (dx)
2881 EDIT_EM_LineScroll(hwnd, es, dx, 0);
2882 return ret;
2883}
2884
2885
2886/*********************************************************************
2887 *
2888 * WM_HSCROLL
2889 *
2890 */
2891static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
2892{
2893 INT dx;
2894 INT fw;
2895
2896 if (!(es->style & ES_MULTILINE))
2897 return 0;
2898
2899 if (!(es->style & ES_AUTOHSCROLL))
2900 return 0;
2901
2902 if (!(es->style & WS_HSCROLL))
2903 return EDIT_HScroll_Hack(hwnd, es, action, pos, scroll_bar);
2904
2905 dx = 0;
2906 fw = es->format_rect.right - es->format_rect.left;
2907 switch (action) {
2908 case SB_LINELEFT:
2909 if (es->x_offset)
2910 dx = -es->char_width;
2911 break;
2912 case SB_LINERIGHT:
2913 if (es->x_offset < es->text_width)
2914 dx = es->char_width;
2915 break;
2916 case SB_PAGELEFT:
2917 if (es->x_offset)
2918 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
2919 break;
2920 case SB_PAGERIGHT:
2921 if (es->x_offset < es->text_width)
2922 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
2923 break;
2924 case SB_LEFT:
2925 if (es->x_offset)
2926 dx = -es->x_offset;
2927 break;
2928 case SB_RIGHT:
2929 if (es->x_offset < es->text_width)
2930 dx = es->text_width - es->x_offset;
2931 break;
2932 case SB_THUMBTRACK:
2933 es->flags |= EF_HSCROLL_TRACK;
2934 dx = pos - es->x_offset;
2935 break;
2936 case SB_THUMBPOSITION:
2937 es->flags &= ~EF_HSCROLL_TRACK;
2938 if (!(dx = pos - es->x_offset)) {
2939 SetScrollPos(hwnd, SB_HORZ, pos, TRUE);
2940 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL, "EN_HSCROLL");
2941 }
2942 break;
2943 case SB_ENDSCROLL:
2944 break;
2945
2946 default:
2947 //ERR_(edit)("undocumented WM_HSCROLL parameter, please report\n");
2948 return 0;
2949 }
2950 if (dx)
2951 EDIT_EM_LineScroll(hwnd, es, dx, 0);
2952 return 0;
2953}
2954
2955
2956/*********************************************************************
2957 *
2958 * EDIT_CheckCombo
2959 *
2960 */
2961static BOOL EDIT_CheckCombo(HWND hwnd, UINT msg, INT key, DWORD key_data)
2962{
2963 HWND hLBox;
2964
2965 if (GetClassWord(GetParent(hwnd),GCW_ATOM) == GlobalFindAtomA(COMBOBOXCLASSNAME) &&
2966 (hLBox = COMBO_GetLBWindow(GetParent(hwnd)))) {
2967 HWND hCombo = GetParent(hwnd);
2968 BOOL bUIFlip = TRUE;
2969
2970 //TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
2971 // wnd->hwndSelf, (UINT16)msg, (UINT16)key);
2972
2973 switch (msg) {
2974 case WM_KEYDOWN: /* Handle F4 and arrow keys */
2975 if (key != VK_F4) {
2976 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
2977 if (SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0))
2978 bUIFlip = FALSE;
2979 }
2980 if (!bUIFlip)
2981 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
2982 else {
2983 /* make sure ComboLBox pops up */
2984 SendMessageA(hCombo, CB_SETEXTENDEDUI, 0, 0);
2985 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
2986 SendMessageA(hCombo, CB_SETEXTENDEDUI, 1, 0);
2987 }
2988 break;
2989 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
2990 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
2991 if (bUIFlip) {
2992 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
2993 SendMessageA(hCombo, CB_SHOWDROPDOWN, (bUIFlip) ? FALSE : TRUE, 0);
2994 } else
2995 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
2996 break;
2997 }
2998 return TRUE;
2999 }
3000 return FALSE;
3001}
3002
3003
3004/*********************************************************************
3005 *
3006 * WM_KEYDOWN
3007 *
3008 * Handling of special keys that don't produce a WM_CHAR
3009 * (i.e. non-printable keys) & Backspace & Delete
3010 *
3011 */
3012static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3013{
3014 BOOL shift;
3015 BOOL control;
3016
3017 if (GetKeyState(VK_MENU) & 0x8000)
3018 return 0;
3019
3020 shift = GetKeyState(VK_SHIFT) & 0x8000;
3021 control = GetKeyState(VK_CONTROL) & 0x8000;
3022
3023 switch (key) {
3024 case VK_F4:
3025 case VK_UP:
3026 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3027 break;
3028 if (key == VK_F4)
3029 break;
3030 /* fall through */
3031 case VK_LEFT:
3032 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3033 EDIT_MoveUp_ML(hwnd, es, shift);
3034 else
3035 if (control)
3036 EDIT_MoveWordBackward(hwnd, es, shift);
3037 else
3038 EDIT_MoveBackward(hwnd, es, shift);
3039 break;
3040 case VK_DOWN:
3041 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3042 break;
3043 /* fall through */
3044 case VK_RIGHT:
3045 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3046 EDIT_MoveDown_ML(hwnd, es, shift);
3047 else if (control)
3048 EDIT_MoveWordForward(hwnd, es, shift);
3049 else
3050 EDIT_MoveForward(hwnd, es, shift);
3051 break;
3052 case VK_HOME:
3053 EDIT_MoveHome(hwnd, es, shift);
3054 break;
3055 case VK_END:
3056 EDIT_MoveEnd(hwnd, es, shift);
3057 break;
3058 case VK_PRIOR:
3059 if (es->style & ES_MULTILINE)
3060 EDIT_MovePageUp_ML(hwnd, es, shift);
3061 break;
3062 case VK_NEXT:
3063 if (es->style & ES_MULTILINE)
3064 EDIT_MovePageDown_ML(hwnd, es, shift);
3065 break;
3066 case VK_DELETE:
3067 if (!(es->style & ES_READONLY) && !(shift && control)) {
3068 if (es->selection_start != es->selection_end) {
3069 if (shift)
3070 EDIT_WM_Cut(hwnd, es);
3071 else
3072 EDIT_WM_Clear(hwnd, es);
3073 } else {
3074 if (shift) {
3075 /* delete character left of caret */
3076 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3077 EDIT_MoveBackward(hwnd, es, TRUE);
3078 EDIT_WM_Clear(hwnd, es);
3079 } else if (control) {
3080 /* delete to end of line */
3081 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3082 EDIT_MoveEnd(hwnd, es, TRUE);
3083 EDIT_WM_Clear(hwnd, es);
3084 } else {
3085 /* delete character right of caret */
3086 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3087 EDIT_MoveForward(hwnd, es, TRUE);
3088 EDIT_WM_Clear(hwnd, es);
3089 }
3090 }
3091 }
3092 break;
3093 case VK_INSERT:
3094 if (shift) {
3095 if (!(es->style & ES_READONLY))
3096 EDIT_WM_Paste(hwnd, es);
3097 } else if (control)
3098 EDIT_WM_Copy(hwnd, es);
3099 break;
3100 case VK_RETURN:
3101 /* If the edit doesn't want the return send a message to the default object */
3102 if(!(es->style & ES_WANTRETURN))
3103 {
3104 HWND hwndParent = GetParent(hwnd);
3105 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
3106 if (HIWORD(dw) == DC_HASDEFID)
3107 {
3108 SendMessageA( hwndParent, WM_COMMAND,
3109 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3110 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3111 }
3112 }
3113 break;
3114 }
3115 return 0;
3116}
3117
3118
3119/*********************************************************************
3120 *
3121 * WM_KILLFOCUS
3122 *
3123 */
3124static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus)
3125{
3126 es->flags &= ~EF_FOCUSED;
3127 DestroyCaret();
3128 if(!(es->style & ES_NOHIDESEL))
3129 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3130 EDIT_NOTIFY_PARENT(hwnd, EN_KILLFOCUS, "EN_KILLFOCUS");
3131 return 0;
3132}
3133
3134
3135/*********************************************************************
3136 *
3137 * WM_LBUTTONDBLCLK
3138 *
3139 * The caret position has been set on the WM_LBUTTONDOWN message
3140 *
3141 */
3142static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3143{
3144 INT s;
3145 INT e = es->selection_end;
3146 INT l;
3147 INT li;
3148 INT ll;
3149
3150 if (!(es->flags & EF_FOCUSED))
3151 return 0;
3152
3153 l = EDIT_EM_LineFromChar(hwnd, es, e);
3154 li = EDIT_EM_LineIndex(hwnd, es, l);
3155 ll = EDIT_EM_LineLength(hwnd, es, e);
3156 s = li + EDIT_CallWordBreakProc (hwnd, es, li, e - li, ll, WB_LEFT);
3157 e = li + EDIT_CallWordBreakProc(hwnd, es, li, e - li, ll, WB_RIGHT);
3158 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
3159 EDIT_EM_ScrollCaret(hwnd, es);
3160 return 0;
3161}
3162
3163
3164/*********************************************************************
3165 *
3166 * WM_LBUTTONDOWN
3167 *
3168 */
3169static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3170{
3171 INT e;
3172 BOOL after_wrap;
3173
3174 if (!(es->flags & EF_FOCUSED))
3175 return 0;
3176
3177 es->bCaptureState = TRUE;
3178 SetCapture(hwnd);
3179 EDIT_ConfinePoint(hwnd, es, &x, &y);
3180 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3181 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3182 EDIT_EM_ScrollCaret(hwnd, es);
3183 es->region_posx = es->region_posy = 0;
3184 SetTimer(hwnd, 0, 100, NULL);
3185 return 0;
3186}
3187
3188
3189/*********************************************************************
3190 *
3191 * WM_LBUTTONUP
3192 *
3193 */
3194static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3195{
3196 if (es->bCaptureState && GetCapture() == hwnd) {
3197 KillTimer(hwnd, 0);
3198 ReleaseCapture();
3199 }
3200 es->bCaptureState = FALSE;
3201
3202 return 0;
3203}
3204
3205
3206/*********************************************************************
3207 *
3208 * WM_MOUSEMOVE
3209 *
3210 */
3211static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3212{
3213 INT e;
3214 BOOL after_wrap;
3215 INT prex, prey;
3216
3217 if (GetCapture() != hwnd)
3218 return 0;
3219
3220 /*
3221 * FIXME: gotta do some scrolling if outside client
3222 * area. Maybe reset the timer ?
3223 */
3224 prex = x; prey = y;
3225 EDIT_ConfinePoint(hwnd, es, &x, &y);
3226 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3227 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3228 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3229 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
3230 return 0;
3231}
3232
3233
3234/*********************************************************************
3235 *
3236 * WM_NCCREATE
3237 *
3238 */
3239static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs)
3240{
3241 EDITSTATE *es;
3242
3243 if (!(es = (EDITSTATE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3244 return FALSE;
3245 SetInfoPtr(hwnd,(DWORD)es);
3246
3247 /*
3248 * Note: since the EDITSTATE has not been fully initialized yet,
3249 * we can't use any API calls that may send
3250 * WM_XXX messages before WM_NCCREATE is completed.
3251 */
3252
3253 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3254 return FALSE;
3255 es->style = cs->style;
3256
3257 es->bEnableState = !(cs->style & WS_DISABLED);
3258
3259 /*
3260 * In Win95 look and feel, the WS_BORDER style is replaced by the
3261 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3262 * control a non client area.
3263 */
3264 if (es->style & WS_BORDER)
3265 {
3266 es->style &= ~WS_BORDER;
3267 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~WS_BORDER);
3268 SetWindowLongA(hwnd,GWL_EXSTYLE,GetWindowLongA(hwnd,GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3269 }
3270
3271 if (es->style & ES_MULTILINE) {
3272 es->buffer_size = BUFSTART_MULTI;
3273 es->buffer_limit = BUFLIMIT_MULTI;
3274 if (es->style & WS_VSCROLL)
3275 es->style |= ES_AUTOVSCROLL;
3276 if (es->style & WS_HSCROLL)
3277 es->style |= ES_AUTOHSCROLL;
3278 es->style &= ~ES_PASSWORD;
3279 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3280 if (es->style & ES_RIGHT)
3281 es->style &= ~ES_CENTER;
3282 es->style &= ~WS_HSCROLL;
3283 es->style &= ~ES_AUTOHSCROLL;
3284 }
3285
3286 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3287 es->style |= ES_AUTOVSCROLL;
3288 } else {
3289 es->buffer_size = BUFSTART_SINGLE;
3290 es->buffer_limit = BUFLIMIT_SINGLE;
3291 es->style &= ~ES_CENTER;
3292 es->style &= ~ES_RIGHT;
3293 es->style &= ~WS_HSCROLL;
3294 es->style &= ~WS_VSCROLL;
3295 es->style &= ~ES_AUTOVSCROLL;
3296 es->style &= ~ES_WANTRETURN;
3297 if (es->style & ES_UPPERCASE) {
3298 es->style &= ~ES_LOWERCASE;
3299 es->style &= ~ES_NUMBER;
3300 } else if (es->style & ES_LOWERCASE)
3301 es->style &= ~ES_NUMBER;
3302 if (es->style & ES_PASSWORD)
3303 es->password_char = '*';
3304
3305 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3306 es->style |= ES_AUTOHSCROLL;
3307 }
3308 if (!(es->text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3309 return FALSE;
3310 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3311 if (!(es->undo_text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3312 return FALSE;
3313 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3314 *es->text = '\0';
3315 if (es->style & ES_MULTILINE)
3316 if (!(es->first_line_def = (LINEDEF*)HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3317 return FALSE;
3318 es->line_count = 1;
3319
3320 return TRUE;
3321}
3322
3323/*********************************************************************
3324 *
3325 * WM_PAINT
3326 *
3327 */
3328static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam)
3329{
3330 PAINTSTRUCT ps;
3331 INT i;
3332 HDC dc;
3333 HFONT old_font = 0;
3334 RECT rc;
3335 RECT rcLine;
3336 RECT rcRgn;
3337 BOOL rev = es->bEnableState &&
3338 ((es->flags & EF_FOCUSED) ||
3339 (es->style & ES_NOHIDESEL));
3340
3341 if (es->flags & EF_UPDATE)
3342 EDIT_NOTIFY_PARENT(hwnd, EN_UPDATE, "EN_UPDATE");
3343
3344 if (!wParam)
3345 dc = BeginPaint(hwnd, &ps);
3346 else
3347 dc = (HDC) wParam;
3348
3349 if(es->style & WS_BORDER) {
3350 GetClientRect(hwnd, &rc);
3351 if(es->style & ES_MULTILINE) {
3352 if(es->style & WS_HSCROLL) rc.bottom++;
3353 if(es->style & WS_VSCROLL) rc.right++;
3354 }
3355 Rectangle(dc, rc.left, rc.top, rc.right, rc.bottom);
3356 }
3357 IntersectClipRect(dc, es->format_rect.left,
3358 es->format_rect.top,
3359 es->format_rect.right,
3360 es->format_rect.bottom);
3361 if (es->style & ES_MULTILINE) {
3362 GetClientRect(hwnd, &rc);
3363 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3364 }
3365 if (es->font)
3366 old_font = SelectObject(dc, es->font);
3367 if (!es->bEnableState || (es->style & ES_READONLY))
3368 EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
3369 else
3370 EDIT_SEND_CTLCOLOR(hwnd, dc);
3371 if (!es->bEnableState)
3372 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3373 GetClipBox(dc, &rcRgn);
3374 if (es->style & ES_MULTILINE) {
3375 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3376 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3377 EDIT_GetLineRect(hwnd, es, i, 0, -1, &rcLine);
3378 if (IntersectRect(&rc, &rcRgn, &rcLine))
3379 EDIT_PaintLine(hwnd, es, dc, i, rev);
3380 }
3381 } else {
3382 EDIT_GetLineRect(hwnd, es, 0, 0, -1, &rcLine);
3383 if (IntersectRect(&rc, &rcRgn, &rcLine))
3384 EDIT_PaintLine(hwnd, es, dc, 0, rev);
3385 }
3386 if (es->font)
3387 SelectObject(dc, old_font);
3388 if (es->flags & EF_FOCUSED)
3389 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3390 es->flags & EF_AFTER_WRAP);
3391 if (!wParam) EndPaint(hwnd, &ps);
3392 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
3393 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3394 SCROLLINFO si;
3395 si.cbSize = sizeof(SCROLLINFO);
3396 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3397 si.nMin = 0;
3398 si.nMax = es->line_count + vlc - 2;
3399 si.nPage = vlc;
3400 si.nPos = es->y_offset;
3401 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3402 }
3403 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
3404 SCROLLINFO si;
3405 INT fw = es->format_rect.right - es->format_rect.left;
3406 si.cbSize = sizeof(SCROLLINFO);
3407 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
3408 si.nMin = 0;
3409 si.nMax = es->text_width + fw - 1;
3410 si.nPage = fw;
3411 si.nPos = es->x_offset;
3412 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
3413 }
3414
3415 if (es->flags & EF_UPDATE) {
3416 es->flags &= ~EF_UPDATE;
3417 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE, "EN_CHANGE");
3418 }
3419}
3420
3421
3422/*********************************************************************
3423 *
3424 * WM_PASTE
3425 *
3426 */
3427static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
3428{
3429 HGLOBAL hsrc;
3430 LPSTR src;
3431
3432 OpenClipboard(hwnd);
3433 hsrc = GetClipboardData(CF_TEXT);
3434 if (hsrc) {
3435 src = (LPSTR)GlobalLock(hsrc);
3436 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src);
3437 GlobalUnlock(hsrc);
3438 }
3439 CloseClipboard();
3440}
3441
3442
3443/*********************************************************************
3444 *
3445 * WM_SETFOCUS
3446 *
3447 */
3448static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus)
3449{
3450 es->flags |= EF_FOCUSED;
3451 CreateCaret(hwnd, 0, 2, es->line_height);
3452 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3453 es->flags & EF_AFTER_WRAP);
3454 if(!(es->style & ES_NOHIDESEL))
3455 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3456 ShowCaret(hwnd);
3457 EDIT_NOTIFY_PARENT(hwnd, EN_SETFOCUS, "EN_SETFOCUS");
3458}
3459
3460
3461/*********************************************************************
3462 *
3463 * WM_SETFONT
3464 *
3465 * With Win95 look the margins are set to default font value unless
3466 * the system font (font == 0) is being set, in which case they are left
3467 * unchanged.
3468 *
3469 */
3470static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
3471{
3472 TEXTMETRICA tm;
3473 HDC dc;
3474 HFONT old_font = 0;
3475 RECT r;
3476
3477 es->font = font;
3478 dc = GetDC(hwnd);
3479 if (font)
3480 old_font = SelectObject(dc, font);
3481 GetTextMetricsA(dc, &tm);
3482 es->line_height = tm.tmHeight;
3483 es->char_width = tm.tmAveCharWidth;
3484 if (font)
3485 SelectObject(dc, old_font);
3486 ReleaseDC(hwnd, dc);
3487 if (font)
3488 EDIT_EM_SetMargins(hwnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3489 EC_USEFONTINFO, EC_USEFONTINFO);
3490 /* Force the recalculation of the format rect for each font change */
3491 GetClientRect(hwnd, &r);
3492 EDIT_SetRectNP(hwnd, es, &r);
3493 if (es->style & ES_MULTILINE)
3494 EDIT_BuildLineDefs_ML(hwnd, es);
3495
3496 if (redraw)
3497 InvalidateRect(hwnd, NULL, TRUE);
3498 if (es->flags & EF_FOCUSED) {
3499 DestroyCaret();
3500 CreateCaret(hwnd, 0, 2, es->line_height);
3501 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3502 es->flags & EF_AFTER_WRAP);
3503 ShowCaret(hwnd);
3504 }
3505}
3506
3507
3508/*********************************************************************
3509 *
3510 * WM_SETTEXT
3511 *
3512 * NOTES
3513 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3514 * The modified flag is reset. No notifications are sent.
3515 *
3516 * For single-line controls, reception of WM_SETTEXT triggers:
3517 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3518 *
3519 */
3520static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text)
3521{
3522 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
3523 if (text) {
3524 //TRACE_(edit)("\t'%s'\n", text);
3525 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text);
3526 } else {
3527 //TRACE_(edit)("\t<NULL>\n");
3528 EDIT_EM_ReplaceSel(hwnd, es, FALSE, "");
3529 }
3530 es->x_offset = 0;
3531 if (es->style & ES_MULTILINE) {
3532 es->flags &= ~EF_UPDATE;
3533 } else {
3534 es->flags |= EF_UPDATE;
3535 }
3536 es->flags &= ~EF_MODIFIED;
3537 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
3538 EDIT_EM_ScrollCaret(hwnd, es);
3539}
3540
3541
3542/*********************************************************************
3543 *
3544 * WM_SIZE
3545 *
3546 */
3547static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
3548{
3549 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
3550 RECT rc;
3551 SetRect(&rc, 0, 0, width, height);
3552 EDIT_SetRectNP(hwnd, es, &rc);
3553 InvalidateRect(hwnd, NULL, TRUE);
3554 }
3555}
3556
3557
3558/*********************************************************************
3559 *
3560 * WM_SYSKEYDOWN
3561 *
3562 */
3563static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3564{
3565 if ((key == VK_BACK) && (key_data & 0x2000)) {
3566 if (EDIT_EM_CanUndo(hwnd, es))
3567 EDIT_EM_Undo(hwnd, es);
3568 return 0;
3569 } else if (key == VK_UP || key == VK_DOWN)
3570 if (EDIT_CheckCombo(hwnd, WM_SYSKEYDOWN, key, key_data))
3571 return 0;
3572 return DefWindowProcA(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
3573}
3574
3575
3576/*********************************************************************
3577 *
3578 * WM_TIMER
3579 *
3580 */
3581static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
3582{
3583 if (es->region_posx < 0) {
3584 EDIT_MoveBackward(hwnd, es, TRUE);
3585 } else if (es->region_posx > 0) {
3586 EDIT_MoveForward(hwnd, es, TRUE);
3587 }
3588/*
3589 * FIXME: gotta do some vertical scrolling here, like
3590 * EDIT_EM_LineScroll(wnd, 0, 1);
3591 */
3592}
3593
3594
3595/*********************************************************************
3596 *
3597 * EDIT_VScroll_Hack
3598 *
3599 * 16 bit notepad needs this. Actually it is not _our_ hack,
3600 * it is notepad's. Notepad is sending us scrollbar messages with
3601 * undocumented parameters without us even having a scrollbar ... !?!?
3602 *
3603 */
3604static LRESULT EDIT_VScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3605{
3606 INT dy = 0;
3607 LRESULT ret = 0;
3608
3609 if (!(es->flags & EF_VSCROLL_HACK)) {
3610 //ERR_(edit)("hacked WM_VSCROLL handler invoked\n");
3611 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
3612 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
3613 es->flags |= EF_VSCROLL_HACK;
3614 }
3615
3616 switch (action) {
3617 case SB_LINEUP:
3618 case SB_LINEDOWN:
3619 case SB_PAGEUP:
3620 case SB_PAGEDOWN:
3621 EDIT_EM_Scroll(hwnd, es, action);
3622 return 0;
3623 case SB_TOP:
3624 dy = -es->y_offset;
3625 break;
3626 case SB_BOTTOM:
3627 dy = es->line_count - 1 - es->y_offset;
3628 break;
3629 case SB_THUMBTRACK:
3630 es->flags |= EF_VSCROLL_TRACK;
3631 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
3632 break;
3633 case SB_THUMBPOSITION:
3634 es->flags &= ~EF_VSCROLL_TRACK;
3635 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
3636 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL, "EN_VSCROLL");
3637 break;
3638 case SB_ENDSCROLL:
3639 break;
3640
3641 default:
3642 //ERR_(edit)("undocumented (hacked) WM_VSCROLL parameter, please report\n");
3643 return 0;
3644 }
3645 if (dy)
3646 EDIT_EM_LineScroll(hwnd, es, 0, dy);
3647 return ret;
3648}
3649
3650
3651/*********************************************************************
3652 *
3653 * WM_VSCROLL
3654 *
3655 */
3656static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3657{
3658 INT dy;
3659
3660 if (!(es->style & ES_MULTILINE))
3661 return 0;
3662
3663 if (!(es->style & ES_AUTOVSCROLL))
3664 return 0;
3665
3666 if (!(es->style & WS_VSCROLL))
3667 return EDIT_VScroll_Hack(hwnd, es, action, pos, scroll_bar);
3668
3669 dy = 0;
3670 switch (action) {
3671 case SB_LINEUP:
3672 case SB_LINEDOWN:
3673 case SB_PAGEUP:
3674 case SB_PAGEDOWN:
3675 EDIT_EM_Scroll(hwnd, es, action);
3676 return 0;
3677
3678 case SB_TOP:
3679 dy = -es->y_offset;
3680 break;
3681 case SB_BOTTOM:
3682 dy = es->line_count - 1 - es->y_offset;
3683 break;
3684 case SB_THUMBTRACK:
3685 es->flags |= EF_VSCROLL_TRACK;
3686 dy = pos - es->y_offset;
3687 break;
3688 case SB_THUMBPOSITION:
3689 es->flags &= ~EF_VSCROLL_TRACK;
3690 if (!(dy = pos - es->y_offset)) {
3691 SetScrollPos(hwnd, SB_VERT, pos, TRUE);
3692 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL, "EN_VSCROLL");
3693 }
3694 break;
3695 case SB_ENDSCROLL:
3696 break;
3697
3698 default:
3699 //ERR_(edit)("undocumented WM_VSCROLL action %d, please report\n",
3700 // action);
3701 return 0;
3702 }
3703 if (dy)
3704 EDIT_EM_LineScroll(hwnd, es, 0, dy);
3705 return 0;
3706}
3707
3708BOOL EDIT_Register()
3709{
3710 WNDCLASSA wndClass;
3711
3712 if (GlobalFindAtomA(EDITCLASSNAME)) return FALSE;
3713
3714 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
3715 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
3716 wndClass.lpfnWndProc = (WNDPROC)EditWndProc;
3717 wndClass.cbClsExtra = 0;
3718 wndClass.cbWndExtra = sizeof(VOID*);
3719 wndClass.hCursor = LoadCursorA(0,IDC_IBEAMA);
3720 wndClass.hbrBackground = (HBRUSH)0;
3721 wndClass.lpszClassName = EDITCLASSNAME;
3722
3723 return RegisterClassA(&wndClass);
3724}
3725
3726BOOL EDIT_Unregister()
3727{
3728 if (GlobalFindAtomA(EDITCLASSNAME))
3729 return UnregisterClassA(EDITCLASSNAME,(HINSTANCE)NULL);
3730 else return FALSE;
3731}
Note: See TracBrowser for help on using the repository browser.