mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-03 06:07:41 +00:00
327 lines
8.5 KiB
ObjectPascal
327 lines
8.5 KiB
ObjectPascal
{
|
|
* Keyman is copyright (C) SIL International. MIT License.
|
|
*
|
|
* Generate a visual keyboard (.kvk[s]) from a .kmx
|
|
}
|
|
unit Keyman.System.VisualKeyboardImportKMX;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Generics.Collections,
|
|
System.SysUtils,
|
|
|
|
Keyman.System.Debug.DebugCore,
|
|
Keyman.System.Debug.DebugEvent,
|
|
Keyman.System.KeymanCore,
|
|
kmxfile,
|
|
VisualKeyboard;
|
|
|
|
type
|
|
EVisualKeyboardImportKMX = class(Exception);
|
|
|
|
TVisualKeyboardImportKMX = class
|
|
private
|
|
type
|
|
TVKKey = class
|
|
vkey: Word;
|
|
kmshift, shift: Integer;
|
|
end;
|
|
private
|
|
keys: TObjectList<TVKKey>;
|
|
FVK: TVisualKeyboard;
|
|
FEvents: TDebugEventList;
|
|
FCore: TDebugCore;
|
|
FShow102Key: Boolean;
|
|
FLeftRightCtrlAlt: Boolean;
|
|
procedure GetKeyboardKeys(const AFilename: string);
|
|
function KMXShiftToActiveShift(shift: Cardinal): Cardinal;
|
|
function KMXShiftToVKShift(shift: Cardinal): Integer;
|
|
procedure ReadKeys(kfh: PKeyboardFileHeader; groupindex: Integer);
|
|
procedure Validate102Key;
|
|
procedure ValidateLeftRightCtrlAlt;
|
|
procedure AddKey(vk: TVKKey; const data: string);
|
|
procedure ImportKey(vk: TVKKey);
|
|
public
|
|
constructor Create(const AFilename: string; AVK: TVisualKeyboard);
|
|
destructor Destroy; override;
|
|
procedure ImportKeys;
|
|
|
|
property Show102Key: Boolean read FShow102Key;
|
|
property LeftRightCtrlAlt: Boolean read FLeftRightCtrlAlt;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
kmxfileconsts,
|
|
Unicode,
|
|
VKeyChars;
|
|
|
|
{ TVisualKeyboardImportKMX }
|
|
|
|
constructor TVisualKeyboardImportKMX.Create(
|
|
const AFilename: string;
|
|
AVK: TVisualKeyboard
|
|
);
|
|
begin
|
|
inherited Create;
|
|
|
|
FVK := AVK;
|
|
|
|
keys := TObjectList<TVKKey>.Create;
|
|
|
|
GetKeyboardKeys(AFilename);
|
|
|
|
FEvents := TDebugEventList.Create;
|
|
try
|
|
FCore := TDebugCore.Create(AFileName, False);
|
|
except
|
|
on E:Exception do
|
|
raise EVisualKeyboardImportKMX.Create('Could not load Core to import keyboard: '+E.Message);
|
|
end;
|
|
end;
|
|
|
|
destructor TVisualKeyboardImportKMX.Destroy;
|
|
begin
|
|
FreeAndNil(keys);
|
|
FreeAndNil(FEvents);
|
|
FreeAndNil(FCore);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TVisualKeyboardImportKMX.ImportKeys;
|
|
var
|
|
key: TVKKey;
|
|
begin
|
|
for key in keys do
|
|
ImportKey(key);
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
- Send a keystroke for processing -
|
|
------------------------------------------------------------------------------}
|
|
|
|
procedure TVisualKeyboardImportKMX.ImportKey(vk: TVKKey);
|
|
var
|
|
data: string;
|
|
i: Integer;
|
|
context: pkm_kbp_context;
|
|
begin
|
|
context := km_kbp_state_context(FCore.State);
|
|
km_kbp_context_clear(context);
|
|
if km_kbp_process_event(FCore.State, vk.vkey, vk.kmshift, 1) = KM_KBP_STATUS_OK then
|
|
begin
|
|
FEvents.Clear;
|
|
FEvents.AddStateItems(FCore.State, vk.vkey, vk.kmshift);
|
|
|
|
data := '';
|
|
for i := 0 to FEvents.Count - 1 do
|
|
begin
|
|
if FEvents[i].EventType <> etAction then
|
|
begin
|
|
Continue;
|
|
end;
|
|
|
|
case FEvents[i].Action.ActionType of
|
|
KM_KBP_IT_CHAR:
|
|
data := data + FEvents[i].Action.Text;
|
|
KM_KBP_IT_BACK:
|
|
if data.Length > 0 then
|
|
begin
|
|
if (data.Length > 1) and
|
|
Uni_IsSurrogate1(data[data.Length-1]) and
|
|
Uni_IsSurrogate2(data[data.Length])
|
|
then Delete(data, data.Length - 1, 2)
|
|
else Delete(data, data.Length, 1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Trim(data) <> '' then
|
|
begin
|
|
AddKey(vk, data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
- Process the results of the keystroke -
|
|
------------------------------------------------------------------------------}
|
|
|
|
procedure TVisualKeyboardImportKMX.AddKey(vk: TVKKey; const data: string);
|
|
var
|
|
k: TVisualKeyboardKey;
|
|
n: Integer;
|
|
begin
|
|
n := FVK.Keys.IndexOf(vk.vkey, vk.shift);
|
|
if n < 0
|
|
then k := TVisualKeyboardKey.Create
|
|
else k := FVK.Keys[n];
|
|
k.Flags := [kvkkUnicode];
|
|
k.VKey := vk.vkey;
|
|
k.Shift := vk.shift;
|
|
k.Text := data;
|
|
if n < 0 then FVK.Keys.Add(k);
|
|
end;
|
|
|
|
{-------------------------------------------------------------------------------
|
|
- Read a list of keys from the keyboard -
|
|
------------------------------------------------------------------------------}
|
|
|
|
procedure TVisualKeyboardImportKMX.GetKeyboardKeys(const AFilename: string);
|
|
var
|
|
ki: TKeyboardInfo;
|
|
i: Integer;
|
|
kfh: PKeyboardFileHeader;
|
|
begin
|
|
try
|
|
GetKeyboardInfo(AFileName, True, ki);
|
|
except
|
|
on E:Exception do
|
|
raise EVisualKeyboardImportKMX.Create('Unable to load keyboard: '+E.Message);
|
|
end;
|
|
|
|
try
|
|
kfh := PKeyboardFileHeader(ki.MemoryDump.Memory);
|
|
for i := 0 to kfh.cxGroupArray - 1 do
|
|
ReadKeys(kfh, i);
|
|
finally
|
|
ki.MemoryDump.Free;
|
|
end;
|
|
|
|
ValidateLeftRightCtrlAlt;
|
|
Validate102Key;
|
|
end;
|
|
|
|
procedure TVisualKeyboardImportKMX.Validate102Key;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FShow102Key := False;
|
|
|
|
for i := 0 to keys.Count - 1 do
|
|
if TVKKey(keys[i]).vkey = KM_KBP_VKEY_oE2 then
|
|
begin
|
|
FShow102Key := True;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
procedure TVisualKeyboardImportKMX.ValidateLeftRightCtrlAlt;
|
|
var
|
|
i: Integer;
|
|
FLR: Boolean;
|
|
vk: TVKKey;
|
|
begin
|
|
FLR := False;
|
|
for i := 0 to keys.Count - 1 do
|
|
begin
|
|
vk := TVKKey(keys[i]);
|
|
if (vk.shift and (KVKS_LALT or KVKS_RALT or KVKS_LCTRL or KVKS_RCTRL)) <> 0 then
|
|
begin
|
|
FLR := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
if FLR then
|
|
for i := 0 to keys.Count - 1 do
|
|
begin
|
|
vk := TVKKey(keys[i]);
|
|
if (vk.shift and KVKS_ALT) <> 0 then
|
|
vk.shift := (vk.shift and (not KVKS_ALT)) or KVKS_LALT;
|
|
if (vk.shift and KVKS_CTRL) <> 0 then
|
|
vk.shift := (vk.shift and (not KVKS_CTRL)) or KVKS_LCTRL;
|
|
end;
|
|
|
|
FLeftRightCtrlAlt := FLR;
|
|
end;
|
|
|
|
function TVisualKeyboardImportKMX.KMXShiftToVKShift(shift: Cardinal): Integer;
|
|
begin
|
|
Result := 0;
|
|
if (shift and KMX_LCTRLFLAG) <> 0 then Result := Result or KVKS_LCTRL;
|
|
if (shift and KMX_RCTRLFLAG) <> 0 then Result := Result or KVKS_RCTRL;
|
|
if (shift and KMX_LALTFLAG) <> 0 then Result := Result or KVKS_LALT;
|
|
if (shift and KMX_RALTFLAG) <> 0 then Result := Result or KVKS_RALT;
|
|
if (shift and KMX_SHIFTFLAG) <> 0 then Result := Result or KVKS_SHIFT;
|
|
if (shift and KMX_CTRLFLAG) <> 0 then Result := Result or KVKS_CTRL;
|
|
if (shift and KMX_ALTFLAG) <> 0 then Result := Result or KVKS_ALT;
|
|
end;
|
|
|
|
function TVisualKeyboardImportKMX.KMXShiftToActiveShift(shift: Cardinal): Cardinal;
|
|
begin
|
|
Result := 0;
|
|
if (shift and KMX_LCTRLFLAG) <> 0 then Result := Result or KMX_LCTRLFLAG;
|
|
if (shift and KMX_RCTRLFLAG) <> 0 then Result := Result or KMX_RCTRLFLAG;
|
|
if (shift and KMX_LALTFLAG) <> 0 then Result := Result or KMX_LALTFLAG;
|
|
if (shift and KMX_RALTFLAG) <> 0 then Result := Result or KMX_RALTFLAG;
|
|
if (shift and KMX_SHIFTFLAG) <> 0 then Result := Result or KMX_SHIFTFLAG;
|
|
if (shift and KMX_CTRLFLAG) <> 0 then Result := Result or KMX_LCTRLFLAG;
|
|
if (shift and KMX_ALTFLAG) <> 0 then Result := Result or KMX_LALTFLAG;
|
|
end;
|
|
|
|
procedure TVisualKeyboardImportKMX.ReadKeys(kfh: PKeyboardFileHeader; groupindex: Integer);
|
|
var
|
|
gp: PKeyboardFileGroup;
|
|
kp: PKeyboardFileKey;
|
|
vkey, kmshift, shift, i, j: Integer;
|
|
vk: TVKKey;
|
|
Found: Boolean;
|
|
usvkey: TUSVKey;
|
|
begin
|
|
gp := PKeyboardFileGroup(NativeUInt(kfh)+kfh.dpGroupArray+NativeUInt(groupindex)*sizeof(TKeyboardFileGroup));
|
|
if not gp.fUsingKeys then Exit;
|
|
|
|
kp := PKeyboardFileKey(NativeUInt(kfh)+gp.dpKeyArray);
|
|
for i := 0 to Integer(gp.cxKeyArray) - 1 do
|
|
begin
|
|
Found := False;
|
|
if (kp.ShiftFlags and KMX_ISVIRTUALKEY) = 0 then
|
|
begin
|
|
usvkey := CharToUSVKey(AnsiChar(kp.Key));
|
|
vkey := usvkey.VKey;
|
|
if usvkey.IsShift
|
|
then kmshift := KMX_SHIFTFLAG
|
|
else kmshift := 0;
|
|
shift := KMXShiftToVKShift(kmshift);
|
|
end
|
|
else
|
|
begin
|
|
vkey := kp.Key;
|
|
shift := KMXShiftToVKShift(kp.ShiftFlags);
|
|
kmshift := KMXShiftToActiveShift(kp.ShiftFlags);
|
|
end;
|
|
|
|
if (vkey > 255) or (vkey = 0) then
|
|
begin
|
|
// We don't try and import keys that are T_ touch virtual keys or any
|
|
// unrecognised keys (vkey = 0)
|
|
Inc(kp);
|
|
Continue;
|
|
end;
|
|
|
|
for j := 0 to keys.Count - 1 do
|
|
if (TVKKey(keys[j]).VKey = vkey) and (TVKKey(keys[j]).Shift = shift) then
|
|
begin
|
|
Found := True;
|
|
Break;
|
|
end;
|
|
if Found then
|
|
begin
|
|
Inc(kp);
|
|
Continue;
|
|
end;
|
|
|
|
vk := TVKKey.Create;
|
|
vk.VKey := vkey;
|
|
vk.Shift := shift;
|
|
vk.kmshift := kmshift;
|
|
keys.Add(vk);
|
|
|
|
Inc(kp);
|
|
end;
|
|
end;
|
|
|
|
end.
|