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