[CSRSS]
[reactos.git] / reactos / dll / win32 / user32 / controls / edit.c
1 /*
2 * Edit control
3 *
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
7 *
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * NOTES
24 *
25 * This code was audited for completeness against the documented features
26 * of Comctl32.dll version 6.0 on Oct. 8, 2004, by Dimitrie O. Paun.
27 *
28 * Unless otherwise noted, we believe this code to be complete, as per
29 * the specification mentioned above.
30 * If you discover missing features, or bugs, please note them below.
31 *
32 * TODO:
33 * - EDITBALLOONTIP structure
34 * - EM_GETCUEBANNER/Edit_GetCueBannerText
35 * - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
36 * - EM_SETCUEBANNER/Edit_SetCueBannerText
37 * - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
38 * - EM_GETIMESTATUS, EM_SETIMESTATUS
39 * - EN_ALIGN_LTR_EC
40 * - EN_ALIGN_RTL_EC
41 * - ES_OEMCONVERT
42 *
43 */
44
45 #include <user32.h>
46
47 #include <wine/debug.h>
48
49 WINE_DEFAULT_DEBUG_CHANNEL(edit);
50 WINE_DECLARE_DEBUG_CHANNEL(combo);
51 WINE_DECLARE_DEBUG_CHANNEL(relay);
52
53 #define BUFLIMIT_INITIAL 30000 /* initial buffer size */
54 #define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
55 #define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
56 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
57
58 /*
59 * extra flags for EDITSTATE.flags field
60 */
61 #define EF_MODIFIED 0x0001 /* text has been modified */
62 #define EF_FOCUSED 0x0002 /* we have input focus */
63 #define EF_UPDATE 0x0004 /* notify parent of changed state */
64 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
65 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
66 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
67 wrapped line, instead of in front of the next character */
68 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
69 #define EF_APP_HAS_HANDLE 0x0200 /* Set when an app sends EM_[G|S]ETHANDLE. We are in sole control of
70 the text buffer if this is clear. */
71 #define EF_DIALOGMODE 0x0400 /* Indicates that we are inside a dialog window */
72
73 typedef enum
74 {
75 END_0 = 0, /* line ends with terminating '\0' character */
76 END_WRAP, /* line is wrapped */
77 END_HARD, /* line ends with a hard return '\r\n' */
78 END_SOFT, /* line ends with a soft return '\r\r\n' */
79 END_RICH /* line ends with a single '\n' */
80 } LINE_END;
81
82 typedef struct tagLINEDEF {
83 INT length; /* bruto length of a line in bytes */
84 INT net_length; /* netto length of a line in visible characters */
85 LINE_END ending;
86 INT width; /* width of the line in pixels */
87 INT index; /* line index into the buffer */
88 struct tagLINEDEF *next;
89 } LINEDEF;
90
91 typedef struct
92 {
93 BOOL is_unicode; /* how the control was created */
94 LPWSTR text; /* the actual contents of the control */
95 UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
96 UINT buffer_size; /* the size of the buffer in characters */
97 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
98 HFONT font; /* NULL means standard system font */
99 INT x_offset; /* scroll offset for multi lines this is in pixels
100 for single lines it's in characters */
101 INT line_height; /* height of a screen line in pixels */
102 INT char_width; /* average character width in pixels */
103 DWORD style; /* sane version of wnd->dwStyle */
104 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
105 INT undo_insert_count; /* number of characters inserted in sequence */
106 UINT undo_position; /* character index of the insertion and deletion */
107 LPWSTR undo_text; /* deleted text */
108 UINT undo_buffer_size; /* size of the deleted text buffer */
109 INT selection_start; /* == selection_end if no selection */
110 INT selection_end; /* == current caret position */
111 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
112 INT left_margin; /* in pixels */
113 INT right_margin; /* in pixels */
114 RECT format_rect;
115 INT text_width; /* width of the widest line in pixels for multi line controls
116 and just line width for single line controls */
117 INT region_posx; /* Position of cursor relative to region: */
118 INT region_posy; /* -1: to left, 0: within, 1: to right */
119 #ifndef __REACTOS__
120 EDITWORDBREAKPROC16 word_break_proc16;
121 #endif
122 void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
123 INT line_count; /* number of lines */
124 INT y_offset; /* scroll offset in number of lines */
125 BOOL bCaptureState; /* flag indicating whether mouse was captured */
126 BOOL bEnableState; /* flag keeping the enable state */
127 HWND hwndSelf; /* the our window handle */
128 HWND hwndParent; /* Handle of parent for sending EN_* messages.
129 Even if parent will change, EN_* messages
130 should be sent to the first parent. */
131 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
132 /*
133 * only for multi line controls
134 */
135 INT lock_count; /* amount of re-entries in the EditWndProc */
136 INT tabs_count;
137 LPINT tabs;
138 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
139 HLOCAL hloc32W; /* our unicode local memory block */
140 #ifndef __REACTOS__
141 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
142 or EM_SETHANDLE16 */
143 #endif
144 HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
145 or EM_SETHANDLE */
146 /*
147 * IME Data
148 */
149 UINT composition_len; /* length of composition, 0 == no composition */
150 int composition_start; /* the character position for the composition */
151 } EDITSTATE;
152
153
154 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
155 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
156
157 /* used for disabled or read-only edit control */
158 #define EDIT_NOTIFY_PARENT(es, wNotifyCode) \
159 do \
160 { /* Notify parent which has created this edit control */ \
161 TRACE("notification " #wNotifyCode " sent to hwnd=%p\n", es->hwndParent); \
162 SendMessageW(es->hwndParent, WM_COMMAND, \
163 MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \
164 (LPARAM)(es->hwndSelf)); \
165 } while(0)
166
167
168 /*********************************************************************
169 *
170 * EM_CANUNDO
171 *
172 */
173 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
174 {
175 return (es->undo_insert_count || strlenW(es->undo_text));
176 }
177
178
179 /*********************************************************************
180 *
181 * EM_EMPTYUNDOBUFFER
182 *
183 */
184 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
185 {
186 es->undo_insert_count = 0;
187 *es->undo_text = '\0';
188 }
189
190
191 /**********************************************************************
192 * get_app_version
193 *
194 * Returns the window version in case Wine emulates a later version
195 * of windows than the application expects.
196 *
197 * In a number of cases when windows runs an application that was
198 * designed for an earlier windows version, windows reverts
199 * to "old" behaviour of that earlier version.
200 *
201 * An example is a disabled edit control that needs to be painted.
202 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
203 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
204 * applications with an expected version 0f 4.0 or higher.
205 *
206 */
207 static DWORD get_app_version(void)
208 {
209 static DWORD version;
210 if (!version)
211 {
212 DWORD dwEmulatedVersion;
213 OSVERSIONINFOW info;
214 DWORD dwProcVersion = GetProcessVersion(0);
215
216 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
217 GetVersionExW( &info );
218 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
219 /* FIXME: this may not be 100% correct; see discussion on the
220 * wine developer list in Nov 1999 */
221 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
222 }
223 return version;
224 }
225
226 static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
227 {
228 HBRUSH hbrush;
229 UINT msg;
230
231 if ( get_app_version() >= 0x40000 && (!es->bEnableState || (es->style & ES_READONLY)))
232 msg = WM_CTLCOLORSTATIC;
233 else
234 msg = WM_CTLCOLOREDIT;
235
236 /* why do we notify to es->hwndParent, and we send this one to GetParent()? */
237 hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
238 if (!hbrush)
239 hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
240 return hbrush;
241 }
242
243
244 /*********************************************************************
245 *
246 * EDIT_WordBreakProc
247 *
248 * Find the beginning of words.
249 * Note: unlike the specs for a WordBreakProc, this function only
250 * allows to be called without linebreaks between s[0] up to
251 * s[count - 1]. Remember it is only called
252 * internally, so we can decide this for ourselves.
253 *
254 */
255 static INT EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action)
256 {
257 INT ret = 0;
258
259 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
260
261 if(!s) return 0;
262
263 switch (action) {
264 case WB_LEFT:
265 if (!count)
266 break;
267 if (index)
268 index--;
269 if (s[index] == ' ') {
270 while (index && (s[index] == ' '))
271 index--;
272 if (index) {
273 while (index && (s[index] != ' '))
274 index--;
275 if (s[index] == ' ')
276 index++;
277 }
278 } else {
279 while (index && (s[index] != ' '))
280 index--;
281 if (s[index] == ' ')
282 index++;
283 }
284 ret = index;
285 break;
286 case WB_RIGHT:
287 if (!count)
288 break;
289 if (index)
290 index--;
291 if (s[index] == ' ')
292 while ((index < count) && (s[index] == ' ')) index++;
293 else {
294 while (s[index] && (s[index] != ' ') && (index < count))
295 index++;
296 while ((s[index] == ' ') && (index < count)) index++;
297 }
298 ret = index;
299 break;
300 case WB_ISDELIMITER:
301 ret = (s[index] == ' ');
302 break;
303 default:
304 ERR("unknown action code, please report !\n");
305 break;
306 }
307 return ret;
308 }
309
310
311 /*********************************************************************
312 *
313 * EDIT_CallWordBreakProc
314 *
315 * Call appropriate WordBreakProc (internal or external).
316 *
317 * Note: The "start" argument should always be an index referring
318 * to es->text. The actual wordbreak proc might be
319 * 16 bit, so we can't always pass any 32 bit LPSTR.
320 * Hence we assume that es->text is the buffer that holds
321 * the string under examination (we can decide this for ourselves).
322 *
323 */
324 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
325 {
326 INT ret;
327
328 #ifndef __REACTOS__
329 if (es->word_break_proc16) {
330 HGLOBAL16 hglob16;
331 SEGPTR segptr;
332 INT countA;
333 WORD args[5];
334 DWORD result;
335
336 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
337 hglob16 = GlobalAlloc16(GMEM_MOVEABLE | GMEM_ZEROINIT, countA);
338 segptr = WOWGlobalLock16(hglob16);
339 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, MapSL(segptr), countA, NULL, NULL);
340 args[4] = SELECTOROF(segptr);
341 args[3] = OFFSETOF(segptr);
342 args[2] = index;
343 args[1] = countA;
344 args[0] = action;
345 WOWCallback16Ex((DWORD)es->word_break_proc16, WCB16_PASCAL, sizeof(args), args, &result);
346 ret = LOWORD(result);
347 GlobalUnlock16(hglob16);
348 GlobalFree16(hglob16);
349 }
350 else if (es->word_break_proc)
351 #else
352 if (es->word_break_proc)
353 #endif
354 {
355 if(es->is_unicode)
356 {
357 EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
358
359 TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
360 es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
361 ret = wbpW(es->text + start, index, count, action);
362 }
363 else
364 {
365 EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
366 INT countA;
367 CHAR *textA;
368
369 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
370 textA = HeapAlloc(GetProcessHeap(), 0, countA);
371 if (textA == NULL)
372 return 0;
373 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
374 TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
375 es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
376 ret = wbpA(textA, index, countA, action);
377 HeapFree(GetProcessHeap(), 0, textA);
378 }
379 }
380 else
381 ret = EDIT_WordBreakProc(es->text + start, index, count, action);
382
383 return ret;
384 }
385
386 /*********************************************************************
387 *
388 * EDIT_BuildLineDefs_ML
389 *
390 * Build linked list of text lines.
391 * Lines can end with '\0' (last line), a character (if it is wrapped),
392 * a soft return '\r\r\n' or a hard return '\r\n'
393 *
394 */
395 static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
396 {
397 HDC dc;
398 HFONT old_font = 0;
399 LPWSTR current_position, cp;
400 INT fw;
401 LINEDEF *current_line;
402 LINEDEF *previous_line;
403 LINEDEF *start_line;
404 INT line_index = 0, nstart_line = 0, nstart_index = 0;
405 INT line_count = es->line_count;
406 INT orig_net_length;
407 RECT rc;
408
409 if (istart == iend && delta == 0)
410 return;
411
412 dc = GetDC(es->hwndSelf);
413 if (es->font)
414 old_font = SelectObject(dc, es->font);
415
416 previous_line = NULL;
417 current_line = es->first_line_def;
418
419 /* Find starting line. istart must lie inside an existing line or
420 * at the end of buffer */
421 do {
422 if (istart < current_line->index + current_line->length ||
423 current_line->ending == END_0)
424 break;
425
426 previous_line = current_line;
427 current_line = current_line->next;
428 line_index++;
429 } while (current_line);
430
431 if (!current_line) /* Error occurred start is not inside previous buffer */
432 {
433 FIXME(" modification occurred outside buffer\n");
434 ReleaseDC(es->hwndSelf, dc);
435 return;
436 }
437
438 /* Remember start of modifications in order to calculate update region */
439 nstart_line = line_index;
440 nstart_index = current_line->index;
441
442 /* We must start to reformat from the previous line since the modifications
443 * may have caused the line to wrap upwards. */
444 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
445 {
446 line_index--;
447 current_line = previous_line;
448 }
449 start_line = current_line;
450
451 fw = es->format_rect.right - es->format_rect.left;
452 current_position = es->text + current_line->index;
453 do {
454 if (current_line != start_line)
455 {
456 if (!current_line || current_line->index + delta > current_position - es->text)
457 {
458 /* The buffer has been expanded, create a new line and
459 insert it into the link list */
460 LINEDEF *new_line = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
461 if (new_line == NULL)
462 break;
463 new_line->next = previous_line->next;
464 previous_line->next = new_line;
465 current_line = new_line;
466 es->line_count++;
467 }
468 else if (current_line->index + delta < current_position - es->text)
469 {
470 /* The previous line merged with this line so we delete this extra entry */
471 previous_line->next = current_line->next;
472 HeapFree(GetProcessHeap(), 0, current_line);
473 current_line = previous_line->next;
474 es->line_count--;
475 continue;
476 }
477 else /* current_line->index + delta == current_position */
478 {
479 if (current_position - es->text > iend)
480 break; /* We reached end of line modifications */
481 /* else recalculate this line */
482 }
483 }
484
485 current_line->index = current_position - es->text;
486 orig_net_length = current_line->net_length;
487
488 /* Find end of line */
489 cp = current_position;
490 while (*cp) {
491 if (*cp == '\n') break;
492 if ((*cp == '\r') && (*(cp + 1) == '\n'))
493 break;
494 cp++;
495 }
496
497 /* Mark type of line termination */
498 if (!(*cp)) {
499 current_line->ending = END_0;
500 current_line->net_length = strlenW(current_position);
501 } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
502 current_line->ending = END_SOFT;
503 current_line->net_length = cp - current_position - 1;
504 } else if (*cp == '\n') {
505 current_line->ending = END_RICH;
506 current_line->net_length = cp - current_position;
507 } else {
508 current_line->ending = END_HARD;
509 current_line->net_length = cp - current_position;
510 }
511
512 /* Calculate line width */
513 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
514 current_position, current_line->net_length,
515 es->tabs_count, es->tabs));
516
517 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
518 if (!(es->style & ES_AUTOHSCROLL)) {
519 if (current_line->width > fw) {
520 INT next = 0;
521 INT prev;
522 do {
523 prev = next;
524 next = EDIT_CallWordBreakProc(es, current_position - es->text,
525 prev + 1, current_line->net_length, WB_RIGHT);
526 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
527 current_position, next, es->tabs_count, es->tabs));
528 } while (current_line->width <= fw);
529 if (!prev) { /* Didn't find a line break so force a break */
530 next = 0;
531 do {
532 prev = next;
533 next++;
534 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
535 current_position, next, es->tabs_count, es->tabs));
536 } while (current_line->width <= fw);
537 if (!prev)
538 prev = 1;
539 }
540
541 /* If the first line we are calculating, wrapped before istart, we must
542 * adjust istart in order for this to be reflected in the update region. */
543 if (current_line->index == nstart_index && istart > current_line->index + prev)
544 istart = current_line->index + prev;
545 /* else if we are updating the previous line before the first line we
546 * are re-calculating and it expanded */
547 else if (current_line == start_line &&
548 current_line->index != nstart_index && orig_net_length < prev)
549 {
550 /* Line expanded due to an upwards line wrap so we must partially include
551 * previous line in update region */
552 nstart_line = line_index;
553 nstart_index = current_line->index;
554 istart = current_line->index + orig_net_length;
555 }
556
557 current_line->net_length = prev;
558 current_line->ending = END_WRAP;
559 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc, current_position,
560 current_line->net_length, es->tabs_count, es->tabs));
561 }
562 else if (current_line == start_line &&
563 current_line->index != nstart_index &&
564 orig_net_length < current_line->net_length) {
565 /* The previous line expanded but it's still not as wide as the client rect */
566 /* The expansion is due to an upwards line wrap so we must partially include
567 it in the update region */
568 nstart_line = line_index;
569 nstart_index = current_line->index;
570 istart = current_line->index + orig_net_length;
571 }
572 }
573
574
575 /* Adjust length to include line termination */
576 switch (current_line->ending) {
577 case END_SOFT:
578 current_line->length = current_line->net_length + 3;
579 break;
580 case END_RICH:
581 current_line->length = current_line->net_length + 1;
582 break;
583 case END_HARD:
584 current_line->length = current_line->net_length + 2;
585 break;
586 case END_WRAP:
587 case END_0:
588 current_line->length = current_line->net_length;
589 break;
590 }
591 es->text_width = max(es->text_width, current_line->width);
592 current_position += current_line->length;
593 previous_line = current_line;
594 current_line = current_line->next;
595 line_index++;
596 } while (previous_line->ending != END_0);
597
598 /* Finish adjusting line indexes by delta or remove hanging lines */
599 if (previous_line->ending == END_0)
600 {
601 LINEDEF *pnext = NULL;
602
603 previous_line->next = NULL;
604 while (current_line)
605 {
606 pnext = current_line->next;
607 HeapFree(GetProcessHeap(), 0, current_line);
608 current_line = pnext;
609 es->line_count--;
610 }
611 }
612 else if (delta != 0)
613 {
614 while (current_line)
615 {
616 current_line->index += delta;
617 current_line = current_line->next;
618 }
619 }
620
621 /* Calculate rest of modification rectangle */
622 if (hrgn)
623 {
624 HRGN tmphrgn;
625 /*
626 * We calculate two rectangles. One for the first line which may have
627 * an indent with respect to the format rect. The other is a format-width
628 * rectangle that spans the rest of the lines that changed or moved.
629 */
630 rc.top = es->format_rect.top + nstart_line * es->line_height -
631 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
632 rc.bottom = rc.top + es->line_height;
633 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT))
634 rc.left = es->format_rect.left;
635 else
636 rc.left = es->format_rect.left + (INT)LOWORD(GetTabbedTextExtentW(dc,
637 es->text + nstart_index, istart - nstart_index,
638 es->tabs_count, es->tabs)) - es->x_offset; /* Adjust for horz scroll */
639 rc.right = es->format_rect.right;
640 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
641
642 rc.top = rc.bottom;
643 rc.left = es->format_rect.left;
644 rc.right = es->format_rect.right;
645 /*
646 * If lines were added or removed we must re-paint the remainder of the
647 * lines since the remaining lines were either shifted up or down.
648 */
649 if (line_count < es->line_count) /* We added lines */
650 rc.bottom = es->line_count * es->line_height;
651 else if (line_count > es->line_count) /* We removed lines */
652 rc.bottom = line_count * es->line_height;
653 else
654 rc.bottom = line_index * es->line_height;
655 rc.bottom += es->format_rect.top;
656 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
657 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
658 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
659 DeleteObject(tmphrgn);
660 }
661
662 if (es->font)
663 SelectObject(dc, old_font);
664
665 ReleaseDC(es->hwndSelf, dc);
666 }
667
668
669 static inline UINT get_text_length(EDITSTATE *es)
670 {
671 if(es->text_length == (UINT)-1)
672 es->text_length = strlenW(es->text);
673 return es->text_length;
674 }
675
676 /*********************************************************************
677 *
678 * EDIT_GetPasswordPointer_SL
679 *
680 * note: caller should free the (optionally) allocated buffer
681 *
682 */
683 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
684 {
685 if (es->style & ES_PASSWORD) {
686 INT len = get_text_length(es);
687 LPWSTR text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
688 if (text == NULL)
689 return NULL;
690 text[len] = '\0';
691 while(len) text[--len] = es->password_char;
692 return text;
693 } else
694 return es->text;
695 }
696
697
698 /*********************************************************************
699 *
700 * EDIT_CalcLineWidth_SL
701 *
702 */
703 static void EDIT_CalcLineWidth_SL(EDITSTATE *es)
704 {
705 SIZE size;
706 LPWSTR text;
707 HDC dc;
708 HFONT old_font = 0;
709
710 text = EDIT_GetPasswordPointer_SL(es);
711
712 dc = GetDC(es->hwndSelf);
713 if (es->font)
714 old_font = SelectObject(dc, es->font);
715
716 GetTextExtentPoint32W(dc, text, strlenW(text), &size);
717
718 if (es->font)
719 SelectObject(dc, old_font);
720 ReleaseDC(es->hwndSelf, dc);
721
722 if (es->style & ES_PASSWORD)
723 HeapFree(GetProcessHeap(), 0, text);
724
725 es->text_width = size.cx;
726 }
727
728 /*********************************************************************
729 *
730 * EDIT_CharFromPos
731 *
732 * Beware: This is not the function called on EM_CHARFROMPOS
733 * The position _can_ be outside the formatting / client
734 * rectangle
735 * The return value is only the character index
736 *
737 */
738 static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
739 {
740 INT index;
741 HDC dc;
742 HFONT old_font = 0;
743 INT x_high = 0, x_low = 0;
744
745 if (es->style & ES_MULTILINE) {
746 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
747 INT line_index = 0;
748 LINEDEF *line_def = es->first_line_def;
749 INT low, high;
750 while ((line > 0) && line_def->next) {
751 line_index += line_def->length;
752 line_def = line_def->next;
753 line--;
754 }
755 x += es->x_offset - es->format_rect.left;
756 if (es->style & ES_RIGHT)
757 x -= (es->format_rect.right - es->format_rect.left) - line_def->width;
758 else if (es->style & ES_CENTER)
759 x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2;
760 if (x >= line_def->width) {
761 if (after_wrap)
762 *after_wrap = (line_def->ending == END_WRAP);
763 return line_index + line_def->net_length;
764 }
765 if (x <= 0) {
766 if (after_wrap)
767 *after_wrap = FALSE;
768 return line_index;
769 }
770 dc = GetDC(es->hwndSelf);
771 if (es->font)
772 old_font = SelectObject(dc, es->font);
773 low = line_index;
774 high = line_index + line_def->net_length + 1;
775 while (low < high - 1)
776 {
777 INT mid = (low + high) / 2;
778 INT x_now = LOWORD(GetTabbedTextExtentW(dc, es->text + line_index, mid - line_index, es->tabs_count, es->tabs));
779 if (x_now > x) {
780 high = mid;
781 x_high = x_now;
782 } else {
783 low = mid;
784 x_low = x_now;
785 }
786 }
787 if (abs(x_high - x) + 1 <= abs(x_low - x))
788 index = high;
789 else
790 index = low;
791
792 if (after_wrap)
793 *after_wrap = ((index == line_index + line_def->net_length) &&
794 (line_def->ending == END_WRAP));
795 } else {
796 LPWSTR text;
797 SIZE size;
798 if (after_wrap)
799 *after_wrap = FALSE;
800 x -= es->format_rect.left;
801 if (!x)
802 return es->x_offset;
803
804 if (!es->x_offset)
805 {
806 INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width;
807 if (es->style & ES_RIGHT)
808 x -= indent;
809 else if (es->style & ES_CENTER)
810 x -= indent / 2;
811 }
812
813 text = EDIT_GetPasswordPointer_SL(es);
814 dc = GetDC(es->hwndSelf);
815 if (es->font)
816 old_font = SelectObject(dc, es->font);
817 if (x < 0)
818 {
819 INT low = 0;
820 INT high = es->x_offset;
821 while (low < high - 1)
822 {
823 INT mid = (low + high) / 2;
824 GetTextExtentPoint32W( dc, text + mid,
825 es->x_offset - mid, &size );
826 if (size.cx > -x) {
827 low = mid;
828 x_low = size.cx;
829 } else {
830 high = mid;
831 x_high = size.cx;
832 }
833 }
834 if (abs(x_high + x) <= abs(x_low + x) + 1)
835 index = high;
836 else
837 index = low;
838 }
839 else
840 {
841 INT low = es->x_offset;
842 INT high = get_text_length(es) + 1;
843 while (low < high - 1)
844 {
845 INT mid = (low + high) / 2;
846 GetTextExtentPoint32W( dc, text + es->x_offset,
847 mid - es->x_offset, &size );
848 if (size.cx > x) {
849 high = mid;
850 x_high = size.cx;
851 } else {
852 low = mid;
853 x_low = size.cx;
854 }
855 }
856 if (abs(x_high - x) <= abs(x_low - x) + 1)
857 index = high;
858 else
859 index = low;
860 }
861 if (es->style & ES_PASSWORD)
862 HeapFree(GetProcessHeap(), 0, text);
863 }
864 if (es->font)
865 SelectObject(dc, old_font);
866 ReleaseDC(es->hwndSelf, dc);
867 return index;
868 }
869
870
871 /*********************************************************************
872 *
873 * EDIT_ConfinePoint
874 *
875 * adjusts the point to be within the formatting rectangle
876 * (so CharFromPos returns the nearest _visible_ character)
877 *
878 */
879 static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y)
880 {
881 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
882 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
883 }
884
885
886 /*********************************************************************
887 *
888 * EM_LINEFROMCHAR
889 *
890 */
891 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
892 {
893 INT line;
894 LINEDEF *line_def;
895
896 if (!(es->style & ES_MULTILINE))
897 return 0;
898 if (index > (INT)get_text_length(es))
899 return es->line_count - 1;
900 if (index == -1)
901 index = min(es->selection_start, es->selection_end);
902
903 line = 0;
904 line_def = es->first_line_def;
905 index -= line_def->length;
906 while ((index >= 0) && line_def->next) {
907 line++;
908 line_def = line_def->next;
909 index -= line_def->length;
910 }
911 return line;
912 }
913
914
915 /*********************************************************************
916 *
917 * EM_LINEINDEX
918 *
919 */
920 static INT EDIT_EM_LineIndex(const EDITSTATE *es, INT line)
921 {
922 INT line_index;
923 const LINEDEF *line_def;
924
925 if (!(es->style & ES_MULTILINE))
926 return 0;
927 if (line >= es->line_count)
928 return -1;
929
930 line_index = 0;
931 line_def = es->first_line_def;
932 if (line == -1) {
933 INT index = es->selection_end - line_def->length;
934 while ((index >= 0) && line_def->next) {
935 line_index += line_def->length;
936 line_def = line_def->next;
937 index -= line_def->length;
938 }
939 } else {
940 while (line > 0) {
941 line_index += line_def->length;
942 line_def = line_def->next;
943 line--;
944 }
945 }
946 return line_index;
947 }
948
949
950 /*********************************************************************
951 *
952 * EM_LINELENGTH
953 *
954 */
955 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
956 {
957 LINEDEF *line_def;
958
959 if (!(es->style & ES_MULTILINE))
960 return get_text_length(es);
961
962 if (index == -1) {
963 /* get the number of remaining non-selected chars of selected lines */
964 INT32 l; /* line number */
965 INT32 li; /* index of first char in line */
966 INT32 count;
967 l = EDIT_EM_LineFromChar(es, es->selection_start);
968 /* # chars before start of selection area */
969 count = es->selection_start - EDIT_EM_LineIndex(es, l);
970 l = EDIT_EM_LineFromChar(es, es->selection_end);
971 /* # chars after end of selection */
972 li = EDIT_EM_LineIndex(es, l);
973 count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
974 return count;
975 }
976 line_def = es->first_line_def;
977 index -= line_def->length;
978 while ((index >= 0) && line_def->next) {
979 line_def = line_def->next;
980 index -= line_def->length;
981 }
982 return line_def->net_length;
983 }
984
985
986 /*********************************************************************
987 *
988 * EM_POSFROMCHAR
989 *
990 */
991 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap)
992 {
993 INT len = get_text_length(es);
994 INT l;
995 INT li;
996 INT x;
997 INT y = 0;
998 INT w;
999 INT lw = 0;
1000 INT ll = 0;
1001 HDC dc;
1002 HFONT old_font = 0;
1003 SIZE size;
1004 LINEDEF *line_def;
1005
1006 index = min(index, len);
1007 dc = GetDC(es->hwndSelf);
1008 if (es->font)
1009 old_font = SelectObject(dc, es->font);
1010 if (es->style & ES_MULTILINE) {
1011 l = EDIT_EM_LineFromChar(es, index);
1012 y = (l - es->y_offset) * es->line_height;
1013 li = EDIT_EM_LineIndex(es, l);
1014 if (after_wrap && (li == index) && l) {
1015 INT l2 = l - 1;
1016 line_def = es->first_line_def;
1017 while (l2) {
1018 line_def = line_def->next;
1019 l2--;
1020 }
1021 if (line_def->ending == END_WRAP) {
1022 l--;
1023 y -= es->line_height;
1024 li = EDIT_EM_LineIndex(es, l);
1025 }
1026 }
1027
1028 line_def = es->first_line_def;
1029 while (line_def->index != li)
1030 line_def = line_def->next;
1031
1032 ll = line_def->net_length;
1033 lw = line_def->width;
1034
1035 w = es->format_rect.right - es->format_rect.left;
1036 if (es->style & ES_RIGHT)
1037 {
1038 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li + (index - li), ll - (index - li),
1039 es->tabs_count, es->tabs)) - es->x_offset;
1040 x = w - x;
1041 }
1042 else if (es->style & ES_CENTER)
1043 {
1044 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
1045 es->tabs_count, es->tabs)) - es->x_offset;
1046 x += (w - lw) / 2;
1047 }
1048 else /* ES_LEFT */
1049 {
1050 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
1051 es->tabs_count, es->tabs)) - es->x_offset;
1052 }
1053 } else {
1054 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
1055 if (index < es->x_offset) {
1056 GetTextExtentPoint32W(dc, text + index,
1057 es->x_offset - index, &size);
1058 x = -size.cx;
1059 } else {
1060 GetTextExtentPoint32W(dc, text + es->x_offset,
1061 index - es->x_offset, &size);
1062 x = size.cx;
1063
1064 if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER)))
1065 {
1066 w = es->format_rect.right - es->format_rect.left;
1067 if (w > es->text_width)
1068 {
1069 if (es->style & ES_RIGHT)
1070 x += w - es->text_width;
1071 else if (es->style & ES_CENTER)
1072 x += (w - es->text_width) / 2;
1073 }
1074 }
1075 }
1076 y = 0;
1077 if (es->style & ES_PASSWORD)
1078 HeapFree(GetProcessHeap(), 0, text);
1079 }
1080 x += es->format_rect.left;
1081 y += es->format_rect.top;
1082 if (es->font)
1083 SelectObject(dc, old_font);
1084 ReleaseDC(es->hwndSelf, dc);
1085 return MAKELONG((INT16)x, (INT16)y);
1086 }
1087
1088
1089 /*********************************************************************
1090 *
1091 * EDIT_GetLineRect
1092 *
1093 * Calculates the bounding rectangle for a line from a starting
1094 * column to an ending column.
1095 *
1096 */
1097 static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1098 {
1099 INT line_index = EDIT_EM_LineIndex(es, line);
1100
1101 if (es->style & ES_MULTILINE)
1102 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1103 else
1104 rc->top = es->format_rect.top;
1105 rc->bottom = rc->top + es->line_height;
1106 rc->left = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
1107 rc->right = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
1108 }
1109
1110
1111 static inline void text_buffer_changed(EDITSTATE *es)
1112 {
1113 es->text_length = (UINT)-1;
1114 }
1115
1116 /*********************************************************************
1117 *
1118 * EDIT_LockBuffer
1119 *
1120 * This acts as a LocalLock16(), but it locks only once. This way
1121 * you can call it whenever you like, without unlocking.
1122 *
1123 * Initially the edit control allocates a HLOCAL32 buffer
1124 * (32 bit linear memory handler). However, 16 bit application
1125 * might send an EM_GETHANDLE message and expect a HLOCAL16 (16 bit SEG:OFF
1126 * handler). From that moment on we have to keep using this 16 bit memory
1127 * handler, because it is supposed to be valid at all times after EM_GETHANDLE.
1128 * What we do is create a HLOCAL16 buffer, copy the text, and do pointer
1129 * conversion.
1130 *
1131 */
1132 static void EDIT_LockBuffer(EDITSTATE *es)
1133 {
1134 #ifndef __REACTOS__
1135 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
1136 HINSTANCE16 hInstance = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
1137 #endif
1138 if (!es->text) {
1139 CHAR *textA = NULL;
1140 UINT countA = 0;
1141 #ifndef __REACTOS__
1142 BOOL _16bit = FALSE;
1143 #endif
1144
1145 if(es->hloc32W)
1146 {
1147 if(es->hloc32A)
1148 {
1149 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1150 textA = LocalLock(es->hloc32A);
1151 countA = strlen(textA) + 1;
1152 }
1153 #ifndef __REACTOS__
1154 else if(es->hloc16)
1155 {
1156 HANDLE16 oldDS = stack16->ds;
1157 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1158 stack16->ds = hInstance;
1159 textA = MapSL(LocalLock16(es->hloc16));
1160 stack16->ds = oldDS;
1161 countA = strlen(textA) + 1;
1162 _16bit = TRUE;
1163 }
1164 #endif
1165 }
1166 else {
1167 ERR("no buffer ... please report\n");
1168 return;
1169 }
1170
1171 if(textA)
1172 {
1173 HLOCAL hloc32W_new;
1174 UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
1175 TRACE("%d bytes translated to %d WCHARs\n", countA, countW_new);
1176 if(countW_new > es->buffer_size + 1)
1177 {
1178 UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1179 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1180 hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1181 if(hloc32W_new)
1182 {
1183 es->hloc32W = hloc32W_new;
1184 es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1185 TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1186 }
1187 else
1188 WARN("FAILED! Will synchronize partially\n");
1189 }
1190 }
1191
1192 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1193 es->text = LocalLock(es->hloc32W);
1194
1195 if(textA)
1196 {
1197 MultiByteToWideChar(CP_ACP, 0, textA, countA, es->text, es->buffer_size + 1);
1198 #ifndef __REACTOS__
1199 if(_16bit)
1200 {
1201 HANDLE16 oldDS = stack16->ds;
1202 stack16->ds = hInstance;
1203 LocalUnlock16(es->hloc16);
1204 stack16->ds = oldDS;
1205 }
1206 else
1207 #endif
1208 LocalUnlock(es->hloc32A);
1209 }
1210 }
1211 if(es->flags & EF_APP_HAS_HANDLE) text_buffer_changed(es);
1212 es->lock_count++;
1213 }
1214
1215
1216 /*********************************************************************
1217 *
1218 * EDIT_UnlockBuffer
1219 *
1220 */
1221 static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force)
1222 {
1223
1224 /* Edit window might be already destroyed */
1225 if(!IsWindow(es->hwndSelf))
1226 {
1227 WARN("edit hwnd %p already destroyed\n", es->hwndSelf);
1228 return;
1229 }
1230
1231 if (!es->lock_count) {
1232 ERR("lock_count == 0 ... please report\n");
1233 return;
1234 }
1235 if (!es->text) {
1236 ERR("es->text == 0 ... please report\n");
1237 return;
1238 }
1239
1240 if (force || (es->lock_count == 1)) {
1241 if (es->hloc32W) {
1242 CHAR *textA = NULL;
1243 UINT countA = 0;
1244 UINT countW = get_text_length(es) + 1;
1245 #ifndef __REACTOS__
1246 STACK16FRAME* stack16 = NULL;
1247 HANDLE16 oldDS = 0;
1248 #endif
1249
1250 if(es->hloc32A)
1251 {
1252 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
1253 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1254 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
1255 countA = LocalSize(es->hloc32A);
1256 if(countA_new > countA)
1257 {
1258 HLOCAL hloc32A_new;
1259 UINT alloc_size = ROUND_TO_GROW(countA_new);
1260 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
1261 hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1262 if(hloc32A_new)
1263 {
1264 es->hloc32A = hloc32A_new;
1265 countA = LocalSize(hloc32A_new);
1266 TRACE("Real new size %d bytes\n", countA);
1267 }
1268 else
1269 WARN("FAILED! Will synchronize partially\n");
1270 }
1271 textA = LocalLock(es->hloc32A);
1272 }
1273 #ifndef __REACTOS__
1274 else if(es->hloc16)
1275 {
1276 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
1277
1278 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1279 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
1280
1281 stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
1282 oldDS = stack16->ds;
1283 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
1284
1285 countA = LocalSize16(es->hloc16);
1286 if(countA_new > countA)
1287 {
1288 HLOCAL16 hloc16_new;
1289 UINT alloc_size = ROUND_TO_GROW(countA_new);
1290 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
1291 hloc16_new = LocalReAlloc16(es->hloc16, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1292 if(hloc16_new)
1293 {
1294 es->hloc16 = hloc16_new;
1295 countA = LocalSize16(hloc16_new);
1296 TRACE("Real new size %d bytes\n", countA);
1297 }
1298 else
1299 WARN("FAILED! Will synchronize partially\n");
1300 }
1301 textA = MapSL(LocalLock16(es->hloc16));
1302 }
1303 #endif
1304
1305 if(textA)
1306 {
1307 WideCharToMultiByte(CP_ACP, 0, es->text, countW, textA, countA, NULL, NULL);
1308 #ifndef __REACTOS__
1309 if(stack16)
1310 LocalUnlock16(es->hloc16);
1311 else
1312 #endif
1313 LocalUnlock(es->hloc32A);
1314 }
1315
1316 #ifndef __REACTOS__
1317 if (stack16) stack16->ds = oldDS;
1318 #endif
1319 LocalUnlock(es->hloc32W);
1320 es->text = NULL;
1321 }
1322 else {
1323 ERR("no buffer ... please report\n");
1324 return;
1325 }
1326 }
1327 es->lock_count--;
1328 }
1329
1330
1331 /*********************************************************************
1332 *
1333 * EDIT_MakeFit
1334 *
1335 * Try to fit size + 1 characters in the buffer.
1336 */
1337 static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size)
1338 {
1339 HLOCAL hNew32W;
1340
1341 if (size <= es->buffer_size)
1342 return TRUE;
1343
1344 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1345
1346 /* Force edit to unlock it's buffer. es->text now NULL */
1347 EDIT_UnlockBuffer(es, TRUE);
1348
1349 if (es->hloc32W) {
1350 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1351 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1352 TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W);
1353 es->hloc32W = hNew32W;
1354 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1355 }
1356 }
1357
1358 EDIT_LockBuffer(es);
1359
1360 if (es->buffer_size < size) {
1361 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1362 EDIT_NOTIFY_PARENT(es, EN_ERRSPACE);
1363 return FALSE;
1364 } else {
1365 TRACE("We now have %d+1\n", es->buffer_size);
1366 return TRUE;
1367 }
1368 }
1369
1370
1371 /*********************************************************************
1372 *
1373 * EDIT_MakeUndoFit
1374 *
1375 * Try to fit size + 1 bytes in the undo buffer.
1376 *
1377 */
1378 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1379 {
1380 UINT alloc_size;
1381
1382 if (size <= es->undo_buffer_size)
1383 return TRUE;
1384
1385 TRACE("trying to ReAlloc to %d+1\n", size);
1386
1387 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1388 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1389 es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1390 return TRUE;
1391 }
1392 else
1393 {
1394 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1395 return FALSE;
1396 }
1397 }
1398
1399
1400 /*********************************************************************
1401 *
1402 * EDIT_UpdateTextRegion
1403 *
1404 */
1405 static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
1406 {
1407 if (es->flags & EF_UPDATE) {
1408 es->flags &= ~EF_UPDATE;
1409 EDIT_NOTIFY_PARENT(es, EN_UPDATE);
1410 }
1411 InvalidateRgn(es->hwndSelf, hrgn, bErase);
1412 }
1413
1414
1415 /*********************************************************************
1416 *
1417 * EDIT_UpdateText
1418 *
1419 */
1420 static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase)
1421 {
1422 if (es->flags & EF_UPDATE) {
1423 es->flags &= ~EF_UPDATE;
1424 EDIT_NOTIFY_PARENT(es, EN_UPDATE);
1425 }
1426 InvalidateRect(es->hwndSelf, rc, bErase);
1427 }
1428
1429 /*********************************************************************
1430 *
1431 * EDIT_SL_InvalidateText
1432 *
1433 * Called from EDIT_InvalidateText().
1434 * Does the job for single-line controls only.
1435 *
1436 */
1437 static void EDIT_SL_InvalidateText(EDITSTATE *es, INT start, INT end)
1438 {
1439 RECT line_rect;
1440 RECT rc;
1441
1442 EDIT_GetLineRect(es, 0, start, end, &line_rect);
1443 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1444 EDIT_UpdateText(es, &rc, TRUE);
1445 }
1446
1447
1448 static inline INT get_vertical_line_count(EDITSTATE *es)
1449 {
1450 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1451 return max(1,vlc);
1452 }
1453
1454 /*********************************************************************
1455 *
1456 * EDIT_ML_InvalidateText
1457 *
1458 * Called from EDIT_InvalidateText().
1459 * Does the job for multi-line controls only.
1460 *
1461 */
1462 static void EDIT_ML_InvalidateText(EDITSTATE *es, INT start, INT end)
1463 {
1464 INT vlc = get_vertical_line_count(es);
1465 INT sl = EDIT_EM_LineFromChar(es, start);
1466 INT el = EDIT_EM_LineFromChar(es, end);
1467 INT sc;
1468 INT ec;
1469 RECT rc1;
1470 RECT rcWnd;
1471 RECT rcLine;
1472 RECT rcUpdate;
1473 INT l;
1474
1475 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1476 return;
1477
1478 sc = start - EDIT_EM_LineIndex(es, sl);
1479 ec = end - EDIT_EM_LineIndex(es, el);
1480 if (sl < es->y_offset) {
1481 sl = es->y_offset;
1482 sc = 0;
1483 }
1484 if (el > es->y_offset + vlc) {
1485 el = es->y_offset + vlc;
1486 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1487 }
1488 GetClientRect(es->hwndSelf, &rc1);
1489 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1490 if (sl == el) {
1491 EDIT_GetLineRect(es, sl, sc, ec, &rcLine);
1492 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1493 EDIT_UpdateText(es, &rcUpdate, TRUE);
1494 } else {
1495 EDIT_GetLineRect(es, sl, sc,
1496 EDIT_EM_LineLength(es,
1497 EDIT_EM_LineIndex(es, sl)),
1498 &rcLine);
1499 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1500 EDIT_UpdateText(es, &rcUpdate, TRUE);
1501 for (l = sl + 1 ; l < el ; l++) {
1502 EDIT_GetLineRect(es, l, 0,
1503 EDIT_EM_LineLength(es,
1504 EDIT_EM_LineIndex(es, l)),
1505 &rcLine);
1506 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1507 EDIT_UpdateText(es, &rcUpdate, TRUE);
1508 }
1509 EDIT_GetLineRect(es, el, 0, ec, &rcLine);
1510 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1511 EDIT_UpdateText(es, &rcUpdate, TRUE);
1512 }
1513 }
1514
1515
1516 /*********************************************************************
1517 *
1518 * EDIT_InvalidateText
1519 *
1520 * Invalidate the text from offset start up to, but not including,
1521 * offset end. Useful for (re)painting the selection.
1522 * Regions outside the linewidth are not invalidated.
1523 * end == -1 means end == TextLength.
1524 * start and end need not be ordered.
1525 *
1526 */
1527 static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end)
1528 {
1529 if (end == start)
1530 return;
1531
1532 if (end == -1)
1533 end = get_text_length(es);
1534
1535 if (end < start) {
1536 INT tmp = start;
1537 start = end;
1538 end = tmp;
1539 }
1540
1541 if (es->style & ES_MULTILINE)
1542 EDIT_ML_InvalidateText(es, start, end);
1543 else
1544 EDIT_SL_InvalidateText(es, start, end);
1545 }
1546
1547
1548 /*********************************************************************
1549 *
1550 * EDIT_EM_SetSel
1551 *
1552 * note: unlike the specs say: the order of start and end
1553 * _is_ preserved in Windows. (i.e. start can be > end)
1554 * In other words: this handler is OK
1555 *
1556 */
1557 static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
1558 {
1559 UINT old_start = es->selection_start;
1560 UINT old_end = es->selection_end;
1561 UINT len = get_text_length(es);
1562
1563 if (start == (UINT)-1) {
1564 start = es->selection_end;
1565 end = es->selection_end;
1566 } else {
1567 start = min(start, len);
1568 end = min(end, len);
1569 }
1570 es->selection_start = start;
1571 es->selection_end = end;
1572 if (after_wrap)
1573 es->flags |= EF_AFTER_WRAP;
1574 else
1575 es->flags &= ~EF_AFTER_WRAP;
1576 /* Compute the necessary invalidation region. */
1577 /* Note that we don't need to invalidate regions which have
1578 * "never" been selected, or those which are "still" selected.
1579 * In fact, every time we hit a selection boundary, we can
1580 * *toggle* whether we need to invalidate. Thus we can optimize by
1581 * *sorting* the interval endpoints. Let's assume that we sort them
1582 * in this order:
1583 * start <= end <= old_start <= old_end
1584 * Knuth 5.3.1 (p 183) assures us that this can be done optimally
1585 * in 5 comparisons; i.e. it is impossible to do better than the
1586 * following: */
1587 ORDER_UINT(end, old_end);
1588 ORDER_UINT(start, old_start);
1589 ORDER_UINT(old_start, old_end);
1590 ORDER_UINT(start, end);
1591 /* Note that at this point 'end' and 'old_start' are not in order, but
1592 * start is definitely the min. and old_end is definitely the max. */
1593 if (end != old_start)
1594 {
1595 /*
1596 * One can also do
1597 * ORDER_UINT32(end, old_start);
1598 * EDIT_InvalidateText(es, start, end);
1599 * EDIT_InvalidateText(es, old_start, old_end);
1600 * in place of the following if statement.
1601 * (That would complete the optimal five-comparison four-element sort.)
1602 */
1603 if (old_start > end )
1604 {
1605 EDIT_InvalidateText(es, start, end);
1606 EDIT_InvalidateText(es, old_start, old_end);
1607 }
1608 else
1609 {
1610 EDIT_InvalidateText(es, start, old_start);
1611 EDIT_InvalidateText(es, end, old_end);
1612 }
1613 }
1614 else EDIT_InvalidateText(es, start, old_end);
1615 }
1616
1617
1618 /*********************************************************************
1619 *
1620 * EDIT_UpdateScrollInfo
1621 *
1622 */
1623 static void EDIT_UpdateScrollInfo(EDITSTATE *es)
1624 {
1625 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
1626 {
1627 SCROLLINFO si;
1628 si.cbSize = sizeof(SCROLLINFO);
1629 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1630 si.nMin = 0;
1631 si.nMax = es->line_count - 1;
1632 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1633 si.nPos = es->y_offset;
1634 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1635 si.nMin, si.nMax, si.nPage, si.nPos);
1636 SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
1637 }
1638
1639 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
1640 {
1641 SCROLLINFO si;
1642 si.cbSize = sizeof(SCROLLINFO);
1643 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1644 si.nMin = 0;
1645 si.nMax = es->text_width - 1;
1646 si.nPage = es->format_rect.right - es->format_rect.left;
1647 si.nPos = es->x_offset;
1648 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1649 si.nMin, si.nMax, si.nPage, si.nPos);
1650 SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
1651 }
1652 }
1653
1654
1655 /*********************************************************************
1656 *
1657 * EDIT_EM_LineScroll_internal
1658 *
1659 * Version of EDIT_EM_LineScroll for internal use.
1660 * It doesn't refuse if ES_MULTILINE is set and assumes that
1661 * dx is in pixels, dy - in lines.
1662 *
1663 */
1664 static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy)
1665 {
1666 INT nyoff;
1667 INT x_offset_in_pixels;
1668 INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) /
1669 es->line_height;
1670
1671 if (es->style & ES_MULTILINE)
1672 {
1673 x_offset_in_pixels = es->x_offset;
1674 }
1675 else
1676 {
1677 dy = 0;
1678 x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE));
1679 }
1680
1681 if (-dx > x_offset_in_pixels)
1682 dx = -x_offset_in_pixels;
1683 if (dx > es->text_width - x_offset_in_pixels)
1684 dx = es->text_width - x_offset_in_pixels;
1685 nyoff = max(0, es->y_offset + dy);
1686 if (nyoff >= es->line_count - lines_per_page)
1687 nyoff = max(0, es->line_count - lines_per_page);
1688 dy = (es->y_offset - nyoff) * es->line_height;
1689 if (dx || dy) {
1690 RECT rc1;
1691 RECT rc;
1692
1693 es->y_offset = nyoff;
1694 if(es->style & ES_MULTILINE)
1695 es->x_offset += dx;
1696 else
1697 es->x_offset += dx / es->char_width;
1698
1699 GetClientRect(es->hwndSelf, &rc1);
1700 IntersectRect(&rc, &rc1, &es->format_rect);
1701 ScrollWindowEx(es->hwndSelf, -dx, dy,
1702 NULL, &rc, NULL, NULL, SW_INVALIDATE);
1703 /* force scroll info update */
1704 EDIT_UpdateScrollInfo(es);
1705 }
1706 if (dx && !(es->flags & EF_HSCROLL_TRACK))
1707 EDIT_NOTIFY_PARENT(es, EN_HSCROLL);
1708 if (dy && !(es->flags & EF_VSCROLL_TRACK))
1709 EDIT_NOTIFY_PARENT(es, EN_VSCROLL);
1710 return TRUE;
1711 }
1712
1713 /*********************************************************************
1714 *
1715 * EM_LINESCROLL
1716 *
1717 * NOTE: dx is in average character widths, dy - in lines;
1718 *
1719 */
1720 static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy)
1721 {
1722 if (!(es->style & ES_MULTILINE))
1723 return FALSE;
1724
1725 dx *= es->char_width;
1726 return EDIT_EM_LineScroll_internal(es, dx, dy);
1727 }
1728
1729
1730 /*********************************************************************
1731 *
1732 * EM_SCROLL
1733 *
1734 */
1735 static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action)
1736 {
1737 INT dy;
1738
1739 if (!(es->style & ES_MULTILINE))
1740 return (LRESULT)FALSE;
1741
1742 dy = 0;
1743
1744 switch (action) {
1745 case SB_LINEUP:
1746 if (es->y_offset)
1747 dy = -1;
1748 break;
1749 case SB_LINEDOWN:
1750 if (es->y_offset < es->line_count - 1)
1751 dy = 1;
1752 break;
1753 case SB_PAGEUP:
1754 if (es->y_offset)
1755 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
1756 break;
1757 case SB_PAGEDOWN:
1758 if (es->y_offset < es->line_count - 1)
1759 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1760 break;
1761 default:
1762 return (LRESULT)FALSE;
1763 }
1764 if (dy) {
1765 INT vlc = get_vertical_line_count(es);
1766 /* check if we are going to move too far */
1767 if(es->y_offset + dy > es->line_count - vlc)
1768 dy = es->line_count - vlc - es->y_offset;
1769
1770 /* Notification is done in EDIT_EM_LineScroll */
1771 if(dy)
1772 EDIT_EM_LineScroll(es, 0, dy);
1773 }
1774 #ifdef __REACTOS__
1775 return MAKELONG((SHORT)dy, (BOOL)TRUE);
1776 #else
1777 return MAKELONG((INT16)dy, (BOOL16)TRUE);
1778 #endif
1779 }
1780
1781
1782 /*********************************************************************
1783 *
1784 * EDIT_SetCaretPos
1785 *
1786 */
1787 static void EDIT_SetCaretPos(EDITSTATE *es, INT pos,
1788 BOOL after_wrap)
1789 {
1790 LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap);
1791 TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res));
1792 SetCaretPos((short)LOWORD(res), (short)HIWORD(res));
1793 }
1794
1795
1796 /*********************************************************************
1797 *
1798 * EM_SCROLLCARET
1799 *
1800 */
1801 static void EDIT_EM_ScrollCaret(EDITSTATE *es)
1802 {
1803 if (es->style & ES_MULTILINE) {
1804 INT l;
1805 INT vlc;
1806 INT ww;
1807 INT cw = es->char_width;
1808 INT x;
1809 INT dy = 0;
1810 INT dx = 0;
1811
1812 l = EDIT_EM_LineFromChar(es, es->selection_end);
1813 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP));
1814 vlc = get_vertical_line_count(es);
1815 if (l >= es->y_offset + vlc)
1816 dy = l - vlc + 1 - es->y_offset;
1817 if (l < es->y_offset)
1818 dy = l - es->y_offset;
1819 ww = es->format_rect.right - es->format_rect.left;
1820 if (x < es->format_rect.left)
1821 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1822 if (x > es->format_rect.right)
1823 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1824 if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1825 {
1826 /* check if we are going to move too far */
1827 if(es->x_offset + dx + ww > es->text_width)
1828 dx = es->text_width - ww - es->x_offset;
1829 if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1830 EDIT_EM_LineScroll_internal(es, dx, dy);
1831 }
1832 } else {
1833 INT x;
1834 INT goal;
1835 INT format_width;
1836
1837 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1838 format_width = es->format_rect.right - es->format_rect.left;
1839 if (x < es->format_rect.left) {
1840 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1841 do {
1842 es->x_offset--;
1843 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1844 } while ((x < goal) && es->x_offset);
1845 /* FIXME: use ScrollWindow() somehow to improve performance */
1846 EDIT_UpdateText(es, NULL, TRUE);
1847 } else if (x > es->format_rect.right) {
1848 INT x_last;
1849 INT len = get_text_length(es);
1850 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1851 do {
1852 es->x_offset++;
1853 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1854 x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE));
1855 } while ((x > goal) && (x_last > es->format_rect.right));
1856 /* FIXME: use ScrollWindow() somehow to improve performance */
1857 EDIT_UpdateText(es, NULL, TRUE);
1858 }
1859 }
1860
1861 if(es->flags & EF_FOCUSED)
1862 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
1863 }
1864
1865
1866 /*********************************************************************
1867 *
1868 * EDIT_MoveBackward
1869 *
1870 */
1871 static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend)
1872 {
1873 INT e = es->selection_end;
1874
1875 if (e) {
1876 e--;
1877 if ((es->style & ES_MULTILINE) && e &&
1878 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1879 e--;
1880 if (e && (es->text[e - 1] == '\r'))
1881 e--;
1882 }
1883 }
1884 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1885 EDIT_EM_ScrollCaret(es);
1886 }
1887
1888
1889 /*********************************************************************
1890 *
1891 * EDIT_MoveDown_ML
1892 *
1893 * Only for multi line controls
1894 * Move the caret one line down, on a column with the nearest
1895 * x coordinate on the screen (might be a different column).
1896 *
1897 */
1898 static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend)
1899 {
1900 INT s = es->selection_start;
1901 INT e = es->selection_end;
1902 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1903 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1904 INT x = (short)LOWORD(pos);
1905 INT y = (short)HIWORD(pos);
1906
1907 e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap);
1908 if (!extend)
1909 s = e;
1910 EDIT_EM_SetSel(es, s, e, after_wrap);
1911 EDIT_EM_ScrollCaret(es);
1912 }
1913
1914
1915 /*********************************************************************
1916 *
1917 * EDIT_MoveEnd
1918 *
1919 */
1920 static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl)
1921 {
1922 BOOL after_wrap = FALSE;
1923 INT e;
1924
1925 /* Pass a high value in x to make sure of receiving the end of the line */
1926 if (!ctrl && (es->style & ES_MULTILINE))
1927 e = EDIT_CharFromPos(es, 0x3fffffff,
1928 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1929 else
1930 e = get_text_length(es);
1931 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap);
1932 EDIT_EM_ScrollCaret(es);
1933 }
1934
1935
1936 /*********************************************************************
1937 *
1938 * EDIT_MoveForward
1939 *
1940 */
1941 static void EDIT_MoveForward(EDITSTATE *es, BOOL extend)
1942 {
1943 INT e = es->selection_end;
1944
1945 if (es->text[e]) {
1946 e++;
1947 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1948 if (es->text[e] == '\n')
1949 e++;
1950 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1951 e += 2;
1952 }
1953 }
1954 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1955 EDIT_EM_ScrollCaret(es);
1956 }
1957
1958
1959 /*********************************************************************
1960 *
1961 * EDIT_MoveHome
1962 *
1963 * Home key: move to beginning of line.
1964 *
1965 */
1966 static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl)
1967 {
1968 INT e;
1969
1970 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1971 if (!ctrl && (es->style & ES_MULTILINE))
1972 e = EDIT_CharFromPos(es, -es->x_offset,
1973 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1974 else
1975 e = 0;
1976 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1977 EDIT_EM_ScrollCaret(es);
1978 }
1979
1980
1981 /*********************************************************************
1982 *
1983 * EDIT_MovePageDown_ML
1984 *
1985 * Only for multi line controls
1986 * Move the caret one page down, on a column with the nearest
1987 * x coordinate on the screen (might be a different column).
1988 *
1989 */
1990 static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend)
1991 {
1992 INT s = es->selection_start;
1993 INT e = es->selection_end;
1994 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1995 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1996 INT x = (short)LOWORD(pos);
1997 INT y = (short)HIWORD(pos);
1998
1999 e = EDIT_CharFromPos(es, x,
2000 y + (es->format_rect.bottom - es->format_rect.top),
2001 &after_wrap);
2002 if (!extend)
2003 s = e;
2004 EDIT_EM_SetSel(es, s, e, after_wrap);
2005 EDIT_EM_ScrollCaret(es);
2006 }
2007
2008
2009 /*********************************************************************
2010 *
2011 * EDIT_MovePageUp_ML
2012 *
2013 * Only for multi line controls
2014 * Move the caret one page up, on a column with the nearest
2015 * x coordinate on the screen (might be a different column).
2016 *
2017 */
2018 static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend)
2019 {
2020 INT s = es->selection_start;
2021 INT e = es->selection_end;
2022 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2023 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2024 INT x = (short)LOWORD(pos);
2025 INT y = (short)HIWORD(pos);
2026
2027 e = EDIT_CharFromPos(es, x,
2028 y - (es->format_rect.bottom - es->format_rect.top),
2029 &after_wrap);
2030 if (!extend)
2031 s = e;
2032 EDIT_EM_SetSel(es, s, e, after_wrap);
2033 EDIT_EM_ScrollCaret(es);
2034 }
2035
2036
2037 /*********************************************************************
2038 *
2039 * EDIT_MoveUp_ML
2040 *
2041 * Only for multi line controls
2042 * Move the caret one line up, on a column with the nearest
2043 * x coordinate on the screen (might be a different column).
2044 *
2045 */
2046 static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend)
2047 {
2048 INT s = es->selection_start;
2049 INT e = es->selection_end;
2050 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2051 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2052 INT x = (short)LOWORD(pos);
2053 INT y = (short)HIWORD(pos);
2054
2055 e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap);
2056 if (!extend)
2057 s = e;
2058 EDIT_EM_SetSel(es, s, e, after_wrap);
2059 EDIT_EM_ScrollCaret(es);
2060 }
2061
2062
2063 /*********************************************************************
2064 *
2065 * EDIT_MoveWordBackward
2066 *
2067 */
2068 static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend)
2069 {
2070 INT s = es->selection_start;
2071 INT e = es->selection_end;
2072 INT l;
2073 INT ll;
2074 INT li;
2075
2076 l = EDIT_EM_LineFromChar(es, e);
2077 ll = EDIT_EM_LineLength(es, e);
2078 li = EDIT_EM_LineIndex(es, l);
2079 if (e - li == 0) {
2080 if (l) {
2081 li = EDIT_EM_LineIndex(es, l - 1);
2082 e = li + EDIT_EM_LineLength(es, li);
2083 }
2084 } else {
2085 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
2086 }
2087 if (!extend)
2088 s = e;
2089 EDIT_EM_SetSel(es, s, e, FALSE);
2090 EDIT_EM_ScrollCaret(es);
2091 }
2092
2093
2094 /*********************************************************************
2095 *
2096 * EDIT_MoveWordForward
2097 *
2098 */
2099 static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend)
2100 {
2101 INT s = es->selection_start;
2102 INT e = es->selection_end;
2103 INT l;
2104 INT ll;
2105 INT li;
2106
2107 l = EDIT_EM_LineFromChar(es, e);
2108 ll = EDIT_EM_LineLength(es, e);
2109 li = EDIT_EM_LineIndex(es, l);
2110 if (e - li == ll) {
2111 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2112 e = EDIT_EM_LineIndex(es, l + 1);
2113 } else {
2114 e = li + EDIT_CallWordBreakProc(es,
2115 li, e - li + 1, ll, WB_RIGHT);
2116 }
2117 if (!extend)
2118 s = e;
2119 EDIT_EM_SetSel(es, s, e, FALSE);
2120 EDIT_EM_ScrollCaret(es);
2121 }
2122
2123
2124 /*********************************************************************
2125 *
2126 * EDIT_PaintText
2127 *
2128 */
2129 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2130 {
2131 COLORREF BkColor;
2132 COLORREF TextColor;
2133 LOGFONTW underline_font;
2134 HFONT hUnderline = 0;
2135 HFONT old_font = 0;
2136 INT ret;
2137 INT li;
2138 INT BkMode;
2139 SIZE size;
2140
2141 if (!count)
2142 return 0;
2143 BkMode = GetBkMode(dc);
2144 BkColor = GetBkColor(dc);
2145 TextColor = GetTextColor(dc);
2146 if (rev) {
2147 if (es->composition_len == 0)
2148 {
2149 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2150 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2151 SetBkMode( dc, OPAQUE);
2152 }
2153 else
2154 {
2155 HFONT current = GetCurrentObject(dc,OBJ_FONT);
2156 GetObjectW(current,sizeof(LOGFONTW),&underline_font);
2157 underline_font.lfUnderline = TRUE;
2158 hUnderline = CreateFontIndirectW(&underline_font);
2159 old_font = SelectObject(dc,hUnderline);
2160 }
2161 }
2162 li = EDIT_EM_LineIndex(es, line);
2163 if (es->style & ES_MULTILINE) {
2164 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2165 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2166 } else {
2167 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2168 TextOutW(dc, x, y, text + li + col, count);
2169 GetTextExtentPoint32W(dc, text + li + col, count, &size);
2170 ret = size.cx;
2171 if (es->style & ES_PASSWORD)
2172 HeapFree(GetProcessHeap(), 0, text);
2173 }
2174 if (rev) {
2175 if (es->composition_len == 0)
2176 {
2177 SetBkColor(dc, BkColor);
2178 SetTextColor(dc, TextColor);
2179 SetBkMode( dc, BkMode);
2180 }
2181 else
2182 {
2183 if (old_font)
2184 SelectObject(dc,old_font);
2185 if (hUnderline)
2186 DeleteObject(hUnderline);
2187 }
2188 }
2189 return ret;
2190 }
2191
2192
2193 /*********************************************************************
2194 *
2195 * EDIT_PaintLine
2196 *
2197 */
2198 static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev)
2199 {
2200 INT s = es->selection_start;
2201 INT e = es->selection_end;
2202 INT li;
2203 INT ll;
2204 INT x;
2205 INT y;
2206 LRESULT pos;
2207
2208 if (es->style & ES_MULTILINE) {
2209 INT vlc = get_vertical_line_count(es);
2210
2211 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2212 return;
2213 } else if (line)
2214 return;
2215
2216 TRACE("line=%d\n", line);
2217
2218 pos = EDIT_EM_PosFromChar(es, EDIT_EM_LineIndex(es, line), FALSE);
2219 x = (short)LOWORD(pos);
2220 y = (short)HIWORD(pos);
2221 li = EDIT_EM_LineIndex(es, line);
2222 ll = EDIT_EM_LineLength(es, li);
2223 s = min(es->selection_start, es->selection_end);
2224 e = max(es->selection_start, es->selection_end);
2225 s = min(li + ll, max(li, s));
2226 e = min(li + ll, max(li, e));
2227 if (rev && (s != e) &&
2228 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2229 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2230 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2231 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2232 } else
2233 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2234 }
2235
2236
2237 /*********************************************************************
2238 *
2239 * EDIT_AdjustFormatRect
2240 *
2241 * Adjusts the format rectangle for the current font and the
2242 * current client rectangle.
2243 *
2244 */
2245 static void EDIT_AdjustFormatRect(EDITSTATE *es)
2246 {
2247 RECT ClientRect;
2248
2249 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2250 if (es->style & ES_MULTILINE)
2251 {
2252 INT fw, vlc, max_x_offset, max_y_offset;
2253
2254 vlc = get_vertical_line_count(es);
2255 es->format_rect.bottom = es->format_rect.top + vlc * es->line_height;
2256
2257 /* correct es->x_offset */
2258 fw = es->format_rect.right - es->format_rect.left;
2259 max_x_offset = es->text_width - fw;
2260 if(max_x_offset < 0) max_x_offset = 0;
2261 if(es->x_offset > max_x_offset)
2262 es->x_offset = max_x_offset;
2263
2264 /* correct es->y_offset */
2265 max_y_offset = es->line_count - vlc;
2266 if(max_y_offset < 0) max_y_offset = 0;
2267 if(es->y_offset > max_y_offset)
2268 es->y_offset = max_y_offset;
2269
2270 /* force scroll info update */
2271 EDIT_UpdateScrollInfo(es);
2272 }
2273 else
2274 /* Windows doesn't care to fix text placement for SL controls */
2275 es->format_rect.bottom = es->format_rect.top + es->line_height;
2276
2277 /* Always stay within the client area */
2278 GetClientRect(es->hwndSelf, &ClientRect);
2279 es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom);
2280
2281 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2282 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2283
2284 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
2285 }
2286
2287
2288 /*********************************************************************
2289 *
2290 * EDIT_SetRectNP
2291 *
2292 * note: this is not (exactly) the handler called on EM_SETRECTNP
2293 * it is also used to set the rect of a single line control
2294 *
2295 */
2296 static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc)
2297 {
2298 LONG_PTR ExStyle;
2299 INT bw, bh;
2300 ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE);
2301
2302 CopyRect(&es->format_rect, rc);
2303
2304 if (ExStyle & WS_EX_CLIENTEDGE) {
2305 es->format_rect.left++;
2306 es->format_rect.right--;
2307
2308 if (es->format_rect.bottom - es->format_rect.top
2309 >= es->line_height + 2)
2310 {
2311 es->format_rect.top++;
2312 es->format_rect.bottom--;
2313 }
2314 }
2315 else if (es->style & WS_BORDER) {
2316 bw = GetSystemMetrics(SM_CXBORDER) + 1;
2317 bh = GetSystemMetrics(SM_CYBORDER) + 1;
2318 es->format_rect.left += bw;
2319 es->format_rect.right -= bw;
2320 if (es->format_rect.bottom - es->format_rect.top
2321 >= es->line_height + 2 * bh)
2322 {
2323 es->format_rect.top += bh;
2324 es->format_rect.bottom -= bh;
2325 }
2326 }
2327
2328 es->format_rect.left += es->left_margin;
2329 es->format_rect.right -= es->right_margin;
2330 EDIT_AdjustFormatRect(es);
2331 }
2332
2333
2334 /*********************************************************************
2335 *
2336 * EM_CHARFROMPOS
2337 *
2338 * returns line number (not index) in high-order word of result.
2339 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2340 * to Richedit, not to the edit control. Original documentation is valid.
2341 * FIXME: do the specs mean to return -1 if outside client area or
2342 * if outside formatting rectangle ???
2343 *
2344 */
2345 static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y)
2346 {
2347 POINT pt;
2348 RECT rc;
2349 INT index;
2350
2351 pt.x = x;
2352 pt.y = y;
2353 GetClientRect(es->hwndSelf, &rc);
2354 if (!PtInRect(&rc, pt))
2355 return -1;
2356
2357 index = EDIT_CharFromPos(es, x, y, NULL);
2358 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2359 }
2360
2361
2362 /*********************************************************************
2363 *
2364 * EM_FMTLINES
2365 *
2366 * Enable or disable soft breaks.
2367 *
2368 * This means: insert or remove the soft linebreak character (\r\r\n).
2369 * Take care to check if the text still fits the buffer after insertion.
2370 * If not, notify with EN_ERRSPACE.
2371 *
2372 */
2373 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2374 {
2375 es->flags &= ~EF_USE_SOFTBRK;
2376 if (add_eol) {
2377 es->flags |= EF_USE_SOFTBRK;
2378 FIXME("soft break enabled, not implemented\n");
2379 }
2380 return add_eol;
2381 }
2382
2383
2384 /*********************************************************************
2385 *
2386 * EM_GETHANDLE
2387 *
2388 * Hopefully this won't fire back at us.
2389 * We always start with a fixed buffer in the local heap.
2390 * Despite of the documentation says that the local heap is used
2391 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2392 * buffer on the local heap.
2393 *
2394 */
2395 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2396 {
2397 HLOCAL hLocal;
2398
2399 if (!(es->style & ES_MULTILINE))
2400 return 0;
2401
2402 if(es->is_unicode)
2403 hLocal = es->hloc32W;
2404 else
2405 {
2406 if(!es->hloc32A)
2407 {
2408 CHAR *textA;
2409 UINT countA, alloc_size;
2410 TRACE("Allocating 32-bit ANSI alias buffer\n");
2411 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2412 alloc_size = ROUND_TO_GROW(countA);
2413 if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2414 {
2415 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2416 return 0;
2417 }
2418 textA = LocalLock(es->hloc32A);
2419 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2420 LocalUnlock(es->hloc32A);
2421 }
2422 hLocal = es->hloc32A;
2423 }
2424
2425 es->flags |= EF_APP_HAS_HANDLE;
2426 TRACE("Returning %p, LocalSize() = %ld\n", hLocal, LocalSize(hLocal));
2427 return hLocal;
2428 }
2429
2430
2431 #ifndef __REACTOS__
2432 /*********************************************************************
2433 *
2434 * EM_GETHANDLE16
2435 *
2436 * Hopefully this won't fire back at us.
2437 * We always start with a buffer in 32 bit linear memory.
2438 * However, with this message a 16 bit application requests
2439 * a handle of 16 bit local heap memory, where it expects to find
2440 * the text.
2441 * It's a pitty that from this moment on we have to use this
2442 * local heap, because applications may rely on the handle
2443 * in the future.
2444 *
2445 * In this function we'll try to switch to local heap.
2446 */
2447 static HLOCAL16 EDIT_EM_GetHandle16(EDITSTATE *es)
2448 {
2449 CHAR *textA;
2450 UINT countA, alloc_size;
2451 STACK16FRAME* stack16;
2452 HANDLE16 oldDS;
2453
2454 if (!(es->style & ES_MULTILINE))
2455 return 0;
2456
2457 if (es->hloc16)
2458 return es->hloc16;
2459
2460 stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2461 oldDS = stack16->ds;
2462 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2463
2464 if (!LocalHeapSize16()) {
2465
2466 if (!LocalInit16(stack16->ds, 0, GlobalSize16(stack16->ds))) {
2467 ERR("could not initialize local heap\n");
2468 goto done;
2469 }
2470 TRACE("local heap initialized\n");
2471 }
2472
2473 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2474 alloc_size = ROUND_TO_GROW(countA);
2475
2476 TRACE("Allocating 16-bit ANSI alias buffer\n");
2477 if (!(es->hloc16 = LocalAlloc16(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) {
2478 ERR("could not allocate new 16 bit buffer\n");
2479 goto done;
2480 }
2481
2482 if (!(textA = MapSL(LocalLock16( es->hloc16)))) {
2483 ERR("could not lock new 16 bit buffer\n");
2484 LocalFree16(es->hloc16);
2485 es->hloc16 = 0;
2486 goto done;
2487 }
2488
2489 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2490 LocalUnlock16(es->hloc16);
2491 es->flags |= EF_APP_HAS_HANDLE;
2492
2493 TRACE("Returning %04X, LocalSize() = %d\n", es->hloc16, LocalSize16(es->hloc16));
2494
2495 done:
2496 stack16->ds = oldDS;
2497 return es->hloc16;
2498 }
2499 #endif
2500
2501
2502 /*********************************************************************
2503 *
2504 * EM_GETLINE
2505 *
2506 */
2507 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst, BOOL unicode)
2508 {
2509 LPWSTR src;
2510 INT line_len, dst_len;
2511 INT i;
2512
2513 if (es->style & ES_MULTILINE) {
2514 if (line >= es->line_count)
2515 return 0;
2516 } else
2517 line = 0;
2518 i = EDIT_EM_LineIndex(es, line);
2519 src = es->text + i;
2520 line_len = EDIT_EM_LineLength(es, i);
2521 dst_len = *(WORD *)dst;
2522 if(unicode)
2523 {
2524 if(dst_len <= line_len)
2525 {
2526 memcpy(dst, src, dst_len * sizeof(WCHAR));
2527 return dst_len;
2528 }
2529 else /* Append 0 if enough space */
2530 {
2531 memcpy(dst, src, line_len * sizeof(WCHAR));
2532 dst[line_len] = 0;
2533 return line_len;
2534 }
2535 }
2536 else
2537 {
2538 INT ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, (LPSTR)dst, dst_len, NULL, NULL);
2539 if(!ret && line_len) /* Insufficient buffer size */
2540 return dst_len;
2541 if(ret < dst_len) /* Append 0 if enough space */
2542 ((LPSTR)dst)[ret] = 0;
2543 return ret;
2544 }
2545 }
2546
2547
2548 /*********************************************************************
2549 *
2550 * EM_GETSEL
2551 *
2552 */
2553 static LRESULT EDIT_EM_GetSel(const EDITSTATE *es, PUINT start, PUINT end)
2554 {
2555 UINT s = es->selection_start;
2556 UINT e = es->selection_end;
2557
2558 ORDER_UINT(s, e);
2559 if (start)
2560 *start = s;
2561 if (end)
2562 *end = e;
2563 return MAKELONG(s, e);
2564 }
2565
2566
2567 /*********************************************************************
2568 *
2569 * EM_REPLACESEL
2570 *
2571 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2572 *
2573 */
2574 static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update, BOOL honor_limit)
2575 {
2576 UINT strl = strlenW(lpsz_replace);
2577 UINT tl = get_text_length(es);
2578 UINT utl;
2579 UINT s;
2580 UINT e;
2581 UINT i;
2582 UINT size;
2583 LPWSTR p;
2584 HRGN hrgn = 0;
2585 LPWSTR buf = NULL;
2586 UINT bufl = 0;
2587
2588 TRACE("%s, can_undo %d, send_update %d\n",
2589 debugstr_w(lpsz_replace), can_undo, send_update);
2590
2591 s = es->selection_start;
2592 e = es->selection_end;
2593
2594 if ((s == e) && !strl)
2595 return;
2596
2597 ORDER_UINT(s, e);
2598
2599 size = tl - (e - s) + strl;
2600 if (!size)
2601 es->text_width = 0;
2602
2603 /* Issue the EN_MAXTEXT notification and continue with replacing text
2604 * such that buffer limit is honored. */
2605 if ((honor_limit) && (size > es->buffer_limit)) {
2606 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2607 /* Buffer limit can be smaller than the actual length of text in combobox */
2608 if (es->buffer_limit < (tl - (e-s)))
2609 strl = 0;
2610 else
2611 strl = es->buffer_limit - (tl - (e-s));
2612 }
2613
2614 if (!EDIT_MakeFit(es, tl - (e - s) + strl))
2615 return;
2616
2617 if (e != s) {
2618 /* there is something to be deleted */
2619 TRACE("deleting stuff.\n");
2620 bufl = e - s;
2621 buf = HeapAlloc(GetProcessHeap(), 0, (bufl + 1) * sizeof(WCHAR));
2622 if (!buf) return;
2623 memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
2624 buf[bufl] = 0; /* ensure 0 termination */
2625 /* now delete */
2626 strcpyW(es->text + s, es->text + e);
2627 text_buffer_changed(es);
2628 }
2629 if (strl) {
2630 /* there is an insertion */
2631 tl = get_text_length(es);
2632 TRACE("inserting stuff (tl %d, strl %d, selstart %d (%s), text %s)\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text));
2633 for (p = es->text + tl ; p >= es->text + s ; p--)
2634 p[strl] = p[0];
2635 for (i = 0 , p = es->text + s ; i < strl ; i++)
2636 p[i] = lpsz_replace[i];
2637 if(es->style & ES_UPPERCASE)
2638 CharUpperBuffW(p, strl);
2639 else if(es->style & ES_LOWERCASE)
2640 CharLowerBuffW(p, strl);
2641 text_buffer_changed(es);
2642 }
2643 if (es->style & ES_MULTILINE)
2644 {
2645 INT st = min(es->selection_start, es->selection_end);
2646 INT vlc = get_vertical_line_count(es);
2647
2648 hrgn = CreateRectRgn(0, 0, 0, 0);
2649 EDIT_BuildLineDefs_ML(es, st, st + strl,
2650 strl - abs(es->selection_end - es->selection_start), hrgn);
2651 /* if text is too long undo all changes */
2652 if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
2653 if (strl)
2654 strcpyW(es->text + e, es->text + e + strl);
2655 if (e != s)
2656 for (i = 0 , p = es->text ; i < e - s ; i++)
2657 p[i + s] = buf[i];
2658 text_buffer_changed(es);
2659 EDIT_BuildLineDefs_ML(es, s, e,
2660 abs(es->selection_end - es->selection_start) - strl, hrgn);
2661 strl = 0;
2662 e = s;
2663 hrgn = CreateRectRgn(0, 0, 0, 0);
2664 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2665 }
2666 }
2667 else {
2668 INT fw = es->format_rect.right - es->format_rect.left;
2669 EDIT_CalcLineWidth_SL(es);
2670 /* remove chars that don't fit */
2671 if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
2672 while ((es->text_width > fw) && s + strl >= s) {
2673 strcpyW(es->text + s + strl - 1, es->text + s + strl);
2674 strl--;
2675 EDIT_CalcLineWidth_SL(es);
2676 }
2677 text_buffer_changed(es);
2678 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2679 }
2680 }
2681
2682 if (e != s) {
2683 if (can_undo) {
2684 utl = strlenW(es->undo_text);
2685 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2686 /* undo-buffer is extended to the right */
2687 EDIT_MakeUndoFit(es, utl + e - s);
2688 memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR));
2689 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2690 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2691 /* undo-buffer is extended to the left */
2692 EDIT_MakeUndoFit(es, utl + e - s);
2693 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2694 p[e - s] = p[0];
2695 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2696 p[i] = buf[i];
2697 es->undo_position = s;
2698 } else {
2699 /* new undo-buffer */
2700 EDIT_MakeUndoFit(es, e - s);
2701 memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR));
2702 es->undo_text[e - s] = 0; /* ensure 0 termination */
2703 es->undo_position = s;
2704 }
2705 /* any deletion makes the old insertion-undo invalid */
2706 es->undo_insert_count = 0;
2707 } else
2708 EDIT_EM_EmptyUndoBuffer(es);
2709 }
2710 if (strl) {
2711 if (can_undo) {
2712 if ((s == es->undo_position) ||
2713 ((es->undo_insert_count) &&
2714 (s == es->undo_position + es->undo_insert_count)))
2715 /*
2716 * insertion is new and at delete position or
2717 * an extension to either left or right
2718 */
2719 es->undo_insert_count += strl;
2720 else {
2721 /* new insertion undo */
2722 es->undo_position = s;
2723 es->undo_insert_count = strl;
2724 /* new insertion makes old delete-buffer invalid */
2725 *es->undo_text = '\0';
2726 }
2727 } else
2728 EDIT_EM_EmptyUndoBuffer(es);
2729 }
2730
2731 if (bufl)
2732 HeapFree(GetProcessHeap(), 0, buf);
2733
2734 s += strl;
2735
2736 /* If text has been deleted and we're right or center aligned then scroll rightward */
2737 if (es->style & (ES_RIGHT | ES_CENTER))
2738 {
2739 INT delta = strl - abs(es->selection_end - es->selection_start);
2740
2741 if (delta < 0 && es->x_offset)
2742 {
2743 if (abs(delta) > es->x_offset)
2744 es->x_offset = 0;
2745 else
2746 es->x_offset += delta;
2747 }
2748 }
2749
2750 EDIT_EM_SetSel(es, s, s, FALSE);
2751 es->flags |= EF_MODIFIED;
2752 if (send_update) es->flags |= EF_UPDATE;
2753 if (hrgn)
2754 {
2755 EDIT_UpdateTextRegion(es, hrgn, TRUE);
2756 DeleteObject(hrgn);
2757 }
2758 else
2759 EDIT_UpdateText(es, NULL, TRUE);
2760
2761 EDIT_EM_ScrollCaret(es);
2762
2763 /* force scroll info update */
2764 EDIT_UpdateScrollInfo(es);
2765
2766
2767 if(send_update || (es->flags & EF_UPDATE))
2768 {
2769 es->flags &= ~EF_UPDATE;
2770 EDIT_NOTIFY_PARENT(es, EN_CHANGE);
2771 }
2772 }
2773
2774
2775 /*********************************************************************
2776 *
2777 * EM_SETHANDLE
2778 *
2779 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2780 *
2781 */
2782 static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc)
2783 {
2784 if (!(es->style & ES_MULTILINE))
2785 return;
2786
2787 if (!hloc) {
2788 WARN("called with NULL handle\n");
2789 return;
2790 }
2791
2792 EDIT_UnlockBuffer(es, TRUE);
2793
2794 #ifndef __REACTOS__
2795 if(es->hloc16)
2796 {
2797 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2798 HANDLE16 oldDS = stack16->ds;
2799
2800 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2801 LocalFree16(es->hloc16);
2802 stack16->ds = oldDS;
2803 es->hloc16 = 0;
2804 }
2805 #endif
2806
2807 if(es->is_unicode)
2808 {
2809 if(es->hloc32A)
2810 {
2811 LocalFree(es->hloc32A);
2812 es->hloc32A = NULL;
2813 }
2814 es->hloc32W = hloc;
2815 }
2816 else
2817 {
2818 INT countW, countA;
2819 HLOCAL hloc32W_new;
2820 WCHAR *textW;
2821 CHAR *textA;
2822
2823 countA = LocalSize(hloc);
2824 textA = LocalLock(hloc);
2825 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
2826 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
2827 {
2828 ERR("Could not allocate new unicode buffer\n");
2829 return;
2830 }
2831 textW = LocalLock(hloc32W_new);
2832 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
2833 LocalUnlock(hloc32W_new);
2834 LocalUnlock(hloc);
2835
2836 if(es->hloc32W)
2837 LocalFree(es->hloc32W);
2838
2839 es->hloc32W = hloc32W_new;
2840 es->hloc32A = hloc;
2841 }
2842
2843 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2844
2845 es->flags |= EF_APP_HAS_HANDLE;
2846 EDIT_LockBuffer(es);
2847
2848 es->x_offset = es->y_offset = 0;
2849 es->selection_start = es->selection_end = 0;
2850 EDIT_EM_EmptyUndoBuffer(es);
2851 es->flags &= ~EF_MODIFIED;
2852 es->flags &= ~EF_UPDATE;
2853 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2854 EDIT_UpdateText(es, NULL, TRUE);
2855 EDIT_EM_ScrollCaret(es);
2856 /* force scroll info update */
2857 EDIT_UpdateScrollInfo(es);
2858 }
2859
2860
2861 #ifndef __REACTOS__
2862 /*********************************************************************
2863 *
2864 * EM_SETHANDLE16
2865 *
2866 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2867 *
2868 */
2869 static void EDIT_EM_SetHandle16(EDITSTATE *es, HLOCAL16 hloc)
2870 {
2871 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2872 HINSTANCE16 hInstance = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2873 HANDLE16 oldDS = stack16->ds;
2874 INT countW, countA;
2875 HLOCAL hloc32W_new;
2876 WCHAR *textW;
2877 CHAR *textA;
2878
2879 if (!(es->style & ES_MULTILINE))
2880 return;
2881
2882 if (!hloc) {
2883 WARN("called with NULL handle\n");
2884 return;
2885 }
2886
2887 EDIT_UnlockBuffer(es, TRUE);
2888
2889 if(es->hloc32A)
2890 {
2891 LocalFree(es->hloc32A);
2892 es->hloc32A = NULL;
2893 }
2894
2895 stack16->ds = hInstance;
2896 countA = LocalSize16(hloc);
2897 textA = MapSL(LocalLock16(hloc));
2898 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
2899 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
2900 {
2901 ERR("Could not allocate new unicode buffer\n");
2902 return;
2903 }
2904 textW = LocalLock(hloc32W_new);
2905 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
2906 LocalUnlock(hloc32W_new);
2907 LocalUnlock16(hloc);
2908 stack16->ds = oldDS;
2909
2910 if(es->hloc32W)
2911 LocalFree(es->hloc32W);
2912
2913 es->hloc32W = hloc32W_new;
2914 es->hloc16 = hloc;
2915
2916 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2917
2918 es->flags |= EF_APP_HAS_HANDLE;
2919 EDIT_LockBuffer(es);
2920
2921 es->x_offset = es->y_offset = 0;
2922 es->selection_start = es->selection_end = 0;
2923 EDIT_EM_EmptyUndoBuffer(es);
2924 es->flags &= ~EF_MODIFIED;
2925 es->flags &= ~EF_UPDATE;
2926 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2927 EDIT_UpdateText(es, NULL, TRUE);
2928 EDIT_EM_ScrollCaret(es);
2929 /* force scroll info update */
2930 EDIT_UpdateScrollInfo(es);
2931 }
2932 #endif
2933
2934
2935 /*********************************************************************
2936 *
2937 * EM_SETLIMITTEXT
2938 *
2939 * NOTE: this version currently implements WinNT limits
2940 *
2941 */
2942 static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
2943 {
2944 if (!limit) limit = ~0u;
2945 if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe);
2946 es->buffer_limit = limit;
2947 }
2948
2949
2950 /*********************************************************************
2951 *
2952 * EM_SETMARGINS
2953 *
2954 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2955 * action wParam despite what the docs say. EC_USEFONTINFO calculates the
2956 * margin according to the textmetrics of the current font.
2957 *
2958 * FIXME - With TrueType or vector fonts EC_USEFONTINFO currently sets one third
2959 * of the char's width as the margin, but this is not how Windows handles this.
2960 * For all other fonts Windows sets the margins to zero.
2961 *
2962 * FIXME - When EC_USEFONTINFO is used the margins only change if the
2963 * edit control is equal to or larger than a certain size.
2964 * Interestingly if one subtracts both the left and right margins from
2965 * this size one always seems to get an even number. The extents of
2966 * the (four character) string "'**'" match this quite closely, so
2967 * we'll use this until we come up with a better idea.
2968 */
2969 static int calc_min_set_margin_size(HDC dc, INT left, INT right)
2970 {
2971 WCHAR magic_string[] = {'\'','*','*','\'', 0};
2972 SIZE sz;
2973
2974 GetTextExtentPointW(dc, magic_string, sizeof(magic_string)/sizeof(WCHAR) - 1, &sz);
2975 return sz.cx + left + right;
2976 }
2977
2978 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
2979 WORD left, WORD right, BOOL repaint)
2980 {
2981 TEXTMETRICW tm;
2982 INT default_left_margin = 0; /* in pixels */
2983 INT default_right_margin = 0; /* in pixels */
2984
2985 /* Set the default margins depending on the font */
2986 if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
2987 HDC dc = GetDC(es->hwndSelf);
2988 HFONT old_font = SelectObject(dc, es->font);
2989 GetTextMetricsW(dc, &tm);
2990 /* The default margins are only non zero for TrueType or Vector fonts */
2991 if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
2992 int min_size;
2993 RECT rc;
2994 /* This must be calculated more exactly! But how? */
2995 default_left_margin = tm.tmAveCharWidth / 2;
2996 default_right_margin = tm.tmAveCharWidth / 2;
2997 min_size = calc_min_set_margin_size(dc, default_left_margin, default_right_margin);
2998 GetClientRect(es->hwndSelf, &rc);
2999 if(rc.right - rc.left < min_size) {
3000 default_left_margin = es->left_margin;
3001 default_right_margin = es->right_margin;
3002 }
3003 }
3004 SelectObject(dc, old_font);
3005 ReleaseDC(es->hwndSelf, dc);
3006 }
3007
3008 if (action & EC_LEFTMARGIN) {
3009 es->format_rect.left -= es->left_margin;
3010 if (left != EC_USEFONTINFO)
3011 es->left_margin = left;
3012 else
3013 es->left_margin = default_left_margin;
3014 es->format_rect.left += es->left_margin;
3015 }
3016
3017 if (action & EC_RIGHTMARGIN) {
3018 es->format_rect.right += es->right_margin;
3019 if (right != EC_USEFONTINFO)
3020 es->right_margin = right;
3021 else
3022 es->right_margin = default_right_margin;
3023 es->format_rect.right -= es->right_margin;
3024 }
3025
3026 if (action & (EC_LEFTMARGIN | EC_RIGHTMARGIN)) {
3027 EDIT_AdjustFormatRect(es);
3028 if (repaint) EDIT_UpdateText(es, NULL, TRUE);
3029 }
3030
3031 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3032 }
3033
3034
3035 /*********************************************************************
3036 *
3037 * EM_SETPASSWORDCHAR
3038 *
3039 */
3040 static void EDIT_EM_SetPasswordChar(EDITSTATE *es, WCHAR c)
3041 {
3042 LONG style;
3043
3044 if (es->style & ES_MULTILINE)
3045 return;
3046
3047 if (es->password_char == c)
3048 return;
3049
3050 style = GetWindowLongPtrW( es->hwndSelf, GWL_STYLE );
3051 es->password_char = c;
3052 if (c) {
3053 SetWindowLongPtrW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD );
3054 es->style |= ES_PASSWORD;
3055 } else {
3056 SetWindowLongPtrW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD );
3057 es->style &= ~ES_PASSWORD;
3058 }
3059 EDIT_UpdateText(es, NULL, TRUE);
3060 }
3061
3062
3063 /*********************************************************************
3064 *
3065 * EM_SETTABSTOPS
3066 *
3067 */
3068 static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, const INT *tabs)
3069 {
3070 if (!(es->style & ES_MULTILINE))
3071 return FALSE;
3072 HeapFree(GetProcessHeap(), 0, es->tabs);
3073 es->tabs_count = count;
3074 if (!count)
3075 es->tabs = NULL;
3076 else {
3077 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3078 if (es->tabs == NULL)
3079 {
3080 es->tabs_count = 0;
3081 return FALSE;
3082 }
3083 memcpy(es->tabs, tabs, count * sizeof(INT));
3084 }
3085 return TRUE;
3086 }
3087
3088
3089 #ifndef __REACTOS__
3090 /*********************************************************************
3091 *
3092 * EM_SETTABSTOPS16
3093 *
3094 */
3095 static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, const INT16 *tabs)
3096 {
3097 if (!(es->style & ES_MULTILINE))
3098 return FALSE;
3099 HeapFree(GetProcessHeap(), 0, es->tabs);
3100 es->tabs_count = count;
3101 if (!count)
3102 es->tabs = NULL;
3103 else {
3104 INT i;
3105 es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3106 for (i = 0 ; i < count ; i++)
3107 es->tabs[i] = *tabs++;
3108 }
3109 return TRUE;
3110 }
3111 #endif
3112
3113
3114 /*********************************************************************
3115 *
3116 * EM_SETWORDBREAKPROC
3117 *
3118 */
3119 static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, void *wbp)
3120 {
3121 if (es->word_break_proc == wbp)
3122 return;
3123
3124 es->word_break_proc = wbp;
3125 #ifndef __REACTOS__
3126 es->word_break_proc16 = NULL;
3127 #endif
3128
3129 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3130 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
3131 EDIT_UpdateText(es, NULL, TRUE);
3132 }
3133 }
3134
3135
3136 #ifndef __REACTOS__
3137 /*********************************************************************
3138 *
3139 * EM_SETWORDBREAKPROC16
3140 *
3141 */
3142 static void EDIT_EM_SetWordBreakProc16(EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
3143 {
3144 if (es->word_break_proc16 == wbp)
3145 return;
3146
3147 es->word_break_proc = NULL;
3148 es->word_break_proc16 = wbp;
3149 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3150 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
3151 EDIT_UpdateText(es, NULL, TRUE);
3152 }
3153 }
3154 #endif
3155
3156
3157 /*********************************************************************
3158 *
3159 * EM_UNDO / WM_UNDO
3160 *
3161 */
3162 static BOOL EDIT_EM_Undo(EDITSTATE *es)
3163 {
3164 INT ulength;
3165 LPWSTR utext;
3166
3167 /* As per MSDN spec, for a single-line edit control,
3168 the return value is always TRUE */
3169 if( es->style & ES_READONLY )
3170 return !(es->style & ES_MULTILINE);
3171
3172 ulength = strlenW(es->undo_text);
3173
3174 utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3175 if (utext == NULL)
3176 return FALSE;
3177
3178 strcpyW(utext, es->undo_text);
3179
3180 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3181 es->undo_insert_count, debugstr_w(utext));
3182
3183 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3184 EDIT_EM_EmptyUndoBuffer(es);
3185 EDIT_EM_ReplaceSel(es, TRUE, utext, TRUE, TRUE);
3186 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3187 /* send the notification after the selection start and end are set */
3188 EDIT_NOTIFY_PARENT(es, EN_CHANGE);
3189 EDIT_EM_ScrollCaret(es);
3190 HeapFree(GetProcessHeap(), 0, utext);
3191
3192 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3193 es->undo_insert_count, debugstr_w(es->undo_text));
3194 return TRUE;
3195 }
3196
3197
3198 /* Helper function for WM_CHAR
3199 *
3200 * According to an MSDN blog article titled "Just because you're a control
3201 * doesn't mean that you're necessarily inside a dialog box," multiline edit
3202 * controls without ES_WANTRETURN would attempt to detect whether it is inside
3203 * a dialog box or not.
3204 */
3205 static BOOL EDIT_IsInsideDialog(EDITSTATE *es)
3206 {
3207 return (es->flags & EF_DIALOGMODE);
3208 }
3209
3210
3211 /*********************************************************************
3212 *
3213 * WM_PASTE
3214 *
3215 */
3216 static void EDIT_WM_Paste(EDITSTATE *es)
3217 {
3218 HGLOBAL hsrc;
3219 LPWSTR src;
3220
3221 /* Protect read-only edit control from modification */
3222 if(es->style & ES_READONLY)
3223 return;
3224
3225 OpenClipboard(es->hwndSelf);
3226 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
3227 src = GlobalLock(hsrc);
3228 EDIT_EM_ReplaceSel(es, TRUE, src, TRUE, TRUE);
3229 GlobalUnlock(hsrc);
3230 }
3231 else if (es->style & ES_PASSWORD) {
3232 /* clear selected text in password edit box even with empty clipboard */
3233 const WCHAR empty_strW[] = { 0 };
3234 EDIT_EM_ReplaceSel(es, TRUE, empty_strW, TRUE, TRUE);
3235 }
3236 CloseClipboard();
3237 }
3238
3239
3240 /*********************************************************************
3241 *
3242 * WM_COPY
3243 *
3244 */
3245 static void EDIT_WM_Copy(EDITSTATE *es)
3246 {
3247 INT s = min(es->selection_start, es->selection_end);
3248 INT e = max(es->selection_start, es->selection_end);
3249 HGLOBAL hdst;
3250 LPWSTR dst;
3251 DWORD len;
3252
3253 if (e == s) return;
3254
3255 len = e - s;
3256 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR));
3257 dst = GlobalLock(hdst);
3258 memcpy(dst, es->text + s, len * sizeof(WCHAR));
3259 dst[len] = 0; /* ensure 0 termination */
3260 TRACE("%s\n", debugstr_w(dst));
3261 GlobalUnlock(hdst);
3262 OpenClipboard(es->hwndSelf);
3263 EmptyClipboard();
3264 SetClipboardData(CF_UNICODETEXT, hdst);
3265 CloseClipboard();
3266 }
3267
3268
3269 /*********************************************************************
3270 *
3271 * WM_CLEAR
3272 *
3273 */
3274 static inline void EDIT_WM_Clear(EDITSTATE *es)
3275 {
3276 static const WCHAR empty_stringW[] = {0};
3277
3278 /* Protect read-only edit control from modification */
3279 if(es->style & ES_READONLY)
3280 return;
3281
3282 EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
3283 }
3284
3285
3286 /*********************************************************************
3287 *
3288 * WM_CUT
3289 *
3290 */
3291 static inline void EDIT_WM_Cut(EDITSTATE *es)
3292 {
3293 EDIT_WM_Copy(es);
3294 EDIT_WM_Clear(es);
3295 }
3296
3297
3298 /*********************************************************************
3299 *
3300 * WM_CHAR
3301 *
3302 */
3303 static LRESULT EDIT_WM_Char(EDITSTATE *es, WCHAR c)
3304 {
3305 BOOL control;
3306
3307 control = GetKeyState(VK_CONTROL) & 0x8000;
3308
3309 switch (c) {
3310 case '\r':
3311 /* If it's not a multiline edit box, it would be ignored below.
3312 * For multiline edit without ES_WANTRETURN, we have to make a
3313 * special case.
3314 */
3315 if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3316 if (EDIT_IsInsideDialog(es))
3317 break;
3318 case '\n':
3319 if (es->style & ES_MULTILINE) {
3320 if (es->style & ES_READONLY) {
3321 EDIT_MoveHome(es, FALSE, FALSE);
3322 EDIT_MoveDown_ML(es, FALSE);
3323 } else {
3324 static const WCHAR cr_lfW[] = {'\r','\n',0};
3325 EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, TRUE, TRUE);
3326 }
3327 }
3328 break;
3329 case '\t':
3330 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3331 {
3332 static const WCHAR tabW[] = {'\t',0};
3333 if (EDIT_IsInsideDialog(es))
3334 break;
3335 EDIT_EM_ReplaceSel(es, TRUE, tabW, TRUE, TRUE);
3336 }
3337 break;
3338 case VK_BACK:
3339 if (!(es->style & ES_READONLY) && !control) {
3340 if (es->selection_start != es->selection_end)
3341 EDIT_WM_Clear(es);
3342 else {
3343 /* delete character left of caret */
3344 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3345 EDIT_MoveBackward(es, TRUE);
3346 EDIT_WM_Clear(es);
3347 }
3348 }
3349 break;
3350 case 0x03: /* ^C */
3351 if (!(es->style & ES_PASSWORD))
3352 SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3353 break;
3354 case 0x16: /* ^V */
3355 if (!(es->style & ES_READONLY))
3356 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3357 break;
3358 case 0x18: /* ^X */
3359 if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD)))
3360 SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3361 break;
3362 case 0x1A: /* ^Z */
3363 if (!(es->style & ES_READONLY))
3364 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3365 break;
3366
3367 default:
3368 /*If Edit control style is ES_NUMBER allow users to key in only numeric values*/
3369 if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') )
3370 break;
3371
3372 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3373 WCHAR str[2];
3374 str[0] = c;
3375 str[1] = '\0';
3376 EDIT_EM_ReplaceSel(es, TRUE, str, TRUE, TRUE);
3377 }
3378 break;
3379 }
3380 return 1;
3381 }
3382
3383
3384 /*********************************************************************
3385 *
3386 * WM_CONTEXTMENU
3387 *
3388 * Note: the resource files resource/sysres_??.rc cannot define a
3389 * single popup menu. Hence we use a (dummy) menubar
3390 * containing the single popup menu as its first item.
3391 *
3392 * FIXME: the message identifiers have been chosen arbitrarily,
3393 * hence we use MF_BYPOSITION.
3394 * We might as well use the "real" values (anybody knows ?)
3395 * The menu definition is in resources/sysres_??.rc.
3396 * Once these are OK, we better use MF_BYCOMMAND here
3397 * (as we do in EDIT_WM_Command()).
3398 *
3399 */
3400 static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y)
3401 {
3402 #ifdef __REACTOS__
3403 HMENU menu = LoadMenuA(User32Instance, "EDITMENU");
3404 #else
3405 HMENU menu = LoadMenuA(user32_module, "EDITMENU");
3406 #endif
3407 HMENU popup = GetSubMenu(menu, 0);
3408 UINT start = es->selection_start;
3409 UINT end = es->selection_end;
3410
3411 BOOL selectedItem;
3412 ORDER_UINT(start, end);
3413
3414 /* undo */
3415 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3416 /* cut */
3417 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3418 /* copy */
3419 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3420 /* paste */
3421 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_UNICODETEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3422 /* delete */
3423 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3424 /* select all */
3425 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != get_text_length(es)) ? MF_ENABLED : MF_GRAYED));
3426
3427 if (x == -1 && y == -1) /* passed via VK_APPS press/release */
3428 {
3429 RECT rc;
3430 /* Windows places the menu at the edit's center in this case */
3431 GetClientRect(es->hwndSelf, &rc);
3432 MapWindowPoints(es->hwndSelf, 0, (POINT *)&rc, 2);
3433 x = rc.left + (rc.right - rc.left) / 2;
3434 y = rc.top + (rc.bottom - rc.top) / 2;
3435 }
3436
3437 selectedItem = TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, x, y, 0, es->hwndSelf, NULL);
3438
3439 switch (selectedItem) {
3440 case EM_UNDO:
3441 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3442 break;
3443 case WM_CUT:
3444 SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3445 break;
3446 case WM_COPY:
3447 SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3448 break;
3449 case WM_PASTE:
3450 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3451 break;
3452 case WM_CLEAR:
3453 SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0);
3454 break;
3455 case EM_SETSEL:
3456 EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE);
3457 EDIT_EM_ScrollCaret(es);
3458 break;
3459 default:
3460 ERR("unknown menu item, please report\n");
3461 break;
3462 }
3463
3464 DestroyMenu(menu);
3465 }
3466
3467
3468 /*********************************************************************
3469 *
3470 * WM_GETTEXT
3471 *
3472 */
3473 static INT EDIT_WM_GetText(const EDITSTATE *es, INT count, LPWSTR dst, BOOL unicode)
3474 {
3475 if(!count) return 0;
3476
3477 if(unicode)
3478 {
3479 lstrcpynW(dst, es->text, count);
3480 return strlenW(dst);
3481 }
3482 else
3483 {
3484 LPSTR textA = (LPSTR)dst;
3485 if (!WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL))
3486 textA[count - 1] = 0; /* ensure 0 termination */
3487 return strlen(textA);
3488 }
3489 }
3490
3491 /*********************************************************************
3492 *
3493 * EDIT_CheckCombo
3494 *
3495 */
3496 static BOOL EDIT_CheckCombo(EDITSTATE *es, UINT msg, INT key)
3497 {
3498 HWND hLBox = es->hwndListBox;
3499 HWND hCombo;
3500 BOOL bDropped;
3501 int nEUI;
3502
3503 if (!hLBox)
3504 return FALSE;
3505
3506 hCombo = GetParent(es->hwndSelf);
3507 bDropped = TRUE;
3508 nEUI = 0;
3509
3510 TRACE_(combo)("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key);
3511
3512 if (key == VK_UP || key == VK_DOWN)
3513 {
3514 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
3515 nEUI = 1;
3516
3517 if (msg == WM_KEYDOWN || nEUI)
3518 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3519 }
3520
3521 switch (msg)
3522 {
3523 case WM_KEYDOWN:
3524 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3525 {
3526 /* make sure ComboLBox pops up */
3527 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3528 key = VK_F4;
3529 nEUI = 2;
3530 }
3531
3532 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3533 break;
3534
3535 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3536 if (nEUI)
3537 SendMessageW(hCombo, CB_SHOWDROPDOWN, bDropped ? FALSE : TRUE, 0);
3538 else
3539 SendMessageW(hLBox, WM_KEYDOWN, (WPARAM)VK_F4, 0);
3540 break;
3541 }
3542
3543 if(nEUI == 2)
3544 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3545
3546 return TRUE;
3547 }
3548
3549
3550 /*********************************************************************
3551 *
3552 * WM_KEYDOWN
3553 *
3554 * Handling of special keys that don't produce a WM_CHAR
3555 * (i.e. non-printable keys) & Backspace & Delete
3556 *
3557 */
3558 static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key)
3559 {
3560 BOOL shift;
3561 BOOL control;
3562
3563 if (GetKeyState(VK_MENU) & 0x8000)
3564 return 0;
3565
3566 shift = GetKeyState(VK_SHIFT) & 0x8000;
3567 control = GetKeyState(VK_CONTROL) & 0x8000;
3568
3569 switch (key) {
3570 case VK_F4:
3571 case VK_UP:
3572 if (EDIT_CheckCombo(es, WM_KEYDOWN, key) || key == VK_F4)
3573 break;
3574
3575 /* fall through */
3576 case VK_LEFT:
3577 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3578 EDIT_MoveUp_ML(es, shift);
3579 else
3580 if (control)
3581 EDIT_MoveWordBackward(es, shift);
3582 else
3583 EDIT_MoveBackward(es, shift);
3584 break;
3585 case VK_DOWN:
3586 if (EDIT_CheckCombo(es, WM_KEYDOWN, key))
3587 break;
3588 /* fall through */
3589 case VK_RIGHT:
3590 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3591 EDIT_MoveDown_ML(es, shift);
3592 else if (control)
3593 EDIT_MoveWordForward(es, shift);
3594 else
3595 EDIT_MoveForward(es, shift);
3596 break;
3597 case VK_HOME:
3598 EDIT_MoveHome(es, shift, control);
3599 break;
3600 case VK_END:
3601 EDIT_MoveEnd(es, shift, control);
3602 break;
3603 case VK_PRIOR:
3604 if (es->style & ES_MULTILINE)
3605 EDIT_MovePageUp_ML(es, shift);
3606 else
3607 EDIT_CheckCombo(es, WM_KEYDOWN, key);
3608 break;
3609 case VK_NEXT:
3610 if (es->style & ES_MULTILINE)
3611 EDIT_MovePageDown_ML(es, shift);
3612 else
3613 EDIT_CheckCombo(es, WM_KEYDOWN, key);
3614 break;
3615 case VK_DELETE:
3616 if (!(es->style & ES_READONLY) && !(shift && control)) {
3617 if (es->selection_start != es->selection_end) {
3618 if (shift)
3619 EDIT_WM_Cut(es);
3620 else
3621 EDIT_WM_Clear(es);
3622 } else {
3623 if (shift) {
3624 /* delete character left of caret */
3625 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3626 EDIT_MoveBackward(es, TRUE);
3627 EDIT_WM_Clear(es);
3628 } else if (control) {
3629 /* delete to end of line */
3630 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3631 EDIT_MoveEnd(es, TRUE, FALSE);
3632 EDIT_WM_Clear(es);
3633 } else {
3634 /* delete character right of caret */
3635 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3636 EDIT_MoveForward(es, TRUE);
3637 EDIT_WM_Clear(es);
3638 }
3639 }
3640 }
3641 break;
3642 case VK_INSERT:
3643 if (shift) {
3644 if (!(es->style & ES_READONLY))
3645 EDIT_WM_Paste(es);
3646 } else if (control)
3647 EDIT_WM_Copy(es);
3648 break;
3649 case VK_RETURN:
3650 /* If the edit doesn't want the return send a message to the default object */
3651 if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN))
3652 {
3653 DWORD dw;
3654
3655 if (!EDIT_IsInsideDialog(es)) break;
3656 if (control) break;
3657 dw = SendMessageW( es->hwndParent, DM_GETDEFID, 0, 0 );
3658 if (HIWORD(dw) == DC_HASDEFID)
3659 {
3660 HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw));
3661 if (hwDefCtrl)
3662 {
3663 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, (LPARAM)TRUE);
3664 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
3665 }
3666 }
3667 }
3668 break;
3669 case VK_ESCAPE:
3670 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3671 PostMessageW(es->hwndParent, WM_CLOSE, 0, 0);
3672 break;
3673 case VK_TAB:
3674 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3675 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0);
3676 break;
3677 }
3678 return TRUE;
3679 }
3680
3681
3682 /*********************************************************************
3683 *
3684 * WM_KILLFOCUS
3685 *
3686 */
3687 static LRESULT EDIT_WM_KillFocus(EDITSTATE *es)
3688 {
3689 es->flags &= ~EF_FOCUSED;
3690 DestroyCaret();
3691 if(!(es->style & ES_NOHIDESEL))
3692 EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3693 EDIT_NOTIFY_PARENT(es, EN_KILLFOCUS);
3694 return 0;
3695 }
3696
3697
3698 /*********************************************************************
3699 *
3700 * WM_LBUTTONDBLCLK
3701 *
3702 * The caret position has been set on the WM_LBUTTONDOWN message
3703 *
3704 */
3705 static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es)
3706 {
3707 INT s;
3708 INT e = es->selection_end;
3709 INT l;
3710 INT li;
3711 INT ll;
3712
3713 es->bCaptureState = TRUE;
3714 SetCapture(es->hwndSelf);
3715
3716 l = EDIT_EM_LineFromChar(es, e);
3717 li = EDIT_EM_LineIndex(es, l);
3718 ll = EDIT_EM_LineLength(es, e);
3719 s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
3720 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
3721 EDIT_EM_SetSel(es, s, e, FALSE);
3722 EDIT_EM_ScrollCaret(es);
3723 es->region_posx = es->region_posy = 0;
3724 SetTimer(es->hwndSelf, 0, 100, NULL);
3725 return 0;
3726 }
3727
3728
3729 /*********************************************************************
3730 *
3731 * WM_LBUTTONDOWN
3732 *
3733 */
3734 static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y)
3735 {
3736 INT e;
3737 BOOL after_wrap;
3738
3739 es->bCaptureState = TRUE;
3740 SetCapture(es->hwndSelf);
3741 EDIT_ConfinePoint(es, &x, &y);
3742 e = EDIT_CharFromPos(es, x, y, &after_wrap);
3743 EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3744 EDIT_EM_ScrollCaret(es);
3745 es->region_posx = es->region_posy = 0;
3746 SetTimer(es->hwndSelf, 0, 100, NULL);
3747
3748 if (!(es->flags & EF_FOCUSED))
3749 SetFocus(es->hwndSelf);
3750
3751 return 0;
3752 }
3753
3754
3755 /*********************************************************************
3756 *
3757 * WM_LBUTTONUP
3758 *
3759 */
3760 static LRESULT EDIT_WM_LButtonUp(EDITSTATE *es)
3761 {
3762 if (es->bCaptureState) {
3763 KillTimer(es->hwndSelf, 0);
3764 if (GetCapture() == es->hwndSelf) ReleaseCapture();
3765 }
3766 es->bCaptureState = FALSE;
3767 return 0;
3768 }
3769
3770
3771 /*********************************************************************
3772 *
3773 * WM_MBUTTONDOWN
3774 *
3775 */
3776 static LRESULT EDIT_WM_MButtonDown(EDITSTATE *es)
3777 {
3778 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3779 return 0;
3780 }
3781
3782
3783 /*********************************************************************
3784 *
3785 * WM_MOUSEMOVE
3786 *
3787 */
3788 static LRESULT EDIT_WM_MouseMove(EDITSTATE *es, INT x, INT y)
3789 {
3790 INT e;
3791 BOOL after_wrap;
3792 INT prex, prey;
3793
3794 /* If the mouse has been captured by process other than the edit control itself,
3795 * the windows edit controls will not select the strings with mouse move.
3796 */
3797 if (!es->bCaptureState || GetCapture() != es->hwndSelf)
3798 return 0;
3799
3800 /*
3801 * FIXME: gotta do some scrolling if outside client
3802 * area. Maybe reset the timer ?
3803 */
3804 prex = x; prey = y;
3805 EDIT_ConfinePoint(es, &x, &y);
3806 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3807 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3808 e = EDIT_CharFromPos(es, x, y, &after_wrap);
3809 EDIT_EM_SetSel(es, es->selection_start, e, after_wrap);
3810 EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP);
3811 return 0;
3812 }
3813
3814
3815 /*********************************************************************
3816 *
3817 * WM_PAINT
3818 *
3819 */
3820 static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc)
3821 {
3822 PAINTSTRUCT ps;
3823 INT i;
3824 HDC dc;
3825 HFONT old_font = 0;
3826 RECT rc;
3827 RECT rcClient;
3828 RECT rcLine;
3829 RECT rcRgn;
3830 HBRUSH brush;
3831 HBRUSH old_brush;
3832 INT bw, bh;
3833 BOOL rev = es->bEnableState &&
3834 ((es->flags & EF_FOCUSED) ||
3835 (es->style & ES_NOHIDESEL));
3836 dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps);
3837
3838 GetClientRect(es->hwndSelf, &rcClient);
3839
3840 /* get the background brush */
3841 brush = EDIT_NotifyCtlColor(es, dc);
3842
3843 /* paint the border and the background */
3844 IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3845
3846 if(es->style & WS_BORDER) {
3847 bw = GetSystemMetrics(SM_CXBORDER);
3848 bh = GetSystemMetrics(SM_CYBORDER);
3849 rc = rcClient;
3850 if(es->style & ES_MULTILINE) {
3851 if(es->style & WS_HSCROLL) rc.bottom+=bh;
3852 if(es->style & WS_VSCROLL) rc.right+=bw;
3853 }
3854
3855 /* Draw the frame. Same code as in nonclient.c */
3856 old_brush = SelectObject(dc, GetSysColorBrush(COLOR_WINDOWFRAME));
3857 PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY);
3858 PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY);
3859 PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY);
3860 PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY);
3861 SelectObject(dc, old_brush);
3862
3863 /* Keep the border clean */
3864 IntersectClipRect(dc, rc.left+bw, rc.top+bh,
3865 max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh));
3866 }
3867
3868 GetClipBox(dc, &rc);
3869 FillRect(dc, &rc, brush);
3870
3871 IntersectClipRect(dc, es->format_rect.left,
3872 es->format_rect.top,
3873 es->format_rect.right,
3874 es->format_rect.bottom);
3875 if (es->style & ES_MULTILINE) {
3876 rc = rcClient;
3877 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3878 }
3879 if (es->font)
3880 old_font = SelectObject(dc, es->font);
3881
3882 if (!es->bEnableState)
3883 SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
3884 GetClipBox(dc, &rcRgn);
3885 if (es->style & ES_MULTILINE) {
3886 INT vlc = get_vertical_line_count(es);
3887 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3888 EDIT_GetLineRect(es, i, 0, -1, &rcLine);
3889 if (IntersectRect(&rc, &rcRgn, &rcLine))
3890 EDIT_PaintLine(es, dc, i, rev);
3891 }
3892 } else {
3893 EDIT_GetLineRect(es, 0, 0, -1, &rcLine);
3894 if (IntersectRect(&rc, &rcRgn, &rcLine))
3895 EDIT_PaintLine(es, dc, 0, rev);
3896 }
3897 if (es->font)
3898 SelectObject(dc, old_font);
3899
3900 if (!hdc)
3901 EndPaint(es->hwndSelf, &ps);
3902 }
3903
3904
3905 /*********************************************************************
3906 *
3907 * WM_SETFOCUS
3908 *
3909 */
3910 static void EDIT_WM_SetFocus(EDITSTATE *es)
3911 {
3912 es->flags |= EF_FOCUSED;
3913
3914 if (!(es->style & ES_NOHIDESEL))
3915 EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3916
3917 /* single line edit updates itself */
3918 if (!(es->style & ES_MULTILINE))
3919 {
3920 HDC hdc = GetDC(es->hwndSelf);
3921 EDIT_WM_Paint(es, hdc);
3922 ReleaseDC(es->hwndSelf, hdc);
3923 }
3924
3925 CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3926 EDIT_SetCaretPos(es, es->selection_end,
3927 es->flags & EF_AFTER_WRAP);
3928 ShowCaret(es->hwndSelf);
3929 EDIT_NOTIFY_PARENT(es, EN_SETFOCUS);
3930 }
3931
3932
3933 /*********************************************************************
3934 *
3935 * WM_SETFONT
3936 *
3937 * With Win95 look the margins are set to default font value unless
3938 * the system font (font == 0) is being set, in which case they are left
3939 * unchanged.
3940 *
3941 */
3942 static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw)
3943 {
3944 TEXTMETRICW tm;
3945 HDC dc;
3946 HFONT old_font = 0;
3947 RECT clientRect;
3948
3949 es->font = font;
3950 dc = GetDC(es->hwndSelf);
3951 if (font)
3952 old_font = SelectObject(dc, font);
3953 GetTextMetricsW(dc, &tm);
3954 es->line_height = tm.tmHeight;
3955 es->char_width = tm.tmAveCharWidth;
3956 if (font)
3957 SelectObject(dc, old_font);
3958 ReleaseDC(es->hwndSelf, dc);
3959
3960 /* Reset the format rect and the margins */
3961 GetClientRect(es->hwndSelf, &clientRect);
3962 EDIT_SetRectNP(es, &clientRect);
3963 EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3964 EC_USEFONTINFO, EC_USEFONTINFO, FALSE);
3965
3966 if (es->style & ES_MULTILINE)
3967 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
3968 else
3969 EDIT_CalcLineWidth_SL(es);
3970
3971 if (redraw)
3972 EDIT_UpdateText(es, NULL, TRUE);
3973 if (es->flags & EF_FOCUSED) {
3974 DestroyCaret();
3975 CreateCaret(es->hwndSelf, 0, 2, es->line_height);
3976 EDIT_SetCaretPos(es, es->selection_end,
3977 es->flags & EF_AFTER_WRAP);
3978 ShowCaret(es->hwndSelf);
3979 }
3980 }
3981
3982
3983 /*********************************************************************
3984 *
3985 * WM_SETTEXT
3986 *
3987 * NOTES
3988 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3989 * The modified flag is reset. No notifications are sent.
3990 *
3991 * For single-line controls, reception of WM_SETTEXT triggers:
3992 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3993 *
3994 */
3995 static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text, BOOL unicode)
3996 {
3997 LPWSTR textW = NULL;
3998 if (!unicode && text)
3999 {
4000 LPCSTR textA = (LPCSTR)text;
4001 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
4002 textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR));
4003 if (textW)
4004 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
4005 text = textW;
4006 }
4007
4008 if (es->flags & EF_UPDATE)
4009 /* fixed this bug once; complain if we see it about to happen again. */
4010 ERR("SetSel may generate UPDATE message whose handler may reset "
4011 "selection.\n");
4012
4013 EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE);
4014 if (text)
4015 {
4016 TRACE("%s\n", debugstr_w(text));
4017 EDIT_EM_ReplaceSel(es, FALSE, text, FALSE, FALSE);
4018 if(!unicode)
4019 HeapFree(GetProcessHeap(), 0, textW);
4020 }
4021 else
4022 {
4023 static const WCHAR empty_stringW[] = {0};
4024 TRACE("<NULL>\n");
4025 EDIT_EM_ReplaceSel(es, FALSE, empty_stringW, FALSE, FALSE);
4026 }
4027 es->x_offset = 0;
4028 es->flags &= ~EF_MODIFIED;
4029 EDIT_EM_SetSel(es, 0, 0, FALSE);
4030
4031 /* Send the notification after the selection start and end have been set
4032 * edit control doesn't send notification on WM_SETTEXT
4033 * if it is multiline, or it is part of combobox
4034 */
4035 if( !((es->style & ES_MULTILINE) || es->hwndListBox))
4036 {
4037 EDIT_NOTIFY_PARENT(es, EN_UPDATE);
4038 EDIT_NOTIFY_PARENT(es, EN_CHANGE);
4039 }
4040 EDIT_EM_ScrollCaret(es);
4041 EDIT_UpdateScrollInfo(es);
4042 }
4043
4044
4045 /*********************************************************************
4046 *
4047 * WM_SIZE
4048 *
4049 */
4050 static void EDIT_WM_Size(EDITSTATE *es, UINT action, INT width, INT height)
4051 {
4052 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4053 RECT rc;
4054 TRACE("width = %d, height = %d\n", width, height);
4055 SetRect(&rc, 0, 0, width, height);
4056 EDIT_SetRectNP(es, &rc);
4057 EDIT_UpdateText(es, NULL, TRUE);
4058 }
4059 }
4060
4061
4062 /*********************************************************************
4063 *
4064 * WM_STYLECHANGED
4065 *
4066 * This message is sent by SetWindowLong on having changed either the Style
4067 * or the extended style.
4068 *
4069 * We ensure that the window's version of the styles and the EDITSTATE's agree.
4070 *
4071 * See also EDIT_WM_NCCreate
4072 *
4073 * It appears that the Windows version of the edit control allows the style
4074 * (as retrieved by GetWindowLong) to be any value and maintains an internal
4075 * style variable which will generally be different. In this function we
4076 * update the internal style based on what changed in the externally visible
4077 * style.
4078 *
4079 * Much of this content as based upon the MSDN, especially:
4080 * Platform SDK Documentation -> User Interface Services ->
4081 * Windows User Interface -> Edit Controls -> Edit Control Reference ->
4082 * Edit Control Styles
4083 */
4084 static LRESULT EDIT_WM_StyleChanged ( EDITSTATE *es, WPARAM which, const STYLESTRUCT *style)
4085 {
4086 if (GWL_STYLE == which) {
4087 DWORD style_change_mask;
4088 DWORD new_style;
4089 /* Only a subset of changes can be applied after the control
4090 * has been created.
4091 */
4092 style_change_mask = ES_UPPERCASE | ES_LOWERCASE |
4093 ES_NUMBER;
4094 if (es->style & ES_MULTILINE)
4095 style_change_mask |= ES_WANTRETURN;
4096
4097 new_style = style->styleNew & style_change_mask;
4098
4099 /* Number overrides lowercase overrides uppercase (at least it
4100 * does in Win95). However I'll bet that ES_NUMBER would be
4101 * invalid under Win 3.1.
4102 */
4103 if (new_style & ES_NUMBER) {
4104 ; /* do not override the ES_NUMBER */
4105 } else if (new_style & ES_LOWERCASE) {
4106 new_style &= ~ES_UPPERCASE;
4107 }
4108
4109 es->style = (es->style & ~style_change_mask) | new_style;
4110 } else if (GWL_EXSTYLE == which) {
4111 ; /* FIXME - what is needed here */
4112 } else {
4113 WARN ("Invalid style change %ld\n",which);
4114 }
4115
4116 return 0;
4117 }
4118
4119 /*********************************************************************
4120 *
4121 * WM_SYSKEYDOWN
4122 *
4123 */
4124 static LRESULT EDIT_WM_SysKeyDown(EDITSTATE *es, INT key, DWORD key_data)
4125 {
4126 if ((key == VK_BACK) && (key_data & 0x2000)) {
4127 if (EDIT_EM_CanUndo(es))
4128 EDIT_EM_Undo(es);
4129 return 0;
4130 } else if (key == VK_UP || key == VK_DOWN) {
4131 if (EDIT_CheckCombo(es, WM_SYSKEYDOWN, key))
4132 return 0;
4133 }
4134 return DefWindowProcW(es->hwndSelf, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4135 }
4136
4137
4138 /*********************************************************************
4139 *
4140 * WM_TIMER
4141 *
4142 */
4143 static void EDIT_WM_Timer(EDITSTATE *es)
4144 {
4145 if (es->region_posx < 0) {
4146 EDIT_MoveBackward(es, TRUE);
4147 } else if (es->region_posx > 0) {
4148 EDIT_MoveForward(es, TRUE);
4149 }
4150 /*
4151 * FIXME: gotta do some vertical scrolling here, like
4152 * EDIT_EM_LineScroll(hwnd, 0, 1);
4153 */
4154 }
4155
4156 /*********************************************************************
4157 *
4158 * WM_HSCROLL
4159 *
4160 */
4161 static LRESULT EDIT_WM_HScroll(EDITSTATE *es, INT action, INT pos)
4162 {
4163 INT dx;
4164 INT fw;
4165
4166 if (!(es->style & ES_MULTILINE))
4167 return 0;
4168
4169 if (!(es->style & ES_AUTOHSCROLL))
4170 return 0;
4171
4172 dx = 0;
4173 fw = es->format_rect.right - es->format_rect.left;
4174 switch (action) {
4175 case SB_LINELEFT:
4176 TRACE("SB_LINELEFT\n");
4177 if (es->x_offset)
4178 dx = -es->char_width;
4179 break;
4180 case SB_LINERIGHT:
4181 TRACE("SB_LINERIGHT\n");
4182 if (es->x_offset < es->text_width)
4183 dx = es->char_width;
4184 break;
4185 case SB_PAGELEFT:
4186 TRACE("SB_PAGELEFT\n");
4187 if (es->x_offset)
4188 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4189 break;
4190 case SB_PAGERIGHT:
4191 TRACE("SB_PAGERIGHT\n");
4192 if (es->x_offset < es->text_width)
4193 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4194 break;
4195 case SB_LEFT:
4196 TRACE("SB_LEFT\n");
4197 if (es->x_offset)
4198 dx = -es->x_offset;
4199 break;
4200 case SB_RIGHT:
4201 TRACE("SB_RIGHT\n");
4202 if (es->x_offset < es->text_width)
4203 dx = es->text_width - es->x_offset;
4204 break;
4205 case SB_THUMBTRACK:
4206 TRACE("SB_THUMBTRACK %d\n", pos);
4207 es->flags |= EF_HSCROLL_TRACK;
4208 if(es->style & WS_HSCROLL)
4209 dx = pos - es->x_offset;
4210 else
4211 {
4212 INT fw, new_x;
4213 /* Sanity check */
4214 if(pos < 0 || pos > 100) return 0;
4215 /* Assume default scroll range 0-100 */
4216 fw = es->format_rect.right - es->format_rect.left;
4217 new_x = pos * (es->text_width - fw) / 100;
4218 dx = es->text_width ? (new_x - es->x_offset) : 0;
4219 }
4220 break;
4221 case SB_THUMBPOSITION:
4222 TRACE("SB_THUMBPOSITION %d\n", pos);
4223 es->flags &= ~EF_HSCROLL_TRACK;
4224 if(GetWindowLongPtrW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL)
4225 dx = pos - es->x_offset;
4226 else
4227 {
4228 INT fw, new_x;
4229 /* Sanity check */
4230 if(pos < 0 || pos > 100) return 0;
4231 /* Assume default scroll range 0-100 */
4232 fw = es->format_rect.right - es->format_rect.left;
4233 new_x = pos * (es->text_width - fw) / 100;
4234 dx = es->text_width ? (new_x - es->x_offset) : 0;
4235 }
4236 if (!dx) {
4237 /* force scroll info update */
4238 EDIT_UpdateScrollInfo(es);
4239 EDIT_NOTIFY_PARENT(es, EN_HSCROLL);
4240 }
4241 break;
4242 case SB_ENDSCROLL:
4243 TRACE("SB_ENDSCROLL\n");
4244 break;
4245 /*
4246 * FIXME : the next two are undocumented !
4247 * Are we doing the right thing ?
4248 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4249 * although it's also a regular control message.
4250 */
4251 case EM_GETTHUMB: /* this one is used by NT notepad */
4252 #ifndef __REACTOS__
4253 case EM_GETTHUMB16:
4254 #endif
4255 {
4256 LRESULT ret;
4257 if(GetWindowLongPtrW( es->hwndSelf, GWL_STYLE ) & WS_HSCROLL)
4258 ret = GetScrollPos(es->hwndSelf, SB_HORZ);
4259 else
4260 {
4261 /* Assume default scroll range 0-100 */
4262 INT fw = es->format_rect.right - es->format_rect.left;
4263 ret = es->text_width ? es->x_offset * 100 / (es->text_width - fw) : 0;
4264 }
4265 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4266 return ret;
4267 }
4268 #ifndef __REACTOS__
4269 case EM_LINESCROLL16:
4270 TRACE("EM_LINESCROLL16\n");
4271 dx = pos;
4272 break;
4273 #endif
4274
4275 default:
4276 ERR("undocumented WM_HSCROLL action %d (0x%04x), please report\n",
4277 action, action);
4278 return 0;
4279 }
4280 if (dx)
4281 {
4282 INT fw = es->format_rect.right - es->format_rect.left;
4283 /* check if we are going to move too far */
4284 if(es->x_offset + dx + fw > es->text_width)
4285 dx = es->text_width - fw - es->x_offset;
4286 if(dx)
4287 EDIT_EM_LineScroll_internal(es, dx, 0);
4288 }
4289 return 0;
4290 }
4291
4292
4293 /*********************************************************************
4294 *
4295 * WM_VSCROLL
4296 *
4297 */
4298 static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos)
4299 {
4300 INT dy;
4301
4302 if (!(es->style & ES_MULTILINE))
4303 return 0;
4304
4305 if (!(es->style & ES_AUTOVSCROLL))
4306 return 0;
4307
4308 dy = 0;
4309 switch (action) {
4310 case SB_LINEUP:
4311 case SB_LINEDOWN:
4312 case SB_PAGEUP:
4313 case SB_PAGEDOWN:
4314 TRACE("action %d (%s)\n", action, (action == SB_LINEUP ? "SB_LINEUP" :
4315 (action == SB_LINEDOWN ? "SB_LINEDOWN" :
4316 (action == SB_PAGEUP ? "SB_PAGEUP" :
4317 "SB_PAGEDOWN"))));
4318 EDIT_EM_Scroll(es, action);
4319 return 0;
4320 case SB_TOP:
4321 TRACE("SB_TOP\n");
4322 dy = -es->y_offset;
4323 break;
4324 case SB_BOTTOM:
4325 TRACE("SB_BOTTOM\n");
4326 dy = es->line_count - 1 - es->y_offset;
4327 break;
4328 case SB_THUMBTRACK:
4329 TRACE("SB_THUMBTRACK %d\n", pos);
4330 es->flags |= EF_VSCROLL_TRACK;
4331 if(es->style & WS_VSCROLL)
4332 dy = pos - es->y_offset;
4333 else
4334 {
4335 /* Assume default scroll range 0-100 */
4336 INT vlc, new_y;
4337 /* Sanity check */
4338 if(pos < 0 || pos > 100) return 0;
4339 vlc = get_vertical_line_count(es);
4340 new_y = pos * (es->line_count - vlc) / 100;
4341 dy = es->line_count ? (new_y - es->y_offset) : 0;
4342 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4343 es->line_count, es->y_offset, pos, dy);
4344 }
4345 break;
4346 case SB_THUMBPOSITION:
4347 TRACE("SB_THUMBPOSITION %d\n", pos);
4348 es->flags &= ~EF_VSCROLL_TRACK;
4349 if(es->style & WS_VSCROLL)
4350 dy = pos - es->y_offset;
4351 else
4352 {
4353 /* Assume default scroll range 0-100 */
4354 INT vlc, new_y;
4355 /* Sanity check */
4356 if(pos < 0 || pos > 100) return 0;
4357 vlc = get_vertical_line_count(es);
4358 new_y = pos * (es->line_count - vlc) / 100;
4359 dy = es->line_count ? (new_y - es->y_offset) : 0;
4360 TRACE("line_count=%d, y_offset=%d, pos=%d, dy = %d\n",
4361 es->line_count, es->y_offset, pos, dy);
4362 }
4363 if (!dy)
4364 {
4365 /* force scroll info update */
4366 EDIT_UpdateScrollInfo(es);
4367 EDIT_NOTIFY_PARENT(es, EN_VSCROLL);
4368 }
4369 break;
4370 case SB_ENDSCROLL:
4371 TRACE("SB_ENDSCROLL\n");
4372 break;
4373 /*
4374 * FIXME : the next two are undocumented !
4375 * Are we doing the right thing ?
4376 * At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
4377 * although it's also a regular control message.
4378 */
4379 case EM_GETTHUMB: /* this one is used by NT notepad */
4380 #ifndef __REACTOS__
4381 case EM_GETTHUMB16:
4382 #endif
4383 {
4384 LRESULT ret;
4385 if(GetWindowLongPtrW( es->hwndSelf, GWL_STYLE ) & WS_VSCROLL)
4386 ret = GetScrollPos(es->hwndSelf, SB_VERT);
4387 else
4388 {
4389 /* Assume default scroll range 0-100 */
4390 INT vlc = get_vertical_line_count(es);
4391 ret = es->line_count ? es->y_offset * 100 / (es->line_count - vlc) : 0;
4392 }
4393 TRACE("EM_GETTHUMB: returning %ld\n", ret);
4394 return ret;
4395 }
4396 #ifndef __REACTOS__
4397 case EM_LINESCROLL16:
4398 TRACE("EM_LINESCROLL16 %d\n", pos);
4399 dy = pos;
4400 break;
4401 #endif
4402
4403 default:
4404 ERR("undocumented WM_VSCROLL action %d (0x%04x), please report\n",
4405 action, action);
4406 return 0;
4407 }
4408 if (dy)
4409 EDIT_EM_LineScroll(es, 0, dy);
4410 return 0;
4411 }
4412
4413 /*********************************************************************
4414 *
4415 * EM_GETTHUMB
4416 *
4417 * FIXME: is this right ? (or should it be only VSCROLL)
4418 * (and maybe only for edit controls that really have their
4419 * own scrollbars) (and maybe only for multiline controls ?)
4420 * All in all: very poorly documented
4421 *
4422 */
4423 static LRESULT EDIT_EM_GetThumb(EDITSTATE *es)
4424 {
4425 #ifdef __REACTOS__
4426 return MAKELONG(EDIT_WM_VScroll(es, EM_GETTHUMB, 0),
4427 EDIT_WM_HScroll(es, EM_GETTHUMB, 0));
4428 #else
4429 return MAKELONG(EDIT_WM_VScroll(es, EM_GETTHUMB16, 0),
4430 EDIT_WM_HScroll(es, EM_GETTHUMB16, 0));
4431 #endif
4432 }
4433
4434 /********************************************************************
4435 *
4436 * The Following code is to handle inline editing from IMEs
4437 */
4438
4439 static void EDIT_GetCompositionStr(HIMC hIMC, LPARAM CompFlag, EDITSTATE *es)
4440 {
4441 LONG buflen;
4442 LPWSTR lpCompStr = NULL;
4443 LPSTR lpCompStrAttr = NULL;
4444 DWORD dwBufLenAttr;
4445
4446 buflen = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
4447
4448 if (buflen < 0)
4449 {
4450 return;
4451 }
4452
4453 lpCompStr = HeapAlloc(GetProcessHeap(),0,buflen + sizeof(WCHAR));
4454 if (!lpCompStr)
4455 {
4456 ERR("Unable to allocate IME CompositionString\n");
4457 return;
4458 }
4459
4460 if (buflen)
4461 ImmGetCompositionStringW(hIMC, GCS_COMPSTR, lpCompStr, buflen);
4462 lpCompStr[buflen/sizeof(WCHAR)] = 0;
4463
4464 if (CompFlag & GCS_COMPATTR)
4465 {
4466 /*
4467 * We do not use the attributes yet. it would tell us what characters
4468 * are in transition and which are converted or decided upon
4469 */
4470 dwBufLenAttr = ImmGetCompositionStringW(hIMC, GCS_COMPATTR, NULL, 0);
4471 if (dwBufLenAttr)
4472 {
4473 dwBufLenAttr ++;
4474 lpCompStrAttr = HeapAlloc(GetProcessHeap(),0,dwBufLenAttr+1);
4475 if (!lpCompStrAttr)
4476 {
4477 ERR("Unable to allocate IME Attribute String\n");
4478 HeapFree(GetProcessHeap(),0,lpCompStr);
4479 return;
4480 }
4481 ImmGetCompositionStringW(hIMC,GCS_COMPATTR, lpCompStrAttr,
4482 dwBufLenAttr);
4483 lpCompStrAttr[dwBufLenAttr] = 0;
4484 }
4485 else
4486 lpCompStrAttr = NULL;
4487 }
4488
4489 /* check for change in composition start */
4490 if (es->selection_end < es->composition_start)
4491 es->composition_start = es->selection_end;
4492
4493 /* replace existing selection string */
4494 es->selection_start = es->composition_start;
4495
4496 if (es->composition_len > 0)
4497 es->selection_end = es->composition_start + es->composition_len;
4498 else
4499 es->selection_end = es->selection_start;
4500
4501 EDIT_EM_ReplaceSel(es, FALSE, lpCompStr, TRUE, TRUE);
4502 es->composition_len = abs(es->composition_start - es->selection_end);
4503
4504 es->selection_start = es->composition_start;
4505 es->selection_end = es->selection_start + es->composition_len;
4506
4507 HeapFree(GetProcessHeap(),0,lpCompStrAttr);
4508 HeapFree(GetProcessHeap(),0,lpCompStr);
4509 }
4510
4511 static void EDIT_GetResultStr(HIMC hIMC, EDITSTATE *es)
4512 {
4513 LONG buflen;
4514 LPWSTR lpResultStr;
4515
4516 buflen = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
4517 if (buflen <= 0)
4518 {
4519 return;
4520 }
4521
4522 lpResultStr = HeapAlloc(GetProcessHeap(),0, buflen+sizeof(WCHAR));
4523 if (!lpResultStr)
4524 {
4525 ERR("Unable to alloc buffer for IME string\n");
4526 return;
4527 }
4528
4529 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, lpResultStr, buflen);
4530 lpResultStr[buflen/sizeof(WCHAR)] = 0;
4531
4532 /* check for change in composition start */
4533 if (es->selection_end < es->composition_start)
4534 es->composition_start = es->selection_end;
4535
4536 es->selection_start = es->composition_start;
4537 es->selection_end = es->composition_start + es->composition_len;
4538 EDIT_EM_ReplaceSel(es, TRUE, lpResultStr, TRUE, TRUE);
4539 es->composition_start = es->selection_end;
4540 es->composition_len = 0;
4541
4542 HeapFree(GetProcessHeap(),0,lpResultStr);
4543 }
4544
4545 static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es)
4546 {
4547 HIMC hIMC;
4548 int cursor;
4549
4550 if (es->composition_len == 0 && es->selection_start != es->selection_end)
4551 {
4552 static const WCHAR empty_stringW[] = {0};
4553 EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
4554 es->composition_start = es->selection_end;
4555 }
4556
4557 hIMC = ImmGetContext(hwnd);
4558 if (!hIMC)
4559 return;
4560
4561 if (CompFlag & GCS_RESULTSTR)
4562 EDIT_GetResultStr(hIMC, es);
4563 if (CompFlag & GCS_COMPSTR)
4564 EDIT_GetCompositionStr(hIMC, CompFlag, es);
4565 cursor = ImmGetCompositionStringW(hIMC, GCS_CURSORPOS, 0, 0);
4566 ImmReleaseContext(hwnd, hIMC);
4567 EDIT_SetCaretPos(es, es->selection_start + cursor, es->flags & EF_AFTER_WRAP);
4568 }
4569
4570
4571 /*********************************************************************
4572 *
4573 * WM_NCCREATE
4574 *
4575 * See also EDIT_WM_StyleChanged
4576 */
4577 static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs, BOOL unicode)
4578 {
4579 EDITSTATE *es;
4580 UINT alloc_size;
4581
4582 TRACE("Creating %s edit control, style = %08x\n",
4583 unicode ? "Unicode" : "ANSI", lpcs->style);
4584
4585 if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
4586 return FALSE;
4587 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)es );
4588
4589 /*
4590 * Note: since the EDITSTATE has not been fully initialized yet,
4591 * we can't use any API calls that may send
4592 * WM_XXX messages before WM_NCCREATE is completed.
4593 */
4594
4595 es->is_unicode = unicode;
4596 es->style = lpcs->style;
4597
4598 es->bEnableState = !(es->style & WS_DISABLED);
4599
4600 es->hwndSelf = hwnd;
4601 /* Save parent, which will be notified by EN_* messages */
4602 es->hwndParent = lpcs->hwndParent;
4603
4604 if (es->style & ES_COMBO)
4605 es->hwndListBox = GetDlgItem(es->hwndParent, ID_CB_LISTBOX);
4606
4607 /* FIXME: should we handle changes to WS_EX_RIGHT style after creation? */
4608 if (lpcs->dwExStyle & WS_EX_RIGHT) es->style |= ES_RIGHT;
4609
4610 /* Number overrides lowercase overrides uppercase (at least it
4611 * does in Win95). However I'll bet that ES_NUMBER would be
4612 * invalid under Win 3.1.
4613 */
4614 if (es->style & ES_NUMBER) {
4615 ; /* do not override the ES_NUMBER */
4616 } else if (es->style & ES_LOWERCASE) {
4617 es->style &= ~ES_UPPERCASE;
4618 }
4619 if (es->style & ES_MULTILINE) {
4620 es->buffer_limit = BUFLIMIT_INITIAL;
4621 if (es->style & WS_VSCROLL)
4622 es->style |= ES_AUTOVSCROLL;
4623 if (es->style & WS_HSCROLL)
4624 es->style |= ES_AUTOHSCROLL;
4625 es->style &= ~ES_PASSWORD;
4626 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
4627 /* Confirmed - RIGHT overrides CENTER */
4628 if (es->style & ES_RIGHT)
4629 es->style &= ~ES_CENTER;
4630 es->style &= ~WS_HSCROLL;
4631 es->style &= ~ES_AUTOHSCROLL;
4632 }
4633 } else {
4634 es->buffer_limit = BUFLIMIT_INITIAL;
4635 if ((es->style & ES_RIGHT) && (es->style & ES_CENTER))
4636 es->style &= ~ES_CENTER;
4637 es->style &= ~WS_HSCROLL;
4638 es->style &= ~WS_VSCROLL;
4639 if (es->style & ES_PASSWORD)
4640 es->password_char = '*';
4641 }
4642
4643 alloc_size = ROUND_TO_GROW((es->buffer_size + 1) * sizeof(WCHAR));
4644 if(!(es->hloc32W = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
4645 return FALSE;
4646 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
4647
4648 if (!(es->undo_text = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (es->buffer_size + 1) * sizeof(WCHAR))))
4649 return FALSE;
4650 es->undo_buffer_size = es->buffer_size;
4651
4652 if (es->style & ES_MULTILINE)
4653 if (!(es->first_line_def = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
4654 return FALSE;
4655 es->line_count = 1;
4656
4657 /*
4658 * In Win95 look and feel, the WS_BORDER style is replaced by the
4659 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
4660 * control a nonclient area so we don't need to draw the border.
4661 * If WS_BORDER without WS_EX_CLIENTEDGE is specified we shouldn't have
4662 * a nonclient area and we should handle painting the border ourselves.
4663 *
4664 * When making modifications please ensure that the code still works
4665 * for edit controls created directly with style 0x50800000, exStyle 0
4666 * (which should have a single pixel border)
4667 */
4668 if (lpcs->dwExStyle & WS_EX_CLIENTEDGE)
4669 es->style &= ~WS_BORDER;
4670 else if (es->style & WS_BORDER)
4671 SetWindowLongPtrW(hwnd, GWL_STYLE, es->style & ~WS_BORDER);
4672
4673 return TRUE;
4674 }
4675
4676
4677 /*********************************************************************
4678 *
4679 * WM_CREATE
4680 *
4681 */
4682 static LRESULT EDIT_WM_Create(EDITSTATE *es, LPCWSTR name)
4683 {
4684 RECT clientRect;
4685
4686 TRACE("%s\n", debugstr_w(name));
4687 /*
4688 * To initialize some final structure members, we call some helper
4689 * functions. However, since the EDITSTATE is not consistent (i.e.
4690 * not fully initialized), we should be very careful which
4691 * functions can be called, and in what order.
4692 */
4693 EDIT_WM_SetFont(es, 0, FALSE);
4694 EDIT_EM_EmptyUndoBuffer(es);
4695
4696 /* We need to calculate the format rect
4697 (applications may send EM_SETMARGINS before the control gets visible) */
4698 GetClientRect(es->hwndSelf, &clientRect);
4699 EDIT_SetRectNP(es, &clientRect);
4700
4701 if (name && *name) {
4702 EDIT_EM_ReplaceSel(es, FALSE, name, FALSE, FALSE);
4703 /* if we insert text to the editline, the text scrolls out
4704 * of the window, as the caret is placed after the insert
4705 * pos normally; thus we reset es->selection... to 0 and
4706 * update caret
4707 */
4708 es->selection_start = es->selection_end = 0;
4709 /* Adobe Photoshop does NOT like this. and MSDN says that EN_CHANGE
4710 * Messages are only to be sent when the USER does something to
4711 * change the contents. So I am removing this EN_CHANGE
4712 *
4713 * EDIT_NOTIFY_PARENT(es, EN_CHANGE);
4714 */
4715 EDIT_EM_ScrollCaret(es);
4716 }
4717 /* force scroll info update */
4718 EDIT_UpdateScrollInfo(es);
4719 /* The rule seems to return 1 here for success */
4720 /* Power Builder masked edit controls will crash */
4721 /* if not. */
4722 /* FIXME: is that in all cases so ? */
4723 return 1;
4724 }
4725
4726
4727 /*********************************************************************
4728 *
4729 * WM_DESTROY
4730 *
4731 */
4732 static LRESULT EDIT_WM_Destroy(EDITSTATE *es)
4733 {
4734 LINEDEF *pc, *pp;
4735
4736 if (es->hloc32W) {
4737 LocalFree(es->hloc32W);
4738 }
4739 if (es->hloc32A) {
4740 LocalFree(es->hloc32A);
4741 }
4742 #ifndef __REACTOS__
4743 if (es->hloc16) {
4744 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
4745 HANDLE16 oldDS = stack16->ds;
4746
4747 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
4748 while (LocalUnlock16(es->hloc16)) ;
4749 LocalFree16(es->hloc16);
4750 stack16->ds = oldDS;
4751 }
4752 #endif
4753
4754 pc = es->first_line_def;
4755 while (pc)
4756 {
4757 pp = pc->next;
4758 HeapFree(GetProcessHeap(), 0, pc);
4759 pc = pp;
4760 }
4761
4762 SetWindowLongPtrW( es->hwndSelf, 0, 0 );
4763 HeapFree(GetProcessHeap(), 0, es);
4764
4765 return 0;
4766 }
4767
4768
4769 static inline LRESULT DefWindowProcT(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode)
4770 {
4771 if(unicode)
4772 return DefWindowProcW(hwnd, msg, wParam, lParam);
4773 else
4774 return DefWindowProcA(hwnd, msg, wParam, lParam);
4775 }
4776
4777 /*********************************************************************
4778 *
4779 * EditWndProc_common
4780 *
4781 * The messages are in the order of the actual integer values
4782 * (which can be found in include/windows.h)
4783 * Wherever possible the 16 bit versions are converted to
4784 * the 32 bit ones, so that we can 'fall through' to the
4785 * helper functions. These are mostly 32 bit (with a few
4786 * exceptions, clearly indicated by a '16' extension to their
4787 * names).
4788 *
4789 */
4790 LRESULT WINAPI EditWndProc_common( HWND hwnd, UINT msg,
4791 WPARAM wParam, LPARAM lParam, BOOL unicode )
4792 {
4793 EDITSTATE *es = (EDITSTATE *)GetWindowLongPtrW( hwnd, 0 );
4794 LRESULT result = 0;
4795
4796 TRACE("hwnd=%p msg=%x (%s) wparam=%lx lparam=%lx\n", hwnd, msg, SPY_GetMsgName(msg, hwnd), wParam, lParam);
4797
4798 if (!es && msg != WM_NCCREATE)
4799 return DefWindowProcT(hwnd, msg, wParam, lParam, unicode);
4800
4801 if (es && (msg != WM_DESTROY)) EDIT_LockBuffer(es);
4802
4803 switch (msg) {
4804 #ifndef __REACTOS__
4805 case EM_GETSEL16:
4806 wParam = 0;
4807 lParam = 0;
4808 /* fall through */
4809 #endif
4810 case EM_GETSEL:
4811 result = EDIT_EM_GetSel(es, (PUINT)wParam, (PUINT)lParam);
4812 break;
4813
4814 #ifndef __REACTOS__
4815 case EM_SETSEL16:
4816 if ((short)LOWORD(lParam) == -1)
4817 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
4818 else
4819 EDIT_EM_SetSel(es, LOWORD(lParam), HIWORD(lParam), FALSE);
4820 if (!wParam)
4821 EDIT_EM_ScrollCaret(es);
4822 result = 1;
4823 break;
4824 #endif
4825 case EM_SETSEL:
4826 EDIT_EM_SetSel(es, wParam, lParam, FALSE);
4827 EDIT_EM_ScrollCaret(es);
4828 result = 1;
4829 break;
4830
4831 #ifndef __REACTOS__
4832 case EM_GETRECT16:
4833 if (lParam)
4834 {
4835 RECT16 *r16 = MapSL(lParam);
4836 r16->left = es->format_rect.left;
4837 r16->top = es->format_rect.top;
4838 r16->right = es->format_rect.right;
4839 r16->bottom = es->format_rect.bottom;
4840 }
4841 break;
4842 #endif
4843 case EM_GETRECT:
4844 if (lParam)
4845 CopyRect((LPRECT)lParam, &es->format_rect);
4846 break;
4847
4848 #ifndef __REACTOS__
4849 case EM_SETRECT16:
4850 if ((es->style & ES_MULTILINE) && lParam) {
4851 RECT rc;
4852 RECT16 *r16 = MapSL(lParam);
4853 rc.left = r16->left;
4854 rc.top = r16->top;
4855 rc.right = r16->right;
4856 rc.bottom = r16->bottom;
4857 EDIT_SetRectNP(es, &rc);
4858 EDIT_UpdateText(es, NULL, TRUE);
4859 }
4860 break;
4861 #endif
4862 case EM_SETRECT:
4863 if ((es->style & ES_MULTILINE) && lParam) {
4864 EDIT_SetRectNP(es, (LPRECT)lParam);
4865 EDIT_UpdateText(es, NULL, TRUE);
4866 }
4867 break;
4868
4869 #ifndef __REACTOS__
4870 case EM_SETRECTNP16:
4871 if ((es->style & ES_MULTILINE) && lParam) {
4872 RECT rc;
4873 RECT16 *r16 = MapSL(lParam);
4874 rc.left = r16->left;
4875 rc.top = r16->top;
4876 rc.right = r16->right;
4877 rc.bottom = r16->bottom;
4878 EDIT_SetRectNP(es, &rc);
4879 }
4880 break;
4881 #endif
4882 case EM_SETRECTNP:
4883 if ((es->style & ES_MULTILINE) && lParam)
4884 EDIT_SetRectNP(es, (LPRECT)lParam);
4885 break;
4886
4887 #ifndef __REACTOS__
4888 case EM_SCROLL16:
4889 #endif
4890 case EM_SCROLL:
4891 result = EDIT_EM_Scroll(es, (INT)wParam);
4892 break;
4893
4894 #ifndef __REACTOS__
4895 case EM_LINESCROLL16:
4896 wParam = (WPARAM)(INT)(SHORT)HIWORD(lParam);
4897 lParam = (LPARAM)(INT)(SHORT)LOWORD(lParam);
4898 /* fall through */
4899 #endif
4900 case EM_LINESCROLL:
4901 result = (LRESULT)EDIT_EM_LineScroll(es, (INT)wParam, (INT)lParam);
4902 break;
4903
4904 #ifndef __REACTOS__
4905 case EM_SCROLLCARET16:
4906 #endif
4907 case EM_SCROLLCARET:
4908 EDIT_EM_ScrollCaret(es);
4909 result = 1;
4910 break;
4911
4912 #ifndef __REACTOS__
4913 case EM_GETMODIFY16:
4914 #endif
4915 case EM_GETMODIFY:
4916 result = ((es->flags & EF_MODIFIED) != 0);
4917 break;
4918
4919 #ifndef __REACTOS__
4920 case EM_SETMODIFY16:
4921 #endif
4922 case EM_SETMODIFY:
4923 if (wParam)
4924 es->flags |= EF_MODIFIED;
4925 else
4926 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
4927 break;
4928
4929 #ifndef __REACTOS__
4930 case EM_GETLINECOUNT16:
4931 #endif
4932 case EM_GETLINECOUNT:
4933 result = (es->style & ES_MULTILINE) ? es->line_count : 1;
4934 break;
4935
4936 #ifndef __REACTOS__
4937 case EM_LINEINDEX16:
4938 if ((INT16)wParam == -1)
4939 wParam = (WPARAM)-1;
4940 /* fall through */
4941 #endif
4942 case EM_LINEINDEX:
4943 result = (LRESULT)EDIT_EM_LineIndex(es, (INT)wParam);
4944 break;
4945
4946 #ifndef __REACTOS__
4947 case EM_SETHANDLE16:
4948 EDIT_EM_SetHandle16(es, (HLOCAL16)wParam);
4949 break;
4950 #endif
4951 case EM_SETHANDLE:
4952 EDIT_EM_SetHandle(es, (HLOCAL)wParam);
4953 break;
4954
4955 #ifndef __REACTOS__
4956 case EM_GETHANDLE16:
4957 result = (LRESULT)EDIT_EM_GetHandle16(es);
4958 break;
4959 #endif
4960 case EM_GETHANDLE:
4961 result = (LRESULT)EDIT_EM_GetHandle(es);
4962 break;
4963
4964 #ifndef __REACTOS__
4965 case EM_GETTHUMB16:
4966 #endif
4967 case EM_GETTHUMB:
4968 result = EDIT_EM_GetThumb(es);
4969 break;
4970
4971 /* these messages missing from specs */
4972 case WM_USER+15:
4973 case 0x00bf:
4974 case WM_USER+16:
4975 case 0x00c0:
4976 case WM_USER+19:
4977 case 0x00c3:
4978 case WM_USER+26:
4979 case 0x00ca:
4980 FIXME("undocumented message 0x%x, please report\n", msg);
4981 result = DefWindowProcW(hwnd, msg, wParam, lParam);
4982 break;
4983
4984 #ifndef __REACTOS__
4985 case EM_LINELENGTH16:
4986 #endif
4987 case EM_LINELENGTH:
4988 result = (LRESULT)EDIT_EM_LineLength(es, (INT)wParam);
4989 break;
4990
4991 #ifndef __REACTOS__
4992 case EM_REPLACESEL16:
4993 lParam = (LPARAM)MapSL(lParam);
4994 unicode = FALSE; /* 16-bit message is always ascii */
4995 /* fall through */
4996 #endif
4997 case EM_REPLACESEL:
4998 {
4999 LPWSTR textW;
5000
5001 if(unicode)
5002 textW = (LPWSTR)lParam;
5003 else
5004 {
5005 LPSTR textA = (LPSTR)lParam;
5006 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
5007 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5008 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
5009 }
5010
5011 EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, TRUE, TRUE);
5012 result = 1;
5013
5014 if(!unicode)
5015 HeapFree(GetProcessHeap(), 0, textW);
5016 break;
5017 }
5018
5019 #ifndef __REACTOS__
5020 case EM_GETLINE16:
5021 lParam = (LPARAM)MapSL(lParam);
5022 unicode = FALSE; /* 16-bit message is always ascii */
5023 /* fall through */
5024 #endif
5025 case EM_GETLINE:
5026 result = (LRESULT)EDIT_EM_GetLine(es, (INT)wParam, (LPWSTR)lParam, unicode);
5027 break;
5028
5029 #ifndef __REACTOS__
5030 case EM_LIMITTEXT16:
5031 #endif
5032 case EM_SETLIMITTEXT:
5033 EDIT_EM_SetLimitText(es, wParam);
5034 break;
5035
5036 #ifndef __REACTOS__
5037 case EM_CANUNDO16:
5038 #endif
5039 case EM_CANUNDO:
5040 result = (LRESULT)EDIT_EM_CanUndo(es);
5041 break;
5042
5043 #ifndef __REACTOS__
5044 case EM_UNDO16:
5045 #endif
5046 case EM_UNDO:
5047 case WM_UNDO:
5048 result = (LRESULT)EDIT_EM_Undo(es);
5049 break;
5050
5051 #ifndef __REACTOS__
5052 case EM_FMTLINES16:
5053 #endif
5054 case EM_FMTLINES:
5055 result = (LRESULT)EDIT_EM_FmtLines(es, (BOOL)wParam);
5056 break;
5057
5058 #ifndef __REACTOS__
5059 case EM_LINEFROMCHAR16:
5060 #endif
5061 case EM_LINEFROMCHAR:
5062 result = (LRESULT)EDIT_EM_LineFromChar(es, (INT)wParam);
5063 break;
5064
5065 #ifndef __REACTOS__
5066 case EM_SETTABSTOPS16:
5067 result = (LRESULT)EDIT_EM_SetTabStops16(es, (INT)wParam, MapSL(lParam));
5068 break;
5069 #endif
5070 case EM_SETTABSTOPS:
5071 result = (LRESULT)EDIT_EM_SetTabStops(es, (INT)wParam, (LPINT)lParam);
5072 break;
5073
5074 #ifndef __REACTOS__
5075 case EM_SETPASSWORDCHAR16:
5076 unicode = FALSE; /* 16-bit message is always ascii */
5077 /* fall through */
5078 #endif
5079 case EM_SETPASSWORDCHAR:
5080 {
5081 WCHAR charW = 0;
5082
5083 if(unicode)
5084 charW = (WCHAR)wParam;
5085 else
5086 {
5087 CHAR charA = wParam;
5088 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
5089 }
5090
5091 EDIT_EM_SetPasswordChar(es, charW);
5092 break;
5093 }
5094
5095 #ifndef __REACTOS__
5096 case EM_EMPTYUNDOBUFFER16:
5097 #endif
5098 case EM_EMPTYUNDOBUFFER:
5099 EDIT_EM_EmptyUndoBuffer(es);
5100 break;
5101
5102 #ifndef __REACTOS__
5103 case EM_GETFIRSTVISIBLELINE16:
5104 result = es->y_offset;
5105 break;
5106 #endif
5107 case EM_GETFIRSTVISIBLELINE:
5108 result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
5109 break;
5110
5111 #ifndef __REACTOS__
5112 case EM_SETREADONLY16:
5113 #endif
5114 case EM_SETREADONLY:
5115 {
5116 DWORD old_style = es->style;
5117
5118 if (wParam) {
5119 SetWindowLongPtrW( hwnd, GWL_STYLE,
5120 GetWindowLongPtrW( hwnd, GWL_STYLE ) | ES_READONLY );
5121 es->style |= ES_READONLY;
5122 } else {
5123 SetWindowLongPtrW( hwnd, GWL_STYLE,
5124 GetWindowLongPtrW( hwnd, GWL_STYLE ) & ~ES_READONLY );
5125 es->style &= ~ES_READONLY;
5126 }
5127
5128 if (old_style ^ es->style)
5129 InvalidateRect(es->hwndSelf, NULL, TRUE);
5130
5131 result = 1;
5132 break;
5133 }
5134
5135 #ifndef __REACTOS__
5136 case EM_SETWORDBREAKPROC16:
5137 EDIT_EM_SetWordBreakProc16(es, (EDITWORDBREAKPROC16)lParam);
5138 break;
5139 #endif
5140 case EM_SETWORDBREAKPROC:
5141 EDIT_EM_SetWordBreakProc(es, (void *)lParam);
5142 break;
5143
5144 #ifndef __REACTOS__
5145 case EM_GETWORDBREAKPROC16:
5146 result = (LRESULT)es->word_break_proc16;
5147 break;
5148 #endif
5149 case EM_GETWORDBREAKPROC:
5150 result = (LRESULT)es->word_break_proc;
5151 break;
5152
5153 #ifndef __REACTOS__
5154 case EM_GETPASSWORDCHAR16:
5155 unicode = FALSE; /* 16-bit message is always ascii */
5156 /* fall through */
5157 #endif
5158 case EM_GETPASSWORDCHAR:
5159 {
5160 if(unicode)
5161 result = es->password_char;
5162 else
5163 {
5164 WCHAR charW = es->password_char;
5165 CHAR charA = 0;
5166 WideCharToMultiByte(CP_ACP, 0, &charW, 1, &charA, 1, NULL, NULL);
5167 result = charA;
5168 }
5169 break;
5170 }
5171
5172 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
5173
5174 case EM_SETMARGINS:
5175 EDIT_EM_SetMargins(es, (INT)wParam, LOWORD(lParam), HIWORD(lParam), TRUE);
5176 break;
5177
5178 case EM_GETMARGINS:
5179 result = MAKELONG(es->left_margin, es->right_margin);
5180 break;
5181
5182 case EM_GETLIMITTEXT:
5183 result = es->buffer_limit;
5184 break;
5185
5186 case EM_POSFROMCHAR:
5187 if ((INT)wParam >= get_text_length(es)) result = -1;
5188 else result = EDIT_EM_PosFromChar(es, (INT)wParam, FALSE);
5189 break;
5190
5191 case EM_CHARFROMPOS:
5192 result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
5193 break;
5194
5195 /* End of the EM_ messages which were in numerical order; what order
5196 * are these in? vaguely alphabetical?
5197 */
5198
5199 case WM_NCCREATE:
5200 result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTW)lParam, unicode);
5201 break;
5202
5203 case WM_DESTROY:
5204 result = EDIT_WM_Destroy(es);
5205 es = NULL;
5206 break;
5207
5208 case WM_GETDLGCODE:
5209 result = DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
5210
5211 if (es->style & ES_MULTILINE)
5212 result |= DLGC_WANTALLKEYS;
5213
5214 if (lParam)
5215 {
5216 es->flags|=EF_DIALOGMODE;
5217
5218 if (((LPMSG)lParam)->message == WM_KEYDOWN)
5219 {
5220 int vk = (int)((LPMSG)lParam)->wParam;
5221
5222 if (es->hwndListBox)
5223 {
5224 if (vk == VK_RETURN || vk == VK_ESCAPE)
5225 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
5226 result |= DLGC_WANTMESSAGE;
5227 }
5228 }
5229 }
5230 break;
5231
5232 case WM_IME_CHAR:
5233 if (!unicode)
5234 {
5235 WCHAR charW;
5236 CHAR strng[2];
5237
5238 strng[0] = wParam >> 8;
5239 strng[1] = wParam & 0xff;
5240 if (strng[0]) MultiByteToWideChar(CP_ACP, 0, strng, 2, &charW, 1);
5241 else MultiByteToWideChar(CP_ACP, 0, &strng[1], 1, &charW, 1);
5242 result = EDIT_WM_Char(es, charW);
5243 break;
5244 }
5245 /* fall through */
5246 case WM_CHAR:
5247 {
5248 WCHAR charW;
5249
5250 if(unicode)
5251 charW = wParam;
5252 else
5253 {
5254 CHAR charA = wParam;
5255 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
5256 }
5257
5258 if (es->hwndListBox)
5259 {
5260 if (charW == VK_RETURN || charW == VK_ESCAPE)
5261 {
5262 if (SendMessageW(GetParent(hwnd), CB_GETDROPPEDSTATE, 0, 0))
5263 SendMessageW(GetParent(hwnd), WM_KEYDOWN, charW, 0);
5264 break;
5265 }
5266 }
5267 result = EDIT_WM_Char(es, charW);
5268 break;
5269 }
5270
5271 case WM_UNICHAR:
5272 if (unicode)
5273 {
5274 if (wParam == UNICODE_NOCHAR) return TRUE;
5275 if (wParam <= 0x000fffff)
5276 {
5277 if(wParam > 0xffff) /* convert to surrogates */
5278 {
5279 wParam -= 0x10000;
5280 EDIT_WM_Char(es, (wParam >> 10) + 0xd800);
5281 EDIT_WM_Char(es, (wParam & 0x03ff) + 0xdc00);
5282 }
5283 else EDIT_WM_Char(es, wParam);
5284 }
5285 return 0;
5286 }
5287 break;
5288
5289 case WM_CLEAR:
5290 EDIT_WM_Clear(es);
5291 break;
5292
5293 case WM_CONTEXTMENU:
5294 EDIT_WM_ContextMenu(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
5295 break;
5296
5297 case WM_COPY:
5298 EDIT_WM_Copy(es);
5299 break;
5300
5301 case WM_CREATE:
5302 if(unicode)
5303 result = EDIT_WM_Create(es, ((LPCREATESTRUCTW)lParam)->lpszName);
5304 else
5305 {
5306 LPCSTR nameA = ((LPCREATESTRUCTA)lParam)->lpszName;
5307 LPWSTR nameW = NULL;
5308 if(nameA)
5309 {
5310 INT countW = MultiByteToWideChar(CP_ACP, 0, nameA, -1, NULL, 0);
5311 if((nameW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
5312 MultiByteToWideChar(CP_ACP, 0, nameA, -1, nameW, countW);
5313 }
5314 result = EDIT_WM_Create(es, nameW);
5315 HeapFree(GetProcessHeap(), 0, nameW);
5316 }
5317 break;
5318
5319 case WM_CUT:
5320 EDIT_WM_Cut(es);
5321 break;
5322
5323 case WM_ENABLE:
5324 es->bEnableState = (BOOL) wParam;
5325 EDIT_UpdateText(es, NULL, TRUE);
5326 break;
5327
5328 case WM_ERASEBKGND:
5329 /* we do the proper erase in EDIT_WM_Paint */
5330 result = 1;
5331 break;
5332
5333 case WM_GETFONT:
5334 result = (LRESULT)es->font;
5335 break;
5336
5337 case WM_GETTEXT:
5338 result = (LRESULT)EDIT_WM_GetText(es, (INT)wParam, (LPWSTR)lParam, unicode);
5339 break;
5340
5341 case WM_GETTEXTLENGTH:
5342 if (unicode) result = get_text_length(es);
5343 else result = WideCharToMultiByte( CP_ACP, 0, es->text, get_text_length(es),
5344 NULL, 0, NULL, NULL );
5345 break;
5346
5347 case WM_HSCROLL:
5348 result = EDIT_WM_HScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
5349 break;
5350
5351 case WM_KEYDOWN:
5352 result = EDIT_WM_KeyDown(es, (INT)wParam);
5353 break;
5354
5355 case WM_KILLFOCUS:
5356 result = EDIT_WM_KillFocus(es);
5357 break;
5358
5359 case WM_LBUTTONDBLCLK:
5360 result = EDIT_WM_LButtonDblClk(es);
5361 break;
5362
5363 case WM_LBUTTONDOWN:
5364 result = EDIT_WM_LButtonDown(es, wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
5365 break;
5366
5367 case WM_LBUTTONUP:
5368 result = EDIT_WM_LButtonUp(es);
5369 break;
5370
5371 case WM_MBUTTONDOWN:
5372 result = EDIT_WM_MButtonDown(es);
5373 break;
5374
5375 case WM_MOUSEMOVE:
5376 result = EDIT_WM_MouseMove(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
5377 break;
5378
5379 case WM_PRINTCLIENT:
5380 case WM_PAINT:
5381 EDIT_WM_Paint(es, (HDC)wParam);
5382 break;
5383
5384 case WM_PASTE:
5385 EDIT_WM_Paste(es);
5386 break;
5387
5388 case WM_SETFOCUS:
5389 EDIT_WM_SetFocus(es);
5390 break;
5391
5392 case WM_SETFONT:
5393 EDIT_WM_SetFont(es, (HFONT)wParam, LOWORD(lParam) != 0);
5394 break;
5395
5396 case WM_SETREDRAW:
5397 /* FIXME: actually set an internal flag and behave accordingly */
5398 break;
5399
5400 case WM_SETTEXT:
5401 EDIT_WM_SetText(es, (LPCWSTR)lParam, unicode);
5402 result = TRUE;
5403 break;
5404
5405 case WM_SIZE:
5406 EDIT_WM_Size(es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
5407 break;
5408
5409 case WM_STYLECHANGED:
5410 result = EDIT_WM_StyleChanged(es, wParam, (const STYLESTRUCT *)lParam);
5411 break;
5412
5413 case WM_STYLECHANGING:
5414 result = 0; /* See EDIT_WM_StyleChanged */
5415 break;
5416
5417 case WM_SYSKEYDOWN:
5418 result = EDIT_WM_SysKeyDown(es, (INT)wParam, (DWORD)lParam);
5419 break;
5420
5421 case WM_TIMER:
5422 EDIT_WM_Timer(es);
5423 break;
5424
5425 case WM_VSCROLL:
5426 result = EDIT_WM_VScroll(es, LOWORD(wParam), (short)HIWORD(wParam));
5427 break;
5428
5429 case WM_MOUSEWHEEL:
5430 {
5431 int gcWheelDelta = 0;
5432 UINT pulScrollLines = 3;
5433 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
5434
5435 if (wParam & (MK_SHIFT | MK_CONTROL)) {
5436 result = DefWindowProcW(hwnd, msg, wParam, lParam);
5437 break;
5438 }
5439 gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
5440 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
5441 {
5442 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
5443 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
5444 result = EDIT_EM_LineScroll(es, 0, cLineScroll);
5445 }
5446 }
5447 break;
5448
5449
5450 /* IME messages to make the edit control IME aware */
5451 case WM_IME_SETCONTEXT:
5452 break;
5453
5454 case WM_IME_STARTCOMPOSITION:
5455 es->composition_start = es->selection_end;
5456 es->composition_len = 0;
5457 break;
5458
5459 case WM_IME_COMPOSITION:
5460 EDIT_ImeComposition(hwnd, lParam, es);
5461 break;
5462
5463 case WM_IME_ENDCOMPOSITION:
5464 if (es->composition_len > 0)
5465 {
5466 static const WCHAR empty_stringW[] = {0};
5467 EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
5468 es->selection_end = es->selection_start;
5469 es->composition_len= 0;
5470 }
5471 break;
5472
5473 case WM_IME_COMPOSITIONFULL:
5474 break;
5475
5476 case WM_IME_SELECT:
5477 break;
5478
5479 case WM_IME_CONTROL:
5480 break;
5481
5482 default:
5483 result = DefWindowProcT(hwnd, msg, wParam, lParam, unicode);
5484 break;
5485 }
5486
5487 if (IsWindow(hwnd) && es) EDIT_UnlockBuffer(es, FALSE);
5488
5489 TRACE("hwnd=%p msg=%x (%s) -- 0x%08lx\n", hwnd, msg, SPY_GetMsgName(msg, hwnd), result);
5490
5491 return result;
5492 }
5493
5494 /*********************************************************************
5495 *
5496 * EditWndProc (USER32.@)
5497 */
5498 LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
5499 {
5500 return EditWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
5501 }
5502
5503 /*********************************************************************
5504 *
5505 * EditWndProcW (USER32.@)
5506 */
5507 LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
5508 {
5509 return EditWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
5510 }
5511
5512 /*********************************************************************
5513 * edit class descriptor
5514 */
5515 static const WCHAR editW[] = {'E','d','i','t',0};
5516 const struct builtin_class_descr EDIT_builtin_class =
5517 {
5518 editW, /* name */
5519 CS_DBLCLKS | CS_PARENTDC, /* style */
5520 EditWndProcA, /* procA */
5521 EditWndProcW, /* procW */
5522 #ifdef _WIN64
5523 sizeof(EDITSTATE *), /* extra */
5524 #else
5525 sizeof(EDITSTATE *) + sizeof(WORD), /* extra */
5526 #endif
5527 IDC_IBEAM, /* cursor */
5528 0 /* brush */
5529 };