[user32]
[reactos.git] / reactos / dll / win32 / user32 / controls / edit.c
1 /*
2 * Edit control
3 *
4 * Copyright David W. Metcalfe, 1994
5 * Copyright William Magro, 1995, 1996
6 * Copyright Frans van Dorsselaer, 1996, 1997
7 *
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * NOTES
24 *
25 * This code was audited for completeness against the documented features
26 * of Comctl32.dll version 6.0 on Oct. 8, 2004, by Dimitrie O. Paun.
27 *
28 * Unless otherwise noted, we believe this code to be complete, as per
29 * the specification mentioned above.
30 * If you discover missing features, or bugs, please note them below.
31 *
32 * TODO:
33 * - EDITBALLOONTIP structure
34 * - EM_GETCUEBANNER/Edit_GetCueBannerText
35 * - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
36 * - EM_SETCUEBANNER/Edit_SetCueBannerText
37 * - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
38 * - EM_GETIMESTATUS, EM_SETIMESTATUS
39 * - EN_ALIGN_LTR_EC
40 * - EN_ALIGN_RTL_EC
41 * - ES_OEMCONVERT
42 *
43 */
44
45 #include <user32.h>
46
47 #include <wine/debug.h>
48
49 WINE_DEFAULT_DEBUG_CHANNEL(edit);
50 WINE_DECLARE_DEBUG_CHANNEL(combo);
51 WINE_DECLARE_DEBUG_CHANNEL(relay);
52
53 #define BUFLIMIT_INITIAL 30000 /* initial buffer size */
54 #define GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
55 #define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
56 #define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
57
58 /*
59 * extra flags for EDITSTATE.flags field
60 */
61 #define EF_MODIFIED 0x0001 /* text has been modified */
62 #define EF_FOCUSED 0x0002 /* we have input focus */
63 #define EF_UPDATE 0x0004 /* notify parent of changed state */
64 #define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
65 #define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
66 #define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
67 wrapped line, instead of in front of the next character */
68 #define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
69 #define EF_APP_HAS_HANDLE 0x0200 /* Set when an app sends EM_[G|S]ETHANDLE. We are in sole control of
70 the text buffer if this is clear. */
71 #define EF_DIALOGMODE 0x0400 /* Indicates that we are inside a dialog window */
72
73 typedef enum
74 {
75 END_0 = 0, /* line ends with terminating '\0' character */
76 END_WRAP, /* line is wrapped */
77 END_HARD, /* line ends with a hard return '\r\n' */
78 END_SOFT, /* line ends with a soft return '\r\r\n' */
79 END_RICH /* line ends with a single '\n' */
80 } LINE_END;
81
82 typedef struct tagLINEDEF {
83 INT length; /* bruto length of a line in bytes */
84 INT net_length; /* netto length of a line in visible characters */
85 LINE_END ending;
86 INT width; /* width of the line in pixels */
87 INT index; /* line index into the buffer */
88 struct tagLINEDEF *next;
89 } LINEDEF;
90
91 typedef struct
92 {
93 BOOL is_unicode; /* how the control was created */
94 LPWSTR text; /* the actual contents of the control */
95 UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
96 UINT buffer_size; /* the size of the buffer in characters */
97 UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
98 HFONT font; /* NULL means standard system font */
99 INT x_offset; /* scroll offset for multi lines this is in pixels
100 for single lines it's in characters */
101 INT line_height; /* height of a screen line in pixels */
102 INT char_width; /* average character width in pixels */
103 DWORD style; /* sane version of wnd->dwStyle */
104 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
105 INT undo_insert_count; /* number of characters inserted in sequence */
106 UINT undo_position; /* character index of the insertion and deletion */
107 LPWSTR undo_text; /* deleted text */
108 UINT undo_buffer_size; /* size of the deleted text buffer */
109 INT selection_start; /* == selection_end if no selection */
110 INT selection_end; /* == current caret position */
111 WCHAR password_char; /* == 0 if no password char, and for multi line controls */
112 INT left_margin; /* in pixels */
113 INT right_margin; /* in pixels */
114 RECT format_rect;
115 INT text_width; /* width of the widest line in pixels for multi line controls
116 and just line width for single line controls */
117 INT region_posx; /* Position of cursor relative to region: */
118 INT region_posy; /* -1: to left, 0: within, 1: to right */
119 #ifndef __REACTOS__
120 EDITWORDBREAKPROC16 word_break_proc16;
121 #endif
122 void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
123 INT line_count; /* number of lines */
124 INT y_offset; /* scroll offset in number of lines */
125 BOOL bCaptureState; /* flag indicating whether mouse was captured */
126 BOOL bEnableState; /* flag keeping the enable state */
127 HWND hwndSelf; /* the our window handle */
128 HWND hwndParent; /* Handle of parent for sending EN_* messages.
129 Even if parent will change, EN_* messages
130 should be sent to the first parent. */
131 HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
132 /*
133 * only for multi line controls
134 */
135 INT lock_count; /* amount of re-entries in the EditWndProc */
136 INT tabs_count;
137 LPINT tabs;
138 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
139 HLOCAL hloc32W; /* our unicode local memory block */
140 #ifndef __REACTOS__
141 HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
142 or EM_SETHANDLE16 */
143 #endif
144 HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
145 or EM_SETHANDLE */
146 /*
147 * IME Data
148 */
149 UINT composition_len; /* length of composition, 0 == no composition */
150 int composition_start; /* the character position for the composition */
151 } EDITSTATE;
152
153
154 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
155 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
156
157 /* used for disabled or read-only edit control */
158 #define EDIT_NOTIFY_PARENT(es, wNotifyCode) \
159 do \
160 { /* Notify parent which has created this edit control */ \
161 TRACE("notification " #wNotifyCode " sent to hwnd=%p\n", es->hwndParent); \
162 SendMessageW(es->hwndParent, WM_COMMAND, \
163 MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \
164 (LPARAM)(es->hwndSelf)); \
165 } while(0)
166
167
168 /*********************************************************************
169 *
170 * EM_CANUNDO
171 *
172 */
173 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
174 {
175 return (es->undo_insert_count || strlenW(es->undo_text));
176 }
177
178
179 /*********************************************************************
180 *
181 * EM_EMPTYUNDOBUFFER
182 *
183 */
184 static inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
185 {
186 es->undo_insert_count = 0;
187 *es->undo_text = '\0';
188 }
189
190
191 /**********************************************************************
192 * get_app_version
193 *
194 * Returns the window version in case Wine emulates a later version
195 * of windows than the application expects.
196 *
197 * In a number of cases when windows runs an application that was
198 * designed for an earlier windows version, windows reverts
199 * to "old" behaviour of that earlier version.
200 *
201 * An example is a disabled edit control that needs to be painted.
202 * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
203 * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
204 * applications with an expected version 0f 4.0 or higher.
205 *
206 */
207 static DWORD get_app_version(void)
208 {
209 static DWORD version;
210 if (!version)
211 {
212 DWORD dwEmulatedVersion;
213 OSVERSIONINFOW info;
214 DWORD dwProcVersion = GetProcessVersion(0);
215
216 info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
217 GetVersionExW( &info );
218 dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
219 /* FIXME: this may not be 100% correct; see discussion on the
220 * wine developer list in Nov 1999 */
221 version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
222 }
223 return version;
224 }
225
226 static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
227 {
228 HBRUSH hbrush;
229 UINT msg;
230
231 if ( get_app_version() >= 0x40000 && (!es->bEnableState || (es->style & ES_READONLY)))
232 msg = WM_CTLCOLORSTATIC;
233 else
234 msg = WM_CTLCOLOREDIT;
235
236 /* why do we notify to es->hwndParent, and we send this one to GetParent()? */
237 hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
238 if (!hbrush)
239 hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
240 return hbrush;
241 }
242
243
244 /*********************************************************************
245 *
246 * EDIT_WordBreakProc
247 *
248 * Find the beginning of words.
249 * Note: unlike the specs for a WordBreakProc, this function only
250 * allows to be called without linebreaks between s[0] up to
251 * s[count - 1]. Remember it is only called
252 * internally, so we can decide this for ourselves.
253 *
254 */
255 static INT EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action)
256 {
257 INT ret = 0;
258
259 TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
260
261 if(!s) return 0;
262
263 switch (action) {
264 case WB_LEFT:
265 if (!count)
266 break;
267 if (index)
268 index--;
269 if (s[index] == ' ') {
270 while (index && (s[index] == ' '))
271 index--;
272 if (index) {
273 while (index && (s[index] != ' '))
274 index--;
275 if (s[index] == ' ')
276 index++;
277 }
278 } else {
279 while (index && (s[index] != ' '))
280 index--;
281 if (s[index] == ' ')
282 index++;
283 }
284 ret = index;
285 break;
286 case WB_RIGHT:
287 if (!count)
288 break;
289 if (index)
290 index--;
291 if (s[index] == ' ')
292 while ((index < count) && (s[index] == ' ')) index++;
293 else {
294 while (s[index] && (s[index] != ' ') && (index < count))
295 index++;
296 while ((s[index] == ' ') && (index < count)) index++;
297 }
298 ret = index;
299 break;
300 case WB_ISDELIMITER:
301 ret = (s[index] == ' ');
302 break;
303 default:
304 ERR("unknown action code, please report !\n");
305 break;
306 }
307 return ret;
308 }
309
310
311 /*********************************************************************
312 *
313 * EDIT_CallWordBreakProc
314 *
315 * Call appropriate WordBreakProc (internal or external).
316 *
317 * Note: The "start" argument should always be an index referring
318 * to es->text. The actual wordbreak proc might be
319 * 16 bit, so we can't always pass any 32 bit LPSTR.
320 * Hence we assume that es->text is the buffer that holds
321 * the string under examination (we can decide this for ourselves).
322 *
323 */
324 static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action)
325 {
326 INT ret;
327
328 #ifndef __REACTOS__
329 if (es->word_break_proc16) {
330 HGLOBAL16 hglob16;
331 SEGPTR segptr;
332 INT countA;
333 WORD args[5];
334 DWORD result;
335
336 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
337 hglob16 = GlobalAlloc16(GMEM_MOVEABLE | GMEM_ZEROINIT, countA);
338 segptr = WOWGlobalLock16(hglob16);
339 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, MapSL(segptr), countA, NULL, NULL);
340 args[4] = SELECTOROF(segptr);
341 args[3] = OFFSETOF(segptr);
342 args[2] = index;
343 args[1] = countA;
344 args[0] = action;
345 WOWCallback16Ex((DWORD)es->word_break_proc16, WCB16_PASCAL, sizeof(args), args, &result);
346 ret = LOWORD(result);
347 GlobalUnlock16(hglob16);
348 GlobalFree16(hglob16);
349 }
350 else if (es->word_break_proc)
351 #else
352 if (es->word_break_proc)
353 #endif
354 {
355 if(es->is_unicode)
356 {
357 EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
358
359 TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
360 es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
361 ret = wbpW(es->text + start, index, count, action);
362 }
363 else
364 {
365 EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
366 INT countA;
367 CHAR *textA;
368
369 countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
370 textA = HeapAlloc(GetProcessHeap(), 0, countA);
371 if (textA == NULL)
372 return 0;
373 WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
374 TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
375 es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
376 ret = wbpA(textA, index, countA, action);
377 HeapFree(GetProcessHeap(), 0, textA);
378 }
379 }
380 else
381 ret = EDIT_WordBreakProc(es->text + start, index, count, action);
382
383 return ret;
384 }
385
386 /*********************************************************************
387 *
388 * EDIT_BuildLineDefs_ML
389 *
390 * Build linked list of text lines.
391 * Lines can end with '\0' (last line), a character (if it is wrapped),
392 * a soft return '\r\r\n' or a hard return '\r\n'
393 *
394 */
395 static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
396 {
397 HDC dc;
398 HFONT old_font = 0;
399 LPWSTR current_position, cp;
400 INT fw;
401 LINEDEF *current_line;
402 LINEDEF *previous_line;
403 LINEDEF *start_line;
404 INT line_index = 0, nstart_line = 0, nstart_index = 0;
405 INT line_count = es->line_count;
406 INT orig_net_length;
407 RECT rc;
408
409 if (istart == iend && delta == 0)
410 return;
411
412 dc = GetDC(es->hwndSelf);
413 if (es->font)
414 old_font = SelectObject(dc, es->font);
415
416 previous_line = NULL;
417 current_line = es->first_line_def;
418
419 /* Find starting line. istart must lie inside an existing line or
420 * at the end of buffer */
421 do {
422 if (istart < current_line->index + current_line->length ||
423 current_line->ending == END_0)
424 break;
425
426 previous_line = current_line;
427 current_line = current_line->next;
428 line_index++;
429 } while (current_line);
430
431 if (!current_line) /* Error occurred start is not inside previous buffer */
432 {
433 FIXME(" modification occurred outside buffer\n");
434 ReleaseDC(es->hwndSelf, dc);
435 return;
436 }
437
438 /* Remember start of modifications in order to calculate update region */
439 nstart_line = line_index;
440 nstart_index = current_line->index;
441
442 /* We must start to reformat from the previous line since the modifications
443 * may have caused the line to wrap upwards. */
444 if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
445 {
446 line_index--;
447 current_line = previous_line;
448 }
449 start_line = current_line;
450
451 fw = es->format_rect.right - es->format_rect.left;
452 current_position = es->text + current_line->index;
453 do {
454 if (current_line != start_line)
455 {
456 if (!current_line || current_line->index + delta > current_position - es->text)
457 {
458 /* The buffer has been expanded, create a new line and
459 insert it into the link list */
460 LINEDEF *new_line = HeapAlloc(GetProcessHeap(), 0, sizeof(LINEDEF));
461 if (new_line == NULL)
462 break;
463 new_line->next = previous_line->next;
464 previous_line->next = new_line;
465 current_line = new_line;
466 es->line_count++;
467 }
468 else if (current_line->index + delta < current_position - es->text)
469 {
470 /* The previous line merged with this line so we delete this extra entry */
471 previous_line->next = current_line->next;
472 HeapFree(GetProcessHeap(), 0, current_line);
473 current_line = previous_line->next;
474 es->line_count--;
475 continue;
476 }
477 else /* current_line->index + delta == current_position */
478 {
479 if (current_position - es->text > iend)
480 break; /* We reached end of line modifications */
481 /* else recalculate this line */
482 }
483 }
484
485 current_line->index = current_position - es->text;
486 orig_net_length = current_line->net_length;
487
488 /* Find end of line */
489 cp = current_position;
490 while (*cp) {
491 if (*cp == '\n') break;
492 if ((*cp == '\r') && (*(cp + 1) == '\n'))
493 break;
494 cp++;
495 }
496
497 /* Mark type of line termination */
498 if (!(*cp)) {
499 current_line->ending = END_0;
500 current_line->net_length = strlenW(current_position);
501 } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
502 current_line->ending = END_SOFT;
503 current_line->net_length = cp - current_position - 1;
504 } else if (*cp == '\n') {
505 current_line->ending = END_RICH;
506 current_line->net_length = cp - current_position;
507 } else {
508 current_line->ending = END_HARD;
509 current_line->net_length = cp - current_position;
510 }
511
512 /* Calculate line width */
513 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
514 current_position, current_line->net_length,
515 es->tabs_count, es->tabs));
516
517 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
518 if (!(es->style & ES_AUTOHSCROLL)) {
519 if (current_line->width > fw) {
520 INT next = 0;
521 INT prev;
522 do {
523 prev = next;
524 next = EDIT_CallWordBreakProc(es, current_position - es->text,
525 prev + 1, current_line->net_length, WB_RIGHT);
526 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
527 current_position, next, es->tabs_count, es->tabs));
528 } while (current_line->width <= fw);
529 if (!prev) { /* Didn't find a line break so force a break */
530 next = 0;
531 do {
532 prev = next;
533 next++;
534 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc,
535 current_position, next, es->tabs_count, es->tabs));
536 } while (current_line->width <= fw);
537 if (!prev)
538 prev = 1;
539 }
540
541 /* If the first line we are calculating, wrapped before istart, we must
542 * adjust istart in order for this to be reflected in the update region. */
543 if (current_line->index == nstart_index && istart > current_line->index + prev)
544 istart = current_line->index + prev;
545 /* else if we are updating the previous line before the first line we
546 * are re-calculating and it expanded */
547 else if (current_line == start_line &&
548 current_line->index != nstart_index && orig_net_length < prev)
549 {
550 /* Line expanded due to an upwards line wrap so we must partially include
551 * previous line in update region */
552 nstart_line = line_index;
553 nstart_index = current_line->index;
554 istart = current_line->index + orig_net_length;
555 }
556
557 current_line->net_length = prev;
558 current_line->ending = END_WRAP;
559 current_line->width = (INT)LOWORD(GetTabbedTextExtentW(dc, current_position,
560 current_line->net_length, es->tabs_count, es->tabs));
561 }
562 else if (current_line == start_line &&
563 current_line->index != nstart_index &&
564 orig_net_length < current_line->net_length) {
565 /* The previous line expanded but it's still not as wide as the client rect */
566 /* The expansion is due to an upwards line wrap so we must partially include
567 it in the update region */
568 nstart_line = line_index;
569 nstart_index = current_line->index;
570 istart = current_line->index + orig_net_length;
571 }
572 }
573
574
575 /* Adjust length to include line termination */
576 switch (current_line->ending) {
577 case END_SOFT:
578 current_line->length = current_line->net_length + 3;
579 break;
580 case END_RICH:
581 current_line->length = current_line->net_length + 1;
582 break;
583 case END_HARD:
584 current_line->length = current_line->net_length + 2;
585 break;
586 case END_WRAP:
587 case END_0:
588 current_line->length = current_line->net_length;
589 break;
590 }
591 es->text_width = max(es->text_width, current_line->width);
592 current_position += current_line->length;
593 previous_line = current_line;
594 current_line = current_line->next;
595 line_index++;
596 } while (previous_line->ending != END_0);
597
598 /* Finish adjusting line indexes by delta or remove hanging lines */
599 if (previous_line->ending == END_0)
600 {
601 LINEDEF *pnext = NULL;
602
603 previous_line->next = NULL;
604 while (current_line)
605 {
606 pnext = current_line->next;
607 HeapFree(GetProcessHeap(), 0, current_line);
608 current_line = pnext;
609 es->line_count--;
610 }
611 }
612 else if (delta != 0)
613 {
614 while (current_line)
615 {
616 current_line->index += delta;
617 current_line = current_line->next;
618 }
619 }
620
621 /* Calculate rest of modification rectangle */
622 if (hrgn)
623 {
624 HRGN tmphrgn;
625 /*
626 * We calculate two rectangles. One for the first line which may have
627 * an indent with respect to the format rect. The other is a format-width
628 * rectangle that spans the rest of the lines that changed or moved.
629 */
630 rc.top = es->format_rect.top + nstart_line * es->line_height -
631 (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
632 rc.bottom = rc.top + es->line_height;
633 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT))
634 rc.left = es->format_rect.left;
635 else
636 rc.left = es->format_rect.left + (INT)LOWORD(GetTabbedTextExtentW(dc,
637 es->text + nstart_index, istart - nstart_index,
638 es->tabs_count, es->tabs)) - es->x_offset; /* Adjust for horz scroll */
639 rc.right = es->format_rect.right;
640 SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
641
642 rc.top = rc.bottom;
643 rc.left = es->format_rect.left;
644 rc.right = es->format_rect.right;
645 /*
646 * If lines were added or removed we must re-paint the remainder of the
647 * lines since the remaining lines were either shifted up or down.
648 */
649 if (line_count < es->line_count) /* We added lines */
650 rc.bottom = es->line_count * es->line_height;
651 else if (line_count > es->line_count) /* We removed lines */
652 rc.bottom = line_count * es->line_height;
653 else
654 rc.bottom = line_index * es->line_height;
655 rc.bottom += es->format_rect.top;
656 rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
657 tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
658 CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
659 DeleteObject(tmphrgn);
660 }
661
662 if (es->font)
663 SelectObject(dc, old_font);
664
665 ReleaseDC(es->hwndSelf, dc);
666 }
667
668
669 static inline UINT get_text_length(EDITSTATE *es)
670 {
671 if(es->text_length == (UINT)-1)
672 es->text_length = strlenW(es->text);
673 return es->text_length;
674 }
675
676 /*********************************************************************
677 *
678 * EDIT_GetPasswordPointer_SL
679 *
680 * note: caller should free the (optionally) allocated buffer
681 *
682 */
683 static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es)
684 {
685 if (es->style & ES_PASSWORD) {
686 INT len = get_text_length(es);
687 LPWSTR text = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
688 if (text == NULL)
689 return NULL;
690 text[len] = '\0';
691 while(len) text[--len] = es->password_char;
692 return text;
693 } else
694 return es->text;
695 }
696
697
698 /*********************************************************************
699 *
700 * EDIT_CalcLineWidth_SL
701 *
702 */
703 static void EDIT_CalcLineWidth_SL(EDITSTATE *es)
704 {
705 SIZE size;
706 LPWSTR text;
707 HDC dc;
708 HFONT old_font = 0;
709
710 text = EDIT_GetPasswordPointer_SL(es);
711
712 dc = GetDC(es->hwndSelf);
713 if (es->font)
714 old_font = SelectObject(dc, es->font);
715
716 GetTextExtentPoint32W(dc, text, strlenW(text), &size);
717
718 if (es->font)
719 SelectObject(dc, old_font);
720 ReleaseDC(es->hwndSelf, dc);
721
722 if (es->style & ES_PASSWORD)
723 HeapFree(GetProcessHeap(), 0, text);
724
725 es->text_width = size.cx;
726 }
727
728 /*********************************************************************
729 *
730 * EDIT_CharFromPos
731 *
732 * Beware: This is not the function called on EM_CHARFROMPOS
733 * The position _can_ be outside the formatting / client
734 * rectangle
735 * The return value is only the character index
736 *
737 */
738 static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
739 {
740 INT index;
741 HDC dc;
742 HFONT old_font = 0;
743 INT x_high = 0, x_low = 0;
744
745 if (es->style & ES_MULTILINE) {
746 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
747 INT line_index = 0;
748 LINEDEF *line_def = es->first_line_def;
749 INT low, high;
750 while ((line > 0) && line_def->next) {
751 line_index += line_def->length;
752 line_def = line_def->next;
753 line--;
754 }
755 x += es->x_offset - es->format_rect.left;
756 if (es->style & ES_RIGHT)
757 x -= (es->format_rect.right - es->format_rect.left) - line_def->width;
758 else if (es->style & ES_CENTER)
759 x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2;
760 if (x >= line_def->width) {
761 if (after_wrap)
762 *after_wrap = (line_def->ending == END_WRAP);
763 return line_index + line_def->net_length;
764 }
765 if (x <= 0) {
766 if (after_wrap)
767 *after_wrap = FALSE;
768 return line_index;
769 }
770 dc = GetDC(es->hwndSelf);
771 if (es->font)
772 old_font = SelectObject(dc, es->font);
773 low = line_index;
774 high = line_index + line_def->net_length + 1;
775 while (low < high - 1)
776 {
777 INT mid = (low + high) / 2;
778 INT x_now = LOWORD(GetTabbedTextExtentW(dc, es->text + line_index, mid - line_index, es->tabs_count, es->tabs));
779 if (x_now > x) {
780 high = mid;
781 x_high = x_now;
782 } else {
783 low = mid;
784 x_low = x_now;
785 }
786 }
787 if (abs(x_high - x) + 1 <= abs(x_low - x))
788 index = high;
789 else
790 index = low;
791
792 if (after_wrap)
793 *after_wrap = ((index == line_index + line_def->net_length) &&
794 (line_def->ending == END_WRAP));
795 } else {
796 LPWSTR text;
797 SIZE size;
798 if (after_wrap)
799 *after_wrap = FALSE;
800 x -= es->format_rect.left;
801 if (!x)
802 return es->x_offset;
803
804 if (!es->x_offset)
805 {
806 INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width;
807 if (es->style & ES_RIGHT)
808 x -= indent;
809 else if (es->style & ES_CENTER)
810 x -= indent / 2;
811 }
812
813 text = EDIT_GetPasswordPointer_SL(es);
814 dc = GetDC(es->hwndSelf);
815 if (es->font)
816 old_font = SelectObject(dc, es->font);
817 if (x < 0)
818 {
819 INT low = 0;
820 INT high = es->x_offset;
821 while (low < high - 1)
822 {
823 INT mid = (low + high) / 2;
824 GetTextExtentPoint32W( dc, text + mid,
825 es->x_offset - mid, &size );
826 if (size.cx > -x) {
827 low = mid;
828 x_low = size.cx;
829 } else {
830 high = mid;
831 x_high = size.cx;
832 }
833 }
834 if (abs(x_high + x) <= abs(x_low + x) + 1)
835 index = high;
836 else
837 index = low;
838 }
839 else
840 {
841 INT low = es->x_offset;
842 INT high = get_text_length(es) + 1;
843 while (low < high - 1)
844 {
845 INT mid = (low + high) / 2;
846 GetTextExtentPoint32W( dc, text + es->x_offset,
847 mid - es->x_offset, &size );
848 if (size.cx > x) {
849 high = mid;
850 x_high = size.cx;
851 } else {
852 low = mid;
853 x_low = size.cx;
854 }
855 }
856 if (abs(x_high - x) <= abs(x_low - x) + 1)
857 index = high;
858 else
859 index = low;
860 }
861 if (es->style & ES_PASSWORD)
862 HeapFree(GetProcessHeap(), 0, text);
863 }
864 if (es->font)
865 SelectObject(dc, old_font);
866 ReleaseDC(es->hwndSelf, dc);
867 return index;
868 }
869
870
871 /*********************************************************************
872 *
873 * EDIT_ConfinePoint
874 *
875 * adjusts the point to be within the formatting rectangle
876 * (so CharFromPos returns the nearest _visible_ character)
877 *
878 */
879 static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y)
880 {
881 *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
882 *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
883 }
884
885
886 /*********************************************************************
887 *
888 * EM_LINEFROMCHAR
889 *
890 */
891 static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index)
892 {
893 INT line;
894 LINEDEF *line_def;
895
896 if (!(es->style & ES_MULTILINE))
897 return 0;
898 if (index > (INT)get_text_length(es))
899 return es->line_count - 1;
900 if (index == -1)
901 index = min(es->selection_start, es->selection_end);
902
903 line = 0;
904 line_def = es->first_line_def;
905 index -= line_def->length;
906 while ((index >= 0) && line_def->next) {
907 line++;
908 line_def = line_def->next;
909 index -= line_def->length;
910 }
911 return line;
912 }
913
914
915 /*********************************************************************
916 *
917 * EM_LINEINDEX
918 *
919 */
920 static INT EDIT_EM_LineIndex(const EDITSTATE *es, INT line)
921 {
922 INT line_index;
923 const LINEDEF *line_def;
924
925 if (!(es->style & ES_MULTILINE))
926 return 0;
927 if (line >= es->line_count)
928 return -1;
929
930 line_index = 0;
931 line_def = es->first_line_def;
932 if (line == -1) {
933 INT index = es->selection_end - line_def->length;
934 while ((index >= 0) && line_def->next) {
935 line_index += line_def->length;
936 line_def = line_def->next;
937 index -= line_def->length;
938 }
939 } else {
940 while (line > 0) {
941 line_index += line_def->length;
942 line_def = line_def->next;
943 line--;
944 }
945 }
946 return line_index;
947 }
948
949
950 /*********************************************************************
951 *
952 * EM_LINELENGTH
953 *
954 */
955 static INT EDIT_EM_LineLength(EDITSTATE *es, INT index)
956 {
957 LINEDEF *line_def;
958
959 if (!(es->style & ES_MULTILINE))
960 return get_text_length(es);
961
962 if (index == -1) {
963 /* get the number of remaining non-selected chars of selected lines */
964 INT32 l; /* line number */
965 INT32 li; /* index of first char in line */
966 INT32 count;
967 l = EDIT_EM_LineFromChar(es, es->selection_start);
968 /* # chars before start of selection area */
969 count = es->selection_start - EDIT_EM_LineIndex(es, l);
970 l = EDIT_EM_LineFromChar(es, es->selection_end);
971 /* # chars after end of selection */
972 li = EDIT_EM_LineIndex(es, l);
973 count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
974 return count;
975 }
976 line_def = es->first_line_def;
977 index -= line_def->length;
978 while ((index >= 0) && line_def->next) {
979 line_def = line_def->next;
980 index -= line_def->length;
981 }
982 return line_def->net_length;
983 }
984
985
986 /*********************************************************************
987 *
988 * EM_POSFROMCHAR
989 *
990 */
991 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap)
992 {
993 INT len = get_text_length(es);
994 INT l;
995 INT li;
996 INT x;
997 INT y = 0;
998 INT w;
999 INT lw = 0;
1000 INT ll = 0;
1001 HDC dc;
1002 HFONT old_font = 0;
1003 SIZE size;
1004 LINEDEF *line_def;
1005
1006 index = min(index, len);
1007 dc = GetDC(es->hwndSelf);
1008 if (es->font)
1009 old_font = SelectObject(dc, es->font);
1010 if (es->style & ES_MULTILINE) {
1011 l = EDIT_EM_LineFromChar(es, index);
1012 y = (l - es->y_offset) * es->line_height;
1013 li = EDIT_EM_LineIndex(es, l);
1014 if (after_wrap && (li == index) && l) {
1015 INT l2 = l - 1;
1016 line_def = es->first_line_def;
1017 while (l2) {
1018 line_def = line_def->next;
1019 l2--;
1020 }
1021 if (line_def->ending == END_WRAP) {
1022 l--;
1023 y -= es->line_height;
1024 li = EDIT_EM_LineIndex(es, l);
1025 }
1026 }
1027
1028 line_def = es->first_line_def;
1029 while (line_def->index != li)
1030 line_def = line_def->next;
1031
1032 ll = line_def->net_length;
1033 lw = line_def->width;
1034
1035 w = es->format_rect.right - es->format_rect.left;
1036 if (es->style & ES_RIGHT)
1037 {
1038 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li + (index - li), ll - (index - li),
1039 es->tabs_count, es->tabs)) - es->x_offset;
1040 x = w - x;
1041 }
1042 else if (es->style & ES_CENTER)
1043 {
1044 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
1045 es->tabs_count, es->tabs)) - es->x_offset;
1046 x += (w - lw) / 2;
1047 }
1048 else /* ES_LEFT */
1049 {
1050 x = LOWORD(GetTabbedTextExtentW(dc, es->text + li, index - li,
1051 es->tabs_count, es->tabs)) - es->x_offset;
1052 }
1053 } else {
1054 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
1055 if (index < es->x_offset) {
1056 GetTextExtentPoint32W(dc, text + index,
1057 es->x_offset - index, &size);
1058 x = -size.cx;
1059 } else {
1060 GetTextExtentPoint32W(dc, text + es->x_offset,
1061 index - es->x_offset, &size);
1062 x = size.cx;
1063
1064 if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER)))
1065 {
1066 w = es->format_rect.right - es->format_rect.left;
1067 if (w > es->text_width)
1068 {
1069 if (es->style & ES_RIGHT)
1070 x += w - es->text_width;
1071 else if (es->style & ES_CENTER)
1072 x += (w - es->text_width) / 2;
1073 }
1074 }
1075 }
1076 y = 0;
1077 if (es->style & ES_PASSWORD)
1078 HeapFree(GetProcessHeap(), 0, text);
1079 }
1080 x += es->format_rect.left;
1081 y += es->format_rect.top;
1082 if (es->font)
1083 SelectObject(dc, old_font);
1084 ReleaseDC(es->hwndSelf, dc);
1085 return MAKELONG((INT16)x, (INT16)y);
1086 }
1087
1088
1089 /*********************************************************************
1090 *
1091 * EDIT_GetLineRect
1092 *
1093 * Calculates the bounding rectangle for a line from a starting
1094 * column to an ending column.
1095 *
1096 */
1097 static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1098 {
1099 INT line_index = EDIT_EM_LineIndex(es, line);
1100
1101 if (es->style & ES_MULTILINE)
1102 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1103 else
1104 rc->top = es->format_rect.top;
1105 rc->bottom = rc->top + es->line_height;
1106 rc->left = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
1107 rc->right = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
1108 }
1109
1110
1111 static inline void text_buffer_changed(EDITSTATE *es)
1112 {
1113 es->text_length = (UINT)-1;
1114 }
1115
1116 /*********************************************************************
1117 *
1118 * EDIT_LockBuffer
1119 *
1120 * This acts as a LocalLock16(), but it locks only once. This way
1121 * you can call it whenever you like, without unlocking.
1122 *
1123 * Initially the edit control allocates a HLOCAL32 buffer
1124 * (32 bit linear memory handler). However, 16 bit application
1125 * might send an EM_GETHANDLE message and expect a HLOCAL16 (16 bit SEG:OFF
1126 * handler). From that moment on we have to keep using this 16 bit memory
1127 * handler, because it is supposed to be valid at all times after EM_GETHANDLE.
1128 * What we do is create a HLOCAL16 buffer, copy the text, and do pointer
1129 * conversion.
1130 *
1131 */
1132 static void EDIT_LockBuffer(EDITSTATE *es)
1133 {
1134 #ifndef __REACTOS__
1135 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
1136 HINSTANCE16 hInstance = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
1137 #endif
1138 if (!es->text) {
1139 CHAR *textA = NULL;
1140 UINT countA = 0;
1141 #ifndef __REACTOS__
1142 BOOL _16bit = FALSE;
1143 #endif
1144
1145 if(es->hloc32W)
1146 {
1147 if(es->hloc32A)
1148 {
1149 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1150 textA = LocalLock(es->hloc32A);
1151 countA = strlen(textA) + 1;
1152 }
1153 #ifndef __REACTOS__
1154 else if(es->hloc16)
1155 {
1156 HANDLE16 oldDS = stack16->ds;
1157 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1158 stack16->ds = hInstance;
1159 textA = MapSL(LocalLock16(es->hloc16));
1160 stack16->ds = oldDS;
1161 countA = strlen(textA) + 1;
1162 _16bit = TRUE;
1163 }
1164 #endif
1165 }
1166 else {
1167 ERR("no buffer ... please report\n");
1168 return;
1169 }
1170
1171 if(textA)
1172 {
1173 HLOCAL hloc32W_new;
1174 UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
1175 TRACE("%d bytes translated to %d WCHARs\n", countA, countW_new);
1176 if(countW_new > es->buffer_size + 1)
1177 {
1178 UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1179 TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1180 hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1181 if(hloc32W_new)
1182 {
1183 es->hloc32W = hloc32W_new;
1184 es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1185 TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1186 }
1187 else
1188 WARN("FAILED! Will synchronize partially\n");
1189 }
1190 }
1191
1192 /*TRACE("Locking 32-bit UNICODE buffer\n");*/
1193 es->text = LocalLock(es->hloc32W);
1194
1195 if(textA)
1196 {
1197 MultiByteToWideChar(CP_ACP, 0, textA, countA, es->text, es->buffer_size + 1);
1198 #ifndef __REACTOS__
1199 if(_16bit)
1200 {
1201 HANDLE16 oldDS = stack16->ds;
1202 stack16->ds = hInstance;
1203 LocalUnlock16(es->hloc16);
1204 stack16->ds = oldDS;
1205 }
1206 else
1207 #endif
1208 LocalUnlock(es->hloc32A);
1209 }
1210 }
1211 if(es->flags & EF_APP_HAS_HANDLE) text_buffer_changed(es);
1212 es->lock_count++;
1213 }
1214
1215
1216 /*********************************************************************
1217 *
1218 * EDIT_UnlockBuffer
1219 *
1220 */
1221 static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force)
1222 {
1223
1224 /* Edit window might be already destroyed */
1225 if(!IsWindow(es->hwndSelf))
1226 {
1227 WARN("edit hwnd %p already destroyed\n", es->hwndSelf);
1228 return;
1229 }
1230
1231 if (!es->lock_count) {
1232 ERR("lock_count == 0 ... please report\n");
1233 return;
1234 }
1235 if (!es->text) {
1236 ERR("es->text == 0 ... please report\n");
1237 return;
1238 }
1239
1240 if (force || (es->lock_count == 1)) {
1241 if (es->hloc32W) {
1242 CHAR *textA = NULL;
1243 UINT countA = 0;
1244 UINT countW = get_text_length(es) + 1;
1245 #ifndef __REACTOS__
1246 STACK16FRAME* stack16 = NULL;
1247 HANDLE16 oldDS = 0;
1248 #endif
1249
1250 if(es->hloc32A)
1251 {
1252 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
1253 TRACE("Synchronizing with 32-bit ANSI buffer\n");
1254 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
1255 countA = LocalSize(es->hloc32A);
1256 if(countA_new > countA)
1257 {
1258 HLOCAL hloc32A_new;
1259 UINT alloc_size = ROUND_TO_GROW(countA_new);
1260 TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
1261 hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1262 if(hloc32A_new)
1263 {
1264 es->hloc32A = hloc32A_new;
1265 countA = LocalSize(hloc32A_new);
1266 TRACE("Real new size %d bytes\n", countA);
1267 }
1268 else
1269 WARN("FAILED! Will synchronize partially\n");
1270 }
1271 textA = LocalLock(es->hloc32A);
1272 }
1273 #ifndef __REACTOS__
1274 else if(es->hloc16)
1275 {
1276 UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
1277
1278 TRACE("Synchronizing with 16-bit ANSI buffer\n");
1279 TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
1280
1281 stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
1282 oldDS = stack16->ds;
1283 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
1284
1285 countA = LocalSize16(es->hloc16);
1286 if(countA_new > countA)
1287 {
1288 HLOCAL16 hloc16_new;
1289 UINT alloc_size = ROUND_TO_GROW(countA_new);
1290 TRACE("Resizing 16-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
1291 hloc16_new = LocalReAlloc16(es->hloc16, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1292 if(hloc16_new)
1293 {
1294 es->hloc16 = hloc16_new;
1295 countA = LocalSize16(hloc16_new);
1296 TRACE("Real new size %d bytes\n", countA);
1297 }
1298 else
1299 WARN("FAILED! Will synchronize partially\n");
1300 }
1301 textA = MapSL(LocalLock16(es->hloc16));
1302 }
1303 #endif
1304
1305 if(textA)
1306 {
1307 WideCharToMultiByte(CP_ACP, 0, es->text, countW, textA, countA, NULL, NULL);
1308 #ifndef __REACTOS__
1309 if(stack16)
1310 LocalUnlock16(es->hloc16);
1311 else
1312 #endif
1313 LocalUnlock(es->hloc32A);
1314 }
1315
1316 #ifndef __REACTOS__
1317 if (stack16) stack16->ds = oldDS;
1318 #endif
1319 LocalUnlock(es->hloc32W);
1320 es->text = NULL;
1321 }
1322 else {
1323 ERR("no buffer ... please report\n");
1324 return;
1325 }
1326 }
1327 es->lock_count--;
1328 }
1329
1330
1331 /*********************************************************************
1332 *
1333 * EDIT_MakeFit
1334 *
1335 * Try to fit size + 1 characters in the buffer.
1336 */
1337 static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size)
1338 {
1339 HLOCAL hNew32W;
1340
1341 if (size <= es->buffer_size)
1342 return TRUE;
1343
1344 TRACE("trying to ReAlloc to %d+1 characters\n", size);
1345
1346 /* Force edit to unlock it's buffer. es->text now NULL */
1347 EDIT_UnlockBuffer(es, TRUE);
1348
1349 if (es->hloc32W) {
1350 UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1351 if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1352 TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W);
1353 es->hloc32W = hNew32W;
1354 es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1355 }
1356 }
1357
1358 EDIT_LockBuffer(es);
1359
1360 if (es->buffer_size < size) {
1361 WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1362 EDIT_NOTIFY_PARENT(es, EN_ERRSPACE);
1363 return FALSE;
1364 } else {
1365 TRACE("We now have %d+1\n", es->buffer_size);
1366 return TRUE;
1367 }
1368 }
1369
1370
1371 /*********************************************************************
1372 *
1373 * EDIT_MakeUndoFit
1374 *
1375 * Try to fit size + 1 bytes in the undo buffer.
1376 *
1377 */
1378 static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size)
1379 {
1380 UINT alloc_size;
1381
1382 if (size <= es->undo_buffer_size)
1383 return TRUE;
1384
1385 TRACE("trying to ReAlloc to %d+1\n", size);
1386
1387 alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1388 if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1389 es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1390 return TRUE;
1391 }
1392 else
1393 {
1394 WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1395 return FALSE;
1396 }
1397 }
1398
1399
1400 /*********************************************************************
1401 *
1402 * EDIT_UpdateTextRegion
1403 *
1404 */
1405 static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
1406 {
1407 if (es->flags & EF_UPDATE) {
1408 es->flags &= ~EF_UPDATE;
1409 EDIT_NOTIFY_PARENT(es, EN_UPDATE);
1410 }
1411 InvalidateRgn(es->hwndSelf, hrgn, bErase);
1412 }
1413
1414
1415 /*********************************************************************
1416 *
1417 * EDIT_UpdateText
1418 *
1419 */
1420 static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase)
1421 {
1422 if (es->flags & EF_UPDATE) {
1423 es->flags &= ~EF_UPDATE;
1424 EDIT_NOTIFY_PARENT(es, EN_UPDATE);
1425 }
1426 InvalidateRect(es->hwndSelf, rc, bErase);
1427 }
1428
1429 /*********************************************************************
1430 *
1431 * EDIT_SL_InvalidateText
1432 *
1433 * Called from EDIT_InvalidateText().
1434 * Does the job for single-line controls only.
1435 *
1436 */
1437 static void EDIT_SL_InvalidateText(EDITSTATE *es, INT start, INT end)
1438 {
1439 RECT line_rect;
1440 RECT rc;
1441
1442 EDIT_GetLineRect(es, 0, start, end, &line_rect);
1443 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1444 EDIT_UpdateText(es, &rc, TRUE);
1445 }
1446
1447
1448 static inline INT get_vertical_line_count(EDITSTATE *es)
1449 {
1450 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1451 return max(1,vlc);
1452 }
1453
1454 /*********************************************************************
1455 *
1456 * EDIT_ML_InvalidateText
1457 *
1458 * Called from EDIT_InvalidateText().
1459 * Does the job for multi-line controls only.
1460 *
1461 */
1462 static void EDIT_ML_InvalidateText(EDITSTATE *es, INT start, INT end)
1463 {
1464 INT vlc = get_vertical_line_count(es);
1465 INT sl = EDIT_EM_LineFromChar(es, start);
1466 INT el = EDIT_EM_LineFromChar(es, end);
1467 INT sc;
1468 INT ec;
1469 RECT rc1;
1470 RECT rcWnd;
1471 RECT rcLine;
1472 RECT rcUpdate;
1473 INT l;
1474
1475 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1476 return;
1477
1478 sc = start - EDIT_EM_LineIndex(es, sl);
1479 ec = end - EDIT_EM_LineIndex(es, el);
1480 if (sl < es->y_offset) {
1481 sl = es->y_offset;
1482 sc = 0;
1483 }
1484 if (el > es->y_offset + vlc) {
1485 el = es->y_offset + vlc;
1486 ec = EDIT_EM_LineLength(es, EDIT_EM_LineIndex(es, el));
1487 }
1488 GetClientRect(es->hwndSelf, &rc1);
1489 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1490 if (sl == el) {
1491 EDIT_GetLineRect(es, sl, sc, ec, &rcLine);
1492 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1493 EDIT_UpdateText(es, &rcUpdate, TRUE);
1494 } else {
1495 EDIT_GetLineRect(es, sl, sc,
1496 EDIT_EM_LineLength(es,
1497 EDIT_EM_LineIndex(es, sl)),
1498 &rcLine);
1499 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1500 EDIT_UpdateText(es, &rcUpdate, TRUE);
1501 for (l = sl + 1 ; l < el ; l++) {
1502 EDIT_GetLineRect(es, l, 0,
1503 EDIT_EM_LineLength(es,
1504 EDIT_EM_LineIndex(es, l)),
1505 &rcLine);
1506 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1507 EDIT_UpdateText(es, &rcUpdate, TRUE);
1508 }
1509 EDIT_GetLineRect(es, el, 0, ec, &rcLine);
1510 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1511 EDIT_UpdateText(es, &rcUpdate, TRUE);
1512 }
1513 }
1514
1515
1516 /*********************************************************************
1517 *
1518 * EDIT_InvalidateText
1519 *
1520 * Invalidate the text from offset start up to, but not including,
1521 * offset end. Useful for (re)painting the selection.
1522 * Regions outside the linewidth are not invalidated.
1523 * end == -1 means end == TextLength.
1524 * start and end need not be ordered.
1525 *
1526 */
1527 static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end)
1528 {
1529 if (end == start)
1530 return;
1531
1532 if (end == -1)
1533 end = get_text_length(es);
1534
1535 if (end < start) {
1536 INT tmp = start;
1537 start = end;
1538 end = tmp;
1539 }
1540
1541 if (es->style & ES_MULTILINE)
1542 EDIT_ML_InvalidateText(es, start, end);
1543 else
1544 EDIT_SL_InvalidateText(es, start, end);
1545 }
1546
1547
1548 /*********************************************************************
1549 *
1550 * EDIT_EM_SetSel
1551 *
1552 * note: unlike the specs say: the order of start and end
1553 * _is_ preserved in Windows. (i.e. start can be > end)
1554 * In other words: this handler is OK
1555 *
1556 */
1557 static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
1558 {
1559 UINT old_start = es->selection_start;
1560 UINT old_end = es->selection_end;
1561 UINT len = get_text_length(es);
1562
1563 if (start == (UINT)-1) {
1564 start = es->selection_end;
1565 end = es->selection_end;
1566 } else {
1567 start = min(start, len);
1568 end = min(end, len);
1569 }
1570 es->selection_start = start;
1571 es->selection_end = end;
1572 if (after_wrap)
1573 es->flags |= EF_AFTER_WRAP;
1574 else
1575 es->flags &= ~EF_AFTER_WRAP;
1576 /* Compute the necessary invalidation region. */
1577 /* Note that we don't need to invalidate regions which have
1578 * "never" been selected, or those which are "still" selected.
1579 * In fact, every time we hit a selection boundary, we can
1580 * *toggle* whether we need to invalidate. Thus we can optimize by
1581 * *sorting* the interval endpoints. Let's assume that we sort them
1582 * in this order:
1583 * start <= end <= old_start <= old_end
1584 * Knuth 5.3.1 (p 183) assures us that this can be done optimally
1585 * in 5 comparisons; i.e. it is impossible to do better than the
1586 * following: */
1587 ORDER_UINT(end, old_end);
1588 ORDER_UINT(start, old_start);
1589 ORDER_UINT(old_start, old_end);
1590 ORDER_UINT(start, end);
1591 /* Note that at this point 'end' and 'old_start' are not in order, but
1592 * start is definitely the min. and old_end is definitely the max. */
1593 if (end != old_start)
1594 {
1595 /*
1596 * One can also do
1597 * ORDER_UINT32(end, old_start);
1598 * EDIT_InvalidateText(es, start, end);
1599 * EDIT_InvalidateText(es, old_start, old_end);
1600 * in place of the following if statement.
1601 * (That would complete the optimal five-comparison four-element sort.)
1602 */
1603 if (old_start > end )
1604 {
1605 EDIT_InvalidateText(es, start, end);
1606 EDIT_InvalidateText(es, old_start, old_end);
1607 }
1608 else
1609 {
1610 EDIT_InvalidateText(es, start, old_start);
1611 EDIT_InvalidateText(es, end, old_end);
1612 }
1613 }
1614 else EDIT_InvalidateText(es, start, old_end);
1615 }
1616
1617
1618 /*********************************************************************
1619 *
1620 * EDIT_UpdateScrollInfo
1621 *
1622 */
1623 static void EDIT_UpdateScrollInfo(EDITSTATE *es)
1624 {
1625 if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
1626 {
1627 SCROLLINFO si;
1628 si.cbSize = sizeof(SCROLLINFO);
1629 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1630 si.nMin = 0;
1631 si.nMax = es->line_count - 1;
1632 si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1633 si.nPos = es->y_offset;
1634 TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1635 si.nMin, si.nMax, si.nPage, si.nPos);
1636 SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
1637 }
1638
1639 if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
1640 {
1641 SCROLLINFO si;
1642 si.cbSize = sizeof(SCROLLINFO);
1643 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
1644 si.nMin = 0;
1645 si.nMax = es->text_width - 1;
1646 si.nPage = es->format_rect.right - es->format_rect.left;
1647 si.nPos = es->x_offset;
1648 TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1649 si.nMin, si.nMax, si.nPage, si.nPos);
1650 SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
1651 }
1652 }
1653
1654
1655 /*********************************************************************
1656 *
1657 * EDIT_EM_LineScroll_internal
1658 *
1659 * Version of EDIT_EM_LineScroll for internal use.
1660 * It doesn't refuse if ES_MULTILINE is set and assumes that
1661 * dx is in pixels, dy - in lines.
1662 *
1663 */
1664 static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy)
1665 {
1666 INT nyoff;
1667 INT x_offset_in_pixels;
1668 INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) /
1669 es->line_height;
1670
1671 if (es->style & ES_MULTILINE)
1672 {
1673 x_offset_in_pixels = es->x_offset;
1674 }
1675 else
1676 {
1677 dy = 0;
1678 x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE));
1679 }
1680
1681 if (-dx > x_offset_in_pixels)
1682 dx = -x_offset_in_pixels;
1683 if (dx > es->text_width - x_offset_in_pixels)
1684 dx = es->text_width - x_offset_in_pixels;
1685 nyoff = max(0, es->y_offset + dy);
1686 if (nyoff >= es->line_count - lines_per_page)
1687 nyoff = max(0, es->line_count - lines_per_page);
1688 dy = (es->y_offset - nyoff) * es->line_height;
1689 if (dx || dy) {
1690 RECT rc1;
1691 RECT rc;
1692
1693 es->y_offset = nyoff;
1694 if(es->style & ES_MULTILINE)
1695 es->x_offset += dx;
1696 else
1697 es->x_offset += dx / es->char_width;
1698
1699 GetClientRect(es->hwndSelf, &rc1);
1700 IntersectRect(&rc, &rc1, &es->format_rect);
1701 ScrollWindowEx(es->hwndSelf, -dx, dy,
1702 NULL, &rc, NULL, NULL, SW_INVALIDATE);
1703 /* force scroll info update */
1704 EDIT_UpdateScrollInfo(es);
1705 }
1706 if (dx && !(es->flags & EF_HSCROLL_TRACK))
1707 EDIT_NOTIFY_PARENT(es, EN_HSCROLL);
1708 if (dy && !(es->flags & EF_VSCROLL_TRACK))
1709 EDIT_NOTIFY_PARENT(es, EN_VSCROLL);
1710 return TRUE;
1711 }
1712
1713 /*********************************************************************
1714 *
1715 * EM_LINESCROLL
1716 *
1717 * NOTE: dx is in average character widths, dy - in lines;
1718 *
1719 */
1720 static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy)
1721 {
1722 if (!(es->style & ES_MULTILINE))
1723 return FALSE;
1724
1725 dx *= es->char_width;
1726 return EDIT_EM_LineScroll_internal(es, dx, dy);
1727 }
1728
1729
1730 /*********************************************************************
1731 *
1732 * EM_SCROLL
1733 *
1734 */
1735 static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action)
1736 {
1737 INT dy;
1738
1739 if (!(es->style & ES_MULTILINE))
1740 return (LRESULT)FALSE;
1741
1742 dy = 0;
1743
1744 switch (action) {
1745 case SB_LINEUP:
1746 if (es->y_offset)
1747 dy = -1;
1748 break;
1749 case SB_LINEDOWN:
1750 if (es->y_offset < es->line_count - 1)
1751 dy = 1;
1752 break;
1753 case SB_PAGEUP:
1754 if (es->y_offset)
1755 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
1756 break;
1757 case SB_PAGEDOWN:
1758 if (es->y_offset < es->line_count - 1)
1759 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1760 break;
1761 default:
1762 return (LRESULT)FALSE;
1763 }
1764 if (dy) {
1765 INT vlc = get_vertical_line_count(es);
1766 /* check if we are going to move too far */
1767 if(es->y_offset + dy > es->line_count - vlc)
1768 dy = es->line_count - vlc - es->y_offset;
1769
1770 /* Notification is done in EDIT_EM_LineScroll */
1771 if(dy)
1772 EDIT_EM_LineScroll(es, 0, dy);
1773 }
1774 #ifdef __REACTOS__
1775 return MAKELONG((SHORT)dy, (BOOL)TRUE);
1776 #else
1777 return MAKELONG((INT16)dy, (BOOL16)TRUE);
1778 #endif
1779 }
1780
1781
1782 /*********************************************************************
1783 *
1784 * EDIT_SetCaretPos
1785 *
1786 */
1787 static void EDIT_SetCaretPos(EDITSTATE *es, INT pos,
1788 BOOL after_wrap)
1789 {
1790 LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap);
1791 TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res));
1792 SetCaretPos((short)LOWORD(res), (short)HIWORD(res));
1793 }
1794
1795
1796 /*********************************************************************
1797 *
1798 * EM_SCROLLCARET
1799 *
1800 */
1801 static void EDIT_EM_ScrollCaret(EDITSTATE *es)
1802 {
1803 if (es->style & ES_MULTILINE) {
1804 INT l;
1805 INT vlc;
1806 INT ww;
1807 INT cw = es->char_width;
1808 INT x;
1809 INT dy = 0;
1810 INT dx = 0;
1811
1812 l = EDIT_EM_LineFromChar(es, es->selection_end);
1813 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP));
1814 vlc = get_vertical_line_count(es);
1815 if (l >= es->y_offset + vlc)
1816 dy = l - vlc + 1 - es->y_offset;
1817 if (l < es->y_offset)
1818 dy = l - es->y_offset;
1819 ww = es->format_rect.right - es->format_rect.left;
1820 if (x < es->format_rect.left)
1821 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1822 if (x > es->format_rect.right)
1823 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1824 if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1825 {
1826 /* check if we are going to move too far */
1827 if(es->x_offset + dx + ww > es->text_width)
1828 dx = es->text_width - ww - es->x_offset;
1829 if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1830 EDIT_EM_LineScroll_internal(es, dx, dy);
1831 }
1832 } else {
1833 INT x;
1834 INT goal;
1835 INT format_width;
1836
1837 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1838 format_width = es->format_rect.right - es->format_rect.left;
1839 if (x < es->format_rect.left) {
1840 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1841 do {
1842 es->x_offset--;
1843 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1844 } while ((x < goal) && es->x_offset);
1845 /* FIXME: use ScrollWindow() somehow to improve performance */
1846 EDIT_UpdateText(es, NULL, TRUE);
1847 } else if (x > es->format_rect.right) {
1848 INT x_last;
1849 INT len = get_text_length(es);
1850 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1851 do {
1852 es->x_offset++;
1853 x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1854 x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE));
1855 } while ((x > goal) && (x_last > es->format_rect.right));
1856 /* FIXME: use ScrollWindow() somehow to improve performance */
1857 EDIT_UpdateText(es, NULL, TRUE);
1858 }
1859 }
1860
1861 if(es->flags & EF_FOCUSED)
1862 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
1863 }
1864
1865
1866 /*********************************************************************
1867 *
1868 * EDIT_MoveBackward
1869 *
1870 */
1871 static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend)
1872 {
1873 INT e = es->selection_end;
1874
1875 if (e) {
1876 e--;
1877 if ((es->style & ES_MULTILINE) && e &&
1878 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1879 e--;
1880 if (e && (es->text[e - 1] == '\r'))
1881 e--;
1882 }
1883 }
1884 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1885 EDIT_EM_ScrollCaret(es);
1886 }
1887
1888
1889 /*********************************************************************
1890 *
1891 * EDIT_MoveDown_ML
1892 *
1893 * Only for multi line controls
1894 * Move the caret one line down, on a column with the nearest
1895 * x coordinate on the screen (might be a different column).
1896 *
1897 */
1898 static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend)
1899 {
1900 INT s = es->selection_start;
1901 INT e = es->selection_end;
1902 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1903 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1904 INT x = (short)LOWORD(pos);
1905 INT y = (short)HIWORD(pos);
1906
1907 e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap);
1908 if (!extend)
1909 s = e;
1910 EDIT_EM_SetSel(es, s, e, after_wrap);
1911 EDIT_EM_ScrollCaret(es);
1912 }
1913
1914
1915 /*********************************************************************
1916 *
1917 * EDIT_MoveEnd
1918 *
1919 */
1920 static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl)
1921 {
1922 BOOL after_wrap = FALSE;
1923 INT e;
1924
1925 /* Pass a high value in x to make sure of receiving the end of the line */
1926 if (!ctrl && (es->style & ES_MULTILINE))
1927 e = EDIT_CharFromPos(es, 0x3fffffff,
1928 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1929 else
1930 e = get_text_length(es);
1931 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap);
1932 EDIT_EM_ScrollCaret(es);
1933 }
1934
1935
1936 /*********************************************************************
1937 *
1938 * EDIT_MoveForward
1939 *
1940 */
1941 static void EDIT_MoveForward(EDITSTATE *es, BOOL extend)
1942 {
1943 INT e = es->selection_end;
1944
1945 if (es->text[e]) {
1946 e++;
1947 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1948 if (es->text[e] == '\n')
1949 e++;
1950 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1951 e += 2;
1952 }
1953 }
1954 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1955 EDIT_EM_ScrollCaret(es);
1956 }
1957
1958
1959 /*********************************************************************
1960 *
1961 * EDIT_MoveHome
1962 *
1963 * Home key: move to beginning of line.
1964 *
1965 */
1966 static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl)
1967 {
1968 INT e;
1969
1970 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1971 if (!ctrl && (es->style & ES_MULTILINE))
1972 e = EDIT_CharFromPos(es, -es->x_offset,
1973 HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1974 else
1975 e = 0;
1976 EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1977 EDIT_EM_ScrollCaret(es);
1978 }
1979
1980
1981 /*********************************************************************
1982 *
1983 * EDIT_MovePageDown_ML
1984 *
1985 * Only for multi line controls
1986 * Move the caret one page down, on a column with the nearest
1987 * x coordinate on the screen (might be a different column).
1988 *
1989 */
1990 static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend)
1991 {
1992 INT s = es->selection_start;
1993 INT e = es->selection_end;
1994 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1995 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1996 INT x = (short)LOWORD(pos);
1997 INT y = (short)HIWORD(pos);
1998
1999 e = EDIT_CharFromPos(es, x,
2000 y + (es->format_rect.bottom - es->format_rect.top),
2001 &after_wrap);
2002 if (!extend)
2003 s = e;
2004 EDIT_EM_SetSel(es, s, e, after_wrap);
2005 EDIT_EM_ScrollCaret(es);
2006 }
2007
2008
2009 /*********************************************************************
2010 *
2011 * EDIT_MovePageUp_ML
2012 *
2013 * Only for multi line controls
2014 * Move the caret one page up, on a column with the nearest
2015 * x coordinate on the screen (might be a different column).
2016 *
2017 */
2018 static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend)
2019 {
2020 INT s = es->selection_start;
2021 INT e = es->selection_end;
2022 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2023 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2024 INT x = (short)LOWORD(pos);
2025 INT y = (short)HIWORD(pos);
2026
2027 e = EDIT_CharFromPos(es, x,
2028 y - (es->format_rect.bottom - es->format_rect.top),
2029 &after_wrap);
2030 if (!extend)
2031 s = e;
2032 EDIT_EM_SetSel(es, s, e, after_wrap);
2033 EDIT_EM_ScrollCaret(es);
2034 }
2035
2036
2037 /*********************************************************************
2038 *
2039 * EDIT_MoveUp_ML
2040 *
2041 * Only for multi line controls
2042 * Move the caret one line up, on a column with the nearest
2043 * x coordinate on the screen (might be a different column).
2044 *
2045 */
2046 static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend)
2047 {
2048 INT s = es->selection_start;
2049 INT e = es->selection_end;
2050 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2051 LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2052 INT x = (short)LOWORD(pos);
2053 INT y = (short)HIWORD(pos);
2054
2055 e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap);
2056 if (!extend)
2057 s = e;
2058 EDIT_EM_SetSel(es, s, e, after_wrap);
2059 EDIT_EM_ScrollCaret(es);
2060 }
2061
2062
2063 /*********************************************************************
2064 *
2065 * EDIT_MoveWordBackward
2066 *
2067 */
2068 static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend)
2069 {
2070 INT s = es->selection_start;
2071 INT e = es->selection_end;
2072 INT l;
2073 INT ll;
2074 INT li;
2075
2076 l = EDIT_EM_LineFromChar(es, e);
2077 ll = EDIT_EM_LineLength(es, e);
2078 li = EDIT_EM_LineIndex(es, l);
2079 if (e - li == 0) {
2080 if (l) {
2081 li = EDIT_EM_LineIndex(es, l - 1);
2082 e = li + EDIT_EM_LineLength(es, li);
2083 }
2084 } else {
2085 e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
2086 }
2087 if (!extend)
2088 s = e;
2089 EDIT_EM_SetSel(es, s, e, FALSE);
2090 EDIT_EM_ScrollCaret(es);
2091 }
2092
2093
2094 /*********************************************************************
2095 *
2096 * EDIT_MoveWordForward
2097 *
2098 */
2099 static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend)
2100 {
2101 INT s = es->selection_start;
2102 INT e = es->selection_end;
2103 INT l;
2104 INT ll;
2105 INT li;
2106
2107 l = EDIT_EM_LineFromChar(es, e);
2108 ll = EDIT_EM_LineLength(es, e);
2109 li = EDIT_EM_LineIndex(es, l);
2110 if (e - li == ll) {
2111 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2112 e = EDIT_EM_LineIndex(es, l + 1);
2113 } else {
2114 e = li + EDIT_CallWordBreakProc(es,
2115 li, e - li + 1, ll, WB_RIGHT);
2116 }
2117 if (!extend)
2118 s = e;
2119 EDIT_EM_SetSel(es, s, e, FALSE);
2120 EDIT_EM_ScrollCaret(es);
2121 }
2122
2123
2124 /*********************************************************************
2125 *
2126 * EDIT_PaintText
2127 *
2128 */
2129 static INT EDIT_PaintText(EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
2130 {
2131 COLORREF BkColor;
2132 COLORREF TextColor;
2133 LOGFONTW underline_font;
2134 HFONT hUnderline = 0;
2135 HFONT old_font = 0;
2136 INT ret;
2137 INT li;
2138 INT BkMode;
2139 SIZE size;
2140
2141 if (!count)
2142 return 0;
2143 BkMode = GetBkMode(dc);
2144 BkColor = GetBkColor(dc);
2145 TextColor = GetTextColor(dc);
2146 if (rev) {
2147 if (es->composition_len == 0)
2148 {
2149 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
2150 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2151 SetBkMode( dc, OPAQUE);
2152 }
2153 else
2154 {
2155 HFONT current = GetCurrentObject(dc,OBJ_FONT);
2156 GetObjectW(current,sizeof(LOGFONTW),&underline_font);
2157 underline_font.lfUnderline = TRUE;
2158 hUnderline = CreateFontIndirectW(&underline_font);
2159 old_font = SelectObject(dc,hUnderline);
2160 }
2161 }
2162 li = EDIT_EM_LineIndex(es, line);
2163 if (es->style & ES_MULTILINE) {
2164 ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2165 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2166 } else {
2167 LPWSTR text = EDIT_GetPasswordPointer_SL(es);
2168 TextOutW(dc, x, y, text + li + col, count);
2169 GetTextExtentPoint32W(dc, text + li + col, count, &size);
2170 ret = size.cx;
2171 if (es->style & ES_PASSWORD)
2172 HeapFree(GetProcessHeap(), 0, text);
2173 }
2174 if (rev) {
2175 if (es->composition_len == 0)
2176 {
2177 SetBkColor(dc, BkColor);
2178 SetTextColor(dc, TextColor);
2179 SetBkMode( dc, BkMode);
2180 }
2181 else
2182 {
2183 if (old_font)
2184 SelectObject(dc,old_font);
2185 if (hUnderline)
2186 DeleteObject(hUnderline);
2187 }
2188 }
2189 return ret;
2190 }
2191
2192
2193 /*********************************************************************
2194 *
2195 * EDIT_PaintLine
2196 *
2197 */
2198 static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev)
2199 {
2200 INT s = es->selection_start;
2201 INT e = es->selection_end;
2202 INT li;
2203 INT ll;
2204 INT x;
2205 INT y;
2206 LRESULT pos;
2207
2208 if (es->style & ES_MULTILINE) {
2209 INT vlc = get_vertical_line_count(es);
2210
2211 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2212 return;
2213 } else if (line)
2214 return;
2215
2216 TRACE("line=%d\n", line);
2217
2218 pos = EDIT_EM_PosFromChar(es, EDIT_EM_LineIndex(es, line), FALSE);
2219 x = (short)LOWORD(pos);
2220 y = (short)HIWORD(pos);
2221 li = EDIT_EM_LineIndex(es, line);
2222 ll = EDIT_EM_LineLength(es, li);
2223 s = min(es->selection_start, es->selection_end);
2224 e = max(es->selection_start, es->selection_end);
2225 s = min(li + ll, max(li, s));
2226 e = min(li + ll, max(li, e));
2227 if (rev && (s != e) &&
2228 ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2229 x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2230 x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2231 x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2232 } else
2233 x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2234 }
2235
2236
2237 /*********************************************************************
2238 *
2239 * EDIT_AdjustFormatRect
2240 *
2241 * Adjusts the format rectangle for the current font and the
2242 * current client rectangle.
2243 *
2244 */
2245 static void EDIT_AdjustFormatRect(EDITSTATE *es)
2246 {
2247 RECT ClientRect;
2248
2249 es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2250 if (es->style & ES_MULTILINE)
2251 {
2252 INT fw, vlc, max_x_offset, max_y_offset;
2253
2254 vlc = get_vertical_line_count(es);
2255 es->format_rect.bottom = es->format_rect.top + vlc * es->line_height;
2256
2257 /* correct es->x_offset */
2258 fw = es->format_rect.right - es->format_rect.left;
2259 max_x_offset = es->text_width - fw;
2260 if(max_x_offset < 0) max_x_offset = 0;
2261 if(es->x_offset > max_x_offset)
2262 es->x_offset = max_x_offset;
2263
2264 /* correct es->y_offset */
2265 max_y_offset = es->line_count - vlc;
2266 if(max_y_offset < 0) max_y_offset = 0;
2267 if(es->y_offset > max_y_offset)
2268 es->y_offset = max_y_offset;
2269
2270 /* force scroll info update */
2271 EDIT_UpdateScrollInfo(es);
2272 }
2273 else
2274 /* Windows doesn't care to fix text placement for SL controls */
2275 es->format_rect.bottom = es->format_rect.top + es->line_height;
2276
2277 /* Always stay within the client area */
2278 GetClientRect(es->hwndSelf, &ClientRect);
2279 es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom);
2280
2281 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2282 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2283
2284 EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
2285 }
2286
2287
2288 /*********************************************************************
2289 *
2290 * EDIT_SetRectNP
2291 *
2292 * note: this is not (exactly) the handler called on EM_SETRECTNP
2293 * it is also used to set the rect of a single line control
2294 *
2295 */
2296 static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc)
2297 {
2298 LONG_PTR ExStyle;
2299 INT bw, bh;
2300 ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE);
2301
2302 CopyRect(&es->format_rect, rc);
2303
2304 if (ExStyle & WS_EX_CLIENTEDGE) {
2305 es->format_rect.left++;
2306 es->format_rect.right--;
2307
2308 if (es->format_rect.bottom - es->format_rect.top
2309 >= es->line_height + 2)
2310 {
2311 es->format_rect.top++;
2312 es->format_rect.bottom--;
2313 }
2314 }
2315 else if (es->style & WS_BORDER) {
2316 bw = GetSystemMetrics(SM_CXBORDER) + 1;
2317 bh = GetSystemMetrics(SM_CYBORDER) + 1;
2318 es->format_rect.left += bw;
2319 es->format_rect.right -= bw;
2320 if (es->format_rect.bottom - es->format_rect.top
2321 >= es->line_height + 2 * bh)
2322 {
2323 es->format_rect.top += bh;
2324 es->format_rect.bottom -= bh;
2325 }
2326 }
2327
2328 es->format_rect.left += es->left_margin;
2329 es->format_rect.right -= es->right_margin;
2330 EDIT_AdjustFormatRect(es);
2331 }
2332
2333
2334 /*********************************************************************
2335 *
2336 * EM_CHARFROMPOS
2337 *
2338 * returns line number (not index) in high-order word of result.
2339 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2340 * to Richedit, not to the edit control. Original documentation is valid.
2341 * FIXME: do the specs mean to return -1 if outside client area or
2342 * if outside formatting rectangle ???
2343 *
2344 */
2345 static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y)
2346 {
2347 POINT pt;
2348 RECT rc;
2349 INT index;
2350
2351 pt.x = x;
2352 pt.y = y;
2353 GetClientRect(es->hwndSelf, &rc);
2354 if (!PtInRect(&rc, pt))
2355 return -1;
2356
2357 index = EDIT_CharFromPos(es, x, y, NULL);
2358 return MAKELONG(index, EDIT_EM_LineFromChar(es, index));
2359 }
2360
2361
2362 /*********************************************************************
2363 *
2364 * EM_FMTLINES
2365 *
2366 * Enable or disable soft breaks.
2367 *
2368 * This means: insert or remove the soft linebreak character (\r\r\n).
2369 * Take care to check if the text still fits the buffer after insertion.
2370 * If not, notify with EN_ERRSPACE.
2371 *
2372 */
2373 static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol)
2374 {
2375 es->flags &= ~EF_USE_SOFTBRK;
2376 if (add_eol) {
2377 es->flags |= EF_USE_SOFTBRK;
2378 FIXME("soft break enabled, not implemented\n");
2379 }
2380 return add_eol;
2381 }
2382
2383
2384 /*********************************************************************
2385 *
2386 * EM_GETHANDLE
2387 *
2388 * Hopefully this won't fire back at us.
2389 * We always start with a fixed buffer in the local heap.
2390 * Despite of the documentation says that the local heap is used
2391 * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2392 * buffer on the local heap.
2393 *
2394 */
2395 static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es)
2396 {
2397 HLOCAL hLocal;
2398
2399 if (!(es->style & ES_MULTILINE))
2400 return 0;
2401
2402 if(es->is_unicode)
2403 hLocal = es->hloc32W;
2404 else
2405 {
2406 if(!es->hloc32A)
2407 {
2408 CHAR *textA;
2409 UINT countA, alloc_size;
2410 TRACE("Allocating 32-bit ANSI alias buffer\n");
2411 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2412 alloc_size = ROUND_TO_GROW(countA);
2413 if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2414 {
2415 ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2416 return 0;
2417 }
2418 textA = LocalLock(es->hloc32A);
2419 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2420 LocalUnlock(es->hloc32A);
2421 }
2422 hLocal = es->hloc32A;
2423 }
2424
2425 es->flags |= EF_APP_HAS_HANDLE;
2426 TRACE("Returning %p, LocalSize() = %ld\n", hLocal, LocalSize(hLocal));
2427 return hLocal;
2428 }
2429
2430
2431 #ifndef __REACTOS__
2432 /*********************************************************************
2433 *
2434 * EM_GETHANDLE16
2435 *
2436 * Hopefully this won't fire back at us.
2437 * We always start with a buffer in 32 bit linear memory.
2438 * However, with this message a 16 bit application requests
2439 * a handle of 16 bit local heap memory, where it expects to find
2440 * the text.
2441 * It's a pitty that from this moment on we have to use this
2442 * local heap, because applications may rely on the handle
2443 * in the future.
2444 *
2445 * In this function we'll try to switch to local heap.
2446 */
2447 static HLOCAL16 EDIT_EM_GetHandle16(EDITSTATE *es)
2448 {
2449 CHAR *textA;
2450 UINT countA, alloc_size;
2451 STACK16FRAME* stack16;
2452 HANDLE16 oldDS;
2453
2454 if (!(es->style & ES_MULTILINE))
2455 return 0;
2456
2457 if (es->hloc16)
2458 return es->hloc16;
2459
2460 stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2461 oldDS = stack16->ds;
2462 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2463
2464 if (!LocalHeapSize16()) {
2465
2466 if (!LocalInit16(stack16->ds, 0, GlobalSize16(stack16->ds))) {
2467 ERR("could not initialize local heap\n");
2468 goto done;
2469 }
2470 TRACE("local heap initialized\n");
2471 }
2472
2473 countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2474 alloc_size = ROUND_TO_GROW(countA);
2475
2476 TRACE("Allocating 16-bit ANSI alias buffer\n");
2477 if (!(es->hloc16 = LocalAlloc16(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size))) {
2478 ERR("could not allocate new 16 bit buffer\n");
2479 goto done;
2480 }
2481
2482 if (!(textA = MapSL(LocalLock16( es->hloc16)))) {
2483 ERR("could not lock new 16 bit buffer\n");
2484 LocalFree16(es->hloc16);
2485 es->hloc16 = 0;
2486 goto done;
2487 }
2488
2489 WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2490 LocalUnlock16(es->hloc16);
2491 es->flags |= EF_APP_HAS_HANDLE;
2492
2493 TRACE("Returning %04X, LocalSize() = %d\n", es->hloc16, LocalSize16(es->hloc16));
2494
2495 done:
2496 stack16->ds = oldDS;
2497 return es->hloc16;
2498 }
2499 #endif
2500
2501
2502 /*********************************************************************
2503 *
2504 * EM_GETLINE
2505 *
2506 */
2507 static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst, BOOL unicode)
2508 {
2509 LPWSTR src;
2510 INT line_len, dst_len;
2511 INT i;
2512
2513 if (es->style & ES_MULTILINE) {
2514 if (line >= es->line_count)
2515 return 0;
2516 } else
2517 line = 0;
2518 i = EDIT_EM_LineIndex(es, line);
2519 src = es->text + i;
2520 line_len = EDIT_EM_LineLength(es, i);
2521 dst_len = *(WORD *)dst;
2522 if(unicode)
2523 {
2524 if(dst_len <= line_len)
2525 {
2526 memcpy(dst, src, dst_len * sizeof(WCHAR));
2527 return dst_len;
2528 }
2529 else /* Append 0 if enough space */
2530 {
2531 memcpy(dst, src, line_len * sizeof(WCHAR));
2532 dst[line_len] = 0;
2533 return line_len;
2534 }
2535 }
2536 else
2537 {
2538 INT ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, (LPSTR)dst, dst_len, NULL, NULL);
2539 if(!ret && line_len) /* Insufficient buffer size */
2540 return dst_len;
2541 if(ret < dst_len) /* Append 0 if enough space */
2542 ((LPSTR)dst)[ret] = 0;
2543 return ret;
2544 }
2545 }
2546
2547
2548 /*********************************************************************
2549 *
2550 * EM_GETSEL
2551 *
2552 */
2553 static LRESULT EDIT_EM_GetSel(const EDITSTATE *es, PUINT start, PUINT end)
2554 {
2555 UINT s = es->selection_start;
2556 UINT e = es->selection_end;
2557
2558 ORDER_UINT(s, e);
2559 if (start)
2560 *start = s;
2561 if (end)
2562 *end = e;
2563 return MAKELONG(s, e);
2564 }
2565
2566
2567 /*********************************************************************
2568 *
2569 * EM_REPLACESEL
2570 *
2571 * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2572 *
2573 */
2574 static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update, BOOL honor_limit)
2575 {
2576 UINT strl = strlenW(lpsz_replace);
2577 UINT tl = get_text_length(es);
2578 UINT utl;
2579 UINT s;
2580 UINT e;
2581 UINT i;
2582 UINT size;
2583 LPWSTR p;
2584 HRGN hrgn = 0;
2585 LPWSTR buf = NULL;
2586 UINT bufl = 0;
2587
2588 TRACE("%s, can_undo %d, send_update %d\n",
2589 debugstr_w(lpsz_replace), can_undo, send_update);
2590
2591 s = es->selection_start;
2592 e = es->selection_end;
2593
2594 if ((s == e) && !strl)
2595 return;
2596
2597 ORDER_UINT(s, e);
2598
2599 size = tl - (e - s) + strl;
2600 if (!size)
2601 es->text_width = 0;
2602
2603 /* Issue the EN_MAXTEXT notification and continue with replacing text
2604 * such that buffer limit is honored. */
2605 if ((honor_limit) && (size > es->buffer_limit)) {
2606 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2607 /* Buffer limit can be smaller than the actual length of text in combobox */
2608 if (es->buffer_limit < (tl - (e-s)))
2609 strl = 0;
2610 else
2611 strl = es->buffer_limit - (tl - (e-s));
2612 }
2613
2614 if (!EDIT_MakeFit(es, tl - (e - s) + strl))
2615 return;
2616
2617 if (e != s) {
2618 /* there is something to be deleted */
2619 TRACE("deleting stuff.\n");
2620 bufl = e - s;
2621 buf = HeapAlloc(GetProcessHeap(), 0, (bufl + 1) * sizeof(WCHAR));
2622 if (!buf) return;
2623 memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
2624 buf[bufl] = 0; /* ensure 0 termination */
2625 /* now delete */
2626 strcpyW(es->text + s, es->text + e);
2627 text_buffer_changed(es);
2628 }
2629 if (strl) {
2630 /* there is an insertion */
2631 tl = get_text_length(es);
2632 TRACE("inserting stuff (tl %d, strl %d, selstart %d (%s), text %s)\n", tl, strl, s, debugstr_w(es->text + s), debugstr_w(es->text));
2633 for (p = es->text + tl ; p >= es->text + s ; p--)
2634 p[strl] = p[0];
2635 for (i = 0 , p = es->text + s ; i < strl ; i++)
2636 p[i] = lpsz_replace[i];
2637 if(es->style & ES_UPPERCASE)
2638 CharUpperBuffW(p, strl);
2639 else if(es->style & ES_LOWERCASE)
2640 CharLowerBuffW(p, strl);
2641 text_buffer_changed(es);
2642 }
2643 if (es->style & ES_MULTILINE)
2644 {
2645 INT st = min(es->selection_start, es->selection_end);
2646 INT vlc = get_vertical_line_count(es);
2647
2648 hrgn = CreateRectRgn(0, 0, 0, 0);
2649 EDIT_BuildLineDefs_ML(es, st, st + strl,
2650 strl - abs(es->selection_end - es->selection_start), hrgn);
2651 /* if text is too long undo all changes */
2652 if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
2653 if (strl)
2654 strcpyW(es->text + e, es->text + e + strl);
2655 if (e != s)
2656 for (i = 0 , p = es->text ; i < e - s ; i++)
2657 p[i + s] = buf[i];
2658 text_buffer_changed(es);
2659 EDIT_BuildLineDefs_ML(es, s, e,
2660 abs(es->selection_end - es->selection_start) - strl, hrgn);
2661 strl = 0;
2662 e = s;
2663 hrgn = CreateRectRgn(0, 0, 0, 0);
2664 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2665 }
2666 }
2667 else {
2668 INT fw = es->format_rect.right - es->format_rect.left;
2669 EDIT_CalcLineWidth_SL(es);
2670 /* remove chars that don't fit */
2671 if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
2672 while ((es->text_width > fw) && s + strl >= s) {
2673 strcpyW(es->text + s + strl - 1, es->text + s + strl);
2674 strl--;
2675 EDIT_CalcLineWidth_SL(es);
2676 }
2677 text_buffer_changed(es);
2678 EDIT_NOTIFY_PARENT(es, EN_MAXTEXT);
2679 }
2680 }
2681
2682 if (e != s) {
2683 if (can_undo) {
2684 utl = strlenW(es->undo_text);
2685 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2686 /* undo-buffer is extended to the right */
2687 EDIT_MakeUndoFit(es, utl + e - s);
2688 memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR));
2689 (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2690 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2691 /* undo-buffer is extended to the left */
2692 EDIT_MakeUndoFit(es, utl + e - s);
2693 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2694 p[e - s] = p[0];
2695 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2696 p[i] = buf[i];
2697 es->undo_position = s;
2698 } else {
2699 /* new undo-buffer */
2700 EDIT_MakeUndoFit(es, e - s);
2701 memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR));
2702 es->undo_text[e - s] = 0; /* ensure 0 termination */
2703 es->undo_position = s;
2704 }
2705 /* any deletion makes the old insertion-undo invalid */
2706 es->undo_insert_count = 0;
2707 } else
2708 EDIT_EM_EmptyUndoBuffer(es);
2709 }
2710 if (strl) {
2711 if (can_undo) {
2712 if ((s == es->undo_position) ||
2713 ((es->undo_insert_count) &&
2714 (s == es->undo_position + es->undo_insert_count)))
2715 /*
2716 * insertion is new and at delete position or
2717 * an extension to either left or right
2718 */
2719 es->undo_insert_count += strl;
2720 else {
2721 /* new insertion undo */
2722 es->undo_position = s;
2723 es->undo_insert_count = strl;
2724 /* new insertion makes old delete-buffer invalid */
2725 *es->undo_text = '\0';
2726 }
2727 } else
2728 EDIT_EM_EmptyUndoBuffer(es);
2729 }
2730
2731 if (bufl)
2732 HeapFree(GetProcessHeap(), 0, buf);
2733
2734 s += strl;
2735
2736 /* If text has been deleted and we're right or center aligned then scroll rightward */
2737 if (es->style & (ES_RIGHT | ES_CENTER))
2738 {
2739 INT delta = strl - abs(es->selection_end - es->selection_start);
2740
2741 if (delta < 0 && es->x_offset)
2742 {
2743 if (abs(delta) > es->x_offset)
2744 es->x_offset = 0;
2745 else
2746 es->x_offset += delta;
2747 }
2748 }
2749
2750 EDIT_EM_SetSel(es, s, s, FALSE);
2751 es->flags |= EF_MODIFIED;
2752 if (send_update) es->flags |= EF_UPDATE;
2753 if (hrgn)
2754 {
2755 EDIT_UpdateTextRegion(es, hrgn, TRUE);
2756 DeleteObject(hrgn);
2757 }
2758 else
2759 EDIT_UpdateText(es, NULL, TRUE);
2760
2761 EDIT_EM_ScrollCaret(es);
2762
2763 /* force scroll info update */
2764 EDIT_UpdateScrollInfo(es);
2765
2766
2767 if(send_update || (es->flags & EF_UPDATE))
2768 {
2769 es->flags &= ~EF_UPDATE;
2770 EDIT_NOTIFY_PARENT(es, EN_CHANGE);
2771 }
2772 }
2773
2774
2775 /*********************************************************************
2776 *
2777 * EM_SETHANDLE
2778 *
2779 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2780 *
2781 */
2782 static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc)
2783 {
2784 if (!(es->style & ES_MULTILINE))
2785 return;
2786
2787 if (!hloc) {
2788 WARN("called with NULL handle\n");
2789 return;
2790 }
2791
2792 EDIT_UnlockBuffer(es, TRUE);
2793
2794 #ifndef __REACTOS__
2795 if(es->hloc16)
2796 {
2797 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2798 HANDLE16 oldDS = stack16->ds;
2799
2800 stack16->ds = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2801 LocalFree16(es->hloc16);
2802 stack16->ds = oldDS;
2803 es->hloc16 = 0;
2804 }
2805 #endif
2806
2807 if(es->is_unicode)
2808 {
2809 if(es->hloc32A)
2810 {
2811 LocalFree(es->hloc32A);
2812 es->hloc32A = NULL;
2813 }
2814 es->hloc32W = hloc;
2815 }
2816 else
2817 {
2818 INT countW, countA;
2819 HLOCAL hloc32W_new;
2820 WCHAR *textW;
2821 CHAR *textA;
2822
2823 countA = LocalSize(hloc);
2824 textA = LocalLock(hloc);
2825 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
2826 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
2827 {
2828 ERR("Could not allocate new unicode buffer\n");
2829 return;
2830 }
2831 textW = LocalLock(hloc32W_new);
2832 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
2833 LocalUnlock(hloc32W_new);
2834 LocalUnlock(hloc);
2835
2836 if(es->hloc32W)
2837 LocalFree(es->hloc32W);
2838
2839 es->hloc32W = hloc32W_new;
2840 es->hloc32A = hloc;
2841 }
2842
2843 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2844
2845 es->flags |= EF_APP_HAS_HANDLE;
2846 EDIT_LockBuffer(es);
2847
2848 es->x_offset = es->y_offset = 0;
2849 es->selection_start = es->selection_end = 0;
2850 EDIT_EM_EmptyUndoBuffer(es);
2851 es->flags &= ~EF_MODIFIED;
2852 es->flags &= ~EF_UPDATE;
2853 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2854 EDIT_UpdateText(es, NULL, TRUE);
2855 EDIT_EM_ScrollCaret(es);
2856 /* force scroll info update */
2857 EDIT_UpdateScrollInfo(es);
2858 }
2859
2860
2861 #ifndef __REACTOS__
2862 /*********************************************************************
2863 *
2864 * EM_SETHANDLE16
2865 *
2866 * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2867 *
2868 */
2869 static void EDIT_EM_SetHandle16(EDITSTATE *es, HLOCAL16 hloc)
2870 {
2871 STACK16FRAME* stack16 = MapSL(PtrToUlong(NtCurrentTeb()->WOW32Reserved));
2872 HINSTANCE16 hInstance = GetWindowLongPtrW( es->hwndSelf, GWLP_HINSTANCE );
2873 HANDLE16 oldDS = stack16->ds;
2874 INT countW, countA;
2875 HLOCAL hloc32W_new;
2876 WCHAR *textW;
2877 CHAR *textA;
2878
2879 if (!(es->style & ES_MULTILINE))
2880 return;
2881
2882 if (!hloc) {
2883 WARN("called with NULL handle\n");
2884 return;
2885 }
2886
2887 EDIT_UnlockBuffer(es, TRUE);
2888
2889 if(es->hloc32A)
2890 {
2891 LocalFree(es->hloc32A);
2892 es->hloc32A = NULL;
2893 }
2894
2895 stack16->ds = hInstance;
2896 countA = LocalSize16(hloc);
2897 textA = MapSL(LocalLock16(hloc));
2898 countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
2899 if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
2900 {
2901 ERR("Could not allocate new unicode buffer\n");
2902 return;
2903 }
2904 textW = LocalLock(hloc32W_new);
2905 MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
2906 LocalUnlock(hloc32W_new);
2907 LocalUnlock16(hloc);
2908 stack16->ds = oldDS;
2909
2910 if(es->hloc32W)
2911 LocalFree(es->hloc32W);
2912
2913 es->hloc32W = hloc32W_new;
2914 es->hloc16 = hloc;
2915
2916 es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2917
2918 es->flags |= EF_APP_HAS_HANDLE;
2919 EDIT_LockBuffer(es);
2920
2921 es->x_offset = es->y_offset = 0;
2922 es->selection_start = es->selection_end = 0;
2923 EDIT_EM_EmptyUndoBuffer(es);
2924 es->flags &= ~EF_MODIFIED;
2925 es->flags &= ~EF_UPDATE;
2926 EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
2927 EDIT_UpdateText(es, NULL, TRUE);
2928 EDIT_EM_ScrollCaret(es);
2929 /* force scroll info update */
2930 EDIT_UpdateScrollInfo(es);
2931 }
2932 #endif
2933
2934
2935 /*********************************************************************
2936 *
2937 * EM_SETLIMITTEXT
2938 *
2939 * NOTE: this version currently implements WinNT limits
2940 *
2941 */
2942 static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
2943 {
2944 if (!limit) limit = ~0u;
2945 if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe);
2946 es->buffer_limit = limit;
2947 }
2948
2949
2950 /*********************************************************************
2951 *
2952 * EM_SETMARGINS
2953 *
2954 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2955 * action wParam despite what the docs say. EC_USEFONTINFO calculates the
2956 * margin according to the textmetrics of the current font.
2957 *
2958 * FIXME - With TrueType or vector fonts EC_USEFONTINFO currently sets one third
2959 * of the char's width as the margin, but this is not how Windows handles this.
2960 * For all other fonts Windows sets the margins to zero.
2961 *
2962 * FIXME - When EC_USEFONTINFO is used the margins only change if the
2963 * edit control is equal to or larger than a certain size.
2964 * Interestingly if one subtracts both the left and right margins from
2965 * this size one always seems to get an even number. The extents of
2966 * the (four character) string "'**'" match this quite closely, so
2967 * we'll use this until we come up with a better idea.
2968 */
2969 static int calc_min_set_margin_size(HDC dc, INT left, INT right)
2970 {
2971 WCHAR magic_string[] = {'\'','*','*','\'', 0};
2972 SIZE sz;
2973
2974 GetTextExtentPointW(dc, magic_string, sizeof(magic_string)/sizeof(WCHAR) - 1, &sz);
2975 return sz.cx + left + right;
2976 }
2977
2978 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
2979 WORD left, WORD right, BOOL repaint)
2980 {
2981 TEXTMETRICW tm;
2982 INT default_left_margin = 0; /* in pixels */
2983 INT default_right_margin = 0; /* in pixels */
2984
2985 /* Set the default margins depending on the font */
2986 if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
2987 HDC dc = GetDC(es->hwndSelf);
2988 HFONT old_font = SelectObject(dc, es->font);
2989 GetTextMetricsW(dc, &tm);
2990 /* The default margins are only non zero for TrueType or Vector fonts */
2991 if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
2992 int min_size;
2993 RECT rc;
2994 /* This must be calculated more exactly! But how? */
2995 default_left_margin = tm.tmAveCharWidth / 2;
2996 default_right_margin = tm.tmAveCharWidth / 2;
2997 min_size = calc_min_set_margin_size(dc, default_left_margin, default_right_margin);
2998 GetClientRect(es->hwndSelf, &rc);
2999 if(rc.right - rc.left < min_size) {
3000 default_left_margin = es->left_margin;
3001 default_right_margin = es->right_margin;
3002 }
3003 }
3004 SelectObject(dc, old_font);
3005 ReleaseDC(es->hwndSelf, dc);
3006 }
3007
3008 if (action & EC_LEFTMARGIN) {
3009 es->format_rect.left -= es->left_margin;
3010 if (left != EC_USEFONTINFO)
3011 es->left_margin = left;
3012 else
3013 es->left_margin = default_left_margin;
3014 es->format_rect.left += es->left_margin;
3015 }
3016
3017 if (action & EC_RIGHTMARGIN) {
3018 es->format_rect.right += es->right_margin;