/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <swidget/SEditor.h>
#include <swidget/SEditorIF.h>
#include <stoolkit/SEncoder.h>

/**
 * All editors are like this one.
 */
SEditor::SEditor(void)
{
  cleared = false;
  paragBreak = "\n";
  editorIF = 0;
  vimode = false;
  undoIndex = 0;
  setup();
  
}

SEditor::SEditor (const SString& lb)
{
  cleared = false;
  paragBreak = lb;
  editorIF = 0;
  vimode = false;
  undoIndex = 0;
  setup();
}

void
SEditor::setup()
{
  inputMethods.append (SS_DEFAULT_IM);
  inputMethods.append ("unicode");
  inputMethods.append (SS_KINPUT2_IM);
  inputMethods.append ("Hungarian");
  inputMethods.append ("Kana");
  inputMethods.append ("Hangul");
  fonts.append (SS_DEFAULT_FONT);
  fonts.append ("Bitmap");
  fonts.append ("TrueType");
}

/**
 * Copy editor and lose undo's
 */
SEditor::SEditor (const SEditor& in)
{
  editorIF = in.editorIF;
  vimode = in.vimode;
  inputMethods = in.inputMethods;
  fonts = in.fonts;
  undoIndex = 0;
  paragBreak = in.paragBreak;
}

/**
 * Copy editor and lose undo's
 */
SEditor
SEditor::operator=(const SEditor& in)
{
  editorIF = in.editorIF;
  vimode = in.vimode;
  inputMethods = in.inputMethods;
  fonts = in.fonts;
  undoIndex = 0;
  paragBreak = in.paragBreak;
  return *this;
}


SEditor::~SEditor ()
{
}

void
SEditor::setInterface(SEditorIF* _editorIF)
{
 editorIF = _editorIF; 
}




/**
 * called when a key has been pressed.
 */
void
SEditor::keyPressed (SWindowListener::SKey key, const SString& s,
            bool ctrl, bool shift, bool meta)
{
  if (editorIF == 0) return;

  if (key == SWindowListener::Key_Shift_R || 				key==SWindowListener::Key_Meta_L ||
key==SWindowListener::Key_Meta_R ||
key==SWindowListener::Key_Control_R ||
key==SWindowListener::Key_Control_L ||  
key==SWindowListener::Key_Shift_L)
  {
     return;
  }
  
  /* Whoops - we did not receive keyrelease */
  //Enable selection with SHIFT, 
  // Addition by Maarten van Gompel <proycon@anaproy.homeip.net>
  if (editorIF->isSelecting() && !meta && !shift && !ctrl ) 
  {
   editorIF->endSelect();  
  }
  SString erasedString ;
  SCursorIndex b = editorIF->getCursorIndex();
  SCursorIndex a;
  SCursorIndex ba;
  SUndo::SType std;
  SS_Embedding embd =  editorIF->getDocumentEmbedding();
  cleared = false;
  SString lineBreak = paragBreak;
  if (meta) lineBreak = SS_LB_LS;
  if (ctrl) lineBreak = SS_LB_FF;
  
  /* If there's a selection, erase it first before adding new input*/
  if ((key  != SWindowListener::Key_F1) &&
    (key  != SWindowListener::Key_F2) &&
    (key  != SWindowListener::Key_F3) &&
    (key  != SWindowListener::Key_F4) &&
    (key  != SWindowListener::Key_F5) &&
    (key  != SWindowListener::Key_F6) &&
    (key  != SWindowListener::Key_F7) &&
    (key  != SWindowListener::Key_F8) &&
    (key  != SWindowListener::Key_F9) &&
    (key  != SWindowListener::Key_F10) &&
    (key  != SWindowListener::Key_F11) &&
    (key  != SWindowListener::Key_F12) &&
    (key  != SWindowListener::Key_Control_R) &&
    (key  != SWindowListener::Key_Control_L) &&
    (key  != SWindowListener::Key_Meta_R) &&
    (key  != SWindowListener::Key_Meta_L) &&
    (key  != SWindowListener::Key_Alt_R) &&
    (key  != SWindowListener::Key_Alt_L) &&
    (key  != SWindowListener::Key_Left) &&
    (key  != SWindowListener::Key_Right) &&
    (key  != SWindowListener::Key_Up) &&
    (key  != SWindowListener::Key_Down) &&  
    (key  != SWindowListener::Key_Prior) &&
    (key  != SWindowListener::Key_Next) &&
    (key  != SWindowListener::Key_Home) &&
    (key  != SWindowListener::Key_End) &&  
    (key  != SWindowListener::Key_Escape) &&  
    (!ctrl) && (!meta))
  {
    std = SUndo::SS_EraseSelect;
    STextIndex mba = editorIF->getSelectedIndex(true);
    ba = SCursorIndex (mba.line, mba.index);
    erasedString = editorIF->eraseSelectedText();

    if (erasedString.size() != 0)
    {
      a = editorIF->getCursorIndex();
      add (SUndo(std, editorIF->getDirection (), embd, erasedString, b, a, ba));
    } 
  }
  switch (key)
  {
  case SWindowListener::Key_Escape:
   // vi
    if (vimode)
    {
      editorIF->setEditable(!editorIF->isEditable());
    }
    else
    {
      editorIF->focusOut();
    }
    return;
  case SWindowListener::Key_Home:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    if (ctrl || meta)
    {
      editorIF->setCursorIndex(SCursorIndex(0,0));
      if (editorIF->isSelecting())
      {
        if (meta)
        {
            editorIF->selectText (editorIF->getCursorIndex());
        }
        else // ctrl should just go to end without selecting.
        {
           editorIF->deselectText();
        }
      }
    }
    else
    {
      SCursorIndex ti = editorIF->getCursorIndex();
      ti.textIndex.index = 0;
      ti.before = true;
      editorIF->setCursorIndex(ti);
      if (editorIF->isSelecting())
      {
         editorIF->selectText (editorIF->getCursorIndex());
      }
    }
    break;
  case SWindowListener::Key_End:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    if (ctrl || meta)
    {
      editorIF->setCursorIndex(SCursorIndex(100000000,100000000));
      if (editorIF->isSelecting())
      {
        if (meta)
        {
           editorIF->selectText (editorIF->getCursorIndex());
        }
        else // ctrl should just go to end without selecting.
        {
           editorIF->deselectText();
        }
      }
    }
    else
    {
      SCursorIndex ti = editorIF->getCursorIndex();
      ti.textIndex.index = 100000000;
      ti.before =true;
      editorIF->setCursorIndex(ti);
      if (editorIF->isSelecting())
      {
         editorIF->selectText (editorIF->getCursorIndex());
      }
    }
    break;
  case SWindowListener::Key_Prior:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    editorIF->pageUp();
    return;
  case SWindowListener::Key_Next:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    editorIF->pageDown();
    return;
  case SWindowListener::Key_Up:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    editorIF->caretUp();
    return;
  case SWindowListener::Key_Down:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
        editorIF->caretDown();
    return;
  case SWindowListener::Key_Left:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    editorIF->caretLeft();
    return;
  case SWindowListener::Key_Right:
    if ((shift || ctrl || meta) && !editorIF->isSelecting()) { editorIF->startSelect(); }
    editorIF->caretRight();
    return;
  /*SGC*/
  case SWindowListener::Key_BackSpace:
    /* switch - delete a single character */
    if (erasedString.size() == 0)
    {
      std = SUndo::SS_BackSpaceOne;
      erasedString = editorIF->backspace();
      /* not even used */
      ba = editorIF->getCursorIndex();
      add (SUndo(std, editorIF->getDirection (), embd, erasedString, b, ba, ba));
    }
    return;
  case SWindowListener::Key_Delete:
    /* switch - backspace */
    if (erasedString.size() == 0)
    {
      std = SUndo::SS_EraseOne;
      erasedString = editorIF->erase();

      /* not even used */
      ba = editorIF->getCursorIndex();
      add (SUndo(std, editorIF->getDirection (), embd, erasedString, b, ba, ba));
    }
    return;
  case SWindowListener::Key_Enter:
    editorIF->insertDirtyText(lineBreak);
    ba = editorIF->getCursorIndex();
    if (cleared) return;
    a = editorIF->getCursorIndex();
    add (SUndo(SUndo::SS_Insert, editorIF->getDirection (), embd, lineBreak, b, a, ba));
    break;
  case SWindowListener::Key_Return:
    editorIF->insertDirtyText(lineBreak);
    ba = editorIF->getCursorIndex();
    if (cleared) return;
    a = editorIF->getCursorIndex();
    add (SUndo(SUndo::SS_Insert, editorIF->getDirection (), embd, lineBreak, b, a, ba));
    break;
  case SWindowListener::Key_F1:
    fKey (0, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F2:
    fKey (1, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F3:
    fKey (2, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F4:
    fKey (3, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F5:
    fKey (4, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F6:
    fKey (5, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F7:
    fKey (6, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F8:
    fKey (7, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F9:
    fKey (8, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F10:
    fKey (9, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F11:
    fKey (10, ctrl|shift|meta);
    break;
  case SWindowListener::Key_F12:
    fKey (11, ctrl|shift|meta);
    break;
  case SWindowListener::Key_B:
  case SWindowListener::Key_b:
    if (ctrl|meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->pageUp ();
      break;
    }
  case SWindowListener::Key_c:
   if (ctrl|meta)
    {
      if (editorIF->isSelecting()) editorIF->endSelect();
      return; /* Automatic copy */
    }
  /* This is used with accelerator anyway - this is never called here */
  case SWindowListener::Key_Y:
  case SWindowListener::Key_y:
   if (ctrl|meta)
    {
      editorIF->setDirection (SS_DR_L);
      break;
    }
  case SWindowListener::Key_D:
  case SWindowListener::Key_d:
   if (ctrl|meta)
    {
      if (editorIF->getDirection () != SS_DR_RO)
      {
        editorIF->setDirection (SS_DR_RO);
      }
      else 
      {
        editorIF->setDirection (SS_DR_LO);
      }
      break;
    }
  /* This is used with accelerator anyway - this is never called here */
  case SWindowListener::Key_E:
  case SWindowListener::Key_e:
   if (ctrl|meta)
    {
      if (editorIF->getDirection () != SS_DR_RE)
      {
        editorIF->setDirection (SS_DR_RE);
      }
      else 
      {
        editorIF->setDirection (SS_DR_LE);
      }
      break;
    }
  case SWindowListener::Key_F:
  case SWindowListener::Key_f:
    if (ctrl|meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->pageDown ();
      break;
    }
  case SWindowListener::Key_H:
  case SWindowListener::Key_h:
    if (ctrl|meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->caretLeft ();
      break;
    }
  case SWindowListener::Key_J:
  case SWindowListener::Key_j:
  case SWindowListener::Key_N:
  case SWindowListener::Key_n:
    if (ctrl||meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->caretDown ();
      break;
    }
  case SWindowListener::Key_K:
  case SWindowListener::Key_k:
    if (ctrl|meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->caretUp ();
      break;
    }
  case SWindowListener::Key_L:
  case SWindowListener::Key_l:
    if (ctrl||meta)
    {
      if (ctrl && editorIF->isSelecting()) editorIF->endSelect();
      editorIF->caretRight ();
      break;
    }
  case SWindowListener::Key_M:
  case SWindowListener::Key_m:
    if (ctrl)
    {
      editorIF->endSelect();
      editorIF->setCursorIndex(SCursorIndex(b.textIndex.line, 0));
      STextIndex mba = editorIF->getIndexAfterLineBreak ();
      ba = SCursorIndex (mba.line, mba.index);
      erasedString =  editorIF->eraseText(mba);
      if (cleared) return;
      if (erasedString.size() != 0)
      {
        a = editorIF->getCursorIndex();
        add (SUndo(SUndo::SS_Erase, editorIF->getDirection (), embd,  
             erasedString, b, a, ba));
      }
      break;
    }
  case SWindowListener::Key_R:
  case SWindowListener::Key_r:
    if (ctrl)
    {
      redo ();
      break;
    }
  case SWindowListener::Key_U:
  case SWindowListener::Key_u:
    if (ctrl)
    {
      undo ();
      break;
    }
  case SWindowListener::Key_v:
   if (ctrl) 
    {
      if (editorIF->isSelecting()) editorIF->endSelect();
      pasteText(b);
      return;
    }
  case SWindowListener::Key_V:
    if (ctrl|meta)
    {
      if (editorIF->isSelecting()) editorIF->endSelect();
      pasteText (b);
      return;
    }
  case SWindowListener::Key_X:
  case SWindowListener::Key_x:
    if (ctrl | meta)
    {
      /* switch - delete */
      std = SUndo::SS_EraseSelect;
      STextIndex mba = editorIF->getSelectedIndex(true);
      erasedString = editorIF->eraseSelectedText();
      if (cleared) return;
      if (erasedString.size() != 0)
      {
        a = editorIF->getCursorIndex();
        add (SUndo(std, editorIF->getDirection (), embd,  erasedString, b, a, ba));
      }
      break;
    }
  case SWindowListener::Key_Send:
  default:
    if (vimode && key != SWindowListener::Key_Send &&
        s == ":" && !editorIF->isEditable())
    {
      /* push the focus to the command window. */
      editorIF->focusOut();
      break;
    }
    SEncoder enc;
    SV_UCS4 ustr = enc.decode(s);
    while (ustr.size() && editorIF->addComposing(ustr[0]))
    {
      std = SUndo::SS_ComposeOne;
      a = editorIF->getCursorIndex();
      SS_UCS4 u4 = ustr[0];
      /* hack, string stores u4 */
      add (SUndo(std, editorIF->getDirection (), embd,  
        SString((char*)&u4, sizeof (u4)), a, a, a));
      ustr.remove (0);
    }
    SString sstr = enc.encode (ustr);
    if (sstr.size())
    {
      b = editorIF->getCursorIndex();
      editorIF->insertDirtyText(sstr);
      ba = editorIF->getCursorIndex();
      a = editorIF->getCursorIndex();
      add (SUndo(SUndo::SS_Insert, editorIF->getDirection (), embd,
          sstr, b, a, ba));
    }
    else /* to update glyph info */
    {
      a = editorIF->getCursorIndex();
      editorIF->setCursorIndex(a);
    }
  }


  
}

/**
 * called when a key was released.
 */
void
SEditor::keyReleased (SWindowListener::SKey key, const SString& s,
            bool ctrl, bool shift, bool meta)
{
  if (editorIF == 0) return;
  if (key==SWindowListener::Key_Meta_R || key == SWindowListener::Key_Meta_L
   || key == SWindowListener::Key_Alt_R || key==SWindowListener::Key_Alt_L 
   || key == SWindowListener::Key_Control_L||key==SWindowListener::Key_Control_R
   || key == SWindowListener::Key_Shift_R || key==SWindowListener::Key_Shift_L)
  {
    editorIF->endSelect();
    return;
  }
}

/**
 * called when a mouse button was pressed.
 */
void
SEditor::buttonPressed (int button, const SCursorIndex& index)
{
  if (editorIF == 0) return;
  editorIF->deselectText();
  if (button == 1)
  {
    pasteText(editorIF->getCursorIndex());
    return;
  }
  /*mousewheel*/
  if (button == 4) { 
    editorIF->caretDown();
  } else if (button == 3) {
    editorIF->caretUp();
  }
  else {
    editorIF->setCursorIndex (index);
    editorIF->startSelect(); 
  }
}

/**
 * paste a text ftom clip
 */
void
SEditor::pasteText (const SCursorIndex& index)
{
  if (editorIF == 0) return;
  editorIF->deselectText();
  editorIF->setCursorIndex(index);
  SCursorIndex b = index;
  /* stop input methods */
  editorIF->insertClipboardText();
  SCursorIndex ba = editorIF->getCursorIndex();
  SString s = editorIF->getText (b.getTextIndex(), ba.getTextIndex());
  SCursorIndex a = editorIF->getCursorIndex();
  add (SUndo(SUndo::SS_Insert, editorIF->getDirection (), editorIF->getDocumentEmbedding(), s, b, a, ba));
}

/**
 * Set clean text.
 */
void
SEditor::insertText (const SString& text)
{
  if (editorIF == 0) return;
  editorIF->deselectText();
  SCursorIndex b = editorIF->getCursorIndex();
  editorIF->insertText(text);
  SCursorIndex ba = editorIF->getCursorIndex();
  SString s = text;
  SCursorIndex a = editorIF->getCursorIndex();
  add (SUndo(SUndo::SS_Insert, editorIF->getDirection (), editorIF->getDocumentEmbedding(), s, b, a, ba));
}

/**
 * called when a mouse button was released.
 */
void
SEditor::buttonReleased (int button, const SCursorIndex& index)
{
  if (editorIF == 0) return;
  if (button == 1)
  {
    return;
  }
  editorIF->selectText (index);
  editorIF->endSelect ();
}

/**
 * called when a mouse button was dragged.
 */
void
SEditor::buttonDragged (int button, const SCursorIndex& index)
{
  if (button == 1)
  {
    return;
  }
  if (editorIF == 0) return;
  editorIF->selectText (index);
}

/**
 * called when a mouse button was nulti-clicked.
 * It can come only if button was already released.
 */
void
SEditor::multiClicked (int button, const SCursorIndex& index, int count)
{
  if (editorIF == 0) return;
  if (button == 1)
  {
    return;
  }
  editorIF->selectText (index);
  editorIF->endSelect ();
  if (count == 2)
  {
    editorIF->selectWord ();
  }
  else if (count > 2)
  {
    editorIF->selectLine ();
  }
}

/**
 * This sets a set of input methods that can be activated with F1-F12.
 */
void
SEditor::setInputMethods (const SStringVector& in)
{
  inputMethods = in;
}

/**
 * set editor mode.
 */
void
SEditor::setVI (bool vi)
{
  vimode = vi;
}
/**
 * This sets a set of fonts that can be activated with <ctrl>F1-F12.
 */
void
SEditor::setFonts (const SStringVector& in)
{
  fonts = in;
}

/**
 * An FKEY was pressed.
 * @param num
 * <ul>
 * <li> F1 - 0 </li>
 * <li> F2 - 1 </li>
 * <li> Fn - n-1 </li>
 * </ul>
 */
void
SEditor::fKey(int num, bool ctrl)
{
  if (ctrl)
  {
    if (fonts.size() >= (unsigned int) num+1)
    {
      editorIF->setFont(fonts[num]);
    }
  }
  else
  {
    if (inputMethods.size() >= (unsigned int) num+1)
    {
       editorIF->setInputMethod(inputMethods[num]);
    }
  }
}
/**
 * Try to undo
 * @return false in case it can not be undone.
 */
bool
SEditor::undo()
{
  //fprintf (stderr, "undo\n");
  if (editorIF == 0) return false;
  /* clear state should be visible */
  if (editorIF->clearState()) return true;
  if (undoIndex == 0)
  {
    //fprintf (stderr, "SEditor::nothing to undo - undoindex\n");
    return false;
  }
  if (!editorIF->isEditable())
  {
     return false;
  }
  if (editorIF->isSelecting())
  {
    // fprintf (stderr, "SEditor::can not undo while selecting.\n");
     return false;
  }
  undoIndex--;
  SUndo u = undoBuffer[undoIndex];
  //fprintf (stderr, "Undo:%d.\n", u.type);
  switch (u.type)
  {
  case SUndo::SS_Insert:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.beforeAfter);
    editorIF->eraseText (u.before.getTextIndex());
    editorIF->setDirection(u.direction);
    break;
  case SUndo::SS_Erase:
  case SUndo::SS_EraseOne:
  case SUndo::SS_BackSpaceOne:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.beforeAfter);
    editorIF->setCursorIndex(u.after);
    editorIF->insertText(u.string, false);
    editorIF->setCursorIndex(u.before);
    break;
  case SUndo::SS_ComposeOne:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.after);
    editorIF->setDirection(u.direction);
    editorIF->removeComposing();
    editorIF->setCursorIndex(u.after);
    editorIF->setDirection(u.direction);
    /* compose does not move caret */
    break;
  case SUndo::SS_EraseSelect:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.after);
    editorIF->insertText(u.string, false);
    editorIF->startSelect();

    /* this is the sleection spot */
    editorIF->selectText(u.after);
    editorIF->endSelect();
    editorIF->setCursorIndex(u.after);
    break;
  }
  return true;
}

/**
 * Try to redo
 * @return false in case it can not be undone.
 */
bool
SEditor::redo()
{
  //fprintf (stderr, "redo\n");
  if (editorIF == 0) return false;
  editorIF->clearState();
  if (undoIndex == undoBuffer.size())
  {
    //fprintf (stderr, "SEditor::redo ends here.\n");
    return false;
  }
  if (!editorIF->isEditable()) return false;
  if (editorIF->isSelecting())
  {
    //fprintf (stderr, "SEditor::can not redo while selecting.\n");
    return false;
  }

  SUndo u = undoBuffer[undoIndex];
  undoIndex++;
  //fprintf (stderr, "redo:%d.\n", u.type);
  switch (u.type) 
  {
  case SUndo::SS_Insert:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.before);
    editorIF->insertText(u.string, false);
    break;
  case SUndo::SS_Erase:
  case SUndo::SS_EraseSelect:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.after);
    editorIF->eraseText(u.beforeAfter.getTextIndex());
    break;
  case SUndo::SS_BackSpaceOne:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.before);
    editorIF->backspace();
    break;
  case SUndo::SS_EraseOne:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.before);
    editorIF->erase();
    break;
  case SUndo::SS_ComposeOne:
    editorIF->setDocumentEmbedding(u.embedding);
    editorIF->setCursorIndex(u.after);
    editorIF->setDirection(u.direction);
    {
      const SS_UCS4* u4 = (const SS_UCS4*) u.string.array();
      editorIF->addComposing(u4[0]);
      editorIF->setCursorIndex(u.after);
    }
    break;
  }
  return true;
}

/**
 * add an item to undo buffer
 * @param u is the new item.
 * TODO: clear buffer after some size.
 */
void
SEditor::add (const SUndo& u)
{
  if (undoBuffer.size() != undoIndex)
  {
    if (undoIndex==0)
    {
      undoBuffer.clear();
    }
    else 
    {
      undoBuffer.truncate(undoIndex);
    }
    undoIndex = undoBuffer.size();
  }
  undoBuffer.append (u);
  undoIndex++;
}

/**
 * clear the undo buffer.
 */
void
SEditor::clearUndo()
{
  undoBuffer.clear();
  undoIndex = 0;
}

void
SEditor::clear ()
{
  cleared = true;
  vimode = false;
  undoIndex = 0;
  undoBuffer.clear();
}

void
SEditor::setParagraphSeparator (const SString& _paragBreak)
{
  paragBreak = _paragBreak;
}

SString
SEditor::getParagraphSeparator () const
{
  return SString (paragBreak);
}
