source: trunk/src/user32/edit.cpp@ 3105

Last change on this file since 3105 was 3028, checked in by sandervl, 26 years ago

fixed EN_LINESCROLL bug

File size: 142.8 KB
Line 
1/* $Id: edit.cpp,v 1.37 2000-03-06 22:39:03 sandervl Exp $ */
2/*
3 * Edit control
4 *
5 * Copyright David W. Metcalfe, 1994
6 * Copyright William Magro, 1995, 1996
7 * Copyright Frans van Dorsselaer, 1996, 1997
8 *
9 * Copyright 1999 Christoph Bratschi
10 *
11 * Corel version: 20000212
12 * WINE version: 991212
13 *
14 * Status: complete
15 * Version: 5.00
16 */
17
18/* CB: todo
19 - EN_ALIGN_LTR_EC, EN_ALIGN_RTL_EC -> undocumented notifications
20 - text alignment for single and multi line (ES_LEFT, ES_RIGHT, ES_CENTER)
21 new in Win98, Win2k: for single line too
22 - WinNT/Win2k: higher size limits (single: 0x7FFFFFFE, multi: none)
23 - too many redraws and recalculations!
24*/
25
26#include <stdlib.h>
27#include <os2win.h>
28#include <string.h>
29#include "controls.h"
30#include "combo.h"
31
32#define DBG_LOCALLOG DBG_edit
33#include "dbglocal.h"
34
35#ifdef DEBUG
36char *GetMsgText(int Msg);
37#endif
38
39#define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
40 FIXME: BTW, Win95 specs say 65535 (do you dare ???) */
41#define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
42
43//#define BUFLIMIT_MULTI 0xFFFFFFFE
44//#define BUFLIMIT_SINGLE 0x7FFFFFFF
45
46#define BUFSTART_MULTI 1024 /* starting size */
47#define BUFSTART_SINGLE 256 /* starting size */
48#define GROWLENGTH 64 /* buffers grow by this much */
49#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
50
51/*
52 * extra flags for EDITSTATE.flags field
53 */
54#define EF_MODIFIED 0x0001 /* text has been modified */
55#define EF_FOCUSED 0x0002 /* we have input focus */
56#define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
57#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
58#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
59#define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
60#define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
61#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
62 wrapped line, instead of in front of the next character */
63#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
64
65typedef enum
66{
67 END_0 = 0, /* line ends with terminating '\0' character */
68 END_WRAP, /* line is wrapped */
69 END_HARD, /* line ends with a hard return '\r\n' */
70 END_SOFT /* line ends with a soft return '\r\r\n' */
71} LINE_END;
72
73typedef struct tagLINEDEF {
74 INT length; /* bruto length of a line in bytes */
75 INT net_length; /* netto length of a line in visible characters */
76 LINE_END ending;
77 INT width; /* width of the line in pixels */
78 struct tagLINEDEF *next;
79} LINEDEF;
80
81typedef struct
82{
83 HANDLE heap; /* our own heap */
84 LPSTR text; /* the actual contents of the control */
85 INT buffer_size; /* the size of the buffer */
86 INT buffer_limit; /* the maximum size to which the buffer may grow */
87 HFONT font; /* NULL means standard system font */
88 INT x_offset; /* scroll offset for multi lines this is in pixels
89 for single lines it's in characters */
90 INT line_height; /* height of a screen line in pixels */
91 INT char_width; /* average character width in pixels */
92 DWORD style; /* sane version of wnd->dwStyle */
93 WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
94 INT undo_insert_count; /* number of characters inserted in sequence */
95 INT undo_position; /* character index of the insertion and deletion */
96 LPSTR undo_text; /* deleted text */
97 INT undo_buffer_size; /* size of the deleted text buffer */
98 INT selection_start; /* == selection_end if no selection */
99 INT selection_end; /* == current caret position */
100 CHAR password_char; /* == 0 if no password char, and for multi line controls */
101 INT left_margin; /* in pixels */
102 INT right_margin; /* in pixels */
103 RECT format_rect;
104 INT region_posx; /* Position of cursor relative to region: */
105 INT region_posy; /* -1: to left, 0: within, 1: to right */
106 EDITWORDBREAKPROCA word_break_procA;
107 INT line_count; /* number of lines */
108 INT y_offset; /* scroll offset in number of lines */
109 BOOL bCaptureState; /* flag indicating whether mouse was captured */
110 BOOL bEnableState; /* flag keeping the enable state */
111 /*
112 * only for multi line controls
113 */
114 INT lock_count; /* amount of re-entries in the EditWndProc */
115 INT tabs_count;
116 LPINT tabs;
117 INT text_width; /* width of the widest line in pixels */
118 LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
119 HLOCAL hloc; /* for controls receiving EM_GETHANDLE */
120} EDITSTATE;
121
122
123#define SWAP_INT32(x,y) do { INT temp = (INT)(x); (x) = (INT)(y); (y) = temp; } while(0)
124#define ORDER_INT(x,y) do { if ((INT)(y) < (INT)(x)) SWAP_INT32((x),(y)); } while(0)
125
126#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
127#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
128
129/* used for disabled or read-only edit control */
130#define EDIT_SEND_CTLCOLORSTATIC(hwnd,hdc) \
131 (SendMessageA(GetParent(hwnd), WM_CTLCOLORSTATIC, \
132 (WPARAM)(hdc), (LPARAM)hwnd))
133#define EDIT_SEND_CTLCOLOR(hwnd,hdc) \
134 (SendMessageA(GetParent(hwnd), WM_CTLCOLOREDIT, \
135 (WPARAM)(hdc), (LPARAM)hwnd))
136#define EDIT_NOTIFY_PARENT(hwnd, wNotifyCode) \
137 (SendMessageA(GetParent(hwnd), WM_COMMAND, \
138 MAKEWPARAM(GetWindowLongA(hwnd,GWL_ID), wNotifyCode), (LPARAM)hwnd))
139
140/*********************************************************************
141 *
142 * Declarations
143 *
144 */
145
146/*
147 * These functions have trivial implementations
148 * We still like to call them internally
149 * "static inline" makes them more like macro's
150 */
151static inline BOOL EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es);
152static inline void EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es);
153static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es);
154static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es);
155
156/*
157 * Helper functions only valid for one type of control
158 */
159static void EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es);
160static LPSTR EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es);
161static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
162static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
163static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
164static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend);
165static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert);
166/*
167 * Helper functions valid for both single line _and_ multi line controls
168 */
169static INT EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action);
170static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
171static void EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y);
172static void EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
173static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end);
174static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es);
175static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size);
176static BOOL EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size);
177static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
178static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend);
179static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend);
180static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend);
181static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend);
182static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend);
183static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC hdc, INT line, BOOL rev);
184static VOID EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
185static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos, BOOL after_wrap);
186static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT lprc);
187static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force);
188static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action);
189static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc);
190static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache);
191
192/*
193 * EM_XXX message handlers
194 */
195static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y);
196static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol);
197static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es);
198static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es);
199static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es);
200static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch);
201static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es);
202static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es);
203static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es);
204static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es);
205static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
206static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end);
207static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es);
208static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es);
209static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index);
210static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line);
211static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index);
212static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy);
213static LRESULT EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap);
214static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace);
215static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action);
216static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es);
217static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc);
218static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit);
219static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action, INT left, INT right);
220static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified);
221static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c);
222static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly);
223static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc);
224static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc);
225static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
226static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs);
227static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp);
228static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es);
229static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
230static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
231/*
232 * WM_XXX message handlers
233 */
234static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data);
235static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND conrtol);
236static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y);
237static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es);
238static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs);
239static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es);
240static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc);
241static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text);
242static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
243static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
244static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus);
245static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
246static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
247static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
248static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es);
249static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y);
250static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs);
251static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam);
252static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es);
253static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus);
254static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw);
255static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text);
256static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height);
257static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data);
258static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc);
259static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar);
260static LRESULT EDIT_WM_MouseWheel(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam);
261
262/*********************************************************************
263 *
264 * EM_CANUNDO
265 *
266 */
267static inline BOOL EDIT_EM_CanUndo(HWND hwnd, EDITSTATE *es)
268{
269 return (es->undo_insert_count || lstrlenA(es->undo_text));
270}
271
272
273/*********************************************************************
274 *
275 * EM_EMPTYUNDOBUFFER
276 *
277 */
278static inline void EDIT_EM_EmptyUndoBuffer(HWND hwnd, EDITSTATE *es)
279{
280 es->undo_insert_count = 0;
281 *es->undo_text = '\0';
282}
283
284
285/*********************************************************************
286 *
287 * WM_CLEAR
288 *
289 */
290static inline void EDIT_WM_Clear(HWND hwnd, EDITSTATE *es)
291{
292 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "");
293 if (es->flags & EF_UPDATE) {
294 es->flags &= ~EF_UPDATE;
295 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
296 }
297}
298
299
300/*********************************************************************
301 *
302 * WM_CUT
303 *
304 */
305static inline void EDIT_WM_Cut(HWND hwnd, EDITSTATE *es)
306{
307 EDIT_WM_Copy(hwnd, es);
308 EDIT_WM_Clear(hwnd, es);
309}
310
311
312/*********************************************************************
313 *
314 * EditWndProc()
315 *
316 * The messages are in the order of the actual integer values
317 * (which can be found in include/windows.h)
318 * Whereever possible the 16 bit versions are converted to
319 * the 32 bit ones, so that we can 'fall through' to the
320 * helper functions. These are mostly 32 bit (with a few
321 * exceptions, clearly indicated by a '16' extension to their
322 * names).
323 *
324 */
325LRESULT WINAPI EditWndProc( HWND hwnd, UINT msg,
326 WPARAM wParam, LPARAM lParam )
327{
328 EDITSTATE *es = (EDITSTATE*)GetInfoPtr(hwnd);
329 LRESULT result = 0;
330
331// dprintf(("EditWndProc hwnd: %04x, msg %s, wp %08x lp %08lx\n",
332// hwnd, GetMsgText(msg), wParam, lParam));
333
334 switch (msg) {
335 case WM_DESTROY:
336 //DPRINTF_EDIT_MSG32("WM_DESTROY");
337 EDIT_WM_Destroy(hwnd, es);
338 result = 0;
339 goto END;
340
341 case WM_NCCREATE:
342 //DPRINTF_EDIT_MSG32("WM_NCCREATE");
343 result = EDIT_WM_NCCreate(hwnd, (LPCREATESTRUCTA)lParam);
344 goto END;
345 }
346
347 if (!es)
348 {
349 result = DefWindowProcA(hwnd, msg, wParam, lParam);
350 goto END;
351 }
352
353
354 EDIT_LockBuffer(hwnd, es);
355 switch (msg) {
356 case EM_GETSEL:
357 //DPRINTF_EDIT_MSG32("EM_GETSEL");
358 result = EDIT_EM_GetSel(hwnd, es, (LPUINT)wParam, (LPUINT)lParam);
359 break;
360
361 case EM_SETSEL:
362 //DPRINTF_EDIT_MSG32("EM_SETSEL");
363 EDIT_EM_SetSel(hwnd, es, wParam, lParam, FALSE);
364 EDIT_EM_ScrollCaret(hwnd,es);
365 result = 1;
366 break;
367
368 case EM_GETRECT:
369 //DPRINTF_EDIT_MSG32("EM_GETRECT");
370 EDIT_EM_GetRect(hwnd,es,(LPRECT)lParam);
371 break;
372
373 case EM_SETRECT:
374 //DPRINTF_EDIT_MSG32("EM_SETRECT");
375 EDIT_EM_SetRect(hwnd,es,(LPRECT)lParam);
376 break;
377
378 case EM_SETRECTNP:
379 //DPRINTF_EDIT_MSG32("EM_SETRECTNP");
380 EDIT_EM_SetRectNP(hwnd,es,(LPRECT)lParam);
381 break;
382
383 case EM_SCROLL:
384 //DPRINTF_EDIT_MSG32("EM_SCROLL");
385 result = EDIT_EM_Scroll(hwnd, es, (INT)wParam);
386 break;
387
388 case EM_LINESCROLL:
389 //DPRINTF_EDIT_MSG32("EM_LINESCROLL");
390 result = (LRESULT)EDIT_EM_LineScroll(hwnd, es, (INT)wParam, (INT)lParam);
391 break;
392
393 case EM_SCROLLCARET:
394 //DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
395 EDIT_EM_ScrollCaret(hwnd, es);
396 result = 1;
397 break;
398
399 case EM_GETMODIFY:
400 //DPRINTF_EDIT_MSG32("EM_GETMODIFY");
401 result = (LRESULT)EDIT_EM_GetModify(hwnd,es);
402 break;
403
404 case EM_SETMODIFY:
405 //DPRINTF_EDIT_MSG32("EM_SETMODIFY");
406 EDIT_EM_SetModify(hwnd,es,(BOOL)wParam);
407 break;
408
409 case EM_GETLINECOUNT:
410 //DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
411 result = (LRESULT)EDIT_EM_GetLineCount(hwnd,es);
412 break;
413
414 case EM_LINEINDEX:
415 //DPRINTF_EDIT_MSG32("EM_LINEINDEX");
416 result = (LRESULT)EDIT_EM_LineIndex(hwnd, es, (INT)wParam);
417 break;
418
419 case EM_SETHANDLE:
420 //DPRINTF_EDIT_MSG32("EM_SETHANDLE");
421 EDIT_EM_SetHandle(hwnd, es, (HLOCAL)wParam);
422 break;
423
424 case EM_GETHANDLE:
425 //DPRINTF_EDIT_MSG32("EM_GETHANDLE");
426 result = (LRESULT)EDIT_EM_GetHandle(hwnd, es);
427 break;
428
429 case EM_GETTHUMB:
430 //DPRINTF_EDIT_MSG32("EM_GETTHUMB");
431 result = EDIT_EM_GetThumb(hwnd, es);
432 break;
433
434 /* messages 0x00bf and 0x00c0 missing from specs */
435
436 case WM_USER+15:
437 //DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
438 /* fall through */
439 case 0x00bf:
440 //DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
441 result = DefWindowProcA(hwnd, msg, wParam, lParam);
442 break;
443
444 case WM_USER+16:
445 //DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
446 /* fall through */
447 case 0x00c0:
448 //DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
449 result = DefWindowProcA(hwnd, msg, wParam, lParam);
450 break;
451
452 case EM_LINELENGTH:
453 //DPRINTF_EDIT_MSG32("EM_LINELENGTH");
454 result = (LRESULT)EDIT_EM_LineLength(hwnd, es, (INT)wParam);
455 break;
456
457 case EM_REPLACESEL:
458 //DPRINTF_EDIT_MSG32("EM_REPLACESEL");
459 EDIT_EM_ReplaceSel(hwnd, es, (BOOL)wParam, (LPCSTR)lParam);
460 result = 1;
461 break;
462
463 /* message 0x00c3 missing from specs */
464
465 case WM_USER+19:
466 //DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
467 /* fall through */
468 case 0x00c3:
469 //DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
470 result = DefWindowProcA(hwnd, msg, wParam, lParam);
471 break;
472
473 case EM_GETLINE:
474 //DPRINTF_EDIT_MSG32("EM_GETLINE");
475 result = (LRESULT)EDIT_EM_GetLine(hwnd, es, (INT)wParam, (LPSTR)lParam);
476 break;
477
478 case EM_SETLIMITTEXT:
479 //DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
480 EDIT_EM_SetLimitText(hwnd, es, (INT)wParam);
481 break;
482
483 case EM_CANUNDO:
484 //DPRINTF_EDIT_MSG32("EM_CANUNDO");
485 result = (LRESULT)EDIT_EM_CanUndo(hwnd, es);
486 break;
487
488 case EM_UNDO:
489 /* fall through */
490 case WM_UNDO:
491 //DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
492 result = (LRESULT)EDIT_EM_Undo(hwnd, es);
493 break;
494
495 case EM_FMTLINES:
496 //DPRINTF_EDIT_MSG32("EM_FMTLINES");
497 result = (LRESULT)EDIT_EM_FmtLines(hwnd, es, (BOOL)wParam);
498 break;
499
500 case EM_LINEFROMCHAR:
501 //DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
502 result = (LRESULT)EDIT_EM_LineFromChar(hwnd, es, (INT)wParam);
503 break;
504
505 /* message 0x00ca missing from specs */
506
507 case WM_USER+26:
508 //DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
509 /* fall through */
510 case 0x00ca:
511 //DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
512 result = DefWindowProcA(hwnd, msg, wParam, lParam);
513 break;
514
515 case EM_SETTABSTOPS:
516 //DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
517 result = (LRESULT)EDIT_EM_SetTabStops(hwnd, es, (INT)wParam, (LPINT)lParam);
518 break;
519
520 case EM_SETPASSWORDCHAR:
521 //DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
522 EDIT_EM_SetPasswordChar(hwnd, es, (CHAR)wParam);
523 break;
524
525 case EM_EMPTYUNDOBUFFER:
526 //DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
527 EDIT_EM_EmptyUndoBuffer(hwnd, es);
528 break;
529
530 case EM_GETFIRSTVISIBLELINE:
531 //DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
532 result = (LRESULT)EDIT_EM_GetFirstVisibleLine(hwnd,es);
533 break;
534
535 case EM_SETREADONLY:
536 //DPRINTF_EDIT_MSG32("EM_SETREADONLY");
537 result = (LRESULT)EDIT_EM_SetReadOnly(hwnd,es,(BOOL)wParam);
538 break;
539
540 case EM_SETWORDBREAKPROC:
541 //DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
542 EDIT_EM_SetWordBreakProc(hwnd, es, (EDITWORDBREAKPROCA)lParam);
543 break;
544
545 case EM_GETWORDBREAKPROC:
546 //DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
547 result = (LRESULT)EDIT_EM_GetWordbreakProc(hwnd,es);
548 break;
549
550 case EM_GETPASSWORDCHAR:
551 //DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
552 result = (LRESULT)EDIT_EM_GetPasswordChar(hwnd,es);
553 break;
554
555 /* The following EM_xxx are new to win95 and don't exist for 16 bit */
556
557 case EM_SETMARGINS:
558 //DPRINTF_EDIT_MSG32("EM_SETMARGINS");
559 EDIT_EM_SetMargins(hwnd, es, (INT)wParam, SLOWORD(lParam), SHIWORD(lParam));
560 break;
561
562 case EM_GETMARGINS:
563 //DPRINTF_EDIT_MSG32("EM_GETMARGINS");
564 result = EDIT_EM_GetMargins(hwnd,es);
565 break;
566
567 case EM_GETLIMITTEXT:
568 //DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
569 result = (LRESULT)EDIT_EM_GetLimitText(hwnd,es);
570 break;
571
572 case EM_POSFROMCHAR:
573 //DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
574 result = EDIT_EM_PosFromChar(hwnd,0, es, (INT)wParam, FALSE);
575 break;
576
577 case EM_CHARFROMPOS:
578 //DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
579 result = EDIT_EM_CharFromPos(hwnd, es, SLOWORD(lParam), SHIWORD(lParam));
580 break;
581
582 case EM_SETIMESTATUS:
583 result = EDIT_EM_SetIMEStatus(hwnd,es,wParam,lParam);
584 break;
585
586 case EM_GETIMESTATUS:
587 result = EDIT_EM_GetIMEStatus(hwnd,es,wParam,lParam);
588 break;
589
590 case WM_GETDLGCODE:
591 //DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
592 result = (es->style & ES_MULTILINE) ?
593 DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
594 DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
595 break;
596
597 case WM_CHAR:
598 //DPRINTF_EDIT_MSG32("WM_CHAR");
599 EDIT_WM_Char(hwnd, es, (CHAR)wParam, (DWORD)lParam);
600 break;
601
602 case WM_CLEAR:
603 //DPRINTF_EDIT_MSG32("WM_CLEAR");
604 EDIT_WM_Clear(hwnd, es);
605 break;
606
607 case WM_COMMAND:
608 //DPRINTF_EDIT_MSG32("WM_COMMAND");
609 EDIT_WM_Command(hwnd, es, HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
610 break;
611
612 case WM_CONTEXTMENU:
613 //DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
614 EDIT_WM_ContextMenu(hwnd, es, (HWND)wParam, SLOWORD(lParam), SHIWORD(lParam));
615 break;
616
617 case WM_COPY:
618 //DPRINTF_EDIT_MSG32("WM_COPY");
619 EDIT_WM_Copy(hwnd, es);
620 break;
621
622 case WM_CREATE:
623 //DPRINTF_EDIT_MSG32("WM_CREATE");
624 result = EDIT_WM_Create(hwnd, es, (LPCREATESTRUCTA)lParam);
625 break;
626
627 case WM_CUT:
628 //DPRINTF_EDIT_MSG32("WM_CUT");
629 EDIT_WM_Cut(hwnd, es);
630 break;
631
632 case WM_ENABLE:
633 //DPRINTF_EDIT_MSG32("WM_ENABLE");
634 es->bEnableState = (BOOL)wParam;
635 EDIT_Refresh(hwnd,es,FALSE);
636 break;
637
638 case WM_ERASEBKGND:
639 //DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
640 result = EDIT_WM_EraseBkGnd(hwnd, es, (HDC)wParam);
641 break;
642
643 case WM_GETFONT:
644 //DPRINTF_EDIT_MSG32("WM_GETFONT");
645 result = (LRESULT)es->font;
646 break;
647
648 case WM_GETTEXT:
649 //DPRINTF_EDIT_MSG32("WM_GETTEXT");
650 result = (LRESULT)EDIT_WM_GetText(hwnd, es, (INT)wParam, (LPSTR)lParam);
651 break;
652
653 case WM_GETTEXTLENGTH:
654 //DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
655 result = lstrlenA(es->text);
656 break;
657
658 case WM_HSCROLL:
659 //DPRINTF_EDIT_MSG32("WM_HSCROLL");
660 result = EDIT_WM_HScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)lParam);
661 break;
662
663 case WM_KEYDOWN:
664 //DPRINTF_EDIT_MSG32("WM_KEYDOWN");
665 result = EDIT_WM_KeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
666 break;
667
668 case WM_KILLFOCUS:
669 //DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
670 result = EDIT_WM_KillFocus(hwnd, es, (HWND)wParam);
671 break;
672
673 case WM_LBUTTONDBLCLK:
674 //DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
675 result = EDIT_WM_LButtonDblClk(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
676 break;
677
678 case WM_LBUTTONDOWN:
679 //DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
680 result = EDIT_WM_LButtonDown(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
681 break;
682
683 case WM_LBUTTONUP:
684 //DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
685 result = EDIT_WM_LButtonUp(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
686 break;
687
688 case WM_CAPTURECHANGED:
689 result = EDIT_WM_CaptureChanged(hwnd,es);
690 break;
691
692 case WM_MOUSEACTIVATE:
693 /*
694 * FIXME: maybe DefWindowProc() screws up, but it seems that
695 * modalless dialog boxes need this. If we don't do this, the focus
696 * will _not_ be set by DefWindowProc() for edit controls in a
697 * modalless dialog box ???
698 */
699 //DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
700 result = MA_ACTIVATE;
701 break;
702
703 case WM_MOUSEMOVE:
704 /*
705 * DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
706 */
707 result = EDIT_WM_MouseMove(hwnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
708 break;
709
710 case WM_PAINT:
711 //DPRINTF_EDIT_MSG32("WM_PAINT");
712 EDIT_WM_Paint(hwnd, es,wParam);
713 break;
714
715 case WM_PASTE:
716 //DPRINTF_EDIT_MSG32("WM_PASTE");
717 EDIT_WM_Paste(hwnd, es);
718 break;
719
720 case WM_SETFOCUS:
721 //DPRINTF_EDIT_MSG32("WM_SETFOCUS");
722 EDIT_WM_SetFocus(hwnd, es, (HWND)wParam);
723 break;
724
725 case WM_SETFONT:
726 //DPRINTF_EDIT_MSG32("WM_SETFONT");
727 EDIT_WM_SetFont(hwnd, es, (HFONT)wParam, LOWORD(lParam) != 0);
728 break;
729
730 case WM_SETTEXT:
731 //DPRINTF_EDIT_MSG32("WM_SETTEXT");
732 EDIT_WM_SetText(hwnd, es, (LPCSTR)lParam);
733 result = TRUE;
734 break;
735
736 case WM_SIZE:
737 //DPRINTF_EDIT_MSG32("WM_SIZE");
738 EDIT_WM_Size(hwnd, es, (UINT)wParam, LOWORD(lParam), HIWORD(lParam));
739 break;
740
741 case WM_SYSKEYDOWN:
742 //DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
743 result = EDIT_WM_SysKeyDown(hwnd, es, (INT)wParam, (DWORD)lParam);
744 break;
745
746 case WM_TIMER:
747 //DPRINTF_EDIT_MSG32("WM_TIMER");
748 EDIT_WM_Timer(hwnd, es, (INT)wParam, (TIMERPROC)lParam);
749 break;
750
751 case WM_VSCROLL:
752 //DPRINTF_EDIT_MSG32("WM_VSCROLL");
753 result = EDIT_WM_VScroll(hwnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND)(lParam));
754 break;
755
756 case WM_MOUSEWHEEL:
757 result = EDIT_WM_MouseWheel(hwnd,es,wParam,lParam);
758 break;
759
760 default:
761 result = DefWindowProcA(hwnd, msg, wParam, lParam);
762 break;
763 }
764 EDIT_UnlockBuffer(hwnd, es, FALSE);
765 END:
766 return result;
767
768}
769
770
771/*********************************************************************
772 *
773 * EDIT_BuildLineDefs_ML
774 *
775 * Build linked list of text lines.
776 * Lines can end with '\0' (last line), a character (if it is wrapped),
777 * a soft return '\r\r\n' or a hard return '\r\n'
778 *
779 */
780static void EDIT_BuildLineDefs_ML(HWND hwnd,EDITSTATE *es)
781{
782 HDC hdc = 0;
783 HFONT old_font = 0;
784 LPSTR start, cp;
785 INT fw;
786 LINEDEF *current_def;
787 LINEDEF **previous_next;
788
789 current_def = es->first_line_def;
790 do {
791 LINEDEF *next_def = current_def->next;
792 HeapFree(es->heap, 0, current_def);
793 current_def = next_def;
794 } while (current_def);
795 es->line_count = 0;
796 es->text_width = 0;
797
798 fw = es->format_rect.right - es->format_rect.left;
799 start = es->text;
800 previous_next = &es->first_line_def;
801 do {
802 current_def = (LINEDEF*)HeapAlloc(es->heap, 0, sizeof(LINEDEF));
803 current_def->next = NULL;
804 cp = start;
805 while (*cp) {
806 if ((*cp == '\r') && (*(cp + 1) == '\n'))
807 break;
808 cp++;
809 }
810 if (!(*cp)) {
811 current_def->ending = END_0;
812 current_def->net_length = lstrlenA(start);
813 } else if ((cp > start) && (*(cp - 1) == '\r')) {
814 current_def->ending = END_SOFT;
815 current_def->net_length = cp - start - 1;
816 } else {
817 current_def->ending = END_HARD;
818 current_def->net_length = cp - start;
819 }
820
821 if (!hdc)
822 {
823 hdc = GetDC(hwnd);
824 if (es->font)
825 old_font = SelectObject(hdc, es->font);
826 }
827
828 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
829 start, current_def->net_length,
830 es->tabs_count, es->tabs));
831 /* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
832 if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
833 INT next = 0;
834 INT prev;
835 do {
836 prev = next;
837 next = EDIT_CallWordBreakProc(hwnd, es, start - es->text,
838 prev + 1, current_def->net_length, WB_RIGHT);
839 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
840 start, next, es->tabs_count, es->tabs));
841 } while (current_def->width <= fw);
842 if (!prev) {
843 next = 0;
844 do {
845 prev = next;
846 next++;
847 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc,
848 start, next, es->tabs_count, es->tabs));
849 } while (current_def->width <= fw);
850 if (!prev)
851 prev = 1;
852 }
853 current_def->net_length = prev;
854 current_def->ending = END_WRAP;
855 current_def->width = (INT)LOWORD(GetTabbedTextExtentA(hdc, start,
856 current_def->net_length, es->tabs_count, es->tabs));
857 }
858 switch (current_def->ending) {
859 case END_SOFT:
860 current_def->length = current_def->net_length + 3;
861 break;
862 case END_HARD:
863 current_def->length = current_def->net_length + 2;
864 break;
865 case END_WRAP:
866 case END_0:
867 current_def->length = current_def->net_length;
868 break;
869 }
870 es->text_width = MAX(es->text_width, current_def->width);
871 start += current_def->length;
872 *previous_next = current_def;
873 previous_next = &current_def->next;
874 es->line_count++;
875 } while (current_def->ending != END_0);
876 if (hdc)
877 {
878 if (es->font)
879 SelectObject(hdc, old_font);
880 ReleaseDC(hwnd, hdc);
881 }
882 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
883}
884
885
886/*********************************************************************
887 *
888 * EDIT_CallWordBreakProc
889 *
890 * Call appropriate WordBreakProc (internal or external).
891 *
892 * Note: The "start" argument should always be an index refering
893 * to es->text. The actual wordbreak proc might be
894 * 16 bit, so we can't always pass any 32 bit LPSTR.
895 * Hence we assume that es->text is the buffer that holds
896 * the string under examination (we can decide this for ourselves).
897 *
898 */
899static INT EDIT_CallWordBreakProc(HWND hwnd, EDITSTATE *es, INT start, INT index, INT count, INT action)
900{
901 if (es->word_break_procA)
902 {
903 //TRACE_(relay)("(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
904 // es->word_break_proc32A, es->text + start, index,
905 // count, action );
906 return (INT)es->word_break_procA( es->text + start, index,
907 count, action );
908 }
909 else
910 return EDIT_WordBreakProc(es->text + start, index, count, action);
911}
912
913
914/*********************************************************************
915 *
916 * EDIT_CharFromPos
917 *
918 * Beware: This is not the function called on EM_CHARFROMPOS
919 * The position _can_ be outside the formatting / client
920 * rectangle
921 * The return value is only the character index
922 *
923 */
924static INT EDIT_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y, LPBOOL after_wrap)
925{
926 INT index;
927 HDC dc = 0;
928 HFONT old_font = 0;
929
930 if (es->style & ES_MULTILINE) {
931 INT line = (y - es->format_rect.top) / es->line_height + es->y_offset;
932 INT line_index = 0;
933 LINEDEF *line_def = es->first_line_def;
934 INT low, high;
935 while ((line > 0) && line_def->next) {
936 line_index += line_def->length;
937 line_def = line_def->next;
938 line--;
939 }
940 x += es->x_offset - es->format_rect.left;
941 if (x >= line_def->width) {
942 if (after_wrap)
943 *after_wrap = (line_def->ending == END_WRAP);
944 return line_index + line_def->net_length;
945 }
946 if (x <= 0) {
947 if (after_wrap)
948 *after_wrap = FALSE;
949 return line_index;
950 }
951 low = line_index + 1;
952 high = line_index + line_def->net_length + 1;
953 if (low < high-1)
954 {
955 if (!dc)
956 {
957 dc = GetDC(hwnd);
958 if (es->font)
959 old_font = SelectObject(dc, es->font);
960 }
961
962 while (low < high-1)
963 {
964 INT mid = (low + high) / 2;
965 if (LOWORD(GetTabbedTextExtentA(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
966 else low = mid;
967 }
968 }
969 index = low;
970
971 if (after_wrap)
972 *after_wrap = ((index == line_index + line_def->net_length) &&
973 (line_def->ending == END_WRAP));
974 } else {
975 LPSTR text;
976 SIZE size;
977 if (after_wrap)
978 *after_wrap = FALSE;
979 x -= es->format_rect.left;
980 if (!x)
981 return es->x_offset;
982 text = EDIT_GetPasswordPointer_SL(hwnd, es);
983 if (x < 0)
984 {
985 INT low = 0;
986 INT high = es->x_offset;
987
988 if (low < high-1)
989 {
990 if (!dc)
991 {
992 dc = GetDC(hwnd);
993 if (es->font)
994 old_font = SelectObject(dc, es->font);
995 }
996
997 while (low < high-1)
998 {
999 INT mid = (low + high) / 2;
1000
1001 GetTextExtentPoint32A( dc, text + mid,
1002 es->x_offset - mid, &size );
1003 if (size.cx > -x) low = mid;
1004 else high = mid;
1005 }
1006 }
1007 index = low;
1008 } else
1009 {
1010 INT low = es->x_offset;
1011 INT high = lstrlenA(es->text) + 1;
1012
1013 if (low < high-1)
1014 {
1015 if (!dc)
1016 {
1017 dc = GetDC(hwnd);
1018 if (es->font)
1019 old_font = SelectObject(dc, es->font);
1020 }
1021
1022 while (low < high-1)
1023 {
1024 INT mid = (low + high) / 2;
1025
1026 GetTextExtentPoint32A( dc, text + es->x_offset,
1027 mid - es->x_offset, &size );
1028 if (size.cx > x) high = mid;
1029 else low = mid;
1030 }
1031 }
1032 index = low;
1033 }
1034 if (es->style & ES_PASSWORD)
1035 HeapFree(es->heap, 0 ,text);
1036 }
1037 if (dc)
1038 {
1039 if (es->font)
1040 SelectObject(dc, old_font);
1041 ReleaseDC(hwnd, dc);
1042 }
1043 return index;
1044}
1045
1046
1047/*********************************************************************
1048 *
1049 * EDIT_ConfinePoint
1050 *
1051 * adjusts the point to be within the formatting rectangle
1052 * (so CharFromPos returns the nearest _visible_ character)
1053 *
1054 */
1055static void EDIT_ConfinePoint(HWND hwnd, EDITSTATE *es, LPINT x, LPINT y)
1056{
1057 *x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
1058 *y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
1059}
1060
1061
1062/*********************************************************************
1063 *
1064 * EDIT_GetLineRect
1065 *
1066 * Calculates the bounding rectangle for a line from a starting
1067 * column to an ending column.
1068 *
1069 */
1070static void EDIT_GetLineRect(HWND hwnd,HDC dc,EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc)
1071{
1072 INT line_index = EDIT_EM_LineIndex(hwnd, es, line);
1073
1074 if (es->style & ES_MULTILINE)
1075 rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
1076 else
1077 rc->top = es->format_rect.top;
1078 rc->bottom = rc->top + es->line_height;
1079
1080 rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + scol, TRUE));
1081 rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(hwnd,dc, es, line_index + ecol, TRUE))+1;
1082}
1083
1084
1085/*********************************************************************
1086 *
1087 * EDIT_GetPasswordPointer_SL
1088 *
1089 * note: caller should free the (optionally) allocated buffer
1090 *
1091 */
1092static LPSTR EDIT_GetPasswordPointer_SL(HWND hwnd, EDITSTATE *es)
1093{
1094 if (es->style & ES_PASSWORD) {
1095 INT len = lstrlenA(es->text);
1096 LPSTR text = (LPSTR)HeapAlloc(es->heap, 0, len + 1);
1097 RtlFillMemory(text, len, es->password_char);
1098 text[len] = '\0';
1099 return text;
1100 } else
1101 return es->text;
1102}
1103
1104
1105/*********************************************************************
1106 *
1107 * EDIT_LockBuffer
1108 *
1109 * This acts as a LOCAL_Lock(), but it locks only once. This way
1110 * you can call it whenever you like, without unlocking.
1111 *
1112 */
1113static void EDIT_LockBuffer(HWND hwnd, EDITSTATE *es)
1114{
1115 if (!es) {
1116 //ERR_(edit)("no EDITSTATE ... please report\n");
1117 return;
1118 }
1119 if (!(es->style & ES_MULTILINE))
1120 return;
1121 if (!es->text) {
1122 if (es->hloc)
1123 es->text = (char*)LocalLock(es->hloc);
1124 else {
1125 //ERR_(edit)("no buffer ... please report\n");
1126 return;
1127 }
1128 }
1129 es->lock_count++;
1130}
1131
1132
1133/*********************************************************************
1134 *
1135 * EDIT_SL_InvalidateText
1136 *
1137 * Called from EDIT_InvalidateText().
1138 * Does the job for single-line controls only.
1139 *
1140 */
1141static void EDIT_SL_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1142{
1143 RECT line_rect;
1144 RECT rc;
1145
1146 EDIT_GetLineRect(hwnd,0, es, 0, start, end, &line_rect);
1147
1148 if (IntersectRect(&rc, &line_rect, &es->format_rect))
1149 {
1150 HideCaret(hwnd);
1151 InvalidateRect(hwnd, &rc, TRUE);
1152 ShowCaret(hwnd);
1153 }
1154}
1155
1156
1157/*********************************************************************
1158 *
1159 * EDIT_ML_InvalidateText
1160 *
1161 * Called from EDIT_InvalidateText().
1162 * Does the job for multi-line controls only.
1163 *
1164 */
1165static void EDIT_ML_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1166{
1167 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1168 INT sl = EDIT_EM_LineFromChar(hwnd, es, start);
1169 INT el = EDIT_EM_LineFromChar(hwnd, es, end);
1170 INT sc;
1171 INT ec;
1172 RECT rc1;
1173 RECT rcWnd;
1174 RECT rcLine;
1175 RECT rcUpdate;
1176 INT l;
1177
1178 if ((el < es->y_offset) || (sl > es->y_offset + vlc))
1179 return;
1180
1181 sc = start - EDIT_EM_LineIndex(hwnd, es, sl);
1182 ec = end - EDIT_EM_LineIndex(hwnd, es, el);
1183 if (sl < es->y_offset) {
1184 sl = es->y_offset;
1185 sc = 0;
1186 }
1187 if (el > es->y_offset + vlc) {
1188 el = es->y_offset + vlc;
1189 ec = EDIT_EM_LineLength(hwnd, es, EDIT_EM_LineIndex(hwnd, es, el));
1190 }
1191 GetClientRect(hwnd, &rc1);
1192 IntersectRect(&rcWnd, &rc1, &es->format_rect);
1193 HideCaret(hwnd);
1194 if (sl == el) {
1195 EDIT_GetLineRect(hwnd,0, es, sl, sc, ec, &rcLine);
1196
1197 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1198 InvalidateRect(hwnd, &rcUpdate, TRUE);
1199 } else {
1200 EDIT_GetLineRect(hwnd,0, es, sl, sc,
1201 EDIT_EM_LineLength(hwnd, es,
1202 EDIT_EM_LineIndex(hwnd, es, sl)),
1203 &rcLine);
1204 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1205 InvalidateRect(hwnd, &rcUpdate, TRUE);
1206 for (l = sl + 1 ; l < el ; l++) {
1207 EDIT_GetLineRect(hwnd,0, es, l, 0,
1208 EDIT_EM_LineLength(hwnd, es,
1209 EDIT_EM_LineIndex(hwnd, es, l)),
1210 &rcLine);
1211 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1212 InvalidateRect(hwnd, &rcUpdate, TRUE);
1213 }
1214 EDIT_GetLineRect(hwnd,0, es, el, 0, ec, &rcLine);
1215 if (IntersectRect(&rcUpdate, &rcWnd, &rcLine))
1216 InvalidateRect(hwnd, &rcUpdate, TRUE);
1217 }
1218 ShowCaret(hwnd);
1219}
1220
1221
1222/*********************************************************************
1223 *
1224 * EDIT_InvalidateText
1225 *
1226 * Invalidate the text from offset start upto, but not including,
1227 * offset end. Useful for (re)painting the selection.
1228 * Regions outside the linewidth are not invalidated.
1229 * end == -1 means end == TextLength.
1230 * start and end need not be ordered.
1231 *
1232 */
1233static void EDIT_InvalidateText(HWND hwnd, EDITSTATE *es, INT start, INT end)
1234{
1235 if (end == start)
1236 return;
1237
1238 if (end == -1)
1239 end = lstrlenA(es->text);
1240
1241 ORDER_INT(start, end);
1242
1243 if (es->style & ES_MULTILINE)
1244 EDIT_ML_InvalidateText(hwnd, es, start, end);
1245 else
1246 EDIT_SL_InvalidateText(hwnd, es, start, end);
1247}
1248
1249
1250/*********************************************************************
1251 *
1252 * EDIT_MakeFit
1253 *
1254 * Try to fit size + 1 bytes in the buffer. Constrain to limits.
1255 *
1256 */
1257static BOOL EDIT_MakeFit(HWND hwnd, EDITSTATE *es, INT size)
1258{
1259 HLOCAL hNew32;
1260
1261 if (size <= es->buffer_size)
1262 return TRUE;
1263 if (size > es->buffer_limit) {
1264 EDIT_NOTIFY_PARENT(hwnd, EN_MAXTEXT);
1265 return FALSE;
1266 }
1267 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1268 if (size > es->buffer_limit)
1269 size = es->buffer_limit;
1270
1271 //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
1272
1273 EDIT_UnlockBuffer(hwnd, es, TRUE);
1274 if (es->text) {
1275 es->text = (char*)HeapReAlloc(es->heap, 0, es->text, size + 1);
1276 if (es->text)
1277 es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
1278 else
1279 es->buffer_size = 0;
1280 } else if (es->hloc) {
1281 hNew32 = LocalReAlloc(es->hloc, size + 1, 0);
1282 if (hNew32) {
1283 //TRACE_(edit)("Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
1284 es->hloc = hNew32;
1285 es->buffer_size = MIN(LocalSize(es->hloc) - 1, es->buffer_limit);
1286 }
1287 }
1288 if (es->buffer_size < size) {
1289 EDIT_LockBuffer(hwnd, es);
1290 //WARN_(edit)("FAILED ! We now have %d+1\n", es->buffer_size);
1291 EDIT_NOTIFY_PARENT(hwnd, EN_ERRSPACE);
1292 return FALSE;
1293 } else {
1294 EDIT_LockBuffer(hwnd, es);
1295 //TRACE_(edit)("We now have %d+1\n", es->buffer_size);
1296 return TRUE;
1297 }
1298}
1299
1300
1301/*********************************************************************
1302 *
1303 * EDIT_MakeUndoFit
1304 *
1305 * Try to fit size + 1 bytes in the undo buffer.
1306 *
1307 */
1308static BOOL EDIT_MakeUndoFit(HWND hwnd, EDITSTATE *es, INT size)
1309{
1310 if (size <= es->undo_buffer_size)
1311 return TRUE;
1312 size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
1313
1314 //TRACE_(edit)("trying to ReAlloc to %d+1\n", size);
1315 es->undo_text = (char*)HeapReAlloc(es->heap, 0, es->undo_text, size + 1);
1316 if (es->undo_text) {
1317 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
1318 if (es->undo_buffer_size < size) {
1319 //WARN_(edit)("FAILED ! We now have %d+1\n", es->undo_buffer_size);
1320 return FALSE;
1321 }
1322 return TRUE;
1323 }
1324 return FALSE;
1325}
1326
1327
1328/*********************************************************************
1329 *
1330 * EDIT_MoveBackward
1331 *
1332 */
1333static void EDIT_MoveBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
1334{
1335 INT e = es->selection_end;
1336
1337 if (e) {
1338 e--;
1339 if ((es->style & ES_MULTILINE) && e &&
1340 (es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
1341 e--;
1342 if (e && (es->text[e - 1] == '\r'))
1343 e--;
1344 }
1345 }
1346 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1347 EDIT_EM_ScrollCaret(hwnd, es);
1348}
1349
1350
1351/*********************************************************************
1352 *
1353 * EDIT_MoveDown_ML
1354 *
1355 * Only for multi line controls
1356 * Move the caret one line down, on a column with the nearest
1357 * x coordinate on the screen (might be a different column).
1358 *
1359 */
1360static void EDIT_MoveDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1361{
1362 INT s = es->selection_start;
1363 INT e = es->selection_end;
1364 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1365 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1366 INT x = SLOWORD(pos);
1367 INT y = SHIWORD(pos);
1368
1369 e = EDIT_CharFromPos(hwnd, es, x, y + es->line_height, &after_wrap);
1370 if (!extend)
1371 s = e;
1372 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1373 EDIT_EM_ScrollCaret(hwnd, es);
1374}
1375
1376
1377/*********************************************************************
1378 *
1379 * EDIT_MoveEnd
1380 *
1381 */
1382static void EDIT_MoveEnd(HWND hwnd, EDITSTATE *es, BOOL extend)
1383{
1384 BOOL after_wrap = FALSE;
1385 INT e;
1386
1387 /* Pass a high value in x to make sure of receiving the en of the line */
1388 if (es->style & ES_MULTILINE)
1389 e = EDIT_CharFromPos(hwnd, es, 0x3fffffff,
1390 HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
1391 else
1392 e = lstrlenA(es->text);
1393 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, after_wrap);
1394 EDIT_EM_ScrollCaret(hwnd, es);
1395}
1396
1397
1398/*********************************************************************
1399 *
1400 * EDIT_MoveForward
1401 *
1402 */
1403static void EDIT_MoveForward(HWND hwnd, EDITSTATE *es, BOOL extend)
1404{
1405 INT e = es->selection_end;
1406
1407 if (es->text[e]) {
1408 e++;
1409 if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
1410 if (es->text[e] == '\n')
1411 e++;
1412 else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
1413 e += 2;
1414 }
1415 }
1416 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1417 EDIT_EM_ScrollCaret(hwnd, es);
1418}
1419
1420
1421/*********************************************************************
1422 *
1423 * EDIT_MoveHome
1424 *
1425 * Home key: move to beginning of line.
1426 *
1427 */
1428static void EDIT_MoveHome(HWND hwnd, EDITSTATE *es, BOOL extend)
1429{
1430 INT e;
1431
1432 /* Pass the x_offset in x to make sure of receiving the first position of the line */
1433 if (es->style & ES_MULTILINE)
1434 e = EDIT_CharFromPos(hwnd, es, -es->x_offset,
1435 HIWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
1436 else
1437 e = 0;
1438 EDIT_EM_SetSel(hwnd, es, extend ? es->selection_start : e, e, FALSE);
1439 EDIT_EM_ScrollCaret(hwnd, es);
1440}
1441
1442
1443/*********************************************************************
1444 *
1445 * EDIT_MovePageDown_ML
1446 *
1447 * Only for multi line controls
1448 * Move the caret one page down, on a column with the nearest
1449 * x coordinate on the screen (might be a different column).
1450 *
1451 */
1452static void EDIT_MovePageDown_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1453{
1454 INT s = es->selection_start;
1455 INT e = es->selection_end;
1456 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1457 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1458 INT x = SLOWORD(pos);
1459 INT y = SHIWORD(pos);
1460
1461 e = EDIT_CharFromPos(hwnd, es, x,
1462 y + (es->format_rect.bottom - es->format_rect.top),
1463 &after_wrap);
1464 if (!extend)
1465 s = e;
1466 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1467 EDIT_EM_ScrollCaret(hwnd, es);
1468}
1469
1470
1471/*********************************************************************
1472 *
1473 * EDIT_MovePageUp_ML
1474 *
1475 * Only for multi line controls
1476 * Move the caret one page up, on a column with the nearest
1477 * x coordinate on the screen (might be a different column).
1478 *
1479 */
1480static void EDIT_MovePageUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1481{
1482 INT s = es->selection_start;
1483 INT e = es->selection_end;
1484 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1485 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1486 INT x = SLOWORD(pos);
1487 INT y = SHIWORD(pos);
1488
1489 e = EDIT_CharFromPos(hwnd, es, x,
1490 y - (es->format_rect.bottom - es->format_rect.top),
1491 &after_wrap);
1492 if (!extend)
1493 s = e;
1494 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1495 EDIT_EM_ScrollCaret(hwnd, es);
1496}
1497
1498
1499/*********************************************************************
1500 *
1501 * EDIT_MoveUp_ML
1502 *
1503 * Only for multi line controls
1504 * Move the caret one line up, on a column with the nearest
1505 * x coordinate on the screen (might be a different column).
1506 *
1507 */
1508static void EDIT_MoveUp_ML(HWND hwnd, EDITSTATE *es, BOOL extend)
1509{
1510 INT s = es->selection_start;
1511 INT e = es->selection_end;
1512 BOOL after_wrap = (es->flags & EF_AFTER_WRAP);
1513 LRESULT pos = EDIT_EM_PosFromChar(hwnd,0, es, e, after_wrap);
1514 INT x = SLOWORD(pos);
1515 INT y = SHIWORD(pos);
1516
1517 e = EDIT_CharFromPos(hwnd, es, x, y - es->line_height, &after_wrap);
1518 if (!extend)
1519 s = e;
1520 EDIT_EM_SetSel(hwnd, es, s, e, after_wrap);
1521 EDIT_EM_ScrollCaret(hwnd, es);
1522}
1523
1524
1525/*********************************************************************
1526 *
1527 * EDIT_MoveWordBackward
1528 *
1529 */
1530static void EDIT_MoveWordBackward(HWND hwnd, EDITSTATE *es, BOOL extend)
1531{
1532 INT s = es->selection_start;
1533 INT e = es->selection_end;
1534 INT l;
1535 INT ll;
1536 INT li;
1537
1538 l = EDIT_EM_LineFromChar(hwnd, es, e);
1539 ll = EDIT_EM_LineLength(hwnd, es, e);
1540 li = EDIT_EM_LineIndex(hwnd, es, l);
1541 if (e - li == 0) {
1542 if (l) {
1543 li = EDIT_EM_LineIndex(hwnd, es, l - 1);
1544 e = li + EDIT_EM_LineLength(hwnd, es, li);
1545 }
1546 } else {
1547 e = li + (INT)EDIT_CallWordBreakProc(hwnd, es,
1548 li, e - li, ll, WB_LEFT);
1549 }
1550 if (!extend)
1551 s = e;
1552 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
1553 EDIT_EM_ScrollCaret(hwnd, es);
1554}
1555
1556
1557/*********************************************************************
1558 *
1559 * EDIT_MoveWordForward
1560 *
1561 */
1562static void EDIT_MoveWordForward(HWND hwnd, EDITSTATE *es, BOOL extend)
1563{
1564 INT s = es->selection_start;
1565 INT e = es->selection_end;
1566 INT l;
1567 INT ll;
1568 INT li;
1569
1570 l = EDIT_EM_LineFromChar(hwnd, es, e);
1571 ll = EDIT_EM_LineLength(hwnd, es, e);
1572 li = EDIT_EM_LineIndex(hwnd, es, l);
1573 if (e - li == ll) {
1574 if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
1575 e = EDIT_EM_LineIndex(hwnd, es, l + 1);
1576 } else {
1577 e = li + EDIT_CallWordBreakProc(hwnd, es,
1578 li, e - li + 1, ll, WB_RIGHT);
1579 }
1580 if (!extend)
1581 s = e;
1582 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
1583 EDIT_EM_ScrollCaret(hwnd, es);
1584}
1585
1586
1587/*********************************************************************
1588 *
1589 * EDIT_PaintLine
1590 *
1591 */
1592static void EDIT_PaintLine(HWND hwnd, EDITSTATE *es, HDC dc, INT line, BOOL rev)
1593{
1594 INT s = es->selection_start;
1595 INT e = es->selection_end;
1596 INT li;
1597 INT ll;
1598 INT x;
1599 INT y;
1600 LRESULT pos;
1601
1602 if (es->style & ES_MULTILINE) {
1603 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
1604 if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
1605 return;
1606 } else if (line)
1607 return;
1608
1609 //TRACE_(edit)("line=%d\n", line);
1610
1611 pos = EDIT_EM_PosFromChar(hwnd,dc, es, EDIT_EM_LineIndex(hwnd, es, line), FALSE);
1612 x = SLOWORD(pos);
1613 y = SHIWORD(pos);
1614 li = EDIT_EM_LineIndex(hwnd, es, line);
1615 ll = EDIT_EM_LineLength(hwnd, es, li);
1616 s = es->selection_start;
1617 e = es->selection_end;
1618 ORDER_INT(s, e);
1619 s = MIN(li + ll, MAX(li, s));
1620 e = MIN(li + ll, MAX(li, e));
1621
1622 if (rev && (s != e) && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL)))
1623 {
1624 HRGN oldRgn,newRgn,combRgn;
1625 RECT rect;
1626 //CB: OS/2 has problems with relative string positions (i.e. Communicator)
1627 // fix: always calculate string from starting point, tab bugs fixed too
1628 // otherwise we have 'dancing characters'
1629
1630 if (!(es->style & ES_MULTILINE))
1631 {
1632 SIZE size;
1633
1634 rect.top = y;
1635 rect.bottom = y+es->line_height;
1636 GetTextExtentPoint32A(dc,es->text+li,s-li,&size);
1637 rect.left = x+size.cx;
1638 GetTextExtentPoint32A(dc,es->text+li,e-li,&size);
1639 rect.right = x+size.cx;
1640
1641 oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1642 GetClipRgn(dc,oldRgn);
1643 newRgn = CreateRectRgnIndirect(&rect);
1644 combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1645 CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
1646 SelectClipRgn(dc,combRgn);
1647 EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
1648 CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
1649 SelectClipRgn(dc,combRgn);
1650 EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
1651 DeleteObject(oldRgn);
1652 DeleteObject(newRgn);
1653 DeleteObject(combRgn);
1654 } else
1655 {
1656 rect.top = y;
1657 rect.bottom = y+es->line_height;
1658 rect.left = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,s-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
1659 rect.right = x+LOWORD(TabbedTextOutA(dc,x,y,es->text+li,e-li,es->tabs_count,es->tabs,es->format_rect.left-es->x_offset));
1660
1661 oldRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1662 GetClipRgn(dc,oldRgn);
1663 newRgn = CreateRectRgnIndirect(&rect);
1664 combRgn = CreateRectRgnIndirect(&rect); //dummy parameter
1665 CombineRgn(combRgn,oldRgn,newRgn,RGN_XOR);
1666 SelectClipRgn(dc,combRgn);
1667 EDIT_PaintText(hwnd,es,dc,x,y,line,0,ll,FALSE);
1668 CombineRgn(combRgn,oldRgn,newRgn,RGN_AND);
1669 SelectClipRgn(dc,combRgn);
1670 EDIT_PaintText(hwnd,es,dc,x,y,line,0,e-li,TRUE);
1671 SelectClipRgn(dc,oldRgn);
1672 DeleteObject(oldRgn);
1673 DeleteObject(newRgn);
1674 DeleteObject(combRgn);
1675 }
1676 } else EDIT_PaintText(hwnd, es, dc, x, y, line, 0, ll, FALSE);
1677}
1678
1679
1680/*********************************************************************
1681 *
1682 * EDIT_PaintText
1683 *
1684 */
1685static VOID EDIT_PaintText(HWND hwnd, EDITSTATE *es, HDC dc, INT x, INT y, INT line, INT col, INT count, BOOL rev)
1686{
1687 COLORREF BkColor;
1688 COLORREF TextColor;
1689 INT li;
1690
1691 if (!count)
1692 return;
1693 BkColor = GetBkColor(dc);
1694 TextColor = GetTextColor(dc);
1695 if (rev)
1696 {
1697 SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT));
1698 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1699 }
1700 li = EDIT_EM_LineIndex(hwnd, es, line);
1701 if (es->style & ES_MULTILINE)
1702 {
1703 TabbedTextOutA(dc, x, y, es->text + li + col, count,
1704 es->tabs_count, es->tabs, es->format_rect.left - es->x_offset);
1705 } else
1706 {
1707 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
1708 POINT pt;
1709
1710 TextOutA(dc,x,y,text+li+col,count);
1711 if (es->style & ES_PASSWORD)
1712 HeapFree(es->heap, 0, text);
1713 }
1714 if (rev)
1715 {
1716 SetBkColor(dc, BkColor);
1717 SetTextColor(dc, TextColor);
1718 }
1719}
1720
1721
1722/*********************************************************************
1723 *
1724 * EDIT_SetCaretPos
1725 *
1726 */
1727static void EDIT_SetCaretPos(HWND hwnd, EDITSTATE *es, INT pos,
1728 BOOL after_wrap)
1729{
1730 LRESULT res = EDIT_EM_PosFromChar(hwnd,0, es, pos, after_wrap);
1731 INT x = SLOWORD(res);
1732 INT y = SHIWORD(res);
1733
1734 if(x < es->format_rect.left)
1735 x = es->format_rect.left;
1736 if(x > es->format_rect.right - 2)
1737 x = es->format_rect.right - 2;
1738 if(y > es->format_rect.bottom)
1739 y = es->format_rect.bottom;
1740 if(y < es->format_rect.top)
1741 y = es->format_rect.top;
1742 SetCaretPos(x, y);
1743 return;
1744}
1745
1746
1747/*********************************************************************
1748 *
1749 * EDIT_SetRectNP
1750 *
1751 * note: this is not (exactly) the handler called on EM_SETRECTNP
1752 * it is also used to set the rect of a single line control
1753 *
1754 */
1755static void EDIT_SetRectNP(HWND hwnd, EDITSTATE *es, LPRECT rc)
1756{
1757 CopyRect(&es->format_rect, rc);
1758 if (es->style & WS_BORDER)
1759 {
1760 INT bw = GetSystemMetrics(SM_CXBORDER)+1,bh = GetSystemMetrics(SM_CYBORDER)+1;
1761
1762 es->format_rect.left += bw;
1763 es->format_rect.top += bh;
1764 es->format_rect.right -= bw;
1765 es->format_rect.bottom -= bh;
1766 }
1767 es->format_rect.left += es->left_margin;
1768 es->format_rect.right -= es->right_margin;
1769 es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
1770 if (es->style & ES_MULTILINE)
1771 es->format_rect.bottom = es->format_rect.top +
1772 MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
1773 else
1774 es->format_rect.bottom = es->format_rect.top + es->line_height;
1775 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
1776 EDIT_BuildLineDefs_ML(hwnd,es);
1777 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
1778}
1779
1780
1781/*********************************************************************
1782 *
1783 * EDIT_UnlockBuffer
1784 *
1785 */
1786static void EDIT_UnlockBuffer(HWND hwnd, EDITSTATE *es, BOOL force)
1787{
1788 if (!es) {
1789 //ERR_(edit)("no EDITSTATE ... please report\n");
1790 return;
1791 }
1792 if (!(es->style & ES_MULTILINE))
1793 return;
1794 if (!es->lock_count) {
1795 //ERR_(edit)("lock_count == 0 ... please report\n");
1796 return;
1797 }
1798 if (!es->text) {
1799 //ERR_(edit)("es->text == 0 ... please report\n");
1800 return;
1801 }
1802 if (force || (es->lock_count == 1)) {
1803 if (es->hloc) {
1804 LocalUnlock(es->hloc);
1805 es->text = NULL;
1806 }
1807 }
1808 es->lock_count--;
1809}
1810
1811
1812/*********************************************************************
1813 *
1814 * EDIT_WordBreakProc
1815 *
1816 * Find the beginning of words.
1817 * Note: unlike the specs for a WordBreakProc, this function only
1818 * allows to be called without linebreaks between s[0] upto
1819 * s[count - 1]. Remember it is only called
1820 * internally, so we can decide this for ourselves.
1821 *
1822 */
1823static INT EDIT_WordBreakProc(LPSTR s, INT index, INT count, INT action)
1824{
1825 INT ret = 0;
1826
1827 //TRACE_(edit)("s=%p, index=%u, count=%u, action=%d\n",
1828 // s, index, count, action);
1829
1830 switch (action) {
1831 case WB_LEFT:
1832 if (!count)
1833 break;
1834 if (index)
1835 index--;
1836 if (s[index] == ' ') {
1837 while (index && (s[index] == ' '))
1838 index--;
1839 if (index) {
1840 while (index && (s[index] != ' '))
1841 index--;
1842 if (s[index] == ' ')
1843 index++;
1844 }
1845 } else {
1846 while (index && (s[index] != ' '))
1847 index--;
1848 if (s[index] == ' ')
1849 index++;
1850 }
1851 ret = index;
1852 break;
1853 case WB_RIGHT:
1854 if (!count)
1855 break;
1856 if (index)
1857 index--;
1858 if (s[index] == ' ')
1859 while ((index < count) && (s[index] == ' ')) index++;
1860 else {
1861 while (s[index] && (s[index] != ' ') && (index < count))
1862 index++;
1863 while ((s[index] == ' ') && (index < count)) index++;
1864 }
1865 ret = index;
1866 break;
1867 case WB_ISDELIMITER:
1868 ret = (s[index] == ' ');
1869 break;
1870 default:
1871 //ERR_(edit)("unknown action code, please report !\n");
1872 break;
1873 }
1874 return ret;
1875}
1876
1877
1878/*********************************************************************
1879 *
1880 * EM_CHARFROMPOS
1881 *
1882 * returns line number (not index) in high-order word of result.
1883 * NB : Q137805 is unclear about this. POINT * pointer in lParam apply
1884 * to Richedit, not to the edit control. Original documentation is valid.
1885 *
1886 */
1887static LRESULT EDIT_EM_CharFromPos(HWND hwnd, EDITSTATE *es, INT x, INT y)
1888{
1889 POINT pt;
1890 INT index;
1891
1892 pt.x = x;
1893 pt.y = y;
1894
1895 if (!PtInRect(&es->format_rect,pt)) return -1;
1896
1897 index = EDIT_CharFromPos(hwnd, es, x, y, NULL);
1898 return MAKELONG(index, EDIT_EM_LineFromChar(hwnd, es, index));
1899}
1900
1901
1902/*********************************************************************
1903 *
1904 * EM_FMTLINES
1905 *
1906 * Enable or disable soft breaks.
1907 */
1908static BOOL EDIT_EM_FmtLines(HWND hwnd, EDITSTATE *es, BOOL add_eol)
1909{
1910 es->flags &= ~EF_USE_SOFTBRK;
1911 if (add_eol) {
1912 es->flags |= EF_USE_SOFTBRK;
1913 dprintf(("EDIT: EM_FMTLINES: soft break enabled, not implemented\n"));
1914 }
1915 return add_eol;
1916}
1917
1918static INT EDIT_EM_GetFirstVisibleLine(HWND hwnd,EDITSTATE *es)
1919{
1920 return (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
1921}
1922
1923/*********************************************************************
1924 *
1925 * EM_GETHANDLE
1926 *
1927 * Hopefully this won't fire back at us.
1928 * We always start with a fixed buffer in our own heap.
1929 * However, with this message a 32 bit application requests
1930 * a handle to 32 bit moveable local heap memory, where it expects
1931 * to find the text.
1932 * It's a pity that from this moment on we have to use this
1933 * local heap, because applications may rely on the handle
1934 * in the future.
1935 *
1936 * In this function we'll try to switch to local heap.
1937 *
1938 */
1939static HLOCAL EDIT_EM_GetHandle(HWND hwnd, EDITSTATE *es)
1940{
1941 HLOCAL newBuf;
1942 LPSTR newText;
1943 INT newSize;
1944
1945 if (!(es->style & ES_MULTILINE))
1946 return 0;
1947
1948 if (es->hloc)
1949 return es->hloc;
1950
1951 if (!(newBuf = LocalAlloc(LMEM_MOVEABLE, lstrlenA(es->text) + 1))) {
1952 //ERR_(edit)("could not allocate new 32 bit buffer\n");
1953 return 0;
1954 }
1955 newSize = MIN(LocalSize(newBuf) - 1, es->buffer_limit);
1956 if (!(newText = (char*)LocalLock(newBuf))) {
1957 //ERR_(edit)("could not lock new 32 bit buffer\n");
1958 LocalFree(newBuf);
1959 return 0;
1960 }
1961 lstrcpyA(newText, es->text);
1962 EDIT_UnlockBuffer(hwnd, es, TRUE);
1963 if (es->text)
1964 HeapFree(es->heap, 0, es->text);
1965 es->hloc = newBuf;
1966 es->buffer_size = newSize;
1967 es->text = newText;
1968 EDIT_LockBuffer(hwnd, es);
1969 //TRACE_(edit)("switched to 32 bit local heap\n");
1970
1971 return es->hloc;
1972}
1973
1974static INT EDIT_EM_GetLimitText(HWND hwnd,EDITSTATE *es)
1975{
1976 return es->buffer_limit;
1977}
1978
1979/*********************************************************************
1980 *
1981 * EM_GETLINE
1982 *
1983 */
1984static INT EDIT_EM_GetLine(HWND hwnd, EDITSTATE *es, INT line, LPSTR lpch)
1985{
1986 LPSTR src;
1987 INT len;
1988 INT i;
1989
1990 if (!lpch || (*(WORD*)lpch == 0)) return 0;
1991
1992 if (es->style & ES_MULTILINE) {
1993 if (line >= es->line_count)
1994 return 0;
1995 } else
1996 line = 0;
1997 i = EDIT_EM_LineIndex(hwnd, es, line);
1998 src = es->text + i;
1999 len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(hwnd, es, i));
2000 for (i = 0 ; i < len ; i++) {
2001 *lpch = *src;
2002 src++;
2003 lpch++;
2004 }
2005 //SvL: Terminate string
2006 *lpch = 0;
2007 return (LRESULT)len;
2008}
2009
2010static INT EDIT_EM_GetLineCount(HWND hwnd,EDITSTATE *es)
2011{
2012 return (es->style & ES_MULTILINE) ? es->line_count : 1;
2013}
2014
2015static LONG EDIT_EM_GetMargins(HWND hwnd,EDITSTATE *es)
2016{
2017 return MAKELONG(es->left_margin, es->right_margin);
2018}
2019
2020static BOOL EDIT_EM_GetModify(HWND hwnd,EDITSTATE *es)
2021{
2022 return ((es->flags & EF_MODIFIED) != 0);
2023}
2024
2025static CHAR EDIT_EM_GetPasswordChar(HWND hwnd,EDITSTATE *es)
2026{
2027 return es->password_char;
2028}
2029
2030static VOID EDIT_EM_GetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2031{
2032 if (lprc) CopyRect(lprc,&es->format_rect);
2033}
2034
2035/*********************************************************************
2036 *
2037 * EM_GETSEL
2038 *
2039 */
2040static LRESULT EDIT_EM_GetSel(HWND hwnd, EDITSTATE *es, LPUINT start, LPUINT end)
2041{
2042 UINT s = es->selection_start;
2043 UINT e = es->selection_end;
2044
2045 ORDER_UINT(s, e);
2046
2047 if (start)
2048 *start = s;
2049 if (end)
2050 *end = e;
2051 return MAKELONG(s, e);
2052}
2053
2054/*********************************************************************
2055 *
2056 * EM_GETTHUMB
2057 */
2058static LRESULT EDIT_EM_GetThumb(HWND hwnd, EDITSTATE *es)
2059{
2060 SCROLLINFO si;
2061
2062 if (!(es->style & ES_MULTILINE)) return 0;
2063
2064 si.cbSize = sizeof(si);
2065 si.fMask = SIF_TRACKPOS;
2066 return GetScrollInfo(hwnd,SB_VERT,&si) ? si.nTrackPos:0;
2067}
2068
2069static PVOID EDIT_EM_GetWordbreakProc(HWND hwnd,EDITSTATE *es)
2070{
2071 return es->word_break_procA;
2072}
2073
2074/*********************************************************************
2075 *
2076 * EM_LINEFROMCHAR
2077 *
2078 */
2079static INT EDIT_EM_LineFromChar(HWND hwnd, EDITSTATE *es, INT index)
2080{
2081 INT line;
2082 LINEDEF *line_def;
2083
2084 if (!(es->style & ES_MULTILINE))
2085 return 0;
2086 if (index > lstrlenA(es->text))
2087 return es->line_count - 1;
2088 if (index == -1)
2089 index = MIN(es->selection_start, es->selection_end); //selection_end == caret pos
2090
2091 line = 0;
2092 line_def = es->first_line_def;
2093 index -= line_def->length;
2094 while ((index >= 0) && line_def->next) {
2095 line++;
2096 line_def = line_def->next;
2097 index -= line_def->length;
2098 }
2099 return line;
2100}
2101
2102
2103/*********************************************************************
2104 *
2105 * EM_LINEINDEX
2106 *
2107 */
2108static INT EDIT_EM_LineIndex(HWND hwnd, EDITSTATE *es, INT line)
2109{
2110 INT line_index;
2111 LINEDEF *line_def;
2112
2113 if (!(es->style & ES_MULTILINE))
2114 return 0;
2115 if (line >= es->line_count)
2116 return -1;
2117
2118 line_index = 0;
2119 line_def = es->first_line_def;
2120 if (line == -1) {
2121 INT index = es->selection_end - line_def->length;
2122 while ((index >= 0) && line_def->next) {
2123 line_index += line_def->length;
2124 line_def = line_def->next;
2125 index -= line_def->length;
2126 }
2127 } else {
2128 while (line > 0) {
2129 line_index += line_def->length;
2130 line_def = line_def->next;
2131 line--;
2132 }
2133 }
2134 return line_index;
2135}
2136
2137
2138/*********************************************************************
2139 *
2140 * EM_LINELENGTH
2141 *
2142 */
2143static INT EDIT_EM_LineLength(HWND hwnd, EDITSTATE *es, INT index)
2144{
2145 LINEDEF *line_def;
2146
2147 if (!(es->style & ES_MULTILINE))
2148 return lstrlenA(es->text);
2149
2150 if (index == -1)
2151 {
2152 INT sl = EDIT_EM_LineFromChar(hwnd,es,MIN(es->selection_start,es->selection_end));
2153 INT el = EDIT_EM_LineFromChar(hwnd,es,MAX(es->selection_start,es->selection_end));
2154
2155 if (sl == el)
2156 return EDIT_EM_LineLength(hwnd,es,sl)+es->selection_start-es->selection_end;
2157 else
2158 return es->selection_start+EDIT_EM_LineLength(hwnd,es,el)-es->selection_end;
2159 }
2160 line_def = es->first_line_def;
2161 index -= line_def->length;
2162 while ((index >= 0) && line_def->next) {
2163 line_def = line_def->next;
2164 index -= line_def->length;
2165 }
2166 return line_def->net_length;
2167}
2168
2169static VOID EDIT_UpdateScrollBars(HWND hwnd,EDITSTATE *es,BOOL updateHorz,BOOL updateVert)
2170{
2171 if (updateHorz && (es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK))
2172 {
2173 SCROLLINFO si;
2174 INT fw = es->format_rect.right - es->format_rect.left;
2175
2176 si.cbSize = sizeof(SCROLLINFO);
2177 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2178 si.nMin = 0;
2179 si.nMax = es->text_width + fw - 1;
2180 si.nPage = fw;
2181 si.nPos = es->x_offset;
2182 SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
2183 }
2184
2185 if (updateVert && (es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK))
2186 {
2187 INT vlc = (es->format_rect.bottom-es->format_rect.top)/es->line_height;
2188 SCROLLINFO si;
2189
2190 si.cbSize = sizeof(SCROLLINFO);
2191 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
2192 si.nMin = 0;
2193 si.nMax = es->line_count + vlc - 2;
2194 si.nPage = vlc;
2195 si.nPos = es->y_offset;
2196 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2197 }
2198}
2199
2200
2201/*********************************************************************
2202 *
2203 * EM_LINESCROLL
2204 *
2205 * FIXME: dx is in average character widths
2206 * However, we assume it is in pixels when we use this
2207 * function internally
2208 *
2209 */
2210static BOOL EDIT_EM_LineScroll(HWND hwnd, EDITSTATE *es, INT dx, INT dy)
2211{
2212 INT nyoff;
2213 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2214
2215 if (!(es->style & ES_MULTILINE))
2216 return FALSE;
2217
2218 if (-dx > es->x_offset)
2219 dx = -es->x_offset;
2220 if (dx > es->text_width - es->x_offset)
2221 dx = es->text_width - es->x_offset;
2222 nyoff = MAX(0, es->y_offset + dy);
2223
2224 //SvL: If nyoff > nr lines in control -> last line in control
2225 // window should show last line of control
2226 // Wine code shows last line at the top of the control
2227 // (Quake 3 startup edit control shows the problem)
2228 if (nyoff >= es->line_count) {
2229 if(es->line_count <= vlc) {
2230 nyoff = es->y_offset; //no need to scroll
2231 }
2232 else nyoff = es->line_count - vlc - 1;
2233 }
2234 dy = (es->y_offset - nyoff) * es->line_height;
2235 if (dx || dy)
2236 {
2237 RECT rc1;
2238 RECT rc;
2239
2240 if (dx && !(es->flags & EF_HSCROLL_TRACK))
2241 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
2242 if (dy && !(es->flags & EF_VSCROLL_TRACK))
2243 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2244
2245 GetClientRect(hwnd, &rc1);
2246 IntersectRect(&rc, &rc1, &es->format_rect);
2247
2248 ScrollWindowEx(hwnd,-dx,dy,NULL,&rc,(HRGN)NULL,NULL,SW_INVALIDATE);
2249 es->y_offset = nyoff;
2250 es->x_offset += dx;
2251 EDIT_UpdateScrollBars(hwnd,es,dx,dy);
2252 }
2253
2254 return TRUE;
2255}
2256
2257
2258/*********************************************************************
2259 *
2260 * EM_POSFROMCHAR
2261 *
2262 */
2263static LRESULT EDIT_EM_PosFromChar(HWND hwnd,HDC dc, EDITSTATE *es, INT index, BOOL after_wrap)
2264{
2265 INT len = lstrlenA(es->text);
2266 INT l;
2267 INT li;
2268 INT x;
2269 INT y = 0;
2270 BOOL createdDC = FALSE;
2271 HFONT old_font = 0;
2272 SIZE size;
2273
2274 index = MIN(index, len);
2275 if (!dc)
2276 {
2277 dc = GetDC(hwnd);
2278 if (es->font)
2279 old_font = SelectObject(dc, es->font);
2280 createdDC = TRUE;
2281 }
2282 if (es->style & ES_MULTILINE) {
2283 l = EDIT_EM_LineFromChar(hwnd, es, index);
2284 y = (l - es->y_offset) * es->line_height;
2285 li = EDIT_EM_LineIndex(hwnd, es, l);
2286 if (after_wrap && (li == index) && l) {
2287 INT l2 = l - 1;
2288 LINEDEF *line_def = es->first_line_def;
2289 while (l2) {
2290 line_def = line_def->next;
2291 l2--;
2292 }
2293 if (line_def->ending == END_WRAP) {
2294 l--;
2295 y -= es->line_height;
2296 li = EDIT_EM_LineIndex(hwnd, es, l);
2297 }
2298 }
2299 x = LOWORD(GetTabbedTextExtentA(dc, es->text + li, index - li,
2300 es->tabs_count, es->tabs)) - es->x_offset;
2301 } else
2302 {
2303 LPSTR text = EDIT_GetPasswordPointer_SL(hwnd, es);
2304
2305 GetTextExtentPoint32A(dc,text,index,&size);
2306 x = size.cx;
2307 if (es->x_offset)
2308 {
2309 GetTextExtentPoint32A(dc,text,es->x_offset,&size);
2310 x -= size.cx;
2311 }
2312 y = 0;
2313 if (es->style & ES_PASSWORD)
2314 HeapFree(es->heap, 0 ,text);
2315 }
2316 x += es->format_rect.left;
2317 y += es->format_rect.top;
2318 if (createdDC)
2319 {
2320 if (es->font)
2321 SelectObject(dc, old_font);
2322 ReleaseDC(hwnd, dc);
2323 }
2324 return MAKELONG((INT16)x, (INT16)y);
2325}
2326
2327BOOL EDIT_CheckNumber(CHAR *text)
2328{
2329 if (!text) return TRUE;
2330
2331 while (text[0] != 0)
2332 {
2333 if ((BYTE)text[0] < '0' || (BYTE)text[0] > '9') return FALSE;
2334 text++;
2335 }
2336
2337 return TRUE;
2338}
2339
2340/*********************************************************************
2341 *
2342 * EM_REPLACESEL
2343 *
2344 */
2345static void EDIT_EM_ReplaceSel(HWND hwnd, EDITSTATE *es, BOOL can_undo, LPCSTR lpsz_replace)
2346{
2347 INT strl = lstrlenA(lpsz_replace);
2348 INT tl = lstrlenA(es->text);
2349 INT utl;
2350 UINT s;
2351 UINT e;
2352 INT i;
2353 LPSTR p;
2354
2355 s = es->selection_start;
2356 e = es->selection_end;
2357
2358 if ((s == e) && !strl)
2359 return;
2360
2361 ORDER_UINT(s, e);
2362
2363 if (!EDIT_MakeFit(hwnd, es, tl - (e - s) + strl))
2364 return;
2365
2366 if (e != s) {
2367 /* there is something to be deleted */
2368 if (can_undo) {
2369 utl = lstrlenA(es->undo_text);
2370 if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
2371 /* undo-buffer is extended to the right */
2372 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2373 lstrcpynA(es->undo_text + utl, es->text + s, e - s + 1);
2374 } else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
2375 /* undo-buffer is extended to the left */
2376 EDIT_MakeUndoFit(hwnd, es, utl + e - s);
2377 for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
2378 p[e - s] = p[0];
2379 for (i = 0 , p = es->undo_text ; i < e - s ; i++)
2380 p[i] = (es->text + s)[i];
2381 es->undo_position = s;
2382 } else {
2383 /* new undo-buffer */
2384 EDIT_MakeUndoFit(hwnd, es, e - s);
2385 lstrcpynA(es->undo_text, es->text + s, e - s + 1);
2386 es->undo_position = s;
2387 }
2388 /* any deletion makes the old insertion-undo invalid */
2389 es->undo_insert_count = 0;
2390 } else
2391 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2392
2393 /* now delete */
2394 lstrcpyA(es->text + s, es->text + e);
2395 }
2396 if (strl)
2397 {
2398 if ((es->style & ES_NUMBER) && !EDIT_CheckNumber((CHAR*)lpsz_replace))
2399 MessageBeep(MB_ICONEXCLAMATION);
2400 else
2401 {
2402 /* there is an insertion */
2403 if (can_undo) {
2404 if ((s == es->undo_position) ||
2405 ((es->undo_insert_count) &&
2406 (s == es->undo_position + es->undo_insert_count)))
2407 /*
2408 * insertion is new and at delete position or
2409 * an extension to either left or right
2410 */
2411 es->undo_insert_count += strl;
2412 else {
2413 /* new insertion undo */
2414 es->undo_position = s;
2415 es->undo_insert_count = strl;
2416 /* new insertion makes old delete-buffer invalid */
2417 *es->undo_text = '\0';
2418 }
2419 } else
2420 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2421
2422
2423 /* now insert */
2424 tl = lstrlenA(es->text);
2425 for (p = es->text + tl ; p >= es->text + s ; p--)
2426 p[strl] = p[0];
2427 for (i = 0 , p = es->text + s ; i < strl ; i++)
2428 p[i] = lpsz_replace[i];
2429
2430 if (es->style & ES_OEMCONVERT)
2431 {
2432 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,strl);
2433
2434 CharToOemBuffA(lpsz_replace,text,strl);
2435 OemToCharBuffA(text,p,strl);
2436 HeapFree(es->heap,0,text);
2437 }
2438
2439 if(es->style & ES_UPPERCASE)
2440 CharUpperBuffA(p, strl);
2441 else if(es->style & ES_LOWERCASE)
2442 CharLowerBuffA(p, strl);
2443
2444 s += strl;
2445 }
2446 }
2447 /* FIXME: really inefficient */
2448 if (es->style & ES_MULTILINE)
2449 EDIT_BuildLineDefs_ML(hwnd,es);
2450
2451 EDIT_EM_SetSel(hwnd, es, s, s, FALSE);
2452
2453 es->flags |= EF_MODIFIED;
2454 es->flags |= EF_UPDATE;
2455 EDIT_EM_ScrollCaret(hwnd, es);
2456
2457 /* FIXME: really inefficient */
2458 EDIT_Refresh(hwnd,es,TRUE);
2459}
2460
2461
2462/*********************************************************************
2463 *
2464 * EM_SCROLL
2465 *
2466 */
2467static LRESULT EDIT_EM_Scroll(HWND hwnd, EDITSTATE *es, INT action)
2468{
2469 INT dy;
2470
2471 if (!(es->style & ES_MULTILINE))
2472 return (LRESULT)FALSE;
2473
2474 dy = 0;
2475
2476 switch (action) {
2477 case SB_LINEUP:
2478 if (es->y_offset)
2479 dy = -1;
2480 break;
2481 case SB_LINEDOWN:
2482 if (es->y_offset < es->line_count - 1)
2483 dy = 1;
2484 break;
2485 case SB_PAGEUP:
2486 if (es->y_offset)
2487 dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
2488 break;
2489 case SB_PAGEDOWN:
2490 if (es->y_offset < es->line_count - 1)
2491 dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2492 break;
2493 default:
2494 return (LRESULT)FALSE;
2495 }
2496 if (dy) {
2497 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
2498 EDIT_EM_LineScroll(hwnd, es, 0, dy);
2499 }
2500 return MAKELONG((INT16)dy, (BOOL16)TRUE);
2501}
2502
2503
2504/*********************************************************************
2505 *
2506 * EM_SCROLLCARET
2507 *
2508 */
2509static void EDIT_EM_ScrollCaret(HWND hwnd, EDITSTATE *es)
2510{
2511 if (es->style & ES_MULTILINE) {
2512 INT l;
2513 INT li;
2514 INT vlc;
2515 INT ww;
2516 INT cw = es->char_width;
2517 INT x;
2518 INT dy = 0;
2519 INT dx = 0;
2520
2521 l = EDIT_EM_LineFromChar(hwnd, es, es->selection_end);
2522 li = EDIT_EM_LineIndex(hwnd, es, l);
2523 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, es->flags & EF_AFTER_WRAP));
2524 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
2525 if (l >= es->y_offset + vlc)
2526 dy = l - vlc + 1 - es->y_offset;
2527 if (l < es->y_offset)
2528 dy = l - es->y_offset;
2529 ww = es->format_rect.right - es->format_rect.left;
2530 if (x < es->format_rect.left)
2531 dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
2532 if (x > es->format_rect.right)
2533 dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
2534 if (dy || dx)
2535 EDIT_EM_LineScroll(hwnd, es, dx, dy);
2536 } else {
2537 INT x;
2538 INT goal;
2539 INT format_width;
2540
2541 if (!(es->style & ES_AUTOHSCROLL))
2542 return;
2543
2544 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2545 format_width = es->format_rect.right - es->format_rect.left;
2546 if (x < es->format_rect.left)
2547 {
2548 goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
2549 do {
2550 es->x_offset--;
2551 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2552 } while ((x < goal) && es->x_offset);
2553 /* FIXME: use ScrollWindow() somehow to improve performance */
2554 EDIT_Refresh(hwnd,es,TRUE);
2555 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2556 } else if (x > es->format_rect.right)
2557 {
2558 INT x_last;
2559 INT len = lstrlenA(es->text);
2560 goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
2561 do {
2562 es->x_offset++;
2563 x = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, es->selection_end, FALSE));
2564 x_last = SLOWORD(EDIT_EM_PosFromChar(hwnd,0, es, len, FALSE));
2565 } while ((x > goal) && (x_last > es->format_rect.right));
2566 /* FIXME: use ScrollWindow() somehow to improve performance */
2567 EDIT_Refresh(hwnd,es,TRUE);
2568 EDIT_UpdateScrollBars(hwnd,es,TRUE,FALSE);
2569 }
2570 }
2571}
2572
2573
2574/*********************************************************************
2575 *
2576 * EM_SETHANDLE
2577 *
2578 */
2579static void EDIT_EM_SetHandle(HWND hwnd, EDITSTATE *es, HLOCAL hloc)
2580{
2581 if (!(es->style & ES_MULTILINE))
2582 return;
2583
2584 if (!hloc) {
2585 //WARN_(edit)("called with NULL handle\n");
2586 return;
2587 }
2588
2589 EDIT_UnlockBuffer(hwnd, es, TRUE);
2590 /*
2591 * old buffer is freed by caller, unless
2592 * it is still in our own heap. (in that case
2593 * we free it, correcting the buggy caller.)
2594 */
2595 if (es->text)
2596 HeapFree(es->heap, 0, es->text);
2597
2598 es->hloc = hloc;
2599 es->text = NULL;
2600 es->buffer_size = LocalSize(es->hloc) - 1;
2601 EDIT_LockBuffer(hwnd, es);
2602
2603 if (es->text && (es->text[0] != 0))
2604 {
2605 if (es->style & ES_NUMBER)
2606 {
2607 //CB: todo
2608 }
2609
2610 if (es->style & ES_OEMCONVERT)
2611 {
2612 INT len = lstrlenA(es->text);
2613 CHAR *text = (LPSTR)HeapAlloc(es->heap,0,len);
2614
2615 CharToOemBuffA(es->text,text,len);
2616 OemToCharBuffA(text,es->text,len);
2617 HeapFree(es->heap,0,text);
2618 }
2619
2620 if(es->style & ES_UPPERCASE)
2621 CharUpperA(es->text);
2622 else if(es->style & ES_LOWERCASE)
2623 CharLowerA(es->text);
2624 }
2625
2626 es->x_offset = es->y_offset = 0;
2627 es->selection_start = es->selection_end = 0;
2628 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2629 es->flags &= ~EF_MODIFIED;
2630 es->flags &= ~EF_UPDATE;
2631 EDIT_BuildLineDefs_ML(hwnd,es);
2632 EDIT_Refresh(hwnd,es,FALSE);
2633 EDIT_EM_ScrollCaret(hwnd, es);
2634 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
2635}
2636
2637/*********************************************************************
2638 *
2639 * EM_SETLIMITTEXT
2640 *
2641 * FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
2642 * However, the windows version is not complied to yet in all of edit.c
2643 *
2644 */
2645static void EDIT_EM_SetLimitText(HWND hwnd, EDITSTATE *es, INT limit)
2646{
2647 if (es->style & ES_MULTILINE) {
2648 if (limit)
2649 es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
2650 else
2651 es->buffer_limit = BUFLIMIT_MULTI;
2652 } else {
2653 if (limit)
2654 es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
2655 else
2656 es->buffer_limit = BUFLIMIT_SINGLE;
2657 }
2658}
2659
2660
2661/*********************************************************************
2662 *
2663 * EM_SETMARGINS
2664 *
2665 * EC_USEFONTINFO is used as a left or right value i.e. lParam and not as an
2666 * action wParam despite what the docs say. EC_USEFONTINFO means one third
2667 * of the char's width, according to the new docs.
2668 *
2669 */
2670static void EDIT_EM_SetMargins(HWND hwnd, EDITSTATE *es, INT action,
2671 INT left, INT right)
2672{
2673 RECT r;
2674 INT oldLeft = es->left_margin,oldRight = es->right_margin;
2675
2676 if (action & EC_LEFTMARGIN) {
2677 if (left != EC_USEFONTINFO)
2678 es->left_margin = left;
2679 else
2680 es->left_margin = es->char_width / 3;
2681 }
2682
2683 if (action & EC_RIGHTMARGIN) {
2684 if (right != EC_USEFONTINFO)
2685 es->right_margin = right;
2686 else
2687 es->right_margin = es->char_width / 3;
2688 }
2689 //TRACE_(edit)("left=%d, right=%d\n", es->left_margin, es->right_margin);
2690
2691 if ((oldLeft != es->left_margin) || (oldRight != es->right_margin))
2692 {
2693 GetClientRect(hwnd, &r);
2694 EDIT_SetRectNP(hwnd, es, &r);
2695 if (es->style & ES_MULTILINE)
2696 EDIT_BuildLineDefs_ML(hwnd,es);
2697
2698 EDIT_Refresh(hwnd,es,FALSE);
2699 if (es->flags & EF_FOCUSED) {
2700 DestroyCaret();
2701 CreateCaret(hwnd, 0, 2, es->line_height);
2702 EDIT_SetCaretPos(hwnd, es, es->selection_end,
2703 es->flags & EF_AFTER_WRAP);
2704 ShowCaret(hwnd);
2705 }
2706 }
2707}
2708
2709static void EDIT_EM_SetModify(HWND hwnd,EDITSTATE *es,BOOL fModified)
2710{
2711 if (fModified)
2712 es->flags |= EF_MODIFIED;
2713 else
2714 es->flags &= ~(EF_MODIFIED | EF_UPDATE); /* reset pending updates */
2715}
2716
2717/*********************************************************************
2718 *
2719 * EM_SETPASSWORDCHAR
2720 *
2721 */
2722static void EDIT_EM_SetPasswordChar(HWND hwnd, EDITSTATE *es, CHAR c)
2723{
2724 if (es->style & ES_MULTILINE)
2725 return;
2726
2727 if (es->password_char == c)
2728 return;
2729
2730 es->password_char = c;
2731 if (c) {
2732 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_PASSWORD);
2733 es->style |= ES_PASSWORD;
2734 } else {
2735 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_PASSWORD);
2736 es->style &= ~ES_PASSWORD;
2737 }
2738 EDIT_Refresh(hwnd,es,FALSE);
2739}
2740
2741static BOOL EDIT_EM_SetReadOnly(HWND hwnd,EDITSTATE *es,BOOL fReadOnly)
2742{
2743 if (fReadOnly)
2744 {
2745 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) | ES_READONLY);
2746 es->style |= ES_READONLY;
2747 } else
2748 {
2749 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~ES_READONLY);
2750 es->style &= ~ES_READONLY;
2751 }
2752
2753 return TRUE;
2754}
2755
2756static void EDIT_EM_SetRect(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2757{
2758 if ((es->style & ES_MULTILINE) && lprc)
2759 {
2760 EDIT_SetRectNP(hwnd,es,lprc);
2761 EDIT_Refresh(hwnd,es,FALSE);
2762 }
2763}
2764
2765static void EDIT_EM_SetRectNP(HWND hwnd,EDITSTATE *es,LPRECT lprc)
2766{
2767 if ((es->style & ES_MULTILINE) && lprc)
2768 EDIT_SetRectNP(hwnd,es,lprc);
2769}
2770
2771/*********************************************************************
2772 *
2773 * EDIT_EM_SetSel
2774 *
2775 * note: unlike the specs say: the order of start and end
2776 * _is_ preserved in Windows. (i.e. start can be > end)
2777 * In other words: this handler is OK
2778 *
2779 */
2780static void EDIT_EM_SetSel(HWND hwnd, EDITSTATE *es, UINT start, UINT end, BOOL after_wrap)
2781{
2782 UINT old_start = es->selection_start;
2783 UINT old_end = es->selection_end;
2784 UINT len = lstrlenA(es->text);
2785
2786 if (start == -1) {
2787 start = es->selection_end;
2788 end = es->selection_end;
2789 } else {
2790 start = MIN(start, len);
2791 end = MIN(end, len);
2792 }
2793 es->selection_start = start;
2794 es->selection_end = end;
2795 if (after_wrap)
2796 es->flags |= EF_AFTER_WRAP;
2797 else
2798 es->flags &= ~EF_AFTER_WRAP;
2799 if (es->flags & EF_FOCUSED)
2800 EDIT_SetCaretPos(hwnd, es, end, after_wrap);
2801/* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
2802 ORDER_UINT(start, end);
2803 ORDER_UINT(end, old_end);
2804 ORDER_UINT(start, old_start);
2805 ORDER_UINT(old_start, old_end);
2806 if ((start == old_start) && (end == old_end)) return;
2807 if (end != old_start)
2808 {
2809/*
2810 * One can also do
2811 * ORDER_UINT32(end, old_start);
2812 * EDIT_InvalidateText(wnd, es, start, end);
2813 * EDIT_InvalidateText(wnd, es, old_start, old_end);
2814 * in place of the following if statement.
2815 */
2816 if (old_start > end )
2817 {
2818 EDIT_InvalidateText(hwnd, es, start, end);
2819 EDIT_InvalidateText(hwnd, es, old_start, old_end);
2820 }
2821 else
2822 {
2823 EDIT_InvalidateText(hwnd, es, start, old_start);
2824 EDIT_InvalidateText(hwnd, es, end, old_end);
2825 }
2826 }
2827 else EDIT_InvalidateText(hwnd, es, start, old_end);
2828}
2829
2830
2831/*********************************************************************
2832 *
2833 * EM_SETTABSTOPS
2834 *
2835 */
2836static BOOL EDIT_EM_SetTabStops(HWND hwnd, EDITSTATE *es, INT count, LPINT tabs)
2837{
2838 if (!(es->style & ES_MULTILINE))
2839 return FALSE;
2840 if (es->tabs)
2841 HeapFree(es->heap, 0, es->tabs);
2842 es->tabs_count = count;
2843 if (!count)
2844 es->tabs = NULL;
2845 else {
2846 es->tabs = (INT*)HeapAlloc(es->heap, 0, count * sizeof(INT));
2847 memcpy(es->tabs, tabs, count * sizeof(INT));
2848 }
2849 return TRUE;
2850}
2851
2852
2853/*********************************************************************
2854 *
2855 * EM_SETWORDBREAKPROC
2856 *
2857 */
2858static void EDIT_EM_SetWordBreakProc(HWND hwnd, EDITSTATE *es, EDITWORDBREAKPROCA wbp)
2859{
2860 if (es->word_break_procA == wbp)
2861 return;
2862
2863 es->word_break_procA = wbp;
2864 if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
2865 EDIT_BuildLineDefs_ML(hwnd,es);
2866 EDIT_Refresh(hwnd,es,FALSE);
2867 }
2868}
2869
2870
2871/*********************************************************************
2872 *
2873 * EM_UNDO / WM_UNDO
2874 *
2875 */
2876static BOOL EDIT_EM_Undo(HWND hwnd, EDITSTATE *es)
2877{
2878 INT ulength = lstrlenA(es->undo_text);
2879 LPSTR utext = (LPSTR)HeapAlloc(es->heap, 0, ulength + 1);
2880
2881 lstrcpyA(utext, es->undo_text);
2882
2883 //TRACE_(edit)("before UNDO:insertion length = %d, deletion buffer = %s\n",
2884 // es->undo_insert_count, utext);
2885
2886 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2887 EDIT_EM_EmptyUndoBuffer(hwnd, es);
2888 EDIT_EM_ReplaceSel(hwnd, es, TRUE, utext);
2889 EDIT_EM_SetSel(hwnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
2890 HeapFree(es->heap, 0, utext);
2891
2892 //TRACE_(edit)("after UNDO:insertion length = %d, deletion buffer = %s\n",
2893 // es->undo_insert_count, es->undo_text);
2894
2895 if (es->flags & EF_UPDATE) {
2896 es->flags &= ~EF_UPDATE;
2897 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
2898 }
2899
2900 return TRUE;
2901}
2902
2903static LRESULT EDIT_EM_SetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2904{
2905 if (wParam == EMSIS_COMPOSITIONSTRING)
2906 {
2907 //CB: todo
2908 }
2909
2910 return 0;
2911}
2912
2913static LRESULT EDIT_EM_GetIMEStatus(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
2914{
2915 //CB: todo
2916
2917 return 0; //default
2918}
2919
2920/*********************************************************************
2921 *
2922 * WM_CHAR
2923 *
2924 */
2925static void EDIT_WM_Char(HWND hwnd, EDITSTATE *es, CHAR c, DWORD key_data)
2926{
2927 BOOL control = GetKeyState(VK_CONTROL) & 0x8000;
2928 switch (c) {
2929 case '\r':
2930 /* If the edit doesn't want the return and it's not a multiline edit, do nothing */
2931 if(!(es->style & ES_MULTILINE) && !(es->style & ES_WANTRETURN))
2932 {
2933 MessageBeep(MB_ICONEXCLAMATION);
2934 break;
2935 }
2936 case '\n':
2937 if (es->style & ES_MULTILINE) {
2938 if (es->style & ES_READONLY) {
2939 EDIT_MoveHome(hwnd, es, FALSE);
2940 EDIT_MoveDown_ML(hwnd, es, FALSE);
2941 } else
2942 {
2943 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\r\n");
2944 if (es->flags & EF_UPDATE) {
2945 es->flags &= ~EF_UPDATE;
2946 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
2947 }
2948 }
2949 }
2950 break;
2951 case '\t':
2952 if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
2953 {
2954 EDIT_EM_ReplaceSel(hwnd, es, TRUE, "\t");
2955 if (es->flags & EF_UPDATE) {
2956 es->flags &= ~EF_UPDATE;
2957 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
2958 }
2959 }
2960 break;
2961 case VK_BACK:
2962 if (!(es->style & ES_READONLY) && !control) {
2963 if (es->selection_start != es->selection_end)
2964 EDIT_WM_Clear(hwnd, es);
2965 else {
2966 /* delete character left of caret */
2967 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
2968 EDIT_MoveBackward(hwnd, es, TRUE);
2969 EDIT_WM_Clear(hwnd, es);
2970 }
2971 }
2972 break;
2973//CB: are these three keys documented or Linux style???
2974 case 0x03: /* ^C */
2975 SendMessageA(hwnd, WM_COPY, 0, 0);
2976 break;
2977 case 0x16: /* ^V */
2978 SendMessageA(hwnd, WM_PASTE, 0, 0);
2979 break;
2980 case 0x18: /* ^X */
2981 SendMessageA(hwnd, WM_CUT, 0, 0);
2982 break;
2983
2984 default:
2985 if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127))
2986 {
2987 char str[2];
2988
2989 if (es->style & ES_NUMBER)
2990 {
2991 if (((BYTE)c < '0') || ((BYTE)c > '9')) MessageBeep(MB_ICONEXCLAMATION);
2992 return;
2993 }
2994 str[0] = c;
2995 str[1] = '\0';
2996 EDIT_EM_ReplaceSel(hwnd, es, TRUE, str);
2997 if (es->flags & EF_UPDATE)
2998 {
2999 es->flags &= ~EF_UPDATE;
3000 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
3001 }
3002 } else MessageBeep(MB_ICONEXCLAMATION);
3003 break;
3004 }
3005}
3006
3007
3008/*********************************************************************
3009 *
3010 * WM_COMMAND
3011 *
3012 */
3013static void EDIT_WM_Command(HWND hwnd, EDITSTATE *es, INT code, INT id, HWND control)
3014{
3015 if (code || control)
3016 return;
3017
3018 switch (id) {
3019 case EM_UNDO:
3020 EDIT_EM_Undo(hwnd, es);
3021 break;
3022 case WM_CUT:
3023 EDIT_WM_Cut(hwnd, es);
3024 break;
3025 case WM_COPY:
3026 EDIT_WM_Copy(hwnd, es);
3027 break;
3028 case WM_PASTE:
3029 EDIT_WM_Paste(hwnd, es);
3030 break;
3031 case WM_CLEAR:
3032 EDIT_WM_Clear(hwnd, es);
3033 break;
3034 case EM_SETSEL:
3035 EDIT_EM_SetSel(hwnd, es, 0, -1, FALSE);
3036 EDIT_EM_ScrollCaret(hwnd, es);
3037 break;
3038 default:
3039 //ERR_(edit)("unknown menu item, please report\n");
3040 break;
3041 }
3042}
3043
3044
3045/*********************************************************************
3046 *
3047 * WM_CONTEXTMENU
3048 *
3049 * Note: the resource files resource/sysres_??.rc cannot define a
3050 * single popup menu. Hence we use a (dummy) menubar
3051 * containing the single popup menu as its first item.
3052 *
3053 * FIXME: the message identifiers have been chosen arbitrarily,
3054 * hence we use MF_BYPOSITION.
3055 * We might as well use the "real" values (anybody knows ?)
3056 * The menu definition is in resources/sysres_??.rc.
3057 * Once these are OK, we better use MF_BYCOMMAND here
3058 * (as we do in EDIT_WM_Command()).
3059 *
3060 */
3061static void EDIT_WM_ContextMenu(HWND hwnd, EDITSTATE *es, HWND hwndBtn, INT x, INT y)
3062{
3063 HMENU menu = LoadMenuA(GetModuleHandleA("USER32"), "EDITMENU");
3064 HMENU popup = GetSubMenu(menu, 0);
3065 UINT start = es->selection_start;
3066 UINT end = es->selection_end;
3067
3068 ORDER_UINT(start, end);
3069
3070 /* undo */
3071 EnableMenuItem(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(hwnd, es) ? MF_ENABLED : MF_GRAYED));
3072 /* cut */
3073 EnableMenuItem(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & (ES_PASSWORD | ES_READONLY)) ? MF_ENABLED : MF_GRAYED));
3074 /* copy */
3075 EnableMenuItem(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
3076 /* paste */
3077 EnableMenuItem(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable(CF_TEXT) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3078 /* delete */
3079 EnableMenuItem(popup, 5, MF_BYPOSITION | ((end - start) && !(es->style & ES_READONLY) ? MF_ENABLED : MF_GRAYED));
3080 /* select all */
3081 EnableMenuItem(popup, 7, MF_BYPOSITION | (start || (end != lstrlenA(es->text)) ? MF_ENABLED : MF_GRAYED));
3082
3083 TrackPopupMenu(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, hwnd, NULL);
3084 DestroyMenu(menu);
3085}
3086
3087
3088/*********************************************************************
3089 *
3090 * WM_COPY
3091 *
3092 */
3093static void EDIT_WM_Copy(HWND hwnd, EDITSTATE *es)
3094{
3095 INT s = es->selection_start;
3096 INT e = es->selection_end;
3097 HGLOBAL hdst;
3098 LPSTR dst;
3099
3100 if (e == s)
3101 return;
3102 ORDER_INT(s, e);
3103 hdst = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(e - s + 1));
3104 dst = (LPSTR)GlobalLock(hdst);
3105 lstrcpynA(dst, es->text + s, e - s + 1);
3106 GlobalUnlock(hdst);
3107 OpenClipboard(hwnd);
3108 EmptyClipboard();
3109 SetClipboardData(CF_TEXT, hdst);
3110 CloseClipboard();
3111}
3112
3113
3114/*********************************************************************
3115 *
3116 * WM_CREATE
3117 *
3118 */
3119static LRESULT EDIT_WM_Create(HWND hwnd, EDITSTATE *es, LPCREATESTRUCTA cs)
3120{
3121 /*
3122 * To initialize some final structure members, we call some helper
3123 * functions. However, since the EDITSTATE is not consistent (i.e.
3124 * not fully initialized), we should be very careful which
3125 * functions can be called, and in what order.
3126 */
3127 EDIT_WM_SetFont(hwnd, es, 0, FALSE);
3128 EDIT_EM_EmptyUndoBuffer(hwnd, es);
3129 if (cs->lpszName && *(cs->lpszName) != '\0') {
3130 EDIT_EM_ReplaceSel(hwnd, es, FALSE, cs->lpszName);
3131 /* if we insert text to the editline, the text scrolls out
3132 * of the window, as the caret is placed after the insert
3133 * pos normally; thus we reset es->selection... to 0 and
3134 * update caret
3135 */
3136 es->selection_start = es->selection_end = 0;
3137 EDIT_EM_ScrollCaret(hwnd, es);
3138 if (es->flags & EF_UPDATE) {
3139 es->flags &= ~EF_UPDATE;
3140 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
3141 }
3142 }
3143 return 0;
3144}
3145
3146
3147/*********************************************************************
3148 *
3149 * WM_DESTROY
3150 *
3151 */
3152static void EDIT_WM_Destroy(HWND hwnd, EDITSTATE *es)
3153{
3154 if (!es) /* Protect against multiple destroy messages */
3155 return;
3156
3157 if (es->hloc) {
3158 while (LocalUnlock(es->hloc)) ;
3159 LocalFree(es->hloc);
3160 }
3161
3162 HeapDestroy(es->heap);
3163 HeapFree(GetProcessHeap(), 0, es);
3164 SetInfoPtr(hwnd,0);
3165}
3166
3167
3168/*********************************************************************
3169 *
3170 * WM_ERASEBKGND
3171 *
3172 */
3173static LRESULT EDIT_WM_EraseBkGnd(HWND hwnd, EDITSTATE *es, HDC dc)
3174{
3175 HBRUSH brush;
3176 RECT rc;
3177
3178 HideCaret(hwnd);
3179
3180 if (!es->bEnableState || (es->style & ES_READONLY))
3181 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, dc);
3182 else
3183 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, dc);
3184
3185 if (!brush)
3186 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3187
3188 GetClientRect(hwnd, &rc);
3189 IntersectClipRect(dc, rc.left, rc.top, rc.right, rc.bottom);
3190 GetClipBox(dc, &rc);
3191 /*
3192 * FIXME: specs say that we should UnrealizeObject() the brush,
3193 * but the specs of UnrealizeObject() say that we shouldn't
3194 * unrealize a stock object. The default brush that
3195 * DefWndProc() returns is ... a stock object.
3196 */
3197 FillRect(dc, &rc, brush);
3198
3199 ShowCaret(hwnd);
3200
3201 return -1;
3202}
3203
3204
3205/*********************************************************************
3206 *
3207 * WM_GETTEXT
3208 *
3209 */
3210static INT EDIT_WM_GetText(HWND hwnd, EDITSTATE *es, INT count, LPSTR text)
3211{
3212 INT len;
3213
3214 if (es->text == NULL) // the only case of failure i can imagine
3215 return 0;
3216
3217 len = min(count, lstrlenA(es->text)+1); // determine length
3218 lstrcpynA(text, es->text, len); // copy as much as possible
3219 return len;
3220}
3221
3222
3223/*********************************************************************
3224 *
3225 * EDIT_HScroll_Hack
3226 *
3227 * 16 bit notepad needs this. Actually it is not _our_ hack,
3228 * it is notepad's. Notepad is sending us scrollbar messages with
3229 * undocumented parameters without us even having a scrollbar ... !?!?
3230 *
3231 */
3232static LRESULT EDIT_HScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3233{
3234 INT dx = 0;
3235 INT fw = es->format_rect.right - es->format_rect.left;
3236 LRESULT ret = 0;
3237
3238 if (!(es->flags & EF_HSCROLL_HACK)) {
3239 //ERR_(edit)("hacked WM_HSCROLL handler invoked\n");
3240 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
3241 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
3242 es->flags |= EF_HSCROLL_HACK;
3243 }
3244
3245 switch (action) {
3246 case SB_LINELEFT:
3247 if (es->x_offset)
3248 dx = -es->char_width;
3249 break;
3250 case SB_LINERIGHT:
3251 if (es->x_offset < es->text_width)
3252 dx = es->char_width;
3253 break;
3254 case SB_PAGELEFT:
3255 if (es->x_offset)
3256 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3257 break;
3258 case SB_PAGERIGHT:
3259 if (es->x_offset < es->text_width)
3260 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3261 break;
3262 case SB_LEFT:
3263 if (es->x_offset)
3264 dx = -es->x_offset;
3265 break;
3266 case SB_RIGHT:
3267 if (es->x_offset < es->text_width)
3268 dx = es->text_width - es->x_offset;
3269 break;
3270 case SB_THUMBTRACK:
3271 es->flags |= EF_HSCROLL_TRACK;
3272 dx = pos * es->text_width / 100 - es->x_offset;
3273 break;
3274 case SB_THUMBPOSITION:
3275 es->flags &= ~EF_HSCROLL_TRACK;
3276 if (!(dx = pos * es->text_width / 100 - es->x_offset))
3277 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3278 break;
3279 case SB_ENDSCROLL:
3280 break;
3281
3282 default:
3283 //ERR_(edit)("undocumented (hacked) WM_HSCROLL parameter, please report\n");
3284 return 0;
3285 }
3286 if (dx)
3287 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3288 return ret;
3289}
3290
3291
3292/*********************************************************************
3293 *
3294 * WM_HSCROLL
3295 *
3296 */
3297static LRESULT EDIT_WM_HScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
3298{
3299 INT dx;
3300 INT fw;
3301
3302 if (!(es->style & ES_MULTILINE))
3303 return 0;
3304
3305 if (!(es->style & ES_AUTOHSCROLL))
3306 return 0;
3307
3308 if (!(es->style & WS_HSCROLL))
3309 return EDIT_HScroll_Hack(hwnd, es, action, pos, scroll_bar);
3310
3311 dx = 0;
3312 fw = es->format_rect.right - es->format_rect.left;
3313 switch (action) {
3314 case SB_LINELEFT:
3315 if (es->x_offset)
3316 dx = -es->char_width;
3317 break;
3318 case SB_LINERIGHT:
3319 if (es->x_offset < es->text_width)
3320 dx = es->char_width;
3321 break;
3322 case SB_PAGELEFT:
3323 if (es->x_offset)
3324 dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3325 break;
3326 case SB_PAGERIGHT:
3327 if (es->x_offset < es->text_width)
3328 dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
3329 break;
3330 case SB_LEFT:
3331 if (es->x_offset)
3332 dx = -es->x_offset;
3333 break;
3334 case SB_RIGHT:
3335 if (es->x_offset < es->text_width)
3336 dx = es->text_width - es->x_offset;
3337 break;
3338 case SB_THUMBTRACK:
3339 es->flags |= EF_HSCROLL_TRACK;
3340 dx = pos - es->x_offset;
3341 break;
3342 case SB_THUMBPOSITION:
3343 es->flags &= ~EF_HSCROLL_TRACK;
3344 if (!(dx = pos - es->x_offset)) {
3345 SetScrollPos(hwnd, SB_HORZ, pos, TRUE);
3346 EDIT_NOTIFY_PARENT(hwnd, EN_HSCROLL);
3347 }
3348 break;
3349 case SB_ENDSCROLL:
3350 break;
3351
3352 default:
3353 //ERR_(edit)("undocumented WM_HSCROLL parameter, please report\n");
3354 return 0;
3355 }
3356 if (dx)
3357 EDIT_EM_LineScroll(hwnd, es, dx, 0);
3358 return 0;
3359}
3360
3361
3362/*********************************************************************
3363 *
3364 * EDIT_CheckCombo
3365 *
3366 */
3367static BOOL EDIT_CheckCombo(HWND hwnd, UINT msg, INT key, DWORD key_data)
3368{
3369 HWND hLBox;
3370
3371 /********************************************************************
3372 * This if statement used to check to see if the parent of the
3373 * edit control was a 'combobox' by comparing the ATOM of the parent
3374 * to a table of internal window control ATOMs. However, this check
3375 * would fail if the parent was a superclassed combobox (Although
3376 * having the same basic functionality of a combobox, it has a
3377 * different name and ATOM, thus defeating this check.)
3378 *
3379 * The safe way to determine if the parent is a combobox is to send it
3380 * a message only a combo box would understand. I send it a message
3381 * CB_GETCOUNT, if I get 0 then the parent is not a combobox -
3382 * return FALSE. If I get > 0, then the parent IS a combobox
3383 * (or sub/super classed derrivative thereof)
3384 ********************************************************************/
3385#if 0 //CB: our code
3386 if (GetClassWord(GetParent(hwnd),GCW_ATOM) == GlobalFindAtomA(COMBOBOXCLASSNAME) &&
3387 (hLBox = COMBO_GetLBWindow(GetParent(hwnd))))
3388#else
3389 if (
3390 ((SendMessageA(GetParent(hwnd), CB_GETCOUNT, 0, 0)) > 0) &&
3391 (hLBox = COMBO_GetLBWindow(GetParent(hwnd)))
3392 )
3393#endif
3394 {
3395 HWND hCombo = GetParent(hwnd);
3396 BOOL bUIFlip = TRUE;
3397
3398 //TRACE_(combo)("[%04x]: handling msg %04x (%04x)\n",
3399 // wnd->hwndSelf, (UINT16)msg, (UINT16)key);
3400
3401 switch (msg) {
3402 case WM_KEYDOWN: /* Handle F4 and arrow keys */
3403 if (key != VK_F4) {
3404 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3405 if (SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0))
3406 bUIFlip = FALSE;
3407 }
3408 if (!bUIFlip)
3409 SendMessageA(hLBox, WM_KEYDOWN, (WPARAM)key, 0);
3410 else {
3411 /* make sure ComboLBox pops up */
3412 SendMessageA(hCombo, CB_SETEXTENDEDUI, 0, 0);
3413 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3414 SendMessageA(hCombo, CB_SETEXTENDEDUI, 1, 0);
3415 }
3416 break;
3417 case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
3418 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETEXTENDEDUI, 0, 0);
3419 if (bUIFlip) {
3420 bUIFlip = (BOOL)SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
3421 SendMessageA(hCombo, CB_SHOWDROPDOWN, (bUIFlip) ? FALSE : TRUE, 0);
3422 } else
3423 SendMessageA(hLBox, WM_KEYDOWN, VK_F4, 0);
3424 break;
3425 }
3426 return TRUE;
3427 }
3428 return FALSE;
3429}
3430
3431
3432/*********************************************************************
3433 *
3434 * WM_KEYDOWN
3435 *
3436 * Handling of special keys that don't produce a WM_CHAR
3437 * (i.e. non-printable keys) & Backspace & Delete
3438 *
3439 */
3440static LRESULT EDIT_WM_KeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
3441{
3442 BOOL shift;
3443 BOOL control;
3444
3445 if (GetKeyState(VK_MENU) & 0x8000)
3446 return 0;
3447
3448 shift = GetKeyState(VK_SHIFT) & 0x8000;
3449 control = GetKeyState(VK_CONTROL) & 0x8000;
3450
3451 switch (key) {
3452 case VK_F4:
3453 case VK_UP:
3454 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3455 break;
3456 if (key == VK_F4)
3457 break;
3458 /* fall through */
3459 case VK_LEFT:
3460 if ((es->style & ES_MULTILINE) && (key == VK_UP))
3461 EDIT_MoveUp_ML(hwnd, es, shift);
3462 else
3463 if (control)
3464 EDIT_MoveWordBackward(hwnd, es, shift);
3465 else
3466 EDIT_MoveBackward(hwnd, es, shift);
3467 break;
3468 case VK_DOWN:
3469 if (EDIT_CheckCombo(hwnd, WM_KEYDOWN, key, key_data))
3470 break;
3471 /* fall through */
3472 case VK_RIGHT:
3473 if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
3474 EDIT_MoveDown_ML(hwnd, es, shift);
3475 else if (control)
3476 EDIT_MoveWordForward(hwnd, es, shift);
3477 else
3478 EDIT_MoveForward(hwnd, es, shift);
3479 break;
3480 case VK_HOME:
3481 EDIT_MoveHome(hwnd, es, shift);
3482 break;
3483 case VK_END:
3484 EDIT_MoveEnd(hwnd, es, shift);
3485 break;
3486 case VK_PRIOR:
3487 if (es->style & ES_MULTILINE)
3488 EDIT_MovePageUp_ML(hwnd, es, shift);
3489 break;
3490 case VK_NEXT:
3491 if (es->style & ES_MULTILINE)
3492 EDIT_MovePageDown_ML(hwnd, es, shift);
3493 break;
3494 case VK_DELETE:
3495 if (!(es->style & ES_READONLY) && !(shift && control)) {
3496 if (es->selection_start != es->selection_end) {
3497 if (shift)
3498 EDIT_WM_Cut(hwnd, es);
3499 else
3500 EDIT_WM_Clear(hwnd, es);
3501 } else {
3502 if (shift) {
3503 /* delete character left of caret */
3504 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3505 EDIT_MoveBackward(hwnd, es, TRUE);
3506 EDIT_WM_Clear(hwnd, es);
3507 } else if (control) {
3508 /* delete to end of line */
3509 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3510 EDIT_MoveEnd(hwnd, es, TRUE);
3511 EDIT_WM_Clear(hwnd, es);
3512 } else {
3513 /* delete character right of caret */
3514 EDIT_EM_SetSel(hwnd, es, -1, 0, FALSE);
3515 EDIT_MoveForward(hwnd, es, TRUE);
3516 EDIT_WM_Clear(hwnd, es);
3517 }
3518 }
3519 }
3520 break;
3521 case VK_INSERT:
3522 if (shift) {
3523 if (!(es->style & ES_READONLY))
3524 EDIT_WM_Paste(hwnd, es);
3525 } else if (control)
3526 EDIT_WM_Copy(hwnd, es);
3527 break;
3528 case VK_RETURN:
3529 /* If the edit doesn't want the return send a message to the default object */
3530 if(!(es->style & ES_WANTRETURN))
3531 {
3532 HWND hwndParent = GetParent(hwnd);
3533 DWORD dw = SendMessageA( hwndParent, DM_GETDEFID, 0, 0 );
3534 if (HIWORD(dw) == DC_HASDEFID)
3535 {
3536 SendMessageA( hwndParent, WM_COMMAND,
3537 MAKEWPARAM( LOWORD(dw), BN_CLICKED ),
3538 (LPARAM)GetDlgItem( hwndParent, LOWORD(dw) ) );
3539 }
3540 }
3541 break;
3542 }
3543 return 0;
3544}
3545
3546
3547/*********************************************************************
3548 *
3549 * WM_KILLFOCUS
3550 *
3551 */
3552static LRESULT EDIT_WM_KillFocus(HWND hwnd, EDITSTATE *es, HWND window_getting_focus)
3553{
3554 es->flags &= ~EF_FOCUSED;
3555 DestroyCaret();
3556 if(!(es->style & ES_NOHIDESEL))
3557 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3558 EDIT_NOTIFY_PARENT(hwnd, EN_KILLFOCUS);
3559 return 0;
3560}
3561
3562
3563/*********************************************************************
3564 *
3565 * WM_LBUTTONDBLCLK
3566 *
3567 * The caret position has been set on the WM_LBUTTONDOWN message
3568 *
3569 */
3570static LRESULT EDIT_WM_LButtonDblClk(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3571{
3572 INT s;
3573 INT e = es->selection_end;
3574 INT l;
3575 INT li;
3576 INT ll;
3577
3578 if (!(es->flags & EF_FOCUSED))
3579 return 0;
3580
3581 l = EDIT_EM_LineFromChar(hwnd, es, e);
3582 li = EDIT_EM_LineIndex(hwnd, es, l);
3583 ll = EDIT_EM_LineLength(hwnd, es, e);
3584 s = li + EDIT_CallWordBreakProc (hwnd, es, li, e - li, ll, WB_LEFT);
3585 e = li + EDIT_CallWordBreakProc(hwnd, es, li, e - li, ll, WB_RIGHT);
3586 EDIT_EM_SetSel(hwnd, es, s, e, FALSE);
3587 EDIT_EM_ScrollCaret(hwnd, es);
3588 return 0;
3589}
3590
3591
3592/*********************************************************************
3593 *
3594 * WM_LBUTTONDOWN
3595 *
3596 */
3597static LRESULT EDIT_WM_LButtonDown(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3598{
3599 INT e;
3600 BOOL after_wrap;
3601
3602 if (!(es->flags & EF_FOCUSED))
3603 SetFocus(hwnd);
3604
3605 es->bCaptureState = TRUE;
3606 SetCapture(hwnd);
3607 EDIT_ConfinePoint(hwnd, es, &x, &y);
3608 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3609 EDIT_EM_SetSel(hwnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
3610 EDIT_EM_ScrollCaret(hwnd, es);
3611 es->region_posx = es->region_posy = 0;
3612 SetTimer(hwnd, 0, 100, NULL);
3613 return 0;
3614}
3615
3616
3617/*********************************************************************
3618 *
3619 * WM_LBUTTONUP
3620 *
3621 */
3622static LRESULT EDIT_WM_LButtonUp(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3623{
3624 if (es->bCaptureState)
3625 {
3626 KillTimer(hwnd,0);
3627 ReleaseCapture();
3628 }
3629 es->bCaptureState = FALSE;
3630
3631 return 0;
3632}
3633
3634static LRESULT EDIT_WM_CaptureChanged(HWND hwnd,EDITSTATE *es)
3635{
3636 if (es->bCaptureState) KillTimer(hwnd,0);
3637 es->bCaptureState = FALSE;
3638
3639 return 0;
3640}
3641
3642/*********************************************************************
3643 *
3644 * WM_MOUSEMOVE
3645 *
3646 */
3647static LRESULT EDIT_WM_MouseMove(HWND hwnd, EDITSTATE *es, DWORD keys, INT x, INT y)
3648{
3649 INT e;
3650 BOOL after_wrap;
3651 INT prex, prey;
3652
3653 if (!es->bCaptureState) return 0;
3654
3655 /*
3656 * FIXME: gotta do some scrolling if outside client
3657 * area. Maybe reset the timer ?
3658 */
3659 prex = x; prey = y;
3660 EDIT_ConfinePoint(hwnd, es, &x, &y);
3661 es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
3662 es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
3663 e = EDIT_CharFromPos(hwnd, es, x, y, &after_wrap);
3664 EDIT_EM_SetSel(hwnd, es, es->selection_start, e, after_wrap);
3665
3666 return 0;
3667}
3668
3669
3670/*********************************************************************
3671 *
3672 * WM_NCCREATE
3673 *
3674 */
3675static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTA cs)
3676{
3677 EDITSTATE *es;
3678
3679 if (!(es = (EDITSTATE*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
3680 return FALSE;
3681 SetInfoPtr(hwnd,(DWORD)es);
3682
3683 /*
3684 * Note: since the EDITSTATE has not been fully initialized yet,
3685 * we can't use any API calls that may send
3686 * WM_XXX messages before WM_NCCREATE is completed.
3687 */
3688
3689 if (!(es->heap = HeapCreate(0, 0x10000, 0)))
3690 return FALSE;
3691 es->style = cs->style;
3692
3693 es->bEnableState = !(cs->style & WS_DISABLED);
3694
3695 /*
3696 * In Win95 look and feel, the WS_BORDER style is replaced by the
3697 * WS_EX_CLIENTEDGE style for the edit control. This gives the edit
3698 * control a non client area.
3699 */
3700 if (es->style & WS_BORDER)
3701 {
3702 es->style &= ~WS_BORDER;
3703 SetWindowLongA(hwnd,GWL_STYLE,GetWindowLongA(hwnd,GWL_STYLE) & ~WS_BORDER);
3704 SetWindowLongA(hwnd,GWL_EXSTYLE,GetWindowLongA(hwnd,GWL_EXSTYLE) | WS_EX_CLIENTEDGE);
3705 }
3706
3707 if (es->style & ES_MULTILINE) {
3708 es->buffer_size = BUFSTART_MULTI;
3709 es->buffer_limit = BUFLIMIT_MULTI;
3710 if (es->style & WS_VSCROLL)
3711 es->style |= ES_AUTOVSCROLL;
3712 if (es->style & WS_HSCROLL)
3713 es->style |= ES_AUTOHSCROLL;
3714 es->style &= ~ES_PASSWORD;
3715 if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
3716 if (es->style & ES_RIGHT)
3717 es->style &= ~ES_CENTER;
3718 es->style &= ~WS_HSCROLL;
3719 es->style &= ~ES_AUTOHSCROLL;
3720 }
3721
3722 /* FIXME: for now, all multi line controls are AUTOVSCROLL */
3723 es->style |= ES_AUTOVSCROLL;
3724 } else {
3725 es->buffer_size = BUFSTART_SINGLE;
3726 es->buffer_limit = BUFLIMIT_SINGLE;
3727 es->style &= ~ES_CENTER;
3728 es->style &= ~ES_RIGHT;
3729 es->style &= ~WS_HSCROLL;
3730 es->style &= ~WS_VSCROLL;
3731 es->style &= ~ES_AUTOVSCROLL;
3732 es->style &= ~ES_WANTRETURN;
3733 if (es->style & ES_UPPERCASE) {
3734 es->style &= ~ES_LOWERCASE;
3735 es->style &= ~ES_NUMBER;
3736 } else if (es->style & ES_LOWERCASE)
3737 es->style &= ~ES_NUMBER;
3738 if (es->style & ES_PASSWORD)
3739 es->password_char = '*';
3740
3741 /* FIXME: for now, all single line controls are AUTOHSCROLL */
3742 es->style |= ES_AUTOHSCROLL;
3743 }
3744 if (!(es->text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3745 return FALSE;
3746 es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
3747 if (!(es->undo_text = (char*)HeapAlloc(es->heap, 0, es->buffer_size + 1)))
3748 return FALSE;
3749 es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
3750 *es->text = '\0';
3751 if (es->style & ES_MULTILINE)
3752 if (!(es->first_line_def = (LINEDEF*)HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
3753 return FALSE;
3754 es->line_count = 1;
3755
3756 return TRUE;
3757}
3758
3759static VOID EDIT_Draw(HWND hwnd,EDITSTATE *es,HDC hdc,BOOL eraseBkGnd)
3760{
3761 INT i;
3762 HFONT old_font = 0;
3763 RECT rc;
3764 RECT rcLine;
3765 RECT rcRgn;
3766 BOOL rev = es->bEnableState && ((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL));
3767
3768 HideCaret(hwnd);
3769
3770 if (eraseBkGnd)
3771 {
3772 HBRUSH brush;
3773
3774 if (!es->bEnableState || (es->style & ES_READONLY))
3775 brush = (HBRUSH)EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3776 else
3777 brush = (HBRUSH)EDIT_SEND_CTLCOLOR(hwnd, hdc);
3778
3779 if (!brush)
3780 brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
3781
3782 GetClientRect(hwnd, &rc);
3783 /*
3784 * FIXME: specs say that we should UnrealizeObject() the brush,
3785 * but the specs of UnrealizeObject() say that we shouldn't
3786 * unrealize a stock object. The default brush that
3787 * DefWndProc() returns is ... a stock object.
3788 */
3789 FillRect(hdc, &rc, brush);
3790 }
3791
3792 if(es->style & WS_BORDER)
3793 {
3794 GetClientRect(hwnd, &rc);
3795 if(es->style & ES_MULTILINE)
3796 {
3797 if(es->style & WS_HSCROLL) rc.bottom++;
3798 if(es->style & WS_VSCROLL) rc.right++;
3799 }
3800 Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
3801 }
3802
3803 IntersectClipRect(hdc, es->format_rect.left,
3804 es->format_rect.top,
3805 es->format_rect.right,
3806 es->format_rect.bottom);
3807 if (es->style & ES_MULTILINE)
3808 {
3809 GetClientRect(hwnd, &rc);
3810 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
3811 }
3812 if (es->font) old_font = SelectObject(hdc, es->font);
3813 if (!es->bEnableState || (es->style & ES_READONLY))
3814 EDIT_SEND_CTLCOLORSTATIC(hwnd, hdc);
3815 else
3816 EDIT_SEND_CTLCOLOR(hwnd, hdc);
3817 if (!es->bEnableState)
3818 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
3819 GetClipBox(hdc, &rcRgn);
3820 if (es->style & ES_MULTILINE)
3821 {
3822 INT vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
3823
3824 for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++)
3825 {
3826 EDIT_GetLineRect(hwnd,hdc, es, i, 0, -1, &rcLine);
3827 if (IntersectRect(&rc, &rcRgn, &rcLine))
3828 EDIT_PaintLine(hwnd, es, hdc, i, rev);
3829 }
3830 } else
3831 {
3832 EDIT_GetLineRect(hwnd,hdc, es, 0, 0, -1, &rcLine);
3833 if (IntersectRect(&rc, &rcRgn, &rcLine))
3834 EDIT_PaintLine(hwnd, es, hdc, 0, rev);
3835 }
3836 if (es->font) SelectObject(hdc, old_font);
3837 if (es->flags & EF_FOCUSED)
3838 EDIT_SetCaretPos(hwnd, es, es->selection_end,es->flags & EF_AFTER_WRAP);
3839
3840 ShowCaret(hwnd);
3841}
3842
3843static VOID EDIT_Refresh(HWND hwnd,EDITSTATE *es,BOOL useCache)
3844{
3845 HDC hdc,hdcCompatible;
3846 HBITMAP bitmap,oldbmp;
3847 RECT rect;
3848
3849 if (es->flags & EF_UPDATE)
3850 {
3851 //CB: don't reset flag or EN_CHANGE is lost!
3852 //es->flags &= ~EF_UPDATE;
3853 EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
3854 }
3855
3856 if (!IsWindowVisible(hwnd)) return;
3857
3858 if (!useCache)
3859 {
3860 InvalidateRect(hwnd,NULL,TRUE);
3861 return;
3862 }
3863
3864 //CB: original control redraws many times, cache drawing
3865 HideCaret(hwnd);
3866 GetClientRect(hwnd,&rect);
3867 hdc = GetDC(hwnd);
3868 hdcCompatible = CreateCompatibleDC(hdc);
3869 bitmap = CreateCompatibleBitmap(hdc,rect.right,rect.bottom);
3870 oldbmp = SelectObject(hdcCompatible,bitmap);
3871 EDIT_Draw(hwnd,es,hdcCompatible,TRUE);
3872 SelectClipRgn(hdcCompatible,0);
3873 BitBlt(hdc,0,0,rect.right,rect.bottom,hdcCompatible,0,0,SRCCOPY);
3874 SelectObject(hdcCompatible,oldbmp);
3875 DeleteObject(bitmap);
3876 DeleteDC(hdcCompatible);
3877 ReleaseDC(hwnd,hdc);
3878 ShowCaret(hwnd);
3879}
3880
3881/*********************************************************************
3882 *
3883 * WM_PAINT
3884 *
3885 */
3886static void EDIT_WM_Paint(HWND hwnd, EDITSTATE *es,WPARAM wParam)
3887{
3888 PAINTSTRUCT ps;
3889 HDC hdc;
3890
3891 if (!wParam) hdc = BeginPaint(hwnd, &ps);
3892 else hdc = (HDC) wParam;
3893
3894 EDIT_Draw(hwnd,es,hdc,FALSE);
3895
3896 if (!wParam) EndPaint(hwnd, &ps);
3897}
3898
3899
3900/*********************************************************************
3901 *
3902 * WM_PASTE
3903 *
3904 */
3905static void EDIT_WM_Paste(HWND hwnd, EDITSTATE *es)
3906{
3907 HGLOBAL hsrc;
3908 LPSTR src;
3909
3910 OpenClipboard(hwnd);
3911 hsrc = GetClipboardData(CF_TEXT);
3912 if (hsrc) {
3913 src = (LPSTR)GlobalLock(hsrc);
3914 EDIT_EM_ReplaceSel(hwnd, es, TRUE, src);
3915 GlobalUnlock(hsrc);
3916
3917 if (es->flags & EF_UPDATE) {
3918 es->flags &= ~EF_UPDATE;
3919 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
3920 }
3921 }
3922 CloseClipboard();
3923}
3924
3925
3926/*********************************************************************
3927 *
3928 * WM_SETFOCUS
3929 *
3930 */
3931static void EDIT_WM_SetFocus(HWND hwnd, EDITSTATE *es, HWND window_losing_focus)
3932{
3933 es->flags |= EF_FOCUSED;
3934 CreateCaret(hwnd, 0, 2, es->line_height);
3935 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3936 es->flags & EF_AFTER_WRAP);
3937 if(!(es->style & ES_NOHIDESEL))
3938 EDIT_InvalidateText(hwnd, es, es->selection_start, es->selection_end);
3939 ShowCaret(hwnd);
3940 EDIT_NOTIFY_PARENT(hwnd, EN_SETFOCUS);
3941}
3942
3943/*********************************************************************
3944 *
3945 * WM_SETFONT
3946 *
3947 * With Win95 look the margins are set to default font value unless
3948 * the system font (font == 0) is being set, in which case they are left
3949 * unchanged.
3950 *
3951 */
3952static void EDIT_WM_SetFont(HWND hwnd, EDITSTATE *es, HFONT font, BOOL redraw)
3953{
3954 TEXTMETRICA tm;
3955 HDC dc;
3956 HFONT old_font = 0;
3957 RECT r;
3958
3959 dc = GetDC(hwnd);
3960 if (font)
3961 old_font = SelectObject(dc, font);
3962 if (!GetTextMetricsA(dc, &tm))
3963 {
3964 if (font) SelectObject(dc,old_font);
3965 ReleaseDC(hwnd,dc);
3966
3967 return;
3968 }
3969 es->font = font;
3970 es->line_height = tm.tmHeight;
3971
3972 es->char_width = tm.tmAveCharWidth;
3973 if (font)
3974 SelectObject(dc, old_font);
3975 ReleaseDC(hwnd, dc);
3976 if (font)
3977 EDIT_EM_SetMargins(hwnd, es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
3978 EC_USEFONTINFO, EC_USEFONTINFO);
3979 /* Force the recalculation of the format rect for each font change */
3980 GetClientRect(hwnd, &r);
3981 EDIT_SetRectNP(hwnd, es, &r);
3982 if (es->style & ES_MULTILINE)
3983 EDIT_BuildLineDefs_ML(hwnd,es);
3984
3985 if (redraw)
3986 EDIT_Refresh(hwnd,es,FALSE);
3987 if (es->flags & EF_FOCUSED) {
3988 DestroyCaret();
3989 CreateCaret(hwnd, 0, 2, es->line_height);
3990 EDIT_SetCaretPos(hwnd, es, es->selection_end,
3991 es->flags & EF_AFTER_WRAP);
3992 ShowCaret(hwnd);
3993 }
3994}
3995
3996
3997/*********************************************************************
3998 *
3999 * WM_SETTEXT
4000 *
4001 * NOTES
4002 * For multiline controls (ES_MULTILINE), reception of WM_SETTEXT triggers:
4003 * The modified flag is reset. No notifications are sent.
4004 *
4005 * For single-line controls, reception of WM_SETTEXT triggers:
4006 * The modified flag is reset. EN_UPDATE and EN_CHANGE notifications are sent.
4007 *
4008 */
4009static void EDIT_WM_SetText(HWND hwnd, EDITSTATE *es, LPCSTR text)
4010{
4011 es->selection_start = 0;
4012 es->selection_end = lstrlenA(es->text);
4013 if (es->flags & EF_FOCUSED)
4014 EDIT_SetCaretPos(hwnd, es, es->selection_end, FALSE);
4015
4016 if (text) {
4017 //TRACE_(edit)("\t'%s'\n", text);
4018 EDIT_EM_ReplaceSel(hwnd, es, FALSE, text);
4019 } else {
4020 //TRACE_(edit)("\t<NULL>\n");
4021 EDIT_EM_ReplaceSel(hwnd, es, FALSE, "");
4022 }
4023 es->x_offset = 0;
4024 if (es->style & ES_MULTILINE) {
4025 es->flags &= ~EF_UPDATE;
4026 } else {
4027 es->flags |= EF_UPDATE;
4028 }
4029 es->flags &= ~EF_MODIFIED;
4030 EDIT_EM_SetSel(hwnd, es, 0, 0, FALSE);
4031 EDIT_EM_ScrollCaret(hwnd, es);
4032 EDIT_UpdateScrollBars(hwnd,es,TRUE,TRUE);
4033 if (es->flags & EF_UPDATE)
4034 {
4035 EDIT_NOTIFY_PARENT(hwnd,EN_UPDATE);
4036 /* EN_CHANGE notification need to be sent too
4037 Windows doc says it's only done after the display,
4038 the doc is WRONG. EN_CHANGE notification is sent
4039 while processing WM_SETTEXT */
4040 EDIT_NOTIFY_PARENT(hwnd, EN_CHANGE);
4041 es->flags &= EF_UPDATE;
4042 }
4043}
4044
4045
4046/*********************************************************************
4047 *
4048 * WM_SIZE
4049 *
4050 */
4051static void EDIT_WM_Size(HWND hwnd, EDITSTATE *es, UINT action, INT width, INT height)
4052{
4053 if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
4054 RECT rc;
4055 SetRect(&rc, 0, 0, width, height);
4056 EDIT_SetRectNP(hwnd, es, &rc);
4057 EDIT_Refresh(hwnd,es,FALSE);
4058 }
4059}
4060
4061
4062/*********************************************************************
4063 *
4064 * WM_SYSKEYDOWN
4065 *
4066 */
4067static LRESULT EDIT_WM_SysKeyDown(HWND hwnd, EDITSTATE *es, INT key, DWORD key_data)
4068{
4069 if ((key == VK_BACK) && (key_data & 0x2000)) {
4070 if (EDIT_EM_CanUndo(hwnd, es))
4071 EDIT_EM_Undo(hwnd, es);
4072 return 0;
4073 } else if ((key == VK_UP) || (key == VK_DOWN))
4074 if (EDIT_CheckCombo(hwnd, WM_SYSKEYDOWN, key, key_data))
4075 return 0;
4076 return DefWindowProcA(hwnd, WM_SYSKEYDOWN, (WPARAM)key, (LPARAM)key_data);
4077}
4078
4079
4080/*********************************************************************
4081 *
4082 * WM_TIMER
4083 *
4084 */
4085static void EDIT_WM_Timer(HWND hwnd, EDITSTATE *es, INT id, TIMERPROC timer_proc)
4086{
4087 if (es->region_posx < 0) {
4088 EDIT_MoveBackward(hwnd, es, TRUE);
4089 } else if (es->region_posx > 0) {
4090 EDIT_MoveForward(hwnd, es, TRUE);
4091 }
4092
4093 if (!(es->style & ES_MULTILINE)) return;
4094
4095 if (es->region_posy < 0)
4096 {
4097 EDIT_MoveUp_ML(hwnd,es,TRUE);
4098 } else if (es->region_posy > 0)
4099 {
4100 EDIT_MoveDown_ML(hwnd,es,TRUE);
4101 }
4102}
4103
4104
4105/*********************************************************************
4106 *
4107 * EDIT_VScroll_Hack
4108 *
4109 * 16 bit notepad needs this. Actually it is not _our_ hack,
4110 * it is notepad's. Notepad is sending us scrollbar messages with
4111 * undocumented parameters without us even having a scrollbar ... !?!?
4112 *
4113 */
4114static LRESULT EDIT_VScroll_Hack(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4115{
4116 INT dy = 0;
4117 LRESULT ret = 0;
4118
4119 if (!(es->flags & EF_VSCROLL_HACK)) {
4120 //ERR_(edit)("hacked WM_VSCROLL handler invoked\n");
4121 //ERR_(edit)(" if you are _not_ running 16 bit notepad, please report\n");
4122 //ERR_(edit)(" (this message is only displayed once per edit control)\n");
4123 es->flags |= EF_VSCROLL_HACK;
4124 }
4125
4126 switch (action) {
4127 case SB_LINEUP:
4128 case SB_LINEDOWN:
4129 case SB_PAGEUP:
4130 case SB_PAGEDOWN:
4131 EDIT_EM_Scroll(hwnd, es, action);
4132 return 0;
4133 case SB_TOP:
4134 dy = -es->y_offset;
4135 break;
4136 case SB_BOTTOM:
4137 dy = es->line_count - 1 - es->y_offset;
4138 break;
4139 case SB_THUMBTRACK:
4140 es->flags |= EF_VSCROLL_TRACK;
4141 dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
4142 break;
4143 case SB_THUMBPOSITION:
4144 es->flags &= ~EF_VSCROLL_TRACK;
4145 if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
4146 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
4147 break;
4148 case SB_ENDSCROLL:
4149 break;
4150
4151 default:
4152 //ERR_(edit)("undocumented (hacked) WM_VSCROLL parameter, please report\n");
4153 return 0;
4154 }
4155 if (dy)
4156 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4157 return ret;
4158}
4159
4160
4161/*********************************************************************
4162 *
4163 * WM_VSCROLL
4164 *
4165 */
4166static LRESULT EDIT_WM_VScroll(HWND hwnd, EDITSTATE *es, INT action, INT pos, HWND scroll_bar)
4167{
4168 INT dy;
4169
4170 if (!(es->style & ES_MULTILINE))
4171 return 0;
4172
4173 if (!(es->style & ES_AUTOVSCROLL))
4174 return 0;
4175
4176 if (!(es->style & WS_VSCROLL))
4177 return EDIT_VScroll_Hack(hwnd, es, action, pos, scroll_bar);
4178
4179 dy = 0;
4180 switch (action) {
4181 case SB_LINEUP:
4182 case SB_LINEDOWN:
4183 case SB_PAGEUP:
4184 case SB_PAGEDOWN:
4185 EDIT_EM_Scroll(hwnd, es, action);
4186 return 0;
4187
4188 case SB_TOP:
4189 dy = -es->y_offset;
4190 break;
4191 case SB_BOTTOM:
4192 dy = es->line_count - 1 - es->y_offset;
4193 break;
4194 case SB_THUMBTRACK:
4195 es->flags |= EF_VSCROLL_TRACK;
4196 dy = pos - es->y_offset;
4197 break;
4198 case SB_THUMBPOSITION:
4199 es->flags &= ~EF_VSCROLL_TRACK;
4200 if (!(dy = pos - es->y_offset)) {
4201 SetScrollPos(hwnd, SB_VERT, pos, TRUE);
4202 EDIT_NOTIFY_PARENT(hwnd, EN_VSCROLL);
4203 }
4204 break;
4205 case SB_ENDSCROLL:
4206 break;
4207
4208 default:
4209 //ERR_(edit)("undocumented WM_VSCROLL action %d, please report\n",
4210 // action);
4211 return 0;
4212 }
4213 if (dy)
4214 EDIT_EM_LineScroll(hwnd, es, 0, dy);
4215 return 0;
4216}
4217
4218static LRESULT EDIT_WM_MouseWheel(HWND hwnd,EDITSTATE *es,WPARAM wParam,LPARAM lParam)
4219{
4220 short gcWheelDelta = 0;
4221 UINT pulScrollLines = 3;
4222 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
4223
4224 if (wParam & (MK_SHIFT | MK_CONTROL))
4225 return DefWindowProcA(hwnd,WM_MOUSEWHEEL, wParam, lParam);
4226 gcWheelDelta -= (short) HIWORD(wParam);
4227 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
4228 {
4229 int cLineScroll= (int) min((UINT) es->line_count, pulScrollLines);
4230
4231 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
4232 return EDIT_EM_LineScroll(hwnd, es, 0, cLineScroll);
4233 }
4234
4235 return 0;
4236}
4237
4238BOOL EDIT_Register()
4239{
4240 WNDCLASSA wndClass;
4241
4242//SvL: Don't check this now
4243// if (GlobalFindAtomA(EDITCLASSNAME)) return FALSE;
4244
4245 ZeroMemory(&wndClass,sizeof(WNDCLASSA));
4246 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
4247 wndClass.lpfnWndProc = (WNDPROC)EditWndProc;
4248 wndClass.cbClsExtra = 0;
4249 wndClass.cbWndExtra = sizeof(VOID*);
4250 wndClass.hCursor = LoadCursorA(0,IDC_IBEAMA);
4251 wndClass.hbrBackground = (HBRUSH)0;
4252 wndClass.lpszClassName = EDITCLASSNAME;
4253
4254 return RegisterClassA(&wndClass);
4255}
4256
4257BOOL EDIT_Unregister()
4258{
4259 if (GlobalFindAtomA(EDITCLASSNAME))
4260 return UnregisterClassA(EDITCLASSNAME,(HINSTANCE)NULL);
4261 else return FALSE;
4262}
Note: See TracBrowser for help on using the repository browser.