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