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.
814 lines
26 KiB
814 lines
26 KiB
//*************************************************
|
|
// r e u t i l . c p p
|
|
//
|
|
// Purpose:
|
|
// implements calls to Richedit controls
|
|
//
|
|
// Owner:
|
|
// dhaws
|
|
//
|
|
// History:
|
|
// March 1999: Created
|
|
//*************************************************
|
|
|
|
#include "pch.hxx"
|
|
#include "reutil.h"
|
|
#include "richole.h"
|
|
#include "richedit.h"
|
|
#include "textserv.h"
|
|
#include "shlwapip.h"
|
|
#include "mirror.h"
|
|
#include "strconst.h"
|
|
#include <demand.h> // must be last!
|
|
|
|
static HINSTANCE g_hRichEditDll = NULL;
|
|
static DWORD g_dwRicheditVer = 0;
|
|
|
|
// @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
|
|
static BOOL g_fSpecialRTLRichedit = FALSE;
|
|
|
|
// Keep these around for future reference
|
|
static const CHAR g_szRE10[] = "RichEdit";
|
|
static const CHAR g_szRE20[] = "RichEdit20A";
|
|
static const WCHAR g_wszRE10[] = L"RichEdit";
|
|
static const WCHAR g_wszRE20[] = L"RichEdit20W";
|
|
|
|
BOOL FInitRichEdit(BOOL fInit)
|
|
{
|
|
if(fInit)
|
|
{
|
|
if(!g_hRichEditDll)
|
|
{
|
|
g_hRichEditDll = LoadLibrary("RICHED20.DLL");
|
|
if (g_hRichEditDll)
|
|
{
|
|
TCHAR szPath[MAX_PATH],
|
|
szGet[MAX_PATH];
|
|
BOOL fSucceeded = FALSE;
|
|
DWORD dwVerInfoSize = 0,
|
|
dwVerHnd = 0,
|
|
dwProdNum = 0,
|
|
dwMajNum = 0,
|
|
dwMinNum = 0;
|
|
LPSTR lpInfo = NULL,
|
|
lpVersion = NULL;
|
|
LPWORD lpwTrans;
|
|
UINT uLen;
|
|
HKEY hkey;
|
|
DWORD dw=0,
|
|
cb;
|
|
|
|
if (GetModuleFileName(g_hRichEditDll, szPath, ARRAYSIZE(szPath)))
|
|
{
|
|
dwVerInfoSize = GetFileVersionInfoSize(szPath, &dwVerHnd);
|
|
if (dwVerInfoSize)
|
|
{
|
|
lpInfo = (LPSTR)GlobalAlloc(GPTR, dwVerInfoSize);
|
|
if (lpInfo)
|
|
{
|
|
if (GetFileVersionInfo(szPath, dwVerHnd, dwVerInfoSize, lpInfo))
|
|
{
|
|
if (VerQueryValue(lpInfo, "\\VarFileInfo\\Translation", (LPVOID *)&lpwTrans, &uLen) &&
|
|
uLen >= (2 * sizeof(WORD)))
|
|
{
|
|
// set up buffer for calls to VerQueryValue()
|
|
wnsprintf(szGet, ARRAYSIZE(szGet), "\\StringFileInfo\\%04X%04X\\FileVersion", lpwTrans[0], lpwTrans[1]);
|
|
if (VerQueryValue(lpInfo, szGet, (LPVOID *)&lpVersion, &uLen) && uLen)
|
|
{
|
|
while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
|
|
{
|
|
dwProdNum *= 10;
|
|
dwProdNum += (*lpVersion++ - '0');
|
|
}
|
|
|
|
if (5 == dwProdNum)
|
|
{
|
|
if (('.' == *lpVersion) || (',' == *lpVersion))
|
|
lpVersion++;
|
|
while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
|
|
{
|
|
dwMajNum *= 10;
|
|
dwMajNum += (*lpVersion++ - '0');
|
|
}
|
|
g_dwRicheditVer = (dwMajNum >= 30) ? 3 : 2;
|
|
|
|
// @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
|
|
if ((2 == g_dwRicheditVer) && (0 != *lpVersion))
|
|
{
|
|
// Check to see if we have turned on the magic key to disable this stuff
|
|
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegRoot, 0, KEY_QUERY_VALUE, &hkey))
|
|
{
|
|
cb = sizeof(dw);
|
|
RegQueryValueEx(hkey, c_szRegRTLRichEditHACK, 0, NULL, (LPBYTE)&dw, &cb);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
// If we didn't find the key, or it is 0, then check to see if we are
|
|
// dealing with those special richedits
|
|
if (0 == dw)
|
|
{
|
|
if (('.' == *lpVersion) || (',' == *lpVersion))
|
|
lpVersion++;
|
|
while (('.' != *lpVersion) && (',' != *lpVersion) && (0 != *lpVersion))
|
|
{
|
|
dwMinNum *= 10;
|
|
dwMinNum += (*lpVersion++ - '0');
|
|
}
|
|
g_fSpecialRTLRichedit = (dwMinNum >= 330);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Treat this as richedit version 3.0
|
|
Assert(5 < dwProdNum);
|
|
g_dwRicheditVer = 3;
|
|
}
|
|
fSucceeded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
GlobalFree((HGLOBAL)lpInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
FreeLibrary(g_hRichEditDll);
|
|
g_hRichEditDll=NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!g_hRichEditDll)
|
|
{
|
|
g_hRichEditDll=LoadLibrary("RICHED32.DLL");
|
|
g_dwRicheditVer = 1;
|
|
}
|
|
|
|
if(!g_hRichEditDll)
|
|
AthMessageBoxW(g_hwndInit, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsErrLoadingHtmlEdit), NULL, MB_OK);
|
|
|
|
return (BOOL)(0 != g_hRichEditDll);
|
|
}
|
|
else
|
|
{
|
|
if(g_hRichEditDll)
|
|
FreeLibrary(g_hRichEditDll);
|
|
g_hRichEditDll=NULL;
|
|
g_dwRicheditVer = 0;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
LPCSTR GetREClassStringA(void)
|
|
{
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
case 1:
|
|
return g_szRE10;
|
|
|
|
case 2:
|
|
case 3:
|
|
return g_szRE20;
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
LPCWSTR GetREClassStringW(void)
|
|
{
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
case 1:
|
|
return g_wszRE10;
|
|
|
|
case 2:
|
|
case 3:
|
|
return g_wszRE20;
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
typedef struct tagHWNDORDERSTRUCT {
|
|
HWND hwndTemplate;
|
|
HWND hwndRichEdit;
|
|
HWND *phwnd;
|
|
} HWNDORDERSTRUCT;
|
|
|
|
BOOL CALLBACK CountChildrenProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
DWORD *pcCount = (DWORD*)lParam;
|
|
(*pcCount)++;
|
|
return TRUE;
|
|
}
|
|
|
|
// This function basically will take the order of the hwnds on the dialog
|
|
// and store that order in the prder->phwnd variable so that we can maintain
|
|
// the taborder on the dialog windows. The only special case we have to do
|
|
// is with the template. In the case of the template, we need to substitute
|
|
// in the richedit into the order.
|
|
BOOL CALLBACK BuildOrderProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
HWNDORDERSTRUCT *pOrder = (HWNDORDERSTRUCT*)lParam;
|
|
|
|
if (hwnd == pOrder->hwndTemplate)
|
|
*pOrder->phwnd = pOrder->hwndRichEdit;
|
|
else
|
|
*pOrder->phwnd = hwnd;
|
|
|
|
pOrder->phwnd++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HWND CreateREInDialogA(HWND hwndParent, int iID)
|
|
{
|
|
HWND hwndTemplate = GetDlgItem(hwndParent, idredtTemplate);
|
|
RECT rcTemplate;
|
|
int width,
|
|
height;
|
|
HWND hwndNewRE = 0;
|
|
DWORD cCount = 0;
|
|
HWNDORDERSTRUCT hos;
|
|
|
|
CHAR szTitle[CCHMAX_STRINGRES];
|
|
|
|
Assert(IsWindow(hwndParent));
|
|
Assert(iID);
|
|
AssertSz(IsWindow(hwndTemplate), "Must have a template control for this to work.");
|
|
|
|
*szTitle = 0;
|
|
ShowWindow(hwndTemplate, SW_HIDE);
|
|
GetWindowText(hwndTemplate, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
// Get location of place holder so we can create the richedit over it
|
|
GetWindowRect(hwndTemplate, &rcTemplate);
|
|
|
|
// Map Screen coordinates to dialog coordinates
|
|
MapWindowPoints(NULL, hwndParent, (LPPOINT)&rcTemplate, 2);
|
|
|
|
width = rcTemplate.right - rcTemplate.left;
|
|
height = rcTemplate.bottom - rcTemplate.top;
|
|
|
|
hwndNewRE = CreateWindow(GetREClassStringA(),
|
|
szTitle,
|
|
ES_MULTILINE|ES_READONLY|ES_SAVESEL|ES_AUTOVSCROLL|
|
|
WS_BORDER|WS_VSCROLL|WS_TABSTOP|WS_CHILD,
|
|
rcTemplate.left, rcTemplate.top, width, height,
|
|
hwndParent,
|
|
(HMENU)IntToPtr(iID),
|
|
g_hInst, NULL);
|
|
|
|
// Count number of child windows in the dialog
|
|
if (EnumChildWindows(hwndParent, CountChildrenProc, (LPARAM)&cCount))
|
|
{
|
|
HWND *phwnd = (HWND*)ZeroAllocate(cCount*sizeof(HWND));
|
|
|
|
if (phwnd)
|
|
{
|
|
hos.hwndRichEdit = hwndNewRE;
|
|
hos.hwndTemplate = hwndTemplate;
|
|
hos.phwnd = phwnd;
|
|
|
|
// Create ordered list of dialog children hwnds.
|
|
if (EnumChildWindows(hwndParent, BuildOrderProc, (LPARAM)&hos))
|
|
{
|
|
cCount--;
|
|
|
|
// So this next section is basically going to reparent all the
|
|
// controls in the order setup in the phwnd array. By setting
|
|
// the parent of the window, we basically set the taborder of that
|
|
// item to be the first in line. That is why we reparent the controls
|
|
// in reverse order.
|
|
HWND *pCurr = &phwnd[cCount];
|
|
while(cCount)
|
|
{
|
|
SetParent(*pCurr, SetParent(*pCurr, NULL));
|
|
pCurr--;
|
|
cCount--;
|
|
}
|
|
}
|
|
MemFree(phwnd);
|
|
}
|
|
}
|
|
|
|
return hwndNewRE;
|
|
}
|
|
|
|
LONG RichEditNormalizeCharPos(HWND hwnd, LONG lByte, LPCSTR pszText)
|
|
{
|
|
LPWSTR pwszText = NULL;
|
|
LPSTR pszConv = NULL;
|
|
LONG cch;
|
|
HRESULT hr = S_OK;
|
|
|
|
//on anything other than richedit 1, we're already normalized
|
|
if(1!=g_dwRicheditVer) return lByte;
|
|
|
|
if(NULL == pszText)
|
|
{
|
|
cch = GetWindowTextLengthWrapW(hwnd);
|
|
|
|
if (0 == cch)
|
|
return 0;
|
|
|
|
cch += 1; // for a null
|
|
if (lByte >= cch)
|
|
return cch;
|
|
|
|
IF_NULLEXIT(MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)));
|
|
*pwszText = '\0';
|
|
|
|
GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
|
|
IF_NULLEXIT((pszConv = PszToANSI(CP_ACP, pwszText)));
|
|
pszText = pszConv;
|
|
}
|
|
|
|
cch = 0;
|
|
while(lByte > 0) // lByte is zero-based
|
|
{
|
|
cch++;
|
|
|
|
if (IsDBCSLeadByte(*pszText)){
|
|
pszText++;
|
|
lByte--;
|
|
}
|
|
|
|
pszText++;
|
|
lByte--;
|
|
}
|
|
|
|
exit:
|
|
MemFree(pwszText);
|
|
MemFree(pszConv);
|
|
return FAILED(hr)?0:cch;
|
|
}
|
|
|
|
LONG GetRichEditTextLen(HWND hwnd)
|
|
{
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
// we always want to return the number of chars; riched 1 will always return
|
|
// the number of bytes; we need to convert...
|
|
case 1:
|
|
{
|
|
LPWSTR pwszText = NULL;
|
|
DWORD cch;
|
|
|
|
cch = GetWindowTextLengthWrapW(hwnd);
|
|
if (0 == cch)
|
|
return 0;
|
|
|
|
cch += 1; // for a null
|
|
if (!MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)))
|
|
return 0;
|
|
*pwszText = '\0';
|
|
|
|
GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
|
|
|
|
cch = lstrlenW(pwszText);
|
|
MemFree(pwszText);
|
|
|
|
return cch;
|
|
}
|
|
|
|
case 2:
|
|
case 3:
|
|
{
|
|
GETTEXTLENGTHEX rTxtStruct;
|
|
|
|
if (!hwnd)
|
|
return 0;
|
|
|
|
rTxtStruct.flags = GTL_DEFAULT;
|
|
rTxtStruct.codepage = CP_UNICODE;
|
|
|
|
return (LONG) SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)&rTxtStruct, 0);
|
|
}
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DWORD GetRichEditText(HWND hwnd, LPWSTR pwchBuff, DWORD cchNumChars, BOOL fSelection, ITextDocument *pDoc)
|
|
{
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
case 1:
|
|
{
|
|
if (fSelection)
|
|
return (DWORD)SendMessageWrapW(hwnd, EM_GETSELTEXT, 0, (LPARAM)pwchBuff);
|
|
else
|
|
return GetWindowTextWrapW(hwnd, pwchBuff, cchNumChars);
|
|
}
|
|
|
|
case 2:
|
|
case 3:
|
|
{
|
|
DWORD cchLen = 0;
|
|
if (!hwnd || !cchNumChars)
|
|
return 0;
|
|
|
|
Assert(pwchBuff);
|
|
|
|
*pwchBuff = 0;
|
|
|
|
if (!fSelection)
|
|
{
|
|
GETTEXTEX rTxtStruct;
|
|
|
|
rTxtStruct.cb = cchNumChars * sizeof(WCHAR);
|
|
rTxtStruct.flags = GT_DEFAULT;
|
|
rTxtStruct.codepage = CP_UNICODE;
|
|
|
|
cchLen = (DWORD) SendMessage(hwnd, EM_GETTEXTEX, (WPARAM)&rTxtStruct, (LPARAM)pwchBuff);
|
|
}
|
|
else
|
|
{
|
|
// There is no way to get a selection of UNICODE
|
|
// without going through TOM
|
|
ITextSelection *pSelRange = NULL;
|
|
ITextDocument *pDocToFree = NULL;
|
|
BSTR bstr = NULL;
|
|
|
|
if (!pDoc)
|
|
{
|
|
LPRICHEDITOLE preole = NULL;
|
|
LRESULT result = SendMessage(hwnd, EM_GETOLEINTERFACE, NULL, (LPARAM)&preole);
|
|
Assert(result);
|
|
Assert(preole);
|
|
|
|
HRESULT hr = preole->QueryInterface(IID_ITextDocument, (LPVOID*)&pDocToFree);
|
|
ReleaseObj(preole);
|
|
TraceResult(hr);
|
|
if (FAILED(hr))
|
|
return 0;
|
|
|
|
pDoc = pDocToFree;
|
|
|
|
}
|
|
|
|
if (SUCCEEDED(pDoc->GetSelection(&pSelRange)))
|
|
{
|
|
if (SUCCEEDED(pSelRange->GetText(&bstr)) && bstr)
|
|
{
|
|
cchLen = SysStringLen(bstr);
|
|
if (cchLen > 0)
|
|
{
|
|
if (cchLen < cchNumChars)
|
|
StrCpyNW(pwchBuff, bstr, cchNumChars);
|
|
else
|
|
cchLen = 0;
|
|
|
|
}
|
|
SysFreeString(bstr);
|
|
}
|
|
ReleaseObj(pSelRange);
|
|
}
|
|
ReleaseObj(pDocToFree);
|
|
}
|
|
return cchLen;
|
|
}
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void SetRichEditText(HWND hwnd, LPWSTR pwchBuff, BOOL fReplaceSel, ITextDocument *pDoc, BOOL fReadOnly)
|
|
{
|
|
if (!hwnd)
|
|
return;
|
|
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
case 1:
|
|
{
|
|
if (fReplaceSel)
|
|
SendMessageWrapW(hwnd, EM_REPLACESEL, 0, (LPARAM)pwchBuff);
|
|
else
|
|
SetWindowTextWrapW(hwnd, pwchBuff);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
ITextServices *pService = NULL;
|
|
ITextDocument *pDocToFree = NULL;
|
|
|
|
if (!pDoc)
|
|
{
|
|
LPRICHEDITOLE preole = NULL;
|
|
LRESULT result = SendMessage(hwnd, EM_GETOLEINTERFACE, NULL, (LPARAM)&preole);
|
|
Assert(result);
|
|
Assert(preole);
|
|
|
|
HRESULT hr = preole->QueryInterface(IID_ITextDocument, (LPVOID*)&pDocToFree);
|
|
ReleaseObj(preole);
|
|
TraceResult(hr);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
pDoc = pDocToFree;
|
|
|
|
}
|
|
|
|
if (FAILED(pDoc->QueryInterface(IID_ITextServices, (LPVOID*)&pService)))
|
|
return;
|
|
|
|
if (!fReplaceSel)
|
|
{
|
|
// TxSetText is documented as a LPCTSTR, but RichEdit always
|
|
// compiles with UNICODE turned on, so we are OK here.
|
|
pService->TxSetText(pwchBuff);
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr;
|
|
ITextSelection *pSelRange = NULL;
|
|
BSTR bstr = SysAllocString(pwchBuff);
|
|
|
|
hr = pDoc->GetSelection(&pSelRange);
|
|
|
|
if (SUCCEEDED(hr) && pSelRange)
|
|
{
|
|
if (fReadOnly)
|
|
pService->OnTxPropertyBitsChange(TXTBIT_READONLY, 0);
|
|
|
|
// If we are readonly, SetText will fail if we don't do the TXTBIT setting
|
|
hr = pSelRange->SetText(bstr);
|
|
if (FAILED(hr))
|
|
TraceResult(hr);
|
|
|
|
// Richedit 2.0 doesn't always collapse to the end of the selection.
|
|
pSelRange->Collapse(tomEnd);
|
|
|
|
if (fReadOnly)
|
|
pService->OnTxPropertyBitsChange(TXTBIT_READONLY, TXTBIT_READONLY);
|
|
|
|
pSelRange->Release();
|
|
}
|
|
else
|
|
TraceResult(hr);
|
|
|
|
SysFreeString(bstr);
|
|
}
|
|
ReleaseObj(pDocToFree);
|
|
pService->Release();
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
// pwchBuff can be NULL. NULL means that we are clearing the field.
|
|
SETTEXTEX rTxtStruct;
|
|
|
|
rTxtStruct.flags = fReplaceSel ? ST_SELECTION : ST_DEFAULT;
|
|
rTxtStruct.codepage = CP_UNICODE;
|
|
|
|
// EM_SETTEXTEX will fail with RichEdit 2.0.
|
|
SendMessage(hwnd, EM_SETTEXTEX, (WPARAM)&rTxtStruct, (LPARAM)pwchBuff);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SetFontOnRichEdit(HWND hwnd, HFONT hfont)
|
|
{
|
|
switch (g_dwRicheditVer)
|
|
{
|
|
case 1:
|
|
case 3:
|
|
{
|
|
CHARFORMAT cf;
|
|
if (SUCCEEDED(FontToCharformat(hfont, &cf)))
|
|
{
|
|
SideAssert(FALSE != SendMessage(hwnd, EM_SETCHARFORMAT, (WPARAM) 0, (LPARAM) &cf));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
SideAssert(FALSE != SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)));
|
|
break;
|
|
|
|
default:
|
|
AssertSz(FALSE, "Bad Richedit Version");
|
|
break;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// RichEditExSetSel
|
|
//
|
|
// Raid 79304. Rich Edit Control version 1.0 does not handle
|
|
// the EM_EXSETSEL message correctly for text with DBCS characters. It sets
|
|
// the selection range based on bytes on not double byte characters.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LRESULT RichEditExSetSel(HWND hwnd, CHARRANGE *pchrg)
|
|
{
|
|
// We only need to deal with this on richedit 1 or we aren't selecting entire text
|
|
Assert(pchrg);
|
|
|
|
if ((1 == g_dwRicheditVer) && (-1 != pchrg->cpMax))
|
|
{
|
|
LRESULT lResult = 0;
|
|
LPWSTR pwszText = NULL;
|
|
LPSTR pszConv = NULL, pszText = NULL;
|
|
CHARRANGE chrg={0};
|
|
CHARRANGE chrgIn;
|
|
DWORD cch, cDBCSBefore = 0, cDBCSIn = 0;
|
|
|
|
chrgIn = *pchrg;
|
|
|
|
// Get the text string for this control
|
|
cch = GetRichEditTextLen(hwnd) + 1;
|
|
if (0 == cch)
|
|
return lResult;
|
|
if (!MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)))
|
|
return lResult;
|
|
*pwszText = '\0';
|
|
|
|
// Richedit 1.0 will never have a pDoc, so don't pass one in
|
|
GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
|
|
pszConv = PszToANSI(CP_ACP, pwszText);
|
|
pszText = pszConv;
|
|
SafeMemFree(pwszText);
|
|
if (!pszText)
|
|
{
|
|
Assert(0);
|
|
return lResult;
|
|
}
|
|
|
|
// Compute the BYTE range based on DBCS characters
|
|
if (chrgIn.cpMax >= chrgIn.cpMin)
|
|
{
|
|
chrgIn.cpMax -= chrgIn.cpMin;
|
|
|
|
// Look for DBCS chars before selection
|
|
while ((chrgIn.cpMin > 0) && *pszText)
|
|
{
|
|
if ( IsDBCSLeadByte(*pszText) )
|
|
{
|
|
cDBCSBefore++;
|
|
pszText += 2;
|
|
}
|
|
else
|
|
{
|
|
pszText++;
|
|
}
|
|
|
|
chrgIn.cpMin--;
|
|
}
|
|
|
|
// Look for DBCS chars in selection
|
|
while ((chrgIn.cpMax > 0) && *pszText)
|
|
{
|
|
if ( IsDBCSLeadByte(*pszText) )
|
|
{
|
|
cDBCSIn++;
|
|
pszText += 2;
|
|
}
|
|
else
|
|
{
|
|
pszText++;
|
|
}
|
|
|
|
chrgIn.cpMax--;
|
|
}
|
|
|
|
chrg.cpMin = pchrg->cpMin + cDBCSBefore;
|
|
chrg.cpMax = pchrg->cpMax + cDBCSBefore + cDBCSIn;
|
|
}
|
|
MemFree(pszConv);
|
|
|
|
lResult = SendMessageA(hwnd, EM_EXSETSEL, 0, (LPARAM)&chrg);
|
|
|
|
return lResult;
|
|
}
|
|
else
|
|
return SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)pchrg);
|
|
}
|
|
|
|
LRESULT RichEditExGetSel(HWND hwnd, CHARRANGE *pchrg)
|
|
{
|
|
LRESULT lResult;
|
|
LPWSTR pwszText = NULL;
|
|
LPSTR pszText = NULL;
|
|
LONG cch;
|
|
HRESULT hr;
|
|
|
|
Assert(pchrg);
|
|
|
|
// We only need to deal with this on richedit 1
|
|
if (1 == g_dwRicheditVer)
|
|
{
|
|
lResult = SendMessageA(hwnd, EM_EXGETSEL, 0, (LPARAM)pchrg);
|
|
|
|
cch = GetWindowTextLengthWrapW(hwnd);
|
|
|
|
if (0 == cch)
|
|
goto exit;
|
|
|
|
IF_NULLEXIT(MemAlloc((LPVOID*) &pwszText, cch * sizeof(WCHAR)));
|
|
*pwszText = '\0';
|
|
|
|
GetRichEditText(hwnd, pwszText, cch, FALSE, NULL);
|
|
IF_NULLEXIT((pszText = PszToANSI(CP_ACP, pwszText)));
|
|
|
|
pchrg->cpMin = RichEditNormalizeCharPos(hwnd, pchrg->cpMin, pszText);
|
|
pchrg->cpMax = RichEditNormalizeCharPos(hwnd, pchrg->cpMax, pszText);
|
|
}
|
|
else
|
|
lResult = SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)pchrg);
|
|
|
|
exit:
|
|
MemFree(pszText);
|
|
MemFree(pwszText);
|
|
return lResult;
|
|
}
|
|
|
|
// ***************************
|
|
// RichEditProtectENChange
|
|
//
|
|
// hwnd = richedit to be protected
|
|
// pdwOldMask = when fProtect, will return the original mask for the richedit
|
|
// when !fProtect, will be passing in the new mask for the richedit
|
|
// The new value should be the one that was passed back when first called
|
|
// fProtect = Are we starting the protection or ending it
|
|
//
|
|
// Notes:
|
|
//
|
|
// This function is to protect the possiblity of a re-entrant notificition
|
|
// while processing an EN_CHANGE message within richedit. It turns out that
|
|
// all richedits except for ver3 that shipped with Office 2k will protect against
|
|
// this themselves.
|
|
//
|
|
// This call should always be used in pairs with the first passing TRUE and the
|
|
// second passing in false. At this point, is only used to Protect the EN_CHANGE
|
|
// notification that is sent to the richedit.
|
|
//
|
|
// BUGBUG: It might be beneficial at some future date to make the granularity
|
|
// of these richedit issues more fine to handle cases like this where most
|
|
// v3 richedits handle this correctly.
|
|
void RichEditProtectENChange(HWND hwnd, DWORD *pdwOldMask, BOOL fProtect)
|
|
{
|
|
Assert(IsWindow(hwnd));
|
|
Assert(pdwOldMask);
|
|
|
|
if (3 == g_dwRicheditVer)
|
|
{
|
|
DWORD dwMask = *pdwOldMask;
|
|
if (fProtect)
|
|
{
|
|
dwMask = (DWORD) SendMessage(hwnd, EM_GETEVENTMASK, 0, 0);
|
|
SendMessage(hwnd, EM_SETEVENTMASK, 0, dwMask & (~ENM_CHANGE));
|
|
*pdwOldMask = dwMask;
|
|
}
|
|
else
|
|
SendMessage(hwnd, EM_SETEVENTMASK, 0, dwMask);
|
|
}
|
|
}
|
|
|
|
// @hack [dhaws] {55073} Do RTL mirroring only in special richedit versions.
|
|
void RichEditRTLMirroring(HWND hwndHeader, BOOL fSubject, LONG *plExtendFlags, BOOL fPreRECreation)
|
|
{
|
|
if (!g_fSpecialRTLRichedit)
|
|
{
|
|
if (fPreRECreation)
|
|
{
|
|
// a-msadek; RichEdit have a lot of problems with mirroring
|
|
// let's disable mirroring for this child and rather 'simulate' it
|
|
if (IS_WINDOW_RTL_MIRRORED(hwndHeader))
|
|
{
|
|
DISABLE_LAYOUT_INHERITANCE(hwndHeader);
|
|
*plExtendFlags |= WS_EX_LEFTSCROLLBAR |WS_EX_RIGHT;
|
|
if (fSubject)
|
|
{
|
|
*plExtendFlags |= WS_EX_RTLREADING;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IS_WINDOW_RTL_MIRRORED(hwndHeader))
|
|
{
|
|
ENABLE_LAYOUT_INHERITANCE(hwndHeader);
|
|
}
|
|
}
|
|
}
|
|
}
|