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

Last change on this file since 1794 was 1794, checked in by achimha, 26 years ago

added message logging statement, outcommented

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