spiegel-keyman/core/src/action.cpp
Marc Durdin cd428ae412 feat(core): address review comments
* Renamed various functions and types
* Clarified return values
* Updated doc comments
2023-10-24 12:22:34 +07:00

135 lines
4.3 KiB
C++

/*
Copyright: © 2023 SIL International.
Description: Implementation of the action API functions using internal
data structures and functions.
Create Date: 23 Oct 2023
Authors: Marc Durdin (MCD)
History: 23 Oct 2023 - MCD - Initial implementation from #9720
*/
#include <cassert>
#include <algorithm>
#include <sstream>
#include <keyman/keyman_core_api.h>
#include "action.hpp"
#include "state.hpp"
#include "option.hpp"
km_core_actions * km::kbp::action_item_list_to_actions_object(
km_core_action_item const *action_items
) {
assert(action_items != nullptr);
if(action_items == nullptr) {
return nullptr;
}
km_core_status status = KM_CORE_STATUS_OK;
km_core_actions* actions = new km_core_actions;
// Set actions defaults
std::vector<km_core_context_item> output;
std::vector<km_core_option_item> options;
actions->delete_back_codepoints = 0;
actions->output = nullptr;
actions->persist_options = nullptr;
actions->do_alert = KM_CORE_FALSE;
actions->emit_keystroke = KM_CORE_FALSE;
actions->new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
for (; action_items->type != KM_CORE_IT_END; ++action_items) {
assert(action_items->type < KM_CORE_IT_MAX_TYPE_ID);
switch(action_items->type) {
case KM_CORE_IT_ALERT:
actions->do_alert = KM_CORE_TRUE;
break;
case KM_CORE_IT_BACK:
switch(action_items->backspace.expected_type) {
case KM_CORE_BT_UNKNOWN:
// this is equivalent to emit_keystroke, because the only time we
// are allowed to do an unknown bksp is when a bksp is passed in
actions->emit_keystroke = KM_CORE_TRUE;
break;
case KM_CORE_BT_CHAR:
if(output.empty()) {
actions->delete_back_codepoints++;
} else {
auto last_context_item = output.back();
output.pop_back();
assert(last_context_item.type == KM_CORE_CT_CHAR);
assert(last_context_item.character == action_items->backspace.expected_value);
}
break;
case KM_CORE_BT_MARKER:
if(output.empty()) {
// deleting a marker has no effect on the application
} else {
auto last_context_item = output.back();
output.pop_back();
assert(last_context_item.type == KM_CORE_CT_MARKER);
assert(last_context_item.marker == action_items->backspace.expected_value);
}
break;
default:
assert(false);
}
break;
case KM_CORE_IT_CAPSLOCK:
actions->new_caps_lock_state = action_items->capsLock ? KM_CORE_CAPS_ON : KM_CORE_CAPS_OFF;
break;
case KM_CORE_IT_CHAR:
output.push_back({KM_CORE_CT_CHAR,{0},{action_items->character}});
break;
case KM_CORE_IT_EMIT_KEYSTROKE:
actions->emit_keystroke = KM_CORE_TRUE;
break;
case KM_CORE_IT_INVALIDATE_CONTEXT:
// no-op
break;
case KM_CORE_IT_MARKER:
output.push_back({KM_CORE_CT_MARKER,{0},{action_items->marker}});
break;
case KM_CORE_IT_PERSIST_OPT:
// TODO: lowpri: replace existing item if already present in options vector?
options.push_back(km::kbp::option(
static_cast<km_core_option_scope>(action_items->option->scope),
action_items->option->key,
action_items->option->value
));
break;
default:
assert(false);
}
}
output.push_back(KM_CORE_CONTEXT_ITEM_END);
// Strip the markers from the output to be passed to the app
size_t buf_size;
if((status = km_core_context_items_to_utf32(output.data(), nullptr, &buf_size)) != KM_CORE_STATUS_OK) {
delete actions;
return nullptr;
}
actions->output = new km_core_usv[buf_size];
if((status = km_core_context_items_to_utf32(output.data(), actions->output, &buf_size)) != KM_CORE_STATUS_OK) {
delete[] actions->output;
delete actions;
return nullptr;
}
// Create an array of the persisted options
if(!options.empty()) {
options.push_back(KM_CORE_OPTIONS_END);
actions->persist_options = new km_core_option_item[options.size()];
std::copy(options.begin(), options.end(), actions->persist_options);
}
return actions;
}