diff --git a/developer/src/kmc-convert/data/MySample.keylayout b/developer/src/kmc-convert/data/MySample.keylayout index 23191ad7eb..2aa7e67fb2 100644 --- a/developer/src/kmc-convert/data/MySample.keylayout +++ b/developer/src/kmc-convert/data/MySample.keylayout @@ -77,6 +77,17 @@ + + + + + + + + + + + diff --git a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts index a315edd86d..841ec61d3d 100644 --- a/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts +++ b/developer/src/kmc-convert/src/keylayout-to-kmn/keylayout-to-kmn-converter.ts @@ -21,6 +21,13 @@ export class KeylayoutToKmnConverter { // be available as class properties } +/** + * @brief run read/convert/write + * @param inputFilename the ukelele .keylayout-file to be converted + * @param outputFilename the resulting keyman .kmn-file + * @param binaryData ... _S2 + * @return + */ async run(inputFilename: string, outputFilename: string, binaryData: Uint8Array): Promise { if(!inputFilename || !outputFilename || !binaryData) { @@ -51,86 +58,101 @@ export class KeylayoutToKmnConverter { throw new Error('Not finished yet'); } -// ............................................................................................................ -// ............................................................................................................ -// ............................................................................................................ +/** + * @brief read filename ( a .keylayout-file) and write contents into Uint8Array keys_all_Layers + * @param filename the ukelele .keylayout-file to be converted + * @return in case of success Uint8Array keys_all_Layers; else null + */ public read(filename: string):Uint8Array[] { -/* -- // _S2 TODO and what about deadkeys??? -- // _S2 TODO which format to use in output ? + [K_A] > 'a' (character code) or + [K_A] > U+0061 (virt Keycode) -- // _S2 which stores? -- // _S2 how to use names of shiftstates*/ + /* + // _S2 TODO and what about deadkeys??? + // _S2 TODO which format to use in output ? + [K_A] > 'a' (character code) or + [K_A] > U+0061 (virt Keycode) + // _S2 which stores? + */ const options = { ignoreAttributes: false, - attributeNamePrefix: '@_', // you have to assign this to use this to access the attribute + attributeNamePrefix: '@_', // to access the attribute }; - - //xml file from https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms762271(v=vs.85) const xmlFile = readFileSync(`${process.cwd()}/data/MySample.keylayout`, 'utf8'); const parser = new XMLParser(options); const jsonObj = parser.parse(xmlFile); // get plain Object const nrOfStates = jsonObj.keyboard.keyMapSet[0].keyMap.length - const nrOfKeys = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length + const nrOfKeys_inLayer = jsonObj.keyboard.keyMapSet[0].keyMap[0].key.length + // create Object:any for storing Uint8tarray will be array[Uint8Array] Uint8Array might have several numbers (unicode) - const Keys_all_Layers :any[] = [] + // all keys for all layers + const keys_all_Layers :any[] = [] + const modifier_array: any[] = [] // why not put modifiers into Uint8Array along with values of keys of layer for (let j = 0; j < nrOfStates; j++) { + + modifier_array[j] = jsonObj.keyboard.modifierMap.keyMapSelect[j].modifier['@_keys'] + // create a new keys_in_Layer (type Uint8tarray) - const keys_in_Layer :Uint8Array[] = [] - for (let i = 0; i < nrOfKeys; i++) { - if(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output'] !== "\0" ) { - // textencoder converts string -> bytes ( 'A' -> [ 65 ], '☺' -> [ 226, 152, 186 ]) - // textencoder is of Uint8Array(1) for A and Uint8Array(3) for ☺ - const textencoder = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output' ]); - keys_in_Layer[i] = textencoder - } + const keys_single_Layer :Uint8Array[] = [] + + for (let i = 0; i < nrOfKeys_inLayer; i++) { + if(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output'] !== "\0" ) { + // textencoder converts string -> bytes ( 'A' -> [ 65 ], '☺' -> [ 226, 152, 186 ]) + // textencoder is of Uint8Array(1) for A and Uint8Array(3) for ☺ + keys_single_Layer[i] = new TextEncoder().encode(jsonObj.keyboard.keyMapSet[0].keyMap[j].key[i]['@_output' ]); } - // add [nrOfStates] Objects:Uint8tarray to Keys_all_Layers - Keys_all_Layers[j] = keys_in_Layer - } - - console.log(" _S2 read finished\n") - return(Keys_all_Layers.length === nrOfStates && Keys_all_Layers[0].length === nrOfKeys) ? Keys_all_Layers : null; -} - -// ............................................................................................................ - -public convert(inArray: any[]):any[] { - - const outArray :any[] = []; - const nrOfMaxKeys = 50 - - for (let j = 0; j < inArray.length ; j++) { - const outkeys_in_Layer :Uint8Array[] = [] - - // initialize keys - for (let i = 0; i < nrOfMaxKeys; i++) { - outkeys_in_Layer[i] = new TextEncoder().encode("\0"); } - // now convert - outkeys_in_Layer[0] = inArray[j][0]; - outkeys_in_Layer[24] = inArray[j][1]; - outkeys_in_Layer[2] = inArray[j][2]; - outkeys_in_Layer[1] = inArray[j][3]; - outkeys_in_Layer[16] = inArray[j][4]; - outkeys_in_Layer[29] = inArray[j][5]; - outkeys_in_Layer[38] = inArray[j][6]; - outkeys_in_Layer[46] = inArray[j][7]; - - outArray[j] = outkeys_in_Layer + keys_all_Layers[j] = keys_single_Layer } - console.log(" _S2 convert finished\n") - return (outArray.length === inArray.length ) ? outArray : null + // add the modifiers-> better idea? + keys_all_Layers[nrOfStates] = modifier_array + + // Keys_all_Layers needs to have nrOfStates entries + 1 for modifiers + return((keys_all_Layers.length === nrOfStates + 1) && keys_all_Layers[0].length === nrOfKeys_inLayer) ? keys_all_Layers : null; } -public write(writeArray: any[]):boolean { +/** + * @brief convert data of .keylayout-file to kmn-file This will convert/rename modifiers, position of Keys and deadkeys and save into an array + * @param data_ukelele (Uint8Array) data of the ukelele .keylayout-file + * @return outArray Uint8Array keys_all_Layers, the converted data for kmn-files if all layers have been converted; else null + */ + public convert(data_ukelele: any[]):any[] { + // sorting order: alphabetically OK? - const KeyArray = [ + const data_kmn :any[] = []; + + for (let j = 0; j < data_ukelele.length-1 ; j++) { + const keys_singleLayer :Uint8Array[] = [] + + // now convert & sort + keys_singleLayer[0] = data_ukelele[j][0]; + keys_singleLayer[24] = data_ukelele[j][1]; + keys_singleLayer[2] = data_ukelele[j][2]; + keys_singleLayer[1] = data_ukelele[j][3]; + keys_singleLayer[16] = data_ukelele[j][4]; + keys_singleLayer[29] = data_ukelele[j][5]; + keys_singleLayer[38] = data_ukelele[j][6]; + keys_singleLayer[46] = data_ukelele[j][7]; + // later more here... + + data_kmn[j] = keys_singleLayer + } + + //copy the modifier states + data_kmn[data_ukelele.length - 1] = data_ukelele[data_ukelele.length - 1] + console.log(" _S2 convert finished\n", data_kmn + ) + + return (data_kmn.length === data_ukelele.length ) ? data_kmn : null + } + +/** @brief write data to file + * @param kmn_array the array holding keyboard data + * @return true if data has been written; false if not + */ + public write(kmn_array: any[]):boolean { + const kmn_Key_Name = [ 'K_A','K_B','K_C','K_D','K_E','K_F','K_G','K_H','K_I','K_J','K_K','K_L','K_M','K_N','K_O','K_P','K_Q','K_R','K_S','K_T','K_U','K_V','K_W','K_X','K_Y','K_Z', 'K_0','K_1','K_2','K_3','K_4','K_5','K_6','K_7','K_8','K_9', 'K_SPACE', @@ -156,16 +178,92 @@ public write(writeArray: any[]):boolean { data += "group(main) using keys\n" data += "\n" - for (let i = 0; i < writeArray.length; i++) { - for (let j = 0; j < writeArray[0].length; j++) { - const textdecoder = new TextDecoder().decode(writeArray[i][j]) - if (textdecoder !== '\0' ) - data += `+ [` + KeyArray[j] + `] > \'` + textdecoder +'\'\n' + for (let i = 0; i < kmn_array.length-1; i++) { + for (let j = 0; j < kmn_array[0].length; j++) { + + // get the modifier for the layer + const label_modifier = this.create_modifier(kmn_array[kmn_array.length-1][i]) + // get the character that will be written as result in kmn-file + const resulting_character = new TextDecoder().decode(kmn_array[i][j]) + + // remove if-stmt later, only here for better visability + if (resulting_character !== '') + data += `+ [` + label_modifier + ' ' + kmn_Key_Name[j] + `] > \'` + resulting_character +'\'\n' + } data += '\n' } + writeFileSync("data/MyResult.kmn", data, { flag: "w"}) console.log(" _S2 write finished\n") - return true; + + if( data.length > 0) + return true; + else + return false } -} \ No newline at end of file + +/** + * @brief check if search_modifier is available in modifier_array + * @param search_modifier the value that will be searched for in modifier_array + * @param modifier_array the array holding all modifiers used + * @return true if search_modifier is found; false if not + */ + public isInArray(search_modifier:string, modifier_array: string[]):boolean { + for (let i =0; i < modifier_array.length; i++) { + if (search_modifier === String(modifier_array[i])) + return true + } + return false + } + +/** + * @brief create modifier in kmn-style from modifier of .keylayout-file + * @param keylayout_modifier the modifier value used in the .keylayout-file + * @return kmn_modifier the modifier value used in the .kmn-file + */ + public create_modifier(keylayout_modifier:any):string { + let add_modifier = "" + let kmn_modifier = "" + + // copy each modifier into element of array modifier_state + const modifier_state: string[] = keylayout_modifier.split(" "); + + // TODO review these conditions-> what else do I need? + // TODO spelling entries of .keylayout( uppercase/lowercase, control<->ctrl,...) + // TODO anyOption? + // opt?+ LOPT?+ ROPT? -> what will be result?? + + for (let i = 0; i < modifier_state.length; i++) { + + if ( String(modifier_state[i]) === "anyOption") add_modifier = "OPT " + else if ( String(modifier_state[i]) === "anyShift") add_modifier = "SHIFT " + else if ( String(modifier_state[i]) === "anyControl") add_modifier = "CTRL " + + else if ( String(modifier_state[i]) === "anyOption?") add_modifier = "" // does anyOption?... happen? + else if ( String(modifier_state[i]) === "anyShift?") add_modifier = "" + else if ( String(modifier_state[i]) === "anyControl?") add_modifier = "" + + else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('rshift',modifier_state)) add_modifier = "RSHIFT " + else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('lshift',modifier_state)) add_modifier = "LSHIFT " + else if( (String(modifier_state[i]) === "shift?" ) && this.isInArray('rshift',modifier_state) && this.isInArray('lshift',modifier_state)) add_modifier = "SHIFT " + + else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('rOption',modifier_state)) add_modifier = "ROPT " + else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('lOption',modifier_state)) add_modifier = "LOPT " + else if( (String(modifier_state[i]) === "option?" ) && this.isInArray('rOption',modifier_state) && this.isInArray('lOption',modifier_state)) add_modifier = "OPT " + + else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('rControl',modifier_state)) add_modifier = "RCTRL " + else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('lControl',modifier_state)) add_modifier = "LCTRL " + else if( (String(modifier_state[i]) === "ctrl?" ) && this.isInArray('rControl',modifier_state) && this.isInArray('lControl',modifier_state)) add_modifier = "CTRL " + + else if( String(modifier_state[i]) === "caps?") add_modifier = "" // capital letters or not? + else if( String(modifier_state[i]) === "shift?") add_modifier = "" + else if( String(modifier_state[i]) === "ctrl?") add_modifier = "" + + else add_modifier = String(modifier_state[i]) + + kmn_modifier += add_modifier + } + return kmn_modifier.trim().toUpperCase() + } +}