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

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

WM_EX_CONTEXTHELP

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