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