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