363 lines
9.8 KiB
Plaintext
Executable File
363 lines
9.8 KiB
Plaintext
Executable File
/**
|
|
* KeyboardActionManager by P.J. Janssen, www.threesixty.nl
|
|
* Feel free to reuse or modify this code, but please leave the credits in.
|
|
*/
|
|
|
|
/**
|
|
* The KeyboardAction represents a single action assigned to a shortcut key combination.
|
|
*
|
|
* The shortcut is a combination of mod_key_code and key_code. Both of these can be easily obtained from a string or flags
|
|
* through the KeyboardActionManager.getModKeyCode and similar functions.
|
|
*
|
|
* A KeyboardAction should always have a table_id, but depending on the type (Action or Macro), it can have either a persisten_id,
|
|
* or a combination of macro_name and macro_category.
|
|
*
|
|
* The run() function executes the action or macro.
|
|
*/
|
|
struct KeyboardAction
|
|
(
|
|
mod_key_code,
|
|
key_code,
|
|
|
|
table_id,
|
|
persistent_id,
|
|
|
|
macro_name,
|
|
macro_category,
|
|
|
|
|
|
fn isAction =
|
|
(
|
|
(persistent_id != undefined AND table_id != 647394);
|
|
),
|
|
|
|
fn isMacro =
|
|
(
|
|
(macro_name != undefined AND macro_category != undefined AND table_id == 647394);
|
|
),
|
|
|
|
fn run =
|
|
(
|
|
if (isAction()) then
|
|
actionMan.executeAction table_id persistent_id;
|
|
else if (isMacro()) then
|
|
macros.run macro_category macro_name;
|
|
),
|
|
|
|
|
|
fn compare a1 a2 =
|
|
(
|
|
case of
|
|
(
|
|
(a1.table_id < a2.table_id): -1
|
|
(a1.table_id > a2.table_id): 1
|
|
default: 0
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
/**
|
|
* The KeyboardActionManager struct handles reading and writing kbd files.
|
|
*
|
|
* After being instantiated, the readActions functions should be run, to load the users shortcuts.
|
|
* The actions property is an array containing all loaded actions.
|
|
*
|
|
* This struct was written for some specific needs, and not necessarily to provide a complete interface to kbd files.
|
|
*/
|
|
struct KeyboardActionManager
|
|
(
|
|
actions,
|
|
main_table_id = 0,
|
|
macro_table_id = 647394,
|
|
|
|
|
|
/**
|
|
* GET (MOD)KEYCODE FUNCTIONS
|
|
*/
|
|
|
|
-- Returns the mod_key_code based on the modifier key flags provided to the function.
|
|
fn getModKeyCode altPressed:false ctrlPressed:false shiftPressed:false =
|
|
(
|
|
local mod_key_code = 3;
|
|
if (shiftPressed) do mod_key_code = bit.or mod_key_code 4;
|
|
if (ctrlPressed) do mod_key_code = bit.or mod_key_code 8;
|
|
if (altPressed) do mod_key_code = bit.or mod_key_code 16;
|
|
|
|
-- Return mod_key_code.
|
|
mod_key_code;
|
|
),
|
|
|
|
--Returns the mod_key_code for the supplied string. Format: "ctrl+alt+x"
|
|
fn getModKeyCodeFromString key_str =
|
|
(
|
|
key_str = toUpper key_str;
|
|
local str_split = filterString key_str "+";
|
|
local mod_key_code = 3;
|
|
for key in str_split do
|
|
(
|
|
case key of
|
|
(
|
|
"SHIFT" : mod_key_code = bit.or mod_key_code 4;
|
|
"CTRL" : mod_key_code = bit.or mod_key_code 8;
|
|
"CONTROL" : mod_key_code = bit.or mod_key_code 8;
|
|
"ALT" : mod_key_code = bit.or mod_key_code 16;
|
|
)
|
|
)
|
|
|
|
-- Return mod_key_code.
|
|
mod_key_code;
|
|
),
|
|
|
|
--Returns the uppercase key_code of the first occurrence of a single character in a string with the format : "ctrl+alt+x"
|
|
fn getKeyCodeFromString key_str =
|
|
(
|
|
key_str = toUpper key_str;
|
|
local str_split = filterString key_str "+";
|
|
|
|
local notfound = true;
|
|
local key_code = 0;
|
|
for key in str_split while notfound do
|
|
(
|
|
if (key.count == 1) do
|
|
(
|
|
key_code = bit.charasint key;
|
|
notfound = false;
|
|
)
|
|
)
|
|
|
|
--Return the key_code.
|
|
key_code
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* GET / RUN ACTIONS FUNCTIONS
|
|
*/
|
|
fn getActionFromKeyCode mod_key_code key_code table_id1:undefined table_id2:undefined =
|
|
(
|
|
if (actions == undefined) do
|
|
throw "No actions loaded.";
|
|
|
|
local notfound = true;
|
|
local action;
|
|
for a in actions while notfound do
|
|
(
|
|
if (a.key_code == key_code AND a.mod_key_code == mod_key_code) do
|
|
(
|
|
if (table_id1 == undefined OR a.table_id == table_id1 OR a.table_id == table_id2) do
|
|
(
|
|
action = a;
|
|
notfound = false;
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Return found action (or undefined if no action was found).
|
|
action;
|
|
),
|
|
|
|
|
|
fn runActionFromKeyCode mod_key_code key_code table_id1:undefined table_id2:undefined =
|
|
(
|
|
local action = getActionFromKeyCode mod_key_code key_code table_id1:table_id1 table_id2:table_id2;
|
|
if (action != undefined) do
|
|
action.run();
|
|
),
|
|
|
|
|
|
fn runActionFromKeyString str =
|
|
(
|
|
local mod_key_code = getModKeyCodeFromString str;
|
|
local key_code = getKeyCodeFromString str;
|
|
runActionFromKeyCode mod_key_code key_code table_id1:main_table_id table_id2:macro_table_id;
|
|
),
|
|
|
|
|
|
|
|
/**
|
|
* ADD ACTION TO ACTIONSET
|
|
*/
|
|
fn addAction mod_key_code key_code table_id persistent_id:undefined macro_name:undefined macro_category:undefined replace:false =
|
|
(
|
|
if (persistent_id == undefined AND macro_name == undefined) do
|
|
throw "Either persistent_id or macro_name + macro_category parameter required.";
|
|
|
|
if ((macro_name != undefined AND macro_category == undefined) OR (macro_name == undefined AND macro_category != undefined)) do
|
|
throw "macro_name and macro_category have to be used together.";
|
|
|
|
if (persistent_id != undefined AND macro_name != undefined AND macro_category != undefined) do
|
|
throw "Using both persistent_id and macro_name + macro_category is not allowed.";
|
|
|
|
if (actions == undefined) do
|
|
throw "No actions defined.";
|
|
|
|
-- Actions that have to be removed are stored in this array to be removed after iteration is completed.
|
|
local removeActions = #();
|
|
|
|
-- Iterate through actions and check for conflicting actions.
|
|
for a = 1 to actions.count do
|
|
(
|
|
local action = actions[a];
|
|
|
|
if (action.table_id == table_id) do
|
|
(
|
|
local conflict = false;
|
|
|
|
-- Check for duplicate key combination.
|
|
if (action.mod_key_code == mod_key_code AND action.key_code == key_code) do conflict = true;
|
|
|
|
-- Check for duplicate persistent_id if necessary.
|
|
if (persistent_id != undefined) do
|
|
if (action.persistent_id == persistent_id) do
|
|
conflict = true;
|
|
|
|
-- Check for duplicate macro_name and macro_category if necessary.
|
|
if (macro_name != undefined AND macro_category != undefined) do
|
|
if (action.macro_name == macro_name AND action.macro_category == macro_category) do
|
|
conflict = true;
|
|
|
|
-- Flag action for removal if it is conflicting with new action and replace is true.
|
|
-- If there are conflicts and replace is false, return false.
|
|
if (conflict AND not replace) then
|
|
return false;
|
|
else if (conflict AND replace) do
|
|
append removeActions a;
|
|
)
|
|
)
|
|
|
|
-- Remove conflicting actions.
|
|
for a in removeActions do deleteItem actions a;
|
|
|
|
-- Append new action.
|
|
append actions (KeyboardAction mod_key_code:mod_key_code key_code:key_code table_id:table_id persistent_id:persistent_id macro_name:macro_name macro_category:macro_category);
|
|
|
|
--Adding was successful, return true;
|
|
true;
|
|
),
|
|
|
|
|
|
fn addActionFromKeyString key_str table_id persistent_id:undefined macro_name:undefined macro_category:undefined replace:false =
|
|
(
|
|
local mod_key_code = getModKeyCodeFromString key_str;
|
|
local key_code = getKeyCodeFromString key_str;
|
|
addAction mod_key_code key_code table_id persistent_id:persistent_id macro_name:macro_name macro_category:macro_category replace:replace;
|
|
),
|
|
|
|
|
|
|
|
/**
|
|
* WRITE ACTIONS FILE
|
|
*/
|
|
fn writeActions file:(actionMan.getKeyboardFile()) =
|
|
(
|
|
if (actions == undefined) do
|
|
throw "No actions defined.";
|
|
|
|
qsort actions KeyboardAction.compare;
|
|
|
|
--Create a backup of the file we're going to write to.
|
|
local backup_file = file + ".bak";
|
|
if ((getFileSize file) > 0) do
|
|
(
|
|
if ((getFileSize backup_file) > 0) do
|
|
deleteFile backup_file;
|
|
|
|
if (not (copyFile file backup_file)) do
|
|
throw "Unable to make a backup kbd file. This is too tricky to do without man.";
|
|
)
|
|
|
|
|
|
local kbd_fileStream = openFile file mode:"w";
|
|
if (kbd_fileStream == undefined) do
|
|
throw "Unable to write to file." kbdFile;
|
|
|
|
try (
|
|
local i = 0;
|
|
local prev_table_id;
|
|
for a in actions do
|
|
(
|
|
if (a.table_id != prev_table_id) do
|
|
i = 0;
|
|
|
|
if (a.isAction()) then
|
|
format "%=% % % %\n" i a.mod_key_code a.key_code a.persistent_id a.table_id to:kbd_fileStream;
|
|
else if (a.isMacro()) then
|
|
format "%=% % %`% %\n" i a.mod_key_code a.key_code a.macro_name a.macro_category a.table_id to:kbd_fileStream;
|
|
|
|
prev_table_id = a.table_id;
|
|
i += 1;
|
|
)
|
|
)
|
|
catch
|
|
(
|
|
-- Restore backup and throw exception.
|
|
close kbd_fileStream;
|
|
deleteFile file;
|
|
copyFile backup_file file;
|
|
--deleteFile backup_file;
|
|
|
|
throw();
|
|
)
|
|
|
|
close kbd_fileStream;
|
|
),
|
|
|
|
|
|
|
|
/**
|
|
* READ & PARSE ACTIONS FILE
|
|
*/
|
|
fn readActions kbdFile:(actionMan.getKeyboardFile()) =
|
|
(
|
|
local kbd_fileStream = openFile kbdFile mode:"rS"
|
|
|
|
if (kbd_fileStream == undefined) do
|
|
throw "Keyboard-File could not be opened." kbdFile;
|
|
|
|
actions = #();
|
|
|
|
while (not eof kbd_fileStream) do
|
|
(
|
|
local kbd_line = readLine kbd_fileStream;
|
|
local split_line = filterString kbd_line "= ";
|
|
if (split_line.count > 4) do
|
|
(
|
|
local action = KeyboardAction mod_key_code:(split_line[2] as integer) key_code:(split_line[3] as integer) table_id:(split_line[split_line.count] as integer);
|
|
|
|
if (split_line.count > 5 OR (matchPattern split_line[4] pattern:"*`*")) then
|
|
(
|
|
--Macro.
|
|
local macro = split_line[4];
|
|
if (split_line.count > 5) do
|
|
(
|
|
for i = 5 to (split_line.count - 1) do
|
|
macro += " " + split_line[i];
|
|
)
|
|
|
|
local split_macro = filterString macro "`";
|
|
action.macro_name = split_macro[1];
|
|
action.macro_category = split_macro[2];
|
|
)
|
|
else
|
|
(
|
|
--Action.
|
|
action.persistent_id = split_line[4];
|
|
)
|
|
|
|
append actions action;
|
|
)
|
|
)
|
|
|
|
close kbd_fileStream;
|
|
),
|
|
|
|
|
|
fn maxReloadKeyboardFile =
|
|
(
|
|
actionMan.loadKeyboardFile (actionMan.getKeyboardFile());
|
|
)
|
|
) |