wine/controls/edit.c
Alexandre Julliard a69b88b2f2 Release 980315
Sun Mar 15 03:46:50 1998  Dimitrie O. Paun  <dimi@mail.cs.toronto.edu>

	* [*/*]
	Fixed some dprintf_ such that there is one and only one
	new line for each dprintf and that new line occurs at the end.
	Transformed some fprintfs into proper debug statements.
	Removed much redundancy from most of the debug statements. The
	redundancy appeared because now the component and function
	name is output automatically. Most debug statements also used to
	output the name of the function.
	All these changes prepared the source to switch completely to
	the new debugging interface.
	For more info, refer to ./documentation/debug-msg

Sat Mar 14 19:45:23 1997  Andreas Mohr <100.30936@germany.net>

	* [misc/shell.c] [if1632/kernel.spec]
	Changed parameters of FUNC004() to fix a crash.
	Not sure if this fix is correct (doc wanted).

	* [windows/user.c] [if1632/user.spec] [include/user.h]
	Implemented UserSeeUserDo.

	* [msdos/int21.c] [include/msdos.h]
	Added "GET LIST OF LISTS" (INT 21/52h).

Sat Mar 14 15:48:02 1998  Douglas Ridgway <ridgway@gmcl.com>

	* [include/windows.h] [relay32/gdi32.spec] [objects/enhmetafile.c]
	Beginnings of enhanced metafile support.

Fri Mar 13 20:53:09 1998  John Richardson <jrichard@zko.dec.com>

	* [win32/console.c]
	Restart interrupted console writes.

Fri Mar 13 18:59:24 1998  Matthew Becker <mbecker@glasscity.net>

	* [*/*.c]
	Updated documentation for API manpages.

	* [windows/dce.c]
	ReleaseDC16: Fixed cast.

	* [include/windows.h] [memory/virtual.c]
	VirtualQuery{Ex} should return DWORD instead of BOOL32.

Fri Mar 13 13:03:06 1998  Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>

	* [README][documentation/status/]
	README updated, added lzexpand,version and multimedia
	status notes to new documentation/status directory.

	* [ole/*.c][if1632/typelib.spec]
	Added typelib stubs, several small additions and fixes.

	* [loader/pe_image.c]
	Fixed a small bug (fixup_imports got passed the wrong hModule in a
	remapcase).

	* [loader/signal.c][if1632/signal.c][misc/winsock_dns.c]
	  [loader/module.c]
	Fixed some recursive debugger crashes (caused by invalid FS).

	* [misc/registry.c]
	Two bugs fixed.

Fri Mar 13 04:55:01 1998  David Lee Lambert <lamber45@egr.msu.edu>

	* [include/winnt.h] [include/winnls.h]
	Moved LANG_xxx flags to winnls.h

	* [include/winnls.h]
	Added flags for GetDateFormat(); fixed validity of
	LOCALE_SYSTEM_DEFAULT.

	* [include/windows.h] 
	Added GetTimeFormat() prototypes.

	* [ole/ole2nls.c]
	Implemented ASCII date- and time-functions,  using an
	optimized common core;  added stubs for Unicode versions;  
	started work on a Unicode core.

	* [AUTHORS]
	Added my name.

Mon Mar  9 20:10:15 1998  Eric Kohl <ekohl@abo.rhein-zeitung.de>

	* [relay32/comctl32.spec] [include/imagelist.h]
	  [include/commctrl.h] [misc/imagelist.c] [misc/Makefile.in]
	First attempt at implementing ImageLists.

Sun Mar  8 20:19:49 1998  Uwe Bonnes  <bon@elektron.ikp.physik.tu-darmstadt.de>

	* [files/dos_fs.c] [configure.in]
	Try to get FileTimeToLocalFileTime,FileTimeToSystemTime and
	SystemTimeToFileTime right.
	Use timegm() where available.

	* [misc/lstr.c]
	Fix an off by one error in FormatMessage and handle the case 
	when args = NULL (used by programs to get the length of the 
	string).

	* [win32/console.c]
	Actual display a per-process Title string, better working
	attempt for WriteConsole32W and ReadConsole32W.

Fri Mar  6 20:33:45 1998  Slaven Rezic  <eserte@cs.tu-berlin.de>

	* [include/config.h.in][configure.in][multimedia/audio.c]
	  [multimedia/dsound.c]
	Added check for FreeBSD sound system.

Sun Mar  1 17:40:10 1998  Jason Schonberg <schon@mti.sgi.com>

	* [controls/edit.c] [include/ole.h] [include/shlobj.h]
	Removed final commas in enum types.

Mon Feb 23 07:52:18 1998  Luiz Otavio L. Zorzella  <zorzella@nr.conexware.com>

	* [multimedia/time.c]
	Workaround to avoid infinite recursion inside timeGetTime.

	* [multimedia/audio.c]
	WODM_GETNUMDEVS and WIDM_GETNUMDEVS only return 1 now if the
	SOUND_DEV can be opened, or if it's busy.
1998-03-15 20:29:56 +00:00

3905 lines
105 KiB
C

/*
* Edit control
*
* Copyright David W. Metcalfe, 1994
* Copyright William Magro, 1995, 1996
* Copyright Frans van Dorsselaer, 1996, 1997
*
*/
/*
* please read EDIT.TODO (and update it when you change things)
*/
#include <stdio.h>
#include "windows.h"
#include "winnt.h"
#include "win.h"
#include "combo.h"
#include "local.h"
#include "resource.h"
#include "debug.h"
#include "callback.h"
#define BUFLIMIT_MULTI 65534 /* maximum buffer size (not including '\0')
FIXME: BTW, new specs say 65535 (do you dare ???) */
#define BUFLIMIT_SINGLE 32766 /* maximum buffer size (not including '\0') */
#define BUFSTART_MULTI 1024 /* starting size */
#define BUFSTART_SINGLE 256 /* starting size */
#define GROWLENGTH 64 /* buffers grow by this much */
#define HSCROLL_FRACTION 3 /* scroll window by 1/3 width */
/*
* extra flags for EDITSTATE.flags field
*/
#define EF_MODIFIED 0x0001 /* text has been modified */
#define EF_FOCUSED 0x0002 /* we have input focus */
#define EF_UPDATE 0x0004 /* notify parent of changed state on next WM_PAINT */
#define EF_VSCROLL_TRACK 0x0008 /* don't SetScrollPos() since we are tracking the thumb */
#define EF_HSCROLL_TRACK 0x0010 /* don't SetScrollPos() since we are tracking the thumb */
#define EF_VSCROLL_HACK 0x0020 /* we already have informed the user of the hacked handler */
#define EF_HSCROLL_HACK 0x0040 /* we already have informed the user of the hacked handler */
#define EF_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
wrapped line, instead of in front of the next character */
typedef BOOL32 *LPBOOL32;
typedef enum
{
END_0 = 0, /* line ends with terminating '\0' character */
END_WRAP, /* line is wrapped */
END_HARD, /* line ends with a hard return '\r\n' */
END_SOFT /* line ends with a soft return '\r\r\n' */
} LINE_END;
typedef struct tagLINEDEF {
INT32 length; /* bruto length of a line in bytes */
INT32 net_length; /* netto length of a line in visible characters */
LINE_END ending;
INT32 width; /* width of the line in pixels */
struct tagLINEDEF *next;
} LINEDEF;
typedef struct
{
HANDLE32 heap; /* our own heap */
LPSTR text; /* the actual contents of the control */
INT32 buffer_size; /* the size of the buffer */
INT32 buffer_limit; /* the maximum size to which the buffer may grow */
HFONT32 font; /* NULL means standard system font */
INT32 x_offset; /* scroll offset for multi lines this is in pixels
for single lines it's in characters */
INT32 line_height; /* height of a screen line in pixels */
INT32 char_width; /* average character width in pixels */
DWORD style; /* sane version of wnd->dwStyle */
WORD flags; /* flags that are not in es->style or wnd->flags (EF_XXX) */
INT32 undo_insert_count; /* number of characters inserted in sequence */
INT32 undo_position; /* character index of the insertion and deletion */
LPSTR undo_text; /* deleted text */
INT32 undo_buffer_size; /* size of the deleted text buffer */
INT32 selection_start; /* == selection_end if no selection */
INT32 selection_end; /* == current caret position */
CHAR password_char; /* == 0 if no password char, and for multi line controls */
INT32 left_margin; /* in pixels */
INT32 right_margin; /* in pixels */
RECT32 format_rect;
INT32 region_posx; /* Position of cursor relative to region: */
INT32 region_posy; /* -1: to left, 0: within, 1: to right */
EDITWORDBREAKPROC16 word_break_proc16;
EDITWORDBREAKPROC32A word_break_proc32A;
INT32 line_count; /* number of lines */
INT32 y_offset; /* scroll offset in number of lines */
/*
* only for multi line controls
*/
INT32 lock_count; /* amount of re-entries in the EditWndProc */
INT32 tabs_count;
LPINT32 tabs;
INT32 text_width; /* width of the widest line in pixels */
LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
HLOCAL16 hloc16; /* for controls receiving EM_GETHANDLE16 */
HLOCAL32 hloc32; /* for controls receiving EM_GETHANDLE */
} EDITSTATE;
#define SWAP_INT32(x,y) do { INT32 temp = (INT32)(x); (x) = (INT32)(y); (y) = temp; } while(0)
#define ORDER_INT32(x,y) do { if ((INT32)(y) < (INT32)(x)) SWAP_INT32((x),(y)); } while(0)
#define SWAP_UINT32(x,y) do { UINT32 temp = (UINT32)(x); (x) = (UINT32)(y); (y) = temp; } while(0)
#define ORDER_UINT32(x,y) do { if ((UINT32)(y) < (UINT32)(x)) SWAP_UINT32((x),(y)); } while(0)
#define DPRINTF_EDIT_NOTIFY(hwnd, str) \
({TRACE(edit, "notification " str " sent to hwnd=%08x\n", \
(UINT32)(hwnd));})
#define EDIT_SEND_CTLCOLOR(wnd,hdc) \
(SendMessage32A((wnd)->parent->hwndSelf, WM_CTLCOLOREDIT, \
(WPARAM32)(hdc), (LPARAM)(wnd)->hwndSelf))
#define EDIT_NOTIFY_PARENT(wnd, wNotifyCode, str) \
(DPRINTF_EDIT_NOTIFY((wnd)->parent->hwndSelf, str), \
SendMessage32A((wnd)->parent->hwndSelf, WM_COMMAND, \
MAKEWPARAM((wnd)->wIDmenu, wNotifyCode), \
(LPARAM)(wnd)->hwndSelf))
#define DPRINTF_EDIT_MSG16(str) \
TRACE(edit, \
"16 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
(UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
#define DPRINTF_EDIT_MSG32(str) \
TRACE(edit, \
"32 bit : " str ": hwnd=%08x, wParam=%08x, lParam=%08x\n", \
(UINT32)hwnd, (UINT32)wParam, (UINT32)lParam)
/*********************************************************************
*
* Declarations
*
*/
/*
* These functions have trivial implementations
* We still like to call them internally
* "static __inline__" makes them more like macro's
*/
static __inline__ BOOL32 EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es);
static __inline__ void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es);
static __inline__ void EDIT_WM_Clear(WND *wnd, EDITSTATE *es);
static __inline__ void EDIT_WM_Cut(WND *wnd, EDITSTATE *es);
/*
* This is the only exported function
*/
LRESULT WINAPI EditWndProc( HWND32 hwnd, UINT32 msg,
WPARAM32 wParam, LPARAM lParam );
/*
* Helper functions only valid for one type of control
*/
static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es);
static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es);
static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend);
/*
* Helper functions valid for both single line _and_ multi line controls
*/
static INT32 EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT32 start, INT32 index, INT32 count, INT32 action);
static INT32 EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y, LPBOOL32 after_wrap);
static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT32 x, LPINT32 y);
static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT32 line, INT32 scol, INT32 ecol, LPRECT32 rc);
static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end);
static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es);
static BOOL32 EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT32 size);
static BOOL32 EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT32 size);
static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL32 extend);
static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC32 hdc, INT32 line, BOOL32 rev);
static INT32 EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC32 hdc, INT32 x, INT32 y, INT32 line, INT32 col, INT32 count, BOOL32 rev);
static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT32 lprc);
static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL32 force);
static INT32 EDIT_WordBreakProc(LPSTR s, INT32 index, INT32 count, INT32 action);
/*
* EM_XXX message handlers
*/
static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y);
static BOOL32 EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL32 add_eol);
static HLOCAL32 EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es);
static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es);
static INT32 EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT32 line, LPSTR lpch);
static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT32 start, LPUINT32 end);
static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es);
static INT32 EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT32 index);
static INT32 EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT32 line);
static INT32 EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT32 index);
static BOOL32 EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT32 dx, INT32 dy);
static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT32 index, BOOL32 after_wrap);
static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL32 can_undo, LPCSTR lpsz_replace);
static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT32 action);
static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es);
static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL32 hloc);
static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc);
static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT32 limit);
static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT32 action, INT32 left, INT32 right);
static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c);
static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT32 start, UINT32 end, BOOL32 after_wrap);
static BOOL32 EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT32 count, LPINT32 tabs);
static BOOL32 EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT32 count, LPINT16 tabs);
static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC32A wbp);
static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
static BOOL32 EDIT_EM_Undo(WND *wnd, EDITSTATE *es);
/*
* WM_XXX message handlers
*/
static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data);
static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT32 code, INT32 id, HWND32 conrtol);
static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND32 hwnd, INT32 x, INT32 y);
static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es);
static LRESULT EDIT_WM_Create(WND *wnd, LPCREATESTRUCT32A cs);
static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es);
static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC32 dc);
static INT32 EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT32 count, LPSTR text);
static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar);
static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data);
static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND32 window_getting_focus);
static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y);
static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es);
static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es);
static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND32 window_losing_focus);
static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT32 font, BOOL32 redraw);
static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text);
static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT32 action, INT32 width, INT32 height);
static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data);
static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT32 id, TIMERPROC32 timer_proc);
static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar);
/*********************************************************************
*
* EM_CANUNDO
*
*/
static __inline__ BOOL32 EDIT_EM_CanUndo(WND *wnd, EDITSTATE *es)
{
return (es->undo_insert_count || lstrlen32A(es->undo_text));
}
/*********************************************************************
*
* EM_EMPTYUNDOBUFFER
*
*/
static __inline__ void EDIT_EM_EmptyUndoBuffer(WND *wnd, EDITSTATE *es)
{
es->undo_insert_count = 0;
*es->undo_text = '\0';
}
/*********************************************************************
*
* WM_CLEAR
*
*/
static __inline__ void EDIT_WM_Clear(WND *wnd, EDITSTATE *es)
{
EDIT_EM_ReplaceSel(wnd, es, TRUE, "");
}
/*********************************************************************
*
* WM_CUT
*
*/
static __inline__ void EDIT_WM_Cut(WND *wnd, EDITSTATE *es)
{
EDIT_WM_Copy(wnd, es);
EDIT_WM_Clear(wnd, es);
}
/*********************************************************************
*
* EditWndProc()
*
* The messages are in the order of the actual integer values
* (which can be found in include/windows.h)
* Whereever possible the 16 bit versions are converted to
* the 32 bit ones, so that we can 'fall through' to the
* helper functions. These are mostly 32 bit (with a few
* exceptions, clearly indicated by a '16' extension to their
* names).
*
*/
LRESULT WINAPI EditWndProc( HWND32 hwnd, UINT32 msg,
WPARAM32 wParam, LPARAM lParam )
{
WND *wnd = WIN_FindWndPtr(hwnd);
EDITSTATE *es = *(EDITSTATE **)((wnd)->wExtra);
LRESULT result = 0;
switch (msg) {
case WM_CREATE:
DPRINTF_EDIT_MSG32("WM_CREATE");
return EDIT_WM_Create(wnd, (LPCREATESTRUCT32A)lParam);
case WM_DESTROY:
DPRINTF_EDIT_MSG32("WM_DESTROY");
EDIT_WM_Destroy(wnd, es);
return 0;
}
if (!es)
return DefWindowProc32A(hwnd, msg, wParam, lParam);
EDIT_LockBuffer(wnd, es);
switch (msg) {
case EM_GETSEL16:
DPRINTF_EDIT_MSG16("EM_GETSEL");
wParam = 0;
lParam = 0;
/* fall through */
case EM_GETSEL32:
DPRINTF_EDIT_MSG32("EM_GETSEL");
result = EDIT_EM_GetSel(wnd, es, (LPUINT32)wParam, (LPUINT32)lParam);
break;
case EM_SETSEL16:
DPRINTF_EDIT_MSG16("EM_SETSEL");
if (SLOWORD(lParam) == -1)
EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
else
EDIT_EM_SetSel(wnd, es, LOWORD(lParam), HIWORD(lParam), FALSE);
if (!wParam)
EDIT_EM_ScrollCaret(wnd, es);
result = 1;
break;
case EM_SETSEL32:
DPRINTF_EDIT_MSG32("EM_SETSEL");
EDIT_EM_SetSel(wnd, es, wParam, lParam, FALSE);
result = 1;
break;
case EM_GETRECT16:
DPRINTF_EDIT_MSG16("EM_GETRECT");
if (lParam)
CONV_RECT32TO16(&es->format_rect, (LPRECT16)PTR_SEG_TO_LIN(lParam));
break;
case EM_GETRECT32:
DPRINTF_EDIT_MSG32("EM_GETRECT");
if (lParam)
CopyRect32((LPRECT32)lParam, &es->format_rect);
break;
case EM_SETRECT16:
DPRINTF_EDIT_MSG16("EM_SETRECT");
if ((es->style & ES_MULTILINE) && lParam) {
RECT32 rc;
CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
EDIT_SetRectNP(wnd, es, &rc);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
break;
case EM_SETRECT32:
DPRINTF_EDIT_MSG32("EM_SETRECT");
if ((es->style & ES_MULTILINE) && lParam) {
EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
break;
case EM_SETRECTNP16:
DPRINTF_EDIT_MSG16("EM_SETRECTNP");
if ((es->style & ES_MULTILINE) && lParam) {
RECT32 rc;
CONV_RECT16TO32((LPRECT16)PTR_SEG_TO_LIN(lParam), &rc);
EDIT_SetRectNP(wnd, es, &rc);
}
break;
case EM_SETRECTNP32:
DPRINTF_EDIT_MSG32("EM_SETRECTNP");
if ((es->style & ES_MULTILINE) && lParam)
EDIT_SetRectNP(wnd, es, (LPRECT32)lParam);
break;
case EM_SCROLL16:
DPRINTF_EDIT_MSG16("EM_SCROLL");
/* fall through */
case EM_SCROLL32:
DPRINTF_EDIT_MSG32("EM_SCROLL");
result = EDIT_EM_Scroll(wnd, es, (INT32)wParam);
break;
case EM_LINESCROLL16:
DPRINTF_EDIT_MSG16("EM_LINESCROLL");
wParam = (WPARAM32)(INT32)SHIWORD(lParam);
lParam = (LPARAM)(INT32)SLOWORD(lParam);
/* fall through */
case EM_LINESCROLL32:
DPRINTF_EDIT_MSG32("EM_LINESCROLL");
result = (LRESULT)EDIT_EM_LineScroll(wnd, es, (INT32)wParam, (INT32)lParam);
break;
case EM_SCROLLCARET16:
DPRINTF_EDIT_MSG16("EM_SCROLLCARET");
/* fall through */
case EM_SCROLLCARET32:
DPRINTF_EDIT_MSG32("EM_SCROLLCARET");
EDIT_EM_ScrollCaret(wnd, es);
result = 1;
break;
case EM_GETMODIFY16:
DPRINTF_EDIT_MSG16("EM_GETMODIFY");
/* fall through */
case EM_GETMODIFY32:
DPRINTF_EDIT_MSG32("EM_GETMODIFY");
return ((es->flags & EF_MODIFIED) != 0);
break;
case EM_SETMODIFY16:
DPRINTF_EDIT_MSG16("EM_SETMODIFY");
/* fall through */
case EM_SETMODIFY32:
DPRINTF_EDIT_MSG32("EM_SETMODIFY");
if (wParam)
es->flags |= EF_MODIFIED;
else
es->flags &= ~EF_MODIFIED;
break;
case EM_GETLINECOUNT16:
DPRINTF_EDIT_MSG16("EM_GETLINECOUNT");
/* fall through */
case EM_GETLINECOUNT32:
DPRINTF_EDIT_MSG32("EM_GETLINECOUNT");
result = (es->style & ES_MULTILINE) ? es->line_count : 1;
break;
case EM_LINEINDEX16:
DPRINTF_EDIT_MSG16("EM_LINEINDEX");
if ((INT16)wParam == -1)
wParam = (WPARAM32)-1;
/* fall through */
case EM_LINEINDEX32:
DPRINTF_EDIT_MSG32("EM_LINEINDEX");
result = (LRESULT)EDIT_EM_LineIndex(wnd, es, (INT32)wParam);
break;
case EM_SETHANDLE16:
DPRINTF_EDIT_MSG16("EM_SETHANDLE");
EDIT_EM_SetHandle16(wnd, es, (HLOCAL16)wParam);
break;
case EM_SETHANDLE32:
DPRINTF_EDIT_MSG32("EM_SETHANDLE");
EDIT_EM_SetHandle(wnd, es, (HLOCAL32)wParam);
break;
case EM_GETHANDLE16:
DPRINTF_EDIT_MSG16("EM_GETHANDLE");
result = (LRESULT)EDIT_EM_GetHandle16(wnd, es);
break;
case EM_GETHANDLE32:
DPRINTF_EDIT_MSG32("EM_GETHANDLE");
result = (LRESULT)EDIT_EM_GetHandle(wnd, es);
break;
case EM_GETTHUMB16:
DPRINTF_EDIT_MSG16("EM_GETTHUMB");
/* fall through */
case EM_GETTHUMB32:
DPRINTF_EDIT_MSG32("EM_GETTHUMB");
result = EDIT_EM_GetThumb(wnd, es);
break;
/* messages 0x00bf and 0x00c0 missing from specs */
case WM_USER+15:
DPRINTF_EDIT_MSG16("undocumented WM_USER+15, please report");
/* fall through */
case 0x00bf:
DPRINTF_EDIT_MSG32("undocumented 0x00bf, please report");
result = DefWindowProc32A(hwnd, msg, wParam, lParam);
break;
case WM_USER+16:
DPRINTF_EDIT_MSG16("undocumented WM_USER+16, please report");
/* fall through */
case 0x00c0:
DPRINTF_EDIT_MSG32("undocumented 0x00c0, please report");
result = DefWindowProc32A(hwnd, msg, wParam, lParam);
break;
case EM_LINELENGTH16:
DPRINTF_EDIT_MSG16("EM_LINELENGTH");
/* fall through */
case EM_LINELENGTH32:
DPRINTF_EDIT_MSG32("EM_LINELENGTH");
result = (LRESULT)EDIT_EM_LineLength(wnd, es, (INT32)wParam);
break;
case EM_REPLACESEL16:
DPRINTF_EDIT_MSG16("EM_REPLACESEL");
lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
/* fall through */
case EM_REPLACESEL32:
DPRINTF_EDIT_MSG32("EM_REPLACESEL");
EDIT_EM_ReplaceSel(wnd, es, (BOOL32)wParam, (LPCSTR)lParam);
break;
/* message 0x00c3 missing from specs */
case WM_USER+19:
DPRINTF_EDIT_MSG16("undocumented WM_USER+19, please report");
/* fall through */
case 0x00c3:
DPRINTF_EDIT_MSG32("undocumented 0x00c3, please report");
result = DefWindowProc32A(hwnd, msg, wParam, lParam);
break;
case EM_GETLINE16:
DPRINTF_EDIT_MSG16("EM_GETLINE");
lParam = (LPARAM)PTR_SEG_TO_LIN((SEGPTR)lParam);
/* fall through */
case EM_GETLINE32:
DPRINTF_EDIT_MSG32("EM_GETLINE");
result = (LRESULT)EDIT_EM_GetLine(wnd, es, (INT32)wParam, (LPSTR)lParam);
break;
case EM_LIMITTEXT16:
DPRINTF_EDIT_MSG16("EM_LIMITTEXT");
/* fall through */
case EM_SETLIMITTEXT32:
DPRINTF_EDIT_MSG32("EM_SETLIMITTEXT");
EDIT_EM_SetLimitText(wnd, es, (INT32)wParam);
break;
case EM_CANUNDO16:
DPRINTF_EDIT_MSG16("EM_CANUNDO");
/* fall through */
case EM_CANUNDO32:
DPRINTF_EDIT_MSG32("EM_CANUNDO");
result = (LRESULT)EDIT_EM_CanUndo(wnd, es);
break;
case EM_UNDO16:
DPRINTF_EDIT_MSG16("EM_UNDO");
/* fall through */
case EM_UNDO32:
/* fall through */
case WM_UNDO:
DPRINTF_EDIT_MSG32("EM_UNDO / WM_UNDO");
result = (LRESULT)EDIT_EM_Undo(wnd, es);
break;
case EM_FMTLINES16:
DPRINTF_EDIT_MSG16("EM_FMTLINES");
/* fall through */
case EM_FMTLINES32:
DPRINTF_EDIT_MSG32("EM_FMTLINES");
result = (LRESULT)EDIT_EM_FmtLines(wnd, es, (BOOL32)wParam);
break;
case EM_LINEFROMCHAR16:
DPRINTF_EDIT_MSG16("EM_LINEFROMCHAR");
/* fall through */
case EM_LINEFROMCHAR32:
DPRINTF_EDIT_MSG32("EM_LINEFROMCHAR");
result = (LRESULT)EDIT_EM_LineFromChar(wnd, es, (INT32)wParam);
break;
/* message 0x00ca missing from specs */
case WM_USER+26:
DPRINTF_EDIT_MSG16("undocumented WM_USER+26, please report");
/* fall through */
case 0x00ca:
DPRINTF_EDIT_MSG32("undocumented 0x00ca, please report");
result = DefWindowProc32A(hwnd, msg, wParam, lParam);
break;
case EM_SETTABSTOPS16:
DPRINTF_EDIT_MSG16("EM_SETTABSTOPS");
result = (LRESULT)EDIT_EM_SetTabStops16(wnd, es, (INT32)wParam, (LPINT16)PTR_SEG_TO_LIN((SEGPTR)lParam));
break;
case EM_SETTABSTOPS32:
DPRINTF_EDIT_MSG32("EM_SETTABSTOPS");
result = (LRESULT)EDIT_EM_SetTabStops(wnd, es, (INT32)wParam, (LPINT32)lParam);
break;
case EM_SETPASSWORDCHAR16:
DPRINTF_EDIT_MSG16("EM_SETPASSWORDCHAR");
/* fall through */
case EM_SETPASSWORDCHAR32:
DPRINTF_EDIT_MSG32("EM_SETPASSWORDCHAR");
EDIT_EM_SetPasswordChar(wnd, es, (CHAR)wParam);
break;
case EM_EMPTYUNDOBUFFER16:
DPRINTF_EDIT_MSG16("EM_EMPTYUNDOBUFFER");
/* fall through */
case EM_EMPTYUNDOBUFFER32:
DPRINTF_EDIT_MSG32("EM_EMPTYUNDOBUFFER");
EDIT_EM_EmptyUndoBuffer(wnd, es);
break;
case EM_GETFIRSTVISIBLELINE16:
DPRINTF_EDIT_MSG16("EM_GETFIRSTVISIBLELINE");
result = es->y_offset;
break;
case EM_GETFIRSTVISIBLELINE32:
DPRINTF_EDIT_MSG32("EM_GETFIRSTVISIBLELINE");
result = (es->style & ES_MULTILINE) ? es->y_offset : es->x_offset;
break;
case EM_SETREADONLY16:
DPRINTF_EDIT_MSG16("EM_SETREADONLY");
/* fall through */
case EM_SETREADONLY32:
DPRINTF_EDIT_MSG32("EM_SETREADONLY");
if (wParam) {
wnd->dwStyle |= ES_READONLY;
es->style |= ES_READONLY;
} else {
wnd->dwStyle &= ~ES_READONLY;
es->style &= ~ES_READONLY;
}
return 1;
break;
case EM_SETWORDBREAKPROC16:
DPRINTF_EDIT_MSG16("EM_SETWORDBREAKPROC");
EDIT_EM_SetWordBreakProc16(wnd, es, (EDITWORDBREAKPROC16)lParam);
break;
case EM_SETWORDBREAKPROC32:
DPRINTF_EDIT_MSG32("EM_SETWORDBREAKPROC");
EDIT_EM_SetWordBreakProc(wnd, es, (EDITWORDBREAKPROC32A)lParam);
break;
case EM_GETWORDBREAKPROC16:
DPRINTF_EDIT_MSG16("EM_GETWORDBREAKPROC");
result = (LRESULT)es->word_break_proc16;
break;
case EM_GETWORDBREAKPROC32:
DPRINTF_EDIT_MSG32("EM_GETWORDBREAKPROC");
result = (LRESULT)es->word_break_proc32A;
break;
case EM_GETPASSWORDCHAR16:
DPRINTF_EDIT_MSG16("EM_GETPASSWORDCHAR");
/* fall through */
case EM_GETPASSWORDCHAR32:
DPRINTF_EDIT_MSG32("EM_GETPASSWORDCHAR");
result = es->password_char;
break;
/* The following EM_xxx are new to win95 and don't exist for 16 bit */
case EM_SETMARGINS32:
DPRINTF_EDIT_MSG32("EM_SETMARGINS");
EDIT_EM_SetMargins(wnd, es, (INT32)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case EM_GETMARGINS32:
DPRINTF_EDIT_MSG32("EM_GETMARGINS");
result = MAKELONG(es->left_margin, es->right_margin);
break;
case EM_GETLIMITTEXT32:
DPRINTF_EDIT_MSG32("EM_GETLIMITTEXT");
result = es->buffer_limit;
break;
case EM_POSFROMCHAR32:
DPRINTF_EDIT_MSG32("EM_POSFROMCHAR");
result = EDIT_EM_PosFromChar(wnd, es, (INT32)wParam, FALSE);
break;
case EM_CHARFROMPOS32:
DPRINTF_EDIT_MSG32("EM_CHARFROMPOS");
result = EDIT_EM_CharFromPos(wnd, es, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_GETDLGCODE:
DPRINTF_EDIT_MSG32("WM_GETDLGCODE");
result = (es->style & ES_MULTILINE) ?
DLGC_WANTALLKEYS | DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS :
DLGC_HASSETSEL | DLGC_WANTCHARS | DLGC_WANTARROWS;
break;
case WM_CHAR:
DPRINTF_EDIT_MSG32("WM_CHAR");
EDIT_WM_Char(wnd, es, (CHAR)wParam, (DWORD)lParam);
break;
case WM_CLEAR:
DPRINTF_EDIT_MSG32("WM_CLEAR");
EDIT_WM_Clear(wnd, es);
break;
case WM_COMMAND:
DPRINTF_EDIT_MSG32("WM_COMMAND");
EDIT_WM_Command(wnd, es, HIWORD(wParam), LOWORD(wParam), (HWND32)lParam);
break;
case WM_CONTEXTMENU:
DPRINTF_EDIT_MSG32("WM_CONTEXTMENU");
EDIT_WM_ContextMenu(wnd, es, (HWND32)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_COPY:
DPRINTF_EDIT_MSG32("WM_COPY");
EDIT_WM_Copy(wnd, es);
break;
case WM_CUT:
DPRINTF_EDIT_MSG32("WM_CUT");
EDIT_WM_Cut(wnd, es);
break;
case WM_ENABLE:
DPRINTF_EDIT_MSG32("WM_ENABLE");
InvalidateRect32(hwnd, NULL, TRUE);
break;
case WM_ERASEBKGND:
DPRINTF_EDIT_MSG32("WM_ERASEBKGND");
result = EDIT_WM_EraseBkGnd(wnd, es, (HDC32)wParam);
break;
case WM_GETFONT:
DPRINTF_EDIT_MSG32("WM_GETFONT");
result = (LRESULT)es->font;
break;
case WM_GETTEXT:
DPRINTF_EDIT_MSG32("WM_GETTEXT");
result = (LRESULT)EDIT_WM_GetText(wnd, es, (INT32)wParam, (LPSTR)lParam);
break;
case WM_GETTEXTLENGTH:
DPRINTF_EDIT_MSG32("WM_GETTEXTLENGTH");
result = lstrlen32A(es->text);
break;
case WM_HSCROLL:
DPRINTF_EDIT_MSG32("WM_HSCROLL");
result = EDIT_WM_HScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)lParam);
break;
case WM_KEYDOWN:
DPRINTF_EDIT_MSG32("WM_KEYDOWN");
result = EDIT_WM_KeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
break;
case WM_KILLFOCUS:
DPRINTF_EDIT_MSG32("WM_KILLFOCUS");
result = EDIT_WM_KillFocus(wnd, es, (HWND32)wParam);
break;
case WM_LBUTTONDBLCLK:
DPRINTF_EDIT_MSG32("WM_LBUTTONDBLCLK");
result = EDIT_WM_LButtonDblClk(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_LBUTTONDOWN:
DPRINTF_EDIT_MSG32("WM_LBUTTONDOWN");
result = EDIT_WM_LButtonDown(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_LBUTTONUP:
DPRINTF_EDIT_MSG32("WM_LBUTTONUP");
result = EDIT_WM_LButtonUp(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_MOUSEACTIVATE:
/*
* FIXME: maybe DefWindowProc() screws up, but it seems that
* modalless dialog boxes need this. If we don't do this, the focus
* will _not_ be set by DefWindowProc() for edit controls in a
* modalless dialog box ???
*/
DPRINTF_EDIT_MSG32("WM_MOUSEACTIVATE");
SetFocus32(wnd->hwndSelf);
result = MA_ACTIVATE;
break;
case WM_MOUSEMOVE:
/*
* DPRINTF_EDIT_MSG32("WM_MOUSEMOVE");
*/
result = EDIT_WM_MouseMove(wnd, es, (DWORD)wParam, SLOWORD(lParam), SHIWORD(lParam));
break;
case WM_PAINT:
DPRINTF_EDIT_MSG32("WM_PAINT");
EDIT_WM_Paint(wnd, es);
break;
case WM_PASTE:
DPRINTF_EDIT_MSG32("WM_PASTE");
EDIT_WM_Paste(wnd, es);
break;
case WM_SETFOCUS:
DPRINTF_EDIT_MSG32("WM_SETFOCUS");
EDIT_WM_SetFocus(wnd, es, (HWND32)wParam);
break;
case WM_SETFONT:
DPRINTF_EDIT_MSG32("WM_SETFONT");
EDIT_WM_SetFont(wnd, es, (HFONT32)wParam, LOWORD(lParam) != 0);
break;
case WM_SETTEXT:
DPRINTF_EDIT_MSG32("WM_SETTEXT");
EDIT_WM_SetText(wnd, es, (LPCSTR)lParam);
result = TRUE;
break;
case WM_SIZE:
DPRINTF_EDIT_MSG32("WM_SIZE");
EDIT_WM_Size(wnd, es, (UINT32)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case WM_SYSKEYDOWN:
DPRINTF_EDIT_MSG32("WM_SYSKEYDOWN");
result = EDIT_WM_SysKeyDown(wnd, es, (INT32)wParam, (DWORD)lParam);
break;
case WM_TIMER:
DPRINTF_EDIT_MSG32("WM_TIMER");
EDIT_WM_Timer(wnd, es, (INT32)wParam, (TIMERPROC32)lParam);
break;
case WM_VSCROLL:
DPRINTF_EDIT_MSG32("WM_VSCROLL");
result = EDIT_WM_VScroll(wnd, es, LOWORD(wParam), SHIWORD(wParam), (HWND32)(lParam));
break;
default:
result = DefWindowProc32A(hwnd, msg, wParam, lParam);
break;
}
EDIT_UnlockBuffer(wnd, es, FALSE);
return result;
}
/*********************************************************************
*
* EDIT_BuildLineDefs_ML
*
* Build linked list of text lines.
* Lines can end with '\0' (last line), a character (if it is wrapped),
* a soft return '\r\r\n' or a hard return '\r\n'
*
*/
static void EDIT_BuildLineDefs_ML(WND *wnd, EDITSTATE *es)
{
HDC32 dc;
HFONT32 old_font = 0;
LPSTR start, cp;
INT32 fw;
LINEDEF *current_def;
LINEDEF **previous_next;
current_def = es->first_line_def;
do {
LINEDEF *next_def = current_def->next;
HeapFree(es->heap, 0, current_def);
current_def = next_def;
} while (current_def);
es->line_count = 0;
es->text_width = 0;
dc = GetDC32(wnd->hwndSelf);
if (es->font)
old_font = SelectObject32(dc, es->font);
fw = es->format_rect.right - es->format_rect.left;
start = es->text;
previous_next = &es->first_line_def;
do {
current_def = HeapAlloc(es->heap, 0, sizeof(LINEDEF));
current_def->next = NULL;
cp = start;
while (*cp) {
if ((*cp == '\r') && (*(cp + 1) == '\n'))
break;
cp++;
}
if (!(*cp)) {
current_def->ending = END_0;
current_def->net_length = lstrlen32A(start);
} else if ((cp > start) && (*(cp - 1) == '\r')) {
current_def->ending = END_SOFT;
current_def->net_length = cp - start - 1;
} else {
current_def->ending = END_HARD;
current_def->net_length = cp - start;
}
current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
start, current_def->net_length,
es->tabs_count, es->tabs));
/* FIXME: check here for lines that are too wide even in AUTOHSCROLL (> 32767 ???) */
if ((!(es->style & ES_AUTOHSCROLL)) && (current_def->width > fw)) {
INT32 next = 0;
INT32 prev;
do {
prev = next;
next = EDIT_CallWordBreakProc(wnd, es, start - es->text,
prev + 1, current_def->net_length, WB_RIGHT);
current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
start, next, es->tabs_count, es->tabs));
} while (current_def->width <= fw);
if (!prev) {
next = 0;
do {
prev = next;
next++;
current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc,
start, next, es->tabs_count, es->tabs));
} while (current_def->width <= fw);
if (!prev)
prev = 1;
}
current_def->net_length = prev;
current_def->ending = END_WRAP;
current_def->width = (INT32)LOWORD(GetTabbedTextExtent32A(dc, start,
current_def->net_length, es->tabs_count, es->tabs));
}
switch (current_def->ending) {
case END_SOFT:
current_def->length = current_def->net_length + 3;
break;
case END_HARD:
current_def->length = current_def->net_length + 2;
break;
case END_WRAP:
case END_0:
current_def->length = current_def->net_length;
break;
}
es->text_width = MAX(es->text_width, current_def->width);
start += current_def->length;
*previous_next = current_def;
previous_next = &current_def->next;
es->line_count++;
} while (current_def->ending != END_0);
if (es->font)
SelectObject32(dc, old_font);
ReleaseDC32(wnd->hwndSelf, dc);
}
/*********************************************************************
*
* EDIT_CallWordBreakProc
*
* Call appropriate WordBreakProc (internal or external).
*
* Note: The "start" argument should always be an index refering
* to es->text. The actual wordbreak proc might be
* 16 bit, so we can't always pass any 32 bit LPSTR.
* Hence we assume that es->text is the buffer that holds
* the string under examination (we can decide this for ourselves).
*
*/
static INT32 EDIT_CallWordBreakProc(WND *wnd, EDITSTATE *es, INT32 start, INT32 index, INT32 count, INT32 action)
{
if (es->word_break_proc16) {
HLOCAL16 hloc16 = EDIT_EM_GetHandle16(wnd, es);
SEGPTR segptr = LocalLock16(hloc16);
INT32 ret = (INT32)Callbacks->CallWordBreakProc(es->word_break_proc16,
segptr + start, index, count, action);
LocalUnlock16(hloc16);
return ret;
}
else if (es->word_break_proc32A)
{
TRACE(relay, "(wordbrk=%p,str='%s',idx=%d,cnt=%d,act=%d)\n",
es->word_break_proc32A, es->text + start, index,
count, action );
return (INT32)es->word_break_proc32A( es->text + start, index,
count, action );
}
else
return EDIT_WordBreakProc(es->text + start, index, count, action);
}
/*********************************************************************
*
* EDIT_CharFromPos
*
* Beware: This is not the function called on EM_CHARFROMPOS
* The position _can_ be outside the formatting / client
* rectangle
* The return value is only the character index
*
*/
static INT32 EDIT_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y, LPBOOL32 after_wrap)
{
INT32 index;
HDC32 dc;
HFONT32 old_font = 0;
if (es->style & ES_MULTILINE) {
INT32 line = (y - es->format_rect.top) / es->line_height + es->y_offset;
INT32 line_index = 0;
LINEDEF *line_def = es->first_line_def;
INT32 low, high;
while ((line > 0) && line_def->next) {
line_index += line_def->length;
line_def = line_def->next;
line--;
}
x += es->x_offset - es->format_rect.left;
if (x >= line_def->width) {
if (after_wrap)
*after_wrap = (line_def->ending == END_WRAP);
return line_index + line_def->net_length;
}
if (x <= 0) {
if (after_wrap)
*after_wrap = FALSE;
return line_index;
}
dc = GetDC32(wnd->hwndSelf);
if (es->font)
old_font = SelectObject32(dc, es->font);
low = line_index + 1;
high = line_index + line_def->net_length + 1;
while (low < high - 1)
{
INT32 mid = (low + high) / 2;
if (LOWORD(GetTabbedTextExtent32A(dc, es->text + line_index,mid - line_index, es->tabs_count, es->tabs)) > x) high = mid;
else low = mid;
}
index = low;
if (after_wrap)
*after_wrap = ((index == line_index + line_def->net_length) &&
(line_def->ending == END_WRAP));
} else {
LPSTR text;
SIZE32 size;
if (after_wrap)
*after_wrap = FALSE;
x -= es->format_rect.left;
if (!x)
return es->x_offset;
text = EDIT_GetPasswordPointer_SL(wnd, es);
dc = GetDC32(wnd->hwndSelf);
if (es->font)
old_font = SelectObject32(dc, es->font);
if (x < 0)
{
INT32 low = 0;
INT32 high = es->x_offset;
while (low < high - 1)
{
INT32 mid = (low + high) / 2;
GetTextExtentPoint32A( dc, text + mid,
es->x_offset - mid, &size );
if (size.cx > -x) low = mid;
else high = mid;
}
index = low;
}
else
{
INT32 low = es->x_offset;
INT32 high = lstrlen32A(es->text) + 1;
while (low < high - 1)
{
INT32 mid = (low + high) / 2;
GetTextExtentPoint32A( dc, text + es->x_offset,
mid - es->x_offset, &size );
if (size.cx > x) high = mid;
else low = mid;
}
index = low;
}
if (es->style & ES_PASSWORD)
HeapFree(es->heap, 0 ,text);
}
if (es->font)
SelectObject32(dc, old_font);
ReleaseDC32(wnd->hwndSelf, dc);
return index;
}
/*********************************************************************
*
* EDIT_ConfinePoint
*
* adjusts the point to be within the formatting rectangle
* (so CharFromPos returns the nearest _visible_ character)
*
*/
static void EDIT_ConfinePoint(WND *wnd, EDITSTATE *es, LPINT32 x, LPINT32 y)
{
*x = MIN(MAX(*x, es->format_rect.left), es->format_rect.right - 1);
*y = MIN(MAX(*y, es->format_rect.top), es->format_rect.bottom - 1);
}
/*********************************************************************
*
* EDIT_GetLineRect
*
* Calculates the bounding rectangle for a line from a starting
* column to an ending column.
*
*/
static void EDIT_GetLineRect(WND *wnd, EDITSTATE *es, INT32 line, INT32 scol, INT32 ecol, LPRECT32 rc)
{
INT32 line_index = EDIT_EM_LineIndex(wnd, es, line);
if (es->style & ES_MULTILINE)
rc->top = es->format_rect.top + (line - es->y_offset) * es->line_height;
else
rc->top = es->format_rect.top;
rc->bottom = rc->top + es->line_height;
rc->left = (scol == 0) ? es->format_rect.left : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + scol, TRUE));
rc->right = (ecol == -1) ? es->format_rect.right : SLOWORD(EDIT_EM_PosFromChar(wnd, es, line_index + ecol, TRUE));
}
/*********************************************************************
*
* EDIT_GetPasswordPointer_SL
*
* note: caller should free the (optionally) allocated buffer
*
*/
static LPSTR EDIT_GetPasswordPointer_SL(WND *wnd, EDITSTATE *es)
{
if (es->style & ES_PASSWORD) {
INT32 len = lstrlen32A(es->text);
LPSTR text = HeapAlloc(es->heap, 0, len + 1);
RtlFillMemory(text, len, es->password_char);
text[len] = '\0';
return text;
} else
return es->text;
}
/*********************************************************************
*
* EDIT_LockBuffer
*
* This acts as a LOCAL_Lock(), but it locks only once. This way
* you can call it whenever you like, without unlocking.
*
*/
static void EDIT_LockBuffer(WND *wnd, EDITSTATE *es)
{
if (!es) {
ERR(edit, "no EDITSTATE ... please report\n");
return;
}
if (!(es->style & ES_MULTILINE))
return;
if (!es->text) {
if (es->hloc32)
es->text = LocalLock32(es->hloc32);
else if (es->hloc16)
es->text = LOCAL_Lock(wnd->hInstance, es->hloc16);
else {
ERR(edit, "no buffer ... please report\n");
return;
}
}
es->lock_count++;
}
/*********************************************************************
*
* EDIT_SL_InvalidateText
*
* Called from EDIT_InvalidateText().
* Does the job for single-line controls only.
*
*/
static void EDIT_SL_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
{
RECT32 line_rect;
RECT32 rc;
EDIT_GetLineRect(wnd, es, 0, start, end, &line_rect);
if (IntersectRect32(&rc, &line_rect, &es->format_rect))
InvalidateRect32(wnd->hwndSelf, &rc, FALSE);
}
/*********************************************************************
*
* EDIT_ML_InvalidateText
*
* Called from EDIT_InvalidateText().
* Does the job for multi-line controls only.
*
*/
static void EDIT_ML_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
{
INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
INT32 sl = EDIT_EM_LineFromChar(wnd, es, start);
INT32 el = EDIT_EM_LineFromChar(wnd, es, end);
INT32 sc;
INT32 ec;
RECT32 rc1;
RECT32 rcWnd;
RECT32 rcLine;
RECT32 rcUpdate;
INT32 l;
if ((el < es->y_offset) || (sl > es->y_offset + vlc))
return;
sc = start - EDIT_EM_LineIndex(wnd, es, sl);
ec = end - EDIT_EM_LineIndex(wnd, es, el);
if (sl < es->y_offset) {
sl = es->y_offset;
sc = 0;
}
if (el > es->y_offset + vlc) {
el = es->y_offset + vlc;
ec = EDIT_EM_LineLength(wnd, es, EDIT_EM_LineIndex(wnd, es, el));
}
GetClientRect32(wnd->hwndSelf, &rc1);
IntersectRect32(&rcWnd, &rc1, &es->format_rect);
if (sl == el) {
EDIT_GetLineRect(wnd, es, sl, sc, ec, &rcLine);
if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
} else {
EDIT_GetLineRect(wnd, es, sl, sc,
EDIT_EM_LineLength(wnd, es,
EDIT_EM_LineIndex(wnd, es, sl)),
&rcLine);
if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
for (l = sl + 1 ; l < el ; l++) {
EDIT_GetLineRect(wnd, es, l, 0,
EDIT_EM_LineLength(wnd, es,
EDIT_EM_LineIndex(wnd, es, l)),
&rcLine);
if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
}
EDIT_GetLineRect(wnd, es, el, 0, ec, &rcLine);
if (IntersectRect32(&rcUpdate, &rcWnd, &rcLine))
InvalidateRect32(wnd->hwndSelf, &rcUpdate, FALSE);
}
}
/*********************************************************************
*
* EDIT_InvalidateText
*
* Invalidate the text from offset start upto, but not including,
* offset end. Useful for (re)painting the selection.
* Regions outside the linewidth are not invalidated.
* end == -1 means end == TextLength.
* start and end need not be ordered.
*
*/
static void EDIT_InvalidateText(WND *wnd, EDITSTATE *es, INT32 start, INT32 end)
{
if (end == start)
return;
if (end == -1)
end = lstrlen32A(es->text);
ORDER_INT32(start, end);
if (es->style & ES_MULTILINE)
EDIT_ML_InvalidateText(wnd, es, start, end);
else
EDIT_SL_InvalidateText(wnd, es, start, end);
}
/*********************************************************************
*
* EDIT_MakeFit
*
* Try to fit size + 1 bytes in the buffer. Constrain to limits.
*
*/
static BOOL32 EDIT_MakeFit(WND *wnd, EDITSTATE *es, INT32 size)
{
HLOCAL32 hNew32;
HLOCAL16 hNew16;
if (size <= es->buffer_size)
return TRUE;
if (size > es->buffer_limit) {
EDIT_NOTIFY_PARENT(wnd, EN_MAXTEXT, "EN_MAXTEXT");
return FALSE;
}
size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
if (size > es->buffer_limit)
size = es->buffer_limit;
TRACE(edit, "trying to ReAlloc to %d+1\n", size);
EDIT_UnlockBuffer(wnd, es, TRUE);
if (es->text) {
if ((es->text = HeapReAlloc(es->heap, 0, es->text, size + 1)))
es->buffer_size = MIN(HeapSize(es->heap, 0, es->text) - 1, es->buffer_limit);
else
es->buffer_size = 0;
} else if (es->hloc32) {
if ((hNew32 = LocalReAlloc32(es->hloc32, size + 1, 0))) {
TRACE(edit, "Old 32 bit handle %08x, new handle %08x\n", es->hloc32, hNew32);
es->hloc32 = hNew32;
es->buffer_size = MIN(LocalSize32(es->hloc32) - 1, es->buffer_limit);
}
} else if (es->hloc16) {
if ((hNew16 = LOCAL_ReAlloc(wnd->hInstance, es->hloc16, size + 1, LMEM_MOVEABLE))) {
TRACE(edit, "Old 16 bit handle %08x, new handle %08x\n", es->hloc16, hNew16);
es->hloc16 = hNew16;
es->buffer_size = MIN(LOCAL_Size(wnd->hInstance, es->hloc16) - 1, es->buffer_limit);
}
}
if (es->buffer_size < size) {
EDIT_LockBuffer(wnd, es);
WARN(edit, "FAILED ! We now have %d+1\n", es->buffer_size);
EDIT_NOTIFY_PARENT(wnd, EN_ERRSPACE, "EN_ERRSPACE");
return FALSE;
} else {
EDIT_LockBuffer(wnd, es);
TRACE(edit, "We now have %d+1\n", es->buffer_size);
return TRUE;
}
}
/*********************************************************************
*
* EDIT_MakeUndoFit
*
* Try to fit size + 1 bytes in the undo buffer.
*
*/
static BOOL32 EDIT_MakeUndoFit(WND *wnd, EDITSTATE *es, INT32 size)
{
if (size <= es->undo_buffer_size)
return TRUE;
size = ((size / GROWLENGTH) + 1) * GROWLENGTH;
TRACE(edit, "trying to ReAlloc to %d+1\n", size);
if ((es->undo_text = HeapReAlloc(es->heap, 0, es->undo_text, size + 1))) {
es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
if (es->undo_buffer_size < size) {
WARN(edit, "FAILED ! We now have %d+1\n", es->undo_buffer_size);
return FALSE;
}
return TRUE;
}
return FALSE;
}
/*********************************************************************
*
* EDIT_MoveBackward
*
*/
static void EDIT_MoveBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 e = es->selection_end;
if (e) {
e--;
if ((es->style & ES_MULTILINE) && e &&
(es->text[e - 1] == '\r') && (es->text[e] == '\n')) {
e--;
if (e && (es->text[e - 1] == '\r'))
e--;
}
}
EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveDown_ML
*
* Only for multi line controls
* Move the caret one line down, on a column with the nearest
* x coordinate on the screen (might be a different column).
*
*/
static void EDIT_MoveDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
INT32 x = SLOWORD(pos);
INT32 y = SHIWORD(pos);
e = EDIT_CharFromPos(wnd, es, x, y + es->line_height, &after_wrap);
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveEnd
*
*/
static void EDIT_MoveEnd(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
BOOL32 after_wrap = FALSE;
INT32 e;
if (es->style & ES_MULTILINE)
e = EDIT_CharFromPos(wnd, es, 0x7fffffff,
HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), &after_wrap);
else
e = lstrlen32A(es->text);
EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveForward
*
*/
static void EDIT_MoveForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 e = es->selection_end;
if (es->text[e]) {
e++;
if ((es->style & ES_MULTILINE) && (es->text[e - 1] == '\r')) {
if (es->text[e] == '\n')
e++;
else if ((es->text[e] == '\r') && (es->text[e + 1] == '\n'))
e += 2;
}
}
EDIT_EM_SetSel(wnd, es, extend ? es->selection_start : e, e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveHome
*
* Home key: move to beginning of line.
*
*/
static void EDIT_MoveHome(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 e;
if (es->style & ES_MULTILINE)
e = EDIT_CharFromPos(wnd, es, 0x80000000,
HIWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP)), NULL);
else
e = 0;
EDIT_EM_SetSel(wnd, es, e, extend ? es->selection_start : e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MovePageDown_ML
*
* Only for multi line controls
* Move the caret one page down, on a column with the nearest
* x coordinate on the screen (might be a different column).
*
*/
static void EDIT_MovePageDown_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
INT32 x = SLOWORD(pos);
INT32 y = SHIWORD(pos);
e = EDIT_CharFromPos(wnd, es, x,
y + (es->format_rect.bottom - es->format_rect.top),
&after_wrap);
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MovePageUp_ML
*
* Only for multi line controls
* Move the caret one page up, on a column with the nearest
* x coordinate on the screen (might be a different column).
*
*/
static void EDIT_MovePageUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
INT32 x = SLOWORD(pos);
INT32 y = SHIWORD(pos);
e = EDIT_CharFromPos(wnd, es, x,
y - (es->format_rect.bottom - es->format_rect.top),
&after_wrap);
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveUp_ML
*
* Only for multi line controls
* Move the caret one line up, on a column with the nearest
* x coordinate on the screen (might be a different column).
*
*/
static void EDIT_MoveUp_ML(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
BOOL32 after_wrap = (es->flags & EF_AFTER_WRAP);
LRESULT pos = EDIT_EM_PosFromChar(wnd, es, e, after_wrap);
INT32 x = SLOWORD(pos);
INT32 y = SHIWORD(pos);
e = EDIT_CharFromPos(wnd, es, x, y - es->line_height, &after_wrap);
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveWordBackward
*
*/
static void EDIT_MoveWordBackward(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
INT32 l;
INT32 ll;
INT32 li;
l = EDIT_EM_LineFromChar(wnd, es, e);
ll = EDIT_EM_LineLength(wnd, es, e);
li = EDIT_EM_LineIndex(wnd, es, l);
if (e - li == 0) {
if (l) {
li = EDIT_EM_LineIndex(wnd, es, l - 1);
e = li + EDIT_EM_LineLength(wnd, es, li);
}
} else {
e = li + (INT32)EDIT_CallWordBreakProc(wnd, es,
li, e - li, ll, WB_LEFT);
}
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_MoveWordForward
*
*/
static void EDIT_MoveWordForward(WND *wnd, EDITSTATE *es, BOOL32 extend)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
INT32 l;
INT32 ll;
INT32 li;
l = EDIT_EM_LineFromChar(wnd, es, e);
ll = EDIT_EM_LineLength(wnd, es, e);
li = EDIT_EM_LineIndex(wnd, es, l);
if (e - li == ll) {
if ((es->style & ES_MULTILINE) && (l != es->line_count - 1))
e = EDIT_EM_LineIndex(wnd, es, l + 1);
} else {
e = li + EDIT_CallWordBreakProc(wnd, es,
li, e - li + 1, ll, WB_RIGHT);
}
if (!extend)
s = e;
EDIT_EM_SetSel(wnd, es, s, e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EDIT_PaintLine
*
*/
static void EDIT_PaintLine(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 line, BOOL32 rev)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
INT32 li;
INT32 ll;
INT32 x;
INT32 y;
LRESULT pos;
if (es->style & ES_MULTILINE) {
INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
if ((line < es->y_offset) || (line > es->y_offset + vlc) || (line >= es->line_count))
return;
} else if (line)
return;
TRACE(edit, "line=%d\n", line);
pos = EDIT_EM_PosFromChar(wnd, es, EDIT_EM_LineIndex(wnd, es, line), FALSE);
x = SLOWORD(pos);
y = SHIWORD(pos);
li = EDIT_EM_LineIndex(wnd, es, line);
ll = EDIT_EM_LineLength(wnd, es, li);
s = es->selection_start;
e = es->selection_end;
ORDER_INT32(s, e);
s = MIN(li + ll, MAX(li, s));
e = MIN(li + ll, MAX(li, e));
if (rev && (s != e) &&
((es->flags & EF_FOCUSED) || (es->style & ES_NOHIDESEL))) {
x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, s - li, FALSE);
x += EDIT_PaintText(wnd, es, dc, x, y, line, s - li, e - s, TRUE);
x += EDIT_PaintText(wnd, es, dc, x, y, line, e - li, li + ll - e, FALSE);
} else
x += EDIT_PaintText(wnd, es, dc, x, y, line, 0, ll, FALSE);
}
/*********************************************************************
*
* EDIT_PaintText
*
*/
static INT32 EDIT_PaintText(WND *wnd, EDITSTATE *es, HDC32 dc, INT32 x, INT32 y, INT32 line, INT32 col, INT32 count, BOOL32 rev)
{
COLORREF BkColor;
COLORREF TextColor;
INT32 ret;
INT32 li;
SIZE32 size;
if (!count)
return 0;
BkColor = GetBkColor32(dc);
TextColor = GetTextColor32(dc);
if (rev) {
SetBkColor32(dc, GetSysColor32(COLOR_HIGHLIGHT));
SetTextColor32(dc, GetSysColor32(COLOR_HIGHLIGHTTEXT));
}
li = EDIT_EM_LineIndex(wnd, es, line);
if (es->style & ES_MULTILINE) {
ret = (INT32)LOWORD(TabbedTextOut32A(dc, x, y, es->text + li + col, count,
es->tabs_count, es->tabs, es->format_rect.left - es->x_offset));
} else {
LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
TextOut32A(dc, x, y, text + li + col, count);
GetTextExtentPoint32A(dc, text + li + col, count, &size);
ret = size.cx;
if (es->style & ES_PASSWORD)
HeapFree(es->heap, 0, text);
}
if (rev) {
SetBkColor32(dc, BkColor);
SetTextColor32(dc, TextColor);
}
return ret;
}
/*********************************************************************
*
* EM_SCROLLCARET
*
*/
static void EDIT_EM_ScrollCaret(WND *wnd, EDITSTATE *es)
{
if (es->style & ES_MULTILINE) {
INT32 l;
INT32 li;
INT32 vlc;
INT32 ww;
INT32 cw = es->char_width;
INT32 x;
INT32 dy = 0;
INT32 dx = 0;
l = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
li = EDIT_EM_LineIndex(wnd, es, l);
x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP));
vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
if (l >= es->y_offset + vlc)
dy = l - vlc + 1 - es->y_offset;
if (l < es->y_offset)
dy = l - es->y_offset;
ww = es->format_rect.right - es->format_rect.left;
if (x < es->format_rect.left)
dx = x - es->format_rect.left - ww / HSCROLL_FRACTION / cw * cw;
if (x > es->format_rect.right)
dx = x - es->format_rect.left - (HSCROLL_FRACTION - 1) * ww / HSCROLL_FRACTION / cw * cw;
if (dy || dx)
EDIT_EM_LineScroll(wnd, es, dx, dy);
} else {
INT32 x;
INT32 goal;
INT32 format_width;
if (!(es->style & ES_AUTOHSCROLL))
return;
x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
format_width = es->format_rect.right - es->format_rect.left;
if (x < es->format_rect.left) {
goal = es->format_rect.left + format_width / HSCROLL_FRACTION;
do {
es->x_offset--;
x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
} while ((x < goal) && es->x_offset);
/* FIXME: use ScrollWindow() somehow to improve performance */
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
} else if (x > es->format_rect.right) {
INT32 x_last;
INT32 len = lstrlen32A(es->text);
goal = es->format_rect.right - format_width / HSCROLL_FRACTION;
do {
es->x_offset++;
x = SLOWORD(EDIT_EM_PosFromChar(wnd, es, es->selection_end, FALSE));
x_last = SLOWORD(EDIT_EM_PosFromChar(wnd, es, len, FALSE));
} while ((x > goal) && (x_last > es->format_rect.right));
/* FIXME: use ScrollWindow() somehow to improve performance */
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
}
}
/*********************************************************************
*
* EDIT_SetRectNP
*
* note: this is not (exactly) the handler called on EM_SETRECTNP
* it is also used to set the rect of a single line control
*
*/
static void EDIT_SetRectNP(WND *wnd, EDITSTATE *es, LPRECT32 rc)
{
CopyRect32(&es->format_rect, rc);
if (es->style & WS_BORDER) {
INT32 bw = GetSystemMetrics32(SM_CXBORDER) + 1;
es->format_rect.left += bw;
es->format_rect.top += bw;
es->format_rect.right -= bw;
es->format_rect.bottom -= bw;
}
es->format_rect.left += es->left_margin;
es->format_rect.right -= es->right_margin;
es->format_rect.right = MAX(es->format_rect.right, es->format_rect.left + es->char_width);
if (es->style & ES_MULTILINE)
es->format_rect.bottom = es->format_rect.top +
MAX(1, (es->format_rect.bottom - es->format_rect.top) / es->line_height) * es->line_height;
else
es->format_rect.bottom = es->format_rect.top + es->line_height;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL))
EDIT_BuildLineDefs_ML(wnd, es);
}
/*********************************************************************
*
* EDIT_UnlockBuffer
*
*/
static void EDIT_UnlockBuffer(WND *wnd, EDITSTATE *es, BOOL32 force)
{
if (!es) {
ERR(edit, "no EDITSTATE ... please report\n");
return;
}
if (!(es->style & ES_MULTILINE))
return;
if (!es->lock_count) {
ERR(edit, "lock_count == 0 ... please report\n");
return;
}
if (!es->text) {
ERR(edit, "es->text == 0 ... please report\n");
return;
}
if (force || (es->lock_count == 1)) {
if (es->hloc32) {
LocalUnlock32(es->hloc32);
es->text = NULL;
} else if (es->hloc16) {
LOCAL_Unlock(wnd->hInstance, es->hloc16);
es->text = NULL;
}
}
es->lock_count--;
}
/*********************************************************************
*
* EDIT_WordBreakProc
*
* Find the beginning of words.
* Note: unlike the specs for a WordBreakProc, this function only
* allows to be called without linebreaks between s[0] upto
* s[count - 1]. Remember it is only called
* internally, so we can decide this for ourselves.
*
*/
static INT32 EDIT_WordBreakProc(LPSTR s, INT32 index, INT32 count, INT32 action)
{
INT32 ret = 0;
TRACE(edit, "s=%p, index=%u, count=%u, action=%d\n",
s, index, count, action);
switch (action) {
case WB_LEFT:
if (!count)
break;
if (index)
index--;
if (s[index] == ' ') {
while (index && (s[index] == ' '))
index--;
if (index) {
while (index && (s[index] != ' '))
index--;
if (s[index] == ' ')
index++;
}
} else {
while (index && (s[index] != ' '))
index--;
if (s[index] == ' ')
index++;
}
ret = index;
break;
case WB_RIGHT:
if (!count)
break;
if (index)
index--;
if (s[index] == ' ')
while ((index < count) && (s[index] == ' ')) index++;
else {
while (s[index] && (s[index] != ' ') && (index < count))
index++;
while ((s[index] == ' ') && (index < count)) index++;
}
ret = index;
break;
case WB_ISDELIMITER:
ret = (s[index] == ' ');
break;
default:
ERR(edit, "unknown action code, please report !\n");
break;
}
return ret;
}
/*********************************************************************
*
* EM_CHARFROMPOS
*
* FIXME: do the specs mean to return LineIndex or LineNumber ???
* Let's assume LineIndex is meant
* FIXME: do the specs mean to return -1 if outside client area or
* if outside formatting rectangle ???
*
*/
static LRESULT EDIT_EM_CharFromPos(WND *wnd, EDITSTATE *es, INT32 x, INT32 y)
{
POINT32 pt;
RECT32 rc;
INT32 index;
pt.x = x;
pt.y = y;
GetClientRect32(wnd->hwndSelf, &rc);
if (!PtInRect32(&rc, pt))
return -1;
index = EDIT_CharFromPos(wnd, es, x, y, NULL);
return MAKELONG(index, EDIT_EM_LineIndex(wnd, es,
EDIT_EM_LineFromChar(wnd, es, index)));
}
/*********************************************************************
*
* EM_FMTLINES
*
*/
static BOOL32 EDIT_EM_FmtLines(WND *wnd, EDITSTATE *es, BOOL32 add_eol)
{
FIXME(edit, "message not implemented\n");
return add_eol;
}
/*********************************************************************
*
* EM_GETHANDLE
*
* Hopefully this won't fire back at us.
* We always start with a fixed buffer in our own heap.
* However, with this message a 32 bit application requests
* a handle to 32 bit moveable local heap memory, where it expects
* to find the text.
* It's a pitty that from this moment on we have to use this
* local heap, because applications may rely on the handle
* in the future.
*
* In this function we'll try to switch to local heap.
*
*/
static HLOCAL32 EDIT_EM_GetHandle(WND *wnd, EDITSTATE *es)
{
HLOCAL32 newBuf;
LPSTR newText;
INT32 newSize;
if (!(es->style & ES_MULTILINE))
return 0;
if (es->hloc32)
return es->hloc32;
else if (es->hloc16)
return (HLOCAL32)es->hloc16;
if (!(newBuf = LocalAlloc32(LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
ERR(edit, "could not allocate new 32 bit buffer\n");
return 0;
}
newSize = MIN(LocalSize32(newBuf) - 1, es->buffer_limit);
if (!(newText = LocalLock32(newBuf))) {
ERR(edit, "could not lock new 32 bit buffer\n");
LocalFree32(newBuf);
return 0;
}
lstrcpy32A(newText, es->text);
EDIT_UnlockBuffer(wnd, es, TRUE);
if (es->text)
HeapFree(es->heap, 0, es->text);
es->hloc32 = newBuf;
es->hloc16 = (HLOCAL16)NULL;
es->buffer_size = newSize;
es->text = newText;
EDIT_LockBuffer(wnd, es);
TRACE(edit, "switched to 32 bit local heap\n");
return es->hloc32;
}
/*********************************************************************
*
* EM_GETHANDLE16
*
* Hopefully this won't fire back at us.
* We always start with a buffer in 32 bit linear memory.
* However, with this message a 16 bit application requests
* a handle of 16 bit local heap memory, where it expects to find
* the text.
* It's a pitty that from this moment on we have to use this
* local heap, because applications may rely on the handle
* in the future.
*
* In this function we'll try to switch to local heap.
*/
static HLOCAL16 EDIT_EM_GetHandle16(WND *wnd, EDITSTATE *es)
{
HLOCAL16 newBuf;
LPSTR newText;
INT32 newSize;
if (!(es->style & ES_MULTILINE))
return 0;
if (es->hloc16)
return es->hloc16;
if (!LOCAL_HeapSize(wnd->hInstance)) {
if (!LocalInit(wnd->hInstance, 0,
GlobalSize16(wnd->hInstance))) {
ERR(edit, "could not initialize local heap\n");
return 0;
}
TRACE(edit, "local heap initialized\n");
}
if (!(newBuf = LOCAL_Alloc(wnd->hInstance, LMEM_MOVEABLE, lstrlen32A(es->text) + 1))) {
ERR(edit, "could not allocate new 16 bit buffer\n");
return 0;
}
newSize = MIN(LOCAL_Size(wnd->hInstance, newBuf) - 1, es->buffer_limit);
if (!(newText = LOCAL_Lock(wnd->hInstance, newBuf))) {
ERR(edit, "could not lock new 16 bit buffer\n");
LOCAL_Free(wnd->hInstance, newBuf);
return 0;
}
lstrcpy32A(newText, es->text);
EDIT_UnlockBuffer(wnd, es, TRUE);
if (es->text)
HeapFree(es->heap, 0, es->text);
else if (es->hloc32) {
while (LocalFree32(es->hloc32)) ;
LocalFree32(es->hloc32);
}
es->hloc32 = (HLOCAL32)NULL;
es->hloc16 = newBuf;
es->buffer_size = newSize;
es->text = newText;
EDIT_LockBuffer(wnd, es);
TRACE(edit, "switched to 16 bit buffer\n");
return es->hloc16;
}
/*********************************************************************
*
* EM_GETLINE
*
*/
static INT32 EDIT_EM_GetLine(WND *wnd, EDITSTATE *es, INT32 line, LPSTR lpch)
{
LPSTR src;
INT32 len;
INT32 i;
if (es->style & ES_MULTILINE) {
if (line >= es->line_count)
return 0;
} else
line = 0;
src = es->text + EDIT_EM_LineIndex(wnd, es, line);
len = MIN(*(WORD *)lpch, EDIT_EM_LineLength(wnd, es, line));
for (i = 0 ; i < len ; i++) {
*lpch = *src;
src++;
lpch++;
}
return (LRESULT)len;
}
/*********************************************************************
*
* EM_GETSEL
*
*/
static LRESULT EDIT_EM_GetSel(WND *wnd, EDITSTATE *es, LPUINT32 start, LPUINT32 end)
{
UINT32 s = es->selection_start;
UINT32 e = es->selection_end;
ORDER_UINT32(s, e);
if (start)
*start = s;
if (end)
*end = e;
return MAKELONG(s, e);
}
/*********************************************************************
*
* EM_GETTHUMB
*
* FIXME: is this right ? (or should it be only VSCROLL)
* (and maybe only for edit controls that really have their
* own scrollbars) (and maybe only for multiline controls ?)
* All in all: very poorly documented
*
* FIXME: now it's also broken, because of the new WM_HSCROLL /
* WM_VSCROLL handlers
*
*/
static LRESULT EDIT_EM_GetThumb(WND *wnd, EDITSTATE *es)
{
return MAKELONG(EDIT_WM_VScroll(wnd, es, EM_GETTHUMB16, 0, 0),
EDIT_WM_HScroll(wnd, es, EM_GETTHUMB16, 0, 0));
}
/*********************************************************************
*
* EM_LINEFROMCHAR
*
*/
static INT32 EDIT_EM_LineFromChar(WND *wnd, EDITSTATE *es, INT32 index)
{
INT32 line;
LINEDEF *line_def;
if (!(es->style & ES_MULTILINE))
return 0;
if (index > lstrlen32A(es->text))
return es->line_count - 1;
if (index == -1)
index = MIN(es->selection_start, es->selection_end);
line = 0;
line_def = es->first_line_def;
index -= line_def->length;
while ((index >= 0) && line_def->next) {
line++;
line_def = line_def->next;
index -= line_def->length;
}
return line;
}
/*********************************************************************
*
* EM_LINEINDEX
*
*/
static INT32 EDIT_EM_LineIndex(WND *wnd, EDITSTATE *es, INT32 line)
{
INT32 line_index;
LINEDEF *line_def;
if (!(es->style & ES_MULTILINE))
return 0;
if (line >= es->line_count)
return -1;
line_index = 0;
line_def = es->first_line_def;
if (line == -1) {
INT32 index = es->selection_end - line_def->length;
while ((index >= 0) && line_def->next) {
line_index += line_def->length;
line_def = line_def->next;
index -= line_def->length;
}
} else {
while (line > 0) {
line_index += line_def->length;
line_def = line_def->next;
line--;
}
}
return line_index;
}
/*********************************************************************
*
* EM_LINELENGTH
*
*/
static INT32 EDIT_EM_LineLength(WND *wnd, EDITSTATE *es, INT32 index)
{
LINEDEF *line_def;
if (!(es->style & ES_MULTILINE))
return lstrlen32A(es->text);
if (index == -1) {
/* FIXME: broken
INT32 sl = EDIT_EM_LineFromChar(wnd, es, es->selection_start);
INT32 el = EDIT_EM_LineFromChar(wnd, es, es->selection_end);
return es->selection_start - es->line_defs[sl].offset +
es->line_defs[el].offset +
es->line_defs[el].length - es->selection_end;
*/
return 0;
}
line_def = es->first_line_def;
index -= line_def->length;
while ((index >= 0) && line_def->next) {
line_def = line_def->next;
index -= line_def->length;
}
return line_def->net_length;
}
/*********************************************************************
*
* EM_LINESCROLL
*
* FIXME: dx is in average character widths
* However, we assume it is in pixels when we use this
* function internally
*
*/
static BOOL32 EDIT_EM_LineScroll(WND *wnd, EDITSTATE *es, INT32 dx, INT32 dy)
{
INT32 nyoff;
if (!(es->style & ES_MULTILINE))
return FALSE;
if (-dx > es->x_offset)
dx = -es->x_offset;
if (dx > es->text_width - es->x_offset)
dx = es->text_width - es->x_offset;
nyoff = MAX(0, es->y_offset + dy);
if (nyoff >= es->line_count)
nyoff = es->line_count - 1;
dy = (es->y_offset - nyoff) * es->line_height;
if (dx || dy) {
RECT32 rc1;
RECT32 rc;
GetClientRect32(wnd->hwndSelf, &rc1);
IntersectRect32(&rc, &rc1, &es->format_rect);
ScrollWindowEx32(wnd->hwndSelf, -dx, dy,
NULL, &rc, (HRGN32)NULL, NULL, SW_INVALIDATE);
es->y_offset = nyoff;
es->x_offset += dx;
}
if (dx && !(es->flags & EF_HSCROLL_TRACK))
EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
if (dy && !(es->flags & EF_VSCROLL_TRACK))
EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
return TRUE;
}
/*********************************************************************
*
* EM_POSFROMCHAR
*
*/
static LRESULT EDIT_EM_PosFromChar(WND *wnd, EDITSTATE *es, INT32 index, BOOL32 after_wrap)
{
INT32 len = lstrlen32A(es->text);
INT32 l;
INT32 li;
INT32 x;
INT32 y = 0;
HDC32 dc;
HFONT32 old_font = 0;
SIZE32 size;
index = MIN(index, len);
dc = GetDC32(wnd->hwndSelf);
if (es->font)
old_font = SelectObject32(dc, es->font);
if (es->style & ES_MULTILINE) {
l = EDIT_EM_LineFromChar(wnd, es, index);
y = (l - es->y_offset) * es->line_height;
li = EDIT_EM_LineIndex(wnd, es, l);
if (after_wrap && (li == index) && l) {
INT32 l2 = l - 1;
LINEDEF *line_def = es->first_line_def;
while (l2) {
line_def = line_def->next;
l2--;
}
if (line_def->ending == END_WRAP) {
l--;
y -= es->line_height;
li = EDIT_EM_LineIndex(wnd, es, l);
}
}
x = LOWORD(GetTabbedTextExtent32A(dc, es->text + li, index - li,
es->tabs_count, es->tabs)) - es->x_offset;
} else {
LPSTR text = EDIT_GetPasswordPointer_SL(wnd, es);
if (index < es->x_offset) {
GetTextExtentPoint32A(dc, text + index,
es->x_offset - index, &size);
x = -size.cx;
} else {
GetTextExtentPoint32A(dc, text + es->x_offset,
index - es->x_offset, &size);
x = size.cx;
}
y = 0;
if (es->style & ES_PASSWORD)
HeapFree(es->heap, 0 ,text);
}
x += es->format_rect.left;
y += es->format_rect.top;
if (es->font)
SelectObject32(dc, old_font);
ReleaseDC32(wnd->hwndSelf, dc);
return MAKELONG((INT16)x, (INT16)y);
}
/*********************************************************************
*
* EM_REPLACESEL
*
* FIXME: handle ES_NUMBER and ES_OEMCONVERT here
*
*/
static void EDIT_EM_ReplaceSel(WND *wnd, EDITSTATE *es, BOOL32 can_undo, LPCSTR lpsz_replace)
{
INT32 strl = lstrlen32A(lpsz_replace);
INT32 tl = lstrlen32A(es->text);
INT32 utl;
UINT32 s;
UINT32 e;
INT32 i;
LPSTR p;
s = es->selection_start;
e = es->selection_end;
if ((s == e) && !strl)
return;
ORDER_UINT32(s, e);
if (!EDIT_MakeFit(wnd, es, tl - (e - s) + strl))
return;
if (e != s) {
/* there is something to be deleted */
if (can_undo) {
utl = lstrlen32A(es->undo_text);
if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
/* undo-buffer is extended to the right */
EDIT_MakeUndoFit(wnd, es, utl + e - s);
lstrcpyn32A(es->undo_text + utl, es->text + s, e - s + 1);
} else if (!es->undo_insert_count && (*es->undo_text && (e == es->undo_position))) {
/* undo-buffer is extended to the left */
EDIT_MakeUndoFit(wnd, es, utl + e - s);
for (p = es->undo_text + utl ; p >= es->undo_text ; p--)
p[e - s] = p[0];
for (i = 0 , p = es->undo_text ; i < e - s ; i++)
p[i] = (es->text + s)[i];
es->undo_position = s;
} else {
/* new undo-buffer */
EDIT_MakeUndoFit(wnd, es, e - s);
lstrcpyn32A(es->undo_text, es->text + s, e - s + 1);
es->undo_position = s;
}
/* any deletion makes the old insertion-undo invalid */
es->undo_insert_count = 0;
} else
EDIT_EM_EmptyUndoBuffer(wnd, es);
/* now delete */
lstrcpy32A(es->text + s, es->text + e);
}
if (strl) {
/* there is an insertion */
if (can_undo) {
if ((s == es->undo_position) ||
((es->undo_insert_count) &&
(s == es->undo_position + es->undo_insert_count)))
/*
* insertion is new and at delete position or
* an extension to either left or right
*/
es->undo_insert_count += strl;
else {
/* new insertion undo */
es->undo_position = s;
es->undo_insert_count = strl;
/* new insertion makes old delete-buffer invalid */
*es->undo_text = '\0';
}
} else
EDIT_EM_EmptyUndoBuffer(wnd, es);
/* now insert */
tl = lstrlen32A(es->text);
for (p = es->text + tl ; p >= es->text + s ; p--)
p[strl] = p[0];
for (i = 0 , p = es->text + s ; i < strl ; i++)
p[i] = lpsz_replace[i];
if(es->style & ES_UPPERCASE)
CharUpperBuff32A(p, strl);
else if(es->style & ES_LOWERCASE)
CharLowerBuff32A(p, strl);
s += strl;
}
/* FIXME: really inefficient */
if (es->style & ES_MULTILINE)
EDIT_BuildLineDefs_ML(wnd, es);
EDIT_EM_SetSel(wnd, es, s, s, FALSE);
es->flags |= EF_MODIFIED;
es->flags |= EF_UPDATE;
EDIT_EM_ScrollCaret(wnd, es);
/* FIXME: really inefficient */
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
/*********************************************************************
*
* EM_SCROLL
*
*/
static LRESULT EDIT_EM_Scroll(WND *wnd, EDITSTATE *es, INT32 action)
{
INT32 dy;
if (!(es->style & ES_MULTILINE))
return (LRESULT)FALSE;
dy = 0;
switch (action) {
case SB_LINEUP:
if (es->y_offset)
dy = -1;
break;
case SB_LINEDOWN:
if (es->y_offset < es->line_count - 1)
dy = 1;
break;
case SB_PAGEUP:
if (es->y_offset)
dy = -(es->format_rect.bottom - es->format_rect.top) / es->line_height;
break;
case SB_PAGEDOWN:
if (es->y_offset < es->line_count - 1)
dy = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
break;
default:
return (LRESULT)FALSE;
}
if (dy) {
EDIT_EM_LineScroll(wnd, es, 0, dy);
EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
}
return MAKELONG((INT16)dy, (BOOL16)TRUE);
}
/*********************************************************************
*
* EM_SETHANDLE
*
* FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
*
*/
static void EDIT_EM_SetHandle(WND *wnd, EDITSTATE *es, HLOCAL32 hloc)
{
if (!(es->style & ES_MULTILINE))
return;
if (!hloc) {
WARN(edit, "called with NULL handle\n");
return;
}
EDIT_UnlockBuffer(wnd, es, TRUE);
/*
* old buffer is freed by caller, unless
* it is still in our own heap. (in that case
* we free it, correcting the buggy caller.)
*/
if (es->text)
HeapFree(es->heap, 0, es->text);
es->hloc16 = (HLOCAL16)NULL;
es->hloc32 = hloc;
es->text = NULL;
es->buffer_size = LocalSize32(es->hloc32) - 1;
EDIT_LockBuffer(wnd, es);
es->x_offset = es->y_offset = 0;
es->selection_start = es->selection_end = 0;
EDIT_EM_EmptyUndoBuffer(wnd, es);
es->flags &= ~EF_MODIFIED;
es->flags &= ~EF_UPDATE;
EDIT_BuildLineDefs_ML(wnd, es);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EM_SETHANDLE16
*
* FIXME: ES_LOWERCASE, ES_UPPERCASE, ES_OEMCONVERT, ES_NUMBER ???
*
*/
static void EDIT_EM_SetHandle16(WND *wnd, EDITSTATE *es, HLOCAL16 hloc)
{
if (!(es->style & ES_MULTILINE))
return;
if (!hloc) {
WARN(edit, "called with NULL handle\n");
return;
}
EDIT_UnlockBuffer(wnd, es, TRUE);
/*
* old buffer is freed by caller, unless
* it is still in our own heap. (in that case
* we free it, correcting the buggy caller.)
*/
if (es->text)
HeapFree(es->heap, 0, es->text);
es->hloc16 = hloc;
es->hloc32 = (HLOCAL32)NULL;
es->text = NULL;
es->buffer_size = LOCAL_Size(wnd->hInstance, es->hloc16) - 1;
EDIT_LockBuffer(wnd, es);
es->x_offset = es->y_offset = 0;
es->selection_start = es->selection_end = 0;
EDIT_EM_EmptyUndoBuffer(wnd, es);
es->flags &= ~EF_MODIFIED;
es->flags &= ~EF_UPDATE;
EDIT_BuildLineDefs_ML(wnd, es);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* EM_SETLIMITTEXT
*
* FIXME: in WinNT maxsize is 0x7FFFFFFF / 0xFFFFFFFF
* However, the windows version is not complied to yet in all of edit.c
*
*/
static void EDIT_EM_SetLimitText(WND *wnd, EDITSTATE *es, INT32 limit)
{
if (es->style & ES_MULTILINE) {
if (limit)
es->buffer_limit = MIN(limit, BUFLIMIT_MULTI);
else
es->buffer_limit = BUFLIMIT_MULTI;
} else {
if (limit)
es->buffer_limit = MIN(limit, BUFLIMIT_SINGLE);
else
es->buffer_limit = BUFLIMIT_SINGLE;
}
}
/*********************************************************************
*
* EM_SETMARGINS
*
*/
static void EDIT_EM_SetMargins(WND *wnd, EDITSTATE *es, INT32 action, INT32 left, INT32 right)
{
if (action & EC_USEFONTINFO) {
if (es->style & ES_MULTILINE) {
/*
* FIXME: do some GetABCCharWidth, or so
* This is just preliminary
*/
es->left_margin = es->right_margin = es->char_width/4;
} else
es->left_margin = es->right_margin = es->char_width/4;
} else {
if (action & EC_LEFTMARGIN)
es->left_margin = left;
if (action & EC_RIGHTMARGIN)
es->right_margin = right;
}
TRACE(edit, "left=%d, right=%d\n", es->left_margin, es->right_margin);
}
/*********************************************************************
*
* EM_SETPASSWORDCHAR
*
*/
static void EDIT_EM_SetPasswordChar(WND *wnd, EDITSTATE *es, CHAR c)
{
if (es->style & ES_MULTILINE)
return;
if (es->password_char == c)
return;
es->password_char = c;
if (c) {
wnd->dwStyle |= ES_PASSWORD;
es->style |= ES_PASSWORD;
} else {
wnd->dwStyle &= ~ES_PASSWORD;
es->style &= ~ES_PASSWORD;
}
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
/*********************************************************************
*
* EDIT_EM_SetSel
*
* note: unlike the specs say: the order of start and end
* _is_ preserved in Windows. (i.e. start can be > end)
* In other words: this handler is OK
*
*/
static void EDIT_EM_SetSel(WND *wnd, EDITSTATE *es, UINT32 start, UINT32 end, BOOL32 after_wrap)
{
UINT32 old_start = es->selection_start;
UINT32 old_end = es->selection_end;
UINT32 len = lstrlen32A(es->text);
if (start == -1) {
start = es->selection_end;
end = es->selection_end;
} else {
start = MIN(start, len);
end = MIN(end, len);
}
es->selection_start = start;
es->selection_end = end;
if (after_wrap)
es->flags |= EF_AFTER_WRAP;
else
es->flags &= ~EF_AFTER_WRAP;
if (es->flags & EF_FOCUSED) {
LRESULT pos = EDIT_EM_PosFromChar(wnd, es, end, after_wrap);
SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
}
/* This is little bit more efficient than before, not sure if it can be improved. FIXME? */
ORDER_UINT32(start, end);
ORDER_UINT32(end, old_end);
ORDER_UINT32(start, old_start);
ORDER_UINT32(old_start, old_end);
if (end != old_start)
{
/*
* One can also do
* ORDER_UINT32(end, old_start);
* EDIT_InvalidateText(wnd, es, start, end);
* EDIT_InvalidateText(wnd, es, old_start, old_end);
* in place of the following if statement.
*/
if (old_start > end )
{
EDIT_InvalidateText(wnd, es, start, end);
EDIT_InvalidateText(wnd, es, old_start, old_end);
}
else
{
EDIT_InvalidateText(wnd, es, start, old_start);
EDIT_InvalidateText(wnd, es, end, old_end);
}
}
else EDIT_InvalidateText(wnd, es, start, old_end);
}
/*********************************************************************
*
* EM_SETTABSTOPS
*
*/
static BOOL32 EDIT_EM_SetTabStops(WND *wnd, EDITSTATE *es, INT32 count, LPINT32 tabs)
{
if (!(es->style & ES_MULTILINE))
return FALSE;
if (es->tabs)
HeapFree(es->heap, 0, es->tabs);
es->tabs_count = count;
if (!count)
es->tabs = NULL;
else {
es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
memcpy(es->tabs, tabs, count * sizeof(INT32));
}
return TRUE;
}
/*********************************************************************
*
* EM_SETTABSTOPS16
*
*/
static BOOL32 EDIT_EM_SetTabStops16(WND *wnd, EDITSTATE *es, INT32 count, LPINT16 tabs)
{
if (!(es->style & ES_MULTILINE))
return FALSE;
if (es->tabs)
HeapFree(es->heap, 0, es->tabs);
es->tabs_count = count;
if (!count)
es->tabs = NULL;
else {
INT32 i;
es->tabs = HeapAlloc(es->heap, 0, count * sizeof(INT32));
for (i = 0 ; i < count ; i++)
es->tabs[i] = *tabs++;
}
return TRUE;
}
/*********************************************************************
*
* EM_SETWORDBREAKPROC
*
*/
static void EDIT_EM_SetWordBreakProc(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC32A wbp)
{
if (es->word_break_proc32A == wbp)
return;
es->word_break_proc32A = wbp;
es->word_break_proc16 = NULL;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
EDIT_BuildLineDefs_ML(wnd, es);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
}
/*********************************************************************
*
* EM_SETWORDBREAKPROC16
*
*/
static void EDIT_EM_SetWordBreakProc16(WND *wnd, EDITSTATE *es, EDITWORDBREAKPROC16 wbp)
{
if (es->word_break_proc16 == wbp)
return;
es->word_break_proc32A = NULL;
es->word_break_proc16 = wbp;
if ((es->style & ES_MULTILINE) && !(es->style & ES_AUTOHSCROLL)) {
EDIT_BuildLineDefs_ML(wnd, es);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
}
/*********************************************************************
*
* EM_UNDO / WM_UNDO
*
*/
static BOOL32 EDIT_EM_Undo(WND *wnd, EDITSTATE *es)
{
INT32 ulength = lstrlen32A(es->undo_text);
LPSTR utext = HeapAlloc(es->heap, 0, ulength + 1);
lstrcpy32A(utext, es->undo_text);
TRACE(edit, "before UNDO:insertion length = %d, deletion buffer = %s\n",
es->undo_insert_count, utext);
EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
EDIT_EM_EmptyUndoBuffer(wnd, es);
EDIT_EM_ReplaceSel(wnd, es, TRUE, utext);
EDIT_EM_SetSel(wnd, es, es->undo_position, es->undo_position + es->undo_insert_count, FALSE);
HeapFree(es->heap, 0, utext);
TRACE(edit, "after UNDO:insertion length = %d, deletion buffer = %s\n",
es->undo_insert_count, es->undo_text);
return TRUE;
}
/*********************************************************************
*
* WM_CHAR
*
*/
static void EDIT_WM_Char(WND *wnd, EDITSTATE *es, CHAR c, DWORD key_data)
{
switch (c) {
case '\r':
case '\n':
if (es->style & ES_MULTILINE) {
if (es->style & ES_READONLY) {
EDIT_MoveHome(wnd, es, FALSE);
EDIT_MoveDown_ML(wnd, es, FALSE);
} else
EDIT_EM_ReplaceSel(wnd, es, TRUE, "\r\n");
}
break;
case '\t':
if ((es->style & ES_MULTILINE) && !(es->style & ES_READONLY))
EDIT_EM_ReplaceSel(wnd, es, TRUE, "\t");
break;
default:
if (!(es->style & ES_READONLY) && ((BYTE)c >= ' ') && (c != 127)) {
char str[2];
str[0] = c;
str[1] = '\0';
EDIT_EM_ReplaceSel(wnd, es, TRUE, str);
}
break;
}
}
/*********************************************************************
*
* WM_COMMAND
*
*/
static void EDIT_WM_Command(WND *wnd, EDITSTATE *es, INT32 code, INT32 id, HWND32 control)
{
if (code || control)
return;
switch (id) {
case EM_UNDO32:
EDIT_EM_Undo(wnd, es);
break;
case WM_CUT:
EDIT_WM_Cut(wnd, es);
break;
case WM_COPY:
EDIT_WM_Copy(wnd, es);
break;
case WM_PASTE:
EDIT_WM_Paste(wnd, es);
break;
case WM_CLEAR:
EDIT_WM_Clear(wnd, es);
break;
case EM_SETSEL32:
EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
break;
default:
ERR(edit, "unknown menu item, please report\n");
break;
}
}
/*********************************************************************
*
* WM_CONTEXTMENU
*
* Note: the resource files resource/sysres_??.rc cannot define a
* single popup menu. Hence we use a (dummy) menubar
* containing the single popup menu as its first item.
*
* FIXME: the message identifiers have been chosen arbitrarily,
* hence we use MF_BYPOSITION.
* We might as well use the "real" values (anybody knows ?)
* The menu definition is in resources/sysres_??.rc.
* Once these are OK, we better use MF_BYCOMMAND here
* (as we do in EDIT_WM_Command()).
*
*/
static void EDIT_WM_ContextMenu(WND *wnd, EDITSTATE *es, HWND32 hwnd, INT32 x, INT32 y)
{
HMENU32 menu = LoadMenuIndirect32A(SYSRES_GetResPtr(SYSRES_MENU_EDITMENU));
HMENU32 popup = GetSubMenu32(menu, 0);
UINT32 start = es->selection_start;
UINT32 end = es->selection_end;
ORDER_UINT32(start, end);
/* undo */
EnableMenuItem32(popup, 0, MF_BYPOSITION | (EDIT_EM_CanUndo(wnd, es) ? MF_ENABLED : MF_GRAYED));
/* cut */
EnableMenuItem32(popup, 2, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
/* copy */
EnableMenuItem32(popup, 3, MF_BYPOSITION | ((end - start) && !(es->style & ES_PASSWORD) ? MF_ENABLED : MF_GRAYED));
/* paste */
EnableMenuItem32(popup, 4, MF_BYPOSITION | (IsClipboardFormatAvailable32(CF_TEXT) ? MF_ENABLED : MF_GRAYED));
/* delete */
EnableMenuItem32(popup, 5, MF_BYPOSITION | ((end - start) ? MF_ENABLED : MF_GRAYED));
/* select all */
EnableMenuItem32(popup, 7, MF_BYPOSITION | (start || (end != lstrlen32A(es->text)) ? MF_ENABLED : MF_GRAYED));
TrackPopupMenu32(popup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, x, y, 0, wnd->hwndSelf, NULL);
DestroyMenu32(menu);
}
/*********************************************************************
*
* WM_COPY
*
* FIXME: replace with 32 bit calls as soon as they are implemented
* in the clipboard code
*
*/
static void EDIT_WM_Copy(WND *wnd, EDITSTATE *es)
{
INT32 s = es->selection_start;
INT32 e = es->selection_end;
HGLOBAL16 hdst;
LPSTR dst;
if (e == s)
return;
ORDER_INT32(s, e);
hdst = GlobalAlloc16(GMEM_MOVEABLE, (DWORD)(e - s + 1));
dst = GlobalLock16(hdst);
lstrcpyn32A(dst, es->text + s, e - s + 1);
GlobalUnlock16(hdst);
OpenClipboard32(wnd->hwndSelf);
EmptyClipboard32();
SetClipboardData16(CF_TEXT, hdst);
CloseClipboard32();
}
/*********************************************************************
*
* WM_CREATE
*
*/
static LRESULT EDIT_WM_Create(WND *wnd, LPCREATESTRUCT32A cs)
{
EDITSTATE *es;
if (!(es = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*es))))
return -1;
*(EDITSTATE **)wnd->wExtra = es;
if (!(es->heap = HeapCreate(0, 0x10000, 0)))
return -1;
es->style = cs->style;
/* remove the WS_CAPTION style if it has been set - this is really a */
/* pseudo option made from a combination of WS_BORDER and WS_DLGFRAME */
if ((es->style & WS_BORDER) && (es->style & WS_DLGFRAME))
es->style ^= WS_DLGFRAME;
if (es->style & ES_MULTILINE) {
es->buffer_size = BUFSTART_MULTI;
es->buffer_limit = BUFLIMIT_MULTI;
if (es->style & WS_VSCROLL)
es->style |= ES_AUTOVSCROLL;
if (es->style & WS_HSCROLL)
es->style |= ES_AUTOHSCROLL;
es->style &= ~ES_PASSWORD;
if ((es->style & ES_CENTER) || (es->style & ES_RIGHT)) {
if (es->style & ES_RIGHT)
es->style &= ~ES_CENTER;
es->style &= ~WS_HSCROLL;
es->style &= ~ES_AUTOHSCROLL;
}
/* FIXME: for now, all multi line controls are AUTOVSCROLL */
es->style |= ES_AUTOVSCROLL;
} else {
es->buffer_size = BUFSTART_SINGLE;
es->buffer_limit = BUFLIMIT_SINGLE;
es->style &= ~ES_CENTER;
es->style &= ~ES_RIGHT;
es->style &= ~WS_HSCROLL;
es->style &= ~WS_VSCROLL;
es->style &= ~ES_AUTOVSCROLL;
es->style &= ~ES_WANTRETURN;
if (es->style & ES_UPPERCASE) {
es->style &= ~ES_LOWERCASE;
es->style &= ~ES_NUMBER;
} else if (es->style & ES_LOWERCASE)
es->style &= ~ES_NUMBER;
if (es->style & ES_PASSWORD)
es->password_char = '*';
/* FIXME: for now, all single line controls are AUTOHSCROLL */
es->style |= ES_AUTOHSCROLL;
}
if (!(es->text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
return -1;
es->buffer_size = HeapSize(es->heap, 0, es->text) - 1;
if (!(es->undo_text = HeapAlloc(es->heap, 0, es->buffer_size + 1)))
return -1;
es->undo_buffer_size = HeapSize(es->heap, 0, es->undo_text) - 1;
*es->text = '\0';
if (es->style & ES_MULTILINE)
if (!(es->first_line_def = HeapAlloc(es->heap, HEAP_ZERO_MEMORY, sizeof(LINEDEF))))
return -1;
es->line_count = 1;
/*
* To initialize some final structure members, we call some helper
* functions. However, since the EDITSTATE is not consistent (i.e.
* not fully initialized), we should be very careful which
* functions can be called, and in what order.
*/
EDIT_WM_SetFont(wnd, es, 0, FALSE);
if (cs->lpszName && *(cs->lpszName) != '\0') {
EDIT_EM_ReplaceSel(wnd, es, FALSE, cs->lpszName);
/* if we insert text to the editline, the text scrolls out of the window, as the caret is placed after the insert pos normally; thus we reset es->selection... to 0 and update caret */
es->selection_start = es->selection_end = 0;
EDIT_EM_ScrollCaret(wnd, es);
}
return 0;
}
/*********************************************************************
*
* WM_DESTROY
*
*/
static void EDIT_WM_Destroy(WND *wnd, EDITSTATE *es)
{
if (es->hloc32) {
while (LocalUnlock32(es->hloc32)) ;
LocalFree32(es->hloc32);
}
if (es->hloc16) {
while (LOCAL_Unlock(wnd->hInstance, es->hloc16)) ;
LOCAL_Free(wnd->hInstance, es->hloc16);
}
HeapDestroy(es->heap);
HeapFree(GetProcessHeap(), 0, es);
*(EDITSTATE **)wnd->wExtra = NULL;
}
/*********************************************************************
*
* WM_ERASEBKGND
*
*/
static LRESULT EDIT_WM_EraseBkGnd(WND *wnd, EDITSTATE *es, HDC32 dc)
{
HBRUSH32 brush;
RECT32 rc;
if (!(brush = (HBRUSH32)EDIT_SEND_CTLCOLOR(wnd, dc)))
brush = (HBRUSH32)GetStockObject32(WHITE_BRUSH);
GetClientRect32(wnd->hwndSelf, &rc);
IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
GetClipBox32(dc, &rc);
/*
* FIXME: specs say that we should UnrealizeObject() the brush,
* but the specs of UnrealizeObject() say that we shouldn't
* unrealize a stock object. The default brush that
* DefWndProc() returns is ... a stock object.
*/
FillRect32(dc, &rc, brush);
return -1;
}
/*********************************************************************
*
* WM_GETTEXT
*
*/
static INT32 EDIT_WM_GetText(WND *wnd, EDITSTATE *es, INT32 count, LPSTR text)
{
INT32 len = lstrlen32A(es->text);
if (count > len) {
lstrcpy32A(text, es->text);
return len;
} else
return -1;
}
/*********************************************************************
*
* EDIT_HScroll_Hack
*
* 16 bit notepad needs this. Actually it is not _our_ hack,
* it is notepad's. Notepad is sending us scrollbar messages with
* undocumented parameters without us even having a scrollbar ... !?!?
*
*/
static LRESULT EDIT_HScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
{
INT32 dx = 0;
INT32 fw = es->format_rect.right - es->format_rect.left;
LRESULT ret = 0;
if (!(es->flags & EF_HSCROLL_HACK)) {
ERR(edit, "hacked WM_HSCROLL handler invoked\n");
ERR(edit, " if you are _not_ running 16 bit notepad, please report\n");
ERR(edit, " (this message is only displayed once per edit control)\n");
es->flags |= EF_HSCROLL_HACK;
}
switch (action) {
case SB_LINELEFT:
if (es->x_offset)
dx = -es->char_width;
break;
case SB_LINERIGHT:
if (es->x_offset < es->text_width)
dx = es->char_width;
break;
case SB_PAGELEFT:
if (es->x_offset)
dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
break;
case SB_PAGERIGHT:
if (es->x_offset < es->text_width)
dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
break;
case SB_LEFT:
if (es->x_offset)
dx = -es->x_offset;
break;
case SB_RIGHT:
if (es->x_offset < es->text_width)
dx = es->text_width - es->x_offset;
break;
case SB_THUMBTRACK:
es->flags |= EF_HSCROLL_TRACK;
dx = pos * es->text_width / 100 - es->x_offset;
break;
case SB_THUMBPOSITION:
es->flags &= ~EF_HSCROLL_TRACK;
if (!(dx = pos * es->text_width / 100 - es->x_offset))
EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
break;
case SB_ENDSCROLL:
break;
/*
* FIXME : the next two are undocumented !
* Are we doing the right thing ?
* At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
* although it's also a regular control message.
*/
case EM_GETTHUMB16:
ret = es->text_width ? es->x_offset * 100 / es->text_width : 0;
break;
case EM_LINESCROLL16:
dx = pos;
break;
default:
ERR(edit, "undocumented (hacked) WM_HSCROLL parameter, please report\n");
return 0;
}
if (dx)
EDIT_EM_LineScroll(wnd, es, dx, 0);
return ret;
}
/*********************************************************************
*
* WM_HSCROLL
*
*/
static LRESULT EDIT_WM_HScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
{
INT32 dx;
INT32 fw;
if (!(es->style & ES_MULTILINE))
return 0;
if (!(es->style & ES_AUTOHSCROLL))
return 0;
if (!(es->style & WS_HSCROLL))
return EDIT_HScroll_Hack(wnd, es, action, pos, scroll_bar);
dx = 0;
fw = es->format_rect.right - es->format_rect.left;
switch (action) {
case SB_LINELEFT:
if (es->x_offset)
dx = -es->char_width;
break;
case SB_LINERIGHT:
if (es->x_offset < es->text_width)
dx = es->char_width;
break;
case SB_PAGELEFT:
if (es->x_offset)
dx = -fw / HSCROLL_FRACTION / es->char_width * es->char_width;
break;
case SB_PAGERIGHT:
if (es->x_offset < es->text_width)
dx = fw / HSCROLL_FRACTION / es->char_width * es->char_width;
break;
case SB_LEFT:
if (es->x_offset)
dx = -es->x_offset;
break;
case SB_RIGHT:
if (es->x_offset < es->text_width)
dx = es->text_width - es->x_offset;
break;
case SB_THUMBTRACK:
es->flags |= EF_HSCROLL_TRACK;
dx = pos - es->x_offset;
break;
case SB_THUMBPOSITION:
es->flags &= ~EF_HSCROLL_TRACK;
if (!(dx = pos - es->x_offset)) {
SetScrollPos32(wnd->hwndSelf, SB_HORZ, pos, TRUE);
EDIT_NOTIFY_PARENT(wnd, EN_HSCROLL, "EN_HSCROLL");
}
break;
case SB_ENDSCROLL:
break;
default:
ERR(edit, "undocumented WM_HSCROLL parameter, please report\n");
return 0;
}
if (dx)
EDIT_EM_LineScroll(wnd, es, dx, 0);
return 0;
}
/*********************************************************************
*
* EDIT_CheckCombo
*
*/
static BOOL32 EDIT_CheckCombo(WND *wnd, UINT32 msg, INT32 key, DWORD key_data)
{
HWND32 hLBox;
if (WIDGETS_IsControl32(wnd->parent, BIC32_COMBO) &&
(hLBox = COMBO_GetLBWindow(wnd->parent))) {
HWND32 hCombo = wnd->parent->hwndSelf;
BOOL32 bUIFlip = TRUE;
TRACE(combo, "[%04x]: handling msg %04x (%04x)\n",
wnd->hwndSelf, (UINT16)msg, (UINT16)key);
switch (msg) {
case WM_KEYDOWN: /* Handle F4 and arrow keys */
if (key != VK_F4) {
bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
if (SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0))
bUIFlip = FALSE;
}
if (!bUIFlip)
SendMessage32A(hLBox, WM_KEYDOWN, (WPARAM32)key, 0);
else {
/* make sure ComboLBox pops up */
SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 0, 0);
SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
SendMessage32A(hCombo, CB_SETEXTENDEDUI32, 1, 0);
}
break;
case WM_SYSKEYDOWN: /* Handle Alt+up/down arrows */
bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETEXTENDEDUI32, 0, 0);
if (bUIFlip) {
bUIFlip = (BOOL32)SendMessage32A(hCombo, CB_GETDROPPEDSTATE32, 0, 0);
SendMessage32A(hCombo, CB_SHOWDROPDOWN32, (bUIFlip) ? FALSE : TRUE, 0);
} else
SendMessage32A(hLBox, WM_KEYDOWN, VK_F4, 0);
break;
}
return TRUE;
}
return FALSE;
}
/*********************************************************************
*
* WM_KEYDOWN
*
* Handling of special keys that don't produce a WM_CHAR
* (i.e. non-printable keys) & Backspace & Delete
*
*/
static LRESULT EDIT_WM_KeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
{
BOOL32 shift;
BOOL32 control;
if (GetKeyState32(VK_MENU) & 0x8000)
return 0;
shift = GetKeyState32(VK_SHIFT) & 0x8000;
control = GetKeyState32(VK_CONTROL) & 0x8000;
switch (key) {
case VK_F4:
case VK_UP:
if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
break;
if (key == VK_F4)
break;
/* fall through */
case VK_LEFT:
if ((es->style & ES_MULTILINE) && (key == VK_UP))
EDIT_MoveUp_ML(wnd, es, shift);
else
if (control)
EDIT_MoveWordBackward(wnd, es, shift);
else
EDIT_MoveBackward(wnd, es, shift);
break;
case VK_DOWN:
if (EDIT_CheckCombo(wnd, WM_KEYDOWN, key, key_data))
break;
/* fall through */
case VK_RIGHT:
if ((es->style & ES_MULTILINE) && (key == VK_DOWN))
EDIT_MoveDown_ML(wnd, es, shift);
else if (control)
EDIT_MoveWordForward(wnd, es, shift);
else
EDIT_MoveForward(wnd, es, shift);
break;
case VK_HOME:
EDIT_MoveHome(wnd, es, shift);
break;
case VK_END:
EDIT_MoveEnd(wnd, es, shift);
break;
case VK_PRIOR:
if (es->style & ES_MULTILINE)
EDIT_MovePageUp_ML(wnd, es, shift);
break;
case VK_NEXT:
if (es->style & ES_MULTILINE)
EDIT_MovePageDown_ML(wnd, es, shift);
break;
case VK_BACK:
if (!(es->style & ES_READONLY) && !control)
if (es->selection_start != es->selection_end)
EDIT_WM_Clear(wnd, es);
else {
/* delete character left of caret */
EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
EDIT_MoveBackward(wnd, es, TRUE);
EDIT_WM_Clear(wnd, es);
}
break;
case VK_DELETE:
if (!(es->style & ES_READONLY) && !(shift && control))
if (es->selection_start != es->selection_end) {
if (shift)
EDIT_WM_Cut(wnd, es);
else
EDIT_WM_Clear(wnd, es);
} else {
if (shift) {
/* delete character left of caret */
EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
EDIT_MoveBackward(wnd, es, TRUE);
EDIT_WM_Clear(wnd, es);
} else if (control) {
/* delete to end of line */
EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
EDIT_MoveEnd(wnd, es, TRUE);
EDIT_WM_Clear(wnd, es);
} else {
/* delete character right of caret */
EDIT_EM_SetSel(wnd, es, -1, 0, FALSE);
EDIT_MoveForward(wnd, es, TRUE);
EDIT_WM_Clear(wnd, es);
}
}
break;
case VK_INSERT:
if (shift) {
if (!(es->style & ES_READONLY))
EDIT_WM_Paste(wnd, es);
} else if (control)
EDIT_WM_Copy(wnd, es);
break;
}
return 0;
}
/*********************************************************************
*
* WM_KILLFOCUS
*
*/
static LRESULT EDIT_WM_KillFocus(WND *wnd, EDITSTATE *es, HWND32 window_getting_focus)
{
es->flags &= ~EF_FOCUSED;
DestroyCaret32();
if(!(es->style & ES_NOHIDESEL))
EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
EDIT_NOTIFY_PARENT(wnd, EN_KILLFOCUS, "EN_KILLFOCUS");
return 0;
}
/*********************************************************************
*
* WM_LBUTTONDBLCLK
*
* The caret position has been set on the WM_LBUTTONDOWN message
*
*/
static LRESULT EDIT_WM_LButtonDblClk(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
{
INT32 s;
INT32 e = es->selection_end;
INT32 l;
INT32 li;
INT32 ll;
if (!(es->flags & EF_FOCUSED))
return 0;
l = EDIT_EM_LineFromChar(wnd, es, e);
li = EDIT_EM_LineIndex(wnd, es, l);
ll = EDIT_EM_LineLength(wnd, es, e);
s = li + EDIT_CallWordBreakProc (wnd, es, li, e - li, ll, WB_LEFT);
e = li + EDIT_CallWordBreakProc(wnd, es, li, e - li, ll, WB_RIGHT);
EDIT_EM_SetSel(wnd, es, s, e, FALSE);
EDIT_EM_ScrollCaret(wnd, es);
return 0;
}
/*********************************************************************
*
* WM_LBUTTONDOWN
*
*/
static LRESULT EDIT_WM_LButtonDown(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
{
INT32 e;
BOOL32 after_wrap;
if (!(es->flags & EF_FOCUSED))
return 0;
SetCapture32(wnd->hwndSelf);
EDIT_ConfinePoint(wnd, es, &x, &y);
e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
EDIT_EM_SetSel(wnd, es, (keys & MK_SHIFT) ? es->selection_start : e, e, after_wrap);
EDIT_EM_ScrollCaret(wnd, es);
es->region_posx = es->region_posy = 0;
SetTimer32(wnd->hwndSelf, 0, 100, NULL);
return 0;
}
/*********************************************************************
*
* WM_LBUTTONUP
*
*/
static LRESULT EDIT_WM_LButtonUp(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
{
if (GetCapture32() == wnd->hwndSelf) {
KillTimer32(wnd->hwndSelf, 0);
ReleaseCapture();
}
return 0;
}
/*********************************************************************
*
* WM_MOUSEMOVE
*
*/
static LRESULT EDIT_WM_MouseMove(WND *wnd, EDITSTATE *es, DWORD keys, INT32 x, INT32 y)
{
INT32 e;
BOOL32 after_wrap;
INT32 prex, prey;
if (GetCapture32() != wnd->hwndSelf)
return 0;
/*
* FIXME: gotta do some scrolling if outside client
* area. Maybe reset the timer ?
*/
prex = x; prey = y;
EDIT_ConfinePoint(wnd, es, &x, &y);
es->region_posx = (prex < x) ? -1 : ((prex > x) ? 1 : 0);
es->region_posy = (prey < y) ? -1 : ((prey > y) ? 1 : 0);
e = EDIT_CharFromPos(wnd, es, x, y, &after_wrap);
EDIT_EM_SetSel(wnd, es, es->selection_start, e, after_wrap);
return 0;
}
/*********************************************************************
*
* WM_PAINT
*
*/
static void EDIT_WM_Paint(WND *wnd, EDITSTATE *es)
{
PAINTSTRUCT32 ps;
INT32 i;
HDC32 dc;
HFONT32 old_font = 0;
RECT32 rc;
RECT32 rcLine;
RECT32 rcRgn;
LRESULT pos;
BOOL32 rev = IsWindowEnabled32(wnd->hwndSelf) &&
((es->flags & EF_FOCUSED) ||
(es->style & ES_NOHIDESEL));
if (es->flags & EF_UPDATE)
EDIT_NOTIFY_PARENT(wnd, EN_UPDATE, "EN_UPDATE");
dc = BeginPaint32(wnd->hwndSelf, &ps);
IntersectClipRect32(dc, es->format_rect.left,
es->format_rect.top,
es->format_rect.right,
es->format_rect.bottom);
if (es->style & ES_MULTILINE) {
GetClientRect32(wnd->hwndSelf, &rc);
IntersectClipRect32(dc, rc.left, rc.top, rc.right, rc.bottom);
}
if (es->font)
old_font = SelectObject32(dc, es->font);
EDIT_SEND_CTLCOLOR(wnd, dc);
if (!IsWindowEnabled32(wnd->hwndSelf))
SetTextColor32(dc, GetSysColor32(COLOR_GRAYTEXT));
GetClipBox32(dc, &rcRgn);
if (es->style & ES_MULTILINE) {
INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
for (i = es->y_offset ; i <= MIN(es->y_offset + vlc, es->y_offset + es->line_count - 1) ; i++) {
EDIT_GetLineRect(wnd, es, i, 0, -1, &rcLine);
if (IntersectRect32(&rc, &rcRgn, &rcLine))
EDIT_PaintLine(wnd, es, dc, i, rev);
}
} else {
EDIT_GetLineRect(wnd, es, 0, 0, -1, &rcLine);
if (IntersectRect32(&rc, &rcRgn, &rcLine))
EDIT_PaintLine(wnd, es, dc, 0, rev);
}
if (es->font)
SelectObject32(dc, old_font);
if (es->flags & EF_FOCUSED) {
pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
}
EndPaint32(wnd->hwndSelf, &ps);
if ((es->style & WS_VSCROLL) && !(es->flags & EF_VSCROLL_TRACK)) {
INT32 vlc = (es->format_rect.bottom - es->format_rect.top) / es->line_height;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->line_count + vlc - 2;
si.nPage = vlc;
si.nPos = es->y_offset;
SetScrollInfo32(wnd->hwndSelf, SB_VERT, &si, TRUE);
}
if ((es->style & WS_HSCROLL) && !(es->flags & EF_HSCROLL_TRACK)) {
SCROLLINFO si;
INT32 fw = es->format_rect.right - es->format_rect.left;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = es->text_width + fw - 1;
si.nPage = fw;
si.nPos = es->x_offset;
SetScrollInfo32(wnd->hwndSelf, SB_HORZ, &si, TRUE);
}
if (es->flags & EF_UPDATE) {
es->flags &= ~EF_UPDATE;
EDIT_NOTIFY_PARENT(wnd, EN_CHANGE, "EN_CHANGE");
}
}
/*********************************************************************
*
* WM_PASTE
*
* FIXME: replace with 32 bit handler once GetClipboardData32() is
* implemented in misc/clipboard.c
*
*/
static void EDIT_WM_Paste(WND *wnd, EDITSTATE *es)
{
HGLOBAL16 hsrc;
LPSTR src;
OpenClipboard32(wnd->hwndSelf);
if ((hsrc = GetClipboardData16(CF_TEXT))) {
src = (LPSTR)GlobalLock16(hsrc);
EDIT_EM_ReplaceSel(wnd, es, TRUE, src);
GlobalUnlock16(hsrc);
}
CloseClipboard32();
}
/*********************************************************************
*
* WM_SETFOCUS
*
*/
static void EDIT_WM_SetFocus(WND *wnd, EDITSTATE *es, HWND32 window_losing_focus)
{
LRESULT pos;
es->flags |= EF_FOCUSED;
CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
if(!(es->style & ES_NOHIDESEL))
EDIT_InvalidateText(wnd, es, es->selection_start, es->selection_end);
ShowCaret32(wnd->hwndSelf);
EDIT_NOTIFY_PARENT(wnd, EN_SETFOCUS, "EN_SETFOCUS");
}
/*********************************************************************
*
* WM_SETFONT
*
*/
static void EDIT_WM_SetFont(WND *wnd, EDITSTATE *es, HFONT32 font, BOOL32 redraw)
{
TEXTMETRIC32A tm;
HDC32 dc;
HFONT32 old_font = 0;
es->font = font;
dc = GetDC32(wnd->hwndSelf);
if (font)
old_font = SelectObject32(dc, font);
GetTextMetrics32A(dc, &tm);
es->line_height = tm.tmHeight;
es->char_width = tm.tmAveCharWidth;
if (font)
SelectObject32(dc, old_font);
ReleaseDC32(wnd->hwndSelf, dc);
if (wnd->flags & WIN_ISWIN32)
EDIT_EM_SetMargins(wnd, es, EC_USEFONTINFO, 0, 0);
if (es->style & ES_MULTILINE)
EDIT_BuildLineDefs_ML(wnd, es);
if (redraw)
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
if (es->flags & EF_FOCUSED) {
LRESULT pos;
DestroyCaret32();
CreateCaret32(wnd->hwndSelf, 0, 2, es->line_height);
pos = EDIT_EM_PosFromChar(wnd, es, es->selection_end, es->flags & EF_AFTER_WRAP);
SetCaretPos32(SLOWORD(pos), SHIWORD(pos));
ShowCaret32(wnd->hwndSelf);
}
}
/*********************************************************************
*
* WM_SETTEXT
*
*/
static void EDIT_WM_SetText(WND *wnd, EDITSTATE *es, LPCSTR text)
{
EDIT_EM_SetSel(wnd, es, 0, -1, FALSE);
if (text) {
TRACE(edit, "\t'%s'\n", text);
EDIT_EM_ReplaceSel(wnd, es, FALSE, text);
es->x_offset = 0;
}
es->flags |= EF_MODIFIED;
es->flags |= EF_UPDATE;
EDIT_EM_ScrollCaret(wnd, es);
}
/*********************************************************************
*
* WM_SIZE
*
*/
static void EDIT_WM_Size(WND *wnd, EDITSTATE *es, UINT32 action, INT32 width, INT32 height)
{
if ((action == SIZE_MAXIMIZED) || (action == SIZE_RESTORED)) {
RECT32 rc;
SetRect32(&rc, 0, 0, width, height);
EDIT_SetRectNP(wnd, es, &rc);
InvalidateRect32(wnd->hwndSelf, NULL, TRUE);
}
}
/*********************************************************************
*
* WM_SYSKEYDOWN
*
*/
static LRESULT EDIT_WM_SysKeyDown(WND *wnd, EDITSTATE *es, INT32 key, DWORD key_data)
{
if ((key == VK_BACK) && (key_data & 0x2000)) {
if (EDIT_EM_CanUndo(wnd, es))
EDIT_EM_Undo(wnd, es);
return 0;
} else if (key == VK_UP || key == VK_DOWN)
if (EDIT_CheckCombo(wnd, WM_SYSKEYDOWN, key, key_data))
return 0;
return DefWindowProc32A(wnd->hwndSelf, WM_SYSKEYDOWN, (WPARAM32)key, (LPARAM)key_data);
}
/*********************************************************************
*
* WM_TIMER
*
*/
static void EDIT_WM_Timer(WND *wnd, EDITSTATE *es, INT32 id, TIMERPROC32 timer_proc)
{
if (es->region_posx < 0) {
EDIT_MoveBackward(wnd, es, TRUE);
} else if (es->region_posx > 0) {
EDIT_MoveForward(wnd, es, TRUE);
}
/*
* FIXME: gotta do some vertical scrolling here, like
* EDIT_EM_LineScroll(wnd, 0, 1);
*/
}
/*********************************************************************
*
* EDIT_VScroll_Hack
*
* 16 bit notepad needs this. Actually it is not _our_ hack,
* it is notepad's. Notepad is sending us scrollbar messages with
* undocumented parameters without us even having a scrollbar ... !?!?
*
*/
static LRESULT EDIT_VScroll_Hack(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
{
INT32 dy = 0;
LRESULT ret = 0;
if (!(es->flags & EF_VSCROLL_HACK)) {
ERR(edit, "hacked WM_VSCROLL handler invoked\n");
ERR(edit, " if you are _not_ running 16 bit notepad, please report\n");
ERR(edit, " (this message is only displayed once per edit control)\n");
es->flags |= EF_VSCROLL_HACK;
}
switch (action) {
case SB_LINEUP:
case SB_LINEDOWN:
case SB_PAGEUP:
case SB_PAGEDOWN:
EDIT_EM_Scroll(wnd, es, action);
return 0;
case SB_TOP:
dy = -es->y_offset;
break;
case SB_BOTTOM:
dy = es->line_count - 1 - es->y_offset;
break;
case SB_THUMBTRACK:
es->flags |= EF_VSCROLL_TRACK;
dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset;
break;
case SB_THUMBPOSITION:
es->flags &= ~EF_VSCROLL_TRACK;
if (!(dy = (pos * (es->line_count - 1) + 50) / 100 - es->y_offset))
EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
break;
case SB_ENDSCROLL:
break;
/*
* FIXME : the next two are undocumented !
* Are we doing the right thing ?
* At least Win 3.1 Notepad makes use of EM_GETTHUMB this way,
* although it's also a regular control message.
*/
case EM_GETTHUMB16:
ret = (es->line_count > 1) ? es->y_offset * 100 / (es->line_count - 1) : 0;
break;
case EM_LINESCROLL16:
dy = pos;
break;
default:
ERR(edit, "undocumented (hacked) WM_VSCROLL parameter, please report\n");
return 0;
}
if (dy)
EDIT_EM_LineScroll(wnd, es, 0, dy);
return ret;
}
/*********************************************************************
*
* WM_VSCROLL
*
*/
static LRESULT EDIT_WM_VScroll(WND *wnd, EDITSTATE *es, INT32 action, INT32 pos, HWND32 scroll_bar)
{
INT32 dy;
if (!(es->style & ES_MULTILINE))
return 0;
if (!(es->style & ES_AUTOVSCROLL))
return 0;
if (!(es->style & WS_VSCROLL))
return EDIT_VScroll_Hack(wnd, es, action, pos, scroll_bar);
dy = 0;
switch (action) {
case SB_LINEUP:
case SB_LINEDOWN:
case SB_PAGEUP:
case SB_PAGEDOWN:
EDIT_EM_Scroll(wnd, es, action);
return 0;
case SB_TOP:
dy = -es->y_offset;
break;
case SB_BOTTOM:
dy = es->line_count - 1 - es->y_offset;
break;
case SB_THUMBTRACK:
es->flags |= EF_VSCROLL_TRACK;
dy = pos - es->y_offset;
break;
case SB_THUMBPOSITION:
es->flags &= ~EF_VSCROLL_TRACK;
if (!(dy = pos - es->y_offset)) {
SetScrollPos32(wnd->hwndSelf, SB_VERT, pos, TRUE);
EDIT_NOTIFY_PARENT(wnd, EN_VSCROLL, "EN_VSCROLL");
}
break;
case SB_ENDSCROLL:
break;
default:
ERR(edit, "undocumented WM_VSCROLL parameter, please report\n");
return 0;
}
if (dy)
EDIT_EM_LineScroll(wnd, es, 0, dy);
return 0;
}