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

Last change on this file since 9217 was 9217, checked in by sandervl, 23 years ago

fixed caret bug in edit control; removed obsolete code in createcaret

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