Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2019 lines
51 KiB

//// LPK_EDIT - Edit control support - C interface
//
// Handles all callouts from the standard US edit control.
//
// David C Brown (dbrown) 17th Nov 1996.
//
// Copyright (c) 1996-1997 Microsoft Corporation. All right reserved.
/*
* Core NT headers
*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntcsrdll.h>
#include <ntcsrsrv.h>
#define NONTOSPINTERLOCK
#include <ntosp.h>
/*
* Standard C runtime headers
*/
#include <limits.h>
#include <memory.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/*
* NtUser Client specific headers
*/
#include "usercli.h"
#include <winnlsp.h>
#include <ntsdexts.h>
#include <windowsx.h>
#include <newres.h>
#include <asdf.h>
/*
* Complex script language pack
*/
#include "lpk.h"
#include "lpk_glob.h"
// Don't link directly to NtUserCreateCaret
#undef CreateCaret
/// Unicode control characters
//
#define U_TAB 0x0009
#define U_FS 0x001C
#define U_GS 0x001D
#define U_RS 0x001E
#define U_US 0x001F
#define U_ZWNJ 0x200C
#define U_ZWJ 0x200D
#define U_LRM 0x200E
#define U_RLM 0x200F
#define U_LRE 0x202A
#define U_RLE 0x202B
#define U_PDF 0x202C
#define U_LRO 0x202D
#define U_RLO 0x202E
#define U_ISS 0x206A
#define U_ASS 0x206B
#define U_IAFS 0x206C
#define U_AAFS 0x206D
#define U_NADS 0x206E
#define U_NODS 0x206F
#define TRACE(a,b)
#define ASSERTS(a,b)
#define ASSERTHR(a,b)
/***************************************************************************\
* BOOL ECIsDBCSLeadByte( PED ped, BYTE cch )
*
* IsDBCSLeadByte for Edit Control use only.
*
* History: 18-Jun-1996 Hideyuki Nagase
\***************************************************************************/
BOOL ECIsDBCSLeadByte(PED ped, BYTE cch)
{
int i;
if (!ped->fDBCS || !ped->fAnsi)
return (FALSE);
for (i = 0; ped->DBCSVector[i]; i += 2) {
if ((ped->DBCSVector[i] <= cch) && (ped->DBCSVector[i+1] >= cch))
return (TRUE);
}
return (FALSE);
}
//// GetEditAnsiConversionCharset - Figure a proper charset to MBTWC ANSI edit control's data
//
// In some Far East settings, they associate the symbol font to their ANSI codepage for
// backward compatibility (this is not the case for Japanese though), otherwise we convert
// using page 0. Currently Uniscribe glyph table maps SYMBOLIC_FONT to 3 pages - U+00xx,
// U+F0xx and the system's ACP page.
//
// For Unicode control returns -1
int GetEditAnsiConversionCharset(PED ped)
{
int iCharset = ped->fAnsi ? ped->charSet : -1;
if (iCharset == SYMBOL_CHARSET || iCharset == OEM_CHARSET)
{
iCharset = ANSI_CHARSET; // assume page U+00xx
}
if (iCharset == ANSI_CHARSET && ped->fDBCS)
{
// In Chinese system, there is font association to map symbol to ACP
// (QueryFontAssocStatus returns non-null). More detail please refer
// to USER's ECGetDBCSVector(...)
CHARSETINFO csi;
if (TranslateCharsetInfo((DWORD*)UIntToPtr(g_ACP), &csi, TCI_SRCCODEPAGE))
iCharset = csi.ciCharset;
}
return iCharset;
}
//// MBCPtoWCCP - Translate multi-byte caret position to wide char caret position
//
// Translates from a multibyte caret position specified as a byte offset
// into an 8 bit string, to a widechar caret position, returned as a
// word offset into a 16 bit character string.
//
// If the codepage isn't a DBCS, the imput offset is returned unchanged.
//
// Returns E_FAIL if icpMbStr addresses the second byte of a double byte character
HRESULT MBCPtoWCCP(
PED ped, // In - Edit control structure
BYTE *pbMbStr, // In - Multi byte string
int icpMbStr, // In - Byte offset of caret in multibyte string
int *picpWcStr) { // Out - Wide char caret position
if (!ped->fDBCS || !ped->fAnsi) {
*picpWcStr = icpMbStr;
return S_OK;
}
// Scan through DBCS string counting characters
*picpWcStr = 0;
while (icpMbStr > 0) {
if (ECIsDBCSLeadByte(ped, *pbMbStr)) {
// Character takes two bytes
icpMbStr -= 2;
pbMbStr += 2;
} else {
// Character takes one byte
icpMbStr--;
pbMbStr++;
}
(*picpWcStr)++;
}
return icpMbStr == 0 ? S_OK : E_FAIL;
}
//// WCCPtoMBCP - Translate wide char caret position to multi-byte caret position
//
// Translates from a widechar caret position specified as a word offset
// into a 16 bit string, to a multibyte caret position, returned as a
// byte offset into an 8 bit character string.
HRESULT WCCPtoMBCP(
PED ped, // In - Edit control structure
BYTE *pbMbStr, // In - Multi byte string
int icpWcStr, // In - Wide char caret position
int *picpMbStr) { // Out - Byte offset of caret in multibyte string
if (!ped->fDBCS || !ped->fAnsi) {
*picpMbStr = icpWcStr;
return S_OK;
}
// Scan through DBCS string counting characters
*picpMbStr = 0;
while (icpWcStr > 0) {
if (ECIsDBCSLeadByte(ped, *pbMbStr)) {
// Character takes two bytes
(*picpMbStr) += 2;
pbMbStr += 2;
} else {
// Character takes one byte
(*picpMbStr)++;
pbMbStr++;
}
icpWcStr--;
}
return S_OK;
}
//// LeftEdgeX
//
// Returns the visual x offset (i.e. from the left edge of the window)
// to the left edge of a line of width iWidth given the current
// formatting state of the edit control, .format and .xOffset.
int LeftEdgeX(PED ped, INT iWidth) {
INT iX;
// First generate logical iX - offset forward from leading margin.
iX = 0;
switch (ped->format) {
case ES_LEFT: // leading margin alignment
if (ped->fWrap) {
iX = 0;
} else {
iX = -(INT)ped->xOffset;
}
break;
case ES_CENTER:
iX = (ped->rcFmt.right - ped->rcFmt.left - iWidth) / 2;
break;
case ES_RIGHT: // far margin alignment
iX = ped->rcFmt.right - ped->rcFmt.left - iWidth;
break;
}
// iX is logical offset from leading margin to leading edge of string
if (ped->format != ES_LEFT && iX < 0) {
iX = !ped->fWrap ? -(INT)ped->xOffset : 0;
}
// Now adjust for right to left origin and incorporate left margin
if (ped->fRtoLReading) {
iX = ped->rcFmt.right - (iX+iWidth);
} else {
iX += ped->rcFmt.left;
}
TRACE(EDIT, ("LeftEdgeX iWidth=%d, format=%d, xOffset=%d, fWrap=%d, fRtoLReading=%d, right-left=%d, returning %d",
iWidth, ped->format, ped->xOffset, ped->fWrap, ped->fRtoLReading, ped->rcFmt.right - ped->rcFmt.left, iX));
return iX;
}
///// Shaping engins IDs.
#define BIDI_SHAPING_ENGINE_DLL 1<<0
#define THAI_SHAPING_ENGINE_DLL 1<<1
#define INDIAN_SHAPING_ENGINE_DLL 1<<4
///
//// EditCreate
//
// Called from edecrare.c ECCreate.
//
// Return TRUE if create succeeded
BOOL EditCreate(PED ped, HWND hWnd) {
LONG_PTR dwExStyle, dwStyle;
TRACE(EDIT, ("EditCreate called."));
// Check if BIDI shaping engine is loaded then
// allow the edit control to switch its direction.
if (g_dwLoadedShapingDLLs & BIDI_SHAPING_ENGINE_DLL) {
ped->fAllowRTL = TRUE;
} else {
ped->fAllowRTL = FALSE;
}
// Process WS_EX flags
dwExStyle = GetWindowLongPtr(hWnd, GWL_EXSTYLE);
if (dwExStyle & WS_EX_LAYOUTRTL) {
dwExStyle = dwExStyle & ~WS_EX_LAYOUTRTL;
dwExStyle = dwExStyle ^ (WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
SetWindowLongPtr(hWnd, GWL_EXSTYLE, dwExStyle);
dwStyle = GetWindowLongPtr(hWnd, GWL_STYLE);
if (!(dwStyle & ES_CENTER)) {
dwStyle = dwStyle ^ ES_RIGHT;
SetWindowLongPtr(hWnd, GWL_STYLE, dwStyle);
}
}
if (dwExStyle & WS_EX_RIGHT && ped->format == ES_LEFT) {
ped->format = ES_RIGHT;
}
if (dwExStyle & WS_EX_RTLREADING) {
ped->fRtoLReading = TRUE;
switch (ped->format) {
case ES_LEFT: ped->format = ES_RIGHT; break;
case ES_RIGHT: ped->format = ES_LEFT; break;
}
}
return TRUE;
}
//// EditStringAnalyse
//
// Creates standard analysis parameters from the PED
HRESULT EditStringAnalyse(
HDC hdc,
PED ped,
PSTR pText,
int cch,
DWORD dwFlags,
int iMaxExtent,
STRING_ANALYSIS **ppsa){
HRESULT hr;
SCRIPT_TABDEF std;
int iTabExtent;
if (!ped->pTabStops)
{
std.cTabStops = 1;
std.iScale = 4;
std.pTabStops = &iTabExtent;
std.iTabOrigin = 0;
iTabExtent = ped->aveCharWidth * 8;
}
else
{
std.cTabStops = *ped->pTabStops;
std.iScale = 4; // Tabstops are already in device units
std.pTabStops = ped->pTabStops + 1;
std.iTabOrigin = 0;
}
hr = LpkStringAnalyse(
hdc,
ped->charPasswordChar ? (char*)&ped->charPasswordChar : pText,
cch, 0,
GetEditAnsiConversionCharset(ped),
dwFlags | SSA_FALLBACK | SSA_TAB
| (ped->fRtoLReading ? SSA_RTL : 0)
| (ped->fDisplayCtrl ? SSA_DZWG : 0)
| (ped->charPasswordChar ? SSA_PASSWORD : 0),
-1, iMaxExtent,
NULL, NULL, // Control, State
NULL, // Overriding Dx array
&std, // Tab definition
NULL, // Input class overrides
ppsa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditStringAnalyse - LpkStringAnalyse"));
}
return hr;
}
//// HScroll
//
// Checks wether the cursor is visible withing the rcFormat
// area, and if not, updates xOffset so that it's 1/4 of the
// way from the edge it was closest to.
//
// However, it never leaves whitespace between the leading margin
// and the leading edge of the string, nor will it leave whitespace
// between the trailing edge and the trailing margin when there's
// enough text in the string to fill the whole window.
//
// Implemented for the single line edit control.
BOOL EditHScroll(PED ped, HDC hdc, PSTR pText) {
int ichCaret;
int dx; // Distance to move RIGHTWARDS (i.e. visually)
int cx; // Original visual cursor position
int tw; // Text width
int rw; // Rectangle width
int ix; // ScriptCPtoX result
UINT uOldXOffset;
HRESULT hr;
STRING_ANALYSIS *psa;
if (!ped->cch || ped->ichCaret > ped->cch) {
ped->xOffset = 0;
return FALSE;
}
hr = EditStringAnalyse(hdc, ped, pText, ped->cch, SSA_GLYPHS, 0, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditHScroll - EditStringAnalyse"));
return FALSE;
}
MBCPtoWCCP(ped, pText, ped->ichCaret, &ichCaret);
uOldXOffset = ped->xOffset;
tw = psa->size.cx; // Text width
rw = ped->rcFmt.right-ped->rcFmt.left; // Window rectangle width
#ifdef CURSOR_ABSOLUTE_HOME_AND_END
if (ichCaret <= 0) {
cx = ped->fRtoLReading ? psa->size.cx : 0;
} else if (ichCaret >= psa->cInChars) {
cx = ped->fRtoLReading ? 0: psa->size.cx;
} else {
cx = ScriptCursorX(psa, ichCaret-1);
}
cx += LeftEdgeX(ped, tw);
#else
if (ichCaret <= 0) {
hr = ScriptStringCPtoX(psa, ichCaret, FALSE, &ix);
} else {
hr = ScriptStringCPtoX(psa, ichCaret-1, TRUE, &ix);
}
if (FAILED(hr)) {
ASSERTHR(hr, ("EditHScroll - ScriptStringCPtoX"));
ScriptStringFree(&psa);
return FALSE;
}
cx = LeftEdgeX(ped, tw) + ix;
#endif
if (cx < ped->rcFmt.left) {
// Bring cursor position to left quartile
dx = rw/4 - cx;
} else if (cx > ped->rcFmt.right) {
// Bring cursor position to right quartile
dx = (3*rw)/4 - cx;
} else
dx = 0;
// Adjust visual position change to logical - relative to reading order
if (ped->fRtoLReading) {
dx = - dx;
}
// Avoid unnecessary leading or trailing whitespace
if (tw - ((INT)ped->xOffset - dx) < rw && tw > rw ) {
// No need to have white space at the end if there's enough text
ped->xOffset = (UINT)(tw-rw);
} else if ((INT)ped->xOffset < dx) {
// No need to have white space at beginning of line
ped->xOffset = 0;
} else {
// Move cursor directly to chosen quartile
ped->xOffset -= (UINT) dx;
}
TRACE(EDIT, ("HScroll format=%d, fWrap=%d, fRtoLReading=%d, right-left=%d, new xOffset %d",
ped->format, ped->fWrap, ped->fRtoLReading, ped->rcFmt.right - ped->rcFmt.left, ped->xOffset));
ScriptStringFree(&psa);
return ped->xOffset != uOldXOffset ? TRUE : FALSE;
}
//// IchToXY
//
// Converts a character position to the corresponding x coordinate
// offset from the left end of the text of the line.
int EditIchToXY(PED ped, HDC hdc, PSTR pText, ICH ichLength, ICH ichPos) {
INT iResult;
HRESULT hr;
STRING_ANALYSIS *psa;
if (ichLength == 0) {
return LeftEdgeX(ped, 0);
}
hr = EditStringAnalyse(hdc, ped, pText, ichLength, SSA_GLYPHS, 0, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditIchToXY - EditStringAnalyse"));
return LeftEdgeX(ped, 0);
}
MBCPtoWCCP(ped, pText, ichPos, &ichPos);
#ifdef CURSOR_ABSOLUTE_HOME_AND_END
if (ichPos <= 0) {
iResult = ped->fRtoLReading ? psa->size.cx : 0;
} else if (ichPos >= psa->cInChars) {
iResult = ped->fRtoLReading ? 0 : psa->size.cx;
} else {
iResult = ScriptStringCPtoX(psa, ichPos-1);
}
#else
if (ichPos <= 0) {
hr = ScriptStringCPtoX(psa, ichPos, FALSE, &iResult);
} else {
hr = ScriptStringCPtoX(psa, ichPos-1, TRUE, &iResult);
}
if (FAILED(hr)) {
ASSERTHR(hr, ("EditIchToXY - ScriptStringCPtoX"));
iResult = 0;
}
#endif
iResult += LeftEdgeX(ped, psa->size.cx);
ScriptStringFree(&psa);
return (int) iResult;
}
//// EditDrawText - draw one line for MLDrawText
//
// Draws the text at offset ichStart from pText length ichLength.
//
// entry pwText - points to beginning of line to display
// iMinSel, - range of characters to be highlighted. May
// iMaxSel be -ve or > cch.
//
// Uses ED structure fields as follows:
//
// rcFmt - drawing area
// xOffset - distance from leading margin to leading edge of text.
// (May be negative if the text is horizontally scrolled).
// RtoLReading - determines leading margin/edge.
// format - ES_LEFT - leading edge aligned (may be scrolled horizontally)
// - ES_CENTRE - centred between margins. (can't be scrolled horizontally)
// - ES_RIGHT - trailing margin aligned (can't be scrolled horizontally)
void EditDrawText(PED ped, HDC hdc, PSTR pText, INT iLength, INT iMinSel, INT iMaxSel, INT iY) {
INT iX; // x position to start display at
INT iWidth; // Width of line
RECT rc; // Locally updated copy of rectangle
int xFarOffset;
HRESULT hr;
STRING_ANALYSIS *psa;
// Establish where to display the line
rc = ped->rcFmt;
rc.top = iY;
rc.bottom = iY + ped->lineHeight;
xFarOffset = ped->xOffset + rc.right - rc.left;
// Include left or right margins in display unless clipped
// by horizontal scrolling.
if (ped->wLeftMargin) {
if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
&& ( (!ped->fRtoLReading && ped->xOffset > 0) // LTR and first char not fully in view
|| ( ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { //RTL and last char not fully in view
rc.left -= ped->wLeftMargin;
}
}
if (ped->wRightMargin) {
if (!( ped->format == ES_LEFT // Only ES_LEFT (Nearside alignment) can get clipped
&& ( ( ped->fRtoLReading && ped->xOffset > 0) // RTL and first char not fully in view
|| (!ped->fRtoLReading && xFarOffset < ped->maxPixelWidth)))) { // LTR and last char not fully in view
rc.right += ped->wRightMargin;
}
}
if (iMinSel < 0) iMinSel = 0;
if (iMaxSel > iLength) iMaxSel = iLength;
if (ped->fSingle) {
// The single line edit control always applies the background color
SetBkMode(hdc, OPAQUE);
}
if (iLength <= 0) {
if ((iMinSel < iMaxSel) || (GetBkMode(hdc) == OPAQUE)) {
// Empty line, just clear it on screen
ExtTextOutW(hdc, 0,iY, ETO_OPAQUE, &rc, NULL, 0, NULL);
}
return;
}
hr = EditStringAnalyse(hdc, ped, pText, iLength, SSA_GLYPHS, 0, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditDrawText - EditStringAnalyse"));
return;
}
MBCPtoWCCP(ped, pText, iMinSel, &iMinSel);
MBCPtoWCCP(ped, pText, iMaxSel, &iMaxSel);
iWidth = psa->size.cx;
iX = LeftEdgeX(ped, iWidth); // Visual x where left edge of string should be.
ScriptStringOut(
psa,
iX,
iY,
ETO_CLIPPED
| (GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0),
&rc,
iMinSel,
ped->fNoHideSel || ped->fFocus ? iMaxSel : iMinSel,
ped->fDisabled);
ScriptStringFree(&psa);
}
//// EditMouseToIch
//
// Returns the logical character offset corresponding to
// a specified x offset.
//
// entry iX - Window (visual) x position
ICH EditMouseToIch(PED ped, HDC hdc, PSTR pText, ICH ichCount, INT iX) {
ICH iCh;
BOOL fTrailing;
int iWidth;
HRESULT hr;
STRING_ANALYSIS *psa;
if (ichCount == 0) {
return 0;
}
hr = EditStringAnalyse(hdc, ped, pText, ichCount, SSA_GLYPHS, 0, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditMouseToIch - EditStringAnalyse"));
return 0;
}
iWidth = psa->size.cx;
// Take horizontal scroll position into consideration.
iX -= LeftEdgeX(ped, iWidth);
// If the user clicked beyond the edge of the string, treat it as a logical
// start or end of string request.
if (iX < 0) {
iCh = ped->fRtoLReading ? ichCount : 0;
} else if (iX > iWidth) {
TRACE(POSN, ("LpkEditMouseToIch iX beyond right edge: iX %d, psa->piOutVW %x, psa->nOutGlyphs %d, psa->piDx[psa->nOutGlyphs-1] %d",
iX, psa->piOutVW, psa->nOutGlyphs, iWidth));
iCh = ped->fRtoLReading ? 0 : ichCount;
} else {
// Otherwise it's in the string. Find the logical character whose centre is nearest.
ScriptStringXtoCP(psa, iX, &iCh, &fTrailing);
iCh += fTrailing; // Snap to nearest character edge
WCCPtoMBCP(ped, pText, iCh, &iCh);
}
ScriptStringFree(&psa);
TRACE(POSN, ("EditMouseToIch iX %d returns ch %d", iX, iCh));
return iCh;
}
//// EditGetLineWidth
//
// Returns width of line in pixels
INT EditGetLineWidth(PED ped, HDC hdc, PSTR pText, ICH cch) {
INT iResult;
HRESULT hr;
STRING_ANALYSIS *psa;
if (cch == 0) {
return 0;
}
if (cch > MAXLINELENGTH) {
cch = MAXLINELENGTH;
}
hr = EditStringAnalyse(hdc, ped, pText, cch, SSA_GLYPHS, 0, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditGetLineWidth - EditStringAnalyse"));
return 0;
}
iResult = psa->size.cx;
ScriptStringFree(&psa);
TRACE(EDIT, ("EditGetLineWidth width %d returns %d", cch, iResult))
return iResult;
}
//// EditCchInWidth
//
// Returns number of characters that will fit in width pixels.
ICH EditCchInWidth(PED ped, HDC hdc, PSTR pText, ICH cch, int width) {
ICH ichResult;
HRESULT hr;
STRING_ANALYSIS *psa;
if (cch > MAXLINELENGTH) {
cch = MAXLINELENGTH;
} else if (cch == 0) {
return 0;
}
hr = EditStringAnalyse(hdc, ped, pText, cch, SSA_GLYPHS | SSA_CLIP, width, &psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("EditCchInWidth - EditStringAnalyse"));
return 0;
}
ichResult = psa->cOutChars;
WCCPtoMBCP(ped, pText, ichResult, &ichResult);
ScriptStringFree(&psa);
TRACE(EDIT, ("EditCchInWidth width %d returns %d", width, ichResult))
return ichResult;
}
//// EditMoveSelection
//
// Returns nearest character position backward or forward from current position.
//
// Position is restricted according to language rules. For example, in Thai it is
// not possible to position the cursor between a base consonant and it's
// associated vowel or tone mark.
ICH EditMoveSelection(PED ped, HDC hdc, PSTR pText, ICH ich, BOOL fBackward) {
#define SP 0x20
#define TAB 0x09
#define CR 0x0D
#define LF 0x0A
#define EDWCH(ich) (ped->fAnsi ? (WCHAR)pText[ich] : ((PWSTR)pText)[ich])
#define EDWCBLANK(ich) ((BOOL) (EDWCH(ich) == SP || EDWCH(ich) == TAB))
#define EDWCCR(ich) ((BOOL) (EDWCH(ich) == CR))
#define EDWCLF(ich) ((BOOL) (EDWCH(ich) == LF))
#define EDSTARTWORD(ich) ( (ich == 0) \
|| ( ( EDWCBLANK(ich-1) \
|| EDWCLF(ich-1)) \
&& !EDWCBLANK(ich)) \
|| ( !EDWCCR(ich-1) \
&& EDWCCR(ich)))
ICH ichNonblankStart; // Leading character of nonblank run containing potential caret position
ICH ichNonblankLimit; // First character beyond nonblank run containing potential caret position
int iOffset; // Offset into nonblank run of ich measued in logical characters
STRING_ANALYSIS *psa;
HRESULT hr;
// Handle simple special cases:
// o At very beginning or end of buffer
// o When target position is blank or start or end of line
if (fBackward) {
if (ich <= 1) {
return 0;
}
ich--;
if (EDWCBLANK(ich)) {
return ich;
}
if (EDWCLF(ich)) {
while ( ich > 0
&& EDWCCR(ich-1)) {
ich--;
}
return ich;
}
} else {
if (ich >= ped->cch-1) {
return ped->cch;
}
ich++;
if (EDWCBLANK(ich)) {
return ich;
}
if (EDWCCR(ich-1)) {
// Moving forward from a CR.
if ( ich < ped->cch
&& EDWCCR(ich)) {
ich++;
}
if ( ich < ped->cch
&& EDWCLF(ich)) {
ich++;
}
return ich;
}
}
// Identify nonblank run containing target position
ichNonblankStart = ich;
ichNonblankLimit = ich+1;
// Move ichNonblankStart back to real start of blank delimited run
while ( ichNonblankStart > 0
&& !(EDSTARTWORD(ichNonblankStart))) {
ichNonblankStart--;
}
// Include one leading space if any
if ( ichNonblankStart > 0
&& EDWCBLANK(ichNonblankStart - 1)) {
ichNonblankStart--;
}
// Move ichNonblankLimit on to real end of blank delimited run
while ( ichNonblankLimit < ped->cch
&& !EDWCBLANK(ichNonblankLimit)
&& !EDWCCR(ichNonblankLimit)) {
ichNonblankLimit++;
}
// Obtain a break analysis of the identified nonblank run
hr = LpkStringAnalyse(
hdc,
pText + ichNonblankStart * ped->cbChar,
ichNonblankLimit - ichNonblankStart,
0,
GetEditAnsiConversionCharset(ped),
SSA_BREAK,
-1, 0,
NULL, NULL, NULL, NULL, NULL,
&psa);
if (SUCCEEDED(hr)) {
// Use the charstop flags in the logical attributes to correct ich
if (ich <= ichNonblankStart) {
iOffset = 0;
} else {
hr = MBCPtoWCCP(ped, pText+ichNonblankStart*ped->cbChar, ich-ichNonblankStart, &iOffset);
if (hr == E_FAIL) {
// ich was the second byte of a double byte character.
// In this case MBCPtoWCCP has returned the subsequent character
if (fBackward) {
iOffset--;
}
}
}
if (fBackward) {
while ( iOffset > 0
&& !psa->pLogAttr[iOffset].fCharStop) {
iOffset--;
}
} else {
while ( iOffset < psa->cInChars
&& !psa->pLogAttr[iOffset].fCharStop) {
iOffset++;
}
}
ScriptStringFree(&psa);
WCCPtoMBCP(ped, pText+ichNonblankStart*ped->cbChar, iOffset, &ich);
return ichNonblankStart + ich;
} else {
ASSERTHR(hr, ("EditMoveSelection - LpkStringAnalyse"));
// Analysis not possible - ignore content of complex scripts.
return ich;
}
}
void EditGetNextBoundaries(
PED ped,
HDC hdc,
PSTR pText,
ICH ichStart,
BOOL fLeft,
ICH *pichMin,
ICH *pichMax,
BOOL fWordStop)
{
ICH sd,ed; // Start and end of blank delimited run
ICH sc,ec; // Star and end of complex script word within sd,se
HRESULT hr;
STRING_ANALYSIS *psa;
// Identify left end of nearest delimited word (see diagram above)
sd = ichStart;
if (fLeft) {
// Going left
if (sd) {
sd--;
while (!(EDSTARTWORD(sd))) {
sd--;
}
}
} else {
// Going right
if (EDWCBLANK(sd)) {
// Move right to first character of word
if (sd < ped->cch) {
sd++;
while (sd < ped->cch && !EDSTARTWORD(sd)) {
sd++;
}
}
} else {
// Move left to first character of this word
while (!EDSTARTWORD(sd)) {
sd--;
}
}
}
// Position 'e' on first character of next word
ed = sd;
if (ed < ped->cch) {
ed++;
while (ed<ped->cch && !EDSTARTWORD(ed)) {
ed++;
}
}
// Obtain an analysis of the identified word
hr = LpkStringAnalyse(
hdc, pText + sd * ped->cbChar, ed - sd, 0,
GetEditAnsiConversionCharset(ped),
SSA_BREAK,
-1, 0,
NULL, NULL, NULL, NULL, NULL,
&psa);
if (SUCCEEDED(hr)) {
// Use the start of word (linebreak) flags in the logical attribute
// to narrow the word where appropriate to complex script handling
if (ichStart > sd) {
MBCPtoWCCP(ped, pText+sd*ped->cbChar, ichStart-sd, &sc);
} else {
sc = 0;
}
// Change ed from byte offset to codepoint index relative to sd
MBCPtoWCCP(ped, pText+sd*ped->cbChar, ed-sd, &ed);
if (fLeft && sc) // Going left
sc--;
if (fWordStop) {
while (sc && !psa->pLogAttr[sc].fSoftBreak)
sc--;
}
else {
while (sc && !psa->pLogAttr[sc].fCharStop)
sc--;
}
// Set ichMax to next stop
ec = sc;
if (ec < ed) {
ec++;
if (fWordStop) {
while (ec < ed && !psa->pLogAttr[ec].fSoftBreak)
ec++;
}
else {
while (ec < ed && !psa->pLogAttr[ec].fCharStop)
ec++;
}
}
WCCPtoMBCP(ped, pText+sd*ped->cbChar, sc, &sc);
WCCPtoMBCP(ped, pText+sd*ped->cbChar, ec, &ec);
if (pichMin) *pichMin = sd + sc;
if (pichMax) *pichMax = sd + ec;
ScriptStringFree(&psa);
} else {
ASSERTHR(hr, ("EditGetNextBoundaries - LpkStringAnalyse"));
// Analysis not possible - ignore content of complex scripts.
if (pichMin) *pichMin = sd;
if (pichMax) *pichMax = ed;
}
}
//// EditNextWord - find adjacent word start and end points
//
// Duplicates the behaviour of US notepad.
//
// First stage identify word range using standard
// blank/tab as delimiter.
//
// Second stage - analyse this run and use the logical
// attributes to narrow down on words identified by
// contextual processing in the complex script shaping
// engines.
//
//
// The following diagram describes the identification
// of the initial character of the nearest word:
//
// GOING LEFT:
//
// Words WWWW WWWW WWWW
// from any of xxxxxxxx
// to x
//
// (Notice that the result is always to the left of the initial
// position).
//
//
// GOING RIGHT:
//
// Words WWWW WWWW WWWW
// from any of xxxxxxxx
// to x
//
//
// Note that CRLF and CRCRLF are treated as words even if not
// delimited by blanks.
void EditNextWord(
PED ped,
HDC hdc,
PSTR pText,
ICH ichStart,
BOOL fLeft,
ICH *pichMin,
ICH *pichMax)
{
EditGetNextBoundaries(ped, hdc, pText, ichStart, fLeft, pichMin, pichMax, TRUE);
}
//// IsVietnameseSequenceValid
//
// Borrow this code from richedit. The logic was provided by Chau Vu.
//
// April 26, 1999 [wchao]
BOOL IsVietnameseSequenceValid (WCHAR ch1, WCHAR ch2)
{
#define IN_RANGE(n1, b, n2) ((unsigned)((b) - (n1)) <= (unsigned)((n2) - (n1)))
int i;
static const BYTE vowels[] = {0xF4, 0xEA, 0xE2, 'y', 'u', 'o', 'i', 'e', 'a'};
if (!IN_RANGE(0x300, ch2, 0x323) || // Fast out
!IN_RANGE(0x300, ch2, 0x301) && ch2 != 0x303 && ch2 != 0x309 && ch2 != 0x323)
{
return TRUE; // Not Vietnamese tone mark
}
for(i = sizeof(vowels) / sizeof(vowels[0]); i--;)
if((ch1 | 0x20) == vowels[i]) // Vietnamese tone mark follows
return TRUE; // vowel
return IN_RANGE(0x102, ch1, 0x103) || // A-breve, a-breve
IN_RANGE(0x1A0, ch1, 0x1A1) || // O-horn, o-horn
IN_RANGE(0x1AF, ch1, 0x1B0); // U-horn, u-horn
}
//// EditStringValidate
//
// Validate the string sequence from the insertion point onward.
// Return S_FALSE if any character beyond the insertion point produces fInvalid.
//
// April 5,1999 [wchao]
HRESULT EditStringValidate (STRING_ANALYSIS* psa, int ichInsert)
{
BOOL fVietnameseCheck = PRIMARYLANGID(THREAD_HKL()) == LANG_VIETNAMESE;
int iItem;
int i;
int l;
if (!psa->pLogAttr)
return E_INVALIDARG;
for (iItem = 0; iItem < psa->cItems; iItem++)
{
if (g_ppScriptProperties[psa->pItems[iItem].a.eScript]->fRejectInvalid)
{
i = psa->pItems[iItem].iCharPos;
l = psa->pItems[iItem + 1].iCharPos - i;
while (l)
{
if (i >= ichInsert && psa->pLogAttr[i].fInvalid)
return S_FALSE;
i++;
l--;
}
}
else if (fVietnameseCheck && g_ppScriptProperties[psa->pItems[iItem].a.eScript]->fCDM)
{
// Vietnamese specific sequence check
i = psa->pItems[iItem].iCharPos;
l = psa->pItems[iItem + 1].iCharPos - i;
while (l)
{
if (i > 0 && i >= ichInsert && !IsVietnameseSequenceValid(psa->pwInChars[i-1], psa->pwInChars[i]))
return S_FALSE;
i++;
l--;
}
}
}
return S_OK;
}
//// EditVerifyText
//
// Verify the sequence of input text at the insertion point by calling
// shaping engine that will return output flag pLogAttr->fInvalid in
// the invalid position of text. Return 0 if the insert text has invalid
// combination.
//
// Mar 31,1997 [wchao]
INT EditVerifyText (PED ped, HDC hdc, PSTR pText, ICH ichInsert, PSTR pInsertText, ICH cchInsert) {
ICH ichRunStart;
ICH ichRunEnd;
ICH ichLineStart;
ICH ichLineEnd;
ICH cchVerify;
PSTR pVerify;
INT iResult;
UINT cbChar;
BOOL fLocateChar;
HRESULT hr;
STRING_ANALYSIS *psa;
ASSERTS(cchInsert > 0 && pInsertText != NULL && pText != NULL, ("Invalid parameters!"));
if (cchInsert > 1)
// What we concern here is about how should we handle a series of characters
// forming invalid combination(s) that gets updated to the backing store as one
// operation (e.g. pasting). We chose to not handle it and the logic is that to
// -only- validate a given input char against the current state of the backing store.
//
// Notice that i use the value 1 so we're safe if inserted text is a DBCS character.
//
// [Dec 10, 98, wchao]
return TRUE;
if (ped->fSingle) {
ichLineStart = 0;
ichLineEnd = ped->cch;
} else {
ichLineStart = ped->chLines[ped->iCaretLine];
ichLineEnd = ped->iCaretLine == ped->cLines-1 ? ped->cch : ped->chLines[ped->iCaretLine+1];
}
ichRunEnd = ichRunStart = ichInsert; // insertion point
// Are we at the space?
fLocateChar = EDWCH(ichInsert) == SP ? TRUE : FALSE;
// Locate a valid char
while ( ichRunStart > ichLineStart && fLocateChar && EDWCH(ichRunStart) == SP ) {
ichRunStart--;
}
// Locate run starting point
// Find space
while (ichRunStart > ichLineStart && EDWCH(ichRunStart - 1) != SP) {
ichRunStart--;
}
// Cover leading spaces
while (ichRunStart > ichLineStart && EDWCH(ichRunStart - 1) == SP) {
ichRunStart--;
}
// Locate a valid char
while ( ichRunEnd < ichLineEnd && fLocateChar && EDWCH(ichRunEnd) == SP ) {
ichRunEnd++;
}
// Locate run ending point
while (ichRunEnd < ichLineEnd && EDWCH(ichRunEnd) != SP) {
ichRunEnd++;
}
ASSERTS(ichRunStart <= ichRunEnd, "Invalid run length!");
// Merge insert text with insertion point run.
cchVerify = ichRunEnd - ichRunStart + cchInsert;
cbChar = ped->cbChar;
pVerify = (PSTR) GlobalAlloc(GMEM_FIXED, cchVerify * cbChar);
if (pVerify) {
PSTR pv;
UINT cbCopy;
pv = pVerify;
cbCopy = (ichInsert - ichRunStart) * cbChar;
memcpy (pv, pText + ichRunStart * cbChar, cbCopy);
pv += cbCopy;
cbCopy = cchInsert * cbChar;
memcpy (pv, pInsertText, cbCopy);
pv += cbCopy;
cbCopy = (ichRunEnd - ichInsert) * cbChar;
memcpy (pv, pText + ichInsert * cbChar, cbCopy);
} else {
ASSERTS(pVerify, "EditVerifyText: Assertion failure: Could not allocate merge text buffer");
return 1; // can do nothing now just simply accept it.
}
psa = NULL;
iResult = TRUE; // assume verification pass.
// Do the real work.
// This will call to shaping engine and proceed item by item.
//
hr = LpkStringAnalyse(
hdc, pVerify, cchVerify, 0,
GetEditAnsiConversionCharset(ped),
SSA_BREAK
| (ped->charPasswordChar ? SSA_PASSWORD : 0)
| (ped->fRtoLReading ? SSA_RTL : 0),
-1, 0,
NULL, NULL, NULL, NULL, NULL,
&psa);
if (SUCCEEDED(hr)) {
MBCPtoWCCP(ped, pVerify, ichInsert - ichRunStart, &ichInsert);
hr = EditStringValidate(psa, ichInsert);
if (hr == S_FALSE) {
MessageBeep((UINT)-1);
iResult = FALSE;
} else if (FAILED(hr)) {
ASSERTHR(hr, ("EditVerifyText - EditStringValidate"));
}
ScriptStringFree(&psa);
} else {
ASSERTHR(hr, ("EditVerifyText - LpkStringAnalyse"));
}
GlobalFree((HGLOBAL) pVerify);
return iResult;
}
//// EditProcessMenu
//
// Process LPK context menu commands.
//
// April 18, 1997 [wchao]
//
INT EditProcessMenu (PED ped, UINT idMenuItem)
{
HWND hwnd;
INT iResult;
iResult = TRUE;
switch (idMenuItem) {
case ID_CNTX_RTL:
hwnd = ped->hwnd;
SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~ES_FMTMASK);
if (!ped->fRtoLReading) {
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE)
| (WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
}
else {
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE)
& ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR));
}
break;
case ID_CNTX_DISPLAYCTRL:
hwnd = ped->hwnd;
ped->fDisplayCtrl = !ped->fDisplayCtrl;
if (ped->fFlatBorder) {
RECT rcT;
int cxBorder, cyBorder;
GetClientRect(hwnd, &rcT);
cxBorder = GetSystemMetrics (SM_CXBORDER);
cyBorder = GetSystemMetrics (SM_CYBORDER);
InflateRect(&rcT, -cxBorder, -cyBorder);
InvalidateRect(hwnd, &rcT, TRUE);
}
else {
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case ID_CNTX_ZWNJ:
if (ped->fAnsi) {
SendMessageA(ped->hwnd, WM_CHAR, 0x9D, 0);
} else {
SendMessageW(ped->hwnd, WM_CHAR, U_ZWNJ, 0);
}
break;
case ID_CNTX_ZWJ:
if (ped->fAnsi) {
SendMessageA(ped->hwnd, WM_CHAR, 0x9E, 0);
} else {
SendMessageW(ped->hwnd, WM_CHAR, U_ZWJ, 0);
}
break;
case ID_CNTX_LRM:
if (ped->fAnsi) {
SendMessageA(ped->hwnd, WM_CHAR, 0xFD, 0);
} else {
SendMessageW(ped->hwnd, WM_CHAR, U_LRM, 0);
}
break;
case ID_CNTX_RLM:
if (ped->fAnsi) {
SendMessageA(ped->hwnd, WM_CHAR, 0xFE, 0);
} else {
SendMessageW(ped->hwnd, WM_CHAR, U_RLM, 0);
}
break;
case ID_CNTX_LRE: SendMessageW(ped->hwnd, WM_CHAR, U_LRE, 0); break;
case ID_CNTX_RLE: SendMessageW(ped->hwnd, WM_CHAR, U_RLE, 0); break;
case ID_CNTX_LRO: SendMessageW(ped->hwnd, WM_CHAR, U_LRO, 0); break;
case ID_CNTX_RLO: SendMessageW(ped->hwnd, WM_CHAR, U_RLO, 0); break;
case ID_CNTX_PDF: SendMessageW(ped->hwnd, WM_CHAR, U_PDF, 0); break;
case ID_CNTX_NADS: SendMessageW(ped->hwnd, WM_CHAR, U_NADS, 0); break;
case ID_CNTX_NODS: SendMessageW(ped->hwnd, WM_CHAR, U_NODS, 0); break;
case ID_CNTX_ASS: SendMessageW(ped->hwnd, WM_CHAR, U_ASS, 0); break;
case ID_CNTX_ISS: SendMessageW(ped->hwnd, WM_CHAR, U_ISS, 0); break;
case ID_CNTX_AAFS: SendMessageW(ped->hwnd, WM_CHAR, U_AAFS, 0); break;
case ID_CNTX_IAFS: SendMessageW(ped->hwnd, WM_CHAR, U_IAFS, 0); break;
case ID_CNTX_RS: SendMessageW(ped->hwnd, WM_CHAR, U_RS, 0); break;
case ID_CNTX_US: SendMessageW(ped->hwnd, WM_CHAR, U_US, 0); break;
}
return iResult;
}
//// EditSetMenu - Set menu state
//
//
void EditSetMenu(PED ped, HMENU hMenu) {
EnableMenuItem(hMenu, ID_CNTX_RTL, MF_BYCOMMAND | MF_ENABLED);
CheckMenuItem (hMenu, ID_CNTX_RTL, MF_BYCOMMAND | (ped->fRtoLReading ? MF_CHECKED : MF_UNCHECKED));
if (!ped->fAnsi || ped->charSet == ARABIC_CHARSET || ped->charSet == HEBREW_CHARSET) {
// It's unicode, Arabic or Hebrew - we can display and enter at least some control characters
EnableMenuItem(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | MF_ENABLED);
CheckMenuItem (hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | (ped->fDisplayCtrl ? MF_CHECKED : MF_UNCHECKED));
EnableMenuItem(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_LRM, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_RLM, MF_BYCOMMAND | MF_ENABLED);
if (!ped->fAnsi || ped->charSet == ARABIC_CHARSET) {
// Controls characters in Unicode and ANSI Arabic only
EnableMenuItem(hMenu, ID_CNTX_ZWJ, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_ZWNJ, MF_BYCOMMAND | MF_ENABLED);
}
if (!ped->fAnsi) {
// These control characters are specific to the Unicode bidi algorithm
EnableMenuItem(hMenu, ID_CNTX_LRE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_RLE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_LRO, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_RLO, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_PDF, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_NADS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_NODS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_ASS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_ISS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_AAFS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_IAFS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_RS, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, ID_CNTX_US, MF_BYCOMMAND | MF_ENABLED);
}
} else {
// No opportunity to enter control characters
EnableMenuItem(hMenu, ID_CNTX_INSERTCTRL, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, ID_CNTX_DISPLAYCTRL, MF_BYCOMMAND | MF_GRAYED);
}
}
//// EditCreateCaretFromFont
//
// Create one of the special caret shapes for complex script languages.
//
// returns FALSE if it couldn't create the caret for example in low
// memory situations.
#define CURSOR_USA 0xffff
#define CURSOR_LTR 0xf00c
#define CURSOR_RTL 0xf00d
#define CURSOR_THAI 0xf00e
BOOL EditCreateCaretFromFont(
PED ped,
HDC hdc,
INT nWidth,
INT nHeight,
WCHAR wcCursorCode
)
{
BOOL fResult = FALSE; // Assume the worst
HBITMAP hbmBits;
HDC hcdcBits;
HFONT hArrowFont;
ABC abcWidth;
COLORREF clrBk;
WORD gidArrow;
UINT uiWidthBits;
HBITMAP hOldBitmap;
// Create caret from the Arial font
hcdcBits = CreateCompatibleDC (hdc);
if (!hcdcBits)
{
return FALSE;
}
// create Arrow font then select into compatible DC
// Bitmap will be XORed with the background color before caret starts blinking.
// Therefore, we need to set our bitmap background to be opposite with the DC
// actual background in order to generate caret properly.
clrBk = GetBkColor(hdc);
SetBkColor (hcdcBits, ~clrBk);
// Creating the caret with white pattern to be consistant with User-edit field.
clrBk = RGB(255, 255 , 255); // White
SetTextColor (hcdcBits, clrBk);
hArrowFont = CreateFontW ( nHeight, 0, 0, 0, nWidth > 1 ? 700 : 400, 0L, 0L, 0L, 1L,
0L, 0L, 0L, 0L, L"Microsoft Sans Serif" );
if (!hArrowFont)
{
goto error;
}
SelectObject (hcdcBits, hArrowFont);
// textout the Arrow char to get its bitmap
if (!GetCharABCWidthsW (hcdcBits, wcCursorCode, wcCursorCode, &abcWidth))
{
goto error;
}
if (!GetGlyphIndicesW (hcdcBits, &wcCursorCode, 1, &gidArrow, 0))
{
goto error;
}
uiWidthBits = (((abcWidth.abcB)+15)/16)*16; // bitmap width must be WORD-aligned
hbmBits = CreateCompatibleBitmap (hdc, uiWidthBits, nHeight);
if (!hbmBits)
{
goto error;
}
hOldBitmap = SelectObject(hcdcBits, hbmBits);
if (!ExtTextOutW (hcdcBits, -abcWidth.abcA, 0, ETO_OPAQUE | ETO_GLYPH_INDEX, NULL, &gidArrow, 1, NULL))
{
DeleteObject(SelectObject(hcdcBits, hOldBitmap));
goto error;
}
// free current caret bitmap handle if we have one
if (ped->hCaretBitmap) {
DeleteObject (ped->hCaretBitmap);
}
ped->hCaretBitmap = hbmBits;
if (wcCursorCode == CURSOR_RTL) {
// RTL cursor has vertical stroke on right hand side. Overlap LTR and RTL
// positions by one pixel so cursor doesn't go outside the edit control
// at small sizes.
ped->iCaretOffset = 1 - (int) abcWidth.abcB; // (Allow one pixel overlap between ltr & rtl)
} else {
ped->iCaretOffset = 0;
}
fResult = CreateCaret (ped->hwnd, hbmBits, 0, 0);
error:
// release allocated objects
if (hArrowFont)
{
DeleteObject(hArrowFont);
}
if (hcdcBits)
{
DeleteDC(hcdcBits);
}
return fResult;
}
//// EditCreateCaret
//
// Create locale specific caret shape
//
// April 25, 1997 [wchao]
// May 1st, 1997 [samera] Added traditional BiDi cursor under #ifdef
// Aug 15th, 2000 [dbrown] Fix prefix bug 43057 - no handling for low memory
//
// Note
// Complex script carets are mapped in the private area of Unicode in the font.
// LTR cursor 0xf00c
// RTL cursor 0xf00d
// Thai cursor 0xf00e
//
#define LANG_ID(x) ((DWORD)(DWORD_PTR)x & 0x000003ff);
INT EditCreateCaret(
PED ped,
HDC hdc,
INT nWidth,
INT nHeight,
UINT hklCurrent) {
UINT uikl;
ULONG ulCsrCacheCount;
WCHAR wcCursorCode;
ped->iCaretOffset = 0;
if (!hklCurrent) {
uikl = LANG_ID(GetKeyboardLayout(0L));
} else {
uikl = LANG_ID(hklCurrent);
}
// Choose caret shape - use either the standard US caret, or a
// special shape from the Arial font.
wcCursorCode = CURSOR_USA;
switch (uikl) {
case LANG_THAI: wcCursorCode = CURSOR_THAI; break;
//
// we may need to call GetLocaleInfo( FONT_SIGNATURE ...) to
// properly detect RTL languages.
//
case LANG_ARABIC:
case LANG_FARSI:
case LANG_URDU:
case LANG_HEBREW: wcCursorCode = CURSOR_RTL; break;
default:
// Make sure the NLS settings are cached before checking on g_UserBidiLocale. it happens!
if ( g_ulNlsUpdateCacheCount==-1
&& (ulCsrCacheCount = NlsGetCacheUpdateCount()) != g_ulNlsUpdateCacheCount) {
TRACE(NLS, ("LPK : Updating NLS cache from EditCreateCaret, lpkNlsCacheCount=%ld, CsrssCacheCount=%ld",
g_ulNlsUpdateCacheCount ,ulCsrCacheCount));
g_ulNlsUpdateCacheCount = ulCsrCacheCount;
// Update the cache now
ReadNLSScriptSettings();
}
if (g_UserBidiLocale) {
// Other keyboards have a left-to-right pointing caret
// in Bidi locales.
wcCursorCode = CURSOR_LTR;
}
}
if (wcCursorCode != CURSOR_USA)
{
// Try to create a caret from the Arial font
if (!EditCreateCaretFromFont(ped, hdc, nWidth, nHeight, wcCursorCode))
{
// Caret from font failed - low memory perhaps
wcCursorCode = CURSOR_USA; // Fall back to USA cursor
}
}
if (wcCursorCode == CURSOR_USA) {
// Use Windows default caret
return CreateCaret (ped->hwnd, NULL, nWidth, nHeight);
} else {
return TRUE;
}
}
//// EditAdjustCaret
//
// Adjust caret after insertion/deletion to avoid the caret in between a cluster,
// very common in Indic e.g.
//
// 1. Deleting the space "X| Y" and it becomes "X|y".
// 2. Inserting 'X' at "|Y" and it becomes "X|y".
//
// May 3,1999 [wchao]
//
INT EditAdjustCaret (
PED ped,
HDC hdc,
PSTR pText,
ICH ich)
{
#if 0
//
// Indiannt unanimously request this feature to be removed for final product.
// (wchao - 7/12/99)
//
ICH ichMin;
ICH ichMax;
if (ich < ped->cch)
{
EditGetNextBoundaries(ped, hdc, pText, ich, FALSE, &ichMin, &ichMax, FALSE);
if (ich > ichMin)
ich = ichMax;
}
#endif
UNREFERENCED_PARAMETER(ped);
UNREFERENCED_PARAMETER(hdc);
UNREFERENCED_PARAMETER(pText);
return ich;
}
//// Edit callout
//
// The LPK edit support functions are acessed by the edit
// control code through a struct defined in user.h.
//
// Here we initialise that struct with the addresses
// of the callout functions.
LPKEDITCALLOUT LpkEditControl = {
EditCreate,
EditIchToXY,
EditMouseToIch,
EditCchInWidth,
EditGetLineWidth,
EditDrawText,
EditHScroll,
EditMoveSelection,
EditVerifyText,
EditNextWord,
EditSetMenu,
EditProcessMenu,
EditCreateCaret,
EditAdjustCaret
};