text: 
  
  //#include "tw/mainshell.h"
  
  #include "tools/tool.h"
  #include "tools/toolutils.h"
  #include "tools/toolhandle.h"
  
  #include "tstroke.h"
  #include "tmathutil.h"
  #include "tproperty.h"
  #include "tenv.h"
  #include "toonz/txsheethandle.h"
  #include "toonz/txshlevelhandle.h"
  #include "toonz/tframehandle.h"
  #include "tvectorimage.h"
  #include "ttoonzimage.h"
  #include "toonz/toonzimageutils.h"
  #include "tools/cursors.h"
  #include "tundo.h"
  #include "tvectorgl.h"
  #include "tgl.h"
  #include "tregion.h"
  #include "tvectorrenderdata.h"
  #include "toonz/tpalettehandle.h"
  
  #include "toonzqt/selection.h"
  #include "toonzqt/imageutils.h"
  #include "trop.h"
  #include "toonz/ttileset.h"
  #include "toonz/glrasterpainter.h"
  #include "toonz/stage.h"
  
  #include "tfont.h"
  
  // For Qt translation support
  #include <QCoreApplication>
  #include <QClipboard>
  #include <QApplication>
  #include <QMimeData>
  
  //#include "tw/message.h"
  
  //#include "tw/ime.h"
  
  using namespace ToolUtils;
  
  namespace {
  
  //---------------------------------------------------------
  //
  // helper functions
  //
  //---------------------------------------------------------
  
  void paintRegion(TRegion *region, int styleId, bool paint) {
    UINT j, regNum = region->getSubregionCount();
    if (paint) region->setStyle(styleId);
  
    for (j = 0; j < regNum; j++)
      paintRegion(region->getSubregion(j), styleId, !paint);
  }
  
  //---------------------------------------------------------
  
  void paintChar(const TVectorImageP &image, int styleId) {
    // image->setPalette( getApplication()->getCurrentScene()->getPalette());
    // image->setPalette(getCurrentPalette());
  
    UINT j;
    UINT strokeNum = image->getStrokeCount();
    for (j = 0; j < strokeNum; j++) image->getStroke(j)->setStyle(styleId);
  
    image->enableRegionComputing(true, true);
    image->findRegions();
  
    UINT regNum = image->getRegionCount();
    for (j = 0; j < regNum; j++) paintRegion(image->getRegion(j), styleId, true);
  }
  
  //---------------------------------------------------------
  // Type, vars, ecc.
  //
  //---------------------------------------------------------
  
  TEnv::StringVar EnvCurrentFont("CurrentFont", "MS UI Gothic");
  
  //! numero di pixel attorno al testo
  const double cBorderSize = 15;
  
  //---------------------------------------------------------
  //
  // UNDO
  //
  //---------------------------------------------------------
  
  class UndoTypeTool final : public ToolUtils::TToolUndo {
    std::vector<TStroke *> m_strokes;
    std::vector<TFilledRegionInf> *m_fillInformationBefore,
        *m_fillInformationAfter;
    TVectorImageP m_image;
  
  public:
    UndoTypeTool(std::vector<TFilledRegionInf> *fillInformationBefore,
                 std::vector<TFilledRegionInf> *fillInformationAfter,
                 TXshSimpleLevel *level, const TFrameId &frameId,
                 bool isFrameCreated, bool isLevelCreated)
        : ToolUtils::TToolUndo(level, frameId, isFrameCreated, isLevelCreated)
        , m_fillInformationBefore(fillInformationBefore)
        , m_fillInformationAfter(fillInformationAfter) {}
  
    ~UndoTypeTool() {
      delete m_fillInformationBefore;
      delete m_fillInformationAfter;
      clearPointerContainer(m_strokes);
    }
  
    void addStroke(TStroke *stroke) {
      TStroke *s = new TStroke(*stroke);
      s->setId(stroke->getId());
      m_strokes.push_back(s);
    }
  
    void undo() const override {
      TTool::Application *application = TTool::getApplication();
  
      TVectorImageP image = m_level->getFrame(m_frameId, true);
      assert(!!image);
      if (!image) return;
      QMutexLocker lock(image->getMutex());
      UINT i;
      for (i = 0; i < m_strokes.size(); i++) {
        VIStroke *stroke = image->getStrokeById(m_strokes[i]->getId());
        if (!stroke) return;
        image->deleteStroke(stroke);
      }
  
      if (m_fillInformationBefore) {
        UINT size = m_fillInformationBefore->size();
        TRegion *reg;
        for (i = 0; i < size; i++) {
          reg = image->getRegion((*m_fillInformationBefore)[i].m_regionId);
          assert(reg);
          if (reg) reg->setStyle((*m_fillInformationBefore)[i].m_styleId);
        }
      }
      removeLevelAndFrameIfNeeded();
      application->getCurrentXsheet()->notifyXsheetChanged();
      notifyImageChanged();
    }
  
    void redo() const override {
      insertLevelAndFrameIfNeeded();
      TVectorImageP image = m_level->getFrame(m_frameId, true);
      assert(!!image);
      if (!image) return;
  
      TTool::Application *application = TTool::getApplication();
  
      QMutexLocker lock(image->getMutex());
      UINT i;
      for (i = 0; i < m_strokes.size(); i++) {
        TStroke *stroke = new TStroke(*m_strokes[i]);
        stroke->setId(m_strokes[i]->getId());
        image->addStroke(stroke);
      }
  
      if (image->isComputedRegionAlmostOnce()) image->findRegions();
  
      if (m_fillInformationAfter) {
        UINT size = m_fillInformationAfter->size();
        TRegion *reg;
        for (i = 0; i < size; i++) {
          reg = image->getRegion((*m_fillInformationAfter)[i].m_regionId);
          assert(reg);
          if (reg) reg->setStyle((*m_fillInformationAfter)[i].m_styleId);
        }
      }
      application->getCurrentXsheet()->notifyXsheetChanged();
      notifyImageChanged();
    }
  
    int getSize() const override {
      if (m_fillInformationAfter && m_fillInformationBefore)
        return sizeof(*this) +
               m_fillInformationBefore->capacity() * sizeof(TFilledRegionInf) +
               m_fillInformationAfter->capacity() * sizeof(TFilledRegionInf) +
               +m_strokes.capacity() * sizeof(TStroke) + 500;
      else
        return sizeof(*this) + m_strokes.capacity() * sizeof(TStroke) + 500;
    }
  
    QString getToolName() override { return QString("Type Tool"); }
  };
  
  //---------------------------------------------------------
  
  class RasterUndoTypeTool final : public TRasterUndo {
    TTileSetCM32 *m_afterTiles;
  
  public:
    RasterUndoTypeTool(TTileSetCM32 *beforeTiles, TTileSetCM32 *afterTiles,
                       TXshSimpleLevel *level, const TFrameId &id,
                       bool createdFrame, bool createdLevel)
        : TRasterUndo(beforeTiles, level, id, createdFrame, createdLevel, 0)
        , m_afterTiles(afterTiles) {}
  
    ~RasterUndoTypeTool() { delete m_afterTiles; }
  
    void redo() const override {
      insertLevelAndFrameIfNeeded();
      TToonzImageP image = getImage();
      if (!image) return;
      if (m_afterTiles) {
        ToonzImageUtils::paste(image, m_afterTiles);
        ToolUtils::updateSaveBox();
      }
  
      TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
      notifyImageChanged();
    }
  
    int getSize() const override {
      if (m_afterTiles)
        return TRasterUndo::getSize() + m_afterTiles->getMemorySize();
      else
        return TRasterUndo::getSize();
    }
  
    QString getToolName() override { return QString("Type Tool"); }
  };
  
  //---------------------------------------------------------
  //
  // StrokeChar
  //
  // E' la TVectorImage che corrisponde ad un carattere
  // (piu' le varie info che permettono di ricrearlo)
  //
  //---------------------------------------------------------
  
  class StrokeChar {
  public:
    // TVectorImageP m_char;
  
    TImageP m_char;
  
    double m_offset;
    TPointD m_charPosition;
    int m_key;
    int m_styleId;
  
    StrokeChar(TImageP _char, double offset, int key, int styleId)
        : m_char(_char)
        , m_offset(offset)
        , m_charPosition(TPointD(0, 0))
        , m_key(key)
        , m_styleId(styleId) {}
  
    bool isReturn() const { return m_key == (int)(QChar('\r').unicode()); }
  
    void update(TAffine scale, int nextCode = 0) {
      if (!isReturn()) {
        if (TVectorImageP vi = m_char) {
          vi = m_char = new TVectorImage;
          TPoint adv  = TFontManager::instance()->drawChar(vi, m_key, nextCode);
          vi->transform(scale);
          paintChar(vi, m_styleId);
          m_offset = (scale * TPointD((double)(adv.x), (double)(adv.y))).x;
        } else {
          TRasterCM32P newRasterCM;
          TPoint p;
          TPoint adv = TFontManager::instance()->drawChar(
              (TRasterCM32P &)newRasterCM, p, m_styleId, (wchar_t)m_key,
              (wchar_t)nextCode);
          // m_char->transform(scale);
          m_offset = (scale * TPointD((double)(adv.x), (double)(adv.y))).x;
  
          m_char = new TToonzImage(newRasterCM, newRasterCM->getBounds());
        }
      }
    }
  };
  
  }  //  namespace
  
  //---------------------------------------------------------
  //
  // TypeTool
  //
  //---------------------------------------------------------
  
  class TypeTool final : public TTool {
    Q_DECLARE_TR_FUNCTIONS(TypeTool)
  
    // Properties
    TEnumProperty m_fontFamilyMenu;
    TEnumProperty m_typeFaceMenu;
    TBoolProperty m_vertical;
    TEnumProperty m_size;
    TPropertyGroup m_prop[2];
  
    // valori correnti di alcune Properties,
    // duplicati per permettere controlli sulla validita' o per ottimizzazione
    std::wstring m_fontFamily;
    std::wstring m_typeface;
    double m_dimension;
  
    bool m_validFonts;  // false iff there are problems with font loading
    bool m_initialized;
  
    int m_cursorId;
    int m_styleId;
    double m_pixelSize;
  
    std::vector<StrokeChar> m_string;
  
    int m_cursorIndex;  // indice del carattere successivo al cursore
    std::pair<int, int> m_preeditRange;
    // posizione (dentro m_string) della preeditString (cfr. IME)
    // n.b. la preeditString va da range.first a range.second-1
    TRectD m_textBox;  // bbox della stringa
    TScale m_scale;  // la dimensione dei char ottenuti dal text manager e' fissa
                     // perche' influisce sula qualita'. Quindi lo scalo
    TPointD m_cursorPoint;  // punto piu alto del cursore
    TPointD m_startPoint;   // punto dove si poggia la prima lettera (coincide col
                            // mouse click)
    double m_fontYOffset;   // spazio per le parti delle lettere sotto il rigo
                            // (dipende dal font)
    bool m_isVertical;      // text orientation
  
    TUndo *m_undo;
  
  public:
    TypeTool();
    ~TypeTool();
  
    void updateTranslation() override;
  
    ToolType getToolType() const override { return TTool::LevelWriteTool; }
  
    void init();
    void initTypeFaces();
  
    void loadFonts();
    void setFont(std::wstring fontFamily);
    void setTypeface(std::wstring typeface);
    void setSize(std::wstring size);
    void setVertical(bool vertical);
    void draw() override;
  
    void updateMouseCursor(const TPointD &pos);
    void updateStrokeChar();
    void updateCharPositions(int updateFrom = 0);
    void updateCursorPoint();
    void updateTextBox();
  
    void setCursorIndexFromPoint(TPointD point);
  
    void mouseMove(const TPointD &pos, const TMouseEvent &) override;
    void leftButtonDown(const TPointD &pos, const TMouseEvent &) override;
    void rightButtonDown(const TPointD &pos, const TMouseEvent &) override;
    bool keyDown(QKeyEvent *event) override;
  
    void onInputText(std::wstring preedit, std::wstring commit,
                     int replacementStart, int replacementLen) override;
  
    // cancella gli StrokeChar fra from e to-1 e inserisce nuovi StrokeChar
    // corrispondenti a text a partire da from
    void replaceText(std::wstring text, int from, int to);
  
    void addBaseChar(std::wstring text);
    void addReturn();
    void cursorUp();
    void cursorDown();
    void cursorLeft();
    void cursorRight();
    void deleteKey();
    void addTextToImage();
    void addTextToVectorImage(const TVectorImageP &currentImage,
                              std::vector<const TVectorImage *> &images);
    void addTextToToonzImage(const TToonzImageP &currentImage);
    void stopEditing();
  
    void reset() override;
  
    void onActivate() override;
    void onDeactivate() override;
    void onImageChanged() override;
  
    int getCursorId() const override {
      if (m_viewer && m_viewer->getGuidedStrokePickerMode())
        return m_viewer->getGuidedStrokePickerCursor();
      return m_cursorId;
    }
  
    bool onPropertyChanged(std::string propertyName) override;
  
    TPropertyGroup *getProperties(int targetType) override {
      return &m_prop[targetType];
    }
  
    int getColorClass() const { return 1; }
  
  } typeTool;
  
  //---------------------------------------------------------
  //
  // TypeTool methods
  //
  //---------------------------------------------------------
  
  TypeTool::TypeTool()
      : TTool("T_Type")
      , m_textBox(TRectD(0, 0, 0, 0))
      , m_cursorPoint(TPointD(0, 0))
      , m_startPoint(TPointD(0, 0))
      , m_dimension(1)      // to be set the m_size value on the first-click
      , m_validFonts(true)  // false)
      , m_fontYOffset(0)
      , m_cursorId(ToolCursor::CURSOR_NO)
      , m_pixelSize(1)
      , m_cursorIndex(0)
      , m_preeditRange(0, 0)
      , m_isVertical(false)
      , m_initialized(false)
      , m_fontFamilyMenu("Font:")                  // W_ToolOptions_FontName
      , m_typeFaceMenu("Style:")                   // W_ToolOptions_TypeFace
      , m_vertical("Vertical Orientation", false)  // W_ToolOptions_Vertical
      , m_size("Size:")                            // W_ToolOptions_Size
      , m_undo(0) {
    bind(TTool::VectorImage | TTool::ToonzImage | TTool::EmptyTarget);
    m_prop[0].bind(m_fontFamilyMenu);
    // Su mac non e' visibile il menu dello style perche' e' stato inserito nel
    // nome
    // della font.
    //#ifndef MACOSX
    m_prop[1].bind(m_typeFaceMenu);
    //#endif
    m_prop[1].bind(m_size);
    m_prop[1].bind(m_vertical);
    m_vertical.setId("Orientation");
    m_fontFamilyMenu.setId("TypeFont");
    m_typeFaceMenu.setId("TypeStyle");
    m_size.setId("TypeSize");
  }
  
  //---------------------------------------------------------
  
  TypeTool::~TypeTool() {}
  
  //---------------------------------------------------------
  
  void TypeTool::updateTranslation() {
    m_fontFamilyMenu.setQStringName(tr("Font:"));
    m_typeFaceMenu.setQStringName(tr("Style:"));
    m_vertical.setQStringName(tr("Vertical Orientation"));
    m_size.setQStringName(tr("Size:"));
  }
  
  //---------------------------------------------------------
  
  bool TypeTool::onPropertyChanged(std::string propertyName) {
    if (!m_validFonts) return false;
  
    if (propertyName == m_fontFamilyMenu.getName()) {
      setFont(m_fontFamilyMenu.getValue());
      return true;
    } else if (propertyName == m_typeFaceMenu.getName()) {
      setTypeface(m_typeFaceMenu.getValue());
      return true;
    } else if (propertyName == m_size.getName()) {
      setSize(m_size.getValue());
      return true;
    } else if (propertyName == m_vertical.getName()) {
      setVertical(m_vertical.getValue());
      return true;
    }
    return false;
  }
  
  //---------------------------------------------------------
  
  void TypeTool::init() {
    if (m_initialized) return;
    m_initialized = true;
  
    loadFonts();
    if (!m_validFonts) return;
  
    m_size.addValue(L"36");
    m_size.addValue(L"58");
    m_size.addValue(L"70");
    m_size.addValue(L"86");
    m_size.addValue(L"100");
    m_size.addValue(L"150");
    m_size.addValue(L"200");
    m_size.setValue(L"70");
  }
  
  //---------------------------------------------------------
  
  void TypeTool::loadFonts() {
    TFontManager *instance = TFontManager::instance();
    try {
      instance->loadFontNames();
      m_validFonts = true;
    } catch (TFontLibraryLoadingError &) {
      m_validFonts = false;
      //    TMessage::error(toString(e.getMessage()));
    }
  
    if (!m_validFonts) return;
  
    std::vector<std::wstring> names;
    instance->getAllFamilies(names);
  
    for (std::vector<std::wstring>::iterator it = names.begin();
         it != names.end(); ++it)
      m_fontFamilyMenu.addValue(*it);
  
    std::string favFontApp     = EnvCurrentFont;
    std::wstring favouriteFont = ::to_wstring(favFontApp);
    if (m_fontFamilyMenu.isValue(favouriteFont)) {
      m_fontFamilyMenu.setValue(favouriteFont);
      setFont(favouriteFont);
    } else {
      setFont(m_fontFamilyMenu.getValue());
    }
  
    // not used for now
    m_scale = TScale();
    // m_scale = TScale(m_dimension /
    // (double)(TFontManager::instance()->getHeight()));
  }
  
  //---------------------------------------------------------
  
  void TypeTool::initTypeFaces() {
    TFontManager *instance = TFontManager::instance();
    std::vector<std::wstring> typefaces;
    instance->getAllTypefaces(typefaces);
    std::wstring oldTypeface = m_typeFaceMenu.getValue();
    m_typeFaceMenu.deleteAllValues();
    for (std::vector<std::wstring>::iterator it = typefaces.begin();
         it != typefaces.end(); ++it)
      m_typeFaceMenu.addValue(*it);
    if (m_typeFaceMenu.isValue(oldTypeface)) m_typeFaceMenu.setValue(oldTypeface);
  
    TTool::getApplication()->getCurrentTool()->notifyToolComboBoxListChanged(
        m_typeFaceMenu.getName());
  }
  
  //---------------------------------------------------------
  
  void TypeTool::setFont(std::wstring family) {
    if (m_fontFamily == family) return;
    TFontManager *instance = TFontManager::instance();
    try {
      instance->setFamily(family);
  
      m_fontFamily             = family;
      std::wstring oldTypeface = m_typeFaceMenu.getValue();
      initTypeFaces();
      if (oldTypeface != m_typeFaceMenu.getValue()) {
        if (m_typeFaceMenu.isValue(L"Regular")) {
          m_typeFaceMenu.setValue(L"Regular");
          m_typeface = L"Regular";
          instance->setTypeface(L"Regular");
        } else {
          m_typeface = m_typeFaceMenu.getValue();
          instance->setTypeface(m_typeface);
        }
      }
  
      // assert chiaramente vero se oldTypeface!=m_typeFaceMenu.getValue().
      // Assume che il TFontManager quando cambia family, si ricordi
      // il vecchio typeface e lo imposti se anche la nuova family lo ha
      assert(instance->getCurrentTypeface() == m_typeFaceMenu.getValue());
  
      updateStrokeChar();
      invalidate();
      EnvCurrentFont = ::to_string(m_fontFamily);
    } catch (TFontCreationError &) {
      //    TMessage::error(toString(e.getMessage()));
      assert(m_fontFamily == instance->getCurrentFamily());
      m_fontFamilyMenu.setValue(m_fontFamily);
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::setTypeface(std::wstring typeface) {
    if (m_typeface == typeface) return;
    TFontManager *instance = TFontManager::instance();
    try {
      instance->setTypeface(typeface);
      m_typeface = typeface;
      updateStrokeChar();
      invalidate();
    } catch (TFontCreationError &) {
      //    TMessage::error(toString(e.getMessage()));
      assert(m_typeface == instance->getCurrentTypeface());
      m_typeFaceMenu.setValue(m_typeface);
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::setSize(std::wstring strSize) {
    // font e tool fields update
    double dimension = std::stod(strSize);
  
    TImageP img      = getImage(true);
    TToonzImageP ti  = img;
    TVectorImageP vi = img;
    // for vector levels, adjust size according to the ratio between
    // the viewer dpi and the vector level's dpi
    if (vi) dimension *= Stage::inch / Stage::standardDpi;
  
    if (m_dimension == dimension) return;
    TFontManager::instance()->setSize((int)dimension);
  
    assert(m_dimension != 0);
    double ratio = dimension / m_dimension;
    m_dimension  = dimension;
  
    // not used for now
    m_scale = TScale();
    // m_scale = TScale(m_dimension /
    // (double)(TFontManager::instance()->getHeight()));
  
    // text update
  
    if (m_string.empty()) return;
  
    for (UINT i = 0; i < m_string.size(); i++) {
      if (TVectorImageP vi = m_string[i].m_char) vi->transform(TScale(ratio));
      m_string[i].m_offset *= ratio;
    }
    if (ti)
      updateStrokeChar();
    else
      updateCharPositions();
  
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::setVertical(bool vertical) {
    if (vertical == m_isVertical) return;
  
    m_isVertical        = vertical;
    bool oldHasVertical = TFontManager::instance()->hasVertical();
    TFontManager::instance()->setVertical(vertical);
    if (oldHasVertical != TFontManager::instance()->hasVertical())
      updateStrokeChar();
    else
      updateCharPositions();
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::stopEditing() {
    if (m_active == false) return;
    m_active = false;
    m_string.clear();
    m_cursorIndex = 0;
    m_textBox     = TRectD();
    //  closeImeWindow();
    //  if(m_viewer) m_viewer->enableIme(false);
    //  enableShortcuts(true);
    m_preeditRange = std::make_pair(0, 0);
    invalidate();
    if (m_undo) {
      TUndoManager::manager()->add(m_undo);
      m_undo = 0;
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::updateStrokeChar() {
    TFontManager *instance = TFontManager::instance();
  
    // not used for now
    m_scale = TScale();
    // m_scale = TScale(m_dimension / (double)(instance->getHeight()));
  
    bool hasKerning = instance->hasKerning();
    for (UINT i = 0; i < m_string.size(); i++) {
      if (hasKerning && i + 1 < m_string.size() && !m_string[i + 1].isReturn())
        m_string[i].update(/*m_font,*/ m_scale, m_string[i + 1].m_key);
      else
        m_string[i].update(/*m_font,*/ m_scale);
    }
  
    updateCharPositions();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::updateCharPositions(int updateFrom) {
    if (updateFrom < 0) updateFrom = 0;
    UINT size = m_string.size();
    TPointD currentOffset;
    TFontManager *instance = TFontManager::instance();
    m_fontYOffset          = (double)(instance->getLineSpacing()) * m_scale.a11;
    double descent         = (double)(instance->getLineDescender()) * m_scale.a11;
    double height          = (double)(instance->getHeight()) * m_scale.a11;
    double vLineSpacing =
        (double)(instance->getAverageCharWidth()) * 2.0 * m_scale.a11;
  
    // Update from "updateFrom"
    if (updateFrom > 0) {
      if ((int)m_string.size() <= updateFrom - 1) return;
      currentOffset = m_string[updateFrom - 1].m_charPosition - m_startPoint;
      // Vertical case
      if (m_isVertical && !instance->hasVertical()) {
        if (m_string[updateFrom - 1].isReturn())
          currentOffset = TPointD(currentOffset.x - vLineSpacing, -height);
        else
          currentOffset = currentOffset + TPointD(0, -height);
      }
      // Horizontal case
      else {
        if (m_string[updateFrom - 1].isReturn())
          currentOffset = TPointD(0, currentOffset.y - m_fontYOffset);
        else
          currentOffset =
              currentOffset + TPointD(m_string[updateFrom - 1].m_offset, 0);
      }
    }
    // Update whole characters
    else {
      if (m_isVertical && !instance->hasVertical())
        currentOffset = currentOffset + TPointD(0, -height);
      else
        currentOffset = currentOffset + TPointD(0, -descent);
    }
  
    for (UINT j = updateFrom; j < size; j++) {
      m_string[j].m_charPosition = m_startPoint + currentOffset;
      // Vertical case
      if (m_isVertical && !instance->hasVertical()) {
        if (m_string[j].isReturn() || m_string[j].m_key == ' ')
          currentOffset = TPointD(currentOffset.x - vLineSpacing, -height);
        else
          currentOffset = currentOffset + TPointD(0, -height);
      }
      // Horizontal case
      else {
        if (m_string[j].isReturn())
          currentOffset = TPointD(0, currentOffset.y - m_fontYOffset);
        else
          currentOffset = currentOffset + TPointD(m_string[j].m_offset, 0);
      }
    }
  
    // To be sure
    if (m_cursorIndex <= (int)m_string.size()) {
      updateCursorPoint();
      updateTextBox();
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::updateCursorPoint() {
    assert(0 <= m_cursorIndex && m_cursorIndex <= (int)m_string.size());
    TFontManager *instance = TFontManager::instance();
    double descent         = (double)(instance->getLineDescender()) * m_scale.a11;
    double height          = (double)(instance->getHeight()) * m_scale.a11;
    double vLineSpacing =
        (double)(instance->getAverageCharWidth()) * 2.0 * m_scale.a11;
    m_fontYOffset          = (double)(instance->getLineSpacing()) * m_scale.a11;
    double scaledDimension = m_dimension * m_scale.a11;
  
    if (m_string.empty()) {
      if (!m_isVertical || instance->hasVertical())
        m_cursorPoint = m_startPoint + TPointD(0, scaledDimension);
      else
        m_cursorPoint = m_startPoint;
    } else if (m_cursorIndex == (int)m_string.size()) {
      // Horizontal case
      if (!m_isVertical || instance->hasVertical()) {
        if (m_string.back().isReturn())
          m_cursorPoint = TPointD(
              m_startPoint.x, m_string.back().m_charPosition.y - m_fontYOffset +
                                  scaledDimension + descent);
        else
          m_cursorPoint = m_string.back().m_charPosition +
                          TPointD(m_string.back().m_offset, 0) +
                          TPointD(0, scaledDimension + descent);
      }
      // Vertical case
      else {
        if (m_string.back().isReturn())
          m_cursorPoint = TPointD(m_string.back().m_charPosition.x - vLineSpacing,
                                  m_startPoint.y);
        else
          m_cursorPoint = m_string.back().m_charPosition;
      }
    } else {
      if (!m_isVertical || instance->hasVertical())
        m_cursorPoint = m_string[m_cursorIndex].m_charPosition +
                        TPointD(0, scaledDimension + descent);
      else
        m_cursorPoint =
            m_string[m_cursorIndex].m_charPosition + TPointD(0, height);
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::updateTextBox() {
    UINT size                = m_string.size();
    UINT returnNumber        = 0;
    double currentLineLength = 0;
    double maxXLength        = 0;
  
    TFontManager *instance = TFontManager::instance();
    double descent         = (double)(instance->getLineDescender()) * m_scale.a11;
    double height          = (double)(instance->getHeight()) * m_scale.a11;
    double vLineSpacing =
        (double)(instance->getAverageCharWidth()) * 2.0 * m_scale.a11;
    m_fontYOffset = (double)(instance->getLineSpacing()) * m_scale.a11;
  
    for (UINT j = 0; j < size; j++) {
      if (m_string[j].isReturn()) {
        if (currentLineLength > maxXLength) {
          maxXLength = currentLineLength;
        }
        currentLineLength = 0;
        returnNumber++;
      } else {
        currentLineLength += (m_isVertical && !instance->hasVertical())
                                 ? height
                                 : m_string[j].m_offset;
      }
    }
  
    if (currentLineLength > maxXLength)  // last line
      maxXLength = currentLineLength;
  
    if (m_isVertical && !instance->hasVertical())
      m_textBox = TRectD(m_startPoint.x - vLineSpacing * returnNumber,
                         m_startPoint.y - maxXLength,
                         m_startPoint.x + vLineSpacing, m_startPoint.y)
                      .enlarge(cBorderSize * m_pixelSize);
    else
      m_textBox =
          TRectD(m_startPoint.x,
                 m_startPoint.y - (m_fontYOffset * returnNumber + descent),
                 m_startPoint.x + maxXLength, m_startPoint.y + height)
              .enlarge(cBorderSize * m_pixelSize);
  }
  
  //---------------------------------------------------------
  
  void TypeTool::updateMouseCursor(const TPointD &pos) {
    int oldCursor = m_cursorId;
  
    if (!m_validFonts)
      m_cursorId = ToolCursor::CURSOR_NO;
    else {
      TPointD clickPoint =
          (TFontManager::instance()->hasVertical() && m_isVertical)
              ? TRotation(m_startPoint, 90) * pos
              : pos;
      if (m_textBox == TRectD(0, 0, 0, 0) || m_string.empty() ||
          !m_textBox.contains(clickPoint))
        m_cursorId = ToolCursor::TypeOutCursor;
      else
        m_cursorId = ToolCursor::TypeInCursor;
    }
  
    //  if(oldCursor != m_cursorId)
    //    TNotifier::instance()->notify(TToolChange());
  }
  
  //---------------------------------------------------------
  
  void TypeTool::draw() {
    if (!m_active || !getImage(false)) return;
  
    TFontManager *instance = TFontManager::instance();
  
    /*TAffine viewMatrix = getViewer()->getViewMatrix();
  glPushMatrix();
    tglMultMatrix(viewMatrix);*/
  
    if (instance->hasVertical() && m_isVertical) {
      glPushMatrix();
      tglMultMatrix(TRotation(m_startPoint, -90));
    }
  
    // draw text
    UINT size = m_string.size();
  
    TPoint descenderP(0, TFontManager::instance()->getLineDescender());
  
    for (int j = 0; j < (int)size; j++) {
      if (m_string[j].isReturn()) continue;
  
      TImageP img = TImageP(getImage(false));
      assert(!!img);
      if (!img) return;
  
      TPalette *vPalette = img->getPalette();
      assert(vPalette);
  
      double charWidth = 0;
      if (TVectorImageP vi = m_string[j].m_char) {
        TTranslation transl(convert(descenderP) + m_string[j].m_charPosition);
        const TVectorRenderData rd(transl, TRect(), vPalette, 0, false);
        tglDraw(rd, vi.getPointer());
        charWidth = vi->getBBox().getLx();
      } else if (TToonzImageP ti = m_string[j].m_char) {
        TDimension dim = ti->getSize();
        ti->setPalette(vPalette);
  
        TPoint rasterCenter(dim.lx / 2, dim.ly / 2);
        TTranslation transl1(convert(rasterCenter));
        TTranslation transl2(m_string[j].m_charPosition);
        GLRasterPainter::drawRaster(transl2 * m_scale * transl1, ti, false);
  
        TPointD adjustedDim = m_scale * TPointD(dim.lx, dim.ly);
        charWidth           = adjustedDim.x;
      }
  
      // sottolineo i caratteri della preedit string
      if (m_preeditRange.first <= j && j < m_preeditRange.second) {
        TPointD a(m_string[j].m_charPosition);
        TPointD b = a + TPointD(charWidth, 0);
        glColor3d(1, 0, 0);
        tglDrawSegment(a, b);
      }
    }
  
    // draw textbox
    double pixelSize = sqrt(tglGetPixelSize2());
    if (!isAlmostZero(pixelSize - m_pixelSize)) {
      m_textBox   = m_textBox.enlarge((pixelSize - m_pixelSize) * cBorderSize);
      m_pixelSize = pixelSize;
    }
    TPixel32 boxColor = TPixel32::Black;
    ToolUtils::drawRect(m_textBox, boxColor, 0x5555);
  
    if (m_active) {
      // draw cursor
      tglColor(TPixel32::Black);
      if (!m_isVertical || instance->hasVertical())
        tglDrawSegment(m_cursorPoint,
                       m_cursorPoint + m_scale * TPointD(0, -m_dimension));
      else
        tglDrawSegment(m_cursorPoint,
                       m_cursorPoint + m_scale * TPointD(m_dimension, 0));
    }
  
    TPointD drawableCursor = m_cursorPoint;
    if (instance->hasVertical() && m_isVertical) {
      drawableCursor = TRotation(m_startPoint, -90) * drawableCursor;
      glPopMatrix();
    }
  
    // glPopMatrix();
  
    //  TPoint ipos = m_viewer->toolToMouse(drawableCursor);
    //  setImePosition(ipos.x, ipos.y, (int)m_dimension);
  }
  
  //---------------------------------------------------------
  
  void TypeTool::addTextToVectorImage(const TVectorImageP &currentImage,
                                      std::vector<const TVectorImage *> &images) {
    UINT oldSize = currentImage->getStrokeCount();
  
    std::vector<TFilledRegionInf> *fillInformationBefore =
        new std::vector<TFilledRegionInf>;
    ImageUtils::getFillingInformationOverlappingArea(
        currentImage, *fillInformationBefore, m_textBox);
  
    currentImage->mergeImage(images);
  
    std::vector<TFilledRegionInf> *fillInformationAfter =
        new std::vector<TFilledRegionInf>;
    ImageUtils::getFillingInformationOverlappingArea(
        currentImage, *fillInformationAfter, m_textBox);
  
    UINT newSize = currentImage->getStrokeCount();
  
    TXshSimpleLevel *level =
        TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
    UndoTypeTool *undo =
        new UndoTypeTool(fillInformationBefore, fillInformationAfter, level,
                         getCurrentFid(), m_isFrameCreated, m_isLevelCreated);
  
    for (UINT j = oldSize; j < newSize; j++)
      undo->addStroke(currentImage->getStroke(j));
    TUndoManager::manager()->add(undo);
    if (m_undo) {
      delete m_undo;
      m_undo = 0;
    }
  }
  //---------------------------------------------------------
  
  void TypeTool::addTextToToonzImage(const TToonzImageP &currentImage) {
    UINT size = m_string.size();
    if (size == 0) return;
  
    // TPalette *palette  = currentImage->getPalette();
    // currentImage->lock();
  
    TRasterCM32P targetRaster = currentImage->getRaster();
    TRect changedArea;
  
    UINT j;
    for (j = 0; j < size; j++) {
      if (m_string[j].isReturn()) continue;
  
      if (TToonzImageP ti = m_string[j].m_char) {
        TRectD srcBBox  = ti->getBBox() + m_string[j].m_charPosition;
        TDimensionD dim = srcBBox.getSize();
        TDimensionD enlargeAmount(dim.lx * (m_scale.a11 - 1.0),
                                  dim.ly * (m_scale.a22 - 1.0));
        changedArea += ToonzImageUtils::convertWorldToRaster(
            srcBBox.enlarge(enlargeAmount), currentImage);
        /*
  if( instance->hasVertical() && m_isVertical)
  vi->transform( TRotation(m_startPoint,-90) );
  */
      }
    }
  
    if (!changedArea.isEmpty()) {
      TTileSetCM32 *beforeTiles = new TTileSetCM32(targetRaster->getSize());
      beforeTiles->add(targetRaster, changedArea);
  
      for (j = 0; j < size; j++) {
        if (m_string[j].isReturn()) continue;
  
        if (TToonzImageP srcTi = m_string[j].m_char) {
          TRasterCM32P srcRaster = srcTi->getRaster();
          TTranslation transl2(m_string[j].m_charPosition +
                               convert(targetRaster->getCenter()));
          TRop::over(targetRaster, srcRaster, transl2 * m_scale);
        }
      }
  
      TTileSetCM32 *afterTiles = new TTileSetCM32(targetRaster->getSize());
      afterTiles->add(targetRaster, changedArea);
  
      TXshSimpleLevel *sl =
          TTool::getApplication()->getCurrentLevel()->getSimpleLevel();
      TFrameId id = getCurrentFid();
  
      TUndoManager::manager()->add(new RasterUndoTypeTool(
          beforeTiles, afterTiles, sl, id, m_isFrameCreated, m_isLevelCreated));
      if (m_undo) {
        delete m_undo;
        m_undo = 0;
      }
  
      ToolUtils::updateSaveBox();
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::addTextToImage() {
    if (!m_validFonts) return;
    TFontManager *instance = TFontManager::instance();
  
    UINT size = m_string.size();
    if (size == 0) return;
  
    TImageP img      = getImage(true);
    TVectorImageP vi = img;
    TToonzImageP ti  = img;
  
    if (!vi && !ti) return;
  
    if (vi) {
      QMutexLocker lock(vi->getMutex());
      std::vector<const TVectorImage *> images;
  
      UINT j;
      for (j = 0; j < size; j++) {
        if (m_string[j].isReturn()) continue;
  
        TPoint descenderP(0, TFontManager::instance()->getLineDescender());
        if (TVectorImageP vi = m_string[j].m_char) {
          vi->transform(
              TTranslation(convert(descenderP) + m_string[j].m_charPosition));
          if (instance->hasVertical() && m_isVertical)
            vi->transform(TRotation(m_startPoint, -90));
          images.push_back(vi.getPointer());
        }
      }
      addTextToVectorImage(vi, images);
    } else if (ti)
      addTextToToonzImage(ti);
  
    notifyImageChanged();
    //  getApplication()->notifyImageChanges();
  
    m_string.clear();
    m_cursorIndex = 0;
    m_textBox     = TRectD();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::setCursorIndexFromPoint(TPointD point) {
    UINT size = m_string.size();
    int line  = 0;
    int retNum;
  
    if (!m_isVertical)
      retNum =
          tround((m_startPoint.y + m_dimension - point.y) / m_dimension - 0.5);
    else
      retNum = tround((m_startPoint.x - point.x) / m_dimension + 0.5);
  
    UINT j = 0;
  
    for (; line < retNum && j < size; j++)
      if (m_string[j].isReturn()) line++;
  
    if (j == size) {
      m_cursorIndex  = size;
      m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      return;
    }
  
    double currentDispl = !m_isVertical ? m_startPoint.x : m_startPoint.y;
  
    for (; j < size; j++) {
      if (m_string[j].isReturn()) {
        m_cursorIndex  = j;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
        return;
      } else {
        if (!m_isVertical) {
          currentDispl += m_string[j].m_offset;
  
          if (currentDispl > point.x) {
            if (fabs(currentDispl - m_string[j].m_offset - point.x) <
                fabs(currentDispl - point.x))
              m_cursorIndex = j;
            else
              m_cursorIndex = j + 1;
            m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
            return;
          }
        } else {
          if (!TFontManager::instance()->hasVertical()) {
            currentDispl -= m_dimension;
            if (currentDispl < point.y) {
              if (fabs(currentDispl + m_dimension - point.y) <
                  fabs(currentDispl - point.y))
                m_cursorIndex = j;
              else
                m_cursorIndex = j + 1;
              m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
              return;
            }
          } else {
            currentDispl -= m_string[j].m_offset;
  
            if (currentDispl < point.y) {
              if (fabs(currentDispl + m_string[j].m_offset - point.y) <
                  fabs(currentDispl - point.y))
                m_cursorIndex = j;
              else
                m_cursorIndex = j + 1;
              m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
              return;
            }
          }
        }
      }
    }
  
    if (j == size) {
      m_cursorIndex  = j;
      m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      return;
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::mouseMove(const TPointD &pos, const TMouseEvent &) {
    updateMouseCursor(pos);
  }
  
  //---------------------------------------------------------
  
  void TypeTool::leftButtonDown(const TPointD &pos, const TMouseEvent &) {
    TSelection::setCurrent(0);
  
    if (getViewer() && getViewer()->getGuidedStrokePickerMode()) {
      getViewer()->doPickGuideStroke(pos);
      return;
    }
  
    if (!m_validFonts) return;
  
    TImageP img;
    if (!m_active)
      img = touchImage();
    else
      img = getImage(true);
    TVectorImageP vi = img;
    TToonzImageP ti  = img;
  
    if (!vi && !ti) return;
  
    setSize(m_size.getValue());
  
    if (m_isFrameCreated) {
      if (vi)
        m_undo = new UndoTypeTool(
            0, 0, getApplication()->getCurrentLevel()->getSimpleLevel(),
            getCurrentFid(), m_isFrameCreated, m_isLevelCreated);
      else
        m_undo = new RasterUndoTypeTool(
            0, 0, getApplication()->getCurrentLevel()->getSimpleLevel(),
            getCurrentFid(), m_isFrameCreated, m_isLevelCreated);
    }
  
    //  closeImeWindow();
    //  if(m_viewer) m_viewer->enableIme(true);
    m_active = true;
  
    if (!m_string.empty()) {
      TPointD clickPoint =
          (TFontManager::instance()->hasVertical() && m_isVertical)
              ? TRotation(m_startPoint, 90) * pos
              : pos;
      if (m_textBox.contains(clickPoint)) {
        setCursorIndexFromPoint(pos);
        updateCursorPoint();
        invalidate();
        return;
      } else {
        resetInputMethod();
        addTextToImage();
      }
    }
  
    m_startPoint = pos;
    updateTextBox();
    updateCursorPoint();
    updateMouseCursor(pos);
    //  enableShortcuts(false);
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::rightButtonDown(const TPointD &pos, const TMouseEvent &) {
    if (!m_validFonts) return;
  
    if (!m_string.empty())
      addTextToImage();
    else
      stopEditing();
    m_cursorIndex = 0;
    updateMouseCursor(pos);
    invalidate();
  }
  
  //-----------------------------------------------------------------------------
  
  // cancella [from,to[ da m_string e lo rimpiazza con text
  // n.b. NON fa updateCharPositions()
  void TypeTool::replaceText(std::wstring text, int from, int to) {
    int stringLength = m_string.size();
    from             = tcrop(from, 0, stringLength);
    to               = tcrop(to, from, stringLength);
  
    // cancello i vecchi caratteri
    m_string.erase(m_string.begin() + from, m_string.begin() + to);
  
    // aggiungo i nuovi
    TFontManager *instance = TFontManager::instance();
    int styleId            = TTool::getApplication()->getCurrentLevelStyleIndex();
  
    TImageP img      = getImage(true);
    TToonzImageP ti  = img;
    TVectorImageP vi = img;
  
    TPoint adv;
    TPointD d_adv;
  
    for (unsigned int i = 0; i < (unsigned int)text.size(); i++) {
      wchar_t character = text[i];
  
      if (vi) {
        TVectorImageP characterImage(new TVectorImage());
        unsigned int index = from + i;
        // se il font ha kerning bisogna tenere conto del carattere successivo
        // (se c'e' e non e' un CR) per calcolare correttamente la distanza adv
        TPoint adv;
        if (instance->hasKerning() && index < m_string.size() &&
            !m_string[index].isReturn())
          adv = instance->drawChar(characterImage, character,
                                   m_string[index].m_key);
        else
          adv = instance->drawChar(characterImage, character);
        TPointD advD = m_scale * TPointD(adv.x, adv.y);
  
        characterImage->transform(m_scale);
        // colora le aree chiuse
        paintChar(characterImage, styleId);
  
        m_string.insert(m_string.begin() + index,
                        StrokeChar(characterImage, advD.x, character, styleId));
      } else if (ti) {
        TRasterCM32P newRasterCM;
        TPoint p;
        unsigned int index = from + i;
  
        if (instance->hasKerning() && (UINT)m_cursorIndex < m_string.size() &&
            !m_string[index].isReturn())
          adv = instance->drawChar((TRasterCM32P &)newRasterCM, p, styleId,
                                   (wchar_t)character,
                                   (wchar_t)m_string[index].m_key);
        else
          adv = instance->drawChar((TRasterCM32P &)newRasterCM, p, styleId,
                                   (wchar_t)character, (wchar_t)0);
  
        d_adv = m_scale * TPointD((double)(adv.x), (double)(adv.y));
  
        TToonzImageP newTImage(
            new TToonzImage(newRasterCM, newRasterCM->getBounds()));
  
        TPalette *vPalette = img->getPalette();
        assert(vPalette);
        newTImage->setPalette(vPalette);
  
        m_string.insert(m_string.begin() + index,
                        StrokeChar(newTImage, d_adv.x, character, styleId));
      }
    }
  
    // se necessario ricalcolo il kerning del carattere precedente
    // all'inserimento/cancellazione
    if (instance->hasKerning() && from - 1 >= 0 &&
        !m_string[from - 1].isReturn() && from < (int)m_string.size() &&
        !m_string[from].isReturn()) {
      TPoint adv =
          instance->getDistance(m_string[from - 1].m_key, m_string[from].m_key);
      TPointD advD = m_scale * TPointD((double)(adv.x), (double)(adv.y));
      m_string[from - 1].m_offset = advD.x;
    }
  }
  
  //---------------------------------------------------------
  
  void TypeTool::addReturn() {
    TVectorImageP vi(new TVectorImage);
    if ((UINT)m_cursorIndex == m_string.size())
      m_string.push_back(StrokeChar(vi, -1., (int)(QChar('\r').unicode()), 0));
    else
      m_string.insert(m_string.begin() + m_cursorIndex,
                      StrokeChar(vi, -1., (int)(QChar('\r').unicode()), 0));
  
    m_cursorIndex++;
    m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
    updateCharPositions(m_cursorIndex - 1);
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::addBaseChar(std::wstring text) {
    TFontManager *instance = TFontManager::instance();
  
    TImageP img      = getImage(true);
    TToonzImageP ti  = img;
    TVectorImageP vi = img;
  
    int styleId = TTool::getApplication()->getCurrentLevelStyleIndex();
    TPoint adv;
    TPointD d_adv;
  
    wchar_t character = text[0];
  
    if (vi) {
      TVectorImageP newVImage(new TVectorImage);
  
      if (instance->hasKerning() && (UINT)m_cursorIndex < m_string.size() &&
          !m_string[m_cursorIndex].isReturn())
        adv = instance->drawChar(newVImage, character,
                                 m_string[m_cursorIndex].m_key);
      else
        adv = instance->drawChar(newVImage, character);
  
      newVImage->transform(m_scale);
      paintChar(newVImage, styleId);
      // if(isSketchStyle(styleId))
      //    enableSketchStyle();
  
      d_adv = m_scale * TPointD((double)(adv.x), (double)(adv.y));
  
      if ((UINT)m_cursorIndex == m_string.size())
        m_string.push_back(StrokeChar(newVImage, d_adv.x, character, styleId));
      else
        m_string.insert(m_string.begin() + m_cursorIndex,
                        StrokeChar(newVImage, d_adv.x, character, styleId));
    } else if (ti) {
      TRasterCM32P newRasterCM;
      TPoint p;
  
      if (instance->hasKerning() && (UINT)m_cursorIndex < m_string.size() &&
          !m_string[m_cursorIndex].isReturn())
        adv = instance->drawChar((TRasterCM32P &)newRasterCM, p, styleId,
                                 (wchar_t)character,
                                 (wchar_t)m_string[m_cursorIndex].m_key);
      else
        adv = instance->drawChar((TRasterCM32P &)newRasterCM, p, styleId,
                                 (wchar_t)character, (wchar_t)0);
  
      // textImage->transform(m_scale);
  
      d_adv = m_scale * TPointD((double)(adv.x), (double)(adv.y));
  
      TToonzImageP newTImage(
          new TToonzImage(newRasterCM, newRasterCM->getBounds()));
  
      TPalette *vPalette = img->getPalette();
      assert(vPalette);
      newTImage->setPalette(vPalette);
  
      // newTImage->updateRGBM(newRasterCM->getBounds(),vPalette);
      // assert(0);
  
      if ((UINT)m_cursorIndex == m_string.size())
        m_string.push_back(StrokeChar(newTImage, d_adv.x, character, styleId));
      else
        m_string.insert(m_string.begin() + m_cursorIndex,
                        StrokeChar(newTImage, d_adv.x, character, styleId));
    }
  
    if (instance->hasKerning() && m_cursorIndex > 0 &&
        !m_string[m_cursorIndex - 1].isReturn()) {
      adv   = instance->getDistance(m_string[m_cursorIndex - 1].m_key, character);
      d_adv = m_scale * TPointD((double)(adv.x), (double)(adv.y));
      m_string[m_cursorIndex - 1].m_offset = d_adv.x;
    }
  
    m_cursorIndex++;
    updateCharPositions(m_cursorIndex - 1);
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::cursorUp() {
    setCursorIndexFromPoint(m_cursorPoint + TPointD(0, 0.5 * m_dimension));
  }
  
  //---------------------------------------------------------
  
  void TypeTool::cursorDown() {
    setCursorIndexFromPoint(m_cursorPoint + TPointD(0, -1.5 * m_dimension));
  }
  
  //---------------------------------------------------------
  
  void TypeTool::cursorLeft() {
    if (TFontManager::instance()->hasVertical()) {
      m_cursorPoint = TRotation(m_startPoint, -90) * m_cursorPoint;
      setCursorIndexFromPoint(m_cursorPoint + TPointD(-1.5 * m_dimension, 0));
    } else
      setCursorIndexFromPoint(m_cursorPoint + TPointD(-0.5 * m_dimension, 0));
  }
  
  //---------------------------------------------------------
  
  void TypeTool::cursorRight() {
    if (TFontManager::instance()->hasVertical()) {
      m_cursorPoint = TRotation(m_startPoint, -90) * m_cursorPoint;
      setCursorIndexFromPoint(m_cursorPoint + TPointD(0.5 * m_dimension, 0));
    } else
      setCursorIndexFromPoint(m_cursorPoint + TPointD(1.5 * m_dimension, 0));
  }
  
  //---------------------------------------------------------
  
  void TypeTool::deleteKey() {
    if ((UINT)m_cursorIndex >= m_string.size()) return;
    TFontManager *instance = TFontManager::instance();
    m_string.erase(m_string.begin() + m_cursorIndex);
  
    if (instance->hasKerning() && m_cursorIndex > 0 &&
        !m_string[m_cursorIndex - 1].isReturn()) {
      TPoint adv;
      if ((UINT)m_cursorIndex < m_string.size() &&
          !m_string[m_cursorIndex].isReturn()) {
        adv = instance->getDistance(m_string[m_cursorIndex - 1].m_key,
                                    m_string[m_cursorIndex].m_key);
      } else {
        adv = instance->getDistance(m_string[m_cursorIndex - 1].m_key, 0);
      }
      TPointD d_adv = m_scale * TPointD((double)(adv.x), (double)(adv.y));
      m_string[m_cursorIndex - 1].m_offset = d_adv.x;
    }
    m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
    updateCharPositions(m_cursorIndex);
    invalidate();
  }
  
  //---------------------------------------------------------
  
  bool TypeTool::keyDown(QKeyEvent *event) {
    QString text = event->text();
    if ((event->modifiers() & Qt::ShiftModifier)) text = text.toUpper();
  
    if (QKeySequence(event->key() + event->modifiers()) == QKeySequence::Paste) {
      QClipboard *clipboard     = QApplication::clipboard();
      const QMimeData *mimeData = clipboard->mimeData();
      if (!mimeData->hasText()) return true;
      text = mimeData->text().replace('\n', '\r');
    }
  
    std::wstring unicodeChar = text.toStdWString();
  
    // return if only ALT, SHIFT or CTRL key is pressed
    if (event->modifiers() != Qt::NoModifier && (unicodeChar.empty()))
      return true;
  
    // per sicurezza
    m_preeditRange = std::make_pair(0, 0);
  
    if (!m_validFonts || !m_active) return true;
  
    switch (event->key()) {
    case Qt::Key_Insert:
    case Qt::Key_CapsLock:
      return true;
  
    case Qt::Key_Home:
    case Qt::Key_PageUp:
      m_cursorIndex  = 0;
      m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      updateCursorPoint();
      invalidate();
      break;
  
    case Qt::Key_End:
    case Qt::Key_PageDown:
      m_cursorIndex  = (int)m_string.size();
      m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      updateCursorPoint();
      invalidate();
      break;
  
    /////////////////// cursors
    case Qt::Key_Up:
      if (!m_isVertical)
        cursorUp();
      else if (m_cursorIndex > 0) {
        m_cursorIndex--;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      }
      updateCursorPoint();
      invalidate();
      break;
  
    case Qt::Key_Down:
      if (!m_isVertical)
        cursorDown();
      else if ((UINT)m_cursorIndex < m_string.size()) {
        m_cursorIndex++;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      }
      updateCursorPoint();
      invalidate();
      break;
  
    case Qt::Key_Left:
      if (m_isVertical)
        cursorLeft();
      else if (m_cursorIndex > 0) {
        m_cursorIndex--;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      }
      updateCursorPoint();
      invalidate();
      break;
  
    case Qt::Key_Right:
      if (m_isVertical)
        cursorRight();
      else if ((UINT)m_cursorIndex < m_string.size()) {
        m_cursorIndex++;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
      }
      updateCursorPoint();
      invalidate();
      break;
  
    /////////////////// end cursors
  
    case Qt::Key_Escape:
      resetInputMethod();
      if (!m_string.empty())
        addTextToImage();
      else
        stopEditing();
      break;
  
    case Qt::Key_Delete:
      deleteKey();
      break;
  
    case Qt::Key_Backspace:
      if (m_cursorIndex > 0) {
        m_cursorIndex--;
        m_preeditRange = std::make_pair(m_cursorIndex, m_cursorIndex);
        deleteKey();
      }
      break;
  
    case Qt::Key_Return:
    case Qt::Key_Enter:
      addReturn();
      break;
  
    default:
      if (unicodeChar.empty()) return false;
      replaceText(unicodeChar, m_cursorIndex, m_cursorIndex);
      int startIndex = m_cursorIndex + 1;
      m_cursorIndex += unicodeChar.size();
      m_preeditRange = std::make_pair(startIndex, m_cursorIndex);
      updateCharPositions(startIndex - 1);
    }
  
    invalidate();
    return true;
  }
  
  //-----------------------------------------------------------------------------
  
  void TypeTool::onInputText(std::wstring preedit, std::wstring commit,
                             int replacementStart, int replacementLen) {
    // butto la vecchia preedit string
    m_preeditRange.first  = std::max(0, m_preeditRange.first);
    m_preeditRange.second = std::min((int)m_string.size(), m_preeditRange.second);
    if (m_preeditRange.first < m_preeditRange.second)
      m_string.erase(m_string.begin() + m_preeditRange.first,
                     m_string.begin() + m_preeditRange.second);
  
    // inserisco la commit string
    int stringLength = m_string.size();
    int a = tcrop(m_preeditRange.first + replacementStart, 0, stringLength);
    int b = tcrop(m_preeditRange.first + replacementStart + replacementLen, a,
                  stringLength);
    replaceText(commit, a, b);
    int index = a + commit.length();
  
    // inserisco la nuova preedit string
    if (preedit.length() > 0) replaceText(preedit, index, index);
    m_preeditRange.first  = index;
    m_preeditRange.second = index + preedit.length();
  
    // aggiorno la posizione del cursore
    m_cursorIndex = m_preeditRange.second;
    updateCharPositions(a);
    invalidate();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::onActivate() {
    init();
    //  getApplication()->editImage();
    m_string.clear();
    m_textBox     = TRectD(0, 0, 0, 0);
    m_cursorIndex = 0;
  }
  
  //---------------------------------------------------------
  
  void TypeTool::onDeactivate() {
    resetInputMethod();
    if (!m_string.empty())
      addTextToImage();  // call internally stopEditing()
    else
      stopEditing();
  }
  
  //---------------------------------------------------------
  
  void TypeTool::reset() {
    m_string.clear();
    m_textBox     = TRectD(0, 0, 0, 0);
    m_cursorIndex = 0;
  }
  
  //---------------------------------------------------------
  
  void TypeTool::onImageChanged() { stopEditing(); }
  
  //=========================================================
  
  static TTool *getTypeTool() { return &typeTool; }
  
  /*void resetTypetTool(TTool*tool)
  {
  
  if (!tool) return;
  TypeTool*tt = dynamic_cast<TypeTool*>(tool);
    if (tt)
      tt->stopEditing();
  }*/
  