| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "gui/character_pad/character_palette.h" |
| |
| #include <QtGui/QtGui> |
| #include <QtGui/QMessageBox> |
| |
| #ifdef OS_WIN |
| #include <Windows.h> |
| #endif // OS_WIN |
| |
| #include "base/logging.h" |
| #include "base/util.h" |
| #include "client/client.h" |
| #include "config/stats_config_util.h" |
| #include "gui/base/win_util.h" |
| #include "gui/character_pad/data/local_character_map.h" |
| #include "gui/character_pad/data/unicode_blocks.h" |
| #include "gui/character_pad/selection_handler.h" |
| #include "session/commands.pb.h" |
| |
| namespace mozc { |
| namespace gui { |
| |
| namespace { |
| |
| const char32 kHexBase = 16; |
| |
| const char kUNICODEName[] = "Unicode"; |
| const char kCP932Name[] = "Shift JIS"; |
| const char kJISX0201Name[] = "JISX 0201"; |
| const char kJISX0208Name[] = "JISX 0208"; |
| const char kJISX0212Name[] = "JISX 0212"; |
| const char kJISX0213Name[] = "JISX 0213"; |
| |
| const CharacterPalette::UnicodeRange kUCS2Range = { 0, 0xffff }; |
| |
| // static const CP932JumpTo kCP932JumpTo[] = { |
| // { "半角英数字", 0x0020 }, |
| // { "半角カタカナ", 0x00A1 }, |
| // { "全角記号", 0x8141 }, |
| // { "全角英数字", 0x8250 }, |
| // { "ひらがな", 0x829F }, |
| // { "カタカナ", 0x8340 }, |
| // { "丸数字", 0x8740 }, |
| // { "ローマ数字", 0xFA40 }, |
| // { "単位", 0x875F }, |
| // { "その他の記号", 0x8780 }, |
| // { "ギリシャ文字", 0x839F }, |
| // { "キリル文字", 0x8440 }, |
| // { "罫線", 0x849F }, |
| // { "第一水準漢字", 0x889F }, |
| // { "第二水準漢字", 0x989F }, |
| // { NULL, 0 } |
| // }; |
| |
| const CharacterPalette::CP932JumpTo kCP932JumpTo[] = { |
| { "\xE5\x8D\x8A\xE8\xA7\x92\xE8\x8B\xB1\xE6\x95\xB0\xE5\xAD\x97", |
| 0x0020 }, |
| { "\xE5\x8D\x8A\xE8\xA7\x92\xE3\x82\xAB\xE3\x82\xBF\xE3\x82\xAB\xE3\x83\x8A", |
| 0x00A1 }, |
| { "\xE5\x85\xA8\xE8\xA7\x92\xE8\xA8\x98\xE5\x8F\xB7", |
| 0x8141 }, |
| { "\xE5\x85\xA8\xE8\xA7\x92\xE8\x8B\xB1\xE6\x95\xB0\xE5\xAD\x97", |
| 0x8250 }, |
| { "\xE3\x81\xB2\xE3\x82\x89\xE3\x81\x8C\xE3\x81\xAA", |
| 0x829F }, |
| { "\xE3\x82\xAB\xE3\x82\xBF\xE3\x82\xAB\xE3\x83\x8A", |
| 0x8340 }, |
| { "\xE4\xB8\xB8\xE6\x95\xB0\xE5\xAD\x97", |
| 0x8740 }, |
| { "\xE3\x83\xAD\xE3\x83\xBC\xE3\x83\x9E\xE6\x95\xB0\xE5\xAD\x97", |
| 0xFA40 }, |
| { "\xE5\x8D\x98\xE4\xBD\x8D", |
| 0x875F }, |
| { "\xE3\x81\x9D\xE3\x81\xAE\xE4\xBB\x96\xE3\x81\xAE\xE8\xA8\x98\xE5\x8F\xB7", |
| 0x8780 }, |
| { "\xE3\x82\xAE\xE3\x83\xAA\xE3\x82\xB7\xE3\x83\xA3\xE6\x96\x87\xE5\xAD\x97", |
| 0x839F }, |
| { "\xE3\x82\xAD\xE3\x83\xAA\xE3\x83\xAB\xE6\x96\x87\xE5\xAD\x97", |
| 0x8440 }, |
| { "\xE7\xBD\xAB\xE7\xB7\x9A", |
| 0x849F }, |
| { "\xE7\xAC\xAC\xE4\xB8\x80\xE6\xB0\xB4\xE6\xBA\x96\xE6\xBC\xA2\xE5\xAD\x97", |
| 0x889F }, |
| { "\xE7\xAC\xAC\xE4\xBA\x8C\xE6\xB0\xB4\xE6\xBA\x96\xE6\xBC\xA2\xE5\xAD\x97", |
| 0x989F }, |
| { NULL, 0 } |
| }; |
| |
| // add child QTreeWidgetItem with the text "name" to parent. |
| // return child item. |
| QTreeWidgetItem *AddItem(QTreeWidgetItem *parent, const char *name) { |
| QTreeWidgetItem *item = new QTreeWidgetItem(parent); |
| item->setText(0, QString::fromUtf8(name)); |
| parent->addChild(item); |
| return item; |
| } |
| } // namespace |
| |
| CharacterPalette::CharacterPalette(QWidget *parent) |
| : QMainWindow(parent), |
| usage_stats_enabled_(mozc::config::StatsConfigUtil::IsEnabled()) { |
| // To reduce the disk IO of reading the stats config, we load it only when the |
| // class is initialized. There is no problem because the config dialog (on |
| // Mac) and the administrator dialog (on Windows) say that the usage stats |
| // setting changes will take effect after the re-login. |
| |
| if (usage_stats_enabled_) { |
| client_.reset(client::ClientFactory::NewClient()); |
| } |
| setupUi(this); |
| |
| fontComboBox->setWritingSystem( |
| static_cast<QFontDatabase::WritingSystem>(QFontDatabase::Any)); |
| fontComboBox->setEditable(false); |
| fontComboBox->setCurrentFont(tableWidget->font()); |
| |
| QObject::connect(fontComboBox, |
| SIGNAL(currentFontChanged(const QFont &)), |
| this, SLOT(updateFont(const QFont &))); |
| |
| QObject::connect(sizeComboBox, |
| SIGNAL(currentIndexChanged(int)), |
| this, SLOT(updateFontSize(int))); |
| |
| sizeComboBox->setCurrentIndex(4); |
| fontComboBox->setCurrentFont(tableWidget->font()); |
| |
| QObject::connect(tableWidget, |
| SIGNAL(itemSelected(const QTableWidgetItem*)), |
| this, |
| SLOT(itemSelected(const QTableWidgetItem*))); |
| |
| // Category Tree |
| QObject::connect(categoryTreeWidget, |
| SIGNAL(itemClicked(QTreeWidgetItem *, int)), |
| SLOT(categorySelected(QTreeWidgetItem *, int))); |
| |
| QTreeWidgetItem *unicode_item = new QTreeWidgetItem; |
| unicode_item->setText(0, QString::fromUtf8(kUNICODEName)); |
| QTreeWidgetItem *sjis_item = new QTreeWidgetItem; |
| sjis_item->setText(0, QString::fromUtf8(kCP932Name)); |
| QTreeWidgetItem *jisx0201_item = new QTreeWidgetItem; |
| jisx0201_item->setText(0, QString::fromUtf8(kJISX0201Name)); |
| QTreeWidgetItem *jisx0208_item = new QTreeWidgetItem; |
| jisx0208_item->setText(0, QString::fromUtf8(kJISX0208Name)); |
| QTreeWidgetItem *jisx0212_item = new QTreeWidgetItem; |
| jisx0212_item->setText(0, QString::fromUtf8(kJISX0212Name)); |
| |
| // Because almost all users use Shift-JIS table instead of |
| // Unicode table, Shift-JIS table is selected and child |
| // items of Shift-JIS table are expanded by default. |
| // In order to let user know the existence of the Unicode table, |
| // shows Unicode table at first, but don't expands the child items. |
| categoryTreeWidget->addTopLevelItem(unicode_item); |
| categoryTreeWidget->addTopLevelItem(sjis_item); |
| categoryTreeWidget->addTopLevelItem(jisx0201_item); |
| categoryTreeWidget->addTopLevelItem(jisx0208_item); |
| categoryTreeWidget->addTopLevelItem(jisx0212_item); |
| |
| for (size_t i = 0; kCP932JumpTo[i].name != NULL; ++i) { |
| AddItem(sjis_item, kCP932JumpTo[i].name); |
| } |
| |
| // Make Unicode block children and look-up table for each character range. |
| for (size_t i = 0; kUnicodeBlockTable[i].name != NULL; ++i) { |
| const UnicodeBlock &block = kUnicodeBlockTable[i]; |
| const UnicodeRange &range = block.range; |
| QTreeWidgetItem *item = new QTreeWidgetItem(unicode_item); |
| const QString original_name(block.name); |
| const QString translated_name(QObject::tr(block.name)); |
| unicode_block_map_[original_name] = range; |
| unicode_block_map_[translated_name] = range; |
| item->setText(0, translated_name); |
| unicode_item->addChild(item); |
| } |
| |
| // Adjust the splitter. |
| splitter->setSizes(QList<int>() |
| << static_cast<int>(width() * 0.25) |
| << static_cast<int>(width() * 0.75)); |
| |
| // set default table |
| showLocalTable(kCP932Map, kCP932MapSize); |
| categoryTreeWidget->setCurrentItem(sjis_item); |
| |
| categoryTreeWidget->setItemExpanded( |
| categoryTreeWidget->topLevelItem(0)->parent(), |
| true); |
| |
| // Select "Shift-JIS" item as a default. |
| categoryTreeWidget->setCurrentItem(sjis_item); |
| sjis_item->setExpanded(true); |
| |
| tableWidget->setAutoScroll(false); |
| |
| if (usage_stats_enabled_) { |
| CHECK(client_.get()); |
| // Sends the usage stats event (CHARACTER_PALETTE_OPEN_EVENT) to mozc |
| // converter. |
| commands::SessionCommand command; |
| command.set_type(commands::SessionCommand::USAGE_STATS_EVENT); |
| command.set_usage_stats_event( |
| commands::SessionCommand::CHARACTER_PALETTE_OPEN_EVENT); |
| commands::Output dummy_output; |
| client_->SendCommand(command, &dummy_output); |
| } |
| |
| repaint(); |
| update(); |
| } |
| |
| CharacterPalette::~CharacterPalette() {} |
| |
| void CharacterPalette::updateFont(const QFont &font) { |
| QFont new_font = font; |
| new_font.setPointSize(tableWidget->font().pointSize()); |
| tableWidget->setFont(new_font); |
| tableWidget->adjustSize(); |
| updateTableSize(); |
| } |
| |
| void CharacterPalette::updateFontSize(int index) { |
| int font_point = 24; |
| switch (index) { |
| case 0: font_point = 32; break; |
| case 1: font_point = 24; break; |
| case 2: font_point = 16; break; |
| case 3: font_point = 14; break; |
| case 4: font_point = 12; break; |
| } |
| |
| QFont font; |
| font.setPointSize(font_point); |
| tableWidget->setFont(font); |
| |
| updateTableSize(); |
| tableWidget->adjustSize(); |
| |
| QTableWidgetItem *item = tableWidget->currentItem(); |
| if (item != NULL) { |
| tableWidget->scrollToItem(item, |
| QAbstractItemView::PositionAtCenter); |
| } |
| } |
| |
| void CharacterPalette::resizeEvent(QResizeEvent *) { |
| updateTableSize(); |
| } |
| |
| void CharacterPalette::updateTableSize() { |
| // here we use "龍" to calc font size, as it looks almsot square |
| // const char kHexBaseChar[]= "龍"; |
| const char kHexBaseChar[]= "\xE9\xBE\x8D"; |
| const QRect rect = |
| QFontMetrics(tableWidget->font()).boundingRect(trUtf8(kHexBaseChar)); |
| |
| #ifdef OS_MACOSX |
| const int width = static_cast<int>(rect.width() * 2.2); |
| const int height = static_cast<int>(rect.height() * 2.0); |
| #else |
| const int width = static_cast<int>(rect.width() * 1.6); |
| const int height = static_cast<int>(rect.height() * 1.2); |
| #endif |
| |
| for (int j = 0; j < tableWidget->columnCount(); ++j) { |
| tableWidget->setColumnWidth(j, width); |
| } |
| for (int j = 0; j < tableWidget->rowCount(); ++j) { |
| tableWidget->setRowHeight(j, height); |
| } |
| tableWidget->setLookupResultItem(NULL); |
| } |
| |
| void CharacterPalette::categorySelected(QTreeWidgetItem *item, |
| int column) { |
| const QString &text = item->text(column); |
| const QTreeWidgetItem *parent = item->parent(); |
| |
| item->setExpanded(!item->isExpanded()); |
| |
| if (text == kUNICODEName) { // TOP Unicode |
| // The number of characters within entire Unicode range is now too large to |
| // show in the table. So we show only UCS2 range when |kUNICODEName| is |
| // clicked. |
| showUnicodeTableByRange(kUCS2Range); |
| } else if (parent != NULL && parent->text(0) == kUNICODEName) { |
| showUnicodeTableByBlockName(text); |
| } else if (parent != NULL && parent->text(0) == kCP932Name) { |
| showSJISBlockTable(text); |
| } else if (text == kJISX0201Name) { |
| showLocalTable(kJISX0201Map, kJISX0201MapSize); |
| } else if (text == kJISX0208Name) { |
| showLocalTable(kJISX0208Map, kJISX0208MapSize); |
| } else if (text == kJISX0212Name) { |
| showLocalTable(kJISX0212Map, kJISX0212MapSize); |
| } else if (text == kCP932Name) { |
| showLocalTable(kCP932Map, kCP932MapSize); |
| } |
| } |
| |
| void CharacterPalette::itemSelected(const QTableWidgetItem *item) { |
| if (!usage_stats_enabled_) { |
| return; |
| } |
| CHECK(client_.get()); |
| // Sends the usage stats event (CHARACTER_PALETTE_COMMIT_EVENT) to mozc |
| // converter. |
| commands::SessionCommand command; |
| command.set_type(commands::SessionCommand::USAGE_STATS_EVENT); |
| command.set_usage_stats_event( |
| commands::SessionCommand::CHARACTER_PALETTE_COMMIT_EVENT); |
| commands::Output dummy_output; |
| client_->SendCommand(command, &dummy_output); |
| } |
| |
| // Unicode operations |
| void CharacterPalette::showUnicodeTableByRange(const UnicodeRange &range) { |
| tableWidget->hide(); |
| tableWidget->clear(); |
| |
| QStringList column_header; |
| for (int col = 0; col < kHexBase; ++col) { |
| column_header << QString::number(col, kHexBase).toUpper(); |
| } |
| |
| QStringList row_header; |
| for (char32 ucs4 = range.first; ucs4 <= range.last; ucs4 += kHexBase) { |
| QString str; |
| str.sprintf("U+%3.3X0", ucs4 / kHexBase); |
| row_header << str; |
| } |
| |
| tableWidget->setColumnCount(kHexBase); |
| tableWidget->setRowCount(row_header.size()); |
| |
| tableWidget->setHorizontalHeaderLabels(column_header); |
| tableWidget->setVerticalHeaderLabels(row_header); |
| |
| const char32 offset = range.first / kHexBase; |
| for (char32 ucs4 = range.first; ucs4 <= range.last; ++ucs4) { |
| const char32 ucs4s[] = { ucs4 }; |
| QTableWidgetItem *item = |
| new QTableWidgetItem(QString::fromUcs4(ucs4s, arraysize(ucs4s))); |
| item->setTextAlignment(Qt::AlignCenter); |
| tableWidget->setItem(ucs4 / kHexBase - offset, ucs4 % kHexBase, item); |
| tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); |
| } |
| |
| tableWidget->scrollToItem(tableWidget->item(0, 0), |
| QAbstractItemView::PositionAtTop); |
| tableWidget->setLookupResultItem(NULL); |
| tableWidget->show(); |
| } |
| |
| void CharacterPalette::showSJISBlockTable(const QString &name) { |
| const CP932JumpTo *block = NULL; |
| for (int i = 0; kCP932JumpTo[i].name !=NULL; ++i) { |
| if (name == QString::fromUtf8(kCP932JumpTo[i].name)) { |
| block = &kCP932JumpTo[i]; |
| break; |
| } |
| } |
| |
| if (block == NULL) { |
| return; |
| } |
| |
| showLocalTable(kCP932Map, kCP932MapSize); |
| |
| tableWidget->hide(); |
| QTableWidgetItem *item = tableWidget->item(block->from / kHexBase - |
| kCP932Map[0].from / kHexBase, |
| block->from % kHexBase); |
| |
| if (item != NULL) { |
| tableWidget->scrollToItem(item, QAbstractItemView::PositionAtTop); |
| item->setSelected(true); |
| } |
| |
| tableWidget->setLookupResultItem(NULL); |
| tableWidget->show(); |
| } |
| |
| void CharacterPalette::showUnicodeTableByBlockName(const QString &block_name) { |
| QMap<QString, UnicodeRange>::const_iterator |
| it = unicode_block_map_.find(block_name); |
| if (it == unicode_block_map_.end()) { |
| return; |
| } |
| showUnicodeTableByRange(it.value()); |
| } |
| |
| // Local table |
| void CharacterPalette::showLocalTable(const LocalCharacterMap *local_map, |
| size_t local_map_size) { |
| tableWidget->hide(); |
| tableWidget->clear(); |
| |
| QStringList column_header; |
| for (int col = 0; col < kHexBase; ++col) { |
| column_header << QString::number(col, kHexBase).toUpper(); |
| } |
| |
| // find range |
| const int from_start = local_map[0].from; |
| const int from_end = local_map[local_map_size - 1].from + kHexBase; |
| |
| QStringList row_header; |
| for (int i = from_start; i < from_end; i += kHexBase) { |
| QString str; |
| str.sprintf("0x%X0", i / kHexBase); |
| row_header << str; |
| } |
| |
| tableWidget->setColumnCount(kHexBase); |
| tableWidget->setRowCount(row_header.size()); |
| |
| tableWidget->setHorizontalHeaderLabels(column_header); |
| tableWidget->setVerticalHeaderLabels(row_header); |
| |
| const int offset = from_start / kHexBase; |
| for (size_t i = 0; i < local_map_size; ++i) { |
| // We do not use QString(QChar(i)) but Util::UCS4ToUTF8 because |
| // QChar is only 16-bit. |
| string utf8; |
| Util::UCS4ToUTF8(local_map[i].ucs2, &utf8); |
| QTableWidgetItem *item = new QTableWidgetItem( |
| QString::fromUtf8(utf8.data(), utf8.size())); |
| item->setTextAlignment(Qt::AlignCenter); |
| tableWidget->setItem(local_map[i].from / kHexBase - offset, |
| local_map[i].from % kHexBase, item); |
| tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); |
| } |
| |
| tableWidget->scrollToItem(tableWidget->item(0, 0), |
| QAbstractItemView::PositionAtCenter); |
| |
| tableWidget->setLookupResultItem(NULL); |
| tableWidget->show(); |
| } |
| |
| #ifdef OS_WIN |
| bool CharacterPalette::winEvent(MSG *message, long *result) { |
| if (message != NULL && |
| message->message == WM_LBUTTONDOWN && |
| WinUtil::IsCompositionEnabled()) { |
| const QWidget *widget = qApp->widgetAt( |
| mapToGlobal(QPoint(message->lParam & 0xFFFF, |
| (message->lParam >> 16) & 0xFFFF))); |
| if (widget == centralwidget) { |
| ::PostMessage(message->hwnd, WM_NCLBUTTONDOWN, |
| static_cast<WPARAM>(HTCAPTION), message->lParam); |
| return true; |
| } |
| } |
| |
| return QWidget::winEvent(message, result); |
| } |
| #endif // OS_WIN |
| } // namespace gui |
| } // namespace mozc |