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

Last change on this file since 3830 was 3584, checked in by cbratschi, 25 years ago

merged controls with Corel WINE 20000513, maximize/minimize fix

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