mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-15 13:19:23 +00:00
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.
287 lines
8.8 KiB
C++
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
|
|
}
|