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

Last change on this file since 3485 was 3153, checked in by cbratschi, 25 years ago

merged with Corel 20000317, small icon

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