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

Last change on this file since 2804 was 2804, checked in by sandervl, 26 years ago

Added new logging feature

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