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

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

export 55AA pattern for COMCTL32, edit EN_CHANGED fix

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