|
| 1 | +#include "proto.h" |
| 2 | + |
| 3 | +#include <rime/component.h> |
| 4 | +#include <rime/composition.h> |
| 5 | +#include <rime/context.h> |
| 6 | +#include <rime/menu.h> |
| 7 | +#include <rime/schema.h> |
| 8 | +#include <rime/service.h> |
| 9 | +#include <rime_api.h> |
| 10 | + |
| 11 | +#include "jni-utils.h" |
| 12 | + |
| 13 | +using namespace rime; |
| 14 | + |
| 15 | +RIME_PROTO_OBJ rime_commit_proto(RimeSessionId session_id) { |
| 16 | + an<Session> session(Service::instance().GetSession(session_id)); |
| 17 | + if (!session) return nullptr; |
| 18 | + auto env = GlobalRef->AttachEnv(); |
| 19 | + const string &commit_text(session->commit_text()); |
| 20 | + RIME_PROTO_OBJ commit = |
| 21 | + env->NewObject(GlobalRef->CommitProto, GlobalRef->CommitProtoInit, |
| 22 | + *JString(env, commit_text)); |
| 23 | + session->ResetCommitText(); |
| 24 | + return commit; |
| 25 | +} |
| 26 | + |
| 27 | +RIME_PROTO_OBJ rime_context_proto(RimeSessionId session_id) { |
| 28 | + an<Session> session = Service::instance().GetSession(session_id); |
| 29 | + if (!session) return nullptr; |
| 30 | + Context *ctx = session->context(); |
| 31 | + if (!ctx) return nullptr; |
| 32 | + auto env = GlobalRef->AttachEnv(); |
| 33 | + jobject composition = env->NewObject(GlobalRef->CompositionProto, |
| 34 | + GlobalRef->CompositionProtoDefault); |
| 35 | + if (ctx->IsComposing()) { |
| 36 | + const Preedit &preedit = ctx->GetPreedit(); |
| 37 | + composition = env->NewObject( |
| 38 | + GlobalRef->CompositionProto, GlobalRef->CompositionProtoInit, |
| 39 | + preedit.text.length(), preedit.caret_pos, preedit.sel_start, |
| 40 | + preedit.sel_end, *JString(env, preedit.text), |
| 41 | + *JString(env, ctx->GetCommitText())); |
| 42 | + } |
| 43 | + jobject menu = |
| 44 | + env->NewObject(GlobalRef->MenuProto, GlobalRef->MenuProtoDefault); |
| 45 | + if (ctx->HasMenu()) { |
| 46 | + Segment &seg = ctx->composition().back(); |
| 47 | + Schema *schema = session->schema(); |
| 48 | + int page_size = schema ? schema->page_size() : 5; |
| 49 | + int selected_index = seg.selected_index; |
| 50 | + int page_number = selected_index / page_size; |
| 51 | + int highlighted_index = selected_index % page_size; |
| 52 | + const string &select_keys = schema ? schema->select_keys() : ""; |
| 53 | + the<Page> page(seg.menu->CreatePage(page_size, page_number)); |
| 54 | + if (page) { |
| 55 | + vector<string> labels; |
| 56 | + auto dest_labels = |
| 57 | + env->NewObjectArray(page_size, GlobalRef->String, nullptr); |
| 58 | + if (schema) { |
| 59 | + Config *config = schema->config(); |
| 60 | + auto src_labels = config->GetList("menu/alternative_select_labels"); |
| 61 | + if (src_labels && (size_t)page_size <= src_labels->size()) { |
| 62 | + for (int i = 0; i < page_size; ++i) { |
| 63 | + if (an<ConfigValue> value = src_labels->GetValueAt(i)) { |
| 64 | + env->SetObjectArrayElement(dest_labels, i, |
| 65 | + *JString(env, value->str())); |
| 66 | + labels.emplace_back(value->str()); |
| 67 | + } |
| 68 | + } |
| 69 | + } else if (!select_keys.empty()) { |
| 70 | + for (const char key : select_keys) { |
| 71 | + labels.emplace_back(1, key); |
| 72 | + if (labels.size() >= page_size) break; |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + int num_candidates = page->candidates.size(); |
| 77 | + auto dest_candidates = env->NewObjectArray( |
| 78 | + num_candidates, GlobalRef->CandidateProto, nullptr); |
| 79 | + int index = 0; |
| 80 | + for (const an<Candidate> &src : page->candidates) { |
| 81 | + const string &label = |
| 82 | + index < labels.size() ? labels[index] : std::to_string(index + 1); |
| 83 | + auto dest = JRef(env, env->NewObject(GlobalRef->CandidateProto, |
| 84 | + GlobalRef->CandidateProtoInit, |
| 85 | + *JString(env, src->text()), |
| 86 | + *JString(env, src->comment()), |
| 87 | + *JString(env, label))); |
| 88 | + env->SetObjectArrayElement(dest_candidates, index++, *dest); |
| 89 | + } |
| 90 | + menu = env->NewObject( |
| 91 | + GlobalRef->MenuProto, GlobalRef->MenuProtoInit, page_size, |
| 92 | + page_number, page->is_last_page, highlighted_index, |
| 93 | + *JRef<jobjectArray>(env, dest_candidates), *JString(env, select_keys), |
| 94 | + *JRef<jobjectArray>(env, dest_labels)); |
| 95 | + } |
| 96 | + } |
| 97 | + return env->NewObject(GlobalRef->ContextProto, GlobalRef->ContextProtoInit, |
| 98 | + *JRef(env, composition), *JRef(env, menu), |
| 99 | + *JString(env, ctx->input()), ctx->caret_pos()); |
| 100 | +} |
| 101 | + |
| 102 | +RIME_PROTO_OBJ rime_status_proto(RimeSessionId session_id) { |
| 103 | + an<Session> session(Service::instance().GetSession(session_id)); |
| 104 | + if (!session) return nullptr; |
| 105 | + Schema *schema = session->schema(); |
| 106 | + Context *ctx = session->context(); |
| 107 | + if (!schema || !ctx) return nullptr; |
| 108 | + auto env = GlobalRef->AttachEnv(); |
| 109 | + return env->NewObject( |
| 110 | + GlobalRef->StatusProto, GlobalRef->StatusProtoInit, |
| 111 | + *JString(env, schema->schema_id()), *JString(env, schema->schema_name()), |
| 112 | + Service::instance().disabled(), ctx->IsComposing(), |
| 113 | + ctx->get_option("ascii_mode"), ctx->get_option("full_shape"), |
| 114 | + ctx->get_option("simplification"), ctx->get_option("traditional"), |
| 115 | + ctx->get_option("ascii_punct")); |
| 116 | +} |
| 117 | + |
| 118 | +static void rime_proto_initialize() {} |
| 119 | + |
| 120 | +static void rime_proto_finalize() {} |
| 121 | + |
| 122 | +static RimeCustomApi *rime_proto_get_api() { |
| 123 | + static RimeProtoApi s_api = {0}; |
| 124 | + if (!s_api.data_size) { |
| 125 | + RIME_STRUCT_INIT(RimeProtoApi, s_api); |
| 126 | + s_api.commit_proto = &rime_commit_proto; |
| 127 | + s_api.context_proto = &rime_context_proto; |
| 128 | + s_api.status_proto = &rime_status_proto; |
| 129 | + } |
| 130 | + return (RimeCustomApi *)&s_api; |
| 131 | +} |
| 132 | + |
| 133 | +RIME_REGISTER_CUSTOM_MODULE(proto) { module->get_api = &rime_proto_get_api; } |
0 commit comments