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