ReactOS 0.4.16-dev-88-ga65b6ae
edit.c
Go to the documentation of this file.
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 * Copyright Frank Richter, 2005
8 *
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 *
24 * TODO:
25 * - EDITBALLOONTIP structure
26 * - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
27 * - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
28 * - EM_GETIMESTATUS, EM_SETIMESTATUS
29 * - EN_ALIGN_LTR_EC
30 * - EN_ALIGN_RTL_EC
31 * - ES_OEMCONVERT
32 *
33 */
34
35#include <stdarg.h>
36#include <string.h>
37#include <stdlib.h>
38
39#include "windef.h"
40#include "winbase.h"
41#include "wingdi.h"
42#include "winuser.h"
43#include "imm.h"
44#ifdef __REACTOS__
45 #include <immdev.h>
46 #include <imm32_undoc.h>
47#endif
48#include "usp10.h"
49#include "commctrl.h"
50#include "uxtheme.h"
51#include "vsstyle.h"
52#include "wine/debug.h"
53#include "wine/heap.h"
54
56
57#define BUFLIMIT_INITIAL 30000 /* initial buffer size */
58#define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
59#define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
60#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
61
62/*
63 * extra flags for EDITSTATE.flags field
64 */
65#define EF_MODIFIED 0x0001 /* text has been modified */
66#define EF_FOCUSED 0x0002 /* we have input focus */
67#define EF_UPDATE 0x0004 /* notify parent of changed state */
68#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
69#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
70#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
71 wrapped line, instead of in front of the next character */
72#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
73#define EF_DIALOGMODE 0x0200 /* Indicates that we are inside a dialog window */
75#define ID_CB_LISTBOX 1000
77typedef enum
79 END_0 = 0, /* line ends with terminating '\0' character */
80 END_WRAP, /* line is wrapped */
81 END_HARD, /* line ends with a hard return '\r\n' */
82 END_SOFT, /* line ends with a soft return '\r\r\n' */
83 END_RICH /* line ends with a single '\n' */
84} LINE_END;
86typedef struct tagLINEDEF {
87 INT length; /* bruto length of a line in bytes */
88 INT net_length; /* netto length of a line in visible characters */
90 INT width; /* width of the line in pixels */
91 INT index; /* line index into the buffer */
92 SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data */
94} LINEDEF;
96typedef struct
98 LPWSTR text; /* the actual contents of the control */
99 UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
100 UINT buffer_size; /* the size of the buffer in characters */
101 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
102 HFONT font; /* NULL means standard system font */
103 INT x_offset; /* scroll offset for multi lines this is in pixels
104 for single lines it's in characters */
105#ifdef __REACTOS__
106 DWORD dwCaretWidth;
107#endif
108 INT line_height; /* height of a screen line in pixels */
109 INT char_width; /* average character width in pixels */
110 DWORD style; /* sane version of wnd->dwStyle */
111 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
112 INT undo_insert_count; /* number of characters inserted in sequence */
113 UINT undo_position; /* character index of the insertion and deletion */
114 LPWSTR undo_text; /* deleted text */
115 UINT undo_buffer_size; /* size of the deleted text buffer */
116 INT selection_start; /* == selection_end if no selection */
117 INT selection_end; /* == current caret position */
118 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
119 INT left_margin; /* in pixels */
120 INT right_margin; /* in pixels */
121 RECT format_rect;
122 INT text_width; /* width of the widest line in pixels for multi line controls
123 and just line width for single line controls */
124 INT region_posx; /* Position of cursor relative to region: */
125 INT region_posy; /* -1: to left, 0: within, 1: to right */
126 EDITWORDBREAKPROCW word_break_proc;
127 INT line_count; /* number of lines */
128 INT y_offset; /* scroll offset in number of lines */
129 BOOL bCaptureState; /* flag indicating whether mouse was captured */
130 BOOL bEnableState; /* flag keeping the enable state */
131 HWND hwndSelf; /* the our window handle */
132 HWND hwndParent; /* Handle of parent for sending EN_* messages.
133 Even if parent will change, EN_* messages
134 should be sent to the first parent. */
135 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
136 INT wheelDeltaRemainder; /* scroll wheel delta left over after scrolling whole lines */
137 WCHAR *cue_banner_text;
138 BOOL cue_banner_draw_focused;
139
140 /*
141 * only for multi line controls
142 */
143 INT lock_count; /* amount of re-entries in the EditWndProc */
144 INT tabs_count;
145 LPINT tabs;
146 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
147 HLOCAL hloc32W; /* our unicode local memory block */
148 HLOCAL hlocapp; /* The text buffer handle belongs to the app */
149 /*
150 * IME Data
151 */
152#ifndef __REACTOS__ /* Rely on the composition window */
153 UINT composition_len; /* length of composition, 0 == no composition */
154 int composition_start; /* the character position for the composition */
155#endif
156 /*
157 * Uniscribe Data
158 */
160 SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data for single line controls */
161} EDITSTATE;
162
164#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
165#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
167static inline BOOL notify_parent(const EDITSTATE *es, INT code)
168{
169 HWND hwnd = es->hwndSelf;
170 TRACE("notification %d sent to %p.\n", code, es->hwndParent);
171 SendMessageW(es->hwndParent, WM_COMMAND, MAKEWPARAM(GetWindowLongPtrW(es->hwndSelf, GWLP_ID), code), (LPARAM)es->hwndSelf);
172 return IsWindow(hwnd);
173}
174
175static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
176
177/*********************************************************************
178 *
179 * EM_CANUNDO
180 *
181 */
182static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
183{
184 return (es->undo_insert_count || lstrlenW(es->undo_text));
185}
186
187
188/*********************************************************************
189 *
190 * EM_EMPTYUNDOBUFFER
191 *
192 */
193static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
194{
195 es->undo_insert_count = 0;
196 *es->undo_text = '\0';
197}
199static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
200{
201 HBRUSH hbrush;
202 UINT msg;
203
204 if ((!es->bEnableState || (es->style & ES_READONLY)))
206 else
208
209 /* Why do we notify to es->hwndParent, and we send this one to GetParent()? */
210 hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
211 if (!hbrush)
212 hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
213 return hbrush;
214}
215
217static inline UINT get_text_length(EDITSTATE *es)
218{
219 if(es->text_length == (UINT)-1)
220 es->text_length = lstrlenW(es->text);
221 return es->text_length;
222}
223
224
225/*********************************************************************
226 *
227 * EDIT_WordBreakProc
228 *
229 * Find the beginning of words.
230 * Note: unlike the specs for a WordBreakProc, this function can
231 * only be called without linebreaks between s[0] up to
232 * s[count - 1]. Remember it is only called
233 * internally, so we can decide this for ourselves.
234 * Additionally we will always be breaking the full string.
235 *
236 */
238{
239 INT ret = 0;
240
241 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
242
243 if(!s) return 0;
244
245 if (!es->logAttr)
246 {
248
249 memset(&psa,0,sizeof(SCRIPT_ANALYSIS));
250 psa.eScript = SCRIPT_UNDEFINED;
251
252 es->logAttr = heap_alloc(sizeof(SCRIPT_LOGATTR) * get_text_length(es));
253 ScriptBreak(es->text, get_text_length(es), &psa, es->logAttr);
254 }
255
256 switch (action) {
257 case WB_LEFT:
258 if (index)
259 index--;
260 while (index && !es->logAttr[index].fSoftBreak)
261 index--;
262 ret = index;
263 break;
264 case WB_RIGHT:
265 if (!count)
266 break;
267 while (index < count && s[index] && !es->logAttr[index].fSoftBreak)
268 index++;
269 ret = index;
270 break;
271 case WB_ISDELIMITER:
272 ret = es->logAttr[index].fWhiteSpace;
273 break;
274 default:
275 ERR("unknown action code, please report !\n");
276 break;
277 }
278 return ret;
279}
280
281
282/*********************************************************************
283 *
284 * EDIT_CallWordBreakProc
285 *
286 * Call appropriate WordBreakProc (internal or external).
287 *
288 * Note: The "start" argument should always be an index referring
289 * to es->text. The actual wordbreak proc might be
290 * 16 bit, so we can't always pass any 32 bit LPSTR.
291 * Hence we assume that es->text is the buffer that holds
292 * the string under examination (we can decide this for ourselves).
293 *
294 */
296{
297 INT ret;
298
299 if (es->word_break_proc)
300 ret = es->word_break_proc(es->text + start, index, count, action);
301 else
303
304 return ret;
305}
307static inline void EDIT_InvalidateUniscribeData_linedef(LINEDEF *line_def)
308{
309 if (line_def->ssa)
310 {
311 ScriptStringFree(&line_def->ssa);
312 line_def->ssa = NULL;
313 }
314}
316static inline void EDIT_InvalidateUniscribeData(EDITSTATE *es)
317{
318 LINEDEF *line_def = es->first_line_def;
319 while (line_def)
320 {
322 line_def = line_def->next;
323 }
324 if (es->ssa)
325 {
326 ScriptStringFree(&es->ssa);
327 es->ssa = NULL;
328 }
329}
332{
333 if (!line_def)
334 return NULL;
335
336 if (line_def->net_length && !line_def->ssa)
337 {
338 int index = line_def->index;
339 HFONT old_font = NULL;
340 HDC udc = dc;
341 SCRIPT_TABDEF tabdef;
342 HRESULT hr;
343
344 if (!udc)
345 udc = GetDC(es->hwndSelf);
346 if (es->font)
347 old_font = SelectObject(udc, es->font);
348
349 tabdef.cTabStops = es->tabs_count;
350 tabdef.iScale = GdiGetCharDimensions(udc, NULL, NULL);
351 tabdef.pTabStops = es->tabs;
352 tabdef.iTabOrigin = 0;
353
354 hr = ScriptStringAnalyse(udc, &es->text[index], line_def->net_length,
355 (1.5*line_def->net_length+16), -1,
357 NULL, NULL, NULL, &tabdef, NULL, &line_def->ssa);
358 if (FAILED(hr))
359 {
360 WARN("ScriptStringAnalyse failed (%x)\n",hr);
361 line_def->ssa = NULL;
362 }
363
364 if (es->font)
365 SelectObject(udc, old_font);
366 if (udc != dc)
367 ReleaseDC(es->hwndSelf, udc);
368 }
369
370 return line_def->ssa;
371}
374{
375 LINEDEF *line_def;
376
377 if (!(es->style & ES_MULTILINE))
378 {
379 if (!es->ssa)
380 {
382 HFONT old_font = NULL;
383 HDC udc = dc;
384
385 if (!udc)
386 udc = GetDC(es->hwndSelf);
387 if (es->font)
388 old_font = SelectObject(udc, es->font);
389
390 if (es->style & ES_PASSWORD)
391 ScriptStringAnalyse(udc, &es->password_char, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_PASSWORD, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
392 else
393 ScriptStringAnalyse(udc, es->text, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
394
395 if (es->font)
396 SelectObject(udc, old_font);
397 if (udc != dc)
398 ReleaseDC(es->hwndSelf, udc);
399 }
400 return es->ssa;
401 }
402 else
403 {
404 line_def = es->first_line_def;
405 while (line_def && line)
406 {
407 line_def = line_def->next;
408 line--;
409 }
410
411 return EDIT_UpdateUniscribeData_linedef(es,dc,line_def);
412 }
413}
416{
417 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
418 return max(1,vlc);
419}
420
421/*********************************************************************
422 *
423 * EDIT_BuildLineDefs_ML
424 *
425 * Build linked list of text lines.
426 * Lines can end with '\0' (last line), a character (if it is wrapped),
427 * a soft return '\r\r\n' or a hard return '\r\n'
428 *
429 */
430static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
431{
432 LPWSTR current_position, cp;
433 INT fw;
434 LINEDEF *current_line;
435 LINEDEF *previous_line;
436 LINEDEF *start_line;
437 INT line_index = 0, nstart_line, nstart_index;
438 INT line_count = es->line_count;
439 INT orig_net_length;
440 RECT rc;
441 INT vlc;
442
443 if (istart == iend && delta == 0)
444 return;
445
446 previous_line = NULL;
447 current_line = es->first_line_def;
448
449 /* Find starting line. istart must lie inside an existing line or
450 * at the end of buffer */
451 do {
452 if (istart < current_line->index + current_line->length ||
453 current_line->ending == END_0)
454 break;
455
456 previous_line = current_line;
457 current_line = current_line->next;
458 line_index++;
459 } while (current_line);
460
461 if (!current_line) /* Error occurred start is not inside previous buffer */
462 {
463 FIXME(" modification occurred outside buffer\n");
464 return;
465 }
466
467 /* Remember start of modifications in order to calculate update region */
468 nstart_line = line_index;
469 nstart_index = current_line->index;
470
471 /* We must start to reformat from the previous line since the modifications
472 * may have caused the line to wrap upwards. */
473 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
474 {
475 line_index--;
476 current_line = previous_line;
477 }
478 start_line = current_line;
479
480 fw = es->format_rect.right - es->format_rect.left;
481 current_position = es->text + current_line->index;
483 do {
484 if (current_line != start_line)
485 {
486 if (!current_line || current_line->index + delta > current_position - es->text)
487 {
488 /* The buffer has been expanded, create a new line and
489 insert it into the link list */
490 LINEDEF *new_line = heap_alloc_zero(sizeof(*new_line));
491 new_line->next = previous_line->next;
492 previous_line->next = new_line;
493 current_line = new_line;
494 es->line_count++;
495 }
496 else if (current_line->index + delta < current_position - es->text)
497 {
498 /* The previous line merged with this line so we delete this extra entry */
499 previous_line->next = current_line->next;
500 heap_free(current_line);
501 current_line = previous_line->next;
502 es->line_count--;
503 continue;
504 }
505 else /* current_line->index + delta == current_position */
506 {
507 if (current_position - es->text > iend)
508 break; /* We reached end of line modifications */
509 /* else recalculate this line */
510 }
511 }
512
513 current_line->index = current_position - es->text;
514 orig_net_length = current_line->net_length;
515
516 /* Find end of line */
517 cp = current_position;
518 while (*cp) {
519 if (*cp == '\n') break;
520 if ((*cp == '\r') && (*(cp + 1) == '\n'))
521 break;
522 cp++;
523 }
524
525 /* Mark type of line termination */
526 if (!(*cp)) {
527 current_line->ending = END_0;
528 current_line->net_length = lstrlenW(current_position);
529 } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
530 current_line->ending = END_SOFT;
531 current_line->net_length = cp - current_position - 1;
532 } else if (*cp == '\n') {
533 current_line->ending = END_RICH;
534 current_line->net_length = cp - current_position;
535 } else {
536 current_line->ending = END_HARD;
537 current_line->net_length = cp - current_position;
538 }
539
540 if (current_line->net_length)
541 {
542 const SIZE *sz;
545 if (current_line->ssa)
546 {
547 sz = ScriptString_pSize(current_line->ssa);
548 /* Calculate line width */
549 current_line->width = sz->cx;
550 }
551 else current_line->width = es->char_width * current_line->net_length;
552 }
553 else current_line->width = 0;
554
555 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
556
557/* Line breaks just look back from the end and find the next break and try that. */
558
559 if (!(es->style & ES_AUTOHSCROLL)) {
560 if (current_line->width > fw && fw > es->char_width) {
561
562 INT prev, next;
563 int w;
564 const SIZE *sz;
565 float d;
566
567 prev = current_line->net_length - 1;
568 w = current_line->net_length;
569 d = (float)current_line->width/(float)fw;
570 if (d > 1.2f) d -= 0.2f;
571 next = prev/d;
572 if (next >= prev) next = prev-1;
573 do {
574 prev = EDIT_CallWordBreakProc(es, current_position - es->text,
575 next, current_line->net_length, WB_LEFT);
576 current_line->net_length = prev;
579 if (current_line->ssa)
580 sz = ScriptString_pSize(current_line->ssa);
581 else sz = 0;
582 if (sz)
583 current_line->width = sz->cx;
584 else
585 prev = 0;
586 next = prev - 1;
587 } while (prev && current_line->width > fw);
588 current_line->net_length = w;
589
590 if (prev == 0) { /* Didn't find a line break so force a break */
591 INT *piDx;
592 const INT *count;
593
596
597 if (current_line->ssa)
598 {
599 count = ScriptString_pcOutChars(current_line->ssa);
600 piDx = heap_alloc(sizeof(INT) * (*count));
601 ScriptStringGetLogicalWidths(current_line->ssa,piDx);
602
603 prev = current_line->net_length-1;
604 do {
605 current_line->width -= piDx[prev];
606 prev--;
607 } while ( prev > 0 && current_line->width > fw);
608 if (prev<=0)
609 prev = 1;
610 heap_free(piDx);
611 }
612 else
613 prev = (fw / es->char_width);
614 }
615
616 /* If the first line we are calculating, wrapped before istart, we must
617 * adjust istart in order for this to be reflected in the update region. */
618 if (current_line->index == nstart_index && istart > current_line->index + prev)
619 istart = current_line->index + prev;
620 /* else if we are updating the previous line before the first line we
621 * are re-calculating and it expanded */
622 else if (current_line == start_line &&
623 current_line->index != nstart_index && orig_net_length < prev)
624 {
625 /* Line expanded due to an upwards line wrap so we must partially include
626 * previous line in update region */
627 nstart_line = line_index;
628 nstart_index = current_line->index;
629 istart = current_line->index + orig_net_length;
630 }
631
632 current_line->net_length = prev;
633 current_line->ending = END_WRAP;
634
635 if (current_line->net_length > 0)
636 {
638 if (current_line->ssa)
639 {
640 sz = ScriptString_pSize(current_line->ssa);
641 current_line->width = sz->cx;
642 }
643 else
644 current_line->width = 0;
645 }
646 else current_line->width = 0;
647 }
648 else if (current_line == start_line &&
649 current_line->index != nstart_index &&
650 orig_net_length < current_line->net_length) {
651 /* The previous line expanded but it's still not as wide as the client rect */
652 /* The expansion is due to an upwards line wrap so we must partially include
653 it in the update region */
654 nstart_line = line_index;
655 nstart_index = current_line->index;
656 istart = current_line->index + orig_net_length;
657 }
658 }
659
660
661 /* Adjust length to include line termination */
662 switch (current_line->ending) {
663 case END_SOFT:
664 current_line->length = current_line->net_length + 3;
665 break;
666 case END_RICH:
667 current_line->length = current_line->net_length + 1;
668 break;
669 case END_HARD:
670 current_line->length = current_line->net_length + 2;
671 break;
672 case END_WRAP:
673 case END_0:
674 current_line->length = current_line->net_length;
675 break;
676 }
677 es->text_width = max(es->text_width, current_line->width);
678 current_position += current_line->length;
679 previous_line = current_line;
680
681 /* Discard data for non-visible lines. It will be calculated as needed */
682 if ((line_index < es->y_offset) || (line_index > es->y_offset + vlc))
684
685 current_line = current_line->next;
686 line_index++;
687 } while (previous_line->ending != END_0);
688
689 /* Finish adjusting line indexes by delta or remove hanging lines */
690 if (previous_line->ending == END_0)
691 {
692 LINEDEF *pnext = NULL;
693
694 previous_line->next = NULL;
695 while (current_line)
696 {
697 pnext = current_line->next;
699 heap_free(current_line);
700 current_line = pnext;
701 es->line_count--;
702 }
703 }
704 else if (delta != 0)
705 {
706 while (current_line)
707 {
708 current_line->index += delta;
709 current_line = current_line->next;
710 }
711 }
712
713 /* Calculate rest of modification rectangle */
714 if (hrgn)
715 {
716 HRGN tmphrgn;
717 /*
718 * We calculate two rectangles. One for the first line which may have
719 * an indent with respect to the format rect. The other is a format-width
720 * rectangle that spans the rest of the lines that changed or moved.
721 */
722 rc.top = es->format_rect.top + nstart_line * es->line_height -
723 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
724 rc.bottom = rc.top + es->line_height;
725 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT))
726 rc.left = es->format_rect.left;
727 else
728#ifdef __REACTOS__ /* CORE-11475 */
729 rc.left = (short)LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
730#else
731 rc.left = LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
732#endif
733 rc.right = es->format_rect.right;
734 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
735
736 rc.top = rc.bottom;
737 rc.left = es->format_rect.left;
738 rc.right = es->format_rect.right;
739 /*
740 * If lines were added or removed we must re-paint the remainder of the
741 * lines since the remaining lines were either shifted up or down.
742 */
743 if (line_count < es->line_count) /* We added lines */
744 rc.bottom = es->line_count * es->line_height;
745 else if (line_count > es->line_count) /* We removed lines */
746 rc.bottom = line_count * es->line_height;
747 else
748 rc.bottom = line_index * es->line_height;
749 rc.bottom += es->format_rect.top;
750 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
751 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
752 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
753 DeleteObject(tmphrgn);
754 }
755}
756
757/*********************************************************************
758 *
759 * EDIT_CalcLineWidth_SL
760 *
761 */
763{
765 if (es->ssa)
766 {
767 const SIZE *size;
768 size = ScriptString_pSize(es->ssa);
769 es->text_width = size->cx;
770 }
771 else
772 es->text_width = 0;
773}
774
775/*********************************************************************
776 *
777 * EDIT_CharFromPos
778 *
779 * Beware: This is not the function called on EM_CHARFROMPOS
780 * The position _can_ be outside the formatting / client
781 * rectangle
782 * The return value is only the character index
783 *
784 */
785static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
786{
787 INT index;
788
789 if (es->style & ES_MULTILINE) {
790 int trailing;
791 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
792 INT line_index = 0;
793 LINEDEF *line_def = es->first_line_def;
795 while ((line > 0) && line_def->next) {
796 line_index += line_def->length;
797 line_def = line_def->next;
798 line--;
799 }
800
801 x += es->x_offset - es->format_rect.left;
802 if (es->style & ES_RIGHT)
803 x -= (es->format_rect.right - es->format_rect.left) - line_def->width;
804 else if (es->style & ES_CENTER)
805 x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2;
806 if (x >= line_def->width) {
807 if (after_wrap)
808 *after_wrap = (line_def->ending == END_WRAP);
809 return line_index + line_def->net_length;
810 }
811 if (x <= 0 || !line_def->ssa) {
812 if (after_wrap)
813 *after_wrap = FALSE;
814 return line_index;
815 }
816
817 ScriptStringXtoCP(line_def->ssa, x , &index, &trailing);
818 if (trailing) index++;
819 index += line_index;
820 if (after_wrap)
821 *after_wrap = ((index == line_index + line_def->net_length) &&
822 (line_def->ending == END_WRAP));
823 } else {
824 INT xoff = 0;
825 INT trailing;
826 if (after_wrap)
827 *after_wrap = FALSE;
828 x -= es->format_rect.left;
829 if (!x)
830 return es->x_offset;
831
832 if (!es->x_offset)
833 {
834 INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width;
835 if (es->style & ES_RIGHT)
836 x -= indent;
837 else if (es->style & ES_CENTER)
838 x -= indent / 2;
839 }
840
842 if (es->x_offset)
843 {
844 if (es->ssa)
845 {
846 if (es->x_offset>= get_text_length(es))
847 {
848 const SIZE *size;
849 size = ScriptString_pSize(es->ssa);
850 xoff = size->cx;
851 }
852 ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
853 }
854 else
855 xoff = 0;
856 }
857 if (x < 0)
858 {
859 if (x + xoff > 0 || !es->ssa)
860 {
861 ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
862 if (trailing) index++;
863 }
864 else
865 index = 0;
866 }
867 else
868 {
869 if (x)
870 {
871 const SIZE *size = NULL;
872 if (es->ssa)
873 size = ScriptString_pSize(es->ssa);
874 if (!size)
875 index = 0;
876 else if (x > size->cx)
878 else if (es->ssa)
879 {
880 ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
881 if (trailing) index++;
882 }
883 else
884 index = 0;
885 }
886 else
887 index = es->x_offset;
888 }
889 }
890 return index;
891}
892
893
894/*********************************************************************
895 *
896 * EDIT_ConfinePoint
897 *
898 * adjusts the point to be within the formatting rectangle
899 * (so CharFromPos returns the nearest _visible_ character)
900 *
901 */
902static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y)
903{
904 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
905 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
906}
907
908
909/*********************************************************************
910 *
911 * EM_LINEFROMCHAR
912 *
913 */
915{
916 INT line;
917 LINEDEF *line_def;
918
919 if (!(es->style & ES_MULTILINE))
920 return 0;
921 if (index > (INT)get_text_length(es))
922 return es->line_count - 1;
923 if (index == -1)
924 index = min(es->selection_start, es->selection_end);
925
926 line = 0;
927 line_def = es->first_line_def;
928 index -= line_def->length;
929 while ((index >= 0) && line_def->next) {
930 line++;
931 line_def = line_def->next;
932 index -= line_def->length;
933 }
934 return line;
935}
936
937
938/*********************************************************************
939 *
940 * EM_LINEINDEX
941 *
942 */
944{
945 INT line_index;
946 const LINEDEF *line_def;
947
948 if (!(es->style & ES_MULTILINE))
949 return 0;
950 if (line >= es->line_count)
951 return -1;
952
953 line_index = 0;
954 line_def = es->first_line_def;
955 if (line == -1) {
956 INT index = es->selection_end - line_def->length;
957 while ((index >= 0) && line_def->next) {
958 line_index += line_def->length;
959 line_def = line_def->next;
960 index -= line_def->length;
961 }
962 } else {
963 while (line > 0) {
964 line_index += line_def->length;
965 line_def = line_def->next;
966 line--;
967 }
968 }
969 return line_index;
970}
971
972
973/*********************************************************************
974 *
975 * EM_LINELENGTH
976 *
977 */
979{
980 LINEDEF *line_def;
981
982 if (!(es->style & ES_MULTILINE))
983 return get_text_length(es);
984
985 if (index == -1) {
986 /* get the number of remaining non-selected chars of selected lines */
987 INT32 l; /* line number */
988 INT32 li; /* index of first char in line */
989 INT32 count;
990 l = EDIT_EM_LineFromChar(es, es->selection_start);
991 /* # chars before start of selection area */
992 count = es->selection_start - EDIT_EM_LineIndex(es, l);
993 l = EDIT_EM_LineFromChar(es, es->selection_end);
994 /* # chars after end of selection */
996 count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
997 return count;
998 }
999 line_def = es->first_line_def;
1000 index -= line_def->length;
1001 while ((index >= 0) && line_def->next) {
1002 line_def = line_def->next;
1003 index -= line_def->length;
1004 }
1005 return line_def->net_length;
1006}
1007
1008
1009/*********************************************************************
1010 *
1011 * EM_POSFROMCHAR
1012 *
1014static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap)
1015{
1017 INT l;
1018 INT li;
1019 INT x = 0;
1020 INT y = 0;
1021 INT w;
1022 INT lw;
1023 LINEDEF *line_def;
1024
1025 index = min(index, len);
1026 if (es->style & ES_MULTILINE) {
1029
1030 y = (l - es->y_offset) * es->line_height;
1032 if (after_wrap && (li == index) && l) {
1033 INT l2 = l - 1;
1034 line_def = es->first_line_def;
1035 while (l2) {
1036 line_def = line_def->next;
1037 l2--;
1038 }
1039 if (line_def->ending == END_WRAP) {
1040 l--;
1041 y -= es->line_height;
1043 }
1044 }
1045
1046 line_def = es->first_line_def;
1047 while (line_def->index != li)
1048 line_def = line_def->next;
1049
1050 lw = line_def->width;
1051 w = es->format_rect.right - es->format_rect.left;
1052 if (line_def->ssa)
1053#ifdef __REACTOS__ /* CORE-19731 & match win32ss/user/user32/controls/edit.c */
1054 {
1055 ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x);
1056 x -= es->x_offset;
1057 }
1058 else
1059#else
1060 ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x);
1061#endif
1062#ifdef __REACTOS__ /* CORE-15780 */
1063 x = (lw > 0 ? es->x_offset : x - es->x_offset);
1064#else
1065 x = es->x_offset;
1066#endif
1067
1068 if (es->style & ES_RIGHT)
1069 x = w - (lw - x);
1070 else if (es->style & ES_CENTER)
1071 x += (w - lw) / 2;
1072 } else {
1073 INT xoff = 0;
1074 INT xi = 0;
1076 if (es->x_offset)
1077 {
1078 if (es->ssa)
1079 {
1080 if (es->x_offset >= get_text_length(es))
1081 {
1082 int leftover = es->x_offset - get_text_length(es);
1083 if (es->ssa)
1084 {
1085 const SIZE *size;
1086 size = ScriptString_pSize(es->ssa);
1087 xoff = size->cx;
1088 }
1089 else
1090 xoff = 0;
1091 xoff += es->char_width * leftover;
1092 }
1093 else
1094 ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
1095 }
1096 else
1097 xoff = 0;
1098 }
1099 if (index)
1100 {
1101 if (index >= get_text_length(es))
1102 {
1103 if (es->ssa)
1104 {
1105 const SIZE *size;
1106 size = ScriptString_pSize(es->ssa);
1107 xi = size->cx;
1108 }
1109 else
1110 xi = 0;
1111 }
1112 else if (es->ssa)
1113 ScriptStringCPtoX(es->ssa, index, FALSE, &xi);
1114 else
1115 xi = 0;
1116 }
1117 x = xi - xoff;
1118
1119 if (index >= es->x_offset) {
1120 if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER)))
1121 {
1122 w = es->format_rect.right - es->format_rect.left;
1123 if (w > es->text_width)
1124 {
1125 if (es->style & ES_RIGHT)
1126 x += w - es->text_width;
1127 else if (es->style & ES_CENTER)
1128 x += (w - es->text_width) / 2;
1129 }
1130 }
1131 }
1132 y = 0;
1133 }
1134 x += es->format_rect.left;
1135 y += es->format_rect.top;
1136 return MAKELONG((INT16)x, (INT16)y);
1137}
1138
1139
1140/*********************************************************************
1141 *
1142 * EDIT_GetLineRect
1143 *
1144 * Calculates the bounding rectangle for a line from a starting
1145 * column to an ending column.
1146 *
1148static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1149{
1151 INT line_index = 0;
1152 INT pt1, pt2, pt3;
1153
1154 if (es->style & ES_MULTILINE)
1155 {
1156 const LINEDEF *line_def = NULL;
1157 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1158 if (line >= es->line_count)
1159 return;
1160
1161 line_def = es->first_line_def;
1162 if (line == -1) {
1163 INT index = es->selection_end - line_def->length;
1164 while ((index >= 0) && line_def->next) {
1165 line_index += line_def->length;
1166 line_def = line_def->next;
1167 index -= line_def->length;
1168 }
1169 } else {
1170 while (line > 0) {
1171 line_index += line_def->length;
1172 line_def = line_def->next;
1173 line--;
1174 }
1175 }
1176 ssa = line_def->ssa;
1177 }
1178 else
1179 {
1180 line_index = 0;
1181 rc->top = es->format_rect.top;
1182 ssa = es->ssa;
1183 }
1184
1185 rc->bottom = rc->top + es->line_height;
1186 pt1 = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
1187 pt2 = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
1188 if (ssa)
1189 {
1190 ScriptStringCPtoX(ssa, scol, FALSE, &pt3);
1191 pt3+=es->format_rect.left;
1192 }
1193 else pt3 = pt1;
1194 rc->right = max(max(pt1 , pt2),pt3);
1195 rc->left = min(min(pt1, pt2),pt3);
1196}
1197
1199static inline void text_buffer_changed(EDITSTATE *es)
1200{
1201 es->text_length = (UINT)-1;
1202
1203 heap_free( es->logAttr );
1204 es->logAttr = NULL;
1206}
1207
1208/*********************************************************************
1209 * EDIT_LockBuffer
1210 *
1212static void EDIT_LockBuffer(EDITSTATE *es)
1213{
1214 if (!es->text)
1215 {
1216 if (!es->hloc32W)
1217 return;
1218
1219 es->text = LocalLock(es->hloc32W);
1220 }
1221
1222 es->lock_count++;
1223}
1224
1225
1226/*********************************************************************
1227 *
1228 * EDIT_UnlockBuffer
1229 *
1231static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force)
1232{
1233 /* Edit window might be already destroyed */
1234 if (!IsWindow(es->hwndSelf))
1235 {
1236 WARN("edit hwnd %p already destroyed\n", es->hwndSelf);
1237 return;
1238 }
1239
1240 if (!es->lock_count)
1241 {
1242 ERR("lock_count == 0 ... please report\n");
1243 return;
1244 }
1245
1246 if (!es->text)
1247 {
1248 ERR("es->text == 0 ... please report\n");
1249 return;
1250 }
1251
1252 if (force || (es->lock_count == 1))
1253 {
1254 if (es->hloc32W)
1255 {
1256 LocalUnlock(es->hloc32W);
1257 es->text = NULL;
1258 }
1259 else
1260 {
1261 ERR("no buffer ... please report\n");
1262 return;
1263 }
1264
1265 }
1266
1267 es->lock_count--;
1268}
1269
1270
1271/*********************************************************************
1272 *
1273 * EDIT_MakeFit
1274 *
1275 * Try to fit size + 1 characters in the buffer.
1278{
1279 HLOCAL hNew32W;
1280
1281 if (size <= es->buffer_size)
1282 return TRUE;
1283
1284 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1285
1286 /* Force edit to unlock its buffer. es->text now NULL */
1288
1289 if (es->hloc32W) {
1290 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1291 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1292 TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W);
1293 es->hloc32W = hNew32W;
1294 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1295 }
1296 }
1297
1299
1300 if (es->buffer_size < size) {
1301 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1303 return FALSE;
1304 } else {
1305 TRACE("We now have %d+1\n", es->buffer_size);
1306 return TRUE;
1307 }
1308}
1309
1310
1311/*********************************************************************
1312 *
1313 * EDIT_MakeUndoFit
1314 *
1315 * Try to fit size + 1 bytes in the undo buffer.
1316 *
1319{
1320 UINT alloc_size;
1321
1322 if (size <= es->undo_buffer_size)
1323 return TRUE;
1324
1325 TRACE("trying to ReAlloc to %d+1\n", size);
1326
1327 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1328 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1329 es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1330 return TRUE;
1331 }
1332 else
1333 {
1334 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1335 return FALSE;
1336 }
1337}
1338
1339
1340/*********************************************************************
1341 *
1342 * EDIT_UpdateTextRegion
1343 *
1345static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
1346{
1347 if (es->flags & EF_UPDATE) {
1348 es->flags &= ~EF_UPDATE;
1349 if (!notify_parent(es, EN_UPDATE)) return;
1350 }
1351 InvalidateRgn(es->hwndSelf, hrgn, bErase);
1352}
1353
1354
1355/*********************************************************************
1356 *
1357 * EDIT_UpdateText
1358 *
1360static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase)
1361{
1362 if (es->flags & EF_UPDATE) {
1363 es->flags &= ~EF_UPDATE;
1364 if (!notify_parent(es, EN_UPDATE)) return;
1365 }
1366 InvalidateRect(es->hwndSelf, rc, bErase);
1367}
1368
1369/*********************************************************************
1370 *
1371 * EDIT_SL_InvalidateText
1372 *
1373 * Called from EDIT_InvalidateText().
1374 * Does the job for single-line controls only.
1375 *
1378{
1379 RECT line_rect;
1380 RECT rc;
1381
1382 EDIT_GetLineRect(es, 0, start, end, &line_rect);
1383 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1384 EDIT_UpdateText(es, &rc, TRUE);
1385}
1386
1387/*********************************************************************
1388 *
1389 * EDIT_ML_InvalidateText
1390 *
1391 * Called from EDIT_InvalidateText().
1392 * Does the job for multi-line controls only.
1393 *
1396{
1400 INT sc;
1401 INT ec;
1402 RECT rc1;
1403 RECT rcWnd;
1404 RECT rcLine;
1405 RECT rcUpdate;
1406 INT l;
1407
1408 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1409 return;
1410
1411 sc = start - EDIT_EM_LineIndex(es, sl);
1412 ec = end - EDIT_EM_LineIndex(es, el);
1413 if (sl < es->y_offset) {
1414 sl = es->y_offset;
1415 sc = 0;
1416 }
1417 if (el > es->y_offset + vlc) {
1418 el = es->y_offset + vlc;
1420 }
1421 GetClientRect(es->hwndSelf, &rc1);
1422 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1423 if (sl == el) {
1424 EDIT_GetLineRect(es, sl, sc, ec, &rcLine);
1425 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1426 EDIT_UpdateText(es, &rcUpdate, TRUE);
1427 } else {
1428 EDIT_GetLineRect(es, sl, sc,
1430 EDIT_EM_LineIndex(es, sl)),
1431 &rcLine);
1432 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1433 EDIT_UpdateText(es, &rcUpdate, TRUE);
1434 for (l = sl + 1 ; l < el ; l++) {
1435 EDIT_GetLineRect(es, l, 0,
1438 &rcLine);
1439 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1440 EDIT_UpdateText(es, &rcUpdate, TRUE);
1441 }
1442 EDIT_GetLineRect(es, el, 0, ec, &rcLine);
1443 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1444 EDIT_UpdateText(es, &rcUpdate, TRUE);
1445 }
1446}
1447
1448
1449/*********************************************************************
1450 *
1451 * EDIT_InvalidateText
1452 *
1453 * Invalidate the text from offset start up to, but not including,
1454 * offset end. Useful for (re)painting the selection.
1455 * Regions outside the linewidth are not invalidated.
1456 * end == -1 means end == TextLength.
1457 * start and end need not be ordered.
1458 *
1461{
1462 if (end == start)
1463 return;
1464
1465 if (end == -1)
1467
1468 if (end < start) {
1469 INT tmp = start;
1470 start = end;
1471 end = tmp;
1472 }
1473
1474 if (es->style & ES_MULTILINE)
1476 else
1478}
1479
1480
1481/*********************************************************************
1482 *
1483 * EDIT_EM_SetSel
1484 *
1485 * note: unlike the specs say: the order of start and end
1486 * _is_ preserved in Windows. (i.e. start can be > end)
1487 * In other words: this handler is OK
1488 *
1490static BOOL EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
1491{
1492 UINT old_start = es->selection_start;
1493 UINT old_end = es->selection_end;
1495
1496 if (start == old_start && end == old_end)
1497 return FALSE;
1498
1499 if (start == (UINT)-1) {
1500 start = es->selection_end;
1501 end = es->selection_end;
1502 } else {
1503 start = min(start, len);
1504 end = min(end, len);
1505 }
1506 es->selection_start = start;
1507 es->selection_end = end;
1508 if (after_wrap)
1509 es->flags |= EF_AFTER_WRAP;
1510 else
1511 es->flags &= ~EF_AFTER_WRAP;
1512 /* Compute the necessary invalidation region. */
1513 /* Note that we don't need to invalidate regions which have
1514 * "never" been selected, or those which are "still" selected.
1515 * In fact, every time we hit a selection boundary, we can
1516 * *toggle* whether we need to invalidate. Thus we can optimize by
1517 * *sorting* the interval endpoints. Let's assume that we sort them
1518 * in this order:
1519 * start <= end <= old_start <= old_end
1520 * Knuth 5.3.1 (p 183) assures us that this can be done optimally
1521 * in 5 comparisons; i.e. it is impossible to do better than the
1522 * following: */
1523 ORDER_UINT(end, old_end);
1524 ORDER_UINT(start, old_start);
1525 ORDER_UINT(old_start, old_end);
1527 /* Note that at this point 'end' and 'old_start' are not in order, but
1528 * start is definitely the min. and old_end is definitely the max. */
1529 if (end != old_start)
1530 {
1531/*
1532 * One can also do
1533 * ORDER_UINT32(end, old_start);
1534 * EDIT_InvalidateText(es, start, end);
1535 * EDIT_InvalidateText(es, old_start, old_end);
1536 * in place of the following if statement.
1537 * (That would complete the optimal five-comparison four-element sort.)
1538 */
1539 if (old_start > end )
1540 {
1542 EDIT_InvalidateText(es, old_start, old_end);
1543 }
1544 else
1545 {
1546 EDIT_InvalidateText(es, start, old_start);
1547 EDIT_InvalidateText(es, end, old_end);
1548 }
1549 }
1550 else EDIT_InvalidateText(es, start, old_end);
1551
1552 return TRUE;
1553}
1554
1555
1556/*********************************************************************
1557 *
1558 * EDIT_UpdateScrollInfo
1559 *
1562{
1563 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
1564 {
1565 SCROLLINFO si;
1566 si.cbSize = sizeof(SCROLLINFO);
1568 si.nMin = 0;
1569 si.nMax = es->line_count - 1;
1570 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1571 si.nPos = es->y_offset;
1572 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1573 si.nMin, si.nMax, si.nPage, si.nPos);
1574 SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
1575 }
1576
1577 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
1578 {
1579 SCROLLINFO si;
1580 si.cbSize = sizeof(SCROLLINFO);
1582 si.nMin = 0;
1583 si.nMax = es->text_width - 1;
1584 si.nPage = es->format_rect.right - es->format_rect.left;
1585 si.nPos = es->x_offset;
1586 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1587 si.nMin, si.nMax, si.nPage, si.nPos);
1588 SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
1589 }
1590}
1591
1592
1593/*********************************************************************
1594 *
1595 * EDIT_EM_LineScroll_internal
1596 *
1597 * Version of EDIT_EM_LineScroll for internal use.
1598 * It doesn't refuse if ES_MULTILINE is set and assumes that
1599 * dx is in pixels, dy - in lines.
1600 *
1603{
1604 INT nyoff;
1605 INT x_offset_in_pixels;
1606 INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) /
1607 es->line_height;
1608
1609 if (es->style & ES_MULTILINE)
1610 {
1611 x_offset_in_pixels = es->x_offset;
1612 }
1613 else
1614 {
1615 dy = 0;
1616 x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE));
1617 }
1618
1619 if (-dx > x_offset_in_pixels)
1620 dx = -x_offset_in_pixels;
1621 if (dx > es->text_width - x_offset_in_pixels)
1622 dx = es->text_width - x_offset_in_pixels;
1623 nyoff = max(0, es->y_offset + dy);
1624 if (nyoff >= es->line_count - lines_per_page)
1625 nyoff = max(0, es->line_count - lines_per_page);
1626 dy = (es->y_offset - nyoff) * es->line_height;
1627 if (dx || dy) {
1628 RECT rc1;
1629 RECT rc;
1630
1631 es->y_offset = nyoff;
1632 if(es->style & ES_MULTILINE)
1633 es->x_offset += dx;
1634 else
1635 es->x_offset += dx / es->char_width;
1636
1637 GetClientRect(es->hwndSelf, &rc1);
1638 IntersectRect(&rc, &rc1, &es->format_rect);
1639 ScrollWindowEx(es->hwndSelf, -dx, dy,
1640 NULL, &rc, NULL, NULL, SW_INVALIDATE);
1641 /* force scroll info update */
1643 }
1644 if (dx && !(es->flags & EF_HSCROLL_TRACK))
1646 if (dy && !(es->flags & EF_VSCROLL_TRACK))
1648 return TRUE;
1649}
1650
1651/*********************************************************************
1652 *
1653 * EM_LINESCROLL
1654 *
1655 * NOTE: dx is in average character widths, dy - in lines;
1656 *
1659{
1660 if (!(es->style & ES_MULTILINE))
1661 return FALSE;
1662
1663 dx *= es->char_width;
1665}
1666
1667
1668/*********************************************************************
1669 *
1670 * EM_SCROLL
1671 *
1674{
1675 INT dy;
1676
1677 if (!(es->style & ES_MULTILINE))
1678 return (LRESULT)FALSE;
1679
1680 dy = 0;
1681
1682 switch (action) {
1683 case SB_LINEUP:
1684 if (es->y_offset)
1685 dy = -1;
1686 break;
1687 case SB_LINEDOWN:
1688 if (es->y_offset < es->line_count - 1)
1689 dy = 1;
1690 break;
1691 case SB_PAGEUP:
1692 if (es->y_offset)
1693 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
1694 break;
1695 case SB_PAGEDOWN:
1696 if (es->y_offset < es->line_count - 1)
1697 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1698 break;
1699 default:
1701 }
1702 if (dy) {
1704 /* check if we are going to move too far */
1705 if(es->y_offset + dy > es->line_count - vlc)
1706 dy = max(es->line_count - vlc, 0) - es->y_offset;
1707
1708 /* Notification is done in EDIT_EM_LineScroll */
1709 if(dy) {
1711 return MAKELONG(dy, TRUE);
1712 }
1713
1714 }
1715 return (LRESULT)FALSE;
1716}
1717
1718
1719#ifdef __REACTOS__
1720static void EDIT_ImmSetCompositionWindow(EDITSTATE *es, POINT pt)
1721{
1722 COMPOSITIONFORM CompForm;
1723 HIMC hIMC = ImmGetContext(es->hwndSelf);
1724 if (!hIMC)
1725 {
1726 ERR("!hIMC\n");
1727 return;
1728 }
1729
1730 CompForm.ptCurrentPos = pt;
1731 if (es->style & ES_MULTILINE)
1732 {
1733 CompForm.dwStyle = CFS_RECT;
1734 CompForm.rcArea = es->format_rect;
1735 }
1736 else
1737 {
1738 CompForm.dwStyle = CFS_POINT;
1739 SetRectEmpty(&CompForm.rcArea);
1740 }
1741
1742 ImmSetCompositionWindow(hIMC, &CompForm);
1743 ImmReleaseContext(es->hwndSelf, hIMC);
1744}
1745#endif
1746/*********************************************************************
1747 *
1748 * EDIT_SetCaretPos
1749 *
1751static void EDIT_SetCaretPos(EDITSTATE *es, INT pos,
1752 BOOL after_wrap)
1753{
1754 LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap);
1755#ifdef __REACTOS__
1756 HKL hKL = GetKeyboardLayout(0);
1757 POINT pt = { (short)LOWORD(res), (short)HIWORD(res) };
1758
1759 /* Don't set caret if not focused */
1760 if ((es->flags & EF_FOCUSED) == 0)
1761 return;
1762
1763 SetCaretPos(pt.x, pt.y);
1764
1765 if (!ImmIsIME(hKL))
1766 return;
1767
1768 EDIT_ImmSetCompositionWindow(es, pt);
1769#else
1770 TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res));
1771 SetCaretPos((short)LOWORD(res), (short)HIWORD(res));
1772#endif
1773}
1774
1775
1776/*********************************************************************
1777 *
1778 * EM_SCROLLCARET
1779 *
1781static void EDIT_EM_ScrollCaret(EDITSTATE *es)
1782{
1783 if (es->style & ES_MULTILINE) {
1784 INT l;
1785 INT vlc;
1786 INT ww;
1787 INT cw = es->char_width;
1788 INT x;
1789 INT dy = 0;
1790 INT dx = 0;
1791
1792 l = EDIT_EM_LineFromChar(es, es->selection_end);
1793 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP));
1795 if (l >= es->y_offset + vlc)
1796 dy = l - vlc + 1 - es->y_offset;
1797 if (l < es->y_offset)
1798 dy = l - es->y_offset;
1799 ww = es->format_rect.right - es->format_rect.left;
1800 if (x < es->format_rect.left)
1801 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1802 if (x > es->format_rect.right)
1803 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1804 if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1805 {
1806 /* check if we are going to move too far */
1807 if(es->x_offset + dx + ww > es->text_width)
1808 dx = es->text_width - ww - es->x_offset;
1809 if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1811 }
1812 } else {
1813 INT x;
1814 INT goal;
1815 INT format_width;
1816
1817 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1818 format_width = es->format_rect.right - es->format_rect.left;
1819 if (x < es->format_rect.left) {
1820 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1821 do {
1822 es->x_offset--;
1823 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1824 } while ((x < goal) && es->x_offset);
1825 /* FIXME: use ScrollWindow() somehow to improve performance */
1827 } else if (x > es->format_rect.right) {
1828 INT x_last;
1830 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1831 do {
1832 es->x_offset++;
1833 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1835 } while ((x > goal) && (x_last > es->format_rect.right));
1836 /* FIXME: use ScrollWindow() somehow to improve performance */
1838 }
1839 }
1840
1841 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
1842}
1843
1844
1845/*********************************************************************
1846 *
1847 * EDIT_MoveBackward
1848 *
1850static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend)
1851{
1852 INT e = es->selection_end;
1853
1854 if (e) {
1855 e--;
1856 if ((es->style & ES_MULTILINE) && e &&
1857 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1858 e--;
1859 if (e && (es->text[e - 1] == '\r'))
1860 e--;
1861 }
1862 }
1863 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1865}
1866
1867
1868/*********************************************************************
1869 *
1870 * EDIT_MoveDown_ML
1871 *
1872 * Only for multi line controls
1873 * Move the caret one line down, on a column with the nearest
1874 * x coordinate on the screen (might be a different column).
1875 *
1877static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend)
1878{
1879 INT s = es->selection_start;
1880 INT e = es->selection_end;
1881 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1882 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1883 INT x = (short)LOWORD(pos);
1884 INT y = (short)HIWORD(pos);
1885
1886 e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap);
1887 if (!extend)
1888 s = e;
1889 EDIT_EM_SetSel(es, s, e, after_wrap);
1891}
1892
1893
1894/*********************************************************************
1895 *
1896 * EDIT_MoveEnd
1897 *
1899static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl)
1900{
1901 BOOL after_wrap = FALSE;
1902 INT e;
1903
1904 /* Pass a high value in x to make sure of receiving the end of the line */
1905 if (!ctrl && (es->style & ES_MULTILINE))
1906 e = EDIT_CharFromPos(es, 0x3fffffff,
1907 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1908 else
1909 e = get_text_length(es);
1910 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap);
1912}
1913
1914
1915/*********************************************************************
1916 *
1917 * EDIT_MoveForward
1918 *
1920static void EDIT_MoveForward(EDITSTATE *es, BOOL extend)
1921{
1922 INT e = es->selection_end;
1923
1924 if (es->text[e]) {
1925 e++;
1926 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1927 if (es->text[e] == '\n')
1928 e++;
1929 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1930 e += 2;
1931 }
1932 }
1933 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1935}
1936
1937
1938/*********************************************************************
1939 *
1940 * EDIT_MoveHome
1941 *
1942 * Home key: move to beginning of line.
1943 *
1945static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl)
1946{
1947 INT e;
1948
1949 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1950 if (!ctrl && (es->style & ES_MULTILINE))
1951 e = EDIT_CharFromPos(es, -es->x_offset,
1952 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1953 else
1954 e = 0;
1955 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1957}
1958
1959
1960/*********************************************************************
1961 *
1962 * EDIT_MovePageDown_ML
1963 *
1964 * Only for multi line controls
1965 * Move the caret one page down, on a column with the nearest
1966 * x coordinate on the screen (might be a different column).
1967 *
1969static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend)
1970{
1971 INT s = es->selection_start;
1972 INT e = es->selection_end;
1973 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1974 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1975 INT x = (short)LOWORD(pos);
1976 INT y = (short)HIWORD(pos);
1977
1979 y + (es->format_rect.bottom - es->format_rect.top),
1980 &after_wrap);
1981 if (!extend)
1982 s = e;
1983 EDIT_EM_SetSel(es, s, e, after_wrap);
1985}
1986
1987
1988/*********************************************************************
1989 *
1990 * EDIT_MovePageUp_ML
1991 *
1992 * Only for multi line controls
1993 * Move the caret one page up, on a column with the nearest
1994 * x coordinate on the screen (might be a different column).
1995 *
1997static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend)
1998{
1999 INT s = es->selection_start;
2000 INT e = es->selection_end;
2001 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2002 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2003 INT x = (short)LOWORD(pos);
2004 INT y = (short)HIWORD(pos);
2005
2007 y - (es->format_rect.bottom - es->format_rect.top),
2008 &after_wrap);
2009 if (!extend)
2010 s = e;
2011 EDIT_EM_SetSel(es, s, e, after_wrap);
2013}
2014
2015
2016/*********************************************************************
2017 *
2018 * EDIT_MoveUp_ML
2019 *
2020 * Only for multi line controls
2021 * Move the caret one line up, on a column with the nearest
2022 * x coordinate on the screen (might be a different column).
2023 *
2025static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend)
2026{
2027 INT s = es->selection_start;
2028 INT e = es->selection_end;
2029 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2030 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2031 INT x = (short)LOWORD(pos);
2032 INT y = (short)HIWORD(pos);
2033
2034 e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap);
2035 if (!extend)
2036 s = e;
2037 EDIT_EM_SetSel(es, s, e, after_wrap);
2039}
2040
2041
2042/*********************************************************************
2043 *
2044 * EDIT_MoveWordBackward
2045 *
2047static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend)
2048{
2049 INT s = es->selection_start;
2050 INT e = es->selection_end;
2051 INT l;
2052 INT ll;
2053 INT li;
2054
2058 if (e - li == 0) {
2059 if (l) {
2060 li = EDIT_EM_LineIndex(es, l - 1);
2061 e = li + EDIT_EM_LineLength(es, li);
2062 }
2063 } else {
2065 }
2066 if (!extend)
2067 s = e;
2070}
2071
2072
2073/*********************************************************************
2074 *
2075 * EDIT_MoveWordForward
2076 *
2078static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend)
2079{
2080 INT s = es->selection_start;
2081 INT e = es->selection_end;
2082 INT l;
2083 INT ll;
2084 INT li;
2085
2089 if (e - li == ll) {
2090 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2091 e = EDIT_EM_LineIndex(es, l + 1);
2092 } else {
2094 li, e - li + 1, ll, WB_RIGHT);
2095 }
2096 if (!extend)
2097 s = e;
2100}
2101
2102
2103/*********************************************************************
2104 *
2105 * EDIT_PaintText
2106 *
2109{
2110 COLORREF BkColor;
2111 COLORREF TextColor;
2112 LOGFONTW underline_font;
2113 HFONT hUnderline = 0;
2114 HFONT old_font = 0;
2115 INT ret;
2116 INT li;
2117 INT BkMode;
2118 SIZE size;
2119
2120 if (!count)
2121 return 0;
2122 BkMode = GetBkMode(dc);
2123 BkColor = GetBkColor(dc);
2124 TextColor = GetTextColor(dc);
2125 if (rev) {
2126#ifdef __REACTOS__
2127 if (TRUE)
2128#else
2129 if (es->composition_len == 0)
2130#endif
2131 {
2134 SetBkMode( dc, OPAQUE);
2135 }
2136 else
2137 {
2139 GetObjectW(current,sizeof(LOGFONTW),&underline_font);
2140 underline_font.lfUnderline = TRUE;
2141 hUnderline = CreateFontIndirectW(&underline_font);
2142 old_font = SelectObject(dc,hUnderline);
2143 }
2144 }
2146 if (es->style & ES_MULTILINE) {
2147 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2148 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2149 } else {
2150 TextOutW(dc, x, y, es->text + li + col, count);
2151 GetTextExtentPoint32W(dc, es->text + li + col, count, &size);
2152 ret = size.cx;
2153 }
2154 if (rev) {
2155#ifdef __REACTOS__
2156 if (TRUE)
2157#else
2158 if (es->composition_len == 0)
2159#endif
2160 {
2161 SetBkColor(dc, BkColor);
2162 SetTextColor(dc, TextColor);
2163 SetBkMode( dc, BkMode);
2164 }
2165 else
2166 {
2167 if (old_font)
2168 SelectObject(dc,old_font);
2169 if (hUnderline)
2170 DeleteObject(hUnderline);
2171 }
2172 }
2173 return ret;
2174}
2175
2176
2177/*********************************************************************
2178 *
2179 * EDIT_PaintLine
2180 *
2182static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev)
2183{
2184 INT s = 0;
2185 INT e = 0;
2186 INT li = 0;
2187 INT ll = 0;
2188 INT x;
2189 INT y;
2190 LRESULT pos;
2192
2193 if (es->style & ES_MULTILINE) {
2195
2196 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2197 return;
2198 } else if (line)
2199 return;
2200
2201 TRACE("line=%d\n", line);
2202
2205 x = (short)LOWORD(pos);
2206 y = (short)HIWORD(pos);
2207
2208 if (es->style & ES_MULTILINE)
2209 {
2210 int line_idx = line;
2211 x = -es->x_offset;
2212 if (es->style & ES_RIGHT || es->style & ES_CENTER)
2213 {
2214 LINEDEF *line_def = es->first_line_def;
2215 int w, lw;
2216
2217 while (line_def && line_idx)
2218 {
2219 line_def = line_def->next;
2220 line_idx--;
2221 }
2222 w = es->format_rect.right - es->format_rect.left;
2223 lw = line_def->width;
2224
2225 if (es->style & ES_RIGHT)
2226 x = w - (lw - x);
2227 else if (es->style & ES_CENTER)
2228 x += (w - lw) / 2;
2229 }
2230 x += es->format_rect.left;
2231 }
2232
2233 if (rev)
2234 {
2237 s = min(es->selection_start, es->selection_end);
2238 e = max(es->selection_start, es->selection_end);
2239 s = min(li + ll, max(li, s));
2240 e = min(li + ll, max(li, e));
2241 }
2242
2243 if (ssa)
2244 ScriptStringOut(ssa, x, y, 0, &es->format_rect, s - li, e - li, FALSE);
2245 else if (rev && (s != e) &&
2246 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2247 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2248 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2249 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2250 } else
2251 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2252
2253 if (es->cue_banner_text && es->text_length == 0 && (!(es->flags & EF_FOCUSED) || es->cue_banner_draw_focused))
2254 {
2256 TextOutW(dc, x, y, es->cue_banner_text, lstrlenW(es->cue_banner_text));
2257 }
2258}
2259
2260
2261/*********************************************************************
2262 *
2263 * EDIT_AdjustFormatRect
2264 *
2265 * Adjusts the format rectangle for the current font and the
2266 * current client rectangle.
2267 *
2270{
2271 RECT ClientRect;
2272
2273 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2274 if (es->style & ES_MULTILINE)
2275 {
2276 INT fw, vlc, max_x_offset, max_y_offset;
2277
2279 es->format_rect.bottom = es->format_rect.top + vlc * es->line_height;
2280
2281 /* correct es->x_offset */
2282 fw = es->format_rect.right - es->format_rect.left;
2283 max_x_offset = es->text_width - fw;
2284 if(max_x_offset < 0) max_x_offset = 0;
2285 if(es->x_offset > max_x_offset)
2286 es->x_offset = max_x_offset;
2287
2288 /* correct es->y_offset */
2289 max_y_offset = es->line_count - vlc;
2290 if(max_y_offset < 0) max_y_offset = 0;
2291 if(es->y_offset > max_y_offset)
2292 es->y_offset = max_y_offset;
2293
2294 /* force scroll info update */
2296 }
2297 else
2298 /* Windows doesn't care to fix text placement for SL controls */
2299 es->format_rect.bottom = es->format_rect.top + es->line_height;
2300
2301 /* Always stay within the client area */
2302 GetClientRect(es->hwndSelf, &ClientRect);
2303 es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom);
2304
2305 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2307
2308 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
2309}
2310
2311
2312/*********************************************************************
2313 *
2314 * EDIT_SetRectNP
2315 *
2316 * note: this is not (exactly) the handler called on EM_SETRECTNP
2317 * it is also used to set the rect of a single line control
2318 *
2320static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc)
2321{
2323 INT bw, bh;
2325
2326 CopyRect(&es->format_rect, rc);
2327
2328 if (ExStyle & WS_EX_CLIENTEDGE) {
2329 es->format_rect.left++;
2330 es->format_rect.right--;
2331
2332 if (es->format_rect.bottom - es->format_rect.top
2333 >= es->line_height + 2)
2334 {
2335 es->format_rect.top++;
2336 es->format_rect.bottom--;
2337 }
2338 }
2339 else if (es->style & WS_BORDER) {
2341 bh = GetSystemMetrics(SM_CYBORDER) + 1;
2342 InflateRect(&es->format_rect, -bw, 0);
2343 if (es->format_rect.bottom - es->format_rect.top >= es->line_height + 2 * bh)
2344 InflateRect(&es->format_rect, 0, -bh);
2345 }
2346
2347 es->format_rect.left += es->left_margin;
2348 es->format_rect.right -= es->right_margin;
2350}
2351
2352
2353/*********************************************************************
2354 *
2355 * EM_CHARFROMPOS
2356 *
2357 * returns line number (not index) in high-order word of result.
2358 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2359 * to Richedit, not to the edit control. Original documentation is valid.
2360 * FIXME: do the specs mean to return -1 if outside client area or
2361 * if outside formatting rectangle ???
2362 *
2365{
2366 POINT pt;
2367 RECT rc;
2368 INT index;
2369
2370 pt.x = x;
2371 pt.y = y;
2372 GetClientRect(es->hwndSelf, &rc);
2373 if (!PtInRect(&rc, pt))
2374 return -1;
2375
2378}
2379
2380
2381/*********************************************************************
2382 *
2383 * EM_FMTLINES
2384 *
2385 * Enable or disable soft breaks.
2386 *
2387 * This means: insert or remove the soft linebreak character (\r\r\n).
2388 * Take care to check if the text still fits the buffer after insertion.
2389 * If not, notify with EN_ERRSPACE.
2390 *
2392static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2393{
2394 es->flags &= ~EF_USE_SOFTBRK;
2395 if (add_eol) {
2396 es->flags |= EF_USE_SOFTBRK;
2397 FIXME("soft break enabled, not implemented\n");
2398 }
2399 return add_eol;
2400}
2401
2402
2403/*********************************************************************
2404 *
2405 * EM_GETHANDLE
2406 *
2407 * Hopefully this won't fire back at us.
2408 * We always start with a fixed buffer in the local heap.
2409 * Despite of the documentation says that the local heap is used
2410 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2411 * buffer on the local heap.
2412 *
2415{
2416 if (!(es->style & ES_MULTILINE))
2417 return 0;
2418
2420
2421 /* The text buffer handle belongs to the app */
2422 es->hlocapp = es->hloc32W;
2423
2424 TRACE("Returning %p, LocalSize() = %ld\n", es->hlocapp, LocalSize(es->hlocapp));
2425 return es->hlocapp;
2426}
2427
2428
2429/*********************************************************************
2430 *
2431 * EM_GETLINE
2432 *
2435{
2436 INT line_len, dst_len;
2437 LPWSTR src;
2438 INT i;
2439
2440 if (es->style & ES_MULTILINE)
2441 {
2442 if (line >= es->line_count)
2443 return 0;
2444 }
2445 else
2446 line = 0;
2447
2449 src = es->text + i;
2450 line_len = EDIT_EM_LineLength(es, i);
2451 dst_len = *(WORD *)dst;
2452
2453 if (dst_len <= line_len)
2454 {
2455 memcpy(dst, src, dst_len * sizeof(WCHAR));
2456 return dst_len;
2457 }
2458 else /* Append 0 if enough space */
2459 {
2460 memcpy(dst, src, line_len * sizeof(WCHAR));
2461 dst[line_len] = 0;
2462 return line_len;
2463 }
2464}
2465
2466
2467/*********************************************************************
2468 *
2469 * EM_GETSEL
2470 *
2473{
2474 UINT s = es->selection_start;
2475 UINT e = es->selection_end;
2476
2477 ORDER_UINT(s, e);
2478 if (start)
2479 *start = s;
2480 if (end)
2481 *end = e;
2482 return MAKELONG(s, e);
2483}
2484
2485
2486/*********************************************************************
2487 *
2488 * EM_REPLACESEL
2489 *
2490 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2491 *
2493static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_replace, UINT strl,
2494 BOOL send_update, BOOL honor_limit)
2495{
2496 UINT tl = get_text_length(es);
2497 UINT utl;
2498 UINT s;
2499 UINT e;
2500 UINT i;
2501 UINT size;
2502 LPWSTR p;
2503 HRGN hrgn = 0;
2504 LPWSTR buf = NULL;
2505 UINT bufl;
2506
2507 TRACE("%s, can_undo %d, send_update %d\n",
2508 debugstr_wn(lpsz_replace, strl), can_undo, send_update);
2509
2510 s = es->selection_start;
2511 e = es->selection_end;
2512
2514 if ((s == e) && !strl)
2515 return;
2516
2517 ORDER_UINT(s, e);
2518
2519 size = tl - (e - s) + strl;
2520 if (!size)
2521 es->text_width = 0;
2522
2523 /* Issue the EN_MAXTEXT notification and continue with replacing text
2524 * so that buffer limit is honored. */
2525 if ((honor_limit) && (size > es->buffer_limit))
2526 {
2527 if (!notify_parent(es, EN_MAXTEXT)) return;
2528 /* Buffer limit can be smaller than the actual length of text in combobox */
2529 if (es->buffer_limit < (tl - (e-s)))
2530 strl = 0;
2531 else
2532 strl = min(strl, es->buffer_limit - (tl - (e-s)));
2533 }
2534
2535 if (!EDIT_MakeFit(es, tl - (e - s) + strl))
2536 return;
2537
2538 if (e != s) {
2539 /* there is something to be deleted */
2540 TRACE("deleting stuff.\n");
2541 bufl = e - s;
2542 buf = heap_alloc((bufl + 1) * sizeof(WCHAR));
2543 if (!buf) return;
2544 memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
2545 buf[bufl] = 0; /* ensure 0 termination */
2546 /* now delete */
2547 lstrcpyW(es->text + s, es->text + e);
2549 }
2550 if (strl) {
2551 /* there is an insertion */
2552 tl = get_text_length(es);
2553 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));
2554 for (p = es->text + tl ; p >= es->text + s ; p--)
2555 p[strl] = p[0];
2556 for (i = 0 , p = es->text + s ; i < strl ; i++)
2557 p[i] = lpsz_replace[i];
2558 if(es->style & ES_UPPERCASE)
2559 CharUpperBuffW(p, strl);
2560 else if(es->style & ES_LOWERCASE)
2561 CharLowerBuffW(p, strl);
2563 }
2564 if (es->style & ES_MULTILINE)
2565 {
2566 INT st = min(es->selection_start, es->selection_end);
2568
2569 hrgn = CreateRectRgn(0, 0, 0, 0);
2570 EDIT_BuildLineDefs_ML(es, st, st + strl,
2571 strl - abs(es->selection_end - es->selection_start), hrgn);
2572 /* if text is too long undo all changes */
2573 if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
2574 if (strl)
2575 lstrcpyW(es->text + e, es->text + e + strl);
2576 if (e != s)
2577 for (i = 0 , p = es->text ; i < e - s ; i++)
2578 p[i + s] = buf[i];
2581 abs(es->selection_end - es->selection_start) - strl, hrgn);
2582 strl = 0;
2583 e = s;
2584 SetRectRgn(hrgn, 0, 0, 0, 0);
2585 if (!notify_parent(es, EN_MAXTEXT)) return;
2586 }
2587 }
2588 else {
2589 INT fw = es->format_rect.right - es->format_rect.left;
2592 /* remove chars that don't fit */
2593 if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
2594 while ((es->text_width > fw) && s + strl >= s) {
2595 lstrcpyW(es->text + s + strl - 1, es->text + s + strl);
2596 strl--;
2597 es->text_length = -1;
2600 }
2602 if (!notify_parent(es, EN_MAXTEXT)) return;
2603 }
2604 }
2605
2606 if (e != s) {
2607 if (can_undo) {
2608 utl = lstrlenW(es->undo_text);
2609 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2610 /* undo-buffer is extended to the right */
2611 EDIT_MakeUndoFit(es, utl + e - s);
2612 memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR));
2613 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2614 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2615 /* undo-buffer is extended to the left */
2616 EDIT_MakeUndoFit(es, utl + e - s);
2617 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2618 p[e - s] = p[0];
2619 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2620 p[i] = buf[i];
2621 es->undo_position = s;
2622 } else {
2623 /* new undo-buffer */
2624 EDIT_MakeUndoFit(es, e - s);
2625 memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR));
2626 es->undo_text[e - s] = 0; /* ensure 0 termination */
2627 es->undo_position = s;
2628 }
2629 /* any deletion makes the old insertion-undo invalid */
2630 es->undo_insert_count = 0;
2631 } else
2633 }
2634 if (strl) {
2635 if (can_undo) {
2636 if ((s == es->undo_position) ||
2637 ((es->undo_insert_count) &&
2638 (s == es->undo_position + es->undo_insert_count)))
2639 /*
2640 * insertion is new and at delete position or
2641 * an extension to either left or right
2642 */
2643 es->undo_insert_count += strl;
2644 else {
2645 /* new insertion undo */
2646 es->undo_position = s;
2647 es->undo_insert_count = strl;
2648 /* new insertion makes old delete-buffer invalid */
2649 *es->undo_text = '\0';
2650 }
2651 } else
2653 }
2654
2655 heap_free(buf);
2656
2657 s += strl;
2658
2659 /* If text has been deleted and we're right or center aligned then scroll rightward */
2660 if (es->style & (ES_RIGHT | ES_CENTER))
2661 {
2662 INT delta = strl - abs(es->selection_end - es->selection_start);
2663
2664 if (delta < 0 && es->x_offset)
2665 {
2666 if (abs(delta) > es->x_offset)
2667 es->x_offset = 0;
2668 else
2669 es->x_offset += delta;
2670 }
2671 }
2672
2674 es->flags |= EF_MODIFIED;
2675 if (send_update) es->flags |= EF_UPDATE;
2676 if (hrgn)
2677 {
2680 }
2681 else
2683
2685
2686 /* force scroll info update */
2688
2689
2690 if(send_update || (es->flags & EF_UPDATE))
2691 {
2692 es->flags &= ~EF_UPDATE;
2693 if (!notify_parent(es, EN_CHANGE)) return;
2694 }
2696}
2697
2698
2699/*********************************************************************
2700 *
2701 * EM_SETHANDLE
2702 *
2703 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2704 *
2706static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc)
2707{
2708 if (!(es->style & ES_MULTILINE))
2709 return;
2710
2711 if (!hloc)
2712 return;
2713
2715
2716 es->hloc32W = hloc;
2717 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2718
2719 /* The text buffer handle belongs to the control */
2720 es->hlocapp = NULL;
2721
2724
2725 es->x_offset = es->y_offset = 0;
2726 es->selection_start = es->selection_end = 0;
2728 es->flags &= ~EF_MODIFIED;
2729 es->flags &= ~EF_UPDATE;
2733 /* force scroll info update */
2735}
2736
2737
2738/*********************************************************************
2739 *
2740 * EM_SETLIMITTEXT
2741 *
2742 * NOTE: this version currently implements WinNT limits
2743 *
2746{
2747 if (!limit) limit = ~0u;
2748 if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe);
2749 es->buffer_limit = limit;
2750}
2752static BOOL is_cjk(HDC dc)
2753{
2756
2757 switch (GdiGetCodePage(dc)) {
2758 case 932: case 936: case 949: case 950: case 1361:
2759 return TRUE;
2760 default:
2761 return (GetTextCharsetInfo(dc, &fs, 0) != DEFAULT_CHARSET &&
2762 (fs.fsCsb[0] & FS_DBCS_MASK));
2763 }
2764}
2766static int get_cjk_fontinfo_margin(int width, int side_bearing)
2767{
2768 int margin;
2769 if (side_bearing < 0)
2770 margin = min(-side_bearing, width/2);
2771 else
2772 margin = 0;
2773 return margin;
2774}
2778};
2779
2780/* Undocumented gdi32 export */
2782
2783/*********************************************************************
2784 *
2785 * EM_SETMARGINS
2786 *
2787 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2788 * action wParam despite what the docs say. EC_USEFONTINFO calculates the
2789 * margin according to the textmetrics of the current font.
2790 *
2791 * When EC_USEFONTINFO is used, the margins only change if the edit control is
2792 * equal to or larger than a certain size. The empty client rect is treated as
2793 * 80 pixels width.
2796 WORD left, WORD right, BOOL repaint)
2797{
2799 INT default_left_margin = 0; /* in pixels */
2800 INT default_right_margin = 0; /* in pixels */
2801
2802 /* Set the default margins depending on the font */
2803 if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
2804 HDC dc = GetDC(es->hwndSelf);
2805 HFONT old_font = SelectObject(dc, es->font);
2806 LONG width = GdiGetCharDimensions(dc, &tm, NULL), rc_width;
2807 RECT rc;
2808
2809 /* The default margins are only non zero for TrueType or Vector fonts */
2810 if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
2811 struct char_width_info width_info;
2812
2813 if (is_cjk(dc) && GetCharWidthInfo(dc, &width_info))
2814 {
2815 default_left_margin = get_cjk_fontinfo_margin(width, width_info.min_lsb);
2816 default_right_margin = get_cjk_fontinfo_margin(width, width_info.min_rsb);
2817 }
2818 else
2819 {
2820 default_left_margin = width / 2;
2821 default_right_margin = width / 2;
2822 }
2823
2824 GetClientRect(es->hwndSelf, &rc);
2825 rc_width = !IsRectEmpty(&rc) ? rc.right - rc.left : 80;
2826 if (rc_width < default_left_margin + default_right_margin + width * 2) {
2827 default_left_margin = es->left_margin;
2828 default_right_margin = es->right_margin;
2829 }
2830 }
2831 SelectObject(dc, old_font);
2832 ReleaseDC(es->hwndSelf, dc);
2833 }
2834
2835 if (action & EC_LEFTMARGIN) {
2836 es->format_rect.left -= es->left_margin;
2837 if (left != EC_USEFONTINFO)
2838 es->left_margin = left;
2839 else
2840 es->left_margin = default_left_margin;
2841 es->format_rect.left += es->left_margin;
2842 }
2843
2844 if (action & EC_RIGHTMARGIN) {
2845 es->format_rect.right += es->right_margin;
2846 if (right != EC_USEFONTINFO)
2847 es->right_margin = right;
2848 else
2849 es->right_margin = default_right_margin;
2850 es->format_rect.right -= es->right_margin;
2851 }
2852
2855 if (repaint) EDIT_UpdateText(es, NULL, TRUE);
2856 }
2857
2858 TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
2859}
2860
2861
2862/*********************************************************************
2863 *
2864 * EM_SETPASSWORDCHAR
2865 *
2868{
2869 LONG style;
2870
2871 if (es->style & ES_MULTILINE)
2872 return;
2873
2874 if (es->password_char == c)
2875 return;
2876
2877 style = GetWindowLongW( es->hwndSelf, GWL_STYLE );
2878 es->password_char = c;
2879 if (c) {
2880 SetWindowLongW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD );
2881 es->style |= ES_PASSWORD;
2882 } else {
2883 SetWindowLongW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD );
2884 es->style &= ~ES_PASSWORD;
2885 }
2888}
2889
2890
2891/*********************************************************************
2892 *
2893 * EM_SETTABSTOPS
2894 *
2896static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, const INT *tabs)
2897{
2898 if (!(es->style & ES_MULTILINE))
2899 return FALSE;
2900 heap_free(es->tabs);
2901 es->tabs_count = count;
2902 if (!count)
2903 es->tabs = NULL;
2904 else {
2905 es->tabs = heap_alloc(count * sizeof(INT));
2906 memcpy(es->tabs, tabs, count * sizeof(INT));
2907 }
2909 return TRUE;
2910}
2911
2912
2913/*********************************************************************
2914 *
2915 * EM_SETWORDBREAKPROC
2916 *
2919{
2920 if (es->word_break_proc == wbp)
2921 return;
2922
2923 es->word_break_proc = wbp;
2924
2925 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2928 }
2929}
2930
2931
2932/*********************************************************************
2933 *
2934 * EM_UNDO / WM_UNDO
2935 *
2938{
2939 INT ulength;
2940 LPWSTR utext;
2941
2942 /* As per MSDN spec, for a single-line edit control,
2943 the return value is always TRUE */
2944 if( es->style & ES_READONLY )
2945 return !(es->style & ES_MULTILINE);
2946
2947 ulength = lstrlenW(es->undo_text);
2948
2949 utext = heap_alloc((ulength + 1) * sizeof(WCHAR));
2950
2951 lstrcpyW(utext, es->undo_text);
2952
2953 TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
2954 es->undo_insert_count, debugstr_w(utext));
2955
2956 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2958 EDIT_EM_ReplaceSel(es, TRUE, utext, ulength, TRUE, TRUE);
2959 EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2960 /* send the notification after the selection start and end are set */
2961 if (!notify_parent(es, EN_CHANGE)) return TRUE;
2963 heap_free(utext);
2964
2965 TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
2966 es->undo_insert_count, debugstr_w(es->undo_text));
2967 return TRUE;
2968}
2969
2970
2971/* Helper function for WM_CHAR
2972 *
2973 * According to an MSDN blog article titled "Just because you're a control
2974 * doesn't mean that you're necessarily inside a dialog box," multiline edit
2975 * controls without ES_WANTRETURN would attempt to detect whether it is inside
2976 * a dialog box or not.
2978static inline BOOL EDIT_IsInsideDialog(EDITSTATE *es)
2979{
2980 return (es->flags & EF_DIALOGMODE);
2981}
2982
2983
2984/*********************************************************************
2985 *
2986 * WM_PASTE
2987 *
2989static void EDIT_WM_Paste(EDITSTATE *es)
2990{
2991 HGLOBAL hsrc;
2992 LPWSTR src, ptr;
2993 int len;
2994
2995 /* Protect read-only edit control from modification */
2996 if(es->style & ES_READONLY)
2997 return;
2998
2999 OpenClipboard(es->hwndSelf);
3000 if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
3001 src = GlobalLock(hsrc);
3002 len = lstrlenW(src);
3003 /* Protect single-line edit against pasting new line character */
3004 if (!(es->style & ES_MULTILINE) && ((ptr = wcschr(src, '\n')))) {
3005 len = ptr - src;
3006 if (len && src[len - 1] == '\r')
3007 --len;
3008 }
3010 GlobalUnlock(hsrc);
3011 }
3012 else if (es->style & ES_PASSWORD) {
3013 /* clear selected text in password edit box even with empty clipboard */
3015 }
3017}
3018
3019
3020/*********************************************************************
3021 *
3022 * WM_COPY
3023 *
3025static void EDIT_WM_Copy(EDITSTATE *es)
3026{
3027 INT s = min(es->selection_start, es->selection_end);
3028 INT e = max(es->selection_start, es->selection_end);
3029 HGLOBAL hdst;
3030 LPWSTR dst;
3031 DWORD len;
3032
3033 if (e == s) return;
3034
3035 len = e - s;
3036 hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR));
3037 dst = GlobalLock(hdst);
3038 memcpy(dst, es->text + s, len * sizeof(WCHAR));
3039 dst[len] = 0; /* ensure 0 termination */
3040 TRACE("%s\n", debugstr_w(dst));
3041 GlobalUnlock(hdst);
3042 OpenClipboard(es->hwndSelf);
3046}
3047
3048
3049/*********************************************************************
3050 *
3051 * WM_CLEAR
3052 *
3054static inline void EDIT_WM_Clear(EDITSTATE *es)
3055{
3056 /* Protect read-only edit control from modification */
3057 if(es->style & ES_READONLY)
3058 return;
3059
3061}
3062
3063
3064/*********************************************************************
3065 *
3066 * WM_CUT
3067 *
3069static inline void EDIT_WM_Cut(EDITSTATE *es)
3070{
3073}
3074
3075
3076/*********************************************************************
3077 *
3078 * WM_CHAR
3079 *
3082{
3083 BOOL control;
3084
3085#ifdef __REACTOS__
3086 if (es->bCaptureState)
3087 return 0;
3088#endif
3089
3090 control = GetKeyState(VK_CONTROL) & 0x8000;
3091
3092 switch (c) {
3093 case '\r':
3094 /* If it's not a multiline edit box, it would be ignored below.
3095 * For multiline edit without ES_WANTRETURN, we have to make a
3096 * special case.
3097 */
3098 if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3100 break;
3101 case '\n':
3102 if (es->style & ES_MULTILINE) {
3103 if (es->style & ES_READONLY) {
3106 } else {
3107 static const WCHAR cr_lfW[] = {'\r','\n'};
3108 EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, 2, TRUE, TRUE);
3109 }
3110 }
3111 break;
3112 case '\t':
3113 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3114 {
3115 static const WCHAR tabW[] = {'\t'};
3117 break;
3118 EDIT_EM_ReplaceSel(es, TRUE, tabW, 1, TRUE, TRUE);
3119 }
3120 break;
3121 case VK_BACK:
3122 if (!(es->style & ES_READONLY) && !control) {
3123 if (es->selection_start != es->selection_end)
3125 else {
3126 /* delete character left of caret */
3127 EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3130 }
3131 }
3132 break;
3133 case 0x03: /* ^C */
3134 if (!(es->style & ES_PASSWORD))
3135 SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3136 break;
3137 case 0x16: /* ^V */
3138 if (!(es->style & ES_READONLY))
3139 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3140 break;
3141 case 0x18: /* ^X */
3142 if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD)))
3143 SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3144 break;
3145 case 0x1A: /* ^Z */
3146 if (!(es->style & ES_READONLY))
3147 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3148 break;
3149
3150 default:
3151 /*If Edit control style is ES_NUMBER allow users to key in only numeric values*/
3152 if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') )
3153 break;
3154
3155 if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127))
3157 break;
3158 }
3159 return 1;
3160}
3161
3162
3163/*********************************************************************
3164 *
3165 * EDIT_ContextMenuCommand
3166 *
3168static void EDIT_ContextMenuCommand(EDITSTATE *es, UINT id)
3169{
3170 switch (id) {
3171 case EM_UNDO:
3172 SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3173 break;
3174 case WM_CUT:
3175 SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3176 break;
3177 case WM_COPY:
3178 SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3179 break;
3180 case WM_PASTE:
3181 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3182 break;
3183 case WM_CLEAR:
3184 SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0);
3185 break;
3186 case EM_SETSEL:
3187 SendMessageW(es->hwndSelf, EM_SETSEL, 0, -1);
3188 break;
3189 default:
3190 ERR("unknown menu item, please report\n");
3191 break;
3192 }
3193}
3194
3195
3196/*********************************************************************
3197 *
3198 * WM_CONTEXTMENU
3199 *
3200 * Note: the resource files resource/sysres_??.rc cannot define a
3201 * single popup menu. Hence we use a (dummy) menubar
3202 * containing the single popup menu as its first item.
3203 *
3204 * FIXME: the message identifiers have been chosen arbitrarily,
3205 * hence we use MF_BYPOSITION.
3206 * We might as well use the "real" values (anybody knows ?)
3207 * The menu definition is in resources/sysres_??.rc.
3208 * Once these are OK, we better use MF_BYCOMMAND here
3209 * (as we do in EDIT_WM_Command()).
3210 *
3212static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y)
3213{
3214 HMENU menu = LoadMenuA(GetModuleHandleA("user32.dll"), "EDITMENU");
3215 HMENU popup = GetSubMenu(menu, 0);
3216 UINT start = es->selection_start;
3217 UINT end = es->selection_end;
3218 UINT cmd;
3219 POINT pt;
3220
3222
3223 /* undo */
3225 /* cut */
3226 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3227 /* copy */
3228 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3229 /* paste */
3231 /* delete */
3232 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3233 /* select all */
3235
3236 pt.x = x;
3237 pt.y = y;
3238
3239 if (pt.x == -1 && pt.y == -1) /* passed via VK_APPS press/release */
3240 {
3241 RECT rc;
3242
3243 /* Windows places the menu at the edit's center in this case */
3244 GetClientRect(es->hwndSelf, &rc);
3245 pt.x = rc.left + (rc.right - rc.left) / 2;
3246 pt.y = rc.top + (rc.bottom - rc.top) / 2;
3247 ClientToScreen(es->hwndSelf, &pt);
3248 }
3249
3250 if (!(es->flags & EF_FOCUSED))
3251 SetFocus(es->hwndSelf);
3252
3254 pt.x, pt.y, 0, es->hwndSelf, NULL);
3255
3256 if (cmd)
3258
3259 DestroyMenu(menu);
3260}
3261
3262
3263/*********************************************************************
3264 *
3265 * WM_GETTEXT
3266 *
3269{
3270 if (!count)
3271 return 0;
3272
3273 lstrcpynW(dst, es->text, count);
3274 return lstrlenW(dst);
3275}
3276
3277/*********************************************************************
3278 *
3279 * EDIT_CheckCombo
3280 *
3283{
3284 HWND hLBox = es->hwndListBox;
3285 HWND hCombo;
3286 BOOL bDropped;
3287 int nEUI;
3288
3289 if (!hLBox)
3290 return FALSE;
3291
3292 hCombo = GetParent(es->hwndSelf);
3293 bDropped = TRUE;
3294 nEUI = 0;
3295
3296 TRACE("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key);
3297
3298 if (key == VK_UP || key == VK_DOWN)
3299 {
3300 if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
3301 nEUI = 1;
3302
3303 if (msg == WM_KEYDOWN || nEUI)
3304 bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3305 }
3306
3307 switch (msg)
3308 {
3309 case WM_KEYDOWN:
3310 if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3311 {
3312 /* make sure ComboLBox pops up */
3313 SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3314 key = VK_F4;
3315 nEUI = 2;
3316 }
3317
3318 SendMessageW(hLBox, WM_KEYDOWN, key, 0);
3319 break;
3320
3321 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3322 if (nEUI)
3323 SendMessageW(hCombo, CB_SHOWDROPDOWN, !bDropped, 0);
3324 else
3325 SendMessageW(hLBox, WM_KEYDOWN, VK_F4, 0);
3326 break;
3327 }
3328
3329 if (nEUI == 2)
3330 SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3331
3332 return TRUE;
3333}
3334
3335
3336/*********************************************************************
3337 *
3338 * WM_KEYDOWN
3339 *
3340 * Handling of special keys that don't produce a WM_CHAR
3341 * (i.e. non-printable keys) & Backspace & Delete
3342 *
3345{
3346 BOOL shift;
3347 BOOL control;
3348
3349 if (GetKeyState(VK_MENU) & 0x8000)
3350 return 0;
3351
3352 shift = GetKeyState(VK_SHIFT) & 0x8000;
3353 control = GetKeyState(VK_CONTROL) & 0x8000;
3354
3355 switch (key) {
3356 case VK_F4:
3357 case VK_UP:
3359 break;
3360
3361 /* fall through */
3362 case VK_LEFT:
3363 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3365 else
3366 if (control)
3368 else
3370 break;
3371 case VK_DOWN:
3373 break;
3374 /* fall through */
3375 case VK_RIGHT:
3376 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3378 else if (control)
3380 else
3382 break;
3383 case VK_HOME:
3385 break;
3386 case VK_END:
3388 break;
3389 case VK_PRIOR:
3390 if (es->style & ES_MULTILINE)
3392 else
3394 break;
3395 case VK_NEXT:
3396 if (es->style & ES_MULTILINE)
3398 else
3400 break;
3401 case VK_DELETE:
3402 if (!(es->style & ES_READONLY) && !(shift && control)) {
3403 if (es->selection_start != es->selection_end) {
3404 if (shift)
3405 EDIT_WM_Cut(es);
3406 else
3408 } else {
3409 EDIT_EM_SetSel(es, ~0u, 0, FALSE);
3410 if (shift)
3411 /* delete character left of caret */
3413 else if (control)
3414 /* delete to end of line */
3416 else
3417 /* delete character right of caret */
3420 }
3421 }
3422 break;
3423 case VK_INSERT:
3424 if (shift) {
3425 if (!(es->style & ES_READONLY))
3427 } else if (control)
3429 break;
3430 case VK_RETURN:
3431 /* If the edit doesn't want the return send a message to the default object */
3432 if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN))
3433 {
3434 DWORD dw;
3435
3436 if (!EDIT_IsInsideDialog(es)) break;
3437 if (control) break;
3438 dw = SendMessageW(es->hwndParent, DM_GETDEFID, 0, 0);
3439 if (HIWORD(dw) == DC_HASDEFID)
3440 {
3441 HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw));
3442 if (hwDefCtrl)
3443 {
3444 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
3445 PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
3446 }
3447 }
3448 }
3449 break;
3450 case VK_ESCAPE:
3451 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3452 PostMessageW(es->hwndParent, WM_CLOSE, 0, 0);
3453 break;
3454 case VK_TAB:
3455 if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3456 SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0);
3457 break;
3458 case 'A':
3459 if (control)
3460 {
3462 {
3463 if (!notify_parent(es, EN_UPDATE)) break;
3465 }
3466 }
3467 break;
3468 }
3469 return TRUE;
3470}
3471
3472
3473/*********************************************************************
3474 *
3475 * WM_KILLFOCUS
3476 *
3479{
3481 HWND hwndSelf = es->hwndSelf;
3482
3483 es->flags &= ~EF_FOCUSED;
3484 DestroyCaret();
3485 if (!(es->style & ES_NOHIDESEL))
3486 EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3487 if (!notify_parent(es, EN_KILLFOCUS)) return 0;
3488 /* Throw away left over scroll when we lose focus */
3489 es->wheelDeltaRemainder = 0;
3490
3491 if (theme)
3492 flags |= RDW_FRAME;
3493
3494 RedrawWindow(hwndSelf, NULL, NULL, flags);
3495 return 0;
3496}
3497
3498
3499/*********************************************************************
3500 *
3501 * WM_LBUTTONDBLCLK
3502 *
3503 * The caret position has been set on the WM_LBUTTONDOWN message
3504 *
3507{
3508 INT s;
3509 INT e = es->selection_end;
3510 INT l;
3511 INT li;
3512 INT ll;
3513
3514 es->bCaptureState = TRUE;
3515 SetCapture(es->hwndSelf);
3516
3524 es->region_posx = es->region_posy = 0;
3525 SetTimer(es->hwndSelf, 0, 100, NULL);
3526 return 0;
3527}
3528
3529
3530/*********************************************************************
3531 *
3532 * WM_LBUTTONDOWN
3533 *
3536{
3537 INT e;
3538 BOOL after_wrap;
3539
3540 es->bCaptureState = TRUE;
3541 SetCapture(es->hwndSelf);
3542 EDIT_ConfinePoint(es, &x, &y);
3543 e = EDIT_CharFromPos(es, x, y, &after_wrap);
3544 EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3546 es->region_posx = es->region_posy = 0;
3547 SetTimer(es->hwndSelf, 0, 100, NULL);
3548
3549 if (!(es->flags & EF_FOCUSED))
3550 SetFocus(es->hwndSelf);
3551
3552 return 0;
3553}
3554
3555
3556/*********************************************************************
3557 *
3558 * WM_LBUTTONUP
3559 *
3562{
3563 if (es->bCaptureState) {
3564 KillTimer(es->hwndSelf, 0);
3565 if (GetCapture() == es->hwndSelf) ReleaseCapture();
3566 }
3567 es->bCaptureState = FALSE;
3568 return 0;
3569}
3570
3571
3572/*********************************************************************
3573 *
3574 * WM_MBUTTONDOWN
3575 *
3578{
3579 SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3580 return 0;
3581}
3582
3583
3584/*********************************************************************
3585 *
3586 * WM_MOUSEMOVE
3587 *
3590{
3591 INT e;
3592 BOOL after_wrap;
3593 INT prex, prey;
3594
3595 /* If the mouse has been captured by process other than the edit control itself,
3596 * the windows edit controls will not select the strings with mouse move.
3597 */
3598 if (!es->bCaptureState || GetCapture() != es->hwndSelf)
3599 return 0;
3600
3601 /*
3602 * FIXME: gotta do some scrolling if outside client
3603 * area. Maybe reset the timer ?
3604 */
3605 prex = x; prey = y;
3606 EDIT_ConfinePoint(es, &x, &y);
3607 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3608 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3609 e = EDIT_CharFromPos(es, x, y, &after_wrap);
3610 EDIT_EM_SetSel(es, es->selection_start, e, after_wrap);
3611 EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP);
3612 return 0;
3613}
3614
3615
3616/*********************************************************************
3617 *
3618 * WM_PAINT
3619 *
3621static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc)
3622{
3623 PAINTSTRUCT ps;
3624 INT i;
3625 HDC dc;
3626 HFONT old_font = 0;
3627 RECT rc;
3628 RECT rcClient;
3629 RECT rcLine;
3630 RECT rcRgn;
3631 HBRUSH brush;
3632 HBRUSH old_brush;
3633 INT bw, bh;
3634 BOOL rev = es->bEnableState &&
3635 ((es->flags & EF_FOCUSED) ||
3636 (es->style & ES_NOHIDESEL));
3637 dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps);
3638
3639 /* The dc we use for calculating may not be the one we paint into.
3640 This is the safest action. */
3642 GetClientRect(es->hwndSelf, &rcClient);
3643
3644 /* get the background brush */
3645 brush = EDIT_NotifyCtlColor(es, dc);
3646
3647 /* paint the border and the background */
3648 IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3649
3650 if(es->style & WS_BORDER) {
3653 rc = rcClient;
3654 if(es->style & ES_MULTILINE) {
3655 if(es->style & WS_HSCROLL) rc.bottom+=bh;
3656 if(es->style & WS_VSCROLL) rc.right+=bw;
3657 }
3658
3659 /* Draw the frame. Same code as in nonclient.c */
3661 PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY);
3662 PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY);
3663 PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY);
3664 PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY);
3665 SelectObject(dc, old_brush);
3666
3667 /* Keep the border clean */
3668 IntersectClipRect(dc, rc.left+bw, rc.top+bh,
3669 max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh));
3670 }
3671
3672 GetClipBox(dc, &rc);
3673 FillRect(dc, &rc, brush);
3674
3675 IntersectClipRect(dc, es->format_rect.left,
3676 es->format_rect.top,
3677 es->format_rect.right,
3678 es->format_rect.bottom);
3679 if (es->style & ES_MULTILINE) {
3680 rc = rcClient;
3681 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3682 }
3683 if (es->font)
3684 old_font = SelectObject(dc, es->font);
3685
3686 if (!es->bEnableState)
3688 GetClipBox(dc, &rcRgn);
3689 if (es->style & ES_MULTILINE) {
3691 for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3693 EDIT_GetLineRect(es, i, 0, -1, &rcLine);
3694 if (IntersectRect(&rc, &rcRgn, &rcLine))
3695 EDIT_PaintLine(es, dc, i, rev);
3696 }
3697 } else {
3699 EDIT_GetLineRect(es, 0, 0, -1, &rcLine);
3700 if (IntersectRect(&rc, &rcRgn, &rcLine))
3701 EDIT_PaintLine(es, dc, 0, rev);
3702 }
3703 if (es->font)
3704 SelectObject(dc, old_font);
3705
3706 if (!hdc)
3707 EndPaint(es->hwndSelf, &ps);
3708}
3710static void EDIT_WM_NCPaint(HWND hwnd, HRGN region)
3711{
3713 HTHEME theme = GetWindowTheme(hwnd);
3714 HRGN cliprgn = region;
3715
3716 if (theme && exStyle & WS_EX_CLIENTEDGE)
3717 {
3718 HDC dc;
3719 RECT r;
3720 int cxEdge = GetSystemMetrics(SM_CXEDGE),
3721 cyEdge = GetSystemMetrics(SM_CYEDGE);
3722 const int part = EP_EDITTEXT;
3723 int state = ETS_NORMAL;
3724 DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
3725
3726 if (!IsWindowEnabled(hwnd))
3728 else if (dwStyle & ES_READONLY)
3730 else if (GetFocus() == hwnd)
3732
3733 GetWindowRect(hwnd, &r);
3734
3735 /* New clipping region passed to default proc to exclude border */
3736 cliprgn = CreateRectRgn(r.left + cxEdge, r.top + cyEdge,
3737 r.right - cxEdge, r.bottom - cyEdge);
3738 if (region != (HRGN)1)
3739 CombineRgn(cliprgn, cliprgn, region, RGN_AND);
3740 OffsetRect(&r, -r.left, -r.top);
3741
3742#ifdef __REACTOS__ /* r73789 */
3743 dc = GetWindowDC(hwnd);
3744 /* Exclude client part */
3746 r.left + cxEdge,
3747 r.top + cyEdge,
3748 r.right - cxEdge,
3749 r.bottom -cyEdge);
3750#else
3752 OffsetRect(&r, -r.left, -r.top);
3753#endif
3754
3757 DrawThemeBackground(theme, dc, part, state, &r, 0);
3758 ReleaseDC(hwnd, dc);
3759 }
3760
3761 /* Call default proc to get the scrollbars etc. also painted */
3762 DefWindowProcW (hwnd, WM_NCPAINT, (WPARAM)cliprgn, 0);
3763 if (cliprgn != region)
3764 DeleteObject(cliprgn);
3765}
3766
3767/*********************************************************************
3768 *
3769 * WM_SETFOCUS
3770 *
3772static void EDIT_WM_SetFocus(HTHEME theme, EDITSTATE *es)
3773{
3775
3776 es->flags |= EF_FOCUSED;
3777
3778 if (!(es->style & ES_NOHIDESEL))
3779 EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3780
3781#ifdef __REACTOS__
3782 SystemParametersInfo(SPI_GETCARETWIDTH, 0, &es->dwCaretWidth, 0);
3783 CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height);
3784#else
3785 CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3786#endif
3787 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
3788 ShowCaret(es->hwndSelf);
3789 if (!notify_parent(es, EN_SETFOCUS)) return;
3790
3791 if (theme)
3793
3794 RedrawWindow(es->hwndSelf, NULL, NULL, flags);
3795}
3796
3799{
3800 ABC abc[256];
3801 SHORT left, right;
3802 UINT i;
3803
3804 if (!(tm->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE)))
3806
3807 if (!is_cjk(hdc))
3809
3810 if (!GetCharABCWidthsW(hdc, 0, 255, abc))
3811 return 0;
3812
3813 left = right = 0;
3814 for (i = 0; i < ARRAY_SIZE(abc); i++) {
3815 if (-abc[i].abcA > right) right = -abc[i].abcA;
3816 if (-abc[i].abcC > left ) left = -abc[i].abcC;
3817 }
3818 return MAKELONG(left, right);
3819}
3820
3821/*********************************************************************
3822 *
3823 * WM_SETFONT
3824 *
3825 * With Win95 look the margins are set to default font value unless
3826 * the system font (font == 0) is being set, in which case they are left
3827 * unchanged.
3828 *
3831{
3833 HDC dc;
3834 HFONT old_font = 0;
3835 RECT clientRect;
3836 DWORD margins;
3837
3838 es->font = font;
3840 dc = GetDC(es->hwndSelf);
3841 if (font)
3842 old_font = SelectObject(dc, font);
3844 es->line_height = tm.tmHeight;
3845 es->char_width = tm.tmAveCharWidth;
3847 if (font)
3848 SelectObject(dc, old_font);
3849 ReleaseDC(es->hwndSelf, dc);
3850
3851 /* Reset the format rect and the margins */
3852 GetClientRect(es->hwndSelf, &