spiegel-keyman/developer/src/kmcmpdll/CheckNCapsConsistency.cpp
Marc Durdin e5b31669af chore(developer): move and rename compiler.h
The name compiler.h was misleading, so renamed to legacy_kmx_file.h, as
it largely describes .kmx files. There are two declarations in the file
which are specific to kmcmpdll, at the bottom of the file, but
everything else is all related to the .kmx file format. (We may move
those in a future update to kmcmpdll, but that should wait until the
kmcmpdll cross-platform refactor is complete.)

Moved to common/windows/cpp/include/ because it is used by both
Developer and Windows projects.

Where possible, removed redundant references, as compfile.h includes
legacy_kmx_file.h anyway.
2022-06-12 06:31:38 +10:00

98 lines
3.1 KiB
C++

#include "pch.h"
#include <compfile.h>
#include <comperr.h>
#include <kmcmpdll.h>
#include "CharToKeyConversion.h"
/**
* If any rule uses CAPS or NCAPS for a given key, then every rule that
* uses that key must also use CAPS or NCAPS, as otherwise the results are
* inconsistent. For example, if Caps Lock is on, then [K_F] may still be
* matched:
*
* + [K_F] > 'foo'
* + [CAPS K_F] > 'bar'
*
* Because of the way that KeymanWeb compiles rules, this may even apply when
* we have a key rule that would otherwise be ignored due to context. In the
* following example, [Caps Lock] + [y] would result in no output (or default
* output) rather than 'bar', because the preceding rule would capture the
* Y key, ignoring Caps Lock, meaning that the subsequent rule would never
* even get tested. (Note that this was introduced in the KeymanWeb compiler
* fix for extremely long if/else ladders in Keyman 12 in #1561, and this is
* technically slightly inconsistent with Keyman Core, although in my analysis
* only in this already ambiguous situation.
*
* 'x' + [K_Y] > 'foo'
* [CAPS K_Y] > 'bar'
*
* Given all this, we'll warn any time we find a key that has inconsistent use
* of CAPS/NCAPS in its rules.
*
* @param fk Keyboard to check
*/
BOOL CheckNCapsConsistency(PFILE_KEYBOARD fk) {
struct CapsUsage {
int ncaps_line, caps_line, neither_line;
};
// 256 virtual key codes + sizeof the virtual key dictionary is max key code possible
const int nkeys = 256 + fk->cxVKDictionary;
const int oldCurrentLine = currentLine;
auto caps_ncaps_usage = new CapsUsage [nkeys];
memset(caps_ncaps_usage, 0, nkeys * sizeof(CapsUsage));
PFILE_GROUP gp;
DWORD gn;
for (gn = 0, gp = fk->dpGroupArray; gn < fk->cxGroupArray; gn++, gp++) {
if (!gp->fUsingKeys) {
continue;
}
PFILE_KEY kp;
DWORD kn;
for (kn = 0, kp = gp->dpKeyArray; kn < gp->cxKeyArray; kn++, kp++) {
UINT key;
UINT shift;
if (kp->ShiftFlags & ISVIRTUALKEY) {
if (kp->Key >= nkeys) {
assert(false);
continue;
}
key = kp->Key;
shift = kp->ShiftFlags;
}
else if (!MapUSCharToVK(kp->Key, &key, &shift)) {
// Not a valid key
continue;
}
if (shift & NOTCAPITALFLAG) {
if (!caps_ncaps_usage[key].ncaps_line) caps_ncaps_usage[key].ncaps_line = (kp->Line == 0 ? 1 : kp->Line);
}
else if (shift & CAPITALFLAG) {
if (!caps_ncaps_usage[key].caps_line) caps_ncaps_usage[key].caps_line = (kp->Line == 0 ? 1 : kp->Line);
}
else {
if (!caps_ncaps_usage[key].neither_line) caps_ncaps_usage[key].neither_line = (kp->Line == 0 ? 1 : kp->Line);
}
}
}
for (int i = 0; i < nkeys; i++) {
if (caps_ncaps_usage[i].neither_line && (caps_ncaps_usage[i].caps_line || caps_ncaps_usage[i].ncaps_line)) {
// We set the current line to one needing work: the developer should add the NCAPS flag
currentLine = caps_ncaps_usage[i].neither_line;
AddWarning(CWARN_KeyShouldIncludeNCaps);
}
}
delete[] caps_ncaps_usage;
currentLine = oldCurrentLine;
return TRUE;
}