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

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

added button styles and messages, bug fixes

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