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

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

scroll: removed WS_* check

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