ReactOS  0.4.15-dev-5615-gc3644fd
edit.c
Go to the documentation of this file.
1 /*
2  * Edit control
3  *
4  * Copyright David W. Metcalfe, 1994
5  * Copyright William Magro, 1995, 1996
6  * Copyright Frans van Dorsselaer, 1996, 1997
7  *
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 #define WIN32_LEAN_AND_MEAN
47 #include <usp10.h>
48 #ifdef __REACTOS__
49 #define ImmGetContext IMM_FN(ImmGetContext)
50 #define ImmGetCompositionStringW IMM_FN(ImmGetCompositionStringW)
51 #define ImmReleaseContext IMM_FN(ImmReleaseContext)
52 #endif
53 
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 typedef enum
77 {
78  END_0 = 0, /* line ends with terminating '\0' character */
79  END_WRAP, /* line is wrapped */
80  END_HARD, /* line ends with a hard return '\r\n' */
81  END_SOFT, /* line ends with a soft return '\r\r\n' */
82  END_RICH /* line ends with a single '\n' */
83 } LINE_END;
84 
85 typedef struct tagLINEDEF {
86  INT length; /* bruto length of a line in bytes */
87  INT net_length; /* netto length of a line in visible characters */
89  INT width; /* width of the line in pixels */
90  INT index; /* line index into the buffer */
91  SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data */
92  struct tagLINEDEF *next;
93 } LINEDEF;
94 
95 typedef struct
96 {
97  BOOL is_unicode; /* how the control was created */
98  LPWSTR text; /* the actual contents of the control */
99  UINT text_length; /* cached length of text buffer (in WCHARs) - use get_text_length() to retrieve */
100  UINT buffer_size; /* the size of the buffer in characters */
101  UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
102  HFONT font; /* NULL means standard system font */
103  INT x_offset; /* scroll offset for multi lines this is in pixels
104  for single lines it's in characters */
105 #ifdef __REACTOS__
106  DWORD dwCaretWidth;
107 #endif
108  INT line_height; /* height of a screen line in pixels */
109  INT char_width; /* average character width in pixels */
110  DWORD style; /* sane version of wnd->dwStyle */
111  WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
112  INT undo_insert_count; /* number of characters inserted in sequence */
113  UINT undo_position; /* character index of the insertion and deletion */
114  LPWSTR undo_text; /* deleted text */
115  UINT undo_buffer_size; /* size of the deleted text buffer */
116  INT selection_start; /* == selection_end if no selection */
117  INT selection_end; /* == current caret position */
118  WCHAR password_char; /* == 0 if no password char, and for multi line controls */
119  INT left_margin; /* in pixels */
120  INT right_margin; /* in pixels */
121  RECT format_rect;
122  INT text_width; /* width of the widest line in pixels for multi line controls
123  and just line width for single line controls */
124  INT region_posx; /* Position of cursor relative to region: */
125  INT region_posy; /* -1: to left, 0: within, 1: to right */
126  void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
127  INT line_count; /* number of lines */
128  INT y_offset; /* scroll offset in number of lines */
129  BOOL bCaptureState; /* flag indicating whether mouse was captured */
130  BOOL bEnableState; /* flag keeping the enable state */
131  HWND hwndSelf; /* the our window handle */
132  HWND hwndParent; /* Handle of parent for sending EN_* messages.
133  Even if parent will change, EN_* messages
134  should be sent to the first parent. */
135  HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
136  INT wheelDeltaRemainder; /* scroll wheel delta left over after scrolling whole lines */
137  /*
138  * only for multi line controls
139  */
140  INT lock_count; /* amount of re-entries in the EditWndProc */
141  INT tabs_count;
142  LPINT tabs;
143  LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
144  HLOCAL hloc32W; /* our unicode local memory block */
145  HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
146  or EM_SETHANDLE */
147  HLOCAL hlocapp; /* The text buffer handle belongs to the app */
148  /*
149  * IME Data
150  */
151  UINT composition_len; /* length of composition, 0 == no composition */
152  int composition_start; /* the character position for the composition */
153  /*
154  * Uniscribe Data
155  */
156  SCRIPT_LOGATTR *logAttr;
157  SCRIPT_STRING_ANALYSIS ssa; /* Uniscribe Data for single line controls */
158 } EDITSTATE;
159 
160 
161 #define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
162 #define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
163 
164 /* used for disabled or read-only edit control */
165 #define EDIT_NOTIFY_PARENT(es, wNotifyCode) \
166  do \
167  { /* Notify parent which has created this edit control */ \
168  TRACE("notification " #wNotifyCode " sent to hwnd=%p\n", es->hwndParent); \
169  SendMessageW(es->hwndParent, WM_COMMAND, \
170  MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \
171  (LPARAM)(es->hwndSelf)); \
172  } while(0)
173 
174 static const WCHAR empty_stringW[] = {0};
175 static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
176 
177 /*********************************************************************
178  *
179  * EM_CANUNDO
180  *
181  */
182 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
183 {
184  return (es->undo_insert_count || strlenW(es->undo_text));
185 }
186 
187 
188 /*********************************************************************
189  *
190  * EM_EMPTYUNDOBUFFER
191  *
192  */
194 {
195  es->undo_insert_count = 0;
196  *es->undo_text = '\0';
197 }
198 
199 
200 /**********************************************************************
201  * get_app_version
202  *
203  * Returns the window version in case Wine emulates a later version
204  * of windows than the application expects.
205  *
206  * In a number of cases when windows runs an application that was
207  * designed for an earlier windows version, windows reverts
208  * to "old" behaviour of that earlier version.
209  *
210  * An example is a disabled edit control that needs to be painted.
211  * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
212  * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
213  * applications with an expected version 0f 4.0 or higher.
214  *
215  */
216 static DWORD get_app_version(void)
217 {
218  static DWORD version;
219  if (!version)
220  {
221  DWORD dwEmulatedVersion;
223  DWORD dwProcVersion = GetProcessVersion(0);
224 
225  info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
226  GetVersionExW( &info );
227  dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
228  /* FIXME: this may not be 100% correct; see discussion on the
229  * wine developer list in Nov 1999 */
230  version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
231  }
232  return version;
233 }
234 
236 {
237  HBRUSH hbrush;
238  UINT msg;
239 
240  if ( get_app_version() >= 0x40000 && (!es->bEnableState || (es->style & ES_READONLY)))
242  else
244 
245  /* why do we notify to es->hwndParent, and we send this one to GetParent()? */
246 #ifdef __REACTOS__
247  /* ReactOS r54259 */
248  hbrush = GetControlBrush(es->hwndSelf, hdc, msg);
249 #else
250  hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
251  if (!hbrush)
252  hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
253 #endif
254  return hbrush;
255 }
256 
257 
259 {
260  if(es->text_length == (UINT)-1)
261  es->text_length = strlenW(es->text);
262  return es->text_length;
263 }
264 
265 
266 /*********************************************************************
267  *
268  * EDIT_WordBreakProc
269  *
270  * Find the beginning of words.
271  * Note: unlike the specs for a WordBreakProc, this function can
272  * only be called without linebreaks between s[0] up to
273  * s[count - 1]. Remember it is only called
274  * internally, so we can decide this for ourselves.
275  * Additionally we will always be breaking the full string.
276  *
277  */
279 {
280  INT ret = 0;
281 
282  TRACE("s=%p, index=%d, count=%d, action=%d\n", s, index, count, action);
283 
284  if(!s) return 0;
285 
286  if (!es->logAttr)
287  {
289 
290  memset(&psa,0,sizeof(SCRIPT_ANALYSIS));
291  psa.eScript = SCRIPT_UNDEFINED;
292 
293  es->logAttr = HeapAlloc(GetProcessHeap(), 0, sizeof(SCRIPT_LOGATTR) * get_text_length(es));
294  ScriptBreak(es->text, get_text_length(es), &psa, es->logAttr);
295  }
296 
297  switch (action) {
298  case WB_LEFT:
299  if (index)
300  index--;
301  while (index && !es->logAttr[index].fSoftBreak)
302  index--;
303  ret = index;
304  break;
305  case WB_RIGHT:
306  if (!count)
307  break;
308  while (index < count && s[index] && !es->logAttr[index].fSoftBreak)
309  index++;
310  ret = index;
311  break;
312  case WB_ISDELIMITER:
313  ret = es->logAttr[index].fWhiteSpace;
314  break;
315  default:
316  ERR("unknown action code, please report !\n");
317  break;
318  }
319  return ret;
320 }
321 
322 
323 /*********************************************************************
324  *
325  * EDIT_CallWordBreakProc
326  *
327  * Call appropriate WordBreakProc (internal or external).
328  *
329  * Note: The "start" argument should always be an index referring
330  * to es->text. The actual wordbreak proc might be
331  * 16 bit, so we can't always pass any 32 bit LPSTR.
332  * Hence we assume that es->text is the buffer that holds
333  * the string under examination (we can decide this for ourselves).
334  *
335  */
337 {
338  INT ret;
339 
340  if (es->word_break_proc)
341  {
342  if(es->is_unicode)
343  {
344  EDITWORDBREAKPROCW wbpW = (EDITWORDBREAKPROCW)es->word_break_proc;
345 
346  TRACE_(relay)("(UNICODE wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
347  es->word_break_proc, debugstr_wn(es->text + start, count), index, count, action);
348  ret = wbpW(es->text + start, index, count, action);
349  }
350  else
351  {
352  EDITWORDBREAKPROCA wbpA = (EDITWORDBREAKPROCA)es->word_break_proc;
353  INT countA;
354  CHAR *textA;
355 
356  countA = WideCharToMultiByte(CP_ACP, 0, es->text + start, count, NULL, 0, NULL, NULL);
357  textA = HeapAlloc(GetProcessHeap(), 0, countA);
358 #ifdef __REACTOS__
359  /* ReactOS r33503 */
360  if (textA == NULL) return 0;
361 #endif
362  WideCharToMultiByte(CP_ACP, 0, es->text + start, count, textA, countA, NULL, NULL);
363  TRACE_(relay)("(ANSI wordbrk=%p,str=%s,idx=%d,cnt=%d,act=%d)\n",
364  es->word_break_proc, debugstr_an(textA, countA), index, countA, action);
365  ret = wbpA(textA, index, countA, action);
367  }
368  }
369  else
371 
372  return ret;
373 }
374 
375 static inline void EDIT_InvalidateUniscribeData_linedef(LINEDEF *line_def)
376 {
377  if (line_def->ssa)
378  {
379  ScriptStringFree(&line_def->ssa);
380  line_def->ssa = NULL;
381  }
382 }
383 
385 {
386  LINEDEF *line_def = es->first_line_def;
387  while (line_def)
388  {
390  line_def = line_def->next;
391  }
392  if (es->ssa)
393  {
394  ScriptStringFree(&es->ssa);
395  es->ssa = NULL;
396  }
397 }
398 
400 {
401  if (!line_def)
402  return NULL;
403 
404  if (line_def->net_length && !line_def->ssa)
405  {
406  int index = line_def->index;
407  HFONT old_font = NULL;
408  HDC udc = dc;
409  SCRIPT_TABDEF tabdef;
410  HRESULT hr;
411 
412  if (!udc)
413  udc = GetDC(es->hwndSelf);
414  if (es->font)
415  old_font = SelectObject(udc, es->font);
416 
417  tabdef.cTabStops = es->tabs_count;
418  tabdef.iScale = 0;
419  tabdef.pTabStops = es->tabs;
420  tabdef.iTabOrigin = 0;
421 
422  hr = ScriptStringAnalyse(udc, &es->text[index], line_def->net_length,
423 #ifdef __REACTOS__
424  /* ReactOS r57679 */
425  (3*line_def->net_length/2+16), -1,
426 #else
427  (1.5*line_def->net_length+16), -1,
428 #endif
430  NULL, NULL, NULL, &tabdef, NULL, &line_def->ssa);
431  if (FAILED(hr))
432  {
433  WARN("ScriptStringAnalyse failed (%x)\n",hr);
434  line_def->ssa = NULL;
435  }
436 
437  if (es->font)
438  SelectObject(udc, old_font);
439  if (udc != dc)
440  ReleaseDC(es->hwndSelf, udc);
441  }
442 
443  return line_def->ssa;
444 }
445 
447 {
448  LINEDEF *line_def;
449 
450  if (!(es->style & ES_MULTILINE))
451  {
452  if (!es->ssa)
453  {
455  HFONT old_font = NULL;
456  HDC udc = dc;
457 
458  if (!udc)
459  udc = GetDC(es->hwndSelf);
460  if (es->font)
461  old_font = SelectObject(udc, es->font);
462 
463  if (es->style & ES_PASSWORD)
464 #ifdef __REACTOS__
465  /* ReactOS r57677 */
466  ScriptStringAnalyse(udc, &es->password_char, length, (3*length/2+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS|SSA_PASSWORD, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
467 #else
468  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);
469 #endif
470  else
471 #ifdef __REACTOS__
472  /* ReactOS r57677 */
473  ScriptStringAnalyse(udc, es->text, length, (3*length/2+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
474 #else
475  ScriptStringAnalyse(udc, es->text, length, (1.5*length+16), -1, SSA_LINK|SSA_FALLBACK|SSA_GLYPHS, -1, NULL, NULL, NULL, NULL, NULL, &es->ssa);
476 #endif
477 
478  if (es->font)
479  SelectObject(udc, old_font);
480  if (udc != dc)
481  ReleaseDC(es->hwndSelf, udc);
482  }
483  return es->ssa;
484  }
485  else
486  {
487  line_def = es->first_line_def;
488  while (line_def && line)
489  {
490  line_def = line_def->next;
491  line--;
492  }
493 
494  return EDIT_UpdateUniscribeData_linedef(es,dc,line_def);
495  }
496 }
497 
499 {
500  INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
501  return max(1,vlc);
502 }
503 
504 /*********************************************************************
505  *
506  * EDIT_BuildLineDefs_ML
507  *
508  * Build linked list of text lines.
509  * Lines can end with '\0' (last line), a character (if it is wrapped),
510  * a soft return '\r\r\n' or a hard return '\r\n'
511  *
512  */
513 static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta, HRGN hrgn)
514 {
515  LPWSTR current_position, cp;
516  INT fw;
517  LINEDEF *current_line;
518  LINEDEF *previous_line;
519  LINEDEF *start_line;
520  INT line_index = 0, nstart_line, nstart_index;
521  INT line_count = es->line_count;
522  INT orig_net_length;
523  RECT rc;
524  INT vlc;
525 
526  if (istart == iend && delta == 0)
527  return;
528 
529  previous_line = NULL;
530  current_line = es->first_line_def;
531 
532  /* Find starting line. istart must lie inside an existing line or
533  * at the end of buffer */
534  do {
535  if (istart < current_line->index + current_line->length ||
536  current_line->ending == END_0)
537  break;
538 
539  previous_line = current_line;
540  current_line = current_line->next;
541  line_index++;
542  } while (current_line);
543 
544  if (!current_line) /* Error occurred start is not inside previous buffer */
545  {
546  FIXME(" modification occurred outside buffer\n");
547  return;
548  }
549 
550  /* Remember start of modifications in order to calculate update region */
551  nstart_line = line_index;
552  nstart_index = current_line->index;
553 
554  /* We must start to reformat from the previous line since the modifications
555  * may have caused the line to wrap upwards. */
556  if (!(es->style & ES_AUTOHSCROLL) && line_index > 0)
557  {
558  line_index--;
559  current_line = previous_line;
560  }
561  start_line = current_line;
562 
563  fw = es->format_rect.right - es->format_rect.left;
564  current_position = es->text + current_line->index;
566  do {
567  if (current_line != start_line)
568  {
569  if (!current_line || current_line->index + delta > current_position - es->text)
570  {
571  /* The buffer has been expanded, create a new line and
572  insert it into the link list */
573  LINEDEF *new_line = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LINEDEF));
574 #ifdef __REACTOS__
575  /* ReactOS r33509 */
576  if (new_line == NULL)
577  break;
578 #endif
579  new_line->next = previous_line->next;
580  previous_line->next = new_line;
581  current_line = new_line;
582  es->line_count++;
583  }
584  else if (current_line->index + delta < current_position - es->text)
585  {
586  /* The previous line merged with this line so we delete this extra entry */
587  previous_line->next = current_line->next;
588  HeapFree(GetProcessHeap(), 0, current_line);
589  current_line = previous_line->next;
590  es->line_count--;
591  continue;
592  }
593  else /* current_line->index + delta == current_position */
594  {
595  if (current_position - es->text > iend)
596  break; /* We reached end of line modifications */
597  /* else recalculate this line */
598  }
599  }
600 
601  current_line->index = current_position - es->text;
602  orig_net_length = current_line->net_length;
603 
604  /* Find end of line */
605  cp = current_position;
606  while (*cp) {
607  if (*cp == '\n') break;
608  if ((*cp == '\r') && (*(cp + 1) == '\n'))
609  break;
610  cp++;
611  }
612 
613  /* Mark type of line termination */
614  if (!(*cp)) {
615  current_line->ending = END_0;
616  current_line->net_length = strlenW(current_position);
617  } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
618  current_line->ending = END_SOFT;
619  current_line->net_length = cp - current_position - 1;
620  } else if (*cp == '\n') {
621  current_line->ending = END_RICH;
622  current_line->net_length = cp - current_position;
623  } else {
624  current_line->ending = END_HARD;
625  current_line->net_length = cp - current_position;
626  }
627 
628  if (current_line->net_length)
629  {
630  const SIZE *sz;
632  EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
633  if (current_line->ssa)
634  {
635  sz = ScriptString_pSize(current_line->ssa);
636  /* Calculate line width */
637  current_line->width = sz->cx;
638  }
639  else current_line->width = es->char_width * current_line->net_length;
640  }
641  else current_line->width = 0;
642 
643  /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
644 
645 /* Line breaks just look back from the end and find the next break and try that. */
646 
647  if (!(es->style & ES_AUTOHSCROLL)) {
648  if (current_line->width > fw && fw > es->char_width) {
649 
650  INT prev, next;
651  int w;
652  const SIZE *sz;
653  float d;
654 
655  prev = current_line->net_length - 1;
656  w = current_line->net_length;
657  d = (float)current_line->width/(float)fw;
658  if (d > 1.2f) d -= 0.2f;
659  next = prev/d;
660  if (next >= prev) next = prev-1;
661  do {
662  prev = EDIT_CallWordBreakProc(es, current_position - es->text,
663  next, current_line->net_length, WB_LEFT);
664  current_line->net_length = prev;
666  EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
667  if (current_line->ssa)
668  sz = ScriptString_pSize(current_line->ssa);
669  else sz = 0;
670  if (sz)
671  current_line->width = sz->cx;
672  else
673  prev = 0;
674  next = prev - 1;
675  } while (prev && current_line->width > fw);
676  current_line->net_length = w;
677 
678  if (prev == 0) { /* Didn't find a line break so force a break */
679  INT *piDx;
680  const INT *count;
681 
683  EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
684 
685  if (current_line->ssa)
686  {
687  count = ScriptString_pcOutChars(current_line->ssa);
688  piDx = HeapAlloc(GetProcessHeap(),0,sizeof(INT) * (*count));
689  ScriptStringGetLogicalWidths(current_line->ssa,piDx);
690 
691  prev = current_line->net_length-1;
692  do {
693  current_line->width -= piDx[prev];
694  prev--;
695  } while ( prev > 0 && current_line->width > fw);
696  if (prev<=0)
697  prev = 1;
698  HeapFree(GetProcessHeap(),0,piDx);
699  }
700  else
701  prev = (fw / es->char_width);
702  }
703 
704  /* If the first line we are calculating, wrapped before istart, we must
705  * adjust istart in order for this to be reflected in the update region. */
706  if (current_line->index == nstart_index && istart > current_line->index + prev)
707  istart = current_line->index + prev;
708  /* else if we are updating the previous line before the first line we
709  * are re-calculating and it expanded */
710  else if (current_line == start_line &&
711  current_line->index != nstart_index && orig_net_length < prev)
712  {
713  /* Line expanded due to an upwards line wrap so we must partially include
714  * previous line in update region */
715  nstart_line = line_index;
716  nstart_index = current_line->index;
717  istart = current_line->index + orig_net_length;
718  }
719 
720  current_line->net_length = prev;
721  current_line->ending = END_WRAP;
722 
723  if (current_line->net_length > 0)
724  {
725  EDIT_UpdateUniscribeData_linedef(es, NULL, current_line);
726  if (current_line->ssa)
727  {
728  sz = ScriptString_pSize(current_line->ssa);
729  current_line->width = sz->cx;
730  }
731  else
732  current_line->width = 0;
733  }
734  else current_line->width = 0;
735  }
736  else if (current_line == start_line &&
737  current_line->index != nstart_index &&
738  orig_net_length < current_line->net_length) {
739  /* The previous line expanded but it's still not as wide as the client rect */
740  /* The expansion is due to an upwards line wrap so we must partially include
741  it in the update region */
742  nstart_line = line_index;
743  nstart_index = current_line->index;
744  istart = current_line->index + orig_net_length;
745  }
746  }
747 
748 
749  /* Adjust length to include line termination */
750  switch (current_line->ending) {
751  case END_SOFT:
752  current_line->length = current_line->net_length + 3;
753  break;
754  case END_RICH:
755  current_line->length = current_line->net_length + 1;
756  break;
757  case END_HARD:
758  current_line->length = current_line->net_length + 2;
759  break;
760  case END_WRAP:
761  case END_0:
762  current_line->length = current_line->net_length;
763  break;
764  }
765  es->text_width = max(es->text_width, current_line->width);
766  current_position += current_line->length;
767  previous_line = current_line;
768 
769  /* Discard data for non-visible lines. It will be calculated as needed */
770  if ((line_index < es->y_offset) || (line_index > es->y_offset + vlc))
772 
773  current_line = current_line->next;
774  line_index++;
775  } while (previous_line->ending != END_0);
776 
777  /* Finish adjusting line indexes by delta or remove hanging lines */
778  if (previous_line->ending == END_0)
779  {
780  LINEDEF *pnext = NULL;
781 
782  previous_line->next = NULL;
783  while (current_line)
784  {
785  pnext = current_line->next;
787  HeapFree(GetProcessHeap(), 0, current_line);
788  current_line = pnext;
789  es->line_count--;
790  }
791  }
792  else if (delta != 0)
793  {
794  while (current_line)
795  {
796  current_line->index += delta;
797  current_line = current_line->next;
798  }
799  }
800 
801  /* Calculate rest of modification rectangle */
802  if (hrgn)
803  {
804  HRGN tmphrgn;
805  /*
806  * We calculate two rectangles. One for the first line which may have
807  * an indent with respect to the format rect. The other is a format-width
808  * rectangle that spans the rest of the lines that changed or moved.
809  */
810  rc.top = es->format_rect.top + nstart_line * es->line_height -
811  (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
812  rc.bottom = rc.top + es->line_height;
813  if ((es->style & ES_CENTER) || (es->style & ES_RIGHT))
814  rc.left = es->format_rect.left;
815  else
816 #ifdef __REACTOS__ /* CORE-11475 */
817  rc.left = (short)LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
818 #else
819  rc.left = LOWORD(EDIT_EM_PosFromChar(es, nstart_index, FALSE));
820 #endif
821  rc.right = es->format_rect.right;
822  SetRectRgn(hrgn, rc.left, rc.top, rc.right, rc.bottom);
823 
824  rc.top = rc.bottom;
825  rc.left = es->format_rect.left;
826  rc.right = es->format_rect.right;
827  /*
828  * If lines were added or removed we must re-paint the remainder of the
829  * lines since the remaining lines were either shifted up or down.
830  */
831  if (line_count < es->line_count) /* We added lines */
832  rc.bottom = es->line_count * es->line_height;
833  else if (line_count > es->line_count) /* We removed lines */
834  rc.bottom = line_count * es->line_height;
835  else
836  rc.bottom = line_index * es->line_height;
837  rc.bottom += es->format_rect.top;
838  rc.bottom -= (es->y_offset * es->line_height); /* Adjust for vertical scrollbar */
839  tmphrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
840  CombineRgn(hrgn, hrgn, tmphrgn, RGN_OR);
841  DeleteObject(tmphrgn);
842  }
843 }
844 
845 /*********************************************************************
846  *
847  * EDIT_CalcLineWidth_SL
848  *
849  */
851 {
853  if (es->ssa)
854  {
855  const SIZE *size;
856  size = ScriptString_pSize(es->ssa);
857  es->text_width = size->cx;
858  }
859  else
860  es->text_width = 0;
861 }
862 
863 /*********************************************************************
864  *
865  * EDIT_CharFromPos
866  *
867  * Beware: This is not the function called on EM_CHARFROMPOS
868  * The position _can_ be outside the formatting / client
869  * rectangle
870  * The return value is only the character index
871  *
872  */
873 static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
874 {
875  INT index;
876 
877  if (es->style & ES_MULTILINE) {
878  int trailing;
879  INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
880  INT line_index = 0;
881  LINEDEF *line_def = es->first_line_def;
883  while ((line > 0) && line_def->next) {
884  line_index += line_def->length;
885  line_def = line_def->next;
886  line--;
887  }
888 
889  x += es->x_offset - es->format_rect.left;
890  if (es->style & ES_RIGHT)
891  x -= (es->format_rect.right - es->format_rect.left) - line_def->width;
892  else if (es->style & ES_CENTER)
893  x -= ((es->format_rect.right - es->format_rect.left) - line_def->width) / 2;
894  if (x >= line_def->width) {
895  if (after_wrap)
896  *after_wrap = (line_def->ending == END_WRAP);
897  return line_index + line_def->net_length;
898  }
900  if (after_wrap)
901  *after_wrap = FALSE;
902  return line_index;
903  }
904 
905  ScriptStringXtoCP(line_def->ssa, x , &index, &trailing);
906  if (trailing) index++;
907  index += line_index;
908  if (after_wrap)
909  *after_wrap = ((index == line_index + line_def->net_length) &&
910  (line_def->ending == END_WRAP));
911  } else {
912  INT xoff = 0;
913  INT trailing;
914  if (after_wrap)
915  *after_wrap = FALSE;
916  x -= es->format_rect.left;
917  if (!x)
918  return es->x_offset;
919 
920  if (!es->x_offset)
921  {
922  INT indent = (es->format_rect.right - es->format_rect.left) - es->text_width;
923  if (es->style & ES_RIGHT)
924  x -= indent;
925  else if (es->style & ES_CENTER)
926  x -= indent / 2;
927  }
928 
930  if (es->x_offset)
931  {
932  if (es->ssa)
933  {
934  if (es->x_offset>= get_text_length(es))
935  {
936  const SIZE *size;
937  size = ScriptString_pSize(es->ssa);
938  xoff = size->cx;
939  }
940  ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
941  }
942  else
943  xoff = 0;
944  }
945  if (x < 0)
946  {
947  if (x + xoff > 0 || !es->ssa)
948  {
949  ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
950  if (trailing) index++;
951  }
952  else
953  index = 0;
954  }
955  else
956  {
957  if (x)
958  {
959  const SIZE *size = NULL;
960  if (es->ssa)
961  size = ScriptString_pSize(es->ssa);
962  if (!size)
963  index = 0;
964  else if (x > size->cx)
966  else if (es->ssa)
967  {
968  ScriptStringXtoCP(es->ssa, x+xoff, &index, &trailing);
969  if (trailing) index++;
970  }
971  else
972  index = 0;
973  }
974  else
975  index = es->x_offset;
976  }
977  }
978  return index;
979 }
980 
981 
982 /*********************************************************************
983  *
984  * EDIT_ConfinePoint
985  *
986  * adjusts the point to be within the formatting rectangle
987  * (so CharFromPos returns the nearest _visible_ character)
988  *
989  */
990 static void EDIT_ConfinePoint(const EDITSTATE *es, LPINT x, LPINT y)
991 {
992  *x = min(max(*x, es->format_rect.left), es->format_rect.right - 1);
993  *y = min(max(*y, es->format_rect.top), es->format_rect.bottom - 1);
994 }
995 
996 
997 /*********************************************************************
998  *
999  * EM_LINEFROMCHAR
1000  *
1001  */
1003 {
1004  INT line;
1005  LINEDEF *line_def;
1006 
1007  if (!(es->style & ES_MULTILINE))
1008  return 0;
1009  if (index > (INT)get_text_length(es))
1010  return es->line_count - 1;
1011  if (index == -1)
1012  index = min(es->selection_start, es->selection_end);
1013 
1014  line = 0;
1015  line_def = es->first_line_def;
1016  index -= line_def->length;
1017  while ((index >= 0) && line_def->next) {
1018  line++;
1019  line_def = line_def->next;
1020  index -= line_def->length;
1021  }
1022  return line;
1023 }
1024 
1025 
1026 /*********************************************************************
1027  *
1028  * EM_LINEINDEX
1029  *
1030  */
1032 {
1033  INT line_index;
1034  const LINEDEF *line_def;
1035 
1036  if (!(es->style & ES_MULTILINE))
1037  return 0;
1038  if (line >= es->line_count)
1039  return -1;
1040 
1041  line_index = 0;
1042  line_def = es->first_line_def;
1043  if (line == -1) {
1044  INT index = es->selection_end - line_def->length;
1045  while ((index >= 0) && line_def->next) {
1046  line_index += line_def->length;
1047  line_def = line_def->next;
1048  index -= line_def->length;
1049  }
1050  } else {
1051  while (line > 0) {
1052  line_index += line_def->length;
1053  line_def = line_def->next;
1054  line--;
1055  }
1056  }
1057  return line_index;
1058 }
1059 
1060 
1061 /*********************************************************************
1062  *
1063  * EM_LINELENGTH
1064  *
1065  */
1067 {
1068  LINEDEF *line_def;
1069 
1070  if (!(es->style & ES_MULTILINE))
1071  return get_text_length(es);
1072 
1073  if (index == -1) {
1074  /* get the number of remaining non-selected chars of selected lines */
1075  INT32 l; /* line number */
1076  INT32 li; /* index of first char in line */
1077  INT32 count;
1078  l = EDIT_EM_LineFromChar(es, es->selection_start);
1079  /* # chars before start of selection area */
1080  count = es->selection_start - EDIT_EM_LineIndex(es, l);
1081  l = EDIT_EM_LineFromChar(es, es->selection_end);
1082  /* # chars after end of selection */
1083  li = EDIT_EM_LineIndex(es, l);
1084  count += li + EDIT_EM_LineLength(es, li) - es->selection_end;
1085  return count;
1086  }
1087  line_def = es->first_line_def;
1088  index -= line_def->length;
1089  while ((index >= 0) && line_def->next) {
1090  line_def = line_def->next;
1091  index -= line_def->length;
1092  }
1093  return line_def->net_length;
1094 }
1095 
1096 
1097 /*********************************************************************
1098  *
1099  * EM_POSFROMCHAR
1100  *
1101  */
1103 {
1105  INT l;
1106  INT li;
1107  INT x = 0;
1108  INT y = 0;
1109  INT w;
1110  INT lw;
1111  LINEDEF *line_def;
1112 
1113  index = min(index, len);
1114  if (es->style & ES_MULTILINE) {
1117 
1118  y = (l - es->y_offset) * es->line_height;
1119  li = EDIT_EM_LineIndex(es, l);
1120  if (after_wrap && (li == index) && l) {
1121  INT l2 = l - 1;
1122  line_def = es->first_line_def;
1123  while (l2) {
1124  line_def = line_def->next;
1125  l2--;
1126  }
1127  if (line_def->ending == END_WRAP) {
1128  l--;
1129  y -= es->line_height;
1130  li = EDIT_EM_LineIndex(es, l);
1131  }
1132  }
1133 
1134  line_def = es->first_line_def;
1135  while (line_def->index != li)
1136  line_def = line_def->next;
1137 
1138  lw = line_def->width;
1139  w = es->format_rect.right - es->format_rect.left;
1140  if (line_def->ssa)
1141  {
1142  ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x);
1143  x -= es->x_offset;
1144  }
1145  else
1146 #ifdef __REACTOS__ /* CORE-15780 */
1147  x = (lw > 0 ? es->x_offset : x - es->x_offset);
1148 #else
1149  x = es->x_offset;
1150 #endif
1151 
1152  if (es->style & ES_RIGHT)
1153  x = w - (lw - x);
1154  else if (es->style & ES_CENTER)
1155  x += (w - lw) / 2;
1156  } else {
1157  INT xoff = 0;
1158  INT xi = 0;
1160  if (es->x_offset)
1161  {
1162  if (es->ssa)
1163  {
1164  if (es->x_offset >= get_text_length(es))
1165  {
1166  int leftover = es->x_offset - get_text_length(es);
1167  if (es->ssa)
1168  {
1169  const SIZE *size;
1170  size = ScriptString_pSize(es->ssa);
1171  xoff = size->cx;
1172  }
1173  else
1174  xoff = 0;
1175  xoff += es->char_width * leftover;
1176  }
1177  else
1178  ScriptStringCPtoX(es->ssa, es->x_offset, FALSE, &xoff);
1179  }
1180  else
1181  xoff = 0;
1182  }
1183  if (index)
1184  {
1185  if (index >= get_text_length(es))
1186  {
1187  if (es->ssa)
1188  {
1189  const SIZE *size;
1190  size = ScriptString_pSize(es->ssa);
1191  xi = size->cx;
1192  }
1193  else
1194  xi = 0;
1195  }
1196  else if (es->ssa)
1197  ScriptStringCPtoX(es->ssa, index, FALSE, &xi);
1198  else
1199  xi = 0;
1200  }
1201  x = xi - xoff;
1202 
1203  if (index >= es->x_offset) {
1204  if (!es->x_offset && (es->style & (ES_RIGHT | ES_CENTER)))
1205  {
1206  w = es->format_rect.right - es->format_rect.left;
1207  if (w > es->text_width)
1208  {
1209  if (es->style & ES_RIGHT)
1210  x += w - es->text_width;
1211  else if (es->style & ES_CENTER)
1212  x += (w - es->text_width) / 2;
1213  }
1214  }
1215  }
1216  y = 0;
1217  }
1218  x += es->format_rect.left;
1219  y += es->format_rect.top;
1220  return MAKELONG((INT16)x, (INT16)y);
1221 }
1222 
1223 
1224 /*********************************************************************
1225  *
1226  * EDIT_GetLineRect
1227  *
1228  * Calculates the bounding rectangle for a line from a starting
1229  * column to an ending column.
1230  *
1231  */
1232 static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1233 {
1235  INT line_index = 0;
1236  INT pt1, pt2, pt3;
1237 
1238  if (es->style & ES_MULTILINE)
1239  {
1240  const LINEDEF *line_def = NULL;
1241  rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1242  if (line >= es->line_count)
1243  return;
1244 
1245  line_def = es->first_line_def;
1246  if (line == -1) {
1247  INT index = es->selection_end - line_def->length;
1248  while ((index >= 0) && line_def->next) {
1249  line_index += line_def->length;
1250  line_def = line_def->next;
1251  index -= line_def->length;
1252  }
1253  } else {
1254  while (line > 0) {
1255  line_index += line_def->length;
1256  line_def = line_def->next;
1257  line--;
1258  }
1259  }
1260  ssa = line_def->ssa;
1261  }
1262  else
1263  {
1264  line_index = 0;
1265  rc->top = es->format_rect.top;
1266  ssa = es->ssa;
1267  }
1268 
1269  rc->bottom = rc->top + es->line_height;
1270  pt1 = (scol == 0) ? es->format_rect.left : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + scol, TRUE));
1271  pt2 = (ecol == -1) ? es->format_rect.right : (short)LOWORD(EDIT_EM_PosFromChar(es, line_index + ecol, TRUE));
1272  if (ssa)
1273  {
1274  ScriptStringCPtoX(ssa, scol, FALSE, &pt3);
1275  pt3+=es->format_rect.left;
1276  }
1277  else pt3 = pt1;
1278  rc->right = max(max(pt1 , pt2),pt3);
1279  rc->left = min(min(pt1, pt2),pt3);
1280 }
1281 
1282 
1283 static inline void text_buffer_changed(EDITSTATE *es)
1284 {
1285  es->text_length = (UINT)-1;
1286 
1287  HeapFree( GetProcessHeap(), 0, es->logAttr );
1288  es->logAttr = NULL;
1290 }
1291 
1292 /*********************************************************************
1293  * EDIT_LockBuffer
1294  *
1295  */
1297 {
1298  if (es->hlocapp) return;
1299 
1300  if (!es->text) {
1301 
1302 #ifdef __REACTOS__
1303 /* FIXME: What is this ? */
1304  CHAR *textA = NULL; // ReactOS Hacked! r45670
1305  //UINT countA = 0;
1306 
1307  if(es->hloc32W)
1308  {
1309  if(es->hloc32A)
1310  {
1311  TRACE("Synchronizing with 32-bit ANSI buffer\n");
1312  textA = LocalLock(es->hloc32A);
1313  //countA = strlen(textA) + 1;
1314  }
1315  }
1316  else {
1317  ERR("no buffer ... please report\n");
1318  return;
1319  }
1320 
1321  if (textA)
1322  {
1323 #else
1324  if(!es->hloc32W) return;
1325 
1326  if(es->hloc32A)
1327  {
1328  CHAR *textA = LocalLock(es->hloc32A);
1329 #endif
1330  HLOCAL hloc32W_new;
1331  UINT countW_new = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
1332  if(countW_new > es->buffer_size + 1)
1333  {
1334  UINT alloc_size = ROUND_TO_GROW(countW_new * sizeof(WCHAR));
1335  TRACE("Resizing 32-bit UNICODE buffer from %d+1 to %d WCHARs\n", es->buffer_size, countW_new);
1336  hloc32W_new = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1337  if(hloc32W_new)
1338  {
1339  es->hloc32W = hloc32W_new;
1340  es->buffer_size = LocalSize(hloc32W_new)/sizeof(WCHAR) - 1;
1341  TRACE("Real new size %d+1 WCHARs\n", es->buffer_size);
1342  }
1343  else
1344  WARN("FAILED! Will synchronize partially\n");
1345  }
1346  es->text = LocalLock(es->hloc32W);
1347  MultiByteToWideChar(CP_ACP, 0, textA, -1, es->text, es->buffer_size + 1);
1348  LocalUnlock(es->hloc32A);
1349  }
1350  else es->text = LocalLock(es->hloc32W);
1351  }
1352  es->lock_count++;
1353 }
1354 
1355 
1356 /*********************************************************************
1357  *
1358  * EDIT_UnlockBuffer
1359  *
1360  */
1361 static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force)
1362 {
1363  if (es->hlocapp) return;
1364 
1365  /* Edit window might be already destroyed */
1366  if(!IsWindow(es->hwndSelf))
1367  {
1368  WARN("edit hwnd %p already destroyed\n", es->hwndSelf);
1369  return;
1370  }
1371 
1372  if (!es->lock_count) {
1373  ERR("lock_count == 0 ... please report\n");
1374  return;
1375  }
1376  if (!es->text) {
1377  ERR("es->text == 0 ... please report\n");
1378  return;
1379  }
1380 
1381  if (force || (es->lock_count == 1)) {
1382  if (es->hloc32W) {
1383  UINT countA = 0;
1384  UINT countW = get_text_length(es) + 1;
1385 
1386  if(es->hloc32A)
1387  {
1388  UINT countA_new = WideCharToMultiByte(CP_ACP, 0, es->text, countW, NULL, 0, NULL, NULL);
1389  TRACE("Synchronizing with 32-bit ANSI buffer\n");
1390  TRACE("%d WCHARs translated to %d bytes\n", countW, countA_new);
1391  countA = LocalSize(es->hloc32A);
1392  if(countA_new > countA)
1393  {
1394  HLOCAL hloc32A_new;
1395  UINT alloc_size = ROUND_TO_GROW(countA_new);
1396  TRACE("Resizing 32-bit ANSI buffer from %d to %d bytes\n", countA, alloc_size);
1397  hloc32A_new = LocalReAlloc(es->hloc32A, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT);
1398  if(hloc32A_new)
1399  {
1400  es->hloc32A = hloc32A_new;
1401  countA = LocalSize(hloc32A_new);
1402  TRACE("Real new size %d bytes\n", countA);
1403  }
1404  else
1405  WARN("FAILED! Will synchronize partially\n");
1406  }
1407  WideCharToMultiByte(CP_ACP, 0, es->text, countW,
1408  LocalLock(es->hloc32A), countA, NULL, NULL);
1409  LocalUnlock(es->hloc32A);
1410  }
1411 
1412  LocalUnlock(es->hloc32W);
1413  es->text = NULL;
1414  }
1415  else {
1416  ERR("no buffer ... please report\n");
1417  return;
1418  }
1419  }
1420  es->lock_count--;
1421 }
1422 
1423 
1424 /*********************************************************************
1425  *
1426  * EDIT_MakeFit
1427  *
1428  * Try to fit size + 1 characters in the buffer.
1429  */
1431 {
1432  HLOCAL hNew32W;
1433 
1435  return TRUE;
1436 
1437  TRACE("trying to ReAlloc to %d+1 characters\n", size);
1438 
1439  /* Force edit to unlock its buffer. es->text now NULL */
1441 
1442  if (es->hloc32W) {
1443  UINT alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1444  if ((hNew32W = LocalReAlloc(es->hloc32W, alloc_size, LMEM_MOVEABLE | LMEM_ZEROINIT))) {
1445  TRACE("Old 32 bit handle %p, new handle %p\n", es->hloc32W, hNew32W);
1446  es->hloc32W = hNew32W;
1447  es->buffer_size = LocalSize(hNew32W)/sizeof(WCHAR) - 1;
1448  }
1449  }
1450 
1452 
1453  if (es->buffer_size < size) {
1454  WARN("FAILED ! We now have %d+1\n", es->buffer_size);
1456  return FALSE;
1457  } else {
1458  TRACE("We now have %d+1\n", es->buffer_size);
1459  return TRUE;
1460  }
1461 }
1462 
1463 
1464 /*********************************************************************
1465  *
1466  * EDIT_MakeUndoFit
1467  *
1468  * Try to fit size + 1 bytes in the undo buffer.
1469  *
1470  */
1472 {
1473  UINT alloc_size;
1474 
1475  if (size <= es->undo_buffer_size)
1476  return TRUE;
1477 
1478  TRACE("trying to ReAlloc to %d+1\n", size);
1479 
1480  alloc_size = ROUND_TO_GROW((size + 1) * sizeof(WCHAR));
1481  if ((es->undo_text = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, es->undo_text, alloc_size))) {
1482  es->undo_buffer_size = alloc_size/sizeof(WCHAR) - 1;
1483  return TRUE;
1484  }
1485  else
1486  {
1487  WARN("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1488  return FALSE;
1489  }
1490 }
1491 
1492 
1493 /*********************************************************************
1494  *
1495  * EDIT_UpdateTextRegion
1496  *
1497  */
1498 static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase)
1499 {
1500  if (es->flags & EF_UPDATE) {
1501  es->flags &= ~EF_UPDATE;
1503  }
1504  InvalidateRgn(es->hwndSelf, hrgn, bErase);
1505 }
1506 
1507 
1508 /*********************************************************************
1509  *
1510  * EDIT_UpdateText
1511  *
1512  */
1513 static void EDIT_UpdateText(EDITSTATE *es, const RECT *rc, BOOL bErase)
1514 {
1515  if (es->flags & EF_UPDATE) {
1516  es->flags &= ~EF_UPDATE;
1518  }
1519  InvalidateRect(es->hwndSelf, rc, bErase);
1520 }
1521 
1522 /*********************************************************************
1523  *
1524  * EDIT_SL_InvalidateText
1525  *
1526  * Called from EDIT_InvalidateText().
1527  * Does the job for single-line controls only.
1528  *
1529  */
1531 {
1532  RECT line_rect;
1533  RECT rc;
1534 
1535  EDIT_GetLineRect(es, 0, start, end, &line_rect);
1536  if (IntersectRect(&rc, &line_rect, &es->format_rect))
1537  EDIT_UpdateText(es, &rc, TRUE);
1538 }
1539 
1540 /*********************************************************************
1541  *
1542  * EDIT_ML_InvalidateText
1543  *
1544  * Called from EDIT_InvalidateText().
1545  * Does the job for multi-line controls only.
1546  *
1547  */
1549 {
1552  INT el = EDIT_EM_LineFromChar(es, end);
1553  INT sc;
1554  INT ec;
1555  RECT rc1;
1556  RECT rcWnd;
1557  RECT rcLine;
1558  RECT rcUpdate;
1559  INT l;
1560 
1561  if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1562  return;
1563 
1564  sc = start - EDIT_EM_LineIndex(es, sl);
1565  ec = end - EDIT_EM_LineIndex(es, el);
1566  if (sl < es->y_offset) {
1567  sl = es->y_offset;
1568  sc = 0;
1569  }
1570  if (el > es->y_offset + vlc) {
1571  el = es->y_offset + vlc;
1573  }
1574  GetClientRect(es->hwndSelf, &rc1);
1575  IntersectRect(&rcWnd, &rc1, &es->format_rect);
1576  if (sl == el) {
1577  EDIT_GetLineRect(es, sl, sc, ec, &rcLine);
1578  if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1579  EDIT_UpdateText(es, &rcUpdate, TRUE);
1580  } else {
1581  EDIT_GetLineRect(es, sl, sc,
1583  EDIT_EM_LineIndex(es, sl)),
1584  &rcLine);
1585  if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1586  EDIT_UpdateText(es, &rcUpdate, TRUE);
1587  for (l = sl + 1 ; l < el ; l++) {
1588  EDIT_GetLineRect(es, l, 0,
1590  EDIT_EM_LineIndex(es, l)),
1591  &rcLine);
1592  if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1593  EDIT_UpdateText(es, &rcUpdate, TRUE);
1594  }
1595  EDIT_GetLineRect(es, el, 0, ec, &rcLine);
1596  if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1597  EDIT_UpdateText(es, &rcUpdate, TRUE);
1598  }
1599 }
1600 
1601 
1602 /*********************************************************************
1603  *
1604  * EDIT_InvalidateText
1605  *
1606  * Invalidate the text from offset start up to, but not including,
1607  * offset end. Useful for (re)painting the selection.
1608  * Regions outside the linewidth are not invalidated.
1609  * end == -1 means end == TextLength.
1610  * start and end need not be ordered.
1611  *
1612  */
1614 {
1615  if (end == start)
1616  return;
1617 
1618  if (end == -1)
1619  end = get_text_length(es);
1620 
1621  if (end < start) {
1622  INT tmp = start;
1623  start = end;
1624  end = tmp;
1625  }
1626 
1627  if (es->style & ES_MULTILINE)
1629  else
1631 }
1632 
1633 
1634 /*********************************************************************
1635  *
1636  * EDIT_EM_SetSel
1637  *
1638  * note: unlike the specs say: the order of start and end
1639  * _is_ preserved in Windows. (i.e. start can be > end)
1640  * In other words: this handler is OK
1641  *
1642  */
1643 static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
1644 {
1645  UINT old_start = es->selection_start;
1646  UINT old_end = es->selection_end;
1648 
1649  if (start == (UINT)-1) {
1650  start = es->selection_end;
1651  end = es->selection_end;
1652  } else {
1653  start = min(start, len);
1654  end = min(end, len);
1655  }
1656  es->selection_start = start;
1657  es->selection_end = end;
1658  if (after_wrap)
1659  es->flags |= EF_AFTER_WRAP;
1660  else
1661  es->flags &= ~EF_AFTER_WRAP;
1662  /* Compute the necessary invalidation region. */
1663  /* Note that we don't need to invalidate regions which have
1664  * "never" been selected, or those which are "still" selected.
1665  * In fact, every time we hit a selection boundary, we can
1666  * *toggle* whether we need to invalidate. Thus we can optimize by
1667  * *sorting* the interval endpoints. Let's assume that we sort them
1668  * in this order:
1669  * start <= end <= old_start <= old_end
1670  * Knuth 5.3.1 (p 183) assures us that this can be done optimally
1671  * in 5 comparisons; i.e. it is impossible to do better than the
1672  * following: */
1673  ORDER_UINT(end, old_end);
1674  ORDER_UINT(start, old_start);
1675  ORDER_UINT(old_start, old_end);
1676  ORDER_UINT(start, end);
1677  /* Note that at this point 'end' and 'old_start' are not in order, but
1678  * start is definitely the min. and old_end is definitely the max. */
1679  if (end != old_start)
1680  {
1681 /*
1682  * One can also do
1683  * ORDER_UINT32(end, old_start);
1684  * EDIT_InvalidateText(es, start, end);
1685  * EDIT_InvalidateText(es, old_start, old_end);
1686  * in place of the following if statement.
1687  * (That would complete the optimal five-comparison four-element sort.)
1688  */
1689  if (old_start > end )
1690  {
1692  EDIT_InvalidateText(es, old_start, old_end);
1693  }
1694  else
1695  {
1696  EDIT_InvalidateText(es, start, old_start);
1697  EDIT_InvalidateText(es, end, old_end);
1698  }
1699  }
1700  else EDIT_InvalidateText(es, start, old_end);
1701 }
1702 
1703 
1704 /*********************************************************************
1705  *
1706  * EDIT_UpdateScrollInfo
1707  *
1708  */
1710 {
1711  if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
1712  {
1713  SCROLLINFO si;
1714  si.cbSize = sizeof(SCROLLINFO);
1716  si.nMin = 0;
1717  si.nMax = es->line_count - 1;
1718  si.nPage = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1719  si.nPos = es->y_offset;
1720  TRACE("SB_VERT, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1721  si.nMin, si.nMax, si.nPage, si.nPos);
1722  SetScrollInfo(es->hwndSelf, SB_VERT, &si, TRUE);
1723  }
1724 
1725  if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
1726  {
1727  SCROLLINFO si;
1728  si.cbSize = sizeof(SCROLLINFO);
1730  si.nMin = 0;
1731  si.nMax = es->text_width - 1;
1732  si.nPage = es->format_rect.right - es->format_rect.left;
1733  si.nPos = es->x_offset;
1734  TRACE("SB_HORZ, nMin=%d, nMax=%d, nPage=%d, nPos=%d\n",
1735  si.nMin, si.nMax, si.nPage, si.nPos);
1736  SetScrollInfo(es->hwndSelf, SB_HORZ, &si, TRUE);
1737  }
1738 }
1739 
1740 
1741 /*********************************************************************
1742  *
1743  * EDIT_EM_LineScroll_internal
1744  *
1745  * Version of EDIT_EM_LineScroll for internal use.
1746  * It doesn't refuse if ES_MULTILINE is set and assumes that
1747  * dx is in pixels, dy - in lines.
1748  *
1749  */
1751 {
1752  INT nyoff;
1753  INT x_offset_in_pixels;
1754  INT lines_per_page = (es->format_rect.bottom - es->format_rect.top) /
1755  es->line_height;
1756 
1757  if (es->style & ES_MULTILINE)
1758  {
1759  x_offset_in_pixels = es->x_offset;
1760  }
1761  else
1762  {
1763  dy = 0;
1764  x_offset_in_pixels = (short)LOWORD(EDIT_EM_PosFromChar(es, es->x_offset, FALSE));
1765  }
1766 
1767  if (-dx > x_offset_in_pixels)
1768  dx = -x_offset_in_pixels;
1769  if (dx > es->text_width - x_offset_in_pixels)
1770  dx = es->text_width - x_offset_in_pixels;
1771  nyoff = max(0, es->y_offset + dy);
1772  if (nyoff >= es->line_count - lines_per_page)
1773  nyoff = max(0, es->line_count - lines_per_page);
1774  dy = (es->y_offset - nyoff) * es->line_height;
1775  if (dx || dy) {
1776  RECT rc1;
1777  RECT rc;
1778 
1779  es->y_offset = nyoff;
1780  if(es->style & ES_MULTILINE)
1781  es->x_offset += dx;
1782  else
1783  es->x_offset += dx / es->char_width;
1784 
1785  GetClientRect(es->hwndSelf, &rc1);
1786  IntersectRect(&rc, &rc1, &es->format_rect);
1787  ScrollWindowEx(es->hwndSelf, -dx, dy,
1788  NULL, &rc, NULL, NULL, SW_INVALIDATE);
1789  /* force scroll info update */
1791  }
1792  if (dx && !(es->flags & EF_HSCROLL_TRACK))
1794  if (dy && !(es->flags & EF_VSCROLL_TRACK))
1796  return TRUE;
1797 }
1798 
1799 /*********************************************************************
1800  *
1801  * EM_LINESCROLL
1802  *
1803  * NOTE: dx is in average character widths, dy - in lines;
1804  *
1805  */
1807 {
1808  if (!(es->style & ES_MULTILINE))
1809  return FALSE;
1810 
1811  dx *= es->char_width;
1812  return EDIT_EM_LineScroll_internal(es, dx, dy);
1813 }
1814 
1815 
1816 /*********************************************************************
1817  *
1818  * EM_SCROLL
1819  *
1820  */
1822 {
1823  INT dy;
1824 
1825  if (!(es->style & ES_MULTILINE))
1826  return (LRESULT)FALSE;
1827 
1828  dy = 0;
1829 
1830  switch (action) {
1831  case SB_LINEUP:
1832  if (es->y_offset)
1833  dy = -1;
1834  break;
1835  case SB_LINEDOWN:
1836  if (es->y_offset < es->line_count - 1)
1837  dy = 1;
1838  break;
1839  case SB_PAGEUP:
1840  if (es->y_offset)
1841  dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
1842  break;
1843  case SB_PAGEDOWN:
1844  if (es->y_offset < es->line_count - 1)
1845  dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1846  break;
1847  default:
1848  return (LRESULT)FALSE;
1849  }
1850  if (dy) {
1852  /* check if we are going to move too far */
1853  if(es->y_offset + dy > es->line_count - vlc)
1854  dy = max(es->line_count - vlc, 0) - es->y_offset;
1855 
1856  /* Notification is done in EDIT_EM_LineScroll */
1857  if(dy) {
1858  EDIT_EM_LineScroll(es, 0, dy);
1859  return MAKELONG(dy, TRUE);
1860  }
1861 
1862  }
1863  return (LRESULT)FALSE;
1864 }
1865 
1866 
1867 /*********************************************************************
1868  *
1869  * EDIT_SetCaretPos
1870  *
1871  */
1873  BOOL after_wrap)
1874 {
1875  LRESULT res = EDIT_EM_PosFromChar(es, pos, after_wrap);
1876  TRACE("%d - %dx%d\n", pos, (short)LOWORD(res), (short)HIWORD(res));
1877  SetCaretPos((short)LOWORD(res), (short)HIWORD(res));
1878 }
1879 
1880 
1881 /*********************************************************************
1882  *
1883  * EM_SCROLLCARET
1884  *
1885  */
1887 {
1888  if (es->style & ES_MULTILINE) {
1889  INT l;
1890  INT vlc;
1891  INT ww;
1892  INT cw = es->char_width;
1893  INT x;
1894  INT dy = 0;
1895  INT dx = 0;
1896 
1897  l = EDIT_EM_LineFromChar(es, es->selection_end);
1898  x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP));
1899  vlc = get_vertical_line_count(es);
1900  if (l >= es->y_offset + vlc)
1901  dy = l - vlc + 1 - es->y_offset;
1902  if (l < es->y_offset)
1903  dy = l - es->y_offset;
1904  ww = es->format_rect.right - es->format_rect.left;
1905  if (x < es->format_rect.left)
1906  dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
1907  if (x > es->format_rect.right)
1908  dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
1909  if (dy || dx || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1910  {
1911  /* check if we are going to move too far */
1912  if(es->x_offset + dx + ww > es->text_width)
1913  dx = es->text_width - ww - es->x_offset;
1914  if(dx || dy || (es->y_offset && (es->line_count - es->y_offset < vlc)))
1916  }
1917  } else {
1918  INT x;
1919  INT goal;
1920  INT format_width;
1921 
1922  x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1923  format_width = es->format_rect.right - es->format_rect.left;
1924  if (x < es->format_rect.left) {
1925  goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
1926  do {
1927  es->x_offset--;
1928  x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1929  } while ((x < goal) && es->x_offset);
1930  /* FIXME: use ScrollWindow() somehow to improve performance */
1932  } else if (x > es->format_rect.right) {
1933  INT x_last;
1935  goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
1936  do {
1937  es->x_offset++;
1938  x = (short)LOWORD(EDIT_EM_PosFromChar(es, es->selection_end, FALSE));
1939  x_last = (short)LOWORD(EDIT_EM_PosFromChar(es, len, FALSE));
1940  } while ((x > goal) && (x_last > es->format_rect.right));
1941  /* FIXME: use ScrollWindow() somehow to improve performance */
1943  }
1944  }
1945 
1946  if(es->flags & EF_FOCUSED)
1947  EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
1948 }
1949 
1950 
1951 /*********************************************************************
1952  *
1953  * EDIT_MoveBackward
1954  *
1955  */
1956 static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend)
1957 {
1958  INT e = es->selection_end;
1959 
1960  if (e) {
1961  e--;
1962  if ((es->style & ES_MULTILINE) && e &&
1963  (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1964  e--;
1965  if (e && (es->text[e - 1] == '\r'))
1966  e--;
1967  }
1968  }
1969  EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
1971 }
1972 
1973 
1974 /*********************************************************************
1975  *
1976  * EDIT_MoveDown_ML
1977  *
1978  * Only for multi line controls
1979  * Move the caret one line down, on a column with the nearest
1980  * x coordinate on the screen (might be a different column).
1981  *
1982  */
1983 static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend)
1984 {
1985  INT s = es->selection_start;
1986  INT e = es->selection_end;
1987  BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1988  LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
1989  INT x = (short)LOWORD(pos);
1990  INT y = (short)HIWORD(pos);
1991 
1992  e = EDIT_CharFromPos(es, x, y + es->line_height, &after_wrap);
1993  if (!extend)
1994  s = e;
1995  EDIT_EM_SetSel(es, s, e, after_wrap);
1997 }
1998 
1999 
2000 /*********************************************************************
2001  *
2002  * EDIT_MoveEnd
2003  *
2004  */
2005 static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend, BOOL ctrl)
2006 {
2007  BOOL after_wrap = FALSE;
2008  INT e;
2009 
2010  /* Pass a high value in x to make sure of receiving the end of the line */
2011  if (!ctrl && (es->style & ES_MULTILINE))
2012  e = EDIT_CharFromPos(es, 0x3fffffff,
2013  HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
2014  else
2015  e = get_text_length(es);
2016  EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, after_wrap);
2018 }
2019 
2020 
2021 /*********************************************************************
2022  *
2023  * EDIT_MoveForward
2024  *
2025  */
2026 static void EDIT_MoveForward(EDITSTATE *es, BOOL extend)
2027 {
2028  INT e = es->selection_end;
2029 
2030  if (es->text[e]) {
2031  e++;
2032  if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
2033  if (es->text[e] == '\n')
2034  e++;
2035  else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
2036  e += 2;
2037  }
2038  }
2039  EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
2041 }
2042 
2043 
2044 /*********************************************************************
2045  *
2046  * EDIT_MoveHome
2047  *
2048  * Home key: move to beginning of line.
2049  *
2050  */
2051 static void EDIT_MoveHome(EDITSTATE *es, BOOL extend, BOOL ctrl)
2052 {
2053  INT e;
2054 
2055  /* Pass the x_offset in x to make sure of receiving the first position of the line */
2056  if (!ctrl && (es->style & ES_MULTILINE))
2057  e = EDIT_CharFromPos(es, -es->x_offset,
2058  HIWORD(EDIT_EM_PosFromChar(es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
2059  else
2060  e = 0;
2061  EDIT_EM_SetSel(es, extend ? es->selection_start : e, e, FALSE);
2063 }
2064 
2065 
2066 /*********************************************************************
2067  *
2068  * EDIT_MovePageDown_ML
2069  *
2070  * Only for multi line controls
2071  * Move the caret one page down, on a column with the nearest
2072  * x coordinate on the screen (might be a different column).
2073  *
2074  */
2075 static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend)
2076 {
2077  INT s = es->selection_start;
2078  INT e = es->selection_end;
2079  BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2080  LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2081  INT x = (short)LOWORD(pos);
2082  INT y = (short)HIWORD(pos);
2083 
2084  e = EDIT_CharFromPos(es, x,
2085  y + (es->format_rect.bottom - es->format_rect.top),
2086  &after_wrap);
2087  if (!extend)
2088  s = e;
2089  EDIT_EM_SetSel(es, s, e, after_wrap);
2091 }
2092 
2093 
2094 /*********************************************************************
2095  *
2096  * EDIT_MovePageUp_ML
2097  *
2098  * Only for multi line controls
2099  * Move the caret one page up, on a column with the nearest
2100  * x coordinate on the screen (might be a different column).
2101  *
2102  */
2103 static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend)
2104 {
2105  INT s = es->selection_start;
2106  INT e = es->selection_end;
2107  BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2108  LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2109  INT x = (short)LOWORD(pos);
2110  INT y = (short)HIWORD(pos);
2111 
2112  e = EDIT_CharFromPos(es, x,
2113  y - (es->format_rect.bottom - es->format_rect.top),
2114  &after_wrap);
2115  if (!extend)
2116  s = e;
2117  EDIT_EM_SetSel(es, s, e, after_wrap);
2119 }
2120 
2121 
2122 /*********************************************************************
2123  *
2124  * EDIT_MoveUp_ML
2125  *
2126  * Only for multi line controls
2127  * Move the caret one line up, on a column with the nearest
2128  * x coordinate on the screen (might be a different column).
2129  *
2130  */
2131 static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend)
2132 {
2133  INT s = es->selection_start;
2134  INT e = es->selection_end;
2135  BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
2136  LRESULT pos = EDIT_EM_PosFromChar(es, e, after_wrap);
2137  INT x = (short)LOWORD(pos);
2138  INT y = (short)HIWORD(pos);
2139 
2140  e = EDIT_CharFromPos(es, x, y - es->line_height, &after_wrap);
2141  if (!extend)
2142  s = e;
2143  EDIT_EM_SetSel(es, s, e, after_wrap);
2145 }
2146 
2147 
2148 /*********************************************************************
2149  *
2150  * EDIT_MoveWordBackward
2151  *
2152  */
2154 {
2155  INT s = es->selection_start;
2156  INT e = es->selection_end;
2157  INT l;
2158  INT ll;
2159  INT li;
2160 
2161  l = EDIT_EM_LineFromChar(es, e);
2162  ll = EDIT_EM_LineLength(es, e);
2163  li = EDIT_EM_LineIndex(es, l);
2164  if (e - li == 0) {
2165  if (l) {
2166  li = EDIT_EM_LineIndex(es, l - 1);
2167  e = li + EDIT_EM_LineLength(es, li);
2168  }
2169  } else {
2170  e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
2171  }
2172  if (!extend)
2173  s = e;
2174  EDIT_EM_SetSel(es, s, e, FALSE);
2176 }
2177 
2178 
2179 /*********************************************************************
2180  *
2181  * EDIT_MoveWordForward
2182  *
2183  */
2184 static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend)
2185 {
2186  INT s = es->selection_start;
2187  INT e = es->selection_end;
2188  INT l;
2189  INT ll;
2190  INT li;
2191 
2192  l = EDIT_EM_LineFromChar(es, e);
2193  ll = EDIT_EM_LineLength(es, e);
2194  li = EDIT_EM_LineIndex(es, l);
2195  if (e - li == ll) {
2196  if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
2197  e = EDIT_EM_LineIndex(es, l + 1);
2198  } else {
2200  li, e - li + 1, ll, WB_RIGHT);
2201  }
2202  if (!extend)
2203  s = e;
2204  EDIT_EM_SetSel(es, s, e, FALSE);
2206 }
2207 
2208 
2209 /*********************************************************************
2210  *
2211  * EDIT_PaintText
2212  *
2213  */
2215 {
2216  COLORREF BkColor;
2217  COLORREF TextColor;
2218  LOGFONTW underline_font;
2219  HFONT hUnderline = 0;
2220  HFONT old_font = 0;
2221  INT ret;
2222  INT li;
2223  INT BkMode;
2224  SIZE size;
2225 
2226  if (!count)
2227  return 0;
2228  BkMode = GetBkMode(dc);
2229  BkColor = GetBkColor(dc);
2230  TextColor = GetTextColor(dc);
2231  if (rev) {
2232  if (es->composition_len == 0)
2233  {
2236  SetBkMode( dc, OPAQUE);
2237  }
2238  else
2239  {
2241  GetObjectW(current,sizeof(LOGFONTW),&underline_font);
2242  underline_font.lfUnderline = TRUE;
2243  hUnderline = CreateFontIndirectW(&underline_font);
2244  old_font = SelectObject(dc,hUnderline);
2245  }
2246  }
2248  if (es->style & ES_MULTILINE) {
2249  ret = (INT)LOWORD(TabbedTextOutW(dc, x, y, es->text + li + col, count,
2250  es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
2251  } else {
2252  TextOutW(dc, x, y, es->text + li + col, count);
2253  GetTextExtentPoint32W(dc, es->text + li + col, count, &size);
2254  ret = size.cx;
2255  }
2256  if (rev) {
2257  if (es->composition_len == 0)
2258  {
2259  SetBkColor(dc, BkColor);
2260  SetTextColor(dc, TextColor);
2261  SetBkMode( dc, BkMode);
2262  }
2263  else
2264  {
2265  if (old_font)
2266  SelectObject(dc,old_font);
2267  if (hUnderline)
2268  DeleteObject(hUnderline);
2269  }
2270  }
2271  return ret;
2272 }
2273 
2274 
2275 /*********************************************************************
2276  *
2277  * EDIT_PaintLine
2278  *
2279  */
2281 {
2282  INT s = 0;
2283  INT e = 0;
2284  INT li = 0;
2285  INT ll = 0;
2286  INT x;
2287  INT y;
2288  LRESULT pos;
2290 
2291  if (es->style & ES_MULTILINE) {
2293 
2294  if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
2295  return;
2296  } else if (line)
2297  return;
2298 
2299  TRACE("line=%d\n", line);
2300 
2303  x = (short)LOWORD(pos);
2304  y = (short)HIWORD(pos);
2305 
2306  if (es->style & ES_MULTILINE)
2307  {
2308  int line_idx = line;
2309  x = -es->x_offset;
2310  if (es->style & ES_RIGHT || es->style & ES_CENTER)
2311  {
2312  LINEDEF *line_def = es->first_line_def;
2313  int w, lw;
2314 
2315  while (line_def && line_idx)
2316  {
2317  line_def = line_def->next;
2318  line_idx--;
2319  }
2320  w = es->format_rect.right - es->format_rect.left;
2321  lw = line_def->width;
2322 
2323  if (es->style & ES_RIGHT)
2324  x = w - (lw - x);
2325  else if (es->style & ES_CENTER)
2326  x += (w - lw) / 2;
2327  }
2328  x += es->format_rect.left;
2329  }
2330 
2331  if (rev)
2332  {
2334  ll = EDIT_EM_LineLength(es, li);
2335  s = min(es->selection_start, es->selection_end);
2336  e = max(es->selection_start, es->selection_end);
2337  s = min(li + ll, max(li, s));
2338  e = min(li + ll, max(li, e));
2339  }
2340 
2341  if (ssa)
2342  ScriptStringOut(ssa, x, y, 0, &es->format_rect, s - li, e - li, FALSE);
2343  else if (rev && (s != e) &&
2344  ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
2345  x += EDIT_PaintText(es, dc, x, y, line, 0, s - li, FALSE);
2346  x += EDIT_PaintText(es, dc, x, y, line, s - li, e - s, TRUE);
2347  x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
2348  } else
2349  x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
2350 }
2351 
2352 
2353 /*********************************************************************
2354  *
2355  * EDIT_AdjustFormatRect
2356  *
2357  * Adjusts the format rectangle for the current font and the
2358  * current client rectangle.
2359  *
2360  */
2362 {
2363  RECT ClientRect;
2364 
2365  es->format_rect.right = max(es->format_rect.right, es->format_rect.left + es->char_width);
2366  if (es->style & ES_MULTILINE)
2367  {
2368  INT fw, vlc, max_x_offset, max_y_offset;
2369 
2370  vlc = get_vertical_line_count(es);
2371  es->format_rect.bottom = es->format_rect.top + vlc * es->line_height;
2372 
2373  /* correct es->x_offset */
2374  fw = es->format_rect.right - es->format_rect.left;
2375  max_x_offset = es->text_width - fw;
2376  if(max_x_offset < 0) max_x_offset = 0;
2377  if(es->x_offset > max_x_offset)
2378  es->x_offset = max_x_offset;
2379 
2380  /* correct es->y_offset */
2381  max_y_offset = es->line_count - vlc;
2382  if(max_y_offset < 0) max_y_offset = 0;
2383  if(es->y_offset > max_y_offset)
2384  es->y_offset = max_y_offset;
2385 
2386  /* force scroll info update */
2388  }
2389  else
2390  /* Windows doesn't care to fix text placement for SL controls */
2391  es->format_rect.bottom = es->format_rect.top + es->line_height;
2392 
2393  /* Always stay within the client area */
2394  GetClientRect(es->hwndSelf, &ClientRect);
2395  es->format_rect.bottom = min(es->format_rect.bottom, ClientRect.bottom);
2396 
2397  if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
2399 
2400  EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
2401 }
2402 
2403 
2404 /*********************************************************************
2405  *
2406  * EDIT_SetRectNP
2407  *
2408  * note: this is not (exactly) the handler called on EM_SETRECTNP
2409  * it is also used to set the rect of a single line control
2410  *
2411  */
2412 static void EDIT_SetRectNP(EDITSTATE *es, const RECT *rc)
2413 {
2414  LONG_PTR ExStyle;
2415  INT bw, bh;
2416  ExStyle = GetWindowLongPtrW(es->hwndSelf, GWL_EXSTYLE);
2417 
2418  CopyRect(&es->format_rect, rc);
2419 
2420  if (ExStyle & WS_EX_CLIENTEDGE) {
2421  es->format_rect.left++;
2422  es->format_rect.right--;
2423 
2424  if (es->format_rect.bottom - es->format_rect.top
2425  >= es->line_height + 2)
2426  {
2427  es->format_rect.top++;
2428  es->format_rect.bottom--;
2429  }
2430  }
2431  else if (es->style & WS_BORDER) {
2433  bh = GetSystemMetrics(SM_CYBORDER) + 1;
2434  es->format_rect.left += bw;
2435  es->format_rect.right -= bw;
2436  if (es->format_rect.bottom - es->format_rect.top
2437  >= es->line_height + 2 * bh)
2438  {
2439  es->format_rect.top += bh;
2440  es->format_rect.bottom -= bh;
2441  }
2442  }
2443 
2444  es->format_rect.left += es->left_margin;
2445  es->format_rect.right -= es->right_margin;
2447 }
2448 
2449 
2450 /*********************************************************************
2451  *
2452  * EM_CHARFROMPOS
2453  *
2454  * returns line number (not index) in high-order word of result.
2455  * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
2456  * to Richedit, not to the edit control. Original documentation is valid.
2457  * FIXME: do the specs mean to return -1 if outside client area or
2458  * if outside formatting rectangle ???
2459  *
2460  */
2462 {
2463  POINT pt;
2464  RECT rc;
2465  INT index;
2466 
2467  pt.x = x;
2468  pt.y = y;
2469  GetClientRect(es->hwndSelf, &rc);
2470  if (!PtInRect(&rc, pt))
2471  return -1;
2472 
2473  index = EDIT_CharFromPos(es, x, y, NULL);
2475 }
2476 
2477 
2478 /*********************************************************************
2479  *
2480  * EM_FMTLINES
2481  *
2482  * Enable or disable soft breaks.
2483  *
2484  * This means: insert or remove the soft linebreak character (\r\r\n).
2485  * Take care to check if the text still fits the buffer after insertion.
2486  * If not, notify with EN_ERRSPACE.
2487  *
2488  */
2490 {
2491  es->flags &= ~EF_USE_SOFTBRK;
2492  if (add_eol) {
2493  es->flags |= EF_USE_SOFTBRK;
2494  FIXME("soft break enabled, not implemented\n");
2495  }
2496  return add_eol;
2497 }
2498 
2499 
2500 /*********************************************************************
2501  *
2502  * EM_GETHANDLE
2503  *
2504  * Hopefully this won't fire back at us.
2505  * We always start with a fixed buffer in the local heap.
2506  * Despite of the documentation says that the local heap is used
2507  * only if DS_LOCALEDIT flag is set, NT and 2000 always allocate
2508  * buffer on the local heap.
2509  *
2510  */
2512 {
2513  HLOCAL hLocal;
2514 
2515  if (!(es->style & ES_MULTILINE))
2516  return 0;
2517 
2518  if(es->is_unicode)
2519  hLocal = es->hloc32W;
2520  else
2521  {
2522  if(!es->hloc32A)
2523  {
2524  CHAR *textA;
2525  UINT countA, alloc_size;
2526  TRACE("Allocating 32-bit ANSI alias buffer\n");
2527  countA = WideCharToMultiByte(CP_ACP, 0, es->text, -1, NULL, 0, NULL, NULL);
2528  alloc_size = ROUND_TO_GROW(countA);
2529  if(!(es->hloc32A = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, alloc_size)))
2530  {
2531  ERR("Could not allocate %d bytes for 32-bit ANSI alias buffer\n", alloc_size);
2532  return 0;
2533  }
2534  textA = LocalLock(es->hloc32A);
2535  WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, countA, NULL, NULL);
2536  LocalUnlock(es->hloc32A);
2537  }
2538  hLocal = es->hloc32A;
2539  }
2540 
2542 
2543  /* The text buffer handle belongs to the app */
2544  es->hlocapp = hLocal;
2545 
2546  TRACE("Returning %p, LocalSize() = %ld\n", hLocal, LocalSize(hLocal));
2547  return hLocal;
2548 }
2549 
2550 
2551 /*********************************************************************
2552  *
2553  * EM_GETLINE
2554  *
2555  */
2557 {
2558  LPWSTR src;
2559  INT line_len, dst_len;
2560  INT i;
2561 
2562  if (es->style & ES_MULTILINE) {
2563  if (line >= es->line_count)
2564  return 0;
2565  } else
2566  line = 0;
2567  i = EDIT_EM_LineIndex(es, line);
2568  src = es->text + i;
2569  line_len = EDIT_EM_LineLength(es, i);
2570  dst_len = *(WORD *)dst;
2571  if(unicode)
2572  {
2573  if(dst_len <= line_len)
2574  {
2575  memcpy(dst, src, dst_len * sizeof(WCHAR));
2576  return dst_len;
2577  }
2578  else /* Append 0 if enough space */
2579  {
2580  memcpy(dst, src, line_len * sizeof(WCHAR));
2581  dst[line_len] = 0;
2582  return line_len;
2583  }
2584  }
2585  else
2586  {
2587  INT ret = WideCharToMultiByte(CP_ACP, 0, src, line_len, (LPSTR)dst, dst_len, NULL, NULL);
2588  if(!ret && line_len) /* Insufficient buffer size */
2589  return dst_len;
2590  if(ret < dst_len) /* Append 0 if enough space */
2591  ((LPSTR)dst)[ret] = 0;
2592  return ret;
2593  }
2594 }
2595 
2596 
2597 /*********************************************************************
2598  *
2599  * EM_GETSEL
2600  *
2601  */
2603 {
2604  UINT s = es->selection_start;
2605  UINT e = es->selection_end;
2606 
2607  ORDER_UINT(s, e);
2608  if (start)
2609  *start = s;
2610  if (end)
2611  *end = e;
2612  return MAKELONG(s, e);
2613 }
2614 
2615 
2616 /*********************************************************************
2617  *
2618  * EM_REPLACESEL
2619  *
2620  * FIXME: handle ES_NUMBER and ES_OEMCONVERT here
2621  *
2622  */
2623 static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update, BOOL honor_limit)
2624 {
2625  UINT strl = strlenW(lpsz_replace);
2626  UINT tl = get_text_length(es);
2627  UINT utl;
2628  UINT s;
2629  UINT e;
2630  UINT i;
2631  UINT size;
2632  LPWSTR p;
2633  HRGN hrgn = 0;
2634  LPWSTR buf = NULL;
2635  UINT bufl;
2636 
2637  TRACE("%s, can_undo %d, send_update %d\n",
2638  debugstr_w(lpsz_replace), can_undo, send_update);
2639 
2640  s = es->selection_start;
2641  e = es->selection_end;
2642 
2644  if ((s == e) && !strl)
2645  return;
2646 
2647  ORDER_UINT(s, e);
2648 
2649  size = tl - (e - s) + strl;
2650  if (!size)
2651  es->text_width = 0;
2652 
2653  /* Issue the EN_MAXTEXT notification and continue with replacing text
2654  * so that buffer limit is honored. */
2655  if ((honor_limit) && (size > es->buffer_limit)) {
2657  /* Buffer limit can be smaller than the actual length of text in combobox */
2658  if (es->buffer_limit < (tl - (e-s)))
2659  strl = 0;
2660  else
2661  strl = min(strl, es->buffer_limit - (tl - (e-s)));
2662  }
2663 
2664  if (!EDIT_MakeFit(es, tl - (e - s) + strl))
2665  return;
2666 
2667  if (e != s) {
2668  /* there is something to be deleted */
2669  TRACE("deleting stuff.\n");
2670  bufl = e - s;
2671  buf = HeapAlloc(GetProcessHeap(), 0, (bufl + 1) * sizeof(WCHAR));
2672  if (!buf) return;
2673  memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
2674  buf[bufl] = 0; /* ensure 0 termination */
2675  /* now delete */
2676  strcpyW(es->text + s, es->text + e);
2678  }
2679  if (strl) {
2680  /* there is an insertion */
2681  tl = get_text_length(es);
2682  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));
2683  for (p = es->text + tl ; p >= es->text + s ; p--)
2684  p[strl] = p[0];
2685  for (i = 0 , p = es->text + s ; i < strl ; i++)
2686  p[i] = lpsz_replace[i];
2687  if(es->style & ES_UPPERCASE)
2688  CharUpperBuffW(p, strl);
2689  else if(es->style & ES_LOWERCASE)
2690  CharLowerBuffW(p, strl);
2692  }
2693  if (es->style & ES_MULTILINE)
2694  {
2695  INT st = min(es->selection_start, es->selection_end);
2697 
2698  hrgn = CreateRectRgn(0, 0, 0, 0);
2699  EDIT_BuildLineDefs_ML(es, st, st + strl,
2700  strl - abs(es->selection_end - es->selection_start), hrgn);
2701  /* if text is too long undo all changes */
2702  if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
2703  if (strl)
2704  strcpyW(es->text + e, es->text + e + strl);
2705  if (e != s)
2706  for (i = 0 , p = es->text ; i < e - s ; i++)
2707  p[i + s] = buf[i];
2710  abs(es->selection_end - es->selection_start) - strl, hrgn);
2711  strl = 0;
2712  e = s;
2713  hrgn = CreateRectRgn(0, 0, 0, 0);
2715  }
2716  }
2717  else {
2718  INT fw = es->format_rect.right - es->format_rect.left;
2721  /* remove chars that don't fit */
2722  if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
2723  while ((es->text_width > fw) && s + strl >= s) {
2724  strcpyW(es->text + s + strl - 1, es->text + s + strl);
2725  strl--;
2726  es->text_length = -1;
2729  }
2732  }
2733  }
2734 
2735  if (e != s) {
2736  if (can_undo) {
2737  utl = strlenW(es->undo_text);
2738  if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2739  /* undo-buffer is extended to the right */
2740  EDIT_MakeUndoFit(es, utl + e - s);
2741  memcpy(es->undo_text + utl, buf, (e - s)*sizeof(WCHAR));
2742  (es->undo_text + utl)[e - s] = 0; /* ensure 0 termination */
2743  } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2744  /* undo-buffer is extended to the left */
2745  EDIT_MakeUndoFit(es, utl + e - s);
2746  for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2747  p[e - s] = p[0];
2748  for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2749  p[i] = buf[i];
2750  es->undo_position = s;
2751  } else {
2752  /* new undo-buffer */
2753  EDIT_MakeUndoFit(es, e - s);
2754  memcpy(es->undo_text, buf, (e - s)*sizeof(WCHAR));
2755  es->undo_text[e - s] = 0; /* ensure 0 termination */
2756  es->undo_position = s;
2757  }
2758  /* any deletion makes the old insertion-undo invalid */
2759  es->undo_insert_count = 0;
2760  } else
2762  }
2763  if (strl) {
2764  if (can_undo) {
2765  if ((s == es->undo_position) ||
2766  ((es->undo_insert_count) &&
2767  (s == es->undo_position + es->undo_insert_count)))
2768  /*
2769  * insertion is new and at delete position or
2770  * an extension to either left or right
2771  */
2772  es->undo_insert_count += strl;
2773  else {
2774  /* new insertion undo */
2775  es->undo_position = s;
2776  es->undo_insert_count = strl;
2777  /* new insertion makes old delete-buffer invalid */
2778  *es->undo_text = '\0';
2779  }
2780  } else
2782  }
2783 
2784  HeapFree(GetProcessHeap(), 0, buf);
2785 
2786  s += strl;
2787 
2788  /* If text has been deleted and we're right or center aligned then scroll rightward */
2789  if (es->style & (ES_RIGHT | ES_CENTER))
2790  {
2791  INT delta = strl - abs(es->selection_end - es->selection_start);
2792 
2793  if (delta < 0 && es->x_offset)
2794  {
2795  if (abs(delta) > es->x_offset)
2796  es->x_offset = 0;
2797  else
2798  es->x_offset += delta;
2799  }
2800  }
2801 
2802  EDIT_EM_SetSel(es, s, s, FALSE);
2803  es->flags |= EF_MODIFIED;
2804  if (send_update) es->flags |= EF_UPDATE;
2805  if (hrgn)
2806  {
2808  DeleteObject(hrgn);
2809  }
2810  else
2812 
2814 
2815  /* force scroll info update */
2817 
2818 
2819  if(send_update || (es->flags & EF_UPDATE))
2820  {
2821  es->flags &= ~EF_UPDATE;
2823  }
2825 }
2826 
2827 
2828 /*********************************************************************
2829  *
2830  * EM_SETHANDLE
2831  *
2832  * FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
2833  *
2834  */
2836 {
2837  if (!(es->style & ES_MULTILINE))
2838  return;
2839 
2840  if (!hloc) {
2841  WARN("called with NULL handle\n");
2842  return;
2843  }
2844 
2846 
2847  if(es->is_unicode)
2848  {
2849  if(es->hloc32A)
2850  {
2851  LocalFree(es->hloc32A);
2852  es->hloc32A = NULL;
2853  }
2854  es->hloc32W = hloc;
2855  }
2856  else
2857  {
2858  INT countW, countA;
2859  HLOCAL hloc32W_new;
2860  WCHAR *textW;
2861  CHAR *textA;
2862 
2863  countA = LocalSize(hloc);
2864  textA = LocalLock(hloc);
2865  countW = MultiByteToWideChar(CP_ACP, 0, textA, countA, NULL, 0);
2866  if(!(hloc32W_new = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, countW * sizeof(WCHAR))))
2867  {
2868  ERR("Could not allocate new unicode buffer\n");
2869  return;
2870  }
2871  textW = LocalLock(hloc32W_new);
2872  MultiByteToWideChar(CP_ACP, 0, textA, countA, textW, countW);
2873  LocalUnlock(hloc32W_new);
2874  LocalUnlock(hloc);
2875 
2876  if(es->hloc32W)
2877  LocalFree(es->hloc32W);
2878 
2879  es->hloc32W = hloc32W_new;
2880  es->hloc32A = hloc;
2881  }
2882 
2883  es->buffer_size = LocalSize(es->hloc32W)/sizeof(WCHAR) - 1;
2884 
2885  /* The text buffer handle belongs to the control */
2886  es->hlocapp = NULL;
2887 
2890 
2891  es->x_offset = es->y_offset = 0;
2892  es->selection_start = es->selection_end = 0;
2894  es->flags &= ~EF_MODIFIED;
2895  es->flags &= ~EF_UPDATE;
2899  /* force scroll info update */
2901 }
2902 
2903 
2904 /*********************************************************************
2905  *
2906  * EM_SETLIMITTEXT
2907  *
2908  * NOTE: this version currently implements WinNT limits
2909  *
2910  */
2912 {
2913  if (!limit) limit = ~0u;
2914  if (!(es->style & ES_MULTILINE)) limit = min(limit, 0x7ffffffe);
2915  es->buffer_limit = limit;
2916 }
2917 
2918 
2919 /*********************************************************************
2920  *
2921  * EM_SETMARGINS
2922  *
2923  * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2924  * action wParam despite what the docs say. EC_USEFONTINFO calculates the
2925  * margin according to the textmetrics of the current font.
2926  *
2927  * When EC_USEFONTINFO is used in the non_cjk case the margins only
2928  * change if the edit control is equal to or larger than a certain
2929  * size. Though there is an exception for the empty client rect case
2930  * with small font sizes.
2931  */
2933 {
2934  switch(charset)
2935  {
2936  case SHIFTJIS_CHARSET:
2937  case HANGUL_CHARSET:
2938  case GB2312_CHARSET:
2939  case CHINESEBIG5_CHARSET:
2940  return TRUE;
2941  }
2942  /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is
2943  * not by other versions including Win 10. */
2944  return FALSE;
2945 }
2946 
2948  WORD left, WORD right, BOOL repaint)
2949 {
2950  TEXTMETRICW tm;
2951  INT default_left_margin = 0; /* in pixels */
2952  INT default_right_margin = 0; /* in pixels */
2953 
2954  /* Set the default margins depending on the font */
2955  if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
2956  HDC dc = GetDC(es->hwndSelf);
2957  HFONT old_font = SelectObject(dc, es->font);
2959  RECT rc;
2960 
2961  /* The default margins are only non zero for TrueType or Vector fonts */
2962  if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
2963  if (!is_cjk(tm.tmCharSet)) {
2964  default_left_margin = width / 2;
2965  default_right_margin = width / 2;
2966 
2967  GetClientRect(es->hwndSelf, &rc);
2968  if (rc.right - rc.left < (width / 2 + width) * 2 &&
2969  (width >= 28 || !IsRectEmpty(&rc)) ) {
2970  default_left_margin = es->left_margin;
2971  default_right_margin = es->right_margin;
2972  }
2973  } else {
2974  /* FIXME: figure out the CJK values. They are not affected by the client rect. */
2975  default_left_margin = width / 2;
2976  default_right_margin = width / 2;
2977  }
2978  }
2979  SelectObject(dc, old_font);
2980  ReleaseDC(es->hwndSelf, dc);
2981  }
2982 
2983  if (action & EC_LEFTMARGIN) {
2984  es->format_rect.left -= es->left_margin;
2985  if (left != EC_USEFONTINFO)
2986  es->left_margin = left;
2987  else
2988  es->left_margin = default_left_margin;
2989  es->format_rect.left += es->left_margin;
2990  }
2991 
2992  if (action & EC_RIGHTMARGIN) {
2993  es->format_rect.right += es->right_margin;
2994  if (right != EC_USEFONTINFO)
2995  es->right_margin = right;
2996  else
2997  es->right_margin = default_right_margin;
2998  es->format_rect.right -= es->right_margin;
2999  }
3000 
3001  if (action & (EC_LEFTMARGIN | EC_RIGHTMARGIN)) {
3003  if (repaint) EDIT_UpdateText(es, NULL, TRUE);
3004  }
3005 
3006  TRACE("left=%d, right=%d\n", es->left_margin, es->right_margin);
3007 }
3008 
3009 
3010 /*********************************************************************
3011  *
3012  * EM_SETPASSWORDCHAR
3013  *
3014  */
3016 {
3017  LONG style;
3018 
3019  if (es->style & ES_MULTILINE)
3020  return;
3021 
3022  if (es->password_char == c)
3023  return;
3024 
3025  style = GetWindowLongW( es->hwndSelf, GWL_STYLE );
3026  es->password_char = c;
3027  if (c) {
3028  SetWindowLongW( es->hwndSelf, GWL_STYLE, style | ES_PASSWORD );
3029  es->style |= ES_PASSWORD;
3030  } else {
3031  SetWindowLongW( es->hwndSelf, GWL_STYLE, style & ~ES_PASSWORD );
3032  es->style &= ~ES_PASSWORD;
3033  }
3036 }
3037 
3038 
3039 /*********************************************************************
3040  *
3041  * EM_SETTABSTOPS
3042  *
3043  */
3045 {
3046  if (!(es->style & ES_MULTILINE))
3047  return FALSE;
3048  HeapFree(GetProcessHeap(), 0, es->tabs);
3049  es->tabs_count = count;
3050  if (!count)
3051  es->tabs = NULL;
3052  else {
3053  es->tabs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(INT));
3054 #ifdef __REACTOS__
3055  /* ReactOS r33503 */
3056  if (es->tabs == NULL)
3057  {
3058  es->tabs_count = 0;
3059  return FALSE;
3060  }
3061 #endif
3062  memcpy(es->tabs, tabs, count * sizeof(INT));
3063  }
3065  return TRUE;
3066 }
3067 
3068 
3069 /*********************************************************************
3070  *
3071  * EM_SETWORDBREAKPROC
3072  *
3073  */
3074 static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, void *wbp)
3075 {
3076  if (es->word_break_proc == wbp)
3077  return;
3078 
3079  es->word_break_proc = wbp;
3080 
3081  if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
3084  }
3085 }
3086 
3087 
3088 /*********************************************************************
3089  *
3090  * EM_UNDO / WM_UNDO
3091  *
3092  */
3094 {
3095  INT ulength;
3096  LPWSTR utext;
3097 
3098  /* As per MSDN spec, for a single-line edit control,
3099  the return value is always TRUE */
3100  if( es->style & ES_READONLY )
3101  return !(es->style & ES_MULTILINE);
3102 
3103  ulength = strlenW(es->undo_text);
3104 
3105  utext = HeapAlloc(GetProcessHeap(), 0, (ulength + 1) * sizeof(WCHAR));
3106 #ifdef __REACTOS__
3107  /* ReactOS r33503 */
3108  if (utext == NULL)
3109  return FALSE;
3110 #endif
3111 
3112  strcpyW(utext, es->undo_text);
3113 
3114  TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
3115  es->undo_insert_count, debugstr_w(utext));
3116 
3117  EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3119  EDIT_EM_ReplaceSel(es, TRUE, utext, TRUE, TRUE);
3120  EDIT_EM_SetSel(es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
3121  /* send the notification after the selection start and end are set */
3124  HeapFree(GetProcessHeap(), 0, utext);
3125 
3126  TRACE("after UNDO:insertion length = %d, deletion buffer = %s\n",
3127  es->undo_insert_count, debugstr_w(es->undo_text));
3128  return TRUE;
3129 }
3130 
3131 
3132 /* Helper function for WM_CHAR
3133  *
3134  * According to an MSDN blog article titled "Just because you're a control
3135  * doesn't mean that you're necessarily inside a dialog box," multiline edit
3136  * controls without ES_WANTRETURN would attempt to detect whether it is inside
3137  * a dialog box or not.
3138  */
3140 {
3141  return (es->flags & EF_DIALOGMODE);
3142 }
3143 
3144 
3145 /*********************************************************************
3146  *
3147  * WM_PASTE
3148  *
3149  */
3151 {
3152  HGLOBAL hsrc;
3153  LPWSTR src;
3154 
3155  /* Protect read-only edit control from modification */
3156  if(es->style & ES_READONLY)
3157  return;
3158 
3159  OpenClipboard(es->hwndSelf);
3160  if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
3161  src = GlobalLock(hsrc);
3163  GlobalUnlock(hsrc);
3164  }
3165  else if (es->style & ES_PASSWORD) {
3166  /* clear selected text in password edit box even with empty clipboard */
3168  }
3169  CloseClipboard();
3170 }
3171 
3172 
3173 /*********************************************************************
3174  *
3175  * WM_COPY
3176  *
3177  */
3179 {
3180  INT s = min(es->selection_start, es->selection_end);
3181  INT e = max(es->selection_start, es->selection_end);
3182  HGLOBAL hdst;
3183  LPWSTR dst;
3184  DWORD len;
3185 
3186  if (e == s) return;
3187 
3188  len = e - s;
3189  hdst = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (len + 1) * sizeof(WCHAR));
3190  dst = GlobalLock(hdst);
3191  memcpy(dst, es->text + s, len * sizeof(WCHAR));
3192  dst[len] = 0; /* ensure 0 termination */
3193  TRACE("%s\n", debugstr_w(dst));
3194  GlobalUnlock(hdst);
3195  OpenClipboard(es->hwndSelf);
3196  EmptyClipboard();
3198  CloseClipboard();
3199 }
3200 
3201 
3202 /*********************************************************************
3203  *
3204  * WM_CLEAR
3205  *
3206  */
3207 static inline void EDIT_WM_Clear(EDITSTATE *es)
3208 {
3209  /* Protect read-only edit control from modification */
3210  if(es->style & ES_READONLY)
3211  return;
3212 
3214 }
3215 
3216 
3217 /*********************************************************************
3218  *
3219  * WM_CUT
3220  *
3221  */
3222 static inline void EDIT_WM_Cut(EDITSTATE *es)
3223 {
3224  EDIT_WM_Copy(es);
3225  EDIT_WM_Clear(es);
3226 }
3227 
3228 
3229 /*********************************************************************
3230  *
3231  * WM_CHAR
3232  *
3233  */
3235 {
3236  BOOL control;
3237 
3238  control = GetKeyState(VK_CONTROL) & 0x8000;
3239 
3240  switch (c) {
3241  case '\r':
3242  /* If it's not a multiline edit box, it would be ignored below.
3243  * For multiline edit without ES_WANTRETURN, we have to make a
3244  * special case.
3245  */
3246  if ((es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
3247  if (EDIT_IsInsideDialog(es))
3248  break;
3249  case '\n':
3250  if (es->style & ES_MULTILINE) {
3251  if (es->style & ES_READONLY) {
3254  } else {
3255  static const WCHAR cr_lfW[] = {'\r','\n',0};
3256  EDIT_EM_ReplaceSel(es, TRUE, cr_lfW, TRUE, TRUE);
3257  }
3258  }
3259  break;
3260  case '\t':
3261  if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
3262  {
3263  static const WCHAR tabW[] = {'\t',0};
3264  if (EDIT_IsInsideDialog(es))
3265  break;
3266  EDIT_EM_ReplaceSel(es, TRUE, tabW, TRUE, TRUE);
3267  }
3268  break;
3269  case VK_BACK:
3270  if (!(es->style & ES_READONLY) && !control) {
3271  if (es->selection_start != es->selection_end)
3272  EDIT_WM_Clear(es);
3273  else {
3274  /* delete character left of caret */
3275  EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3277  EDIT_WM_Clear(es);
3278  }
3279  }
3280  break;
3281  case 0x03: /* ^C */
3282  if (!(es->style & ES_PASSWORD))
3283  SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3284  break;
3285  case 0x16: /* ^V */
3286  if (!(es->style & ES_READONLY))
3287  SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3288  break;
3289  case 0x18: /* ^X */
3290  if (!((es->style & ES_READONLY) || (es->style & ES_PASSWORD)))
3291  SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3292  break;
3293  case 0x1A: /* ^Z */
3294  if (!(es->style & ES_READONLY))
3295  SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3296  break;
3297 
3298  default:
3299  /*If Edit control style is ES_NUMBER allow users to key in only numeric values*/
3300  if( (es->style & ES_NUMBER) && !( c >= '0' && c <= '9') )
3301  break;
3302 
3303  if (!(es->style & ES_READONLY) && (c >= ' ') && (c != 127)) {
3304  WCHAR str[2];
3305  str[0] = c;
3306  str[1] = '\0';
3308  }
3309  break;
3310  }
3311  return 1;
3312 }
3313 
3314 
3315 /*********************************************************************
3316  *
3317  * EDIT_ContextMenuCommand
3318  *
3319  */
3321 {
3322  switch (id) {
3323  case EM_UNDO:
3324  SendMessageW(es->hwndSelf, WM_UNDO, 0, 0);
3325  break;
3326  case WM_CUT:
3327  SendMessageW(es->hwndSelf, WM_CUT, 0, 0);
3328  break;
3329  case WM_COPY:
3330  SendMessageW(es->hwndSelf, WM_COPY, 0, 0);
3331  break;
3332  case WM_PASTE:
3333  SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3334  break;
3335  case WM_CLEAR:
3336  SendMessageW(es->hwndSelf, WM_CLEAR, 0, 0);
3337  break;
3338  case EM_SETSEL:
3339  SendMessageW(es->hwndSelf, EM_SETSEL, 0, -1);
3340  break;
3341  default:
3342  ERR("unknown menu item, please report\n");
3343  break;
3344  }
3345 }
3346 
3347 
3348 /*********************************************************************
3349  *
3350  * WM_CONTEXTMENU
3351  *
3352  * Note: the resource files resource/sysres_??.rc cannot define a
3353  * single popup menu. Hence we use a (dummy) menubar
3354  * containing the single popup menu as its first item.
3355  *
3356  * FIXME: the message identifiers have been chosen arbitrarily,
3357  * hence we use MF_BYPOSITION.
3358  * We might as well use the "real" values (anybody knows ?)
3359  * The menu definition is in resources/sysres_??.rc.
3360  * Once these are OK, we better use MF_BYCOMMAND here
3361  * (as we do in EDIT_WM_Command()).
3362  *
3363  */
3365 {
3366  HMENU menu = LoadMenuA(user32_module, "EDITMENU");
3367  HMENU popup = GetSubMenu(menu, 0);
3368  UINT start = es->selection_start;
3369  UINT end = es->selection_end;
3370  UINT cmd;
3371 
3372  ORDER_UINT(start, end);
3373 
3374  /* undo */
3375  EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(es) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3376  /* cut */
3377  EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3378  /* copy */
3379  EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3380  /* paste */
3382  /* delete */
3383  EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3384  /* select all */
3386 
3387  if (x == -1 && y == -1) /* passed via VK_APPS press/release */
3388  {
3389  RECT rc;
3390  /* Windows places the menu at the edit's center in this case */
3391 #ifdef __REACTOS__
3392  /* ReactOS r55202 */
3393  GetClientRect(es->hwndSelf, &rc);
3394  MapWindowPoints(es->hwndSelf, 0, (POINT *)&rc, 2);
3395 #else
3396  WIN_GetRectangles( es->hwndSelf, COORDS_SCREEN, NULL, &rc );
3397 #endif
3398  x = rc.left + (rc.right - rc.left) / 2;
3399  y = rc.top + (rc.bottom - rc.top) / 2;
3400  }
3401 
3402  if (!(es->flags & EF_FOCUSED))
3403  SetFocus(es->hwndSelf);
3404 
3406  x, y, 0, es->hwndSelf, NULL);
3407 
3408  if (cmd)
3410 
3411  DestroyMenu(menu);
3412 }
3413 
3414 
3415 /*********************************************************************
3416  *
3417  * WM_GETTEXT
3418  *
3419  */
3421 {
3422  if(!count) return 0;
3423 
3424  if(unicode)
3425  {
3426  lstrcpynW(dst, es->text, count);
3427  return strlenW(dst);
3428  }
3429  else
3430  {
3431  LPSTR textA = (LPSTR)dst;
3432  if (!WideCharToMultiByte(CP_ACP, 0, es->text, -1, textA, count, NULL, NULL))
3433  textA[count - 1] = 0; /* ensure 0 termination */
3434  return strlen(textA);
3435  }
3436 }
3437 
3438 /*********************************************************************
3439  *
3440  * EDIT_CheckCombo
3441  *
3442  */
3444 {
3445  HWND hLBox = es->hwndListBox;
3446  HWND hCombo;
3447  BOOL bDropped;
3448  int nEUI;
3449 
3450  if (!hLBox)
3451  return FALSE;
3452 
3453  hCombo = GetParent(es->hwndSelf);
3454  bDropped = TRUE;
3455  nEUI = 0;
3456 
3457  TRACE_(combo)("[%p]: handling msg %x (%x)\n", es->hwndSelf, msg, key);
3458 
3459  if (key == VK_UP || key == VK_DOWN)
3460  {
3461  if (SendMessageW(hCombo, CB_GETEXTENDEDUI, 0, 0))
3462  nEUI = 1;
3463 
3464  if (msg == WM_KEYDOWN || nEUI)
3465  bDropped = (BOOL)SendMessageW(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3466  }
3467 
3468  switch (msg)
3469  {
3470  case WM_KEYDOWN:
3471  if (!bDropped && nEUI && (key == VK_UP || key == VK_DOWN))
3472  {
3473  /* make sure ComboLBox pops up */
3474  SendMessageW(hCombo, CB_SETEXTENDEDUI, FALSE, 0);
3475  key = VK_F4;
3476  nEUI = 2;
3477  }
3478 
3479  SendMessageW(hLBox, WM_KEYDOWN, key, 0);
3480  break;
3481 
3482  case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3483  if (nEUI)
3484  SendMessageW(hCombo, CB_SHOWDROPDOWN, !bDropped, 0);
3485  else
3486  SendMessageW(hLBox, WM_KEYDOWN, VK_F4, 0);
3487  break;
3488  }
3489 
3490  if(nEUI == 2)
3491  SendMessageW(hCombo, CB_SETEXTENDEDUI, TRUE, 0);
3492 
3493  return TRUE;
3494 }
3495 
3496 
3497 /*********************************************************************
3498  *
3499  * WM_KEYDOWN
3500  *
3501  * Handling of special keys that don't produce a WM_CHAR
3502  * (i.e. non-printable keys) & Backspace & Delete
3503  *
3504  */
3506 {
3507  BOOL shift;
3508  BOOL control;
3509 
3510  if (GetKeyState(VK_MENU) & 0x8000)
3511  return 0;
3512 
3513  shift = GetKeyState(VK_SHIFT) & 0x8000;
3514  control = GetKeyState(VK_CONTROL) & 0x8000;
3515 
3516  switch (key) {
3517  case VK_F4:
3518  case VK_UP:
3519  if (EDIT_CheckCombo(es, WM_KEYDOWN, key) || key == VK_F4)
3520  break;
3521 
3522  /* fall through */
3523  case VK_LEFT:
3524  if ((es->style & ES_MULTILINE) && (key == VK_UP))
3526  else
3527  if (control)
3529  else
3531  break;
3532  case VK_DOWN:
3534  break;
3535  /* fall through */
3536  case VK_RIGHT:
3537  if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3539  else if (control)
3541  else
3543  break;
3544  case VK_HOME:
3545  EDIT_MoveHome(es, shift, control);
3546  break;
3547  case VK_END:
3548  EDIT_MoveEnd(es, shift, control);
3549  break;
3550  case VK_PRIOR:
3551  if (es->style & ES_MULTILINE)
3553  else
3555  break;
3556  case VK_NEXT:
3557  if (es->style & ES_MULTILINE)
3559  else
3561  break;
3562  case VK_DELETE:
3563  if (!(es->style & ES_READONLY) && !(shift && control)) {
3564  if (es->selection_start != es->selection_end) {
3565  if (shift)
3566  EDIT_WM_Cut(es);
3567  else
3568  EDIT_WM_Clear(es);
3569  } else {
3570  if (shift) {
3571  /* delete character left of caret */
3572  EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3574  EDIT_WM_Clear(es);
3575  } else if (control) {
3576  /* delete to end of line */
3577  EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3579  EDIT_WM_Clear(es);
3580  } else {
3581  /* delete character right of caret */
3582  EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
3584  EDIT_WM_Clear(es);
3585  }
3586  }
3587  }
3588  break;
3589  case VK_INSERT:
3590  if (shift) {
3591  if (!(es->style & ES_READONLY))
3592  EDIT_WM_Paste(es);
3593  } else if (control)
3594  EDIT_WM_Copy(es);
3595  break;
3596  case VK_RETURN:
3597  /* If the edit doesn't want the return send a message to the default object */
3598  if(!(es->style & ES_MULTILINE) || !(es->style & ES_WANTRETURN))
3599  {
3600  DWORD dw;
3601 
3602  if (!EDIT_IsInsideDialog(es)) break;
3603  if (control) break;
3604  dw = SendMessageW(es->hwndParent, DM_GETDEFID, 0, 0);
3605  if (HIWORD(dw) == DC_HASDEFID)
3606  {
3607  HWND hwDefCtrl = GetDlgItem(es->hwndParent, LOWORD(dw));
3608  if (hwDefCtrl)
3609  {
3610  SendMessageW(es->hwndParent, WM_NEXTDLGCTL, (WPARAM)hwDefCtrl, TRUE);
3611  PostMessageW(hwDefCtrl, WM_KEYDOWN, VK_RETURN, 0);
3612  }
3613  }
3614  }
3615  break;
3616  case VK_ESCAPE:
3617  if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3618  PostMessageW(es->hwndParent, WM_CLOSE, 0, 0);
3619  break;
3620  case VK_TAB:
3621  if ((es->style & ES_MULTILINE) && EDIT_IsInsideDialog(es))
3622  SendMessageW(es->hwndParent, WM_NEXTDLGCTL, shift, 0);
3623  break;
3624 #ifdef __REACTOS__
3625  /* ReactOS CORE-1419 */
3626  case VK_BACK:
3627  if (control)
3628  {
3629  FIXME("Ctrl+Backspace\n");
3630  }
3631  break;
3632 #endif
3633  }
3634  return TRUE;
3635 }
3636 
3637 
3638 /*********************************************************************
3639  *
3640  * WM_KILLFOCUS
3641  *
3642  */
3644 {
3645 #ifdef __REACTOS__
3646  HWND hCombo;
3647  LONG lStyles;
3648 
3649  es->flags &= ~EF_FOCUSED;
3650  DestroyCaret();
3651  if(!(es->style & ES_NOHIDESEL))
3652  EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3653 
3654  /* throw away left over scroll when we lose focus */
3655  es->wheelDeltaRemainder = 0;
3656 
3657  if (es->hwndListBox == NULL)
3659  else
3660  { /* send the undocumented WM_CBLOSTTEXTFOCUS message to combobox */
3661  hCombo = GetParent(es->hwndSelf);
3662  lStyles = GetWindowLong(hCombo, GWL_STYLE);
3663  if ((lStyles & CBS_DROPDOWN) || (lStyles & CBS_SIMPLE))
3664  SendMessage(hCombo, WM_CBLOSTTEXTFOCUS, 0, 0);
3665  }
3666 #else
3667  es->flags &= ~EF_FOCUSED;
3668  DestroyCaret();
3669  if(!(es->style & ES_NOHIDESEL))
3670  EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3672  /* throw away left over scroll when we lose focus */
3673  es->wheelDeltaRemainder = 0;
3674 #endif
3675  return 0;
3676 }
3677 
3678 
3679 /*********************************************************************
3680  *
3681  * WM_LBUTTONDBLCLK
3682  *
3683  * The caret position has been set on the WM_LBUTTONDOWN message
3684  *
3685  */
3687 {
3688  INT s;
3689  INT e = es->selection_end;
3690  INT l;
3691  INT li;
3692  INT ll;
3693 
3694  es->bCaptureState = TRUE;
3695  SetCapture(es->hwndSelf);
3696 
3697  l = EDIT_EM_LineFromChar(es, e);
3698  li = EDIT_EM_LineIndex(es, l);
3699  ll = EDIT_EM_LineLength(es, e);
3700  s = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_LEFT);
3701  e = li + EDIT_CallWordBreakProc(es, li, e - li, ll, WB_RIGHT);
3702  EDIT_EM_SetSel(es, s, e, FALSE);
3704  es->region_posx = es->region_posy = 0;
3705  SetTimer(es->hwndSelf, 0, 100, NULL);
3706  return 0;
3707 }
3708 
3709 
3710 /*********************************************************************
3711  *
3712  * WM_LBUTTONDOWN
3713  *
3714  */
3716 {
3717  INT e;
3718  BOOL after_wrap;
3719 
3720  es->bCaptureState = TRUE;
3721  SetCapture(es->hwndSelf);
3722  EDIT_ConfinePoint(es, &x, &y);
3723  e = EDIT_CharFromPos(es, x, y, &after_wrap);
3724  EDIT_EM_SetSel(es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3726  es->region_posx = es->region_posy = 0;
3727  SetTimer(es->hwndSelf, 0, 100, NULL);
3728 
3729  if (!(es->flags & EF_FOCUSED))
3730  SetFocus(es->hwndSelf);
3731 
3732  return 0;
3733 }
3734 
3735 
3736 /*********************************************************************
3737  *
3738  * WM_LBUTTONUP
3739  *
3740  */
3742 {
3743  if (es->bCaptureState) {
3744  KillTimer(es->hwndSelf, 0);
3745  if (GetCapture() == es->hwndSelf) ReleaseCapture();
3746  }
3747  es->bCaptureState = FALSE;
3748  return 0;
3749 }
3750 
3751 
3752 /*********************************************************************
3753  *
3754  * WM_MBUTTONDOWN
3755  *
3756  */
3758 {
3759  SendMessageW(es->hwndSelf, WM_PASTE, 0, 0);
3760  return 0;
3761 }
3762 
3763 
3764 /*********************************************************************
3765  *
3766  * WM_MOUSEMOVE
3767  *
3768  */
3770 {
3771  INT e;
3772  BOOL after_wrap;
3773  INT prex, prey;
3774 
3775  /* If the mouse has been captured by process other than the edit control itself,
3776  * the windows edit controls will not select the strings with mouse move.
3777  */
3778  if (!es->bCaptureState || GetCapture() != es->hwndSelf)
3779  return 0;
3780 
3781  /*
3782  * FIXME: gotta do some scrolling if outside client
3783  * area. Maybe reset the timer ?
3784  */
3785  prex = x; prey = y;
3786  EDIT_ConfinePoint(es, &x, &y);
3787  es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3788  es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3789  e = EDIT_CharFromPos(es, x, y, &after_wrap);
3790  EDIT_EM_SetSel(es, es->selection_start, e, after_wrap);
3791  EDIT_SetCaretPos(es,es->selection_end,es->flags & EF_AFTER_WRAP);
3792  return 0;
3793 }
3794 
3795 
3796 /*********************************************************************
3797  *
3798  * WM_PAINT
3799  *
3800  */
3802 {
3803  PAINTSTRUCT ps;
3804  INT i;
3805  HDC dc;
3806  HFONT old_font = 0;
3807  RECT rc;
3808  RECT rcClient;
3809  RECT rcLine;
3810  RECT rcRgn;
3811  HBRUSH brush;
3812  HBRUSH old_brush;
3813  INT bw, bh;
3814  BOOL rev = es->bEnableState &&
3815  ((es->flags & EF_FOCUSED) ||
3816  (es->style & ES_NOHIDESEL));
3817  dc = hdc ? hdc : BeginPaint(es->hwndSelf, &ps);
3818 
3819  /* The dc we use for calculating may not be the one we paint into.
3820  This is the safest action. */
3822  GetClientRect(es->hwndSelf, &rcClient);
3823 
3824  /* get the background brush */
3825  brush = EDIT_NotifyCtlColor(es, dc);
3826 
3827  /* paint the border and the background */
3828  IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
3829 
3830  if(es->style & WS_BORDER) {
3833  rc = rcClient;
3834  if(es->style & ES_MULTILINE) {
3835  if(es->style & WS_HSCROLL) rc.bottom+=bh;
3836  if(es->style & WS_VSCROLL) rc.right+=bw;
3837  }
3838 
3839  /* Draw the frame. Same code as in nonclient.c */
3841  PatBlt(dc, rc.left, rc.top, rc.right - rc.left, bh, PATCOPY);
3842  PatBlt(dc, rc.left, rc.top, bw, rc.bottom - rc.top, PATCOPY);
3843  PatBlt(dc, rc.left, rc.bottom - 1, rc.right - rc.left, -bw, PATCOPY);
3844  PatBlt(dc, rc.right - 1, rc.top, -bw, rc.bottom - rc.top, PATCOPY);
3845  SelectObject(dc, old_brush);
3846 
3847  /* Keep the border clean */
3848  IntersectClipRect(dc, rc.left+bw, rc.top+bh,
3849  max(rc.right-bw, rc.left+bw), max(rc.bottom-bh, rc.top+bh));
3850  }
3851 
3852  GetClipBox(dc, &rc);
3853  FillRect(dc, &rc, brush);
3854 
3855  IntersectClipRect(dc, es->format_rect.left,
3856  es->format_rect.top,
3857  es->format_rect.right,
3858  es->format_rect.bottom);
3859  if (es->style & ES_MULTILINE) {
3860  rc = rcClient;
3861  IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3862  }
3863  if (es->font)
3864  old_font = SelectObject(dc, es->font);
3865 
3866  if (!es->bEnableState)
3868  GetClipBox(dc, &rcRgn);
3869  if (es->style & ES_MULTILINE) {
3871  for (i = es->y_offset ; i <= min(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
3873  EDIT_GetLineRect(es, i, 0, -1, &rcLine);
3874  if (IntersectRect(&rc, &rcRgn, &rcLine))
3875  EDIT_PaintLine(es, dc, i, rev);
3876  }
3877  } else {
3879  EDIT_GetLineRect(es, 0, 0, -1, &rcLine);
3880  if (IntersectRect(&rc, &rcRgn, &rcLine))
3881  EDIT_PaintLine(es, dc, 0, rev);
3882  }
3883  if (es->font)
3884  SelectObject(dc, old_font);
3885 
3886  if (!hdc)
3887  EndPaint(es->hwndSelf, &ps);
3888 }
3889 
3890 
3891 /*********************************************************************
3892  *
3893  * WM_SETFOCUS
3894  *
3895  */
3897 {
3898  es->flags |= EF_FOCUSED;
3899 
3900  if (!(es->style & ES_NOHIDESEL))
3901  EDIT_InvalidateText(es, es->selection_start, es->selection_end);
3902 
3903  /* single line edit updates itself */
3904  if (IsWindowVisible(es->hwndSelf) && !(es->style & ES_MULTILINE))
3905  {
3906  HDC hdc = GetDC(es->hwndSelf);
3907  EDIT_WM_Paint(es, hdc);
3908  ReleaseDC(es->hwndSelf, hdc);
3909  }
3910 
3911 #ifdef __REACTOS__
3912  SystemParametersInfo(SPI_GETCARETWIDTH, 0, &es->dwCaretWidth, 0);
3913  CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height);
3914 #else
3915  CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3916 #endif
3917  EDIT_SetCaretPos(es, es->selection_end,
3918  es->flags & EF_AFTER_WRAP);
3919  ShowCaret(es->hwndSelf);
3921 }
3922 
3923 
3924 /*********************************************************************
3925  *
3926  * WM_SETFONT
3927  *
3928  * With Win95 look the margins are set to default font value unless
3929  * the system font (font == 0) is being set, in which case they are left
3930  * unchanged.
3931  *
3932  */
3934 {
3935  TEXTMETRICW tm;
3936  HDC dc;
3937  HFONT old_font = 0;
3938  RECT clientRect;
3939 
3940  es->font = font;
3942  dc = GetDC(es->hwndSelf);
3943  if (font)
3944  old_font = SelectObject(dc, font);
3945  GetTextMetricsW(dc, &tm);
3946  es->line_height = tm.tmHeight;
3947  es->char_width = tm.tmAveCharWidth;
3948  if (font)
3949  SelectObject(dc, old_font);
3950  ReleaseDC(es->hwndSelf, dc);
3951 
3952  /* Reset the format rect and the margins */
3953  GetClientRect(es->hwndSelf, &clientRect);
3954  EDIT_SetRectNP(es, &clientRect);
3957 
3958  if (es->style & ES_MULTILINE)
3960  else
3962 
3963  if (redraw)
3965  if (es->flags & EF_FOCUSED) {
3966  DestroyCaret();
3967 #ifdef __REACTOS__
3968  CreateCaret(es->hwndSelf, NULL, es->dwCaretWidth, es->line_height);
3969 #else
3970  CreateCaret(es->hwndSelf, 0, 1, es->line_height);
3971 #endif
3972  EDIT_SetCaretPos(es, es->selection_end,
3973  es->flags & EF_AFTER_WRAP);
3974  ShowCaret(es->hwndSelf);
3975  }
3976 }
3977 
3978 
3979 /*********************************************************************
3980  *
3981  * WM_SETTEXT
3982  *
3983  * NOTES
3984  * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
3985  * The modified flag is reset. No notifications are sent.
3986  *
3987  * For single-line controls, reception of WM_SETTEXT triggers:
3988  * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
3989  *
3990  */
3991 static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text, BOOL unicode)
3992 {
3993  LPWSTR textW = NULL;
3994  if (!unicode && text)
3995  {
3996  LPCSTR textA = (LPCSTR)text;
3997  INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
3998  textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR));
3999  if (textW)
4000  MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
4001  text = textW;
4002  }
4003 
4004  if (es->flags & EF_UPDATE)
4005  /* fixed this bug once; complain if we see it about to happen again. */
4006  ERR("SetSel may generate UPDATE message whose handler may reset "
4007  "selection.\n");
4008 
4009  EDIT_EM_SetSel(es, 0, (UINT)-1, FALSE);
4010  if (text)
4011  {
4012  TRACE("%s\n", debugstr_w(text));
4014  if(!unicode)
4015  HeapFree(GetProcessHeap(), 0, textW);
4016  }
4017  else
4018  {
4019  TRACE("<NULL>\n");
4021  }
4022  es->x_offset = 0;
4023  es->flags &= ~EF_MODIFIED;
4024  EDIT_EM_SetSel(es, 0, 0, FALSE);
4025 
4026  /* Send the notification after the selection start and end have been set
4027  * edit control doesn't send notification on WM_SETTEXT
4028  * if it is multiline, or it is part of combobox
4029  */
4030  if( !((es->style & ES_MULTILINE) || es->hwndListBox))
4031  {
4034  }
4038 }
4039 
4040 
4041 /*********************************************************************
4042  *
4043  * WM_SIZE
4044  *
4045  */
4047 {
4048  if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4049  RECT rc;
4050  GetClientRect(es->hwndSelf, &rc);
4051  EDIT_SetRectNP(es, &rc);
4053  }
4054 }
4055 
4056 
4057 /*********************************************************************
4058  *
4059  * WM_STYLECHANGED
4060  *
4061  * This message is sent by SetWindowLong on having changed either the Style
4062  * or the extended style.
4063  *
4064  * We ensure that the window's version of the styles and the EDITSTATE's agree.
4065  *
4066  * See also EDIT_WM_NCCreate
4067  *
4068  * It appears that the Windows version of the edit control allows the style
4069  * (as retrieved by GetWindowLong) to be any value and maintains an internal
4070  * style variable which will generally be different. In this function we
4071  * update the internal style based on what changed in the externally visible
4072  * style.
4073  *
4074  * Much of this content as based upon the MSDN, especially:
4075  * Platform SDK Documentation -> User Interface Services ->
4076  * Windows User Interface -> Edit Controls -> Edit Control Reference ->
4077  * Edit Control Styles
4078  */
4080 {
4081  if (GWL_STYLE == which) {
4082  DWORD style_change_mask;
4083  DWORD new_style;
4084  /* Only a subset of changes can be applied after the control
4085  * has been created.
4086  */
4087  style_change_mask = ES_UPPERCASE | ES_LOWERCASE |
4088  ES_NUMBER;
4089  if (es->style & ES_MULTILINE)
4090  style_change_mask |= ES_WANTRETURN;
4091 
4092  new_style = style->styleNew & style_change_mask;
4093 
4094  /* Number overrides lowercase overrides uppercase (at least it
4095  * does in Win95). However I'll bet that ES_NUMBER would be
4096  * invalid under Win 3.1.
4097  */
4098  if (new_style & ES_NUMBER) {
4099  ; /* do not override the ES_NUMBER */
4100  } else if (new_style & ES_LOWERCASE) {
4101  new_style &= ~ES_UPPERCASE;
4102  }
4103 
4104  es->style = (es->style & ~style_change_mask) | new_style;
4105  } else if (GWL_EXSTYLE == which) {
4106  ; /* FIXME - what is needed here */
4107  } else {
4108  WARN ("Invalid style change %ld\n",which);
4109  }
4110 
4111  return 0;
4112 }
4113 
4114 /*********************************************************************
4115  *
4116  * WM_SYSKEYDOWN
4117  *
4118  */
4120 {
4121  if ((key == VK_BACK) && (key_data & 0x2000)) {
4122  if (EDIT_EM_CanUndo(es))
4123  EDIT_EM_Undo(es);
4124  return 0;
4125  } else if (key == VK_UP || key == VK_DOWN) {
4127  return 0;
4128  }
4129  return DefWindowProcW(es->hwndSelf, WM_SYSKEYDOWN, key, key_data);
4130 }
4131 
4132 
4133 /*********************************************************************
4134  *
4135  * WM_TIMER
4136  *
4137  */
4139 {
4140  if (es->region_posx < 0) {
4142  } else if (es->region_posx > 0) {
4144  }
4145 /*
4146  * FIXME: gotta do some vertical scrolling here, like
4147  * EDIT_EM_LineScroll(hwnd, 0, 1);
4148  */
4149 }
4150 
4151 /*********************************************************************
4152  *
4153  * WM_HSCROLL
4154  *
4155  */
4157 {
4158  INT dx;
4159  INT fw;
4160 
4161  if (!(es->style & ES_MULTILINE))
4162  return 0;
4163 
4164  if (!(es->style & ES_AUTOHSCROLL))
4165  return 0;
4166 
4167  dx = 0;
4168  fw = es->format_rect.right - es->format_rect.left;
4169  switch (action) {
4170  case SB_LINELEFT:
4171  TRACE("SB_LINELEFT\n");
4172  if (es->x_offset)
4173  dx = -es->char_width;
4174  break;
4175  case SB_LINERIGHT:
4176  TRACE("SB_LINERIGHT\n");
4177  if (es->x_offset < es->text_width)
4178  dx = es->char_width;
4179  break;
4180  case SB_PAGELEFT:
4181  TRACE("SB_PAGELEFT\n");
4182  if (es->x_offset)
4183  dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
4184  break;
4185  case SB_PAGERIGHT:
4186  TRACE("SB_PAGERIGHT\n");
4187  if (