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 "gui/character_pad/character_palette.h"
31
32 #include <QtGui/QtGui>
33 #include <QtWidgets/QMessageBox>
34
35 #ifdef OS_WIN
36 #include <Windows.h>
37 #endif // OS_WIN
38
39 #include "base/logging.h"
40 #include "base/util.h"
41 #include "client/client.h"
42 #include "config/stats_config_util.h"
43 #include "gui/base/win_util.h"
44 #include "gui/character_pad/data/local_character_map.h"
45 #include "gui/character_pad/data/unicode_blocks.h"
46 #include "gui/character_pad/selection_handler.h"
47 #include "protocol/commands.pb.h"
48
49 namespace mozc {
50 namespace gui {
51 namespace {
52
53 const char32 kHexBase = 16;
54
55 const char kUNICODEName[] = "Unicode";
56 const char kCP932Name[] = "Shift JIS";
57 const char kJISX0201Name[] = "JISX 0201";
58 const char kJISX0208Name[] = "JISX 0208";
59 const char kJISX0212Name[] = "JISX 0212";
60
61 const CharacterPalette::UnicodeRange kUCS2Range = { 0, 0xffff };
62
63 const CharacterPalette::CP932JumpTo kCP932JumpTo[] = {
64 {"半角英数字", 0x0020},
65 {"半角カタカナ", 0x00A1},
66 {"全角記号", 0x8141},
67 {"全角英数字", 0x8250},
68 {"ひらがな", 0x829F},
69 {"カタカナ", 0x8340},
70 {"丸数字", 0x8740},
71 {"ローマ数字", 0xFA40},
72 {"単位", 0x875F},
73 {"その他の記号", 0x8780},
74 {"ギリシャ文字", 0x839F},
75 {"キリル文字", 0x8440},
76 {"罫線", 0x849F},
77 {"第一水準漢字", 0x889F},
78 {"第二水準漢字", 0x989F},
79 {NULL, 0},
80 };
81
82 // Adds a child QTreeWidgetItem with the text "name" to parent.
83 // Returns the added child item.
AddItem(QTreeWidgetItem * parent,const char * name)84 QTreeWidgetItem *AddItem(QTreeWidgetItem *parent, const char *name) {
85 QTreeWidgetItem *item = new QTreeWidgetItem(parent);
86 item->setText(0, QString::fromUtf8(name));
87 parent->addChild(item);
88 return item;
89 }
90
91 } // namespace
92
CharacterPalette(QWidget * parent)93 CharacterPalette::CharacterPalette(QWidget *parent)
94 : QMainWindow(parent),
95 usage_stats_enabled_(mozc::config::StatsConfigUtil::IsEnabled()) {
96 // To reduce the disk IO of reading the stats config, we load it only when the
97 // class is initialized. There is no problem because the config dialog (on
98 // Mac) and the administrator dialog (on Windows) say that the usage stats
99 // setting changes will take effect after the re-login.
100
101 if (usage_stats_enabled_) {
102 client_.reset(client::ClientFactory::NewClient());
103 }
104 setupUi(this);
105
106 fontComboBox->setWritingSystem(
107 static_cast<QFontDatabase::WritingSystem>(QFontDatabase::Any));
108 fontComboBox->setEditable(false);
109 fontComboBox->setCurrentFont(tableWidget->font());
110
111 QObject::connect(fontComboBox,
112 SIGNAL(currentFontChanged(const QFont &)),
113 this, SLOT(updateFont(const QFont &)));
114
115 QObject::connect(sizeComboBox,
116 SIGNAL(currentIndexChanged(int)),
117 this, SLOT(updateFontSize(int)));
118
119 sizeComboBox->setCurrentIndex(4);
120 fontComboBox->setCurrentFont(tableWidget->font());
121
122 QObject::connect(tableWidget,
123 SIGNAL(itemSelected(const QTableWidgetItem*)),
124 this,
125 SLOT(itemSelected(const QTableWidgetItem*)));
126
127 // Category Tree
128 QObject::connect(categoryTreeWidget,
129 SIGNAL(itemClicked(QTreeWidgetItem *, int)),
130 SLOT(categorySelected(QTreeWidgetItem *, int)));
131
132 QTreeWidgetItem *unicode_item = new QTreeWidgetItem;
133 unicode_item->setText(0, QString::fromUtf8(kUNICODEName));
134 QTreeWidgetItem *sjis_item = new QTreeWidgetItem;
135 sjis_item->setText(0, QString::fromUtf8(kCP932Name));
136 QTreeWidgetItem *jisx0201_item = new QTreeWidgetItem;
137 jisx0201_item->setText(0, QString::fromUtf8(kJISX0201Name));
138 QTreeWidgetItem *jisx0208_item = new QTreeWidgetItem;
139 jisx0208_item->setText(0, QString::fromUtf8(kJISX0208Name));
140 QTreeWidgetItem *jisx0212_item = new QTreeWidgetItem;
141 jisx0212_item->setText(0, QString::fromUtf8(kJISX0212Name));
142
143 // Because almost all users use Shift-JIS table instead of
144 // Unicode table, Shift-JIS table is selected and child
145 // items of Shift-JIS table are expanded by default.
146 // In order to let user know the existence of the Unicode table,
147 // shows Unicode table at first, but don't expands the child items.
148 categoryTreeWidget->addTopLevelItem(unicode_item);
149 categoryTreeWidget->addTopLevelItem(sjis_item);
150 categoryTreeWidget->addTopLevelItem(jisx0201_item);
151 categoryTreeWidget->addTopLevelItem(jisx0208_item);
152 categoryTreeWidget->addTopLevelItem(jisx0212_item);
153
154 for (size_t i = 0; kCP932JumpTo[i].name != NULL; ++i) {
155 AddItem(sjis_item, kCP932JumpTo[i].name);
156 }
157
158 // Make Unicode block children and look-up table for each character range.
159 for (size_t i = 0; kUnicodeBlockTable[i].name != NULL; ++i) {
160 const UnicodeBlock &block = kUnicodeBlockTable[i];
161 const UnicodeRange &range = block.range;
162 QTreeWidgetItem *item = new QTreeWidgetItem(unicode_item);
163 const QString original_name(block.name);
164 const QString translated_name(QObject::tr(block.name));
165 unicode_block_map_[original_name] = range;
166 unicode_block_map_[translated_name] = range;
167 item->setText(0, translated_name);
168 unicode_item->addChild(item);
169 }
170
171 // Adjust the splitter.
172 splitter->setSizes(QList<int>()
173 << static_cast<int>(width() * 0.25)
174 << static_cast<int>(width() * 0.75));
175
176 // set default table
177 showLocalTable(kCP932Map, kCP932MapSize);
178 categoryTreeWidget->setCurrentItem(sjis_item);
179
180 categoryTreeWidget->setItemExpanded(
181 categoryTreeWidget->topLevelItem(0)->parent(),
182 true);
183
184 // Select "Shift-JIS" item as a default.
185 categoryTreeWidget->setCurrentItem(sjis_item);
186 sjis_item->setExpanded(true);
187
188 tableWidget->setAutoScroll(false);
189
190 if (usage_stats_enabled_) {
191 CHECK(client_.get());
192 // Sends the usage stats event (CHARACTER_PALETTE_OPEN_EVENT) to mozc
193 // converter.
194 commands::SessionCommand command;
195 command.set_type(commands::SessionCommand::USAGE_STATS_EVENT);
196 command.set_usage_stats_event(
197 commands::SessionCommand::CHARACTER_PALETTE_OPEN_EVENT);
198 commands::Output dummy_output;
199 client_->SendCommand(command, &dummy_output);
200 }
201
202 repaint();
203 update();
204 }
205
~CharacterPalette()206 CharacterPalette::~CharacterPalette() {}
207
updateFont(const QFont & font)208 void CharacterPalette::updateFont(const QFont &font) {
209 QFont new_font = font;
210 new_font.setPointSize(tableWidget->font().pointSize());
211 tableWidget->setFont(new_font);
212 tableWidget->adjustSize();
213 updateTableSize();
214 }
215
updateFontSize(int index)216 void CharacterPalette::updateFontSize(int index) {
217 int font_point = 24;
218 switch (index) {
219 case 0: font_point = 32; break;
220 case 1: font_point = 24; break;
221 case 2: font_point = 16; break;
222 case 3: font_point = 14; break;
223 case 4: font_point = 12; break;
224 }
225
226 QFont font;
227 font.setPointSize(font_point);
228 tableWidget->setFont(font);
229
230 updateTableSize();
231 tableWidget->adjustSize();
232
233 QTableWidgetItem *item = tableWidget->currentItem();
234 if (item != NULL) {
235 tableWidget->scrollToItem(item,
236 QAbstractItemView::PositionAtCenter);
237 }
238 }
239
resizeEvent(QResizeEvent *)240 void CharacterPalette::resizeEvent(QResizeEvent *) {
241 updateTableSize();
242 }
243
updateTableSize()244 void CharacterPalette::updateTableSize() {
245 // here we use "龍" to calc font size, as it looks almsot square
246 const char kHexBaseChar[]= "龍";
247 const QRect rect =
248 QFontMetrics(tableWidget->font()).boundingRect(trUtf8(kHexBaseChar));
249
250 #ifdef OS_MACOSX
251 const int width = static_cast<int>(rect.width() * 2.2);
252 const int height = static_cast<int>(rect.height() * 2.0);
253 #else
254 const int width = static_cast<int>(rect.width() * 1.6);
255 const int height = static_cast<int>(rect.height() * 1.2);
256 #endif
257
258 for (int j = 0; j < tableWidget->columnCount(); ++j) {
259 tableWidget->setColumnWidth(j, width);
260 }
261 for (int j = 0; j < tableWidget->rowCount(); ++j) {
262 tableWidget->setRowHeight(j, height);
263 }
264 tableWidget->setLookupResultItem(NULL);
265 }
266
categorySelected(QTreeWidgetItem * item,int column)267 void CharacterPalette::categorySelected(QTreeWidgetItem *item,
268 int column) {
269 const QString &text = item->text(column);
270 const QTreeWidgetItem *parent = item->parent();
271
272 item->setExpanded(!item->isExpanded());
273
274 if (text == kUNICODEName) { // TOP Unicode
275 // The number of characters within entire Unicode range is now too large to
276 // show in the table. So we show only UCS2 range when |kUNICODEName| is
277 // clicked.
278 showUnicodeTableByRange(kUCS2Range);
279 } else if (parent != NULL && parent->text(0) == kUNICODEName) {
280 showUnicodeTableByBlockName(text);
281 } else if (parent != NULL && parent->text(0) == kCP932Name) {
282 showSJISBlockTable(text);
283 } else if (text == kJISX0201Name) {
284 showLocalTable(kJISX0201Map, kJISX0201MapSize);
285 } else if (text == kJISX0208Name) {
286 showLocalTable(kJISX0208Map, kJISX0208MapSize);
287 } else if (text == kJISX0212Name) {
288 showLocalTable(kJISX0212Map, kJISX0212MapSize);
289 } else if (text == kCP932Name) {
290 showLocalTable(kCP932Map, kCP932MapSize);
291 }
292 }
293
itemSelected(const QTableWidgetItem * item)294 void CharacterPalette::itemSelected(const QTableWidgetItem *item) {
295 if (!usage_stats_enabled_) {
296 return;
297 }
298 CHECK(client_.get());
299 // Sends the usage stats event (CHARACTER_PALETTE_COMMIT_EVENT) to mozc
300 // converter.
301 commands::SessionCommand command;
302 command.set_type(commands::SessionCommand::USAGE_STATS_EVENT);
303 command.set_usage_stats_event(
304 commands::SessionCommand::CHARACTER_PALETTE_COMMIT_EVENT);
305 commands::Output dummy_output;
306 client_->SendCommand(command, &dummy_output);
307 }
308
309 // Unicode operations
showUnicodeTableByRange(const UnicodeRange & range)310 void CharacterPalette::showUnicodeTableByRange(const UnicodeRange &range) {
311 tableWidget->hide();
312 tableWidget->clear();
313
314 QStringList column_header;
315 for (int col = 0; col < kHexBase; ++col) {
316 column_header << QString::number(col, kHexBase).toUpper();
317 }
318
319 QStringList row_header;
320 for (char32 ucs4 = range.first; ucs4 <= range.last; ucs4 += kHexBase) {
321 QString str;
322 str.sprintf("U+%3.3X0", ucs4 / kHexBase);
323 row_header << str;
324 }
325
326 tableWidget->setColumnCount(kHexBase);
327 tableWidget->setRowCount(row_header.size());
328
329 tableWidget->setHorizontalHeaderLabels(column_header);
330 tableWidget->setVerticalHeaderLabels(row_header);
331
332 const char32 offset = range.first / kHexBase;
333 for (char32 ucs4 = range.first; ucs4 <= range.last; ++ucs4) {
334 const char32 ucs4s[] = { ucs4 };
335 QTableWidgetItem *item =
336 new QTableWidgetItem(QString::fromUcs4(ucs4s, arraysize(ucs4s)));
337 item->setTextAlignment(Qt::AlignCenter);
338 tableWidget->setItem(ucs4 / kHexBase - offset, ucs4 % kHexBase, item);
339 tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
340 }
341
342 tableWidget->scrollToItem(tableWidget->item(0, 0),
343 QAbstractItemView::PositionAtTop);
344 tableWidget->setLookupResultItem(NULL);
345 tableWidget->show();
346 }
347
showSJISBlockTable(const QString & name)348 void CharacterPalette::showSJISBlockTable(const QString &name) {
349 const CP932JumpTo *block = NULL;
350 for (int i = 0; kCP932JumpTo[i].name !=NULL; ++i) {
351 if (name == QString::fromUtf8(kCP932JumpTo[i].name)) {
352 block = &kCP932JumpTo[i];
353 break;
354 }
355 }
356
357 if (block == NULL) {
358 return;
359 }
360
361 showLocalTable(kCP932Map, kCP932MapSize);
362
363 tableWidget->hide();
364 QTableWidgetItem *item = tableWidget->item(block->from / kHexBase -
365 kCP932Map[0].from / kHexBase,
366 block->from % kHexBase);
367
368 if (item != NULL) {
369 tableWidget->scrollToItem(item, QAbstractItemView::PositionAtTop);
370 item->setSelected(true);
371 }
372
373 tableWidget->setLookupResultItem(NULL);
374 tableWidget->show();
375 }
376
showUnicodeTableByBlockName(const QString & block_name)377 void CharacterPalette::showUnicodeTableByBlockName(const QString &block_name) {
378 QMap<QString, UnicodeRange>::const_iterator
379 it = unicode_block_map_.find(block_name);
380 if (it == unicode_block_map_.end()) {
381 return;
382 }
383 showUnicodeTableByRange(it.value());
384 }
385
386 // Local table
showLocalTable(const LocalCharacterMap * local_map,size_t local_map_size)387 void CharacterPalette::showLocalTable(const LocalCharacterMap *local_map,
388 size_t local_map_size) {
389 tableWidget->hide();
390 tableWidget->clear();
391
392 QStringList column_header;
393 for (int col = 0; col < kHexBase; ++col) {
394 column_header << QString::number(col, kHexBase).toUpper();
395 }
396
397 // find range
398 const int from_start = local_map[0].from;
399 const int from_end = local_map[local_map_size - 1].from + kHexBase;
400
401 QStringList row_header;
402 for (int i = from_start; i < from_end; i += kHexBase) {
403 QString str;
404 str.sprintf("0x%X0", i / kHexBase);
405 row_header << str;
406 }
407
408 tableWidget->setColumnCount(kHexBase);
409 tableWidget->setRowCount(row_header.size());
410
411 tableWidget->setHorizontalHeaderLabels(column_header);
412 tableWidget->setVerticalHeaderLabels(row_header);
413
414 const int offset = from_start / kHexBase;
415 for (size_t i = 0; i < local_map_size; ++i) {
416 // We do not use QString(QChar(i)) but Util::UCS4ToUTF8 because
417 // QChar is only 16-bit.
418 string utf8;
419 Util::UCS4ToUTF8(local_map[i].ucs2, &utf8);
420 QTableWidgetItem *item = new QTableWidgetItem(
421 QString::fromUtf8(utf8.data(), utf8.size()));
422 item->setTextAlignment(Qt::AlignCenter);
423 tableWidget->setItem(local_map[i].from / kHexBase - offset,
424 local_map[i].from % kHexBase, item);
425 tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
426 }
427
428 tableWidget->scrollToItem(tableWidget->item(0, 0),
429 QAbstractItemView::PositionAtCenter);
430
431 tableWidget->setLookupResultItem(NULL);
432 tableWidget->show();
433 }
434
435 } // namespace gui
436 } // namespace mozc
437