1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "unix/ibus/ibus_candidate_window_handler.h"
31
32 #include <cstdio>
33 #include <cstdlib>
34
35 #include "base/logging.h"
36 #include "protocol/commands.pb.h"
37 #include "unix/ibus/mozc_engine_property.h"
38
39 namespace mozc {
40 namespace ibus {
41
42 namespace {
43
44 // Returns an IBusText used for showing the auxiliary text in the candidate
45 // window. Returns NULL if no text has to be shown. Caller must release the
46 // returned IBusText object.
ComposeAuxiliaryText(const commands::Candidates & candidates)47 IBusText *ComposeAuxiliaryText(const commands::Candidates &candidates) {
48 if (!candidates.has_footer()) {
49 // We don't have to show the auxiliary text.
50 return NULL;
51 }
52 const commands::Footer &footer = candidates.footer();
53
54 string auxiliary_text;
55 if (footer.has_label()) {
56 // TODO(yusukes,mozc-team): label() is not localized. Currently, it's always
57 // written in Japanese (in UTF-8).
58 auxiliary_text = footer.label();
59 } else if (footer.has_sub_label()) {
60 // Windows client shows sub_label() only when label() is not specified. We
61 // follow the policy.
62 auxiliary_text = footer.sub_label();
63 }
64
65 if (footer.has_index_visible() && footer.index_visible() &&
66 candidates.has_focused_index()) {
67 // Max size of candidates is 200 so 128 is sufficient size for the buffer.
68 char index_buf[128] = {0};
69 const int result = snprintf(index_buf,
70 sizeof(index_buf) - 1,
71 "%s%d/%d",
72 (auxiliary_text.empty() ? "" : " "),
73 candidates.focused_index() + 1,
74 candidates.size());
75 DCHECK_GE(result, 0) << "snprintf in ComposeAuxiliaryText failed";
76 auxiliary_text += index_buf;
77 }
78 return auxiliary_text.empty() ?
79 NULL : ibus_text_new_from_string(auxiliary_text.c_str());
80 }
81 } // namespace
82
IBusCandidateWindowHandler()83 IBusCandidateWindowHandler::IBusCandidateWindowHandler() {
84 }
85
~IBusCandidateWindowHandler()86 IBusCandidateWindowHandler::~IBusCandidateWindowHandler() {
87 }
88
Update(IBusEngine * engine,const commands::Output & output)89 void IBusCandidateWindowHandler::Update(IBusEngine *engine,
90 const commands::Output &output) {
91 UpdateCandidates(engine, output);
92 UpdateAuxiliaryText(engine, output);
93 }
94
UpdateCursorRect(IBusEngine * engine)95 void IBusCandidateWindowHandler::UpdateCursorRect(IBusEngine *engine) {
96 // Nothing to do because IBus takes care of where to show its candidate
97 // window.
98 }
99
Hide(IBusEngine * engine)100 void IBusCandidateWindowHandler::Hide(IBusEngine *engine) {
101 ibus_engine_hide_lookup_table(engine);
102 ibus_engine_hide_auxiliary_text(engine);
103 }
104
Show(IBusEngine * engine)105 void IBusCandidateWindowHandler::Show(IBusEngine *engine) {
106 ibus_engine_show_lookup_table(engine);
107 ibus_engine_show_auxiliary_text(engine);
108 }
109
110 // TODO(hsumita): Writes test for this method.
UpdateCandidates(IBusEngine * engine,const commands::Output & output)111 bool IBusCandidateWindowHandler::UpdateCandidates(
112 IBusEngine *engine,
113 const commands::Output &output) {
114 if (!output.has_candidates() || output.candidates().candidate_size() == 0) {
115 ibus_engine_hide_lookup_table(engine);
116 return true;
117 }
118
119 const gboolean kRound = TRUE;
120 const commands::Candidates &candidates = output.candidates();
121 const gboolean cursor_visible = candidates.has_focused_index() ?
122 TRUE : FALSE;
123 int cursor_pos = 0;
124 if (candidates.has_focused_index()) {
125 for (int i = 0; i < candidates.candidate_size(); ++i) {
126 if (candidates.focused_index() == candidates.candidate(i).index()) {
127 cursor_pos = i;
128 }
129 }
130 }
131
132 size_t page_size = kPageSize;
133 if (candidates.has_category() &&
134 candidates.category() == commands::SUGGESTION &&
135 page_size > candidates.candidate_size()) {
136 page_size = candidates.candidate_size();
137 }
138 IBusLookupTable *table = ibus_lookup_table_new(page_size,
139 cursor_pos,
140 cursor_visible,
141 kRound);
142 if (candidates.direction() == commands::Candidates::VERTICAL) {
143 ibus_lookup_table_set_orientation(table, IBUS_ORIENTATION_VERTICAL);
144 } else {
145 ibus_lookup_table_set_orientation(table, IBUS_ORIENTATION_HORIZONTAL);
146 }
147
148 for (int i = 0; i < candidates.candidate_size(); ++i) {
149 const commands::Candidates::Candidate &candidate = candidates.candidate(i);
150 IBusText *text = ibus_text_new_from_string(candidate.value().c_str());
151 ibus_lookup_table_append_candidate(table, text);
152 // |text| is released by ibus_engine_update_lookup_table along with |table|.
153
154 const bool has_label = candidate.has_annotation() &&
155 candidate.annotation().has_shortcut();
156 // Need to append an empty string when the candidate does not have a
157 // shortcut. Otherwise the ibus lookup table shows numeric labels.
158 IBusText *label =
159 ibus_text_new_from_string(has_label ?
160 candidate.annotation().shortcut().c_str() :
161 "");
162 ibus_lookup_table_append_label(table, label);
163 // |label| is released by ibus_engine_update_lookup_table along with
164 // |table|.
165 }
166
167 ibus_engine_update_lookup_table(engine, table, TRUE);
168 // |table| is released by ibus_engine_update_lookup_table.
169 return true;
170 }
171
172 // TODO(hsumita): Writes test for this method.
UpdateAuxiliaryText(IBusEngine * engine,const commands::Output & output)173 bool IBusCandidateWindowHandler::UpdateAuxiliaryText(
174 IBusEngine *engine,
175 const commands::Output &output) {
176 if (!output.has_candidates()) {
177 ibus_engine_hide_auxiliary_text(engine);
178 return true;
179 }
180
181 IBusText *auxiliary_text = ComposeAuxiliaryText(output.candidates());
182 if (auxiliary_text) {
183 ibus_engine_update_auxiliary_text(engine, auxiliary_text, TRUE);
184 // |auxiliary_text| is released by ibus_engine_update_auxiliary_text.
185 } else {
186 ibus_engine_hide_auxiliary_text(engine);
187 }
188
189 return true;
190 }
191
OnIBusCustomFontDescriptionChanged(const string & custom_font_description)192 void IBusCandidateWindowHandler::OnIBusCustomFontDescriptionChanged(
193 const string &custom_font_description) {
194 // Do nothing
195 // The custom font description is managed by ibus directly.
196 }
197
OnIBusUseCustomFontDescriptionChanged(bool use_custom_font_description)198 void IBusCandidateWindowHandler::OnIBusUseCustomFontDescriptionChanged(
199 bool use_custom_font_description) {
200 // Do nothing
201 // The custom font description is managed by ibus directly.
202 }
203
204 } // namespace ibus
205 } // namespace mozc
206