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

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

cursor handling fixed

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