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

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

map rect, WM_ERASEBKGND and other fixes

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