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

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

edit fixes, monitor API

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