source: trunk/src/user32/new/edit.cpp@ 394

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

edit menu resource

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