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