📄 edit.c
字号:
/*
* Edit control
*
* Copyright David W. Metcalfe, 1994
* Copyright William Magro, 1995, 1996
* Copyright Frans van Dorsselaer, 1996, 1997
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Oct. 8, 2004, by Dimitrie O. Paun.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features, or bugs, please note them below.
*
* TODO:
* - EDITBALLOONTIP structure
* - EM_GETCUEBANNER/Edit_GetCueBannerText
* - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
* - EM_SETCUEBANNER/Edit_SetCueBannerText
* - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
* - EM_GETIMESTATUS, EM_SETIMESTATUS
* - EN_ALIGN_LTR_EC
* - EN_ALIGN_RTL_EC
* - ES_OEMCONVERT
*
*/
#include <user32.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(edit);
WINE_DECLARE_DEBUG_CHANNEL(combo);
WINE_DECLARE_DEBUG_CHANNEL(relay);
#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 GROWLENGTH 32 /* buffers granularity in bytes: must be power of 2 */
#define ROUND_TO_GROW(size) (((size) + (GROWLENGTH - 1)) & ~(GROWLENGTH - 1))
#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 */
#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_AFTER_WRAP 0x0080 /* the caret is displayed after the last character of a
wrapped line, instead of in front of the next character */
#define EF_USE_SOFTBRK 0x0100 /* Enable soft breaks in text. */
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' */
END_RICH /* line ends with a single '\n' */
} LINE_END;
typedef struct tagLINEDEF {
INT length; /* bruto length of a line in bytes */
INT net_length; /* netto length of a line in visible characters */
LINE_END ending;
INT width; /* width of the line in pixels */
INT index; /* line index into the buffer */
struct tagLINEDEF *next;
} LINEDEF;
typedef struct
{
BOOL is_unicode; /* how the control was created */
LPWSTR text; /* the actual contents of the control */
UINT buffer_size; /* the size of the buffer in characters */
UINT buffer_limit; /* the maximum size to which the buffer may grow in characters */
HFONT font; /* NULL means standard system font */
INT x_offset; /* scroll offset for multi lines this is in pixels
for single lines it's in characters */
INT line_height; /* height of a screen line in pixels */
INT 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) */
INT undo_insert_count; /* number of characters inserted in sequence */
UINT undo_position; /* character index of the insertion and deletion */
LPWSTR undo_text; /* deleted text */
UINT undo_buffer_size; /* size of the deleted text buffer */
INT selection_start; /* == selection_end if no selection */
INT selection_end; /* == current caret position */
WCHAR password_char; /* == 0 if no password char, and for multi line controls */
INT left_margin; /* in pixels */
INT right_margin; /* in pixels */
RECT format_rect;
INT text_width; /* width of the widest line in pixels for multi line controls
and just line width for single line controls */
INT region_posx; /* Position of cursor relative to region: */
INT region_posy; /* -1: to left, 0: within, 1: to right */
#ifndef __REACTOS__
EDITWORDBREAKPROC16 word_break_proc16;
#endif
void *word_break_proc; /* 32-bit word break proc: ANSI or Unicode */
INT line_count; /* number of lines */
INT y_offset; /* scroll offset in number of lines */
BOOL bCaptureState; /* flag indicating whether mouse was captured */
BOOL bEnableState; /* flag keeping the enable state */
HWND hwndSelf; /* the our window handle */
HWND hwndParent; /* Handle of parent for sending EN_* messages.
Even if parent will change, EN_* messages
should be sent to the first parent. */
HWND hwndListBox; /* handle of ComboBox's listbox or NULL */
/*
* only for multi line controls
*/
INT lock_count; /* amount of re-entries in the EditWndProc */
INT tabs_count;
LPINT tabs;
LINEDEF *first_line_def; /* linked list of (soft) linebreaks */
HLOCAL hloc32W; /* our unicode local memory block */
#ifndef __REACTOS__
HLOCAL16 hloc16; /* alias for 16-bit control receiving EM_GETHANDLE16
or EM_SETHANDLE16 */
#endif
HLOCAL hloc32A; /* alias for ANSI control receiving EM_GETHANDLE
or EM_SETHANDLE */
/*
* IME Data
*/
UINT composition_len; /* length of composition, 0 == no composition */
int composition_start; /* the character position for the composition */
} EDITSTATE;
#define SWAP_UINT32(x,y) do { UINT temp = (UINT)(x); (x) = (UINT)(y); (y) = temp; } while(0)
#define ORDER_UINT(x,y) do { if ((UINT)(y) < (UINT)(x)) SWAP_UINT32((x),(y)); } while(0)
/* used for disabled or read-only edit control */
#define EDIT_NOTIFY_PARENT(es, wNotifyCode) \
do \
{ /* Notify parent which has created this edit control */ \
TRACE("notification " #wNotifyCode " sent to hwnd=%p\n", es->hwndParent); \
SendMessageW(es->hwndParent, WM_COMMAND, \
MAKEWPARAM(GetWindowLongPtrW((es->hwndSelf),GWLP_ID), wNotifyCode), \
(LPARAM)(es->hwndSelf)); \
} while(0)
/*********************************************************************
*
* Declarations
*
*/
/*
* These functions have trivial implementations
* We still like to call them internally
* "static inline" makes them more like macro's
*/
static __inline BOOL EDIT_EM_CanUndo(EDITSTATE *es);
static __inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es);
static __inline void EDIT_WM_Clear(EDITSTATE *es);
static __inline void EDIT_WM_Cut(EDITSTATE *es);
/*
* Helper functions only valid for one type of control
*/
static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT iStart, INT iEnd, INT delta, HRGN hrgn);
static void EDIT_CalcLineWidth_SL(EDITSTATE *es);
static LPWSTR EDIT_GetPasswordPointer_SL(EDITSTATE *es);
static void EDIT_MoveDown_ML(EDITSTATE *es, BOOL extend);
static void EDIT_MovePageDown_ML(EDITSTATE *es, BOOL extend);
static void EDIT_MovePageUp_ML(EDITSTATE *es, BOOL extend);
static void EDIT_MoveUp_ML(EDITSTATE *es, BOOL extend);
/*
* Helper functions valid for both single line _and_ multi line controls
*/
static INT EDIT_CallWordBreakProc(EDITSTATE *es, INT start, INT index, INT count, INT action);
static INT EDIT_CharFromPos(EDITSTATE *es, INT x, INT y, LPBOOL after_wrap);
static void EDIT_ConfinePoint(EDITSTATE *es, LPINT x, LPINT y);
static void EDIT_GetLineRect(EDITSTATE *es, INT line, INT scol, INT ecol, LPRECT rc);
static void EDIT_InvalidateText(EDITSTATE *es, INT start, INT end);
static void EDIT_LockBuffer(EDITSTATE *es);
static BOOL EDIT_MakeFit(EDITSTATE *es, UINT size);
static BOOL EDIT_MakeUndoFit(EDITSTATE *es, UINT size);
static void EDIT_MoveBackward(EDITSTATE *es, BOOL extend);
static void EDIT_MoveEnd(EDITSTATE *es, BOOL extend);
static void EDIT_MoveForward(EDITSTATE *es, BOOL extend);
static void EDIT_MoveHome(EDITSTATE *es, BOOL extend);
static void EDIT_MoveWordBackward(EDITSTATE *es, BOOL extend);
static void EDIT_MoveWordForward(EDITSTATE *es, BOOL extend);
static void EDIT_PaintLine(EDITSTATE *es, HDC hdc, INT line, BOOL rev);
static INT EDIT_PaintText(EDITSTATE *es, HDC hdc, INT x, INT y, INT line, INT col, INT count, BOOL rev);
static void EDIT_SetCaretPos(EDITSTATE *es, INT pos, BOOL after_wrap);
static void EDIT_AdjustFormatRect(EDITSTATE *es);
static void EDIT_SetRectNP(EDITSTATE *es, LPRECT lprc);
static void EDIT_UnlockBuffer(EDITSTATE *es, BOOL force);
static void EDIT_UpdateScrollInfo(EDITSTATE *es);
static INT CALLBACK EDIT_WordBreakProc(LPWSTR s, INT index, INT count, INT action);
/*
* EM_XXX message handlers
*/
static LRESULT EDIT_EM_CharFromPos(EDITSTATE *es, INT x, INT y);
static BOOL EDIT_EM_FmtLines(EDITSTATE *es, BOOL add_eol);
static HLOCAL EDIT_EM_GetHandle(EDITSTATE *es);
#ifndef __REACTOS__
static HLOCAL16 EDIT_EM_GetHandle16(EDITSTATE *es);
#endif
static INT EDIT_EM_GetLine(EDITSTATE *es, INT line, LPWSTR dst, BOOL unicode);
static LRESULT EDIT_EM_GetSel(EDITSTATE *es, PUINT start, PUINT end);
static LRESULT EDIT_EM_GetThumb(EDITSTATE *es);
static INT EDIT_EM_LineFromChar(EDITSTATE *es, INT index);
static INT EDIT_EM_LineIndex(EDITSTATE *es, INT line);
static INT EDIT_EM_LineLength(EDITSTATE *es, INT index);
static BOOL EDIT_EM_LineScroll(EDITSTATE *es, INT dx, INT dy);
static BOOL EDIT_EM_LineScroll_internal(EDITSTATE *es, INT dx, INT dy);
static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, LPCWSTR lpsz_replace, BOOL send_update, BOOL honor_limit);
static LRESULT EDIT_EM_Scroll(EDITSTATE *es, INT action);
static void EDIT_EM_ScrollCaret(EDITSTATE *es);
static void EDIT_EM_SetHandle(EDITSTATE *es, HLOCAL hloc);
#ifndef __REACTOS__
static void EDIT_EM_SetHandle16(EDITSTATE *es, HLOCAL16 hloc);
#endif
static void EDIT_EM_SetLimitText(EDITSTATE *es, INT limit);
static void EDIT_EM_SetMargins(EDITSTATE *es, INT action, WORD left, WORD right, BOOL repaint);
static void EDIT_EM_SetPasswordChar(EDITSTATE *es, WCHAR c);
static void EDIT_EM_SetSel(EDITSTATE *es, UINT start, UINT end, BOOL after_wrap);
static BOOL EDIT_EM_SetTabStops(EDITSTATE *es, INT count, LPINT tabs);
#ifndef __REACTOS__
static BOOL EDIT_EM_SetTabStops16(EDITSTATE *es, INT count, LPINT16 tabs);
#endif
static void EDIT_EM_SetWordBreakProc(EDITSTATE *es, void *wbp);
#ifndef __REACTOS__
static void EDIT_EM_SetWordBreakProc16(EDITSTATE *es, EDITWORDBREAKPROC16 wbp);
#endif
static BOOL EDIT_EM_Undo(EDITSTATE *es);
/*
* WM_XXX message handlers
*/
static void EDIT_WM_Char(EDITSTATE *es, WCHAR c);
static void EDIT_WM_Command(EDITSTATE *es, INT code, INT id, HWND conrtol);
static void EDIT_WM_ContextMenu(EDITSTATE *es, INT x, INT y);
static void EDIT_WM_Copy(EDITSTATE *es);
static LRESULT EDIT_WM_Create(EDITSTATE *es, LPCWSTR name);
static LRESULT EDIT_WM_Destroy(EDITSTATE *es);
static LRESULT EDIT_WM_EraseBkGnd(EDITSTATE *es, HDC dc);
static INT EDIT_WM_GetText(EDITSTATE *es, INT count, LPWSTR dst, BOOL unicode);
static LRESULT EDIT_WM_HScroll(EDITSTATE *es, INT action, INT pos);
static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key);
static LRESULT EDIT_WM_KillFocus(EDITSTATE *es);
static LRESULT EDIT_WM_LButtonDblClk(EDITSTATE *es);
static LRESULT EDIT_WM_LButtonDown(EDITSTATE *es, DWORD keys, INT x, INT y);
static LRESULT EDIT_WM_LButtonUp(EDITSTATE *es);
static LRESULT EDIT_WM_MButtonDown(EDITSTATE *es);
static LRESULT EDIT_WM_MouseMove(EDITSTATE *es, INT x, INT y);
static LRESULT EDIT_WM_NCCreate(HWND hwnd, LPCREATESTRUCTW lpcs, BOOL unicode);
static void EDIT_WM_Paint(EDITSTATE *es, HDC hdc);
static void EDIT_WM_Paste(EDITSTATE *es);
static void EDIT_WM_SetFocus(EDITSTATE *es);
static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw);
static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text, BOOL unicode);
static void EDIT_WM_Size(EDITSTATE *es, UINT action, INT width, INT height);
static LRESULT EDIT_WM_StyleChanged(EDITSTATE *es, WPARAM which, const STYLESTRUCT *style);
static LRESULT EDIT_WM_SysKeyDown(EDITSTATE *es, INT key, DWORD key_data);
static void EDIT_WM_Timer(EDITSTATE *es);
static LRESULT EDIT_WM_VScroll(EDITSTATE *es, INT action, INT pos);
static void EDIT_UpdateText(EDITSTATE *es, LPRECT rc, BOOL bErase);
static void EDIT_UpdateTextRegion(EDITSTATE *es, HRGN hrgn, BOOL bErase);
static void EDIT_ImeComposition(HWND hwnd, LPARAM CompFlag, EDITSTATE *es);
LRESULT WINAPI EditWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WINAPI EditWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/*********************************************************************
* edit class descriptor
*/
const struct builtin_class_descr EDIT_builtin_class =
{
#ifdef __REACTOS__
L"Edit", /* name */
CS_DBLCLKS | CS_PARENTDC, /* style */
(WNDPROC)EditWndProcW, /* procW */
(WNDPROC)EditWndProcA, /* procA */
sizeof(EDITSTATE *), /* extra */
(LPWSTR)IDC_IBEAM, /* cursor */
0 /* brush */
#else
"Edit", /* name */
CS_DBLCLKS | CS_PARENTDC, /* style */
EditWndProcA, /* procA */
EditWndProcW, /* procW */
sizeof(EDITSTATE *), /* extra */
IDC_IBEAM, /* cursor */
0 /* brush */
#endif
};
/*********************************************************************
*
* EM_CANUNDO
*
*/
static __inline BOOL EDIT_EM_CanUndo(EDITSTATE *es)
{
return (es->undo_insert_count || strlenW(es->undo_text));
}
/*********************************************************************
*
* EM_EMPTYUNDOBUFFER
*
*/
static __inline void EDIT_EM_EmptyUndoBuffer(EDITSTATE *es)
{
es->undo_insert_count = 0;
*es->undo_text = '\0';
}
/*********************************************************************
*
* WM_CLEAR
*
*/
static __inline void EDIT_WM_Clear(EDITSTATE *es)
{
static const WCHAR empty_stringW[] = {0};
/* Protect read-only edit control from modification */
if(es->style & ES_READONLY)
return;
EDIT_EM_ReplaceSel(es, TRUE, empty_stringW, TRUE, TRUE);
}
/*********************************************************************
*
* WM_CUT
*
*/
static __inline void EDIT_WM_Cut(EDITSTATE *es)
{
EDIT_WM_Copy(es);
EDIT_WM_Clear(es);
}
/**********************************************************************
* get_app_version
*
* Returns the window version in case Wine emulates a later version
* of windows than the application expects.
*
* In a number of cases when windows runs an application that was
* designed for an earlier windows version, windows reverts
* to "old" behaviour of that earlier version.
*
* An example is a disabled edit control that needs to be painted.
* Old style behaviour is to send a WM_CTLCOLOREDIT message. This was
* changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for
* applications with an expected version 0f 4.0 or higher.
*
*/
static DWORD get_app_version(void)
{
static DWORD version;
if (!version)
{
DWORD dwEmulatedVersion;
OSVERSIONINFOW info;
DWORD dwProcVersion = GetProcessVersion(0);
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
GetVersionExW( &info );
dwEmulatedVersion = MAKELONG( info.dwMinorVersion, info.dwMajorVersion );
/* FIXME: this may not be 100% correct; see discussion on the
* wine developer list in Nov 1999 */
version = dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion;
}
return version;
}
static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
{
HBRUSH hbrush;
UINT msg;
if ( get_app_version() >= 0x40000 && (!es->bEnableState || (es->style & ES_READONLY)))
msg = WM_CTLCOLORSTATIC;
else
msg = WM_CTLCOLOREDIT;
/* why do we notify to es->hwndParent, and we send this one to GetParent()? */
hbrush = (HBRUSH)SendMessageW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
if (!hbrush)
hbrush = (HBRUSH)DefWindowProcW(GetParent(es->hwndSelf), msg, (WPARAM)hdc, (LPARAM)es->hwndSelf);
return hbrush;
}
static __inline LRESULT DefWindowProcT(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, BOOL unicode)
{
if(unicode)
return DefWindowProcW(hwnd, msg, wParam, lParam);
else
return DefWindowProcA(hwnd, msg, wParam, lParam);
}
/*********************************************************************
*
* EditWndProc_common
*
* The messages are in the order of the actual integer values
* (which can be found in include/windows.h)
* Wherever possible the 16 bit versions are converted to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -