#include "gui/character_pad/character_palette.h"

#include <QtGui/QtGui>
#include <QtWidgets/QMessageBox>

#ifdef OS_WIN
#include <Windows.h>
#endif  

#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 "protocol/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 CharacterPalette::UnicodeRange kUCS2Range = { 0, 0xffff };

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 }
};

QTreeWidgetItem *AddItem(QTreeWidgetItem *parent, const char *name) {
  QTreeWidgetItem *item = new QTreeWidgetItem(parent);
  item->setText(0, QString::fromUtf8(name));
  parent->addChild(item);
  return item;
}

}  

CharacterPalette::CharacterPalette(QWidget *parent)
    : QMainWindow(parent),
      usage_stats_enabled_(mozc::config::StatsConfigUtil::IsEnabled()) {

  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*)));

  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));

  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);
  }

  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);
  }

  splitter->setSizes(QList<int>()
                     << static_cast<int>(width() * 0.25)
                     << static_cast<int>(width() * 0.75));

  showLocalTable(kCP932Map, kCP932MapSize);
  categoryTreeWidget->setCurrentItem(sjis_item);

  categoryTreeWidget->setItemExpanded(
      categoryTreeWidget->topLevelItem(0)->parent(),
      true);

  categoryTreeWidget->setCurrentItem(sjis_item);
  sjis_item->setExpanded(true);

  tableWidget->setAutoScroll(false);

  if (usage_stats_enabled_) {
    CHECK(client_.get());

    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() {

  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) {     

    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());

  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);
}

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());
}

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();
  }

  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) {

    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();
}

}  
}
// Copyright 2010-2016, 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 <QtWidgets/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 "protocol/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 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 }
};

// Adds a child QTreeWidgetItem with the text "name" to parent.
// Returns the added 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();
}

}  // namespace gui
}  // namespace mozc
