1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/dbus/ibus/ibus_lookup_table.h"
6
7#include <string>
8#include "base/logging.h"
9#include "base/values.h"
10#include "chromeos/dbus/ibus/ibus_object.h"
11#include "chromeos/dbus/ibus/ibus_text.h"
12#include "dbus/message.h"
13
14namespace chromeos {
15
16namespace {
17// The default entry number of a page in IBusLookupTable.
18const int kDefaultPageSize = 9;
19const char kShowWindowAtCompositionKey[] = "show_window_at_composition";
20}  // namespace
21
22void AppendIBusLookupTable(const IBusLookupTable& table,
23                           dbus::MessageWriter* writer) {
24  IBusObjectWriter ibus_lookup_table_writer("IBusLookupTable",
25                                            "uubbiavav",
26                                            writer);
27  scoped_ptr<base::Value> show_position(
28      base::Value::CreateBooleanValue(table.show_window_at_composition()));
29  ibus_lookup_table_writer.AddAttachment(kShowWindowAtCompositionKey,
30                                         *show_position.get());
31  ibus_lookup_table_writer.CloseHeader();
32  ibus_lookup_table_writer.AppendUint32(table.page_size());
33  ibus_lookup_table_writer.AppendUint32(table.cursor_position());
34  ibus_lookup_table_writer.AppendBool(table.is_cursor_visible());
35  ibus_lookup_table_writer.AppendBool(false);  // Not used in Chrome.
36  ibus_lookup_table_writer.AppendInt32(static_cast<int32>(table.orientation()));
37
38  const std::vector<IBusLookupTable::Entry>& candidates = table.candidates();
39  dbus::MessageWriter text_writer(NULL);
40  ibus_lookup_table_writer.OpenArray("v", &text_writer);
41  bool have_labels = false;
42  for (size_t i = 0; i < candidates.size(); ++i) {
43    // Write candidate string as IBusText.
44    IBusText text;
45    text.set_text(candidates[i].value);
46    text.set_annotation(candidates[i].annotation);
47    text.set_description_title(candidates[i].description_title);
48    text.set_description_body(candidates[i].description_body);
49    AppendIBusText(text, &text_writer);
50    if (!candidates[i].label.empty())
51      have_labels = true;
52  }
53  ibus_lookup_table_writer.CloseContainer(&text_writer);
54
55  dbus::MessageWriter label_writer(NULL);
56  ibus_lookup_table_writer.OpenArray("v", &label_writer);
57
58  // If there are not any labels, don't append labels.
59  if (have_labels) {
60    for (size_t i = 0; i < candidates.size(); ++i) {
61      // Write label string as IBusText.
62      AppendStringAsIBusText(candidates[i].label, &label_writer);
63    }
64  }
65  ibus_lookup_table_writer.CloseContainer(&label_writer);
66
67  ibus_lookup_table_writer.CloseAll();
68}
69
70bool PopIBusLookupTable(dbus::MessageReader* reader, IBusLookupTable* table) {
71  IBusObjectReader ibus_object_reader("IBusLookupTable", reader);
72
73  if (!ibus_object_reader.Init())
74    return false;
75
76  const base::Value* value =
77      ibus_object_reader.GetAttachment(kShowWindowAtCompositionKey);
78  if (value) {
79    bool show_window_at_composition;
80    if (value->GetAsBoolean(&show_window_at_composition))
81      table->set_show_window_at_composition(show_window_at_composition);
82  }
83
84  uint32 page_size = 0;
85  if (!ibus_object_reader.PopUint32(&page_size)) {
86    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
87               << "1st argument should be uint32.";
88    return false;
89  }
90  table->set_page_size(page_size);
91
92  uint32 cursor_position = 0;
93  if (!ibus_object_reader.PopUint32(&cursor_position)) {
94    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
95               << "2nd argument should be uint32.";
96    return false;
97  }
98  table->set_cursor_position(cursor_position);
99
100  bool cursor_visible = true;
101  if (!ibus_object_reader.PopBool(&cursor_visible)) {
102    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
103               << "3rd argument should be boolean.";
104    return false;
105  }
106  table->set_is_cursor_visible(cursor_visible);
107
108  bool unused_round_value = true;
109  if (!ibus_object_reader.PopBool(&unused_round_value)) {
110    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
111               << "4th argument should be boolean.";
112    return false;
113  }
114
115  int32 orientation = 0;
116  if (!ibus_object_reader.PopInt32(&orientation)) {
117    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
118               << "5th argument should be int32.";
119    return false;
120  }
121
122  // Original IBus spec has third orientation IBUS_ORIENTATION_SYSTEM but it
123  // was not supported in Chrome OS. Thus do not cast from integer to enum.
124  table->set_orientation(
125      orientation == IBusLookupTable::HORIZONTAL ?
126      IBusLookupTable::HORIZONTAL : IBusLookupTable::VERTICAL);
127
128  dbus::MessageReader text_array_reader(NULL);
129  if (!ibus_object_reader.PopArray(&text_array_reader)) {
130    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
131               << "6th argument should be array.";
132    return false;
133  }
134
135  std::vector<IBusLookupTable::Entry>* candidates = table->mutable_candidates();
136  while (text_array_reader.HasMoreData()) {
137    IBusText candidate_text;
138    // The attributes in IBusText are not used in Chrome.
139    if (!PopIBusText(&text_array_reader, &candidate_text)) {
140      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
141                 << "6th argument should be array of IBusText.";
142      return false;
143    }
144    IBusLookupTable::Entry entry;
145    entry.value = candidate_text.text();
146    entry.annotation = candidate_text.annotation();
147    entry.description_title = candidate_text.description_title();
148    entry.description_body = candidate_text.description_body();
149    candidates->push_back(entry);
150  }
151
152  dbus::MessageReader label_array_reader(NULL);
153  if (!ibus_object_reader.PopArray(&label_array_reader)) {
154    LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
155               << "7th argument should be array.";
156    return false;
157  }
158
159  if (!label_array_reader.HasMoreData()) {
160    return true;
161  }
162
163  for (size_t i = 0; i < candidates->size(); ++i) {
164    if (!label_array_reader.HasMoreData()) {
165      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
166                 << "The number of label entry does not match with candidate "
167                 << "text. Same length or no label entry can be accepted.";
168      return false;
169    }
170
171    std::string label_text;
172    // The attributes in IBusText are not used in Chrome.
173    if (!PopStringFromIBusText(&label_array_reader, &label_text)) {
174      LOG(ERROR) << "Invalid variant structure[IBusLookupTable]: "
175                 << "7th argument should be array of IBusText.";
176      return false;
177    }
178    (*candidates)[i].label = label_text;
179  }
180  return true;
181}
182
183///////////////////////////////////////////////////////////////////////////////
184// IBusLookupTable
185IBusLookupTable::IBusLookupTable()
186    : page_size_(kDefaultPageSize),
187      cursor_position_(0),
188      is_cursor_visible_(true),
189      orientation_(HORIZONTAL),
190      show_window_at_composition_(false) {
191}
192
193IBusLookupTable::~IBusLookupTable() {
194}
195
196bool IBusLookupTable::IsEqual(const IBusLookupTable& table) const {
197  if (page_size_ != table.page_size_ ||
198      cursor_position_ != table.cursor_position_ ||
199      is_cursor_visible_ != table.is_cursor_visible_ ||
200      orientation_ != table.orientation_ ||
201      show_window_at_composition_ != table.show_window_at_composition_ ||
202      candidates_.size() != table.candidates_.size())
203    return false;
204
205  for (size_t i = 0; i < candidates_.size(); ++i) {
206    const Entry& left = candidates_[i];
207    const Entry& right = table.candidates_[i];
208    if (left.value != right.value ||
209        left.label != right.label ||
210        left.annotation != right.annotation ||
211        left.description_title != right.description_title ||
212        left.description_body != right.description_body)
213      return false;
214  }
215  return true;
216}
217
218void IBusLookupTable::CopyFrom(const IBusLookupTable& table) {
219  page_size_ = table.page_size_;
220  cursor_position_ = table.cursor_position_;
221  is_cursor_visible_ = table.is_cursor_visible_;
222  orientation_ = table.orientation_;
223  show_window_at_composition_ = table.show_window_at_composition_;
224  candidates_.clear();
225  candidates_ = table.candidates_;
226}
227
228IBusLookupTable::Entry::Entry() {
229}
230
231IBusLookupTable::Entry::~Entry() {
232}
233
234}  // namespace chromeos
235