#include "kanjiconvert.h"
#include "sockkanjiclient.h"
#include "dicthiragana.h"
#include "preeditarea.h"
#include "inputmode.h"
#include "config.h"
#include <X11/keysym.h>

QIntDict<FuncPtr>  KanjiConvert::funcptrdict = QIntDict<FuncPtr>();

KanjiConvert::KanjiConvert() : _curphsidx(0),
			       _curphsyomi(QString()),
			       _inputkeys(QString()),
			       _yomigana(QString()),
			       _convtkanjilist(QStringList()),
			       _stat(NONE_YOMI),
			       _mode(InputMode()), 
			       _spotpreedit(QPoint()),
			       _dict(DictHiragana()),
			       _functbl(QIntDict<FuncNameArray>())
{
  // Create socket
  _sockkanjiclt = new SockKanjiClient();

  // Create PreeditArea instance
  _preedit = new PreeditArea();

  // Create CandidacyListBox instance
  _candlist = new CandidacyListBox();

  connect(_sockkanjiclt, SIGNAL(allPhasesConvt(const QStringList&)), 
	  this,  SLOT(slotConvertAllPhases(const QStringList&)));
  connect(_sockkanjiclt, SIGNAL(reconvtPhases(const QStringList&)), 
	  this,  SLOT(slotReconvtPhases(const QStringList&)));
  connect(_sockkanjiclt, SIGNAL(candidacyList(const QStringList&)), 
  	  this, SLOT(slotCandidacyList(const QStringList&)));
  connect(_sockkanjiclt, SIGNAL(currentYomi(const QString&)), 
	  this, SLOT(slotYomigana(const QString&)));
  connect(_candlist, SIGNAL(clicked(QListBoxItem*)),
	  this, SLOT(slotDecidePhases(QListBoxItem*)));

  clear();

  // Create function table instance
  _functbl.setAutoDelete( TRUE );
}
 

KanjiConvert::~KanjiConvert()
{
  delete _sockkanjiclt;
}


void
KanjiConvert::setInputMode(const InputMode& mode)
{
  _mode.merge(mode);
  clear();
}


void    
KanjiConvert::clear()
{
  _inputkeys = "";
  _yomigana = "";
  _curphsidx = 0;
  //_curcandcyidx = 0;
  _convtkanjilist.clear();
  //_curcandcylist.clear();
  if (_stat >= CONVERTING_YOMI) {
    _sockkanjiclt->endConvert();
  }
  _stat = NONE_YOMI;
  toolboxHide();
}


void
KanjiConvert::setFont(QFont ft) 
{
  _preedit->setFont(ft);        // Set font to preedit area
  _candlist->setFont(ft);       // Set font to candidacy list box
}


void
KanjiConvert::convertAllPhases()
{
  if (_mode.id() & InputMode::RomaInput && 
      (_mode.id() & InputMode::Hiragana || _mode.id() & InputMode::Katakana)) {
    _yomigana = _dict.forceConvtYomi(_inputkeys, InputMode(InputMode::Hiragana)); 
  }
  
  if (_yomigana.isEmpty()) return;
  
  _sockkanjiclt->beginConvert(_yomigana);
}


void
KanjiConvert::convertCurrPhase()
{
  if (_sockkanjiclt->isConverting()) {
    _sockkanjiclt->getCandidacyList(_curphsidx);
  } else {
    convertAllPhases();
  }
}


void 
KanjiConvert::nextCandidacy()
{
  if ( _candlist->isVisible() ) {  
    // means CandidacyListBox shows
    _candlist->showNextCandidacy();
    
    // Replace string of preeidt area
    _convtkanjilist.pop_front();
    _convtkanjilist.push_front(_candlist->currentText());
    
    // show preedit
    //_preedit->showText(_spotpreedit, _convtkanjilist);
    _preedit->showConvertingPhases(_spotpreedit, _convtkanjilist);

    _stat = SHOWING_LIST;

  } else if (_sockkanjiclt->isConverting()) {
    // Get Candidacies
    _sockkanjiclt->getCandidacyList(_curphsidx);

  } else {
    convertAllPhases();
  }
}


void 
KanjiConvert::previousCandidacy()
{
  if ( _candlist->isVisible() ) {  
    // It means CandidacyListBox shows
    _candlist->showPreviousCandidacy();
    
    // Replace string of preeidt area
    _convtkanjilist.pop_front();
    _convtkanjilist.push_front(_candlist->currentText());
    
    // show preedit
    //_preedit->showText(_spotpreedit, _convtkanjilist);
    _preedit->showConvertingPhases(_spotpreedit, _convtkanjilist);

    _stat = SHOWING_LIST;
  }  
}


// Append a character to roma string
void
KanjiConvert::appendChar(const QKeyEvent& ke)
{
  if ( ke.count() != 1 ) return;
  
  _stat = INPUTING_YOMI;
  
  if (ke.state() == Keypad && Config::readEntry("_cmbtenkey", "") == tr("半角")) {
    // case Tenkey
    _inputkeys += ke.text();
    _yomigana  += ke.text();
    
  } else if (_mode.id() & InputMode::ZenkakuEisu || _mode.id() & InputMode::RomaInput) {
    // case Zenkaku Eisu or Roma-Input 
    _inputkeys += ke.text();
    _yomigana = _dict.convertYomi(_inputkeys, _mode);
    
  } else if (_mode.id() & InputMode::KanaInput) {
    // case Kana-Input
    _yomigana += _dict.convertYomi( ke );
    _yomigana = _dict.convertYomi(_yomigana, InputMode(InputMode::Hiragana));  // 濁点文字変換
    _yomigana = _dict.convertYomi(_yomigana, _mode);
  }
  
  _preedit->showInputingString(_spotpreedit,  _yomigana);
  qDebug("Yomigana:%s", _yomigana.local8Bit().data());
}


void
KanjiConvert::deleteBackward()
{
  int nrmstr = 1;
  int nrmkeys = 1;

  if ( _inputkeys.isEmpty() && _yomigana.isEmpty()) {
    _stat = NONE_YOMI;

  } else if (_mode.id() & InputMode::KanaInput || _mode.id() & InputMode::ZenkakuEisu) {
    // case Kana Input or Zenkaku-Eisu
    _inputkeys.remove(_inputkeys.length() - 1, 1);
    _yomigana.remove(_yomigana.length() - 1, 1);
    _stat = INPUTING_YOMI;
 
  } else if(_mode.id() & InputMode::RomaInput) {
    // case Roma Input
    while (nrmstr < 4) {
      QString s = _yomigana.right(nrmstr);
      QString t = _inputkeys.right(nrmkeys);
      if ( _dict.convertYomi(t, _mode.id()) == s) break;

      nrmkeys++;
      if (nrmkeys > 4) {
	nrmstr++;
	nrmkeys = 1;
      }
    }

    if (nrmstr >= 4) {
      nrmkeys = nrmstr = 1;
    }
    //qDebug("%d %d", nrmstr, nrmkeys);
    
    _inputkeys.remove(_inputkeys.length() - nrmkeys, nrmkeys);
    _yomigana.remove(_yomigana.length() - nrmstr, nrmstr);
    Q_ASSERT(_yomigana == _dict.convertYomi(_inputkeys, _mode.id()) || _yomigana == _dict.forceConvtYomi(_inputkeys, _mode.id()));

    qDebug("current Roma string:%s", _inputkeys.local8Bit().data());
    _stat = INPUTING_YOMI;
    
  } else {
    Q_ASSERT(0);
  }

  // Show preedit 
  if ( _yomigana.isEmpty() ) {
    _stat = NONE_YOMI;
    toolboxHide();

  } else { 
    _preedit->showInputingString(_spotpreedit, _yomigana);
  }

  qDebug("current Yomigana:%s", _yomigana.local8Bit().data());
}


void
KanjiConvert::decideCurrPhase()
{
  toolboxHide();

  emit decidePhases(_convtkanjilist.first());
  _convtkanjilist.pop_front();
  _curphsidx++;

  // Check if convtkanjilist is empty or not
  if ( _convtkanjilist.isEmpty() ) {
    qDebug("_convtkanjilist is Empty");
    _sockkanjiclt->endConvert(); 
    _stat = NONE_YOMI;
    clear();
    return;
  }

  qDebug("phases remaining(%d): %s", _convtkanjilist.count(), _convtkanjilist.join(" ").local8Bit().data());

  //_preedit->showText(_spotpreedit, _convtkanjilist);
  _preedit->showConvertingPhases(_spotpreedit, _convtkanjilist);

  // Remove the converted string from Yomigana
  _yomigana.remove(0, _curphsyomi.length()); 
  Q_ASSERT( !_yomigana.isEmpty() );
  qDebug("current yomigana:%s", _yomigana.local8Bit().data());
  
  if (_mode.id() & InputMode::Hiragana || _mode.id() & InputMode::Katakana) {
    // Remove the converted string from Roma string
    for (int i = 1; i <= _inputkeys.length(); i++) {
      if (_dict.convertYomi(_inputkeys.left(i)) == _curphsyomi) {
	_inputkeys.remove(0, i);
	break;
	
      } else if (_dict.forceConvtYomi(_inputkeys.left(i - 1)) == _curphsyomi) {
	_inputkeys.remove(0, i - 1);
	break;
      }
    }

    Q_ASSERT( !_inputkeys.isEmpty() );
    Q_ASSERT(_dict.forceConvtYomi(_inputkeys) == _yomigana);
    qDebug("roma string:%s", _inputkeys.local8Bit().data());
  }

  // Get yomigana of next candidacy
  _sockkanjiclt->getYomi(_curphsidx);

  _stat = CONVERTING_YOMI;
}


void
KanjiConvert::decideAllPhases()
{
  if (_stat > INPUTING_YOMI) {
    _sockkanjiclt->endConvert();
    _stat = NONE_YOMI;
  }
 
  if ( _convtkanjilist.isEmpty() ) {
    emit decidePhases(_yomigana);
  } else {
    emit decidePhases(_convtkanjilist.join(""));
  }

  clear();  
}


void
KanjiConvert::cancelConversion()
{
  // send End Convert message
  _sockkanjiclt->endConvert(); 
  _stat = INPUTING_YOMI;
  
  _curphsidx = 0;
  _convtkanjilist.clear();
  toolboxHide();
  
  if (_mode.id() & InputMode::ZenkakuEisu ||_mode.id() & InputMode::RomaInput) {
    // case Roma-Input
    _preedit->showInputingString(_spotpreedit, _dict.forceConvtYomi(_inputkeys, _mode));
    
  } else if (_mode.id() & InputMode::KanaInput) {
    // case Kana-Input
    _preedit->showInputingString(_spotpreedit, _yomigana);
  }
}


void    
KanjiConvert::lengthenPhase()
{
  if (_curphsyomi.isEmpty()) return;
  Q_ASSERT(!_yomigana.isEmpty());
  
  if (_mode.id() & InputMode::RomaInput) {
    // case Roma-Input
    int offset_ym = 1;
    int offset_ik = 1;
    
    while (_yomigana.length() >= _curphsyomi.length() + offset_ym) {
      // 入力キーと整合をとりながら文節の長さを長くする
      QString yomi = _yomigana.left(_curphsyomi.length() + offset_ym);
      QString keys = _dict.convertYomi(_inputkeys.left(offset_ik));
      if (yomi == keys || (_inputkeys.length() == offset_ik && yomi == _dict.forceConvtYomi(_inputkeys))) {
	toolboxHide();
	_sockkanjiclt->resizePause(_curphsidx, _curphsyomi.length() + offset_ym);
	
	// set current yomigana
	_curphsyomi = _yomigana.left(_curphsyomi.length() + offset_ym);
	_stat = CHANGING_LENGTH;
	break;
      }
      
      if (offset_ik++ >= _inputkeys.length() || (keys.length() > yomi.length() + 3)) {
	offset_ym++;
	offset_ik = 1;
      }
    }

  } else if (_mode.id() & InputMode::KanaInput) {
    // case Kana-Input
    toolboxHide();
    _sockkanjiclt->resizePause(_curphsidx, _curphsyomi.length() + 1);
    
    // set current yomigana
    _curphsyomi = _yomigana.left(_curphsyomi.length() + 1);
    _stat = CHANGING_LENGTH;
  }
}


void    
KanjiConvert::shortenPhase()
{  
  if (_curphsyomi.isEmpty()) return;
  Q_ASSERT(!_yomigana.isEmpty());
  
  if (_mode.id() & InputMode::RomaInput) {
    // case Roma-Input
    int offset_ym = 1;
    int offset_ik = 1;
    while (_curphsyomi.length() > offset_ym) {
      // 入力キーと整合をとりながら文節の長さを短くする
      if (_curphsyomi.left(_curphsyomi.length() - offset_ym) == _dict.convertYomi(_inputkeys.left(offset_ik))) {
	toolboxHide();
	_sockkanjiclt->resizePause(_curphsidx, _curphsyomi.length() - offset_ym);
	
	// set current yomigana
	_curphsyomi = _curphsyomi.left(_curphsyomi.length() - offset_ym);
	
	_stat = CHANGING_LENGTH;
	break;
      }
      
      if (offset_ik++ >= _inputkeys.length()) {
	offset_ym++;
	offset_ik = 1;
      }
    }

  } else if (_mode.id() & InputMode::KanaInput) {
    toolboxHide();
    _sockkanjiclt->resizePause(_curphsidx, _curphsyomi.length() - 1);
    
    // set current yomigana
    _curphsyomi = _curphsyomi.left(_curphsyomi.length() - 1);
    
    _stat = CHANGING_LENGTH;
  }
}


void
KanjiConvert::inputSpaceChar()
{
  QString s = Config::readEntry("_cmbspacekey", "");
  if (s == tr("半角")) {
    emit decidePhases(" ");   // Ascii space
    
  } else if (s == tr("全角")) {
    emit decidePhases(tr("　"));  // Multibyte space
  }
  
  clear(); 
}


void
KanjiConvert::convertToHira()
{
  toolboxHide();

  switch (_stat) {
  case INPUTING_YOMI:
    Q_ASSERT(!_yomigana.isEmpty());
    _yomigana = _curphsyomi = _dict.forceConvtYomi(_inputkeys);
    slotConvertAllPhases(QStringList(_yomigana));  // Show HIRAGANA
    break;
 
  case CONVERTING_YOMI:
  case SHOWING_LIST:
  case CHANGING_LENGTH:
    // convert first phase to Hiragara
    Q_ASSERT(!_curphsyomi.isEmpty());
    _convtkanjilist.pop_front();
    _convtkanjilist.push_front(_curphsyomi);
    slotConvertAllPhases(_convtkanjilist);
    break;

  default:
    Q_ASSERT(0);
    break;
  }
}


void
KanjiConvert::convertToKata()
{
  toolboxHide();
 
  switch (_stat) {
  case INPUTING_YOMI:
    _yomigana = _curphsyomi = _dict.forceConvtYomi(_inputkeys);
    slotConvertAllPhases(QStringList(_dict.forceConvtYomi(_inputkeys, InputMode(InputMode::Katakana))));   // Show KATAKANA
    break;
    
  case CONVERTING_YOMI:
  case SHOWING_LIST:
  case CHANGING_LENGTH:
    // convert first phase to Katakana
    Q_ASSERT(!_curphsyomi.isEmpty());
    _convtkanjilist.pop_front();
    _convtkanjilist.push_front(_dict.forceConvtYomi(_curphsyomi, InputMode(InputMode::Katakana)));
    slotConvertAllPhases(_convtkanjilist);
    break;

  default:
    Q_ASSERT(0);
    break;
  }
}


void
KanjiConvert::convertToHankaku()
{
  toolboxHide();

  switch (_stat) {
  case INPUTING_YOMI:
    _yomigana = _curphsyomi = _dict.forceConvtYomi(_inputkeys);
    slotConvertAllPhases(QStringList(_inputkeys));   // Show HANKAKU
    break;
    
  case CONVERTING_YOMI:
  case SHOWING_LIST:
  case CHANGING_LENGTH:
    Q_ASSERT(!_curphsyomi.isEmpty());
    for (int i = 1; i <= _inputkeys.length(); i++) {
      if (_dict.convertYomi(_inputkeys.left(i)) == _curphsyomi) {
	// convert first phase to Hiragara
	_convtkanjilist.pop_front();
	_convtkanjilist.push_front(_inputkeys.left(i));
	break;
      } else if (_dict.forceConvtYomi(_inputkeys.left(i - 1)) == _curphsyomi) {
	// convert first phase to Hiragara
	_convtkanjilist.pop_front();
	_convtkanjilist.push_front(_inputkeys.left(i - 1));
	break;
      }
    }
    slotConvertAllPhases(_convtkanjilist);
    break;

  default:
    Q_ASSERT(0);
    break;
  }
}


void
KanjiConvert::convertToZenkaku()
{
  toolboxHide();
 
  switch (_stat) {
  case INPUTING_YOMI:
    _yomigana = _curphsyomi = _dict.forceConvtYomi(_inputkeys);
    slotConvertAllPhases(QStringList(_dict.forceConvtYomi(_inputkeys, InputMode(InputMode::ZenkakuEisu))));   // Show HANKAKU
    break;
    
  case CONVERTING_YOMI:
  case SHOWING_LIST:
  case CHANGING_LENGTH:
    Q_ASSERT(!_curphsyomi.isEmpty());
    for (int i = 1; i <= _inputkeys.length(); i++) {
      if (_dict.convertYomi(_inputkeys.left(i)) == _curphsyomi) {
	// convert first phase to Hiragara
	_convtkanjilist.pop_front();
	_convtkanjilist.push_front(_dict.forceConvtYomi(_inputkeys.left(i), InputMode(InputMode::ZenkakuEisu)));
	break;
      } else if (_dict.forceConvtYomi(_inputkeys.left(i - 1)) == _curphsyomi) {
	// convert first phase to Hiragara
	_convtkanjilist.pop_front();
	_convtkanjilist.push_front(_dict.forceConvtYomi(_inputkeys.left(i - 1), InputMode(InputMode::ZenkakuEisu)));
	break;
      }
    }
    slotConvertAllPhases(_convtkanjilist);
    break;

  default:
    Q_ASSERT(0);
    break;
  }
}


void
KanjiConvert::slotConvertAllPhases(const QStringList& strs)
{
  if (_stat == NONE_YOMI) {
    _sockkanjiclt->endConvert();
    return;
  }
  
  qDebug("High priority candidacies(%d):%s", strs.count(), strs.join(" ").local8Bit().data());

  _convtkanjilist = strs;
  _preedit->showConvertingPhases(_spotpreedit, _convtkanjilist);

  // get yomigana of current phase
  _sockkanjiclt->getYomi(_curphsidx);

  _stat = CONVERTING_YOMI;
}


void 
KanjiConvert::slotReconvtPhases(const QStringList& strs)
{
  qDebug("High-priority-candidacies(%d):%s", strs.count(), strs.join(" ").local8Bit().data());
  
  // show yomigana
  _convtkanjilist = strs;
  _convtkanjilist.pop_front();
  _convtkanjilist.push_front(_curphsyomi);
  _preedit->showChangingPhaseLength(_spotpreedit, _convtkanjilist);

  _stat = CHANGING_LENGTH;
}


// curidx: current selected candidacy index
// strs:   candidacy list
void
KanjiConvert::slotCandidacyList(const QStringList& strs)
{
  qDebug("candidacy:%s", strs.join(" ").local8Bit().data()); 

  QStringList list = strs;
  if ( list.isEmpty() ) list.push_front(_curphsyomi);
  
  if (_stat == CONVERTING_YOMI && list.first() == _convtkanjilist.first()) {
    _candlist->showList(_spotpreedit, list, 1);
  } else {
    _candlist->showList(_spotpreedit, list);
  }
  
  // Replace string of converted kanji string list
  _convtkanjilist.pop_front();
  _convtkanjilist.push_front(_candlist->currentText());
  _preedit->showConvertingPhases(_spotpreedit, _convtkanjilist); // show preedit

  _stat = SHOWING_LIST;
}


void 
KanjiConvert::slotYomigana(const QString& str)
{
  _curphsyomi = str;
  qDebug("current phase yomigana:%s", _curphsyomi.local8Bit().data());
}



void 
KanjiConvert::slotDecidePhases(QListBoxItem* item)
{
  if ( item ) {
    _convtkanjilist.pop_front();
    _convtkanjilist.push_front(item->text());
    decideCurrPhase();
  }
}


void
KanjiConvert::toolboxHide()
{
  _preedit->hide();
  _candlist->hide();
}


bool
KanjiConvert::processKeyEvent(const QKeyEvent& key)
{
  // If pushing ctrl-key, returns this event.
  if (key.state() & Qt::ControlButton || key.state() & Qt::AltButton) return FALSE;

  FuncName* pn = (FuncName*)_functbl[key.key()];
  if ( !pn ) {
    if (_stat >= CONVERTING_YOMI && _stat <= CHANGING_LENGTH) {
      decideAllPhases();
    }
    
    qDebug("The key code: %x   key count: %d  ascii: %s", key.key(), key.count(), key.text().data());
    if (key.count() > 0 && !key.text().isNull()) {
      appendChar( key );
      return TRUE;
    } else {
      return FALSE;
    }
  }
  
  if (pn[_stat] == NONE) {
    return TRUE;         // Do nothing
  } else if (pn[_stat] == KEYFORWARD) {
    return FALSE;        // Returns this event.
  }

  pmfunc  pf = funcptrdict[pn[_stat]]->ptr();  // get pointer
  if ( !pf ) {
    qWarning("%s(%d): incorrect processing?", __FUNCTION__, __LINE__);
    return FALSE;
  }
  
  qDebug("FuncName: %s", funcptrdict[pn[_stat]]->funcName().data());
  (this->*pf)();      // execute function
  return TRUE;
}


void 
KanjiConvert::setPreeditPoint(QPoint p)
{
  _spotpreedit = p;
  
  if ( !_preedit->isHidden() ) {
    _preedit->showText(_spotpreedit);
  }
}


void 
KanjiConvert::init()
{
  // initialize socket
  _sockkanjiclt->init();
  clear();

  // Dictionary Initialization
  _dict.init();

  /*
  // Set default mode
  QString s(Config::readEntry("_cmbinputmode", ""));
  if (s == tr("ひらがな")) {
    _mode = InputMode::Hiragana;
    
  } else if (s == tr("カタカナ")) {
    _mode = InputMode::Katakana;
    
  } else if (s == tr("全角英数")) {
    _mode = InputMode::ZenkakuEisu;

  } else {
    Q_ASSERT(0);
  }
  
  s = Config::readEntry("_cmbinputmethod", "");
  if (s == tr("ローマ字入力")) {
    _mode.merge(InputMode::RomaInput);
 
  } else if (s == tr("かな入力")) {
    _mode.merge(InputMode::KanaInput);
 
  } else {
    Q_ASSERT(0);
  }
  */

  if ( ! _functbl.isEmpty() ) return;

  // initialze key table
  _functbl.insert((int)Qt::Key_Space, newFuncNameArray(INPUTSPACECHAR, CONVERTALLPHASES, NEXTCANDIDACY, NEXTCANDIDACY, CONVERTCURRPHASE));
  _functbl.insert((int)Qt::Key_Return, newFuncNameArray(KEYFORWARD, DECIDEALLPHASES, DECIDEALLPHASES, DECIDEALLPHASES, DECIDEALLPHASES));
  _functbl.insert((int)Qt::Key_Enter, newFuncNameArray(KEYFORWARD, DECIDEALLPHASES, DECIDEALLPHASES, DECIDEALLPHASES, DECIDEALLPHASES));
  _functbl.insert((int)Qt::Key_Backspace, newFuncNameArray(KEYFORWARD, DELETEBACKWARD, CANCELCONVERSION, CANCELCONVERSION, CANCELCONVERSION));
  _functbl.insert((int)Qt::Key_Down, newFuncNameArray(KEYFORWARD, NONE, DECIDECURRPHASE, DECIDECURRPHASE, DECIDECURRPHASE));
  _functbl.insert((int)Qt::Key_Up, newFuncNameArray(KEYFORWARD, NONE, PREVIOUSCANDIDACY, PREVIOUSCANDIDACY, NONE));
  _functbl.insert((int)Qt::Key_Right, newFuncNameArray(KEYFORWARD, NONE, LENGTHENPHASE, LENGTHENPHASE, LENGTHENPHASE));
  _functbl.insert((int)Qt::Key_Left, newFuncNameArray(KEYFORWARD, NONE, SHORTENPHASE, SHORTENPHASE, SHORTENPHASE));
  _functbl.insert((int)Qt::Key_Delete, newFuncNameArray(KEYFORWARD, NONE, NONE, NONE, NONE));
  _functbl.insert((int)Qt::Key_F6, newFuncNameArray(KEYFORWARD, CONVERTTOHIRA, CONVERTTOHIRA, CONVERTTOHIRA, CONVERTTOHIRA));
  _functbl.insert((int)Qt::Key_F7, newFuncNameArray(KEYFORWARD, CONVERTTOKANA, CONVERTTOKANA, CONVERTTOKANA, CONVERTTOKANA));
  _functbl.insert((int)Qt::Key_F8, newFuncNameArray(KEYFORWARD, CONVERTTOHANKAKU, CONVERTTOHANKAKU, CONVERTTOHANKAKU, CONVERTTOHANKAKU));
  _functbl.insert((int)Qt::Key_F9, newFuncNameArray(KEYFORWARD, CONVERTTOZENKAKU, CONVERTTOZENKAKU, CONVERTTOZENKAKU, CONVERTTOZENKAKU));

  // Initialize  dictionary of function pointer
  init_funcptrdict();
}


void 
KanjiConvert::init_funcptrdict()
{
  if ( ! funcptrdict.isEmpty() ) return;

  funcptrdict.setAutoDelete(TRUE);
  
  funcptrdict.insert(NEXTCANDIDACY, new FuncPtr(QString("次候補"), &KanjiConvert::nextCandidacy));
  funcptrdict.insert(PREVIOUSCANDIDACY, new FuncPtr(QString("前候補"), &KanjiConvert::previousCandidacy));
  funcptrdict.insert(CONVERTALLPHASES, new FuncPtr(QString("全変換"), &KanjiConvert::convertAllPhases));
  funcptrdict.insert(CONVERTCURRPHASE, new FuncPtr(QString("文節変換"), &KanjiConvert::convertCurrPhase));
  funcptrdict.insert(DELETEBACKWARD, new FuncPtr(QString("前字削除"), &KanjiConvert::deleteBackward));
  funcptrdict.insert(DECIDEALLPHASES, new FuncPtr(QString("全確定"),  &KanjiConvert::decideAllPhases));
  funcptrdict.insert(DECIDECURRPHASE, new FuncPtr(QString("文節確定"), &KanjiConvert::decideCurrPhase));
  funcptrdict.insert(CANCELCONVERSION, new FuncPtr(QString("全戻し"), &KanjiConvert::cancelConversion));
  funcptrdict.insert(LENGTHENPHASE, new FuncPtr(QString("文節長 +1"), &KanjiConvert::lengthenPhase));
  funcptrdict.insert(SHORTENPHASE, new FuncPtr(QString("文節長 -1"), &KanjiConvert::shortenPhase));
  funcptrdict.insert(INPUTSPACECHAR, new FuncPtr(QString("空白文字"), &KanjiConvert::inputSpaceChar));
  funcptrdict.insert(CONVERTTOHIRA, new FuncPtr(QString("ひらがな"), &KanjiConvert::convertToHira));
  funcptrdict.insert(CONVERTTOKANA, new FuncPtr(QString("カタカナ"), &KanjiConvert::convertToKata));
  funcptrdict.insert(CONVERTTOHANKAKU, new FuncPtr(QString("半角英数"), &KanjiConvert::convertToHankaku));
  funcptrdict.insert(CONVERTTOZENKAKU, new FuncPtr(QString("全角英数"), &KanjiConvert::convertToZenkaku));
}


// Allocate and initialize FuncNameArray
FuncNameArray*
KanjiConvert::newFuncNameArray(FuncName f0, FuncName f1, FuncName f2, FuncName f3, FuncName f4) const
{
  FuncName* pa = new FuncNameArray;
  Q_CHECK_PTR( pa );
  pa[0] = f0;      // for NONE_YOMI
  pa[1] = f1;      // for INPUTING_YOMI
  pa[2] = f2;      // for CONVERTING_YOMI
  pa[3] = f3;      // for SHOWING_LIST
  pa[4] = f4;      // for CHANGING_LENGTH

  return (FuncNameArray*)pa;
}








