mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-21 13:27:53 +00:00
Merge remote-tracking branch 'origin/master' into feat/android/improve-globe-key
This commit is contained in:
commit
9e8fed7dcd
104 changed files with 1589 additions and 1461 deletions
23
HISTORY.md
23
HISTORY.md
|
|
@ -1,5 +1,28 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 15.0.83 alpha 2021-07-08
|
||||
|
||||
* change(ios): setText now uses JSON serialization (#5321)
|
||||
* feat(common/core/web): engine correction-prep optimizations (#5319)
|
||||
* change(web): fat-finger-performance unit-test threshold (#5404)
|
||||
* feat(web): spacebar text controls (#5348)
|
||||
* chore(windows): remove backup build step (#5434)
|
||||
* refactor(common/core/desktop): Fix file and class names 🚧 (#5445)
|
||||
* refactor(web): longpresses, groundwork for additional gestures 🍕 (#5387)
|
||||
* fix(android/engine): Display dictionary help link (#5427)
|
||||
* refactor(common/core/desktop): Rename Load method 🚧 (#5444)
|
||||
|
||||
## 15.0.82 alpha 2021-07-07
|
||||
|
||||
* fix(common/models/types): fixes test script config (#5441)
|
||||
|
||||
## 15.0.81 alpha 2021-07-06
|
||||
|
||||
* fix(android/engine): Fix font file path (#5424)
|
||||
* chore(common): npm audit fixes (#5419)
|
||||
* chore(web): updates regression test-deps to same versions as web (#5420)
|
||||
* chore(windows): make build paths consistent (#5405)
|
||||
|
||||
## 15.0.80 alpha 2021-07-02
|
||||
|
||||
* refactor(android): setKeyboard and setKeymanLanguage (#5338)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
15.0.81
|
||||
15.0.84
|
||||
|
|
@ -65,7 +65,7 @@ public class LexicalModel extends LanguageResource implements Serializable {
|
|||
String version = lexicalModelJSON.optString(KMManager.KMKey_Version, "1.0");
|
||||
version = lexicalModelJSON.optString(KMManager.KMKey_LexicalModelVersion, version);
|
||||
|
||||
String helpLink = ""; // TOODO: Handle help links
|
||||
String helpLink = lexicalModelJSON.optString(KMManager.KMKey_CustomHelpLink, "");
|
||||
|
||||
// language ID and language name from lexicalModelJSON. Iterate through language array
|
||||
String languageID = "", languageName = "";
|
||||
|
|
@ -95,9 +95,8 @@ public class LexicalModel extends LanguageResource implements Serializable {
|
|||
public LexicalModel(String packageID, String lexicalModelID, String lexicalModelName,
|
||||
String languageID, String languageName, String version,
|
||||
String helpLink, String kmp) {
|
||||
// TODO: handle help links
|
||||
super(packageID, lexicalModelID, lexicalModelName, languageID, languageName,
|
||||
version, "", kmp);
|
||||
version, helpLink, kmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include <keyman/keyboardprocessor.h>
|
||||
#include "keyboard.hpp"
|
||||
#include "processor.hpp"
|
||||
#include "kmx/kmx_processevent.hpp"
|
||||
#include "kmx/kmx_processor.hpp"
|
||||
#include "mock/mock_processor.hpp"
|
||||
#include "rust/rust_mock_processor.hpp"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include <kmx/kmx_processor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include <kmx/kmx_processor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
||||
void KMX_Processor::ResetCapsLock(void)
|
||||
void KMX_ProcessEvent::ResetCapsLock(void)
|
||||
{
|
||||
DebugLog("ResetCapsLock: enter");
|
||||
|
||||
if(m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
|
||||
if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
|
||||
{
|
||||
DebugLog("ResetCapsLock: caps lock should be always off");
|
||||
if(m_environment.capsLock())
|
||||
if (m_environment.capsLock())
|
||||
{
|
||||
DebugLog("ResetCapsLock: caps lock is on, switching off caps lock");
|
||||
m_actions.QueueAction(QIT_CAPSLOCK, 0);
|
||||
|
|
@ -24,16 +24,16 @@ void KMX_Processor::ResetCapsLock(void)
|
|||
}
|
||||
|
||||
|
||||
void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp)
|
||||
void KMX_ProcessEvent::KeyCapsLockPress(KMX_BOOL FIsUp)
|
||||
{
|
||||
if(m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY)
|
||||
if (m_keyboard.Keyboard->dwFlags & KF_CAPSONONLY)
|
||||
{
|
||||
if(FIsUp && !m_environment.capsLock())
|
||||
{
|
||||
m_actions.QueueAction(QIT_CAPSLOCK, 1);
|
||||
}
|
||||
}
|
||||
else if(m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
|
||||
else if (m_keyboard.Keyboard->dwFlags & KF_CAPSALWAYSOFF)
|
||||
{
|
||||
if(!FIsUp && m_environment.capsLock())
|
||||
{
|
||||
|
|
@ -43,11 +43,11 @@ void KMX_Processor::KeyCapsLockPress(KMX_BOOL FIsUp)
|
|||
}
|
||||
|
||||
|
||||
void KMX_Processor::KeyShiftPress(KMX_BOOL FIsUp)
|
||||
void KMX_ProcessEvent::KeyShiftPress(KMX_BOOL FIsUp)
|
||||
{
|
||||
if(!m_environment.capsLock()) return;
|
||||
|
||||
if(m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS)
|
||||
if (m_keyboard.Keyboard->dwFlags & KF_SHIFTFREESCAPS)
|
||||
{
|
||||
if(!FIsUp)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
|
||||
namespace km {
|
||||
namespace kbp {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include <kmx/kmx_processor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
#include <stdarg.h>
|
||||
#include <iostream>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include <kmx/kmx_processor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
#include <keyman/keyboardprocessor_consts.h>
|
||||
#include <state.hpp>
|
||||
#include <option.hpp>
|
||||
|
|
@ -21,12 +21,12 @@ namespace {
|
|||
}
|
||||
|
||||
KMX_Environment::KMX_Environment() {
|
||||
Load(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
|
||||
Load(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
|
||||
Load(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
|
||||
Load(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
|
||||
Load(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
|
||||
Load(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
|
||||
Set(KM_KBP_KMX_ENV_PLATFORM, DEFAULT_PLATFORM);
|
||||
Set(KM_KBP_KMX_ENV_BASELAYOUT, DEFAULT_BASELAYOUT);
|
||||
Set(KM_KBP_KMX_ENV_BASELAYOUTALT, DEFAULT_BASELAYOUTALT);
|
||||
Set(KM_KBP_KMX_ENV_SIMULATEALTGR, DEFAULT_SIMULATEALTGR);
|
||||
Set(KM_KBP_KMX_ENV_CAPSLOCK, DEFAULT_CAPSLOCK);
|
||||
Set(KM_KBP_KMX_ENV_BASELAYOUTGIVESCTRLRALTFORRALT, DEFAULT_BASELAYOUTGIVESCTRLRALTFORRALT);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ char16_t const * KMX_Environment::LookUp(std::u16string const & key) const {
|
|||
}
|
||||
|
||||
|
||||
void KMX_Environment::Load(std::u16string const & key, std::u16string const & value) {
|
||||
void KMX_Environment::Set(std::u16string const & key, std::u16string const & value) {
|
||||
assert(!key.empty());
|
||||
|
||||
if (!u16icmp(key.c_str(), KM_KBP_KMX_ENV_PLATFORM)) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ private:
|
|||
km_kbp_cp const * default_value);
|
||||
public:
|
||||
KMX_Environment();
|
||||
void Load(std::u16string const & key, std::u16string const & value);
|
||||
void Set(std::u16string const & key, std::u16string const & value);
|
||||
char16_t const * LookUp(std::u16string const & key) const;
|
||||
|
||||
KMX_BOOL capsLock() const noexcept { return _capsLock; }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
|
@ -11,7 +11,7 @@ using namespace kmx;
|
|||
#include <share.h>
|
||||
#endif
|
||||
|
||||
KMX_BOOL KMX_Processor::Load(km_kbp_path_name KeyboardName)
|
||||
KMX_BOOL KMX_ProcessEvent::Load(km_kbp_path_name KeyboardName)
|
||||
{
|
||||
if(!LoadKeyboard(KeyboardName, &m_keyboard.Keyboard)) return FALSE; // I5136
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ unsigned long CalculateBufferCRC(unsigned long count, KMX_BYTE *p)
|
|||
return crc;
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard)
|
||||
KMX_BOOL KMX_ProcessEvent::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKeyboard)
|
||||
{
|
||||
PKMX_BYTE buf;
|
||||
FILE *fp;
|
||||
|
|
@ -174,7 +174,7 @@ KMX_BOOL KMX_Processor::LoadKeyboard(km_kbp_path_name fileName, LPKEYBOARD *lpKe
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
|
||||
PKMX_WCHAR KMX_ProcessEvent::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
|
||||
{
|
||||
if(offset == 0) return NULL;
|
||||
return (PKMX_WCHAR)(base + offset);
|
||||
|
|
@ -188,7 +188,7 @@ PKMX_WCHAR KMX_Processor::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
|
|||
we don't copy the strings
|
||||
This method is used on 64-bit architectures.
|
||||
*/
|
||||
LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
||||
LPKEYBOARD KMX_ProcessEvent::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
||||
{
|
||||
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
|
||||
|
||||
|
|
@ -271,7 +271,7 @@ LPKEYBOARD KMX_Processor::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
|||
Fixup the keyboard by expanding pointers. On disk the pointers are stored relative to the
|
||||
beginning of the file, but we need real pointers. This method is used on 32-bit architectures.
|
||||
*/
|
||||
LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
||||
LPKEYBOARD KMX_ProcessEvent::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
||||
{
|
||||
KMX_DWORD i, j;
|
||||
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
|
||||
|
|
@ -311,7 +311,7 @@ LPKEYBOARD KMX_Processor::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
|||
|
||||
#endif
|
||||
|
||||
KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz)
|
||||
KMX_BOOL KMX_ProcessEvent::VerifyChecksum(PKMX_BYTE buf, size_t sz)
|
||||
{
|
||||
KMX_DWORD tempcs;
|
||||
PCOMP_KEYBOARD ckbp;
|
||||
|
|
@ -324,7 +324,7 @@ KMX_BOOL KMX_Processor::VerifyChecksum(PKMX_BYTE buf, size_t sz)
|
|||
return tempcs == CalculateBufferCRC(sz, buf);
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_Processor::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
|
||||
KMX_BOOL KMX_ProcessEvent::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
|
||||
{
|
||||
KMX_DWORD i;
|
||||
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) filebase;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
|
@ -78,7 +78,7 @@ static KMX_BYTE states[MAX_RSHIFT][MAX_KSHIFT] = {
|
|||
* K_CTRLFLAG and K_ALTFLAG as well as specific keys correctly.
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_Processor::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) {
|
||||
KMX_BOOL KMX_ProcessEvent::IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift) {
|
||||
//
|
||||
// The rule shift must have ISVIRTUALKEY bit set for virt.keys
|
||||
//
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Authors: mcdurdin
|
||||
*/
|
||||
#include "processor.hpp"
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
#include <option.hpp>
|
||||
#include <state.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,189 +1,682 @@
|
|||
#include <keyman/keyboardprocessor.h>
|
||||
/*
|
||||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include "kmx_processevent.h"
|
||||
#include "state.hpp"
|
||||
#include "kmx/kmx_processevent.hpp"
|
||||
|
||||
namespace km {
|
||||
namespace kbp
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
||||
/* Globals */
|
||||
|
||||
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE;
|
||||
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE;
|
||||
KMX_BOOL km::kbp::kmx::g_silent = FALSE;
|
||||
|
||||
/*
|
||||
* KMX_ProcessEvent
|
||||
*/
|
||||
|
||||
KMX_ProcessEvent::KMX_ProcessEvent() : m_actions(&m_context), m_options(&m_keyboard) {
|
||||
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
|
||||
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
|
||||
m_miniContextIfLen = 0;
|
||||
}
|
||||
|
||||
KMX_ProcessEvent::~KMX_ProcessEvent() {
|
||||
delete[] m_indexStack;
|
||||
delete[] m_miniContext;
|
||||
}
|
||||
|
||||
char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
|
||||
// We only map SHIFT and UNSHIFTED, and CAPS LOCK
|
||||
|
||||
if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG,
|
||||
caps = (modifiers & CAPITALFLAG) == CAPITALFLAG;
|
||||
|
||||
if (vk == KM_KBP_VKEY_SPACE) {
|
||||
// Override for space because it is the same for
|
||||
// shifted and unshifted.
|
||||
return 32;
|
||||
}
|
||||
|
||||
for (int i = 0; s_char_to_vkey[i].vk; i++) {
|
||||
if (s_char_to_vkey[i].caps && caps) {
|
||||
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32;
|
||||
}
|
||||
else {
|
||||
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* KMX_BOOL ProcessEvent();
|
||||
*
|
||||
* Parameters: none
|
||||
*
|
||||
* Returns: TRUE if keystroke should be eaten
|
||||
*
|
||||
* Called by: FilterFunc
|
||||
*
|
||||
* ProcessEvent organizes the messages and gives them to the appropriate routines to
|
||||
* process, and checks the state of Windows for the keyboard handling.
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers)
|
||||
{
|
||||
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
||||
|
||||
m_kbp_state = state;
|
||||
|
||||
if (m_environment.capsLock())
|
||||
modifiers |= CAPITALFLAG;
|
||||
|
||||
m_state.vkey = vkey;
|
||||
m_state.charCode = VKeyToChar(modifiers, vkey);
|
||||
m_modifiers = modifiers;
|
||||
m_state.LoopTimes = 0;
|
||||
|
||||
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
|
||||
DebugLog("Non-Unicode keyboards are not supported.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
|
||||
|
||||
KMX_BOOL fOutputKeystroke = FALSE;
|
||||
|
||||
ProcessGroup(gp, &fOutputKeystroke);
|
||||
|
||||
m_kbp_state = nullptr;
|
||||
|
||||
return !fOutputKeystroke;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp);
|
||||
*
|
||||
* Parameters: gp Pointer to group to process inside
|
||||
*
|
||||
* Returns: TRUE if messages are to be sent,
|
||||
* and FALSE if no messages are to be sent.
|
||||
*
|
||||
* Called by: ProcessEvent, recursive inside groups
|
||||
*
|
||||
* ProcessKey is where the keystroke conversion and output takes place. This routine
|
||||
* has a lot of crucial code in it!
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
|
||||
{
|
||||
KMX_DWORD i;
|
||||
LPKEY kkp = NULL;
|
||||
PKMX_WCHAR p;
|
||||
int sdmfI;
|
||||
|
||||
/*
|
||||
If the number of nested groups goes higher than 50, then break out - this is
|
||||
a limitation of stack size. This is basically a catch-all for freaky apps that
|
||||
cause message loopbacks and nasty things like that. Okay, it's really a catch all
|
||||
for bugs! This means the user's system shouldn't hang.
|
||||
*/
|
||||
|
||||
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
||||
|
||||
sdmfI = -1;
|
||||
|
||||
for(i = 0; i < kbd->cxGroupArray; i++)
|
||||
if(gp == &kbd->dpGroupArray[i])
|
||||
{
|
||||
sdmfI = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if(++m_state.LoopTimes > 50)
|
||||
{
|
||||
DebugLog("Aborting output: m_state.LoopTimes exceeded.");
|
||||
m_state.StopOutput = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
km_kbp_status kmx_processor::validate() const {
|
||||
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
|
||||
}
|
||||
/*
|
||||
The rule matching loop.
|
||||
|
||||
kmx_processor::kmx_processor(kbp::path p)
|
||||
This loop iterates through all the rules in the group that is currently being
|
||||
processed. Each rule in a group can be of three different types:
|
||||
1. A virtual key rule, where the key to be matched is a virtual key
|
||||
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
|
||||
3. A rule in a keyless group, where only the context is matched.
|
||||
|
||||
The loop goes through and checks the rules like that. This loop could be optimized
|
||||
with standard searching techniques - the ContextMatch may be difficult.
|
||||
*/
|
||||
|
||||
DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582
|
||||
DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext()));
|
||||
|
||||
if(gp)
|
||||
{
|
||||
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
|
||||
{
|
||||
p.replace_extension(".kmx");
|
||||
_valid = bool(_kmx.Load(p.c_str()));
|
||||
|
||||
if (!_valid)
|
||||
return;
|
||||
|
||||
keyboard_attributes::options_store defaults;
|
||||
_kmx.GetOptions()->Init(defaults);
|
||||
|
||||
for (auto const & opt: defaults)
|
||||
if(!ContextMatch(kkp)) continue;
|
||||
if(!gp->fUsingKeys)
|
||||
{
|
||||
if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
|
||||
persisted_store()[opt.key] = opt.value;
|
||||
if(kkp->dpContext[0] != 0) break; else continue;
|
||||
}
|
||||
// Fill out attributes
|
||||
auto v = _kmx.GetKeyboard()->Keyboard->version;
|
||||
auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff);
|
||||
|
||||
_attributes = keyboard_attributes(static_cast<std::u16string>(p.stem()),
|
||||
std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
|
||||
}
|
||||
//if(kkp->Key == m_state.vkey)
|
||||
//SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
|
||||
// kkp->Key, kkp->ShiftFlags);
|
||||
|
||||
|
||||
char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
|
||||
{
|
||||
char16_t const * pValue = nullptr;
|
||||
switch(scope)
|
||||
/* Keyman 6.0: support Virtual Characters */
|
||||
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
|
||||
{
|
||||
case KM_KBP_OPT_KEYBOARD:
|
||||
pValue = _kmx.GetOptions()->LookUp(key);
|
||||
break;
|
||||
case KM_KBP_OPT_ENVIRONMENT:
|
||||
pValue = _kmx.GetEnvironment()->LookUp(key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
|
||||
else if(kkp->Key == m_state.vkey) break; // I4169
|
||||
}
|
||||
|
||||
return pValue ? pValue : nullptr;
|
||||
else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
|
||||
if(!gp || i == gp->cxKeyArray)
|
||||
{
|
||||
/*
|
||||
No rule was found that corresponded to the current state of the context and
|
||||
keyboard. NoMatch should be checked for everything except virtual keys; and
|
||||
context should also be kept.
|
||||
|
||||
If the message was a virtual key, then just return without checking NoMatch.
|
||||
NoMatch shouldn't be used for virtual keys because it will mean that no key
|
||||
can ever get through that isn't matched - including arrows, func. keys, etc !!
|
||||
Context is not kept for virtual keys being output.
|
||||
*/
|
||||
|
||||
DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray);
|
||||
|
||||
if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585
|
||||
// 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
|
||||
{
|
||||
switch(scope) {
|
||||
case KM_KBP_OPT_KEYBOARD:
|
||||
_kmx.GetOptions()->Set(key, value);
|
||||
persisted_store()[key] = value;
|
||||
break;
|
||||
case KM_KBP_OPT_ENVIRONMENT:
|
||||
_kmx.GetEnvironment()->Load(key, value);
|
||||
break;
|
||||
default:
|
||||
return option();
|
||||
break;
|
||||
}
|
||||
KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
|
||||
|
||||
return option(scope, key, value);
|
||||
if(fIsBackspace) { // I4838 // I4933
|
||||
PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933
|
||||
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
|
||||
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
||||
*pOutputKeystroke = TRUE; // I4933
|
||||
return FALSE; // I4933
|
||||
}
|
||||
m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933
|
||||
} else { // I4024 // I4128 // I4287 // I4290
|
||||
DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
|
||||
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
||||
*pOutputKeystroke = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
|
||||
{
|
||||
/* NoMatch rule found, and is a character key */
|
||||
PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
||||
}
|
||||
else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys)
|
||||
{
|
||||
/* No rule found, is a character key */
|
||||
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
|
||||
}
|
||||
|
||||
km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) {
|
||||
// Construct a context buffer from the items
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::u16string ctxt;
|
||||
auto cp = state->context();
|
||||
for (auto c = cp.begin(); c != cp.end(); c++) {
|
||||
switch (c->type) {
|
||||
case KM_KBP_CT_CHAR:
|
||||
if (Uni_IsSMP(c->character)) {
|
||||
ctxt += Uni_UTF32ToSurrogate1(c->character);
|
||||
ctxt += Uni_UTF32ToSurrogate2(c->character);
|
||||
}
|
||||
else {
|
||||
ctxt += (km_kbp_cp) c->character;
|
||||
}
|
||||
break;
|
||||
case KM_KBP_CT_MARKER:
|
||||
assert(c->marker > 0);
|
||||
ctxt += UC_SENTINEL;
|
||||
ctxt += CODE_DEADKEY;
|
||||
ctxt += c->marker;
|
||||
break;
|
||||
DebugLog("match found in rule %d", i);
|
||||
|
||||
/*
|
||||
Save the context that will be used for output when the 'context' keyword is used.
|
||||
For each deadkey, we need to add 2 characters; look in related stores as well...
|
||||
*/
|
||||
|
||||
assert(kkp != NULL);
|
||||
|
||||
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
|
||||
|
||||
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
||||
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
else
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
|
||||
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
|
||||
|
||||
/*
|
||||
The next section includes several optimizations that make the code a little harder
|
||||
to read, but are probably worth it in the time that they save.
|
||||
|
||||
If the output string doesn't have a "context" byte at the start, post backspaces
|
||||
to erase the appropriate number of characters in the application. If it does have
|
||||
a "context" byte at the start, then the string won't change, and no backspaces are
|
||||
necessary. You could go one step further with this optimization, in PostAllKeys,
|
||||
by comparing the starts of the strings to see what is same, and not backspacing
|
||||
that, but it is probably not necessary.
|
||||
*/
|
||||
|
||||
p = kkp->dpOutput;
|
||||
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT)
|
||||
{
|
||||
for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext))
|
||||
{
|
||||
if(*p == UC_SENTINEL)
|
||||
switch(*(p+1))
|
||||
{
|
||||
case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break;
|
||||
case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
||||
}
|
||||
else
|
||||
{
|
||||
m_actions.QueueAction(QIT_BACK, 0);
|
||||
}
|
||||
}
|
||||
p = kkp->dpOutput;
|
||||
}
|
||||
else
|
||||
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
|
||||
|
||||
/* Use PostString to post the rest of the output string. */
|
||||
|
||||
if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches)
|
||||
{
|
||||
if(gp->dpMatch && *gp->dpMatch)
|
||||
{
|
||||
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
|
||||
* LPKEYBOARD lpkb );
|
||||
*
|
||||
* Parameters: str Pointer to string to send
|
||||
* useMode Pointer to KMX_BOOL about whether a "use" command was found
|
||||
* mp Pointer to MSG structure to copy in outputting messages
|
||||
* lpkb Pointer to global keyboard structure
|
||||
*
|
||||
* Returns: 0 to continue, 1 and 2 to return.
|
||||
*
|
||||
* Called by: ProcessKey
|
||||
*
|
||||
* PostString posts a string of "context", "index", "beep", characters and virtual keys
|
||||
* to the active application, via the Keyman PostKey buffer.
|
||||
*/
|
||||
|
||||
int KMX_ProcessEvent::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke)
|
||||
{
|
||||
PKMX_WCHAR p, q, temp;
|
||||
LPSTORE s;
|
||||
int n1, n2;
|
||||
int i, n, shift;
|
||||
KMX_BOOL FoundUse = FALSE;
|
||||
|
||||
// TODO: Refactor to use incxstr
|
||||
for(p = str; *p && (p < endstr || !endstr); p++)
|
||||
{
|
||||
if(*p == UC_SENTINEL)
|
||||
switch(*(++p))
|
||||
{
|
||||
case CODE_EXTENDED: // Start of a virtual key section w/shift codes
|
||||
p++;
|
||||
|
||||
shift = *p; //(*p<<8) | *(p+1);
|
||||
m_actions.QueueAction(QIT_VSHIFTDOWN, shift);
|
||||
|
||||
p++;
|
||||
|
||||
m_actions.QueueAction(QIT_VKEYDOWN, *p);
|
||||
m_actions.QueueAction(QIT_VKEYUP, *p);
|
||||
|
||||
m_actions.QueueAction(QIT_VSHIFTUP, shift);
|
||||
|
||||
p++; // CODE_EXTENDEDEND
|
||||
////// CODE_EXTENDEDEND will be incremented by loop
|
||||
|
||||
break;
|
||||
|
||||
case CODE_DEADKEY: // A deadkey to be output
|
||||
p++;
|
||||
m_actions.QueueAction(QIT_DEADKEY, *p);
|
||||
break;
|
||||
case CODE_BEEP: // Sound an 'iconasterisk' beep
|
||||
m_actions.QueueAction(QIT_BELL, 0);
|
||||
break;
|
||||
case CODE_CONTEXT: // copy the context to the output
|
||||
{
|
||||
KMX_BOOL ignoreOutputKeystroke;
|
||||
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
|
||||
break;
|
||||
}
|
||||
case CODE_CONTEXTEX:
|
||||
p++;
|
||||
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
|
||||
if (*q) {
|
||||
KMX_BOOL ignoreOutputKeystroke;
|
||||
temp = incxstr(q);
|
||||
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
|
||||
}
|
||||
break;
|
||||
case CODE_RETURN: // stop processing and start PostAllKeys
|
||||
m_state.StopOutput = TRUE;
|
||||
return psrPostMessages;
|
||||
|
||||
case CODE_CALL:
|
||||
p++;
|
||||
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
|
||||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_USE: // use another group
|
||||
p++;
|
||||
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
|
||||
if(m_state.StopOutput) return psrPostMessages;
|
||||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_CLEARCONTEXT:
|
||||
// no longer supported, no-op
|
||||
break;
|
||||
case CODE_INDEX:
|
||||
p++;
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[*p - 1];
|
||||
p++;
|
||||
|
||||
n = m_indexStack[*p - 1];
|
||||
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
||||
PostString(temp, lpkb, incxstr(temp), pOutputKeystroke);
|
||||
break;
|
||||
case CODE_SETOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
p++;
|
||||
n2 = *p - 1;
|
||||
GetOptions()->Set(n1, n2);
|
||||
break;
|
||||
case CODE_RESETOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
GetOptions()->Reset(m_kbp_state->processor(), n1);
|
||||
break;
|
||||
case CODE_SAVEOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
GetOptions()->Save(*m_kbp_state, n1);
|
||||
break;
|
||||
case CODE_IFSYSTEMSTORE:
|
||||
p+=3;
|
||||
break;
|
||||
case CODE_SETSYSTEMSTORE:
|
||||
p+=2;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) {
|
||||
m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1)));
|
||||
p++;
|
||||
} else {
|
||||
m_actions.QueueAction(QIT_CHAR, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FoundUse ? psrPostMessages : psrCheckMatches;
|
||||
}
|
||||
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432
|
||||
{
|
||||
KMX_BOOL bEqual = u16icmp(layoutName, static_cast<const km_kbp_cp *>(m_environment.baseLayout().c_str())) == 0 || // I4583
|
||||
u16icmp(layoutName, static_cast<const km_kbp_cp*>(m_environment.baseLayoutAlt().c_str())) == 0; // I4583
|
||||
|
||||
return bEqual;
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
|
||||
{
|
||||
// TODO retrieve platform string from client environment
|
||||
// TODO cleanup to use a vector<string>.
|
||||
PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL;
|
||||
u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str());
|
||||
PKMX_WCHAR p = u16tok(t, u' ', &context);
|
||||
while (p != NULL) {
|
||||
if (u16icmp(platform, p) == 0) {
|
||||
delete [] t;
|
||||
return TRUE;
|
||||
}
|
||||
p = u16tok(NULL, u' ', &context);
|
||||
}
|
||||
|
||||
delete [] t;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::IsMatchingPlatform(LPSTORE s) // I3432
|
||||
{
|
||||
PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1];
|
||||
u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString);
|
||||
PKMX_WCHAR context = NULL;
|
||||
PKMX_WCHAR platform = u16tok(t, u' ', &context);
|
||||
while(platform != NULL)
|
||||
{
|
||||
if(!IsMatchingPlatformString(platform))
|
||||
{
|
||||
s->dwSystemID = TSS_PLATFORM_NOMATCH;
|
||||
delete [] t;
|
||||
return FALSE;
|
||||
}
|
||||
platform = u16tok(NULL, u' ', &context);
|
||||
}
|
||||
|
||||
s->dwSystemID = TSS_PLATFORM_MATCH;
|
||||
delete [] t;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* KMX_BOOL ContextMatch( LPKEY kkp );
|
||||
*
|
||||
* Parameters: kkp Rule to compare
|
||||
*
|
||||
* Returns: 0 on OK, 1 on not equal
|
||||
*
|
||||
* Called by: ProcessKey
|
||||
*
|
||||
* ContextMatch compares the context of a rule with the current context.
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::ContextMatch(LPKEY kkp)
|
||||
{
|
||||
KMX_WORD /*i,*/ n;
|
||||
PKMX_WCHAR p, q, qbuf, temp;
|
||||
PKMX_WORD indexp;
|
||||
LPSTORE s, t;
|
||||
KMX_BOOL bEqual;
|
||||
|
||||
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
|
||||
|
||||
p = kkp->dpContext;
|
||||
|
||||
if(*p == 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
|
||||
{
|
||||
// If context buf is longer than the context, then obviously not start of doc.
|
||||
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
|
||||
p = incxstr(p);
|
||||
if(*p == 0) return TRUE;
|
||||
}
|
||||
|
||||
for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp))
|
||||
{
|
||||
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
|
||||
{
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1];
|
||||
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
||||
|
||||
bEqual = u16cmp(s->dpString, t->dpString) == 0;
|
||||
if(*(pp+3) == 1 && bEqual) return FALSE;
|
||||
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
||||
}
|
||||
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE)
|
||||
{
|
||||
KMX_DWORD dwSystemID = *(pp+2)-1;
|
||||
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
||||
switch(dwSystemID)
|
||||
{
|
||||
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
|
||||
bEqual = TRUE;
|
||||
break;
|
||||
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
|
||||
bEqual = FALSE;
|
||||
break;
|
||||
case TSS_PLATFORM:
|
||||
bEqual = IsMatchingPlatform(t);
|
||||
break;
|
||||
case TSS_BASELAYOUT:
|
||||
bEqual = IsMatchingBaseLayout(t->dpString);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID);
|
||||
if(ss == NULL) return FALSE;
|
||||
bEqual = u16cmp(ss, t->dpString) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
_kmx.GetContext()->Set(ctxt.c_str());
|
||||
_kmx.GetActions()->ResetQueue();
|
||||
state->actions().clear();
|
||||
if(*(pp+3) == 1 && bEqual) return FALSE;
|
||||
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_kmx.ProcessEvent(state, vk, modifier_state)) {
|
||||
// We need to output the default keystroke
|
||||
state->actions().push_emit_keystroke();
|
||||
}
|
||||
q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p));
|
||||
if(!q)
|
||||
{
|
||||
return FALSE; // context buf is too short!
|
||||
}
|
||||
indexp = m_indexStack;
|
||||
|
||||
for (auto i = 0; i < _kmx.GetActions()->Length(); i++) {
|
||||
auto a = _kmx.GetActions()->Get(i);
|
||||
switch (a.ItemType) {
|
||||
case QIT_CAPSLOCK:
|
||||
//TODO: add Caps Event
|
||||
//dwData = 0 == off; 1 == on
|
||||
//state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CAPSLOCK, {0,}, {0} });
|
||||
break;
|
||||
case QIT_VKEYDOWN:
|
||||
case QIT_VKEYUP:
|
||||
case QIT_VSHIFTDOWN:
|
||||
case QIT_VSHIFTUP:
|
||||
//TODO: eliminate??
|
||||
break;
|
||||
case QIT_CHAR:
|
||||
state->context().push_character(a.dwData);
|
||||
state->actions().push_character(a.dwData);
|
||||
break;
|
||||
case QIT_DEADKEY:
|
||||
state->context().push_marker(a.dwData);
|
||||
state->actions().push_marker(a.dwData);
|
||||
break;
|
||||
case QIT_BELL:
|
||||
state->actions().push_alert();
|
||||
break;
|
||||
case QIT_BACK:
|
||||
switch (a.dwData) {
|
||||
case BK_DEFAULT:
|
||||
// This only happens if we know we have context to delete. Last item must be a character
|
||||
assert(!state->context().empty() && state->context().back().type != KM_KBP_IT_MARKER);
|
||||
if (!state->context().empty()) state->context().pop_back();
|
||||
state->actions().push_backspace();
|
||||
break;
|
||||
case BK_DEADKEY:
|
||||
// This only happens if we know we have context to delete. Last item must be a deadkey
|
||||
assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER);
|
||||
if (!state->context().empty()) state->context().pop_back();
|
||||
break;
|
||||
case BK_BACKSPACE:
|
||||
// User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted
|
||||
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
|
||||
if (!state->context().empty()) {
|
||||
state->context().pop_back();
|
||||
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
|
||||
}
|
||||
// Even if context is empty, we send the backspace event, because we may not
|
||||
// know the context.
|
||||
state->actions().push_backspace();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case QIT_INVALIDATECONTEXT:
|
||||
state->actions().push_invalidate_context();
|
||||
break;
|
||||
default:
|
||||
//std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl;
|
||||
assert(false);
|
||||
for(; *p && *q; p = incxstr(p))
|
||||
{
|
||||
*indexp = 0;
|
||||
|
||||
if(*p == UC_SENTINEL)
|
||||
{
|
||||
switch(*(p+1))
|
||||
{
|
||||
case CODE_DEADKEY:
|
||||
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case CODE_ANY:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
|
||||
temp = xstrchr(s->dpString, q);
|
||||
|
||||
if(temp != NULL)
|
||||
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
|
||||
|
||||
else
|
||||
return FALSE;
|
||||
break;
|
||||
case CODE_NOTANY:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
|
||||
if((temp = xstrchr(s->dpString, q)) != NULL)
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
case CODE_INDEX:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
*indexp = n = m_indexStack[(*(p+3))-1];
|
||||
|
||||
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
||||
if(n != 0) return FALSE;
|
||||
if(xchrcmp(temp, q) != 0) return FALSE;
|
||||
break;
|
||||
case CODE_CONTEXTEX:
|
||||
// only the nth character
|
||||
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
|
||||
if(n == 0)
|
||||
if(xchrcmp(temp, q) != 0) return FALSE;
|
||||
break;
|
||||
case CODE_IFOPT:
|
||||
case CODE_IFSYSTEMSTORE:
|
||||
indexp++;
|
||||
continue; // don't increment q
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
state->actions().commit();
|
||||
|
||||
return KM_KBP_STATUS_OK;
|
||||
}
|
||||
|
||||
constexpr km_kbp_attr const engine_attrs = {
|
||||
256,
|
||||
KM_KBP_LIB_CURRENT,
|
||||
KM_KBP_LIB_AGE,
|
||||
KM_KBP_LIB_REVISION,
|
||||
KM_KBP_TECH_KMX,
|
||||
"SIL International"
|
||||
};
|
||||
|
||||
km_kbp_attr const & kmx_processor::attributes() const {
|
||||
return engine_attrs;
|
||||
else if(xchrcmp(p, q) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
indexp++;
|
||||
q = incxstr(q);
|
||||
}
|
||||
|
||||
} // namespace kbp
|
||||
} // namespace km
|
||||
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested
|
||||
|
||||
return *p == *q; /*at least one must ==0 at this point*/
|
||||
}
|
||||
|
||||
KMX_Actions *KMX_ProcessEvent::GetActions() {
|
||||
return &m_actions;
|
||||
}
|
||||
|
||||
KMX_Context *KMX_ProcessEvent::GetContext() {
|
||||
return &m_context;
|
||||
}
|
||||
|
||||
KMX_Options *KMX_ProcessEvent::GetOptions() {
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
KMX_Options const *KMX_ProcessEvent::GetOptions() const {
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
KMX_Environment *KMX_ProcessEvent::GetEnvironment() {
|
||||
return &m_environment;
|
||||
}
|
||||
|
||||
KMX_Environment const *KMX_ProcessEvent::GetEnvironment() const {
|
||||
return &m_environment;
|
||||
}
|
||||
|
||||
LPINTKEYBOARDINFO KMX_ProcessEvent::GetKeyboard() {
|
||||
return &m_keyboard;
|
||||
}
|
||||
|
||||
|
||||
KMX_BOOL KMX_ProcessEvent::ReleaseKeyboardMemory(LPKEYBOARD kbd)
|
||||
{
|
||||
if (!kbd) return TRUE;
|
||||
delete kbd;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PKMX_WCHAR KMX_ProcessEvent::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
|
||||
{
|
||||
for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++)
|
||||
if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace kmx {
|
|||
|
||||
#define GLOBAL_ContextStackSize 80
|
||||
|
||||
class KMX_Processor {
|
||||
class KMX_ProcessEvent {
|
||||
private:
|
||||
PKMX_WORD m_indexStack;
|
||||
PKMX_WCHAR m_miniContext;
|
||||
|
|
@ -78,8 +78,8 @@ private:
|
|||
KMX_BOOL IsEquivalentShift(KMX_UINT rshift, KMX_UINT kshift);
|
||||
|
||||
public:
|
||||
KMX_Processor();
|
||||
~KMX_Processor();
|
||||
KMX_ProcessEvent();
|
||||
~KMX_ProcessEvent();
|
||||
|
||||
KMX_BOOL Load(km_kbp_path_name keyboardName);
|
||||
KMX_BOOL ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers); // returns FALSE on error or key not matched
|
||||
|
|
@ -1,682 +1,189 @@
|
|||
/*
|
||||
Copyright: Copyright (C) 2003-2018 SIL International.
|
||||
Authors: mcdurdin
|
||||
*/
|
||||
#include "kmx_processor.h"
|
||||
#include <keyman/keyboardprocessor.h>
|
||||
#include "state.hpp"
|
||||
#include "kmx/kmx_processor.hpp"
|
||||
|
||||
using namespace km::kbp;
|
||||
using namespace kmx;
|
||||
|
||||
/* Globals */
|
||||
|
||||
KMX_BOOL km::kbp::kmx::g_debug_ToConsole = FALSE;
|
||||
KMX_BOOL km::kbp::kmx::g_debug_KeymanLog = TRUE;
|
||||
KMX_BOOL km::kbp::kmx::g_silent = FALSE;
|
||||
|
||||
/*
|
||||
* KMX_Processor
|
||||
*/
|
||||
|
||||
KMX_Processor::KMX_Processor() : m_actions(&m_context), m_options(&m_keyboard) {
|
||||
m_indexStack = new KMX_WORD[GLOBAL_ContextStackSize];
|
||||
m_miniContext = new KMX_WCHAR[GLOBAL_ContextStackSize];
|
||||
m_miniContextIfLen = 0;
|
||||
}
|
||||
|
||||
KMX_Processor::~KMX_Processor() {
|
||||
delete[] m_indexStack;
|
||||
delete[] m_miniContext;
|
||||
}
|
||||
|
||||
char VKeyToChar(KMX_UINT modifiers, KMX_UINT vk) {
|
||||
// We only map SHIFT and UNSHIFTED, and CAPS LOCK
|
||||
|
||||
if ((modifiers & ~(K_SHIFTFLAG | CAPITALFLAG)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
shifted = (modifiers & K_SHIFTFLAG) == K_SHIFTFLAG,
|
||||
caps = (modifiers & CAPITALFLAG) == CAPITALFLAG;
|
||||
|
||||
if (vk == KM_KBP_VKEY_SPACE) {
|
||||
// Override for space because it is the same for
|
||||
// shifted and unshifted.
|
||||
return 32;
|
||||
}
|
||||
|
||||
for (int i = 0; s_char_to_vkey[i].vk; i++) {
|
||||
if (s_char_to_vkey[i].caps && caps) {
|
||||
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == !shifted) return i + 32;
|
||||
}
|
||||
else {
|
||||
if (s_char_to_vkey[i].vk == vk && s_char_to_vkey[i].shifted == shifted) return i + 32;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* KMX_BOOL ProcessEvent();
|
||||
*
|
||||
* Parameters: none
|
||||
*
|
||||
* Returns: TRUE if keystroke should be eaten
|
||||
*
|
||||
* Called by: FilterFunc
|
||||
*
|
||||
* ProcessEvent organizes the messages and gives them to the appropriate routines to
|
||||
* process, and checks the state of Windows for the keyboard handling.
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_Processor::ProcessEvent(km_kbp_state *state, KMX_UINT vkey, KMX_DWORD modifiers)
|
||||
{
|
||||
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
||||
|
||||
m_kbp_state = state;
|
||||
|
||||
if (m_environment.capsLock())
|
||||
modifiers |= CAPITALFLAG;
|
||||
|
||||
m_state.vkey = vkey;
|
||||
m_state.charCode = VKeyToChar(modifiers, vkey);
|
||||
m_modifiers = modifiers;
|
||||
m_state.LoopTimes = 0;
|
||||
|
||||
if (kbd->StartGroup[BEGIN_UNICODE] == (KMX_DWORD) -1) {
|
||||
DebugLog("Non-Unicode keyboards are not supported.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[BEGIN_UNICODE]];
|
||||
|
||||
KMX_BOOL fOutputKeystroke = FALSE;
|
||||
|
||||
ProcessGroup(gp, &fOutputKeystroke);
|
||||
|
||||
m_kbp_state = nullptr;
|
||||
|
||||
return !fOutputKeystroke;
|
||||
}
|
||||
|
||||
/*
|
||||
* PRIVATE KMX_BOOL ProcessGroup(LPGROUP gp);
|
||||
*
|
||||
* Parameters: gp Pointer to group to process inside
|
||||
*
|
||||
* Returns: TRUE if messages are to be sent,
|
||||
* and FALSE if no messages are to be sent.
|
||||
*
|
||||
* Called by: ProcessEvent, recursive inside groups
|
||||
*
|
||||
* ProcessKey is where the keystroke conversion and output takes place. This routine
|
||||
* has a lot of crucial code in it!
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_Processor::ProcessGroup(LPGROUP gp, KMX_BOOL *pOutputKeystroke)
|
||||
{
|
||||
KMX_DWORD i;
|
||||
LPKEY kkp = NULL;
|
||||
PKMX_WCHAR p;
|
||||
int sdmfI;
|
||||
|
||||
/*
|
||||
If the number of nested groups goes higher than 50, then break out - this is
|
||||
a limitation of stack size. This is basically a catch-all for freaky apps that
|
||||
cause message loopbacks and nasty things like that. Okay, it's really a catch all
|
||||
for bugs! This means the user's system shouldn't hang.
|
||||
*/
|
||||
|
||||
LPKEYBOARD kbd = m_keyboard.Keyboard;
|
||||
|
||||
sdmfI = -1;
|
||||
|
||||
for(i = 0; i < kbd->cxGroupArray; i++)
|
||||
if(gp == &kbd->dpGroupArray[i])
|
||||
{
|
||||
sdmfI = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if(++m_state.LoopTimes > 50)
|
||||
namespace km {
|
||||
namespace kbp
|
||||
{
|
||||
DebugLog("Aborting output: m_state.LoopTimes exceeded.");
|
||||
m_state.StopOutput = TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
The rule matching loop.
|
||||
km_kbp_status kmx_processor::validate() const {
|
||||
return _valid ? KM_KBP_STATUS_OK : KM_KBP_STATUS_INVALID_KEYBOARD;
|
||||
}
|
||||
|
||||
This loop iterates through all the rules in the group that is currently being
|
||||
processed. Each rule in a group can be of three different types:
|
||||
1. A virtual key rule, where the key to be matched is a virtual key
|
||||
2. A normal key rule (WM_CHAR), where the key to be matched is an Ascii char.
|
||||
3. A rule in a keyless group, where only the context is matched.
|
||||
|
||||
The loop goes through and checks the rules like that. This loop could be optimized
|
||||
with standard searching techniques - the ContextMatch may be difficult.
|
||||
*/
|
||||
|
||||
DebugLog("m_state.vkey: %s shiftFlags: %x; charCode: %X", Debug_VirtualKey(m_state.vkey), m_modifiers, m_state.charCode); // I4582
|
||||
DebugLog("m_context: %s", Debug_UnicodeString(m_context.GetFullContext()));
|
||||
|
||||
if(gp)
|
||||
{
|
||||
for(kkp = gp->dpKeyArray, i=0; i < gp->cxKeyArray; i++, kkp++)
|
||||
kmx_processor::kmx_processor(kbp::path p)
|
||||
{
|
||||
if(!ContextMatch(kkp)) continue;
|
||||
if(!gp->fUsingKeys)
|
||||
p.replace_extension(".kmx");
|
||||
_valid = bool(_kmx.Load(p.c_str()));
|
||||
|
||||
if (!_valid)
|
||||
return;
|
||||
|
||||
keyboard_attributes::options_store defaults;
|
||||
_kmx.GetOptions()->Init(defaults);
|
||||
|
||||
for (auto const & opt: defaults)
|
||||
{
|
||||
if(kkp->dpContext[0] != 0) break; else continue;
|
||||
if (!opt.empty() && opt.scope == KM_KBP_OPT_KEYBOARD )
|
||||
persisted_store()[opt.key] = opt.value;
|
||||
}
|
||||
// Fill out attributes
|
||||
auto v = _kmx.GetKeyboard()->Keyboard->version;
|
||||
auto vs = std::to_string(v >> 16) + "." + std::to_string(v & 0xffff);
|
||||
|
||||
//if(kkp->Key == m_state.vkey)
|
||||
//SendDebugMessageFormat(m_state.msg.hwnd, sdmKeyboard, 0, "kkp->Key: %d kkp->ShiftFlags: %x",
|
||||
// kkp->Key, kkp->ShiftFlags);
|
||||
_attributes = keyboard_attributes(static_cast<std::u16string>(p.stem()),
|
||||
std::u16string(vs.begin(), vs.end()), p.parent(), defaults);
|
||||
}
|
||||
|
||||
/* Keyman 6.0: support Virtual Characters */
|
||||
if(IsEquivalentShift(kkp->ShiftFlags, m_modifiers))
|
||||
|
||||
char16_t const * kmx_processor::lookup_option(km_kbp_option_scope scope, std::u16string const & key) const
|
||||
{
|
||||
char16_t const * pValue = nullptr;
|
||||
switch(scope)
|
||||
{
|
||||
if(kkp->Key > VK__MAX && kkp->Key == m_state.vkey) break; // I3438 // I4582
|
||||
else if(kkp->Key == m_state.vkey) break; // I4169
|
||||
case KM_KBP_OPT_KEYBOARD:
|
||||
pValue = _kmx.GetOptions()->LookUp(key);
|
||||
break;
|
||||
case KM_KBP_OPT_ENVIRONMENT:
|
||||
pValue = _kmx.GetEnvironment()->LookUp(key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
else if(kkp->ShiftFlags == 0 && kkp->Key == m_state.charCode && m_state.charCode != 0) break;
|
||||
|
||||
return pValue ? pValue : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!gp || i == gp->cxKeyArray)
|
||||
{
|
||||
/*
|
||||
No rule was found that corresponded to the current state of the context and
|
||||
keyboard. NoMatch should be checked for everything except virtual keys; and
|
||||
context should also be kept.
|
||||
|
||||
If the message was a virtual key, then just return without checking NoMatch.
|
||||
NoMatch shouldn't be used for virtual keys because it will mean that no key
|
||||
can ever get through that isn't matched - including arrows, func. keys, etc !!
|
||||
Context is not kept for virtual keys being output.
|
||||
*/
|
||||
|
||||
DebugLog("No match was found in group %d of %d", sdmfI, m_keyboard.Keyboard->cxGroupArray);
|
||||
|
||||
if(!gp || (m_state.charCode == 0 && gp->fUsingKeys)) // I4585
|
||||
// 7.0.241.0: I1133 - Fix mismatched parentheses on m_state.charCode - ie. we don't want to output this letter if !gp->fUsingKeys
|
||||
option kmx_processor::update_option(km_kbp_option_scope scope, std::u16string const & key, std::u16string const & value)
|
||||
{
|
||||
KMX_BOOL fIsBackspace = m_state.vkey == KM_KBP_VKEY_BKSP && (m_modifiers & (LCTRLFLAG|RCTRLFLAG|LALTFLAG|RALTFLAG)) == 0; // I4128
|
||||
|
||||
if(fIsBackspace) { // I4838 // I4933
|
||||
PKMX_WCHAR pdeletecontext = m_context.Buf(1); // I4933
|
||||
if(!pdeletecontext || *pdeletecontext == 0) { // I4933
|
||||
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
||||
*pOutputKeystroke = TRUE; // I4933
|
||||
return FALSE; // I4933
|
||||
}
|
||||
m_actions.QueueAction(QIT_BACK, BK_BACKSPACE); // I4933
|
||||
} else { // I4024 // I4128 // I4287 // I4290
|
||||
DebugLog(" ... IsLegacy = FALSE; IsTIP = TRUE"); // I4128
|
||||
m_actions.QueueAction(QIT_INVALIDATECONTEXT, 0);
|
||||
*pOutputKeystroke = TRUE;
|
||||
return FALSE;
|
||||
switch(scope) {
|
||||
case KM_KBP_OPT_KEYBOARD:
|
||||
_kmx.GetOptions()->Set(key, value);
|
||||
persisted_store()[key] = value;
|
||||
break;
|
||||
case KM_KBP_OPT_ENVIRONMENT:
|
||||
_kmx.GetEnvironment()->Set(key, value);
|
||||
break;
|
||||
default:
|
||||
return option();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (gp->dpNoMatch != NULL && *gp->dpNoMatch != 0)
|
||||
{
|
||||
/* NoMatch rule found, and is a character key */
|
||||
PostString(gp->dpNoMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
||||
}
|
||||
else if (m_state.charCode != 0 && m_state.charCode != 0xFFFF && gp->fUsingKeys)
|
||||
{
|
||||
/* No rule found, is a character key */
|
||||
m_actions.QueueAction(QIT_CHAR, m_state.charCode);
|
||||
|
||||
return option(scope, key, value);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
km_kbp_status kmx_processor::process_event(km_kbp_state *state, km_kbp_virtual_key vk, uint16_t modifier_state) {
|
||||
// Construct a context buffer from the items
|
||||
|
||||
DebugLog("match found in rule %d", i);
|
||||
|
||||
/*
|
||||
Save the context that will be used for output when the 'context' keyword is used.
|
||||
For each deadkey, we need to add 2 characters; look in related stores as well...
|
||||
*/
|
||||
|
||||
assert(kkp != NULL);
|
||||
|
||||
m_miniContextIfLen = xstrlen(kkp->dpContext) - xstrlen_ignoreifopt(kkp->dpContext);
|
||||
|
||||
// 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
||||
if(*kkp->dpContext == UC_SENTINEL && *(kkp->dpContext+1) == CODE_NUL)
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)-1) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
else
|
||||
u16cpy(m_miniContext, /*GLOBAL_ContextStackSize,*/ m_context.Buf(xstrlen_ignoreifopt(kkp->dpContext)) /*, GLOBAL_ContextStackSize*/); // I3162 // I3536
|
||||
|
||||
m_miniContext[GLOBAL_ContextStackSize-1] = 0;
|
||||
|
||||
/*
|
||||
The next section includes several optimizations that make the code a little harder
|
||||
to read, but are probably worth it in the time that they save.
|
||||
|
||||
If the output string doesn't have a "context" byte at the start, post backspaces
|
||||
to erase the appropriate number of characters in the application. If it does have
|
||||
a "context" byte at the start, then the string won't change, and no backspaces are
|
||||
necessary. You could go one step further with this optimization, in PostAllKeys,
|
||||
by comparing the starts of the strings to see what is same, and not backspacing
|
||||
that, but it is probably not necessary.
|
||||
*/
|
||||
|
||||
p = kkp->dpOutput;
|
||||
if(*p != UC_SENTINEL || *(p+1) != CODE_CONTEXT)
|
||||
{
|
||||
for(p = decxstr((KMX_WCHAR *) u16chr(m_miniContext, 0), m_miniContext); p != NULL; p = decxstr(p, m_miniContext))
|
||||
{
|
||||
if(*p == UC_SENTINEL)
|
||||
switch(*(p+1))
|
||||
{
|
||||
case CODE_DEADKEY: m_actions.QueueAction(QIT_BACK, BK_DEADKEY); break;
|
||||
case CODE_NUL: break; // 11 Aug 2003 - I25(v6) - mcdurdin - CODE_NUL context support
|
||||
}
|
||||
else
|
||||
{
|
||||
m_actions.QueueAction(QIT_BACK, 0);
|
||||
}
|
||||
}
|
||||
p = kkp->dpOutput;
|
||||
}
|
||||
else
|
||||
p = incxstr(p); // otherwise, the "context" entry has to be jumped over
|
||||
|
||||
/* Use PostString to post the rest of the output string. */
|
||||
|
||||
if(PostString(p, m_keyboard.Keyboard, NULL, pOutputKeystroke) == psrCheckMatches)
|
||||
{
|
||||
if(gp->dpMatch && *gp->dpMatch)
|
||||
{
|
||||
PostString(gp->dpMatch, m_keyboard.Keyboard, NULL, pOutputKeystroke);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* int PostString( PKMX_CHAR str, KMX_BOOL *useMode, LPMSG mp,
|
||||
* LPKEYBOARD lpkb );
|
||||
*
|
||||
* Parameters: str Pointer to string to send
|
||||
* useMode Pointer to KMX_BOOL about whether a "use" command was found
|
||||
* mp Pointer to MSG structure to copy in outputting messages
|
||||
* lpkb Pointer to global keyboard structure
|
||||
*
|
||||
* Returns: 0 to continue, 1 and 2 to return.
|
||||
*
|
||||
* Called by: ProcessKey
|
||||
*
|
||||
* PostString posts a string of "context", "index", "beep", characters and virtual keys
|
||||
* to the active application, via the Keyman PostKey buffer.
|
||||
*/
|
||||
|
||||
int KMX_Processor::PostString(PKMX_WCHAR str, LPKEYBOARD lpkb, PKMX_WCHAR endstr, KMX_BOOL *pOutputKeystroke)
|
||||
{
|
||||
PKMX_WCHAR p, q, temp;
|
||||
LPSTORE s;
|
||||
int n1, n2;
|
||||
int i, n, shift;
|
||||
KMX_BOOL FoundUse = FALSE;
|
||||
|
||||
// TODO: Refactor to use incxstr
|
||||
for(p = str; *p && (p < endstr || !endstr); p++)
|
||||
{
|
||||
if(*p == UC_SENTINEL)
|
||||
switch(*(++p))
|
||||
{
|
||||
case CODE_EXTENDED: // Start of a virtual key section w/shift codes
|
||||
p++;
|
||||
|
||||
shift = *p; //(*p<<8) | *(p+1);
|
||||
m_actions.QueueAction(QIT_VSHIFTDOWN, shift);
|
||||
|
||||
p++;
|
||||
|
||||
m_actions.QueueAction(QIT_VKEYDOWN, *p);
|
||||
m_actions.QueueAction(QIT_VKEYUP, *p);
|
||||
|
||||
m_actions.QueueAction(QIT_VSHIFTUP, shift);
|
||||
|
||||
p++; // CODE_EXTENDEDEND
|
||||
////// CODE_EXTENDEDEND will be incremented by loop
|
||||
|
||||
break;
|
||||
|
||||
case CODE_DEADKEY: // A deadkey to be output
|
||||
p++;
|
||||
m_actions.QueueAction(QIT_DEADKEY, *p);
|
||||
break;
|
||||
case CODE_BEEP: // Sound an 'iconasterisk' beep
|
||||
m_actions.QueueAction(QIT_BELL, 0);
|
||||
break;
|
||||
case CODE_CONTEXT: // copy the context to the output
|
||||
{
|
||||
KMX_BOOL ignoreOutputKeystroke;
|
||||
PostString(m_miniContext, lpkb, (PKMX_WCHAR) u16chr(m_miniContext, 0), &ignoreOutputKeystroke);
|
||||
break;
|
||||
}
|
||||
case CODE_CONTEXTEX:
|
||||
p++;
|
||||
for (q = m_miniContext, i = m_miniContextIfLen; *q && i < *p - 1; i++, q = incxstr(q));
|
||||
if (*q) {
|
||||
KMX_BOOL ignoreOutputKeystroke;
|
||||
temp = incxstr(q);
|
||||
PostString(q, lpkb, temp, &ignoreOutputKeystroke);
|
||||
}
|
||||
break;
|
||||
case CODE_RETURN: // stop processing and start PostAllKeys
|
||||
m_state.StopOutput = TRUE;
|
||||
return psrPostMessages;
|
||||
|
||||
case CODE_CALL:
|
||||
p++;
|
||||
DebugLog("CallDLL not supported [store=%d].\n", *p-1);
|
||||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_USE: // use another group
|
||||
p++;
|
||||
ProcessGroup(&lpkb->dpGroupArray[*p-1], pOutputKeystroke);
|
||||
if(m_state.StopOutput) return psrPostMessages;
|
||||
FoundUse = TRUE;
|
||||
break;
|
||||
case CODE_CLEARCONTEXT:
|
||||
// no longer supported, no-op
|
||||
break;
|
||||
case CODE_INDEX:
|
||||
p++;
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[*p - 1];
|
||||
p++;
|
||||
|
||||
n = m_indexStack[*p - 1];
|
||||
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
||||
PostString(temp, lpkb, incxstr(temp), pOutputKeystroke);
|
||||
break;
|
||||
case CODE_SETOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
p++;
|
||||
n2 = *p - 1;
|
||||
GetOptions()->Set(n1, n2);
|
||||
break;
|
||||
case CODE_RESETOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
GetOptions()->Reset(m_kbp_state->processor(), n1);
|
||||
break;
|
||||
case CODE_SAVEOPT:
|
||||
p++;
|
||||
n1 = *p - 1;
|
||||
GetOptions()->Save(*m_kbp_state, n1);
|
||||
break;
|
||||
case CODE_IFSYSTEMSTORE:
|
||||
p+=3;
|
||||
break;
|
||||
case CODE_SETSYSTEMSTORE:
|
||||
p+=2;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if(Uni_IsSurrogate1(*p) && Uni_IsSurrogate2(*(p+1))) {
|
||||
m_actions.QueueAction(QIT_CHAR, Uni_SurrogateToUTF32(*p, *(p+1)));
|
||||
p++;
|
||||
} else {
|
||||
m_actions.QueueAction(QIT_CHAR, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FoundUse ? psrPostMessages : psrCheckMatches;
|
||||
}
|
||||
|
||||
|
||||
KMX_BOOL KMX_Processor::IsMatchingBaseLayout(PKMX_WCHAR layoutName) // I3432
|
||||
{
|
||||
KMX_BOOL bEqual = u16icmp(layoutName, static_cast<const km_kbp_cp *>(m_environment.baseLayout().c_str())) == 0 || // I4583
|
||||
u16icmp(layoutName, static_cast<const km_kbp_cp*>(m_environment.baseLayoutAlt().c_str())) == 0; // I4583
|
||||
|
||||
return bEqual;
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_Processor::IsMatchingPlatformString(PKMX_WCHAR platform) // I3432
|
||||
{
|
||||
// TODO retrieve platform string from client environment
|
||||
// TODO cleanup to use a vector<string>.
|
||||
PKMX_WCHAR t = new KMX_WCHAR[u16len(m_environment.platform().c_str()) + 1], context = NULL;
|
||||
u16cpy(t, /*wcslen(s->dpString)+1,*/ m_environment.platform().c_str());
|
||||
PKMX_WCHAR p = u16tok(t, u' ', &context);
|
||||
while (p != NULL) {
|
||||
if (u16icmp(platform, p) == 0) {
|
||||
delete [] t;
|
||||
return TRUE;
|
||||
}
|
||||
p = u16tok(NULL, u' ', &context);
|
||||
}
|
||||
|
||||
delete [] t;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
KMX_BOOL KMX_Processor::IsMatchingPlatform(LPSTORE s) // I3432
|
||||
{
|
||||
PKMX_WCHAR t = new KMX_WCHAR[u16len(s->dpString)+1];
|
||||
u16cpy(t, /*wcslen(s->dpString)+1,*/ s->dpString);
|
||||
PKMX_WCHAR context = NULL;
|
||||
PKMX_WCHAR platform = u16tok(t, u' ', &context);
|
||||
while(platform != NULL)
|
||||
{
|
||||
if(!IsMatchingPlatformString(platform))
|
||||
{
|
||||
s->dwSystemID = TSS_PLATFORM_NOMATCH;
|
||||
delete [] t;
|
||||
return FALSE;
|
||||
}
|
||||
platform = u16tok(NULL, u' ', &context);
|
||||
}
|
||||
|
||||
s->dwSystemID = TSS_PLATFORM_MATCH;
|
||||
delete [] t;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* KMX_BOOL ContextMatch( LPKEY kkp );
|
||||
*
|
||||
* Parameters: kkp Rule to compare
|
||||
*
|
||||
* Returns: 0 on OK, 1 on not equal
|
||||
*
|
||||
* Called by: ProcessKey
|
||||
*
|
||||
* ContextMatch compares the context of a rule with the current context.
|
||||
*/
|
||||
|
||||
KMX_BOOL KMX_Processor::ContextMatch(LPKEY kkp)
|
||||
{
|
||||
KMX_WORD /*i,*/ n;
|
||||
PKMX_WCHAR p, q, qbuf, temp;
|
||||
PKMX_WORD indexp;
|
||||
LPSTORE s, t;
|
||||
KMX_BOOL bEqual;
|
||||
|
||||
memset(m_indexStack, 0, GLOBAL_ContextStackSize*sizeof(KMX_WORD));
|
||||
|
||||
p = kkp->dpContext;
|
||||
|
||||
if(*p == 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(*p == UC_SENTINEL && *(p+1) == CODE_NUL)
|
||||
{
|
||||
// If context buf is longer than the context, then obviously not start of doc.
|
||||
if(m_context.Buf(xstrlen_ignoreifopt(p))) return FALSE;
|
||||
p = incxstr(p);
|
||||
if(*p == 0) return TRUE;
|
||||
}
|
||||
|
||||
for(PKMX_WCHAR pp = p; pp && *pp; pp = incxstr(pp))
|
||||
{
|
||||
if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFOPT)
|
||||
{
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(pp+2))-1];
|
||||
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
||||
|
||||
bEqual = u16cmp(s->dpString, t->dpString) == 0;
|
||||
if(*(pp+3) == 1 && bEqual) return FALSE;
|
||||
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
||||
}
|
||||
else if(*pp == UC_SENTINEL && *(pp+1) == CODE_IFSYSTEMSTORE)
|
||||
{
|
||||
KMX_DWORD dwSystemID = *(pp+2)-1;
|
||||
t = &m_keyboard.Keyboard->dpStoreArray[(*(pp+4))-1];
|
||||
switch(dwSystemID)
|
||||
{
|
||||
case TSS_PLATFORM_MATCH: // Cached platform result - a matching platform
|
||||
bEqual = TRUE;
|
||||
break;
|
||||
case TSS_PLATFORM_NOMATCH: // Cached platform result - not a matching platform
|
||||
bEqual = FALSE;
|
||||
break;
|
||||
case TSS_PLATFORM:
|
||||
bEqual = IsMatchingPlatform(t);
|
||||
break;
|
||||
case TSS_BASELAYOUT:
|
||||
bEqual = IsMatchingBaseLayout(t->dpString);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
PKMX_WCHAR ss = GetSystemStore(m_keyboard.Keyboard, dwSystemID);
|
||||
if(ss == NULL) return FALSE;
|
||||
bEqual = u16cmp(ss, t->dpString) == 0;
|
||||
std::u16string ctxt;
|
||||
auto cp = state->context();
|
||||
for (auto c = cp.begin(); c != cp.end(); c++) {
|
||||
switch (c->type) {
|
||||
case KM_KBP_CT_CHAR:
|
||||
if (Uni_IsSMP(c->character)) {
|
||||
ctxt += Uni_UTF32ToSurrogate1(c->character);
|
||||
ctxt += Uni_UTF32ToSurrogate2(c->character);
|
||||
}
|
||||
else {
|
||||
ctxt += (km_kbp_cp) c->character;
|
||||
}
|
||||
break;
|
||||
case KM_KBP_CT_MARKER:
|
||||
assert(c->marker > 0);
|
||||
ctxt += UC_SENTINEL;
|
||||
ctxt += CODE_DEADKEY;
|
||||
ctxt += c->marker;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(*(pp+3) == 1 && bEqual) return FALSE;
|
||||
if(*(pp+3) == 2 && !bEqual) return FALSE;
|
||||
}
|
||||
}
|
||||
_kmx.GetContext()->Set(ctxt.c_str());
|
||||
_kmx.GetActions()->ResetQueue();
|
||||
state->actions().clear();
|
||||
|
||||
q = qbuf = m_context.Buf(xstrlen_ignoreifopt(p));
|
||||
if(!q)
|
||||
{
|
||||
return FALSE; // context buf is too short!
|
||||
}
|
||||
indexp = m_indexStack;
|
||||
|
||||
for(; *p && *q; p = incxstr(p))
|
||||
{
|
||||
*indexp = 0;
|
||||
|
||||
if(*p == UC_SENTINEL)
|
||||
{
|
||||
switch(*(p+1))
|
||||
{
|
||||
case CODE_DEADKEY:
|
||||
if(*q != UC_SENTINEL || *(q+1) != CODE_DEADKEY || *(q+2) != *(p+2))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
case CODE_ANY:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
|
||||
temp = xstrchr(s->dpString, q);
|
||||
|
||||
if(temp != NULL)
|
||||
*indexp = (KMX_WORD) xstrpos(temp, s->dpString);
|
||||
|
||||
else
|
||||
return FALSE;
|
||||
break;
|
||||
case CODE_NOTANY:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
|
||||
if((temp = xstrchr(s->dpString, q)) != NULL)
|
||||
return FALSE;
|
||||
|
||||
break;
|
||||
case CODE_INDEX:
|
||||
s = &m_keyboard.Keyboard->dpStoreArray[(*(p+2))-1];
|
||||
*indexp = n = m_indexStack[(*(p+3))-1];
|
||||
|
||||
for(temp = s->dpString; *temp && n > 0; temp = incxstr(temp), n--);
|
||||
if(n != 0) return FALSE;
|
||||
if(xchrcmp(temp, q) != 0) return FALSE;
|
||||
break;
|
||||
case CODE_CONTEXTEX:
|
||||
// only the nth character
|
||||
for(n = *(p+2) - 1, temp = qbuf; temp < q && n > 0; n--, temp = incxstr(temp));
|
||||
if(n == 0)
|
||||
if(xchrcmp(temp, q) != 0) return FALSE;
|
||||
break;
|
||||
case CODE_IFOPT:
|
||||
case CODE_IFSYSTEMSTORE:
|
||||
indexp++;
|
||||
continue; // don't increment q
|
||||
default:
|
||||
return FALSE;
|
||||
if (!_kmx.ProcessEvent(state, vk, modifier_state)) {
|
||||
// We need to output the default keystroke
|
||||
state->actions().push_emit_keystroke();
|
||||
}
|
||||
|
||||
for (auto i = 0; i < _kmx.GetActions()->Length(); i++) {
|
||||
auto a = _kmx.GetActions()->Get(i);
|
||||
switch (a.ItemType) {
|
||||
case QIT_CAPSLOCK:
|
||||
//TODO: add Caps Event
|
||||
//dwData = 0 == off; 1 == on
|
||||
//state->actions.emplace_back(km_kbp_action_item{ KM_KBP_IT_CAPSLOCK, {0,}, {0} });
|
||||
break;
|
||||
case QIT_VKEYDOWN:
|
||||
case QIT_VKEYUP:
|
||||
case QIT_VSHIFTDOWN:
|
||||
case QIT_VSHIFTUP:
|
||||
//TODO: eliminate??
|
||||
break;
|
||||
case QIT_CHAR:
|
||||
state->context().push_character(a.dwData);
|
||||
state->actions().push_character(a.dwData);
|
||||
break;
|
||||
case QIT_DEADKEY:
|
||||
state->context().push_marker(a.dwData);
|
||||
state->actions().push_marker(a.dwData);
|
||||
break;
|
||||
case QIT_BELL:
|
||||
state->actions().push_alert();
|
||||
break;
|
||||
case QIT_BACK:
|
||||
switch (a.dwData) {
|
||||
case BK_DEFAULT:
|
||||
// This only happens if we know we have context to delete. Last item must be a character
|
||||
assert(!state->context().empty() && state->context().back().type != KM_KBP_IT_MARKER);
|
||||
if (!state->context().empty()) state->context().pop_back();
|
||||
state->actions().push_backspace();
|
||||
break;
|
||||
case BK_DEADKEY:
|
||||
// This only happens if we know we have context to delete. Last item must be a deadkey
|
||||
assert(!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER);
|
||||
if (!state->context().empty()) state->context().pop_back();
|
||||
break;
|
||||
case BK_BACKSPACE:
|
||||
// User-initiated backspace. We need to delete deadkeys from context, both sides of the character deleted
|
||||
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
|
||||
if (!state->context().empty()) {
|
||||
state->context().pop_back();
|
||||
while (!state->context().empty() && state->context().back().type == KM_KBP_IT_MARKER) state->context().pop_back();
|
||||
}
|
||||
// Even if context is empty, we send the backspace event, because we may not
|
||||
// know the context.
|
||||
state->actions().push_backspace();
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
case QIT_INVALIDATECONTEXT:
|
||||
state->actions().push_invalidate_context();
|
||||
break;
|
||||
default:
|
||||
//std::cout << "Unexpected item type " << a.ItemType << ", " << a.dwData << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
state->actions().commit();
|
||||
|
||||
return KM_KBP_STATUS_OK;
|
||||
}
|
||||
else if(xchrcmp(p, q) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
|
||||
constexpr km_kbp_attr const engine_attrs = {
|
||||
256,
|
||||
KM_KBP_LIB_CURRENT,
|
||||
KM_KBP_LIB_AGE,
|
||||
KM_KBP_LIB_REVISION,
|
||||
KM_KBP_TECH_KMX,
|
||||
"SIL International"
|
||||
};
|
||||
|
||||
km_kbp_attr const & kmx_processor::attributes() const {
|
||||
return engine_attrs;
|
||||
}
|
||||
indexp++;
|
||||
q = incxstr(q);
|
||||
}
|
||||
|
||||
while(*p == UC_SENTINEL && (*(p+1) == CODE_IFOPT || *(p+1) == CODE_IFSYSTEMSTORE)) p = incxstr(p); // already tested
|
||||
|
||||
return *p == *q; /*at least one must ==0 at this point*/
|
||||
}
|
||||
|
||||
KMX_Actions *KMX_Processor::GetActions() {
|
||||
return &m_actions;
|
||||
}
|
||||
|
||||
KMX_Context *KMX_Processor::GetContext() {
|
||||
return &m_context;
|
||||
}
|
||||
|
||||
KMX_Options *KMX_Processor::GetOptions() {
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
KMX_Options const *KMX_Processor::GetOptions() const {
|
||||
return &m_options;
|
||||
}
|
||||
|
||||
KMX_Environment *KMX_Processor::GetEnvironment() {
|
||||
return &m_environment;
|
||||
}
|
||||
|
||||
KMX_Environment const *KMX_Processor::GetEnvironment() const {
|
||||
return &m_environment;
|
||||
}
|
||||
|
||||
LPINTKEYBOARDINFO KMX_Processor::GetKeyboard() {
|
||||
return &m_keyboard;
|
||||
}
|
||||
|
||||
|
||||
KMX_BOOL KMX_Processor::ReleaseKeyboardMemory(LPKEYBOARD kbd)
|
||||
{
|
||||
if (!kbd) return TRUE;
|
||||
delete kbd;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
PKMX_WCHAR KMX_Processor::GetSystemStore(LPKEYBOARD kb, KMX_DWORD SystemID)
|
||||
{
|
||||
for (KMX_DWORD i = 0; i < kb->cxStoreArray; i++)
|
||||
if (kb->dpStoreArray[i].dwSystemID == SystemID) return kb->dpStoreArray[i].dpString;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
} // namespace kbp
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <keyman/keyboardprocessor.h>
|
||||
#include "kmx/kmx_processor.h"
|
||||
#include "kmx/kmx_processevent.h"
|
||||
#include "keyboard.hpp"
|
||||
#include "processor.hpp"
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ namespace kbp
|
|||
{
|
||||
private:
|
||||
bool _valid;
|
||||
kmx::KMX_Processor _kmx;
|
||||
kmx::KMX_ProcessEvent _kmx;
|
||||
public:
|
||||
kmx_processor(path);
|
||||
km_kbp_status process_event(km_kbp_state *state,
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
#include <iterator>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include "kmx_processor.h"
|
||||
#include "kmx_processevent.h"
|
||||
#include "utfcodec.hpp"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <kmx/kmx_processor.h>
|
||||
#include <kmx/kmx_processevent.h>
|
||||
|
||||
#include "path.hpp"
|
||||
#include "state.hpp"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/input-processor",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/input-processor",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "The core text and prediction processing engine for KeymanWeb",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"devDependencies": {
|
||||
"@keymanapp/lexical-model-compiler": "^15.0.81",
|
||||
"@keymanapp/resources-gosh": "^15.0.81",
|
||||
"@keymanapp/lexical-model-compiler": "^15.0.84",
|
||||
"@keymanapp/resources-gosh": "^15.0.84",
|
||||
"@types/node": "^11.9.4",
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^8.4.0",
|
||||
|
|
@ -34,11 +34,11 @@
|
|||
"mocha": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/keyboard-processor": "^15.0.81",
|
||||
"@keymanapp/lexical-model-layer": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/keyboard-processor": "^15.0.84",
|
||||
"@keymanapp/lexical-model-layer": "^15.0.84",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"eventemitter3": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/keyboard-processor",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/keyboard-processor",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Core module for Keyman keyboard support in KeymanWeb.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"devDependencies": {
|
||||
"@keymanapp/resources-gosh": "^15.0.81",
|
||||
"@keymanapp/resources-gosh": "^15.0.84",
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^8.4.0",
|
||||
"mocha-teamcity-reporter": "^4.0.0",
|
||||
|
|
@ -32,9 +32,9 @@
|
|||
"mocha": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"@types/node": "^11.9.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,10 +221,10 @@ namespace com.keyman.keyboards {
|
|||
return Lkc;
|
||||
}
|
||||
|
||||
public getSubkey(id: string): ActiveKey {
|
||||
public getSubkey(coreID: string): ActiveKey {
|
||||
if(this.sk) {
|
||||
for(let key of this.sk) {
|
||||
if(key.id == id) {
|
||||
if(key.coreID == coreID) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
common/core/web/tools/recorder/package-lock.json
generated
2
common/core/web/tools/recorder/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/recorder-core",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/recorder-core",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Core classes used to develop KeymanWeb test cases based on keystrokes",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
@ -17,10 +17,10 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"dependencies": {
|
||||
"@keymanapp/keyboard-processor": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/keyboard-processor": "^15.0.84",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"@types/node": "^11.9.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/web-sentry-manager",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/web-sentry-manager",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Common setup for KeymanWeb's Sentry-based error reporting",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"typescript": "^3.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@sentry/browser": "^5.27.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
common/core/web/utils/package-lock.json
generated
2
common/core/web/utils/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/web-utils",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/web-utils",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Common utility functions used throughout other Keyman packages",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
|
@ -20,8 +20,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"devDependencies": {
|
||||
"@keymanapp/resources-gosh": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/resources-gosh": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@types/node": "^14.0.5",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
|
|
|
|||
2
common/models/templates/package-lock.json
generated
2
common/models/templates/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/models-templates",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/models-templates",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Backing model implementations (templates) for Keyman's modelling layer",
|
||||
"keywords": [
|
||||
"lm",
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
"url": "https://github.com/keymanapp/keyman/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^14.0.4",
|
||||
|
|
@ -52,6 +52,6 @@
|
|||
"typescript": "^3.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/models-wordbreakers": "^15.0.81"
|
||||
"@keymanapp/models-wordbreakers": "^15.0.84"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
common/models/types/package-lock.json
generated
2
common/models/types/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/models-types",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"name": "@keymanapp/models-types",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Type definitions in used in the modeling (lexical model/predictive text) component of Keyman.",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {
|
||||
"test": "tsc test.ts"
|
||||
"test": "tsc test.ts -lib es6"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
2
common/models/wordbreakers/package-lock.json
generated
2
common/models/wordbreakers/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/models-wordbreakers",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/models-wordbreakers",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Word breakers bundled in the modeling layer",
|
||||
"keywords": [
|
||||
"lexical model",
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
"url": "https://github.com/keymanapp/keyman/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"chai": "^4.3.4",
|
||||
|
|
|
|||
2
common/predictive-text/package-lock.json
generated
2
common/predictive-text/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/lexical-model-layer",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/lexical-model-layer",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Keyman/Predictive Text integration layer",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
|
|
@ -21,8 +21,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"devDependencies": {
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/resources-gosh": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/resources-gosh": "^15.0.84",
|
||||
"@types/node": "^10.17.21",
|
||||
"chai": "^4.3.4",
|
||||
"karma": "^6.3.4",
|
||||
|
|
@ -46,10 +46,10 @@
|
|||
"typescript": "^3.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/models-templates": "^15.0.81",
|
||||
"@keymanapp/models-wordbreakers": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/models-templates": "^15.0.84",
|
||||
"@keymanapp/models-wordbreakers": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"es6-shim": "^0.35.5",
|
||||
"string.prototype.codepointat": "^0.2.1",
|
||||
"string.prototype.startswith": "^0.2.0"
|
||||
|
|
|
|||
2
developer/js/package-lock.json
generated
2
developer/js/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/lexical-model-compiler",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/lexical-model-compiler",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Keyman Developer lexical model compiler",
|
||||
"keywords": [
|
||||
"keyboard",
|
||||
|
|
@ -37,14 +37,14 @@
|
|||
"node": ">=12.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"commander": "^3.0.0",
|
||||
"typescript": "^3.8.3",
|
||||
"xml2js": "^0.4.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/models-templates": "^15.0.81",
|
||||
"@keymanapp/models-wordbreakers": "^15.0.81",
|
||||
"@keymanapp/models-templates": "^15.0.84",
|
||||
"@keymanapp/models-wordbreakers": "^15.0.84",
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^10.14.6",
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@
|
|||
"ts-node",
|
||||
"typescript"
|
||||
],
|
||||
"version": "15.0.81"
|
||||
"version": "15.0.84"
|
||||
}
|
||||
|
|
|
|||
2
resources/gosh/package-lock.json
generated
2
resources/gosh/package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@keymanapp/resources-gosh",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/resources-gosh",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Good Ol' Shell",
|
||||
"author": "Marc Durdin <marc@keyman.com> (https://github.com/mcdurdin)",
|
||||
"contributors": [
|
||||
|
|
|
|||
2
resources/web-environment/package-lock.json
generated
2
resources/web-environment/package-lock.json
generated
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "@keymanapp/web-environment",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@keymanapp/web-environment",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "A compilation utility for passing select environment variables into our TS builds",
|
||||
"author": "Joshua Horton <joshua_horton@sil.org> (https://github.com/jahorton)",
|
||||
"homepage": "https://github.com/keymanapp/keyman",
|
||||
|
|
@ -17,6 +17,6 @@
|
|||
"url": "https://github.com/keymanapp/keyman/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@keymanapp/resources-gosh": "^15.0.81"
|
||||
"@keymanapp/resources-gosh": "^15.0.84"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
web/package-lock.json
generated
2
web/package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keyman",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keyman",
|
||||
"version": "15.0.81",
|
||||
"version": "15.0.84",
|
||||
"description": "Facilitates text input in any language.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
},
|
||||
"homepage": "https://github.com/keymanapp/keyman#readme",
|
||||
"devDependencies": {
|
||||
"@keymanapp/resources-gosh": "^15.0.81",
|
||||
"@keymanapp/web-sentry-manager": "^15.0.81",
|
||||
"@keymanapp/resources-gosh": "^15.0.84",
|
||||
"@keymanapp/web-sentry-manager": "^15.0.84",
|
||||
"@sentry/cli": "^1.52.3",
|
||||
"chai": "^4.3.4",
|
||||
"google-closure-compiler-java": "^20200224.0.0",
|
||||
|
|
@ -52,13 +52,13 @@
|
|||
"sentry-cli": "sentry-cli"
|
||||
},
|
||||
"dependencies": {
|
||||
"@keymanapp/input-processor": "^15.0.81",
|
||||
"@keymanapp/keyboard-processor": "^15.0.81",
|
||||
"@keymanapp/lexical-model-layer": "^15.0.81",
|
||||
"@keymanapp/models-types": "^15.0.81",
|
||||
"@keymanapp/recorder-core": "^15.0.81",
|
||||
"@keymanapp/web-environment": "^15.0.81",
|
||||
"@keymanapp/web-utils": "^15.0.81",
|
||||
"@keymanapp/input-processor": "^15.0.84",
|
||||
"@keymanapp/keyboard-processor": "^15.0.84",
|
||||
"@keymanapp/lexical-model-layer": "^15.0.84",
|
||||
"@keymanapp/models-types": "^15.0.84",
|
||||
"@keymanapp/recorder-core": "^15.0.84",
|
||||
"@keymanapp/web-environment": "^15.0.84",
|
||||
"@keymanapp/web-utils": "^15.0.84",
|
||||
"@types/node": "^11.9.4",
|
||||
"es6-shim": "^0.35.5",
|
||||
"eventemitter3": "^4.0.0"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// References the base Keyman object (and consequently, the rest of the core objects).
|
||||
/// <reference path="kmwbase.ts" />
|
||||
/// <reference path="osk/embedded/keytip.ts" />
|
||||
/// <reference path="osk/embedded/pendingLongpress.ts" />
|
||||
|
||||
// KeymanWeb 11.0
|
||||
// Copyright 2019 SIL International
|
||||
|
|
@ -52,17 +53,22 @@ namespace com.keyman.osk {
|
|||
*
|
||||
* @param {Object} key base key element
|
||||
*/
|
||||
VisualKeyboard.prototype.touchHold = function(this: VisualKeyboard, key: KeyElement) {
|
||||
let util = com.keyman.singleton.util;
|
||||
if(key['subKeys'] && (typeof(window['oskCreatePopup']) == 'function')) {
|
||||
VisualKeyboard.prototype.startLongpress = function(this: VisualKeyboard, key: KeyElement): PendingGesture {
|
||||
if(typeof(window['oskCreatePopup']) == 'function') {
|
||||
var xBase = dom.Utils.getAbsoluteX(key) - dom.Utils.getAbsoluteX(this.kbdDiv) + key.offsetWidth/2,
|
||||
yBase = dom.Utils.getAbsoluteY(key);
|
||||
|
||||
// #3718: No longer prepend base key to subkey array
|
||||
|
||||
this.popupBaseKey = key;
|
||||
this.popupPending=true;
|
||||
window['oskCreatePopup'](key['subKeys'], xBase, yBase, key.offsetWidth, key.offsetHeight);
|
||||
|
||||
return new embedded.PendingLongpress(this, key);
|
||||
} else {
|
||||
// When embedded within our Android app, we expect the `oskCreatePopup` function to
|
||||
// exist; all subkey control is delegated to the app.
|
||||
//
|
||||
// No function = big problem.
|
||||
console.error("Missing `oskCreatePopup` function for engine integration.");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -73,10 +79,6 @@ namespace com.keyman.osk {
|
|||
}
|
||||
};
|
||||
|
||||
VisualKeyboard.prototype.highlightSubKeys = function(this: VisualKeyboard, k, x, y) {
|
||||
// a dummy function; it's only really used for 'native' KMW.
|
||||
}
|
||||
|
||||
VisualKeyboard.prototype.waitForFonts = function(this: VisualKeyboard, kfd, ofd) {
|
||||
// a dummy function; it's only really used for 'native' KMW.
|
||||
return true;
|
||||
|
|
@ -134,8 +136,6 @@ namespace com.keyman.text {
|
|||
// Skip full page initialization - skips native-mode only code
|
||||
keymanweb.isEmbedded = true;
|
||||
|
||||
com.keyman.osk.VisualKeyboard.prototype.popupDelay = 400; // Delay must be less than native touch-hold delay
|
||||
|
||||
// Set default device options
|
||||
keymanweb.setDefaultDeviceOptions = function(opt: com.keyman.OptionType) {
|
||||
opt['attachType'] = 'manual';
|
||||
|
|
@ -270,14 +270,45 @@ namespace com.keyman.text {
|
|||
};
|
||||
|
||||
/**
|
||||
* Function called by Android and iOS when a device-implemented keyboard popup is displayed or hidden
|
||||
* Function called by Android and iOS when a device-implemented keyboard popup
|
||||
* is displayed or hidden. As this is controlled by the app, we use it as a
|
||||
* trigger for 'embedded'-mode gesture state management.
|
||||
*
|
||||
* @param {boolean} isVisible
|
||||
*
|
||||
**/
|
||||
keymanweb['popupVisible'] = function(isVisible)
|
||||
{
|
||||
osk.vkbd.popupVisible = isVisible;
|
||||
keymanweb['popupVisible'] = function(isVisible) {
|
||||
let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator;
|
||||
let pendingLongpress = osk.vkbd.pendingSubkey;
|
||||
|
||||
/*
|
||||
* If a longpress popup was visible, but is no longer, this means that the
|
||||
* associated longpress gesture was cancelled. It is possible for the base
|
||||
* key to emit if selected at this time; detecton of this is managed by
|
||||
* the `SubkeyDelegator` class.
|
||||
*/
|
||||
if(!isVisible) {
|
||||
if(gesture) {
|
||||
gesture.resolve(null);
|
||||
osk.vkbd.subkeyGesture = null;
|
||||
} else if(pendingLongpress) {
|
||||
pendingLongpress.cancel();
|
||||
osk.vkbd.pendingSubkey = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the popup was not visible, but now is, that means our previously-pending
|
||||
* longpress is now 'realized' (complete). The OSK relies upon this state
|
||||
* information, which will be properly updated by `resolve`.
|
||||
*
|
||||
* Prominent uses of such state info helps prevent change of base key, key
|
||||
* previews, and key output from occurring while a subkey popup remains active.
|
||||
*/
|
||||
if(isVisible && pendingLongpress) {
|
||||
// Fulfills the first-stage promise.
|
||||
pendingLongpress.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -318,8 +349,6 @@ namespace com.keyman.text {
|
|||
* @param {string} keyName key identifier
|
||||
**/
|
||||
keymanweb['executePopupKey'] = function(keyName: string) {
|
||||
let core = (<KeymanBase> keymanweb).core;
|
||||
|
||||
var origArg = keyName;
|
||||
if(!keymanweb.core.activeKeyboard || !osk.vkbd) {
|
||||
return false;
|
||||
|
|
@ -333,99 +362,24 @@ namespace com.keyman.text {
|
|||
|
||||
// Can't just split on '-' because some layers like ctrl-shift contain it.
|
||||
let separatorIndex = keyName.lastIndexOf('-');
|
||||
var layer = core.keyboardProcessor.layerId;
|
||||
|
||||
if (separatorIndex > 0) {
|
||||
layer = keyName.substring(0, separatorIndex);
|
||||
keyName = keyName.substring(separatorIndex+1);
|
||||
}
|
||||
if(layer == 'undefined') {
|
||||
layer=core.keyboardProcessor.layerId;
|
||||
}
|
||||
|
||||
// Note: this assumes Lelem is properly attached and has an element interface.
|
||||
// Currently true in the Android and iOS apps.
|
||||
var Lelem=keymanweb.domManager.getLastActiveElement(),keyShiftState=com.keyman.text.KeyboardProcessor.getModifierState(layer);
|
||||
|
||||
var Lelem=keymanweb.domManager.getLastActiveElement();
|
||||
keymanweb.domManager.initActiveElement(Lelem);
|
||||
|
||||
var nextLayer: string;
|
||||
|
||||
// This should be set if we're within this method... but it's best to guard against nulls here, just in case.
|
||||
if(osk.vkbd.popupBaseKey && osk.vkbd.popupBaseKey['key']) {
|
||||
// This is set with the base key of our current subkey elsewhere within the engine.
|
||||
var baseKey: com.keyman.osk.OSKKeySpec = osk.vkbd.popupBaseKey['key'].spec;
|
||||
var found = false;
|
||||
|
||||
if(baseKey.coreID == keyName) {
|
||||
nextLayer = baseKey.nextlayer;
|
||||
found = true;
|
||||
} else {
|
||||
// Search for the specified subkey so we can retrieve its useful properties.
|
||||
// It should be within the popupBaseKey's subkey list.
|
||||
for(let subKey of baseKey.sk) {
|
||||
if(subKey.coreID == keyName) {
|
||||
// ... to consider: why are we not just taking the keyspec wholesale right here?
|
||||
nextLayer = subKey.nextlayer;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
console.warn("Could not find subkey '" + origArg + "' under the current base key '" + baseKey.coreID + "'!");
|
||||
}
|
||||
if(osk.vkbd.subkeyGesture) {
|
||||
let gesture = osk.vkbd.subkeyGesture as com.keyman.osk.embedded.SubkeyDelegator;
|
||||
gesture.resolve(keyName);
|
||||
osk.vkbd.subkeyGesture = null;
|
||||
} else {
|
||||
console.warn("No base key exists for the subkey being executed: '" + origArg + "'");
|
||||
}
|
||||
|
||||
let Codes = com.keyman.text.Codes;
|
||||
|
||||
// Check the virtual key
|
||||
let Lkc: com.keyman.text.KeyEvent = {
|
||||
Lmodifiers: keyShiftState,
|
||||
Lstates: 0,
|
||||
Lcode: Codes.keyCodes[keyName],
|
||||
LisVirtualKey: true,
|
||||
kName: keyName,
|
||||
kNextLayer: nextLayer,
|
||||
vkCode: null, // was originally undefined
|
||||
isSynthetic: true,
|
||||
device: keymanweb.util.device.coreSpec
|
||||
};
|
||||
|
||||
// Process modifier key action
|
||||
if(core.keyboardProcessor.selectLayer(Lkc, true)) { // ignores key's 'nextLayer' property for this check
|
||||
return true;
|
||||
}
|
||||
|
||||
// While we can't source the base KeyEvent properties for embedded subkeys the same way as native,
|
||||
// we can handle many other pre-processing steps the same way with this common method.
|
||||
core.keyboardProcessor.setSyntheticEventDefaults(Lkc);
|
||||
|
||||
//if(!Lkc.Lcode) return false; // Value is now zero if not known (Build 347)
|
||||
//Build 353: revert to prior test to try to fix lack of KMEI output, May 1, 2014
|
||||
if(isNaN(Lkc.Lcode) || !Lkc.Lcode) {
|
||||
// Addresses modifier SHIFT keys.
|
||||
if(nextLayer) {
|
||||
core.keyboardProcessor.selectLayer(Lkc);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Lkc.vkCode=Lkc.Lcode;
|
||||
|
||||
// Now that we have a valid key event, hand it off to the Processor for execution.
|
||||
// This allows the Processor to also handle any predictive-text tasks necessary.
|
||||
let retVal = com.keyman.osk.PreProcessor.handleClick(Lkc, com.keyman.dom.Utils.getOutputTarget(Lelem), null);
|
||||
|
||||
// Special case for embedded to pass K_TAB back to device to process
|
||||
if(Lkc.Lcode == Codes.keyCodes["K_TAB"] || Lkc.Lcode == Codes.keyCodes["K_TABBACK"]
|
||||
|| Lkc.Lcode == Codes.keyCodes["K_TABFWD"]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -493,7 +493,7 @@ namespace com.keyman.osk {
|
|||
// Utilized by the mobile apps; allows them to 'take over' touch handling,
|
||||
// blocking it within KMW when the apps are already managing an ongoing touch-hold.
|
||||
let keyman = com.keyman.singleton;
|
||||
return keyman['osk'].vkbd.popupVisible;
|
||||
return keyman['osk'].vkbd.subkeyGesture && keyman.isEmbedded;
|
||||
}
|
||||
|
||||
protected dealiasSubTarget(target: HTMLDivElement): HTMLDivElement {
|
||||
|
|
|
|||
65
web/source/osk/browser/pendingLongpress.ts
Normal file
65
web/source/osk/browser/pendingLongpress.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/// <reference path="subkeyPopup.ts" />
|
||||
/// <reference path="../pendingGesture.interface.ts" />
|
||||
|
||||
namespace com.keyman.osk.browser {
|
||||
/**
|
||||
* (Conceptually) represents a finite-state-machine that determines
|
||||
* whether or not a series of touch events corresponds to a longpress
|
||||
* touch input. The `resolve` method may be used to trigger the
|
||||
* subkey menu early, as with the upward quick-display shortcut.
|
||||
*
|
||||
* This is the default implementation of longpress behavior for KMW.
|
||||
* Alterate implementations are modeled through the `embedded`
|
||||
* namespace's equivalent, which is designed to facilitate custom
|
||||
* modeling for such gestures.
|
||||
*
|
||||
* Once the conditions to recognize a longpress gesture have been
|
||||
* fulfilled, this class's `promise` will resolve with a `SubkeyPopup`
|
||||
* matching the gesture's 'base' key, which itself provides a
|
||||
* `promise` field that will resolve to a `KeyEvent` once the touch
|
||||
* sequence is completed.
|
||||
*/
|
||||
export class PendingLongpress implements PendingGesture {
|
||||
public readonly baseKey: KeyElement;
|
||||
public readonly promise: Promise<SubkeyPopup>;
|
||||
|
||||
public readonly subkeyUI: SubkeyPopup;
|
||||
|
||||
private readonly vkbd: VisualKeyboard;
|
||||
private resolver: (subkeyPopup: SubkeyPopup) => void;
|
||||
|
||||
private timerId: number;
|
||||
private popupDelay: number = 500;
|
||||
|
||||
constructor(vkbd: VisualKeyboard, baseKey: KeyElement) {
|
||||
this.vkbd = vkbd;
|
||||
this.baseKey = baseKey;
|
||||
|
||||
let _this = this;
|
||||
this.promise = new Promise<SubkeyPopup>(function(resolve, reject) {
|
||||
_this.resolver = resolve;
|
||||
// After the timeout, it's no longer deferred; it's being fulfilled.
|
||||
// Even if the actual subkey itself is still async.
|
||||
_this.timerId = window.setTimeout(_this.resolve.bind(_this), _this.popupDelay);
|
||||
});
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
if(this.timerId) {
|
||||
window.clearTimeout(this.timerId);
|
||||
this.timerId = null;
|
||||
}
|
||||
|
||||
if(this.resolver) {
|
||||
this.resolver(null);
|
||||
this.resolver = null;
|
||||
}
|
||||
}
|
||||
|
||||
public resolve() {
|
||||
if(this.resolver) {
|
||||
this.resolver(new SubkeyPopup(this.vkbd, this.baseKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,54 @@
|
|||
/// <reference path="oskSubKey.ts" />
|
||||
/// <reference path="../realizedGesture.interface.ts" />
|
||||
|
||||
namespace com.keyman.osk.browser {
|
||||
export class SubkeyPopup {
|
||||
/**
|
||||
* Represents a 'realized' longpress gesture's default implementation
|
||||
* within KeymanWeb. Once a touch sequence has been confirmed to
|
||||
* correspond to a longpress gesture, implementations of this class
|
||||
* provide the following:
|
||||
* * The UI needed to present a subkey menu
|
||||
* * The state management needed to present feedback about the
|
||||
* currently-selected subkey to the user
|
||||
* * A `Promise` that will resolve to the user's selected subkey
|
||||
* once the longpress operation is complete.
|
||||
*
|
||||
* As selection of the subkey occurs after the subkey popup is
|
||||
* displayed, selection of the subkey is inherently asynchronous.
|
||||
* The `Promise` may also resolve to `null` if the user indicates
|
||||
* the desire to cancel subkey selection.
|
||||
*/
|
||||
export class SubkeyPopup implements RealizedGesture {
|
||||
public readonly element: HTMLDivElement;
|
||||
public readonly shim: HTMLDivElement;
|
||||
|
||||
private baseKey: KeyElement;
|
||||
|
||||
private vkbd: VisualKeyboard;
|
||||
private currentSelection: KeyElement;
|
||||
|
||||
private callout: HTMLDivElement;
|
||||
|
||||
public readonly baseKey: KeyElement;
|
||||
public readonly promise: Promise<text.KeyEvent>;
|
||||
|
||||
// Resolves the promise that generated this SubkeyPopup.
|
||||
private resolver: (keyEvent: text.KeyEvent) => void;
|
||||
|
||||
constructor(vkbd: VisualKeyboard, e: KeyElement) {
|
||||
let keyman = com.keyman.singleton;
|
||||
let _this = this;
|
||||
|
||||
this.promise = new Promise<text.KeyEvent>(function(resolve) {
|
||||
_this.resolver = resolve;
|
||||
})
|
||||
|
||||
this.vkbd = vkbd;
|
||||
this.baseKey = e;
|
||||
|
||||
// If the user doesn't move their finger and releases, we'll output the base key
|
||||
// by default.
|
||||
this.currentSelection = e;
|
||||
e.key.highlight(true);
|
||||
|
||||
// A tag we directly set on a key element during its construction.
|
||||
let subKeySpec: OSKKeySpec[] = e['subKeys'];
|
||||
|
||||
|
|
@ -18,7 +56,7 @@ namespace com.keyman.osk.browser {
|
|||
// is possible while the array is visible. So it is simplest to let the keys have
|
||||
// position:static and display:inline-block
|
||||
var subKeys = this.element = document.createElement('div');
|
||||
this.baseKey = e;
|
||||
|
||||
var i;
|
||||
subKeys.id='kmw-popup-keys';
|
||||
|
||||
|
|
@ -72,6 +110,18 @@ namespace com.keyman.osk.browser {
|
|||
}
|
||||
}
|
||||
|
||||
finalize(touch: Touch) {
|
||||
if(this.resolver) {
|
||||
let keyEvent: text.KeyEvent = null;
|
||||
if(this.currentSelection) {
|
||||
keyEvent = this.vkbd.initKeyEvent(this.currentSelection, touch);
|
||||
this.currentSelection.key.highlight(false);
|
||||
}
|
||||
this.resolver(keyEvent);
|
||||
}
|
||||
this.resolver = null;
|
||||
}
|
||||
|
||||
reposition(vkbd: VisualKeyboard) {
|
||||
let keyman = com.keyman.singleton;
|
||||
|
||||
|
|
@ -181,7 +231,16 @@ namespace com.keyman.osk.browser {
|
|||
}
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.element.style.visibility == 'visible';
|
||||
}
|
||||
|
||||
clear() {
|
||||
// Discard the reference to the Promise's resolve method, allowing
|
||||
// GC to clean it up. The corresponding Promise's contract allows
|
||||
// passive cancellation.
|
||||
this.resolver = null;
|
||||
|
||||
// Remove the displayed subkey array, if any
|
||||
if(this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
|
|
@ -195,5 +254,34 @@ namespace com.keyman.osk.browser {
|
|||
this.callout.parentNode.removeChild(this.callout);
|
||||
}
|
||||
}
|
||||
|
||||
updateTouch(touch: Touch) {
|
||||
this.currentSelection = null;
|
||||
this.baseKey.key.highlight(false);
|
||||
|
||||
for(let i=0; i < this.baseKey['subKeys'].length; i++) {
|
||||
try {
|
||||
let sk = this.element.childNodes[i].firstChild as KeyElement;
|
||||
|
||||
let onKey = sk.key.isUnderTouch(touch);
|
||||
if(onKey) {
|
||||
this.currentSelection = sk;
|
||||
}
|
||||
sk.key.highlight(onKey);
|
||||
} catch(ex) {
|
||||
if(ex.message) {
|
||||
console.error("Unexpected error when attempting to update selected subkey:" + ex.message);
|
||||
} else {
|
||||
console.error("Unexpected error (and error type) when attempting to update selected subkey.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the popup duplicate of the base key if a phone with a visible popup array
|
||||
if(!this.currentSelection && this.baseKey.key.isUnderTouch(touch)) {
|
||||
this.baseKey.key.highlight(true);
|
||||
this.currentSelection = this.baseKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
48
web/source/osk/embedded/pendingLongpress.ts
Normal file
48
web/source/osk/embedded/pendingLongpress.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/// <reference path="subkeyDelegator.ts" />
|
||||
/// <reference path="../pendingGesture.interface.ts" />
|
||||
|
||||
namespace com.keyman.osk.embedded {
|
||||
/**
|
||||
* As control over the subkey display timer and the subkey popup are
|
||||
* both handled by the host app within the Android app, this class
|
||||
* serves mostly to communicate longpress state management from the
|
||||
* app to the VisualKeyboard.
|
||||
*
|
||||
* The `resolve()` function should be triggered by the host app
|
||||
* whenever it has recognized a completed longpress and has thus
|
||||
* begun displaying its subkey popup. (Should these two events
|
||||
* ever become separated in time, the latter is the more critical
|
||||
* aspect.)
|
||||
*/
|
||||
export class PendingLongpress implements PendingGesture {
|
||||
private resolver: (delegator: SubkeyDelegator) => void;
|
||||
private readonly vkbd: VisualKeyboard;
|
||||
|
||||
public readonly baseKey: KeyElement;
|
||||
public readonly promise: Promise<SubkeyDelegator>;
|
||||
|
||||
constructor(vkbd: VisualKeyboard, e: KeyElement) {
|
||||
this.vkbd = vkbd;
|
||||
let _this = this;
|
||||
|
||||
this.promise = new Promise<SubkeyDelegator>(function(resolve) {
|
||||
_this.resolver = resolve;
|
||||
});
|
||||
this.baseKey = e;
|
||||
}
|
||||
|
||||
public resolve() {
|
||||
if(this.resolver) {
|
||||
this.resolver(new SubkeyDelegator(this.vkbd, this.baseKey));
|
||||
}
|
||||
this.resolver = null;
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
if(this.resolver) {
|
||||
this.resolver(null);
|
||||
this.resolver = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
web/source/osk/embedded/subkeyDelegator.ts
Normal file
110
web/source/osk/embedded/subkeyDelegator.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/// <reference path="../realizedGesture.interface.ts" />
|
||||
|
||||
namespace com.keyman.osk.embedded {
|
||||
/**
|
||||
* As the subkey popup view is handled by the host app when in embedded mode
|
||||
* within our Android app, this class represents the fact that KMW has
|
||||
* "delegated" subkey UI and selection to the host app. Hence, "Delegator",
|
||||
* rather than "Popup".
|
||||
*
|
||||
* The `resolve` method should be triggered, in some fashion, by the host app
|
||||
* whenever the user has completed their longpress, potentially selecting
|
||||
* a subkey.
|
||||
*
|
||||
* This class will also track the ongoing touch event in case the base key is
|
||||
* reselected, which _is_ managed by this class, not the host app.
|
||||
*/
|
||||
export class SubkeyDelegator implements RealizedGesture {
|
||||
private resolver: (keyEvent: text.KeyEvent) => void;
|
||||
private readonly vkbd: VisualKeyboard;
|
||||
|
||||
public readonly baseKey: KeyElement;
|
||||
public readonly promise: Promise<text.KeyEvent>;
|
||||
|
||||
private movedFromBaseKey: boolean = false;
|
||||
private baseKeySelected: boolean = false;
|
||||
|
||||
constructor(vkbd: VisualKeyboard, e: KeyElement) {
|
||||
this.vkbd = vkbd;
|
||||
|
||||
let _this = this;
|
||||
this.promise = new Promise<text.KeyEvent>(function(resolve) {
|
||||
_this.resolver = resolve;
|
||||
});
|
||||
|
||||
this.baseKey = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the ongoing longpress -> subkey gesture, fulfilling this
|
||||
* `SubkeyDelegator`'s `promise` of a `KeyEvent`.
|
||||
*
|
||||
* If no subkey is selected but the original base key is, `resolve(null)`
|
||||
* will return a key event corresponding to the base key.
|
||||
*
|
||||
* @param keyCoreID {string} The 'core ID' (id + modifier layer) of
|
||||
* a selected subkey. May be `null`.
|
||||
*/
|
||||
public resolve(keyCoreID: string) {
|
||||
if(this.resolver) {
|
||||
let keyEvent: text.KeyEvent = null;
|
||||
|
||||
if(keyCoreID == null && this.baseKeySelected) {
|
||||
// Handle selection of base key underneath the subkey array.
|
||||
keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null);
|
||||
this.baseKey.key.highlight(false);
|
||||
} else if(keyCoreID != null) {
|
||||
// This is set with the base key of our current subkey elsewhere within the engine.
|
||||
let baseKey: OSKKeySpec = this.baseKey.key.spec;
|
||||
let selectedKey: OSKKeySpec;
|
||||
|
||||
if(baseKey.coreID == keyCoreID) {
|
||||
selectedKey = baseKey;
|
||||
} else {
|
||||
// ... yeah, there are some funky type shenanigans between the two.
|
||||
// OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type.
|
||||
selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec;
|
||||
}
|
||||
|
||||
if(!selectedKey) {
|
||||
// While we can't complete successfully, the subkey operation is done; we
|
||||
// should still signal that and update related gesture state management.
|
||||
this.resolver(null);
|
||||
console.error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!");
|
||||
return;
|
||||
}
|
||||
|
||||
keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null);
|
||||
keyEvent.vkCode=keyEvent.Lcode;
|
||||
} // else /* if(keyCoreID == null) */ keyEvent = null; // As initialized at the top.
|
||||
|
||||
this.resolver(keyEvent);
|
||||
}
|
||||
this.resolver = null;
|
||||
}
|
||||
|
||||
public isVisible(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public clear() {
|
||||
// no-op; it's fully controlled on the app side.
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows this class to detect if the user may have changed their mind and
|
||||
* re-selected the base key.
|
||||
* @param touch
|
||||
*/
|
||||
updateTouch(touch: Touch) {
|
||||
this.baseKeySelected = this.baseKey.key.isUnderTouch(touch);
|
||||
|
||||
// Prevent highlighting & selection before the touch has moved from the base key.
|
||||
if(this.movedFromBaseKey) {
|
||||
this.baseKey.key.highlight(this.baseKeySelected);
|
||||
} else {
|
||||
this.movedFromBaseKey = !this.baseKeySelected;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -430,5 +430,19 @@ namespace com.keyman.osk {
|
|||
|
||||
return t;
|
||||
}
|
||||
|
||||
public isUnderTouch(touch: Touch): boolean {
|
||||
let x = touch.clientX;
|
||||
let y = touch.clientY;
|
||||
|
||||
let btn = this.btn;
|
||||
let x0 = dom.Utils.getAbsoluteX(btn);
|
||||
let y0 = dom.Utils.getAbsoluteY(btn);//-document.body.scrollTop;
|
||||
|
||||
let x1 = x0 + btn.offsetWidth;
|
||||
let y1 = y0 + btn.offsetHeight;
|
||||
|
||||
return (x > x0 && x < x1 && y > y0 && y < y1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
web/source/osk/pendingGesture.interface.ts
Normal file
45
web/source/osk/pendingGesture.interface.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
namespace com.keyman.osk {
|
||||
/**
|
||||
* Used for evaluating potential gestures. Classes adhering to this interface
|
||||
* should be instantiated whenever the (implied) state-machine allows a new
|
||||
* touch event to mark the start of a potential new gesture.
|
||||
*
|
||||
* For example, whenever a user touches a base key and there are no "realized"
|
||||
* (fully-completed, but as-of-yet unresolved) gestures, that state allows the
|
||||
* start of a potential new longpress event.
|
||||
*
|
||||
* The role of the `PendingGesture` is complete whenever all touch-events and
|
||||
* conditions necessary for a modeled gesture have been met. As this point,
|
||||
* it should be `resolve`d, fulfilling its `promise`. This results in a
|
||||
* `RealizedGesture` appropriate for the gesture type that is used to obtain
|
||||
* the final `KeyEvent` for the overall gesture sequence.
|
||||
*
|
||||
* For example, a "longpress" is considered resolved once the user has maintained
|
||||
* an active, stationary touch point on the same key for a sufficiently long
|
||||
* period without releasing it.
|
||||
* * Were it released earlier, that would result in selection of a base key.
|
||||
*
|
||||
* Alternatively, a "flick" might be considered resolved if:
|
||||
* * a user has rapidly moved a touch point in a consistent direction
|
||||
* * for a long enough distance
|
||||
* * and _then_ releases that touch point within a short timeframe.
|
||||
*
|
||||
* The pending gesture should only `resolve` to a realized gesture once
|
||||
* _all_ such conditions are met, confirming that this specific gesture,
|
||||
* and _only_ this specific gesture, could have resulted from the active
|
||||
* touch sequence.
|
||||
*
|
||||
* The `RealizedGesture` that results and is 'returned' via the Promise will
|
||||
* be handled by the `VisualKeyboard` class, which will retrieve and forward
|
||||
* any `KeyEvent` that results from the overall gesture input sequence.
|
||||
*
|
||||
* @see `RealizedGesture`
|
||||
*/
|
||||
export interface PendingGesture {
|
||||
readonly baseKey: KeyElement;
|
||||
readonly promise: Promise<RealizedGesture>;
|
||||
|
||||
cancel(): void;
|
||||
resolve?(): void;
|
||||
}
|
||||
}
|
||||
34
web/source/osk/realizedGesture.interface.ts
Normal file
34
web/source/osk/realizedGesture.interface.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
namespace com.keyman.osk {
|
||||
/**
|
||||
* Implementations of this interface allow individual types of gestures to
|
||||
* specify any additional user interaction and functionality (which may
|
||||
* include UI elements) appropriate for obtaining a key event that may be
|
||||
* produced by the modeled gesture type. These should only be instantiated
|
||||
* once the associated `PendingLongpress` is no longer 'pending' - once it
|
||||
* has become clear that the input touch-event sequence could only correspond
|
||||
* to the modeled gesture.
|
||||
*
|
||||
* For example, when a longpress gesture completes - and hence, the user has
|
||||
* kept their finger stationary on the same key for a long enough period -
|
||||
* we display a popup view presenting subkeys corresponding to the gesture's
|
||||
* underlying element. This popup view accepts touch input and completes only
|
||||
* upon release of the ongoing touch sequence.
|
||||
*
|
||||
* Gestures are events that occur over intervals of time, and since some of them
|
||||
* will require time and user interaction after becoming 'realized', these cases
|
||||
* will be inherently async. The simplest way to model this is with `Promise`s.
|
||||
*
|
||||
* If appropriate for the modeled gesture type, an implementation may supply an
|
||||
* instantly-resolving `Promise`. This may be appropriate for modeling "flick"
|
||||
* or "swipe" gestures in the future, which may require no additional input once
|
||||
* such a gesture is fully realized.
|
||||
*/
|
||||
export interface RealizedGesture {
|
||||
readonly baseKey: KeyElement;
|
||||
readonly promise: Promise<text.KeyEvent>;
|
||||
|
||||
clear(): void;
|
||||
isVisible(): boolean;
|
||||
updateTouch(touch: Touch): void;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
/// <reference path="oskBaseKey.ts" />
|
||||
/// <reference path="keytip.interface.ts" />
|
||||
/// <reference path="browser/keytip.ts" />
|
||||
/// <reference path="browser/subkeyPopup.ts" />
|
||||
/// <reference path="browser/pendingLongpress.ts" />
|
||||
|
||||
namespace com.keyman.osk {
|
||||
|
||||
|
|
@ -41,7 +41,6 @@ namespace com.keyman.osk {
|
|||
|
||||
// State-related properties
|
||||
ddOSK: boolean = false;
|
||||
popupVisible: boolean;
|
||||
keyPending: KeyElement;
|
||||
touchPending: Touch;
|
||||
deleteKey: KeyElement;
|
||||
|
|
@ -60,14 +59,13 @@ namespace com.keyman.osk {
|
|||
touchCount: number;
|
||||
currentTarget: KeyElement;
|
||||
|
||||
// Popup key management
|
||||
popupBaseKey: KeyElement;
|
||||
popupPending: boolean = false;
|
||||
subkeyDelayTimer: number;
|
||||
popupDelay: number = 500;
|
||||
// Used by embedded-mode's globe key
|
||||
menuEvent: KeyElement; // Used by embedded-mode.
|
||||
|
||||
// Popup key management
|
||||
keytip: KeyTip;
|
||||
subkeyPopup: browser.SubkeyPopup;
|
||||
pendingSubkey: PendingGesture;
|
||||
subkeyGesture: RealizedGesture;
|
||||
|
||||
get layerId(): string {
|
||||
return this._layerId;
|
||||
|
|
@ -426,7 +424,7 @@ namespace com.keyman.osk {
|
|||
let kbdAspectRatio = layerGroup.offsetWidth / this.kbdDiv.offsetHeight;
|
||||
let baseKeyProbabilities = this.layout.getLayer(this.layerId).getTouchProbabilities(touchKbdPos, kbdAspectRatio);
|
||||
|
||||
if(!this.popupBaseKey || !this.popupBaseKey.key) {
|
||||
if(!this.subkeyGesture || !this.subkeyGesture.baseKey.key) {
|
||||
return baseKeyProbabilities;
|
||||
} else {
|
||||
// A temp-hack, as this was noted just before 14.0's release.
|
||||
|
|
@ -438,7 +436,7 @@ namespace com.keyman.osk {
|
|||
let baseMass = 1.0;
|
||||
|
||||
let baseKeyMass = 1.0;
|
||||
let baseKeyID = this.popupBaseKey.key.spec.coreID;
|
||||
let baseKeyID = this.subkeyGesture.baseKey.key.spec.coreID;
|
||||
|
||||
let popupKeyMass = 0.0;
|
||||
let popupKeyID: string = null;
|
||||
|
|
@ -514,8 +512,7 @@ namespace com.keyman.osk {
|
|||
this.cancelDelete();
|
||||
|
||||
// Prevent multi-touch if popup displayed
|
||||
var sk = document.getElementById('kmw-popup-keys');
|
||||
if((sk && sk.style.visibility == 'visible') || this.popupVisible) {
|
||||
if(this.subkeyGesture && this.subkeyGesture.isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -561,14 +558,20 @@ namespace com.keyman.osk {
|
|||
} else {
|
||||
if(this.keyPending) {
|
||||
this.highlightKey(this.keyPending, false);
|
||||
this.modelKeyClick(this.keyPending, this.touchPending);
|
||||
this.clearPopup();
|
||||
|
||||
if(this.subkeyGesture && this.subkeyGesture instanceof browser.SubkeyPopup) {
|
||||
let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup;
|
||||
subkeyPopup.updateTouch(e.changedTouches[0]);
|
||||
subkeyPopup.finalize(e.changedTouches[0]);
|
||||
} else {
|
||||
this.modelKeyClick(this.keyPending, this.touchPending);
|
||||
}
|
||||
// Decrement the number of unreleased touch points to prevent
|
||||
// sending the keystroke again when the key is actually released
|
||||
this.touchCount--;
|
||||
} else {
|
||||
// If this key has subkey, start timer to display subkeys after delay, set up release
|
||||
this.touchHold(key);
|
||||
this.initGestures(key, e.changedTouches[0]);
|
||||
}
|
||||
this.keyPending = key;
|
||||
this.touchPending = e.changedTouches[0];
|
||||
|
|
@ -583,32 +586,25 @@ namespace com.keyman.osk {
|
|||
**/
|
||||
release: (e: TouchEvent) => void = function(this: VisualKeyboard, e: TouchEvent) {
|
||||
// Prevent incorrect multi-touch behaviour if native or device popup visible
|
||||
var sk = document.getElementById('kmw-popup-keys'), t = this.currentTarget;
|
||||
var t = this.currentTarget;
|
||||
|
||||
// Clear repeated backspace if active, preventing 'sticky' behavior.
|
||||
this.cancelDelete();
|
||||
|
||||
if((sk && sk.style.visibility == 'visible')) {
|
||||
if((this.subkeyGesture && this.subkeyGesture.isVisible())) {
|
||||
// Ignore release if a multiple touch
|
||||
if(e.touches.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel (but do not execute) pending key if neither a popup key or the base key
|
||||
if((t == null) || ((t.id.indexOf('popup') < 0) && (t.id != this.popupBaseKey.id))) {
|
||||
this.highlightKey(this.keyPending,false);
|
||||
this.clearPopup();
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
if(this.subkeyGesture instanceof browser.SubkeyPopup) {
|
||||
let subkeyPopup = this.subkeyGesture as browser.SubkeyPopup;
|
||||
subkeyPopup.finalize(e.changedTouches[0]);
|
||||
}
|
||||
}
|
||||
this.highlightKey(this.keyPending,false);
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
|
||||
// Only set when embedded in our Android/iOS app. Signals that the device is handling
|
||||
// subkeys, so we shouldn't allow output for the base key.
|
||||
//
|
||||
// Note that on iOS (at least), this.release() will trigger before kmwembedded.ts's
|
||||
// executePopupKey() function.
|
||||
if(this.popupVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +627,6 @@ namespace com.keyman.osk {
|
|||
// Process and clear highlighting of pending target
|
||||
if(this.keyPending) {
|
||||
this.highlightKey(this.keyPending,false);
|
||||
|
||||
// Output character unless moved off key
|
||||
if(this.keyPending.className.indexOf('hidden') < 0 && tc > 0 && !beyondEdge) {
|
||||
this.modelKeyClick(this.keyPending, e.changedTouches[0]);
|
||||
|
|
@ -702,77 +697,29 @@ namespace com.keyman.osk {
|
|||
return;
|
||||
}
|
||||
|
||||
// Do not move over keys if device popup visible
|
||||
if(this.popupVisible) {
|
||||
if(key1 == null) {
|
||||
if(key0) {
|
||||
this.highlightKey(key0,false);
|
||||
}
|
||||
this.keyPending=null;
|
||||
this.touchPending=null;
|
||||
} else {
|
||||
if(key1 == this.popupBaseKey) {
|
||||
if(!key1.classList.contains('kmw-key-touched')) {
|
||||
this.highlightKey(key1,true);
|
||||
}
|
||||
this.keyPending = key1;
|
||||
this.touchPending = e.touches[0];
|
||||
} else {
|
||||
if(key0) {
|
||||
this.highlightKey(key0,false);
|
||||
}
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
}
|
||||
}
|
||||
// Update all gesture tracking. The function returns true if further input processing
|
||||
// should be blocked.
|
||||
if(this.updateGestures(key1, key0, e.changedTouches[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sk=document.getElementById('kmw-popup-keys');
|
||||
|
||||
// Use the popup duplicate of the base key if a phone with a visible popup array
|
||||
if(sk && sk.style.visibility == 'visible' && this.device.formFactor == 'phone' && key1 == this.popupBaseKey) {
|
||||
key1 = <KeyElement> sk.childNodes[0].firstChild;
|
||||
}
|
||||
|
||||
// Identify current touch position (to manage off-key release)
|
||||
this.currentTarget = key1;
|
||||
|
||||
// Clear previous key highlighting
|
||||
if(key0 && key1 && key1 !== key0) {
|
||||
// _Box has (most of) the useful client values.
|
||||
let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box;
|
||||
let height = this.kbdDiv.offsetHeight;
|
||||
// We need to adjust the offset properties by any offsets related to the active banner.
|
||||
|
||||
// Determine the y-threshold at which touch-cancellation should automatically occur.
|
||||
let rowCount = this.layers[this.layerIndex].row.length;
|
||||
let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row.
|
||||
var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5;
|
||||
if(key0 && e.touches[0].pageY < yMin) {
|
||||
this.highlightKey(key0,false);
|
||||
}
|
||||
|
||||
// If popup is visible, need to move over popup, not over main keyboard
|
||||
this.highlightSubKeys(key1,x,y);
|
||||
|
||||
if(sk && sk.style.visibility == 'visible') {
|
||||
// Once a subkey array is displayed, do not allow changing the base key.
|
||||
// Keep that array visible and accept no other options until the touch ends.
|
||||
if(key1 && key1.id.indexOf('popup') < 0 && key1 != this.popupBaseKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Highlight the base key on devices that do not append it to the subkey array.
|
||||
if(key1 && key1 == this.popupBaseKey && key1.className.indexOf('kmw-key-touched') < 0) {
|
||||
this.highlightKey(key1,true);
|
||||
}
|
||||
// Cancel touch if moved up and off keyboard, unless popup keys visible
|
||||
} else {
|
||||
// _Box has (most of) the useful client values.
|
||||
let _Box = this.kbdDiv.parentElement ? this.kbdDiv.parentElement : keyman.osk._Box;
|
||||
let height = this.kbdDiv.offsetHeight;
|
||||
|
||||
// Determine the y-threshold at which touch-cancellation should automatically occur.
|
||||
let rowCount = this.layers[this.layerIndex].row.length;
|
||||
let yBufferThreshold = (0.333 * height / rowCount); // Allows vertical movement by 1/3 the height of a row.
|
||||
var yMin = (this.kbdDiv && _Box) ? Math.max(5, this.kbdDiv.offsetTop - yBufferThreshold) : 5;
|
||||
if(key0 && e.touches[0].pageY < yMin) {
|
||||
this.highlightKey(key0,false);
|
||||
this.showKeyTip(null,false);
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
}
|
||||
this.showKeyTip(null,false);
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
}
|
||||
|
||||
// Replace the target key, if any, by the new target key
|
||||
|
|
@ -787,29 +734,6 @@ namespace com.keyman.osk {
|
|||
this.highlightKey(key1,true);
|
||||
}
|
||||
}
|
||||
|
||||
if(key0 && key1 && (key1 != key0) && (key1.id != '')) {
|
||||
// Display the touch-hold keys (after a pause)
|
||||
this.touchHold(key1);
|
||||
/*
|
||||
// Clear and restart the popup timer
|
||||
if(this.subkeyDelayTimer)
|
||||
{
|
||||
window.clearTimeout(this.subkeyDelayTimer);
|
||||
this.subkeyDelayTimer = null;
|
||||
}
|
||||
if(key1.subKeys != null)
|
||||
{
|
||||
this.subkeyDelayTimer = window.setTimeout(
|
||||
function()
|
||||
{
|
||||
this.clearPopup();
|
||||
this.showSubKeys(key1);
|
||||
}.bind(this),
|
||||
this.popupDelay);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
|
|
@ -945,8 +869,6 @@ namespace com.keyman.osk {
|
|||
// Turn off key highlighting (or preview)
|
||||
this.highlightKey(e,false);
|
||||
|
||||
let core = com.keyman.singleton.core; // only singleton-based ref currently needed here.
|
||||
|
||||
// Future note: we need to refactor osk.OSKKeySpec to instead be a 'tag field' for
|
||||
// keyboards.ActiveKey. (Prob with generics, allowing the Web-only parts to
|
||||
// be fully specified within the tag.)
|
||||
|
|
@ -958,7 +880,13 @@ namespace com.keyman.osk {
|
|||
console.error("OSK key with ID '" + e.id + "', keyID '" + e.keyId + "' missing needed specification");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the event object.
|
||||
return this.keyEventFromSpec(keySpec, touch);
|
||||
}
|
||||
|
||||
keyEventFromSpec(keySpec: keyboards.ActiveKey, touch?: Touch) {
|
||||
let core = com.keyman.singleton.core; // only singleton-based ref currently needed here.
|
||||
|
||||
// Start: mirrors _GetKeyEventProperties
|
||||
|
||||
|
|
@ -1074,42 +1002,15 @@ namespace com.keyman.osk {
|
|||
|
||||
clearPopup() {
|
||||
// Remove the displayed subkey array, if any, and cancel popup request
|
||||
if(this.subkeyPopup) {
|
||||
this.subkeyPopup.clear();
|
||||
this.subkeyPopup = null;
|
||||
if(this.subkeyGesture) {
|
||||
this.subkeyGesture.clear();
|
||||
this.subkeyGesture = null;
|
||||
}
|
||||
|
||||
if(this.subkeyDelayTimer) {
|
||||
window.clearTimeout(this.subkeyDelayTimer);
|
||||
this.subkeyDelayTimer = null;
|
||||
if(this.pendingSubkey) {
|
||||
this.pendingSubkey.cancel();
|
||||
this.pendingSubkey = null;
|
||||
}
|
||||
this.popupBaseKey = null;
|
||||
}
|
||||
|
||||
//#region 'native'-mode subkey handling
|
||||
/**
|
||||
* Display touch-hold array of 'sub-keys' above the currently touched key
|
||||
* @param {Object} e primary key element
|
||||
*/
|
||||
showSubKeys(e: KeyElement) {
|
||||
// Do not show subkeys if key already released
|
||||
if(this.keyPending == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear key preview if any
|
||||
this.showKeyTip(null,false);
|
||||
this.popupBaseKey = e;
|
||||
|
||||
let subKeys = this.subkeyPopup = new browser.SubkeyPopup(this, e);
|
||||
|
||||
// Otherwise append the touch-hold (subkey) array to the OSK
|
||||
let keyman = com.keyman.singleton;
|
||||
keyman.osk._Box.appendChild(subKeys.element);
|
||||
keyman.osk._Box.appendChild(subKeys.shim);
|
||||
|
||||
// Must be placed after its `.element` has been inserted into the DOM.
|
||||
subKeys.reposition(this);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
|
@ -1692,26 +1593,123 @@ namespace com.keyman.osk {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch hold key display management
|
||||
*
|
||||
* @param {Object} key base key object
|
||||
*/
|
||||
touchHold(key: KeyElement) {
|
||||
// Clear and restart the popup timer
|
||||
if(this.subkeyDelayTimer) {
|
||||
window.clearTimeout(this.subkeyDelayTimer);
|
||||
this.subkeyDelayTimer = null;
|
||||
/**
|
||||
* Starts an implementation-specific longpress gesture. Separately implemented for
|
||||
* in-browser and embedded modes.
|
||||
* @param key The base key of the longpress.
|
||||
* @returns
|
||||
*/
|
||||
startLongpress(key: KeyElement): PendingGesture {
|
||||
let _this = this;
|
||||
|
||||
// First-level object/Promise: will produce a subkey popup when the longpress gesture completes.
|
||||
// 'Returns' a second-level object/Promise: resolves when a subkey is selected or is cancelled.
|
||||
let pendingLongpress = new browser.PendingLongpress(this, key);
|
||||
pendingLongpress.promise.then(function(subkeyPopup) {
|
||||
// In-browser-specific handling.
|
||||
if(subkeyPopup) {
|
||||
// Append the touch-hold (subkey) array to the OSK
|
||||
let keyman = com.keyman.singleton;
|
||||
keyman.osk._Box.appendChild(subkeyPopup.element);
|
||||
keyman.osk._Box.appendChild(subkeyPopup.shim);
|
||||
|
||||
// Must be placed after its `.element` has been inserted into the DOM.
|
||||
subkeyPopup.reposition(_this);
|
||||
}
|
||||
});
|
||||
|
||||
return pendingLongpress;
|
||||
}
|
||||
|
||||
if(typeof key['subKeys'] != 'undefined' && key['subKeys'] != null) {
|
||||
this.subkeyDelayTimer = window.setTimeout(
|
||||
function(this: VisualKeyboard) {
|
||||
this.clearPopup();
|
||||
this.showSubKeys(key);
|
||||
}.bind(this), this.popupDelay);
|
||||
/**
|
||||
* Initializes all supported gestures given a base key and the triggering touch coordinates.
|
||||
* @param key The gesture's base key
|
||||
* @param touch The starting touch coordinates for the gesture
|
||||
* @returns
|
||||
*/
|
||||
initGestures(key: KeyElement, touch: Touch) {
|
||||
if(key['subKeys']) {
|
||||
let _this = this;
|
||||
|
||||
let pendingLongpress = this.startLongpress(key);
|
||||
if(pendingLongpress == null) {
|
||||
return;
|
||||
}
|
||||
this.pendingSubkey = pendingLongpress;
|
||||
|
||||
pendingLongpress.promise.then(function(subkeyPopup) {
|
||||
if(_this.pendingSubkey == pendingLongpress) {
|
||||
_this.pendingSubkey = null;
|
||||
}
|
||||
|
||||
if(subkeyPopup) {
|
||||
// Clear key preview if any
|
||||
_this.showKeyTip(null,false);
|
||||
|
||||
_this.subkeyGesture = subkeyPopup;
|
||||
subkeyPopup.promise.then(function(keyEvent: text.KeyEvent) {
|
||||
// Allow active cancellation, even if the source should allow passive.
|
||||
// It's an easy and cheap null guard.
|
||||
if(keyEvent) {
|
||||
PreProcessor.raiseKeyEvent(keyEvent);
|
||||
}
|
||||
_this.clearPopup();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all currently-pending and activated gestures.
|
||||
*
|
||||
* @param currentKey The key currently underneath the most recent touch coordinate
|
||||
* @param previousKey The previously-selected key
|
||||
* @param touch The current touch-coordinate for the gesture
|
||||
* @returns true if should fully capture input, false if input should 'fall through'.
|
||||
*/
|
||||
updateGestures(currentKey: KeyElement, previousKey: KeyElement, touch: Touch): boolean {
|
||||
let key0 = previousKey;
|
||||
let key1 = currentKey;
|
||||
|
||||
// Clear previous key highlighting, allow subkey controller to highlight as appropriate.
|
||||
if(this.subkeyGesture) {
|
||||
if(key0) {
|
||||
key0.key.highlight(false);
|
||||
}
|
||||
this.subkeyGesture.updateTouch(touch);
|
||||
|
||||
this.keyPending = null;
|
||||
this.touchPending = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.currentTarget = null;
|
||||
|
||||
// If popup is visible, need to move over popup, not over main keyboard
|
||||
// Could be turned into a browser-longpress specific implementation within browser.PendingLongpress?
|
||||
if(key1 && key1['subKeys'] != null) {
|
||||
// Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353)
|
||||
if((this.touchY - touch.pageY > 5) && this.pendingSubkey && this.pendingSubkey instanceof browser.PendingLongpress) {
|
||||
this.pendingSubkey.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an active popup menu (which can occur from the previous block),
|
||||
// a subkey popup exists; do not allow base key output.
|
||||
if(this.subkeyGesture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(key0 && key1 && (key1 != key0) && (key1.id != '')) {
|
||||
// While there may not be an active subkey menu, we should probably update which base key
|
||||
// is being highlighted by the current touch & start a pending longpress for it.
|
||||
this.clearPopup();
|
||||
this.initGestures(key1, touch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
optionKey(e: KeyElement, keyName: string, keyDown: boolean) {
|
||||
let keyman = com.keyman.singleton;
|
||||
|
|
@ -1731,32 +1729,6 @@ namespace com.keyman.osk {
|
|||
}
|
||||
};
|
||||
|
||||
// Manage popup key highlighting
|
||||
highlightSubKeys(k: KeyElement, x: number, y: number) {
|
||||
// Test for subkey array, return if none
|
||||
|
||||
// Issue: if `k` is itself a subkey, this won't do subkey highlighting correctly.
|
||||
// That "common case" is actually handled through _standard_ key highlighting.
|
||||
if(k == null || k['subKeys'] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Highlight key at touch position (and clear other highlighting)
|
||||
var skBox=document.getElementById('kmw-popup-keys');
|
||||
|
||||
//#region This section fills a different role than the method name would suggest.
|
||||
// Might correspond better to a 'checkInstantSubkeys' or something.
|
||||
|
||||
// Show popup keys immediately if touch moved up towards key array (KMEW-100, Build 353)
|
||||
if((this.touchY-y > 5) && skBox == null) {
|
||||
if(this.subkeyDelayTimer) {
|
||||
window.clearTimeout(this.subkeyDelayTimer);
|
||||
}
|
||||
this.showSubKeys(k);
|
||||
}
|
||||
//#endregion
|
||||
};
|
||||
|
||||
/**
|
||||
* Add (or remove) the keytip preview (if KeymanWeb on a phone device)
|
||||
*
|
||||
|
|
@ -1771,8 +1743,8 @@ namespace com.keyman.osk {
|
|||
return;
|
||||
}
|
||||
|
||||
var sk=this.subkeyPopup,
|
||||
popup = (sk && sk.element.style.visibility == 'visible')
|
||||
let sk = this.subkeyGesture;
|
||||
let popup = (sk && sk.isVisible());
|
||||
|
||||
// If popup keys are active, do not show the key tip.
|
||||
on = popup ? false : on;
|
||||
|
|
|
|||
|
|
@ -205,8 +205,6 @@ WIXHEAT=$(WIXPATH)\heat.exe
|
|||
|
||||
LINKPATH=link.exe
|
||||
|
||||
BACKUPDEFAULTS=*.map *.tds *.rsm *.dbg *.pdb *.cod
|
||||
|
||||
TORTOISEPROC=C:\Progra~1\TortoiseSVN\bin\TortoiseProc.exe
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ ROOT=c:\keyman\windows
|
|||
!include $(ROOT)\src\Defines.mak
|
||||
|
||||
#
|
||||
# The targets build, signcode, symbols, backup are standard
|
||||
# The targets build, signcode, symbols are standard
|
||||
# targets for all projects
|
||||
#
|
||||
|
||||
|
|
@ -32,18 +32,12 @@ signcode:
|
|||
|
||||
symbols:
|
||||
$(MAKE) -DTARGET=symbols $(TARGETS)
|
||||
|
||||
backup:
|
||||
$(MAKE) -DTARGET=backup $(TARGETS)
|
||||
!ELSE
|
||||
signcode:
|
||||
rem no signcode required
|
||||
|
||||
symbols:
|
||||
rem no symbols required
|
||||
|
||||
backup:
|
||||
rem no backup required
|
||||
!ENDIF
|
||||
|
||||
build-release:
|
||||
|
|
|
|||
|
|
@ -244,7 +244,6 @@ build-release-wrapper: clean-output global-versions
|
|||
$(MAKE) -DSIGNCODE_BUILD build-release
|
||||
$(MAKE) signcode
|
||||
$(MAKE) -DTARGET=test test
|
||||
$(MAKE) backup
|
||||
!endif
|
||||
|
||||
cd $(ROOT)\src\engine\inst
|
||||
|
|
@ -260,9 +259,6 @@ build-release-wrapper: clean-output global-versions
|
|||
|
||||
cd $(ROOT)\src\developer\inst
|
||||
$(MAKE)
|
||||
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
$(MAKE)
|
||||
!endif
|
||||
|
||||
build-release-wrapper-oem:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ NOTARGET_SIGNCODE=yes
|
|||
|
||||
TARGETS=devtools sentrytool tds2dbg mkver \
|
||||
buildunidata build_standards_data buildpkg test-klog delphiprojectmanager
|
||||
CLEANS=inst clean-buildtools
|
||||
CLEANS=clean-buildtools
|
||||
|
||||
!include ..\Header.mak
|
||||
|
||||
|
|
@ -40,10 +40,6 @@ mkver:
|
|||
cd $(ROOT)\src\buildtools\mkver
|
||||
$(MAKE) $(TARGET)
|
||||
|
||||
inst:
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
$(MAKE) $(TARGET)
|
||||
|
||||
buildpkg:
|
||||
cd $(ROOT)\src\buildtools\buildpkg
|
||||
$(MAKE) $(TARGET)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,4 @@ clean: def-clean
|
|||
signcode:
|
||||
$(SIGNCODE) /d "Package Installer Creator for Server" $(PROGRAM)\online\Buildpkg.exe
|
||||
|
||||
backup:
|
||||
rem #$(WZZIP) $(BUILD)\buildtools\Buildpkg.zip $(BACKUPDEFAULTS) Buildpkg.exe
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
|
|||
2
windows/src/buildtools/inst/.gitignore
vendored
2
windows/src/buildtools/inst/.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
|||
# These files generated during release build should not be committed
|
||||
copydebug.mak
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#
|
||||
# Install tools Makefile
|
||||
#
|
||||
|
||||
!include ..\..\Defines.mak
|
||||
|
||||
build: copydebug
|
||||
|
||||
clean:
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
-del copydebug.mak
|
||||
-del srcbackup.mak
|
||||
|
||||
copydebug:
|
||||
cd $(ROOT)\src
|
||||
-del $(ROOT)\src\tds_file.txt
|
||||
dir /b/s desktop\*.dbg desktop\*.pdb > $(ROOT)\src\tds_file.txt
|
||||
dir /b/s developer\*.dbg developer\*.pdb >> $(ROOT)\src\tds_file.txt
|
||||
dir /b/s engine\*.dbg engine\*.pdb >> $(ROOT)\src\tds_file.txt
|
||||
$(WZZIP) -x!vc*.pdb tds.zip @tds_file.txt
|
||||
-del $(ROOT)\src\tds_file.txt
|
||||
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
$(MKVER_U) copydebug.in copydebug.mak
|
||||
$(MAKE) -fcopydebug.mak
|
||||
|
||||
uploadsymbols:
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
$(MKVER_U) copydebug.in copydebug.mak
|
||||
$(MAKE) -fcopydebug.mak uploadsymbols
|
||||
|
||||
srcbackup:
|
||||
cd $(ROOT)\src\buildtools\inst
|
||||
$(MKVER_U) $(ROOT)\src\buildtools\inst\srcbackup.in srcbackup.mak
|
||||
make -fsrcbackup.mak
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#
|
||||
# Copy debug information to the RELEASE directory
|
||||
#
|
||||
|
||||
SIGNCODE_BUILD=1
|
||||
|
||||
!include ..\..\Defines.mak
|
||||
|
||||
copydebug:
|
||||
-mkdir $(ROOT)\release\$Version
|
||||
|
||||
echo mkdir c:\kmsymbols > $(ROOT)\src\install.bat
|
||||
echo mkdir c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
|
||||
echo copy *.tds c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
|
||||
echo copy *.dbg c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
|
||||
echo copy *.pdb c:\kmsymbols\$Version >> $(ROOT)\src\install.bat
|
||||
echo regedit /s tds.reg >> $(ROOT)\src\install.bat
|
||||
$(WZZIP) $(ROOT)\src\tds.zip $(ROOT)\src\install.bat $(ROOT)\src\buildtools\inst\tds.reg
|
||||
|
||||
rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin)
|
||||
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
|
||||
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
|
||||
|
||||
# ping localhost # delay for a few seconds
|
||||
# $(WZSE) $(ROOT)\src\tds.zip -setup -t $(ROOT)\src\buildtools\inst\tds_dialog.txt -c .\install.bat
|
||||
copy $(ROOT)\src\tds.zip $(ROOT)\release\$Version\tds-$Version.zip
|
||||
-del $(ROOT)\src\tds.zip
|
||||
|
||||
del $(ROOT)\src\install.bat
|
||||
|
||||
# wait 1 second -- gives winzip time to finish
|
||||
# ping -n 2 127.0.0.1
|
||||
# copy $(ROOT)\src\tds.exe $(ROOT)\release\$Version\tds-$Version.exe
|
||||
cd $(BUILD)
|
||||
$(WZZIP) -r $(ROOT)\release\$Version\debug-$Version.zip *.zip
|
||||
|
||||
|
||||
uploadsymbols:
|
||||
rem Disabling symsrv upload from the build agents (2018-06-19 mcdurdin)
|
||||
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\debug\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
|
||||
# "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\symstore" add /r /f $(ROOT)\bin\*.* /s c:\Tavultesoft\Debug\Symbols /t "Keyman" /v "Build $Version" /c "Release $Version"
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#
|
||||
# Copy debug information to the RELEASE directory
|
||||
#
|
||||
|
||||
!include ..\..\Defines.mak
|
||||
|
||||
srcbackup:
|
||||
-mkdir $(ROOT)\release\$Version
|
||||
cd $(ROOT)\src
|
||||
$(WZZIP) -r -x!*.ipch -x!*.sdf -x!*.db $(ROOT)\release\$Version\src-$Version.zip *.*
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
REGEDIT4
|
||||
|
||||
[HKEY_LOCAL_MACHINE\SOFTWARE\Tavultesoft\Exception]
|
||||
"SymbolPath"="c:\\kmsymbols\\"
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
Install Tavultesoft Keyman Desktop and Keyman Developer Symbol Files for Exception Reports
|
||||
|
|
@ -32,9 +32,6 @@ web:
|
|||
signcode:
|
||||
@rem
|
||||
|
||||
backup:
|
||||
@rem
|
||||
|
||||
clean:
|
||||
@rem
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\desktop\insthelp.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\desktop\insthelp.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\desktop\insthelp.zip $(BACKUPDEFAULTS) insthelp.exe
|
||||
|
||||
install:
|
||||
rem
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,4 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\desktop\kmbrowserhost.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\desktop\kmbrowserhost.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\desktop\kmbrowserhost.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmbrowserhost.exe $(WIN32_TARGET_PATH)\kmbrowserhost.dbg
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\desktop\kmconfig.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\desktop\kmconfig.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\desktop\kmconfig.zip $(BACKUPDEFAULTS) kmconfig.exe
|
||||
|
||||
test-manifest:
|
||||
# test that linked manifest exists and correct
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmconfig.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\desktop\kmshell.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\desktop\kmshell.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\desktop\kmshell.zip $(BACKUPDEFAULTS) kmshell.exe
|
||||
|
||||
test-manifest:
|
||||
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\kmshell.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\desktop\setup.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\desktop\setup.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\desktop\setup.zip $(BACKUPDEFAULTS) setup.exe
|
||||
|
||||
test-manifest:
|
||||
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\desktop\setup.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -76,10 +76,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\tike.exe /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\tike.dbg /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\tike.zip $(BACKUPDEFAULTS) tike.exe
|
||||
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\developer\tike.exe "$(INSTALLPATH_KEYMANDEVELOPER)\tike.exe"
|
||||
|
||||
|
|
|
|||
|
|
@ -44,9 +44,6 @@ build:
|
|||
signcode:
|
||||
@rem
|
||||
|
||||
backup:
|
||||
@rem
|
||||
|
||||
wrap-symbols:
|
||||
@rem
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\kmanalyze.exe /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\kmanalyze.pdb /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmanalyze.exe $(BACKUPDEFAULTS) kmanalyze.exe
|
||||
|
||||
test-manifest:
|
||||
@rem
|
||||
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.pdb /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\kmcmpdll.x64.pdb /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmcmpdll.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcmpdll.dll $(PROGRAM)\developer\kmcmpdll.x64.dll
|
||||
|
||||
test-manifest:
|
||||
@rem This target needed as dependency for TIKE and KMCMPDLL
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.dbg /t keyman-developer
|
||||
#TODO: $(SYMSTORE) $(DEBUGPATH)\developer\kmcomp.x64.dbg /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmcomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmcomp.exe $(PROGRAM)\developer\kmcomp.x64.exe
|
||||
|
||||
test-manifest:
|
||||
# test that (a) linked manifest exists and correct
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\developer\kmcomp.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\kmconvert.exe /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\kmconvert.dbg /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmconvert.zip $(BACKUPDEFAULTS) kmconvert.exe
|
||||
|
||||
test-manifest:
|
||||
# test that (a) linked manifest exists and correct
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\developer\kmconvert.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\kmdecomp.exe /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\kmdecomp.pdb /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\kmdecomp.zip $(BACKUPDEFAULTS) $(PROGRAM)\developer\kmdecomp.exe
|
||||
|
||||
test-manifest:
|
||||
@rem This target needed as dependency for KMDECOMP
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\imsample.dll /t keyman-developer
|
||||
$(SYMSTORE) $(DEBUGPATH)\developer\imsample.pdb /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\imsample.zip $(BACKUPDEFAULTS) imsample.dll
|
||||
|
||||
clean: def-clean
|
||||
$(MSBUILD) imsample.sln $(MSBUILD_CLEAN)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\developer\setup.exe /t keyman-developer
|
||||
$(SYMSTORE) $(ROOT)\src\developer\setup\setup.dbg /t keyman-developer
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\developer\devsetup.zip $(BACKUPDEFAULTS) setup.exe
|
||||
|
||||
test-manifest:
|
||||
# test that (a) linked manifest exists and correct, and (b) has uiAccess=true
|
||||
$(MT) -nologo -inputresource:$(PROGRAM)\developer\setup.exe -validate_manifest
|
||||
|
|
|
|||
|
|
@ -18,10 +18,6 @@ signcode:
|
|||
cd $(ROOT)\src\engine\inst\insthelper
|
||||
$(MAKE) signcode
|
||||
|
||||
backup:
|
||||
cd $(ROOT)\src\engine\inst\insthelper
|
||||
$(MAKE) backup
|
||||
|
||||
wrap-symbols:
|
||||
cd $(ROOT)\src\engine\inst\insthelper
|
||||
$(MAKE) symbols
|
||||
|
|
|
|||
|
|
@ -23,7 +23,4 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\inst\insthelper.dll /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\inst\insthelper.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\inst\insthelper.zip $(BACKUPDEFAULTS) insthelper.dll
|
||||
|
||||
!include ..\..\..\Target.mak
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\keyman.dll /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\keyman.dbg /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\keyman.zip $(BACKUPDEFAULTS) keyman.exe
|
||||
|
||||
keymanmenuitem.res:
|
||||
rc keymanmenuitem.rc
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\keyman32.dll /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\keyman32.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\keyman32.zip $(BACKUPDEFAULTS) keyman32.dll
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\keyman32.dll "$(INSTALLPATH_KEYMANENGINE)\keyman32.dll"
|
||||
echo You may want to manually tweak keyman-debug-etw.man and fill in $(INSTALLPATH_KEYMANENGINE)\keyman32.dll
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\keyman64.dll /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\keyman64.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\keyman64.zip $(BACKUPDEFAULTS) keyman64.dll
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\keyman64.dll "$(INSTALLPATH_KEYMANENGINE)\keyman64.dll"
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ signcode:
|
|||
wrap-symbols:
|
||||
rem no symbols
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\keymanmc.zip $(BACKUPDEFAULTS) keymanmc.dll
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\keymanmc.dll "$(INSTALLPATH_KEYMANENGINE)\keymanmc.dll"
|
||||
reg add HKLM\SYSTEM\CurrentControlSet\services\eventlog\Application\Keyman /v CategoryCount /t REG_DWORD /d 1 /f
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\keymanx64.exe /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\keymanx64.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\keymanx64.exe $(BACKUPDEFAULTS) keymanx64.exe
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\keymanx64.exe "$(INSTALLPATH_KEYMANENGINE)"
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,4 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(DEBUGPATH)\engine\kmcomapi.dbg /t keyman-engine-windows
|
||||
#TODO: $(SYMSTORE) $(PROGRAM)\engine\kmcomapi.x64.dbg /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\kmcomapi.zip $(BACKUPDEFAULTS) $(WIN32_TARGET_PATH)\kmcomapi.dll $(WIN64_TARGET_PATH)\kmcomapi.x64.dll $(WIN32_TARGET_PATH)\kmcomapi.dbg
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\kmrefresh.x64.exe /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\kmrefresh.x64.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\kmrefresh.x86.exe $(BACKUPDEFAULTS) kmrefresh.x86.exe
|
||||
$(WZZIP) $(BUILD)\engine\kmrefresh.x64.exe $(BACKUPDEFAULTS) kmrefresh.x64.exe
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\kmrefresh.x86.exe "$(INSTALLPATH_KEYMANENGINE)"
|
||||
$(COPY) $(PROGRAM)\engine\kmrefresh.x64.exe "$(INSTALLPATH_KEYMANENGINE)"
|
||||
|
|
|
|||
|
|
@ -26,10 +26,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(DEBUGPATH)\engine\kmtip.pdb /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\kmtip64.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\kmtip.zip x86\kmtip.pdb $(BACKUPDEFAULTS) x86\kmtip.dll
|
||||
$(WZZIP) $(BUILD)\engine\kmtip64.zip x64\kmtip64.pdb $(BACKUPDEFAULTS) x64\kmtip64.dll
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\kmtip.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip.dll"
|
||||
$(COPY) $(PROGRAM)\engine\kmtip64.dll "$(INSTALLPATH_KEYMANENGINE)\kmtip64.dll"
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\mcompile.exe /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\mcompile.pdb /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\mcompile.exe $(BACKUPDEFAULTS) mcompile.exe
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\mcompile.exe "$(INSTALLPATH_KEYMANENGINE)"
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\tsysinfo.exe /t keyman-engine-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\engine\tsysinfo.dbg /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\tsysinfo.zip $(BACKUPDEFAULTS) tsysinfo.exe
|
||||
|
||||
install:
|
||||
$(COPY) $(PROGRAM)\engine\tsysinfo.exe "$(INSTALLPATH_KEYMANENGINE)"
|
||||
$(COPY) $(ROOT)\src\engine\tsysinfo\*.xsl "$(INSTALLPATH_KEYMANENGINE)"
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\engine\tsysinfox64.exe /t keyman-engine-windows
|
||||
#$(SYMSTORE) $(DEBUGPATH)\engine\tsysinfox64.dbg /t keyman-engine-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\engine\tsysinfox64.zip $(BACKUPDEFAULTS) $(WIN64_TARGET_PATH)\*.map $(WIN64_TARGET_PATH)\tsysinfox64.exe
|
||||
|
||||
install:
|
||||
echo Nothing to do.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,6 @@ clean:
|
|||
signcode:
|
||||
@rem no signcode
|
||||
|
||||
backup:
|
||||
@rem
|
||||
|
||||
test-manifest:
|
||||
@rem
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,4 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\support\charident.exe /t keyman-windows
|
||||
#TODO: $(SYMSTORE) $(DEBUGPATH)\support\charident.dbg /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\support\charident.zip $(BACKUPDEFAULTS) charident.exe
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
|
|||
|
|
@ -19,7 +19,4 @@ wrap-symbols:
|
|||
$(SYMSTORE) $(PROGRAM)\support\etl2log.exe /t keyman-windows
|
||||
$(SYMSTORE) $(DEBUGPATH)\support\etl2log.pdb /t keyman-windows
|
||||
|
||||
backup:
|
||||
$(WZZIP) $(BUILD)\support\etl2log.zip $(BACKUPDEFAULTS) etl2log.exe
|
||||
|
||||
!include ..\..\Target.mak
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue