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

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

fixed several edit bugs

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