source: trunk/src/user32/new/edit.cpp@ 500

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

wine-990731 update

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