spiegel-keyman/windows/src/engine/kmtip/inserttext.cpp
Marc Durdin 754c1295aa chore(windows): cleanup keyman64.h usage
keyman64.h is now only used by keyman32/keyman64. Shared components have
been moved to legacy_kmx_memory.h - KMX structures in-memory, as used by
kmanalyze, kmdecomp.

Removed unnecessary references to keyman64.h from kmtip.

Also removed aiDebugger.* which are obsolete files.

One day in the future, we could merge keyman64.h with keymanengine.h.
2022-06-12 13:44:56 +10:00

287 lines
8.8 KiB
C++

/*
Name: inserttext
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 19 Jun 2007
Modified Date: 28 Mar 2016
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 19 Jun 2007 - mcdurdin - I890 - Fix deadkeys in TSF
19 Jun 2007 - mcdurdin - I890 - Fix crash with deadkeys - logging
11 Dec 2009 - mcdurdin - Header files
22 Mar 2010 - mcdurdin - Compiler fixup
24 Oct 2012 - mcdurdin - I3481 - V9.0 - Eliminate unsafe calls in C++
17 Nov 2012 - mcdurdin - I3564 - V9.0 - Add 'pseudo' mapping of characters to indicate kmtip is running
17 Nov 2012 - mcdurdin - I3565 - V9.0 - Add extra validation and logging to GetLeftOfSelection
01 Dec 2012 - mcdurdin - I3607 - V9.0 - Disable 'pseudo' mode in kmtip except for debugging
10 Jun 2014 - mcdurdin - I4262 - V9.0 - TSF deadkeys do not function correctly
28 Mar 2016 - mcdurdin - I4933 - Compat issue with Firefox 42 and IE and Keyman 9 TSF
*/
#include "pch.h" // I4262
#include "kmtip.h"
//+---------------------------------------------------------------------------
//
// InsertTextAtSelection
//
//----------------------------------------------------------------------------
#ifdef DEBUG_PSEUDO // I3607
WCHAR PseudoMap[128] = // I3564
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,L'Ą',L'Ɓ',L'Ĉ',L'Đ',L'Ĕ',L'',L'Ĝ',L'Ħ',L'Ĭ',L'Ĵ',L'Ķ',L'Ł',L'',L'',L'',
L'',L'',L'Ȑ',L'',L'Ŧ',L'Ʉ',L'',L'',L'',L'Ɏ',L'', 0x5B,0x5C,0x5D,0x5E,0x5F,
0x60,L'',L'',L'',L'',L'',L'',L'',L'',L'',L'ɉ',L'',L'ƚ',L'',L'',L'ȭ',
L'',L'ʠ',L'ɍ',L'ȿ',L'ŧ',L'ů',L'',L'ŵ',L'',L'ɏ',L'ƶ', 0x7B,0x7C,0x7D,0x7E,0x7F
//0x40,'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
//'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0x5B,0x5C,0x5D,0x5E,0x5F,
//0x60,'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
//'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0x7B,0x7C,0x7D,0x7E,0x7F
};
void Pseudofy(WCHAR *pchText) // I3564
{
for(; *pchText; pchText++)
{
if(*pchText < 0x80) *pchText = PseudoMap[*pchText];
}
}
void DePseudofy(WCHAR *pchText) // I3564
{
for(; *pchText; pchText++)
{
for(int k = 0; k < 0x80; k++)
{
if(*pchText == PseudoMap[k])
{
*pchText = k;
break;
}
}
}
}
#endif // I3607
void InsertTextAtSelection(TfEditCookie ec, ITfContext *pContext, const WCHAR *pchText, ULONG cchText)
{
LogEnter();
ITfInsertAtSelection *pInsertAtSelection;
ITfRange *pRange;
TF_SELECTION tfSelection;
// we need a special interface to insert text at the selection
if (pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection) != S_OK)
return;
#ifdef DEBUG_PSEUDO // I3607
WCHAR *PseudoBuf = new WCHAR[cchText+1]; // I3564
wcsncpy_s(PseudoBuf, cchText+1, pchText, cchText);
PseudoBuf[cchText] = 0;
Pseudofy(PseudoBuf);
// insert the text
if (pInsertAtSelection->InsertTextAtSelection(ec, 0, PseudoBuf, cchText, &pRange) != S_OK) // I3564
goto Exit;
#else // I3607
// insert the text
if (pInsertAtSelection->InsertTextAtSelection(ec, 0, pchText, cchText, &pRange) != S_OK) // I3564
goto Exit;
#endif
// update the selection, we'll make it an insertion point just past
// the inserted text.
pRange->Collapse(ec, TF_ANCHOR_END);
tfSelection.range = pRange;
tfSelection.style.ase = TF_AE_NONE;
tfSelection.style.fInterimChar = FALSE;
pContext->SetSelection(ec, 1, &tfSelection);
pRange->Release();
Exit:
#ifdef DEBUG_PSEUDO // I3607
delete PseudoBuf; // I3564
#endif // I3607
pInsertAtSelection->Release();
}
void DeleteLeftOfSelection(TfEditCookie ec, ITfContext *pContext, LONG n)
{
LogEnter();
TF_SELECTION tfSelection;
ULONG cFetched;
LONG outn;
if(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched) != S_OK || cFetched == 0)
return;
//TODO: log failures
if(tfSelection.range->ShiftStart(ec, -n, &outn, NULL) != S_OK) {
tfSelection.range->Release();
return;
}
WCHAR buf[2] = L"";
if(tfSelection.range->SetText(ec, 0, buf, 0) != S_OK) {
tfSelection.range->Release();
return;
}
// release the range
tfSelection.range->Release();
}
char *debugstr(PWSTR buf) {
WCHAR *p;
char *bufout = new char[20*7+1];
char *q;
*bufout = 0;
for(p = buf, q = bufout; *p && (p-buf < 20); p++)
{
wsprintf(q, "U+%4.4X ", *p); q = strchr(q, 0);
}
//WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL);
return bufout;
}
BOOL GetLeftOfSelection(TfEditCookie ec, ITfContext *pContext, WCHAR *buf, LONG n) // I4933
{
LogEnter();
TF_SELECTION tfSelection = {0};
TF_STATUS tfStatus = {0};
ULONG cFetched;
LONG outn;
ITfRange *pRange, *pRangeEnd;
HRESULT hr;
/* // I4933
First we will try to see if there is any text in the control, and if not, then treat
it as transitory (that is, no ability to read context. This seems to happen in Firefox
with RICHEDIT controls - for example, SourceForge comment fields e.g. reported on page
https://sourceforge.net/p/greekpolytonicsp/discussion/general/thread/9b6fa46d/
This also happens in Internet Explorer in the same fields. It is unclear at this point
if there is the same root cause or if Firefox just happens to be copying IE behaviour.
*/
if(!SUCCEEDED(hr = pContext->GetStart(ec, &pRange))) {
Log(L"GetLeftOfSelection: Exit -- Failed GetStart = %x", hr); // I3565
return FALSE;
}
if(!SUCCEEDED(hr = pContext->GetEnd(ec, &pRangeEnd))) {
Log(L"GetLeftOfSelection: Exit -- Failed GetEnd = %x", hr); // I3565
pRange->Release(); pRange = NULL;
return FALSE;
}
if(!SUCCEEDED(hr = pRange->ShiftEndToRange(ec, pRangeEnd, TF_ANCHOR_END))) {
Log(L"GetLeftOfSelection: Exit -- Failed ShiftEndToRange = %x", hr);
pRange->Release(); pRange = NULL;
pRangeEnd->Release(); pRangeEnd = NULL;
return FALSE;
}
BOOL bTreatAsTransitory = FALSE;
if(!SUCCEEDED(hr = pRange->GetText(ec, 0, buf, n, &cFetched))) {
Log(L"GetLeftOfSelection: Exit -- Failed GetRange (all text to 63 chars) = %x", hr);
bTreatAsTransitory = TRUE;
} else if(cFetched == 0) {
Log(L"GetLeftOfSelection: Exit -- no text in edit control, treating as transitory");
bTreatAsTransitory = TRUE;
}
pRange->Release(); pRange = NULL;
pRangeEnd->Release(); pRangeEnd = NULL;
if(bTreatAsTransitory) {
buf[0] = 0;
return FALSE;
}
/*
At this point, we know we can read content from the edit control, so we just need
to read the range to the left of the selection - up to (n) characters.
*/
if(!SUCCEEDED(hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &cFetched))) // I3565
{
Log(L"GetLeftOfSelection: Exit -- Failed GetSelection = %x", hr); // I3565
return FALSE;
}
if(cFetched == 0) // I3565
{
Log(L"GetLeftOfSelection: Exit -- cFetched == 0"); // I3565
if(tfSelection.range != NULL)
tfSelection.range->Release();
return FALSE;
}
if(!SUCCEEDED(hr = tfSelection.range->Clone(&pRange))) // I3565
{
Log(L"GetLeftOfSelection: Failed range->Clone = %x", hr); // I3565
tfSelection.range->Release();
return FALSE;
}
if(!SUCCEEDED(hr = pRange->Collapse(ec, TF_ANCHOR_START))) // I3565
{
Log(L"GetLeftOfSelection: Failed range->Collapse = %x", hr); // I3565
tfSelection.range->Release();
pRange->Release();
return FALSE;
}
if(!SUCCEEDED(hr = pRange->ShiftStart(ec, -n, &outn, NULL))) // I3565
{
Log(L"GetLeftOfSelection: Failed range->ShiftStart = %x", hr); // I3565
tfSelection.range->Release();
pRange->Release();
return FALSE;
}
BOOL result = TRUE;
if(SUCCEEDED(hr = pRange->GetText(ec, 0, buf, n, &cFetched))) // I3565
{
buf[cFetched] = 0;
if(ShouldDebug()) {
char *p = debugstr(buf);
Log(L"GetLeftOfSelection(%d) = %hs [%d fetched]", n, p, cFetched);
delete[] p;
}
#ifdef DEBUG_PSEUDO // I3607
DePseudofy(buf); // I3564
#endif // I3607
}
else
{
Log(L"GetLeftOfSelection: Failed range->GetText = %x", hr); // I3565
buf[0] = 0;
result = FALSE;
}
pRange->Release();
tfSelection.range->Release();
return result; // I4933
}