spiegel-keyman/developer/src/tike/main/Keyman.System.VisualKeyboardImportKMX.pas

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.