/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2015/10/11 - 2017/02/03
/// 
/// @brief  簡易エディタ
/// 
/// ****************************************************************************

#include "../DX/CPL_DX_AutoSaveRenderTargets.h"
#include "CPL_GUC_Editor.h"

namespace CPL {
namespace GUC {

// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @copydoc doxycomment::arg_in_sp_component_for_parent
/// @copydoc doxycomment::arg_in_position_for_component
/// @copydoc doxycomment::arg_in_size_for_component
/// @param[in] in_sp_font_mng_default
///   : デフォルトで使用するフォントオブジェクトのスマートポインタを指定します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Editor::Editor
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<float2          > in_position_for_component
, _in<float2          > in_size_for_component
, _in<SpFontMng       > in_sp_font_mng_default
) : RenderOwner
    ( in_sp_component_for_parent
    , in_position_for_component
    , in_size_for_component
    , float2( 2, 2 )
    , in_size_for_component - float2(4,4)
    )
  , m_sp_font_mng( in_sp_font_mng_default ? in_sp_font_mng_default : sm_def_font_mng() )
{
  m_list_string.push_back( CPL_MAKE_SHARED( td_DefString, CPL_GUC_DEF_STR("") ) );

  auto height_font = m_sp_font_mng->GetFont()->GetHeight();
  GetComponent_ScrollV()->SetStep( static_cast<float>(height_font) );

  zzf_setupDrawState( true, true, true );
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Editor::Editor( cref<Editor> in_editor )
  : RenderOwner( in_editor )
  , m_sp_font_mng( in_editor.m_sp_font_mng )
  , m_size_line_max( in_editor.m_size_line_max )
{
  for( auto& spstr : in_editor.m_list_string )
  {
    m_list_string.push_back
      ( CPL_MAKE_SHARED(td_DefString, *spstr)
      );
  }
}


// _____________________________________________________________________________
/// @brief コピー
/// 
/// ※ 現在はUTF-16を前提とした処理のみ実装。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::Copy()
{
  // コピー用文字列の生成。
  // ※ 内部的にはライン毎の文字列配列で管理しているため、
  //    クリップボードにコピーするため、１つのバッファにまとめる。
  td_DefString buf{L""};
  auto const idx_low  = sm_caret().GetLowIndex();
  auto const idx_high = sm_caret().GetHighIndex();
  if( idx_low.y == idx_high.y )
  {
    auto const length = idx_high.x - idx_low.x;
    auto const size   = length * sizeof(buf[0]);
    buf.resize( length + 1 );
    auto& str = *m_list_string[ idx_low.y ];
    memcpy( &buf[0], &str[ idx_low.x ], size );
  }
  else
  {
    buf = &(*m_list_string[ idx_low.y ])[ idx_low.x ];
    if( idx_high.y - idx_low.y > 1 )
    {
      for( auto idx_y = idx_low.y + 1; idx_y < idx_high.y; ++idx_y )
      {
        buf += L"\n";
        auto sp_str = m_list_string[ idx_y ];
        buf += *sp_str;
      }
    }
    buf += L"\n";
    buf.Insert( -1, &(*m_list_string[ idx_high.y ])[0], idx_high.x );
  }
  //buf += L"\n";

  // クリップボードへコピー
  WIN::Clipboard cb;
  cb.SetData
    ( WIN::Clipboard::eFormat::UnicodeText
    , &buf[0]
    , (buf.length()+1) * sizeof(buf[0])
    );
}

// _____________________________________________________________________________
/// @brief ペースト
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::Paste()
{
  // クリップボードからコピー。
  WIN::Clipboard cb;
  td_DefString str;
  std::vector<char> buf;
  if( cb.GetData( WIN::Clipboard::eFormat::UnicodeText, buf ) )
  {
    Core::ChangeCodeStrings( str, (wchar_t_cptr)&buf[0] );
  }
  else
  if( cb.GetData( WIN::Clipboard::eFormat::Text, buf ) )
  {
    Core::ChangeCodeStrings( str, (char_cptr)&buf[0] );
  }
  else
  {
    return;
  }

  // 現在位置に追加。
  InsertString( str );

  //
  auto height_font = m_sp_font_mng->GetFont()->GetHeight();
  GetComponent_ScrollV()->SetStep( static_cast<float>(height_font) );

  // 変換位置更新
  zzf_updateCaret_();
}

// _____________________________________________________________________________
/// @brief 削除
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::Delete()
{
  zzf_keyDown_Delete_();
}

// _____________________________________________________________________________
/// @brief 全選択
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::AllSelect()
{
  sm_caret().idx0 = { 0, 0 };
  sm_caret().idx1 =
    { static_cast<int>(m_list_string.back()->length())
    , static_cast<int>(m_list_string.size()) - 1
    };
  zzf_updateCaret_();
}


// _____________________________________________________________________________
/// @brief 文字挿入
/// 
/// @param[in] in_char
///   : 挿入する文字を指定します。
/// @param[in] in_ptr_idxs_insert
///   : 補正前のカレットインデックスをint2(row,col)のポインタで指定します。\n
///   : nullptrを渡すと現在のキャレット位置が使用されます。\n
///   : rowを負数にすると指定された行または現在の行の最後尾の列インデックス、\n
///   : colを負数にすると最後尾の行インデックスが使用されます。
/// 
/// @return
/// 文字挿入後のキャレット位置インデックスを次の形式で返します。\n
/// int2( row, col )
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2
  Editor::InsertChar
    ( pin<td_DefChar> in_char
    , pin<int2      > in_ptr_idxs_insert
    )
{
  auto& caret = sm_caret();

  if( in_char && *in_char == '\n' )
  {
    return zzf_insertEnter_( in_ptr_idxs_insert );
  }

  auto idxs        = zzf_reviseIndex_( in_ptr_idxs_insert );
  auto size_char   = Core::GetCharByte_UTF( in_char );
  auto sp_str_line = m_list_string[ idxs.y ];
  sp_str_line->Insert( idxs.x, in_char, size_char );

  ++idxs.x;

  sm_caret().idx0 =
  sm_caret().idx1 = idxs;

  return idxs;
}


// _____________________________________________________________________________
/// @brief 文字列の挿入
/// 
/// @param[in] in_str
///   : 挿入する文字列の先頭アドレスを指定します。
/// @param[in] in_ptr_idxs_insert
///   : 補正前のカレットインデックスをint2(row,col)のポインタで指定します。\n
///   : nullptrを渡すと現在のキャレット位置が使用されます。\n
///   : rowを負数にすると指定された行または現在の行の最後尾の列インデックス、\n
///   : colを負数にすると最後尾の行インデックスが使用されます。
/// 
/// @return 
/// 文字挿入後のキャレット位置インデックスを次の形式で返します。\n
/// int2( row, col )
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2 Editor::InsertString( _in<td_DefString> in_str, pin<int2> in_ptr_idxs_insert )
{
  auto idxs = zzf_reviseIndex_( in_ptr_idxs_insert );
  auto s = &in_str[0];
  while( *s )
  {
    idxs = InsertChar( s, &idxs );
    ++s;
  }
  return idxs;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  Editor::CreateClone
    ( _in<sdptr<Component>> in_sp_component_for_parent
    , _in<float2          > in_offset_position_from_original
    ) const
{
  return
    zzz_CreateClone_Basic<td_self>
      ( in_sp_component_for_parent
      , in_offset_position_from_original
      );
}




// _____________________________________________________________________________
/// @brief 座標からキャレット情報取得
///
/// @param[in] in_width_return
///   : 自動で改行する幅を指定する。\n
///   : が、現状は未対応。\n
///   : デフォルトでは（ほぼ）改行しない意味で100000.0fとなっている。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U16
  Editor::zzf_getCaretData_by_Position_
    ( rout<td_FDM_CD> out_caret_data
    , _in <float    > in_mouse_x
    , _in <float    > in_mouse_y
    , _in <float    > in_width_return
    )
{
  auto sp_font = m_sp_font_mng->GetFont();
  auto height_font = sp_font->GetHeight();
  // ラインインデックスの計算
  int idx_y = static_cast<int>(in_mouse_y / height_font);
  idx_y = Clamp( idx_y, 0, (int)m_list_string.size() - 1 );

  auto sp_str = m_list_string[ idx_y ];
  DX::FontDrawManager::MsgInfo mi =
  { sp_str->GetTop()
  , RECT{ 0, 0, static_cast<int>(in_width_return), 1000 }
  };
  out_caret_data.get_type = DX::FontDrawManager::CaretData::kXY_TO_INDEX;
  out_caret_data.x = static_cast<int>(in_mouse_x);
  out_caret_data.y = height_font / 2;
  m_sp_font_mng->GetCaretData( mi, out_caret_data );
  out_caret_data.y += idx_y * height_font;

  // 文字の中心を境界とするよう、補正する。
  // 具体的には、次の文字の座標を取得し、差分からクリックした文字の幅が求める。
  if( out_caret_data.idx < static_cast<int>(sp_str->length()) )
  {
    auto tmp_mi = mi;
    auto tmp_cd = out_caret_data;
    tmp_cd.idx++;
    tmp_cd.get_type = DX::FontDrawManager::CaretData::kINDEX_TO_XY;
    m_sp_font_mng->GetCaretData( tmp_mi, tmp_cd );
    if( in_mouse_x > (out_caret_data.x + tmp_cd.x) / 2 )
    {
      out_caret_data.idx = tmp_cd.idx;
      out_caret_data.x = tmp_cd.x;
      out_caret_data.y = tmp_cd.y;
    }
  }

  return static_cast<U16>(idx_y);
}


// _____________________________________________________________________________
/// @brief インデックスからキャレット情報取得
/// 
/// @param[out] out_caret_data
///   : キャレットデータ取得用のDX::FontDrawManager::CaretDataオブジェクトを指定します。
/// @param[in] in_idxs
///   : インデックスをint2(row,col)形式で指定します。
/// 
/// @return 行インデックスを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U16
  Editor::zzf_getCaretData_by_Index_
    ( rout<td_FDM_CD> out_caret_data
    , _in <int2     > in_idxs
    )
{
  auto sp_font = m_sp_font_mng->GetFont();
  auto height_font = sp_font->GetHeight();

  // 
  DX::FontDrawManager::MsgInfo mi =
  { m_list_string[ in_idxs.y ]->GetTop()
  , RECT{ 0, 0, 10000,10000 }
  };
  out_caret_data.get_type = DX::FontDrawManager::CaretData::kINDEX_TO_XY;
  out_caret_data.idx = in_idxs.x;
  m_sp_font_mng->GetCaretData( mi, out_caret_data );
  out_caret_data.y += height_font * in_idxs.y;

  return in_idxs.y;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Size
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_Size( _in<sdptr<Component>> in_sp_component )
{
  if( in_sp_component == this->GetLinkMyself()  &&
      GetInitialized() >= zzf_GetClassLevel() )
  {
    auto offs_x = GetComponent_ScrollH()->GetPos();
    auto offs_y = GetComponent_ScrollV()->GetPos();
    SetupWorkingArea
      ( float4
        { -offs_x
        , -offs_y
        , static_cast<float>(m_size_line_max.x) - offs_x
        , static_cast<float>(m_size_line_max.y) - offs_y
        }
      );
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseDown
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_MouseDown
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    , _in<eMouseButton    > in_type_mouse_btn
    )
{
  if( in_sp_component == this->GetComponent_RenderBox() )
  {
    //MoveLinkEnd();
    ActivateTopOrder();
    SetFocus( GetLinkMyself() );

    if( in_type_mouse_btn == eMouseButton::Left )
    {
      auto scb_h = GetComponent_ScrollH()->GetPos();
      auto scb_v = GetComponent_ScrollV()->GetPos();
      auto mx = in_mouse_x - kDRAW_OFFS_X ;
      auto my = in_mouse_y - kDRAW_OFFS_Y;
      td_FDM_CD cd;
      cd.get_type = td_FDM_CD::kXY_TO_INDEX;
      U16 idx_y   = zzf_getCaretData_by_Position_( cd, mx + scb_h, my + scb_v );
      U16 idx_x   = cd.idx;
      //
      auto& caret = sm_caret();
      //caret.pos0.x = cd.x;
      //caret.pos0.y = cd.y;
      //caret.pos1   = caret.pos0;
      caret.height = cd.height;
      caret.idx0   = caret.idx1 = { idx_x, idx_y };

      //SetCapture( GetLinkMyself() );

      return eEventReturnID::LastCall;
    }
  }
  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseUp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_MouseUp
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    , _in<eMouseButton    > in_type_mouse_btn
    )
{
  if( in_sp_component == this->GetComponent_RenderBox() )
  {
    if( in_type_mouse_btn == eMouseButton::Left )
    {
      auto scb_h = GetComponent_ScrollH()->GetPos();
      auto scb_v = GetComponent_ScrollV()->GetPos();
      td_FDM_CD cd;
      U16 idx_y =
        zzf_getCaretData_by_Position_( cd, in_mouse_x + scb_h, in_mouse_y + scb_v );
      U16 idx_x = cd.idx;
      //
      auto& caret = sm_caret();
      //caret.pos1.x = cd.x;
      //caret.pos1.y = cd.y;
      caret.height = cd.height;
      caret.idx1   = { idx_x, idx_y };

      //SetCapture( nullptr );

      // 変換位置更新
      zzf_updateCaret_();
    }
    else
    if( in_type_mouse_btn == eMouseButton::Right )
    {
      //float2 xy{ in_mouse_x, in_mouse_y };
      //// GUIコンポーネント座標からクライアント座標に変換。
      //MappingPosition( GetLinkMyself(), nullptr, &xy, 1 );
      //// クライアント座標からスクリーン座標に変換。
      //POINT p{ (int)xy.x, (int)xy.y };
      //ClientToScreen( GetSubjectWindow()->GetWindowHandle(), &p );
      PopupBasicMenu
        ( GetLinkMyself()
        , in_mouse_x
        , in_mouse_y
        );
    }
    return eEventReturnID::LastCall;
  }
  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseMove
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_MouseMove
    ( _in<sdptr<Component>> in_sp_component
    , _in<float           > in_mouse_x
    , _in<float           > in_mouse_y
    )
{
  if( WIN::Mouse::GetDownL()  &&  in_sp_component == this->GetComponent_RenderBox() )
  {
    auto scb_h = GetComponent_ScrollH()->GetPos();
    auto scb_v = GetComponent_ScrollV()->GetPos();
    td_FDM_CD cd;
    U16 idx_y =
      zzf_getCaretData_by_Position_( cd, in_mouse_x + scb_h, in_mouse_y + scb_v );
    U16 idx_x = cd.idx;
    //
    auto& caret = sm_caret();
    //caret.pos1.x = cd.x;
    //caret.pos1.y = cd.y;
    caret.height = cd.height;
    caret.idx1   = { idx_x, idx_y };
    Update();

    return eEventReturnID::LastCall;
  }
  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Char
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_Char
    ( _in<sdptr<Component>> in_sp_component
    , _in<TCHAR           > in_char_code
    , _in<int             > in_key_data
    )
{
  if( in_sp_component == this->GetLinkMyself() )
  {
    TCHAR char_code = in_char_code;
    auto& caret = sm_caret();

    // キー判別
    switch( (int)char_code )
    {
    case VK_BACK:
    case VK_TAB:
    case VK_CLEAR:
    case VK_ESCAPE:
      break;

    //case VK_RETURN:
    //  //char_code = '\n';
    //  zzf_DeleteSelectStrings( caret.idx0, caret.idx1 );
    //  zzf_insertEnter_( caret.idx0 );
    //  m_size_line_max = zzf_computeWorkingAreaSize_();
    //  SetupWorkingArea( static_cast<float2>(m_size_line_max) );
    //  {
    //    auto height_font = m_sp_font_mng->GetFont()->GetHeight();
    //    GetComponent_ScrollV()->SetStep( static_cast<float>(height_font) );
    //  }

    //  break;

    default:
      {
        if( WIN::Keyboard::Control() )
        {
          switch( char_code )
          {
          case 0x01: AllSelect(); break; // Ctrl + A
          case 0x03: Copy();  break; // Ctrl + C
          case 0x16: Paste(); break; // Ctrl + V
          case 0x18: Cut();   break; // Ctrl + X
          }

          break;
        }
        zzf_DeleteSelectStrings( caret.idx0, caret.idx1 );
        if( (int)char_code == VK_RETURN )
        {
          zzf_insertEnter_( nullptr );
        }
        else
        {
          InsertChar( (int)in_char_code );
        }

        auto height_font = m_sp_font_mng->GetFont()->GetHeight();
        GetComponent_ScrollV()->SetStep( static_cast<float>(height_font) );

        // 変換位置更新
        zzf_updateCaret_();
      }
    }
    Update();
  }

  return eEventReturnID::LastCall;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::KeyEventInterface::On_KeyDown
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_KeyDown
    ( _in<sdptr<Component>> in_sp_component
    , _in<int             > in_virtual_key_code
    )
{
  auto& caret = sm_caret();
  auto  idx0  = caret.GetLowIndex();
  auto  idx1  = caret.GetHighIndex();
  bool  flag_caret_update = false;

  if( in_virtual_key_code == VK_DELETE )
  {
    flag_caret_update = zzf_keyDown_Delete_();
  }
  else
  if( in_virtual_key_code == VK_BACK )
  {
    flag_caret_update = zzf_keyDown_BackSpace_();
  }
  else
  if( in_virtual_key_code == VK_LEFT )
  {
    flag_caret_update = zzf_keyDown_Left_();
  }
  else
  if( in_virtual_key_code == VK_RIGHT )
  {
    flag_caret_update = zzf_keyDown_Right_();
  }
  else
  if( in_virtual_key_code == VK_UP )
  {
    flag_caret_update = zzf_keyDown_Up_();
  }
  else
  if( in_virtual_key_code == VK_DOWN )
  {
    flag_caret_update = zzf_keyDown_Down_();
  }
  else
  if( in_virtual_key_code == VK_HOME )
  {
    flag_caret_update = zzf_keyDown_Home_();
  }
  else
  if( in_virtual_key_code == VK_END )
  {
    flag_caret_update = zzf_keyDown_End_();
  }
  
  if( flag_caret_update )
  {
    zzf_updateCaret_();
  }

  GetComponent_RenderBox()->Update();
  return eEventReturnID::LastCall;
}

// _____________________________________________________________________________
/// @brief キャレット位置更新にともなうもろもろの処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::zzf_updateCaret_()
{
  auto  sp_scb_h    = GetComponent_ScrollH();
  auto  sp_scb_v    = GetComponent_ScrollV();
  auto  size_self   = GetComponent_RenderBox()->GetSize();
  auto  height_font = m_sp_font_mng->GetFont()->GetHeight();
  auto& caret       = sm_caret();
  DX::FontDrawManager::CaretData cd;
  zzf_getCaretData_by_Index_( cd, caret.idx1 );
  int2 pos1{ cd.x - static_cast<int>(sp_scb_h->GetPos())
           , cd.y - static_cast<int>(sp_scb_v->GetPos())
           };

  // 範囲外チェック
  if( pos1.x < 0 )
  {
    sp_scb_h->SetPos( sp_scb_h->GetPos() + pos1.x );
    pos1.x = 0;
  }
  else if( pos1.x > size_self.x )
  {
    sp_scb_h->SetPos( sp_scb_h->GetPos() + (pos1.x - size_self.x) );
    pos1.x = static_cast<int>(size_self.x);
  }
  if( pos1.y < 0 )
  {
    sp_scb_v->SetPos( sp_scb_v->GetPos() + pos1.y );
    pos1.y = 0;
  }
  else if( pos1.y + height_font > size_self.y )
  {
    sp_scb_v->SetPos
      ( sp_scb_v->GetPos() + (pos1.y + height_font - size_self.y)
      , false
      );
    pos1.y = static_cast<int>(size_self.y - height_font);
  }


  UpdateCaret
    ( this->GetSubjectWindow()
    , GetLinkMyself()
    , pos1.x
    , pos1.y
    , caret.height
    );

  m_size_line_max = zzf_computeWorkingAreaSize_();
  SetupWorkingArea( static_cast<float2>(m_size_line_max) );
  Update();
}


// _____________________________________________________________________________
/// @brief Delete Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Delete_()
{
  auto& caret = sm_caret();
  auto  idx0  = caret.GetLowIndex();
  auto  idx1  = caret.GetHighIndex();
  auto sp_str = m_list_string[ idx1.y ];
  if( idx0 == idx1 )
  {
    if( idx1.x >= static_cast<int>(sp_str->length()) )
    {
      if( idx1.y < static_cast<int>(m_list_string.size()) - 1 )
      {
        idx1.x = 0;
        idx1.y++;
      }
    }
    else
    {
      ++idx1.x;
    }
  }
  zzf_DeleteSelectStrings( idx0, idx1 );
  caret.idx0 =
  caret.idx1 = idx1 = idx0;

  return true;
}

// _____________________________________________________________________________
/// @brief Back Space Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_BackSpace_()
{
  auto& caret = sm_caret();
  auto  idx0  = caret.GetLowIndex();
  auto  idx1  = caret.GetHighIndex();

  if( idx0 == idx1 )
  {
    if( idx0.x == 0  &&  idx0.y > 0 )
    {
      --idx0.y;
      idx0.x = static_cast<int>(m_list_string[ idx0.y ]->length());
    }
    else
    {
      idx0.x = Max( 0, idx0.x - 1 );
    }
  }
  zzf_DeleteSelectStrings( idx0, idx1 );
  caret.idx0 =
  caret.idx1 = idx1 = idx0;

  return true;
}


// _____________________________________________________________________________
/// @brief Left Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Left_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_ctrl  = WIN::Keyboard::Control();
  bool is_shift = WIN::Keyboard::Shift();

  if( idx1.x > 0 )
  {
    if( is_ctrl ){ idx1.x = 0; }
    else         { idx1.x--; }
  }
  else
  if( idx1.y > 0 )
  {
    idx1.y--;
    idx1.x = static_cast<int>(m_list_string[ idx1.y ]->length());
  }
  if( !is_shift )
  {
    idx0 = idx1;
  }

  return true;
}

// _____________________________________________________________________________
/// @brief Right Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Right_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_ctrl  = WIN::Keyboard::Control();
  bool is_shift = WIN::Keyboard::Shift();

  auto sp_str = m_list_string[ idx1.y ];
  auto length = static_cast<int>(sp_str->length());
  if( idx1.x < length )
  {
    if( is_ctrl ){ idx1.x = length; }
    else         { idx1.x++; }
  }
  else
  if( idx1.y < static_cast<int>(m_list_string.size())-1 )
  {
    idx1.x = 0;
    idx1.y++;
  }
  if( !is_shift )
  {
    idx0 = idx1;
  }

  return true;
}

// _____________________________________________________________________________
/// @brief Home Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Home_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_shift = WIN::Keyboard::Shift();

  idx1.x = 0;
  if( !is_shift )
  {
    idx0 = idx1;
  }

  return true;
}

// _____________________________________________________________________________
/// @brief End Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_End_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_shift = WIN::Keyboard::Shift();

  auto sp_str = m_list_string[ idx1.y ];
  auto length = static_cast<int>(sp_str->length());

  idx1.x = length;
  if( !is_shift )
  {
    idx0 = idx1;
  }

  return true;
}

// _____________________________________________________________________________
/// @brief Up Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Up_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_shift = WIN::Keyboard::Shift();
  int  height_font = m_sp_font_mng->GetFont()->GetHeight();

  if( idx1.y > 0 )
  {
    auto scb_h = GetComponent_ScrollH()->GetPos();
    auto scb_v = GetComponent_ScrollV()->GetPos();
    DX::FontDrawManager::CaretData cd;
    // 現在のカーソル位置を取得。
    zzf_getCaretData_by_Index_( cd, idx1 );
    // 現在の位置から１行上にずらした座標のインデックスを求める。
    zzf_getCaretData_by_Position_
      ( cd
      , static_cast<float>(cd.x)
      , static_cast<float>(cd.y - height_font)
      );
    //
    idx1.x = cd.idx;
    idx1.y = idx1.y - 1;
    if( !is_shift )
    {
      idx0 = idx1;
    }
    return true;
  }
  return false;
}

// _____________________________________________________________________________
/// @brief Down Key
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool Editor::zzf_keyDown_Down_()
{
  auto& idx0 = sm_caret().idx0;
  auto& idx1 = sm_caret().idx1;
  bool is_shift = WIN::Keyboard::Shift();
  int  height_font = m_sp_font_mng->GetFont()->GetHeight();

  if( idx1.y < static_cast<int>(m_list_string.size()) - 1 )
  {
    auto scb_h = GetComponent_ScrollH()->GetPos();
    auto scb_v = GetComponent_ScrollV()->GetPos();
    DX::FontDrawManager::CaretData cd;
    // 現在のカーソル位置を取得。
    zzf_getCaretData_by_Index_( cd, idx1 );
    // 現在の位置から１行下にずらした座標のインデックスを求める。
    idx1.y =
      zzf_getCaretData_by_Position_
        ( cd
        , static_cast<float>(cd.x)
        , static_cast<float>(cd.y + height_font)
        );
    // 
    idx1.x = cd.idx;
    if( !is_shift )
    {
      idx0 = idx1;
    }
    return true;
  }
  return false;
}



// _____________________________________________________________________________
/// @brief 選択範囲の削除
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  Editor::zzf_DeleteSelectStrings
    ( rio<int2> io_idx_begin
    , rio<int2> io_idx_end
    )
{
  auto& idx0 = io_idx_begin;
  auto& idx1 = io_idx_end;
  if( idx0.y > idx1.y )
  {
    Swap( idx0, idx1 );
  }
  else
  if( idx0.y == idx1.y  &&  idx0.x > idx1.x )
  {
    Swap( idx0, idx1 );
  }

  if( io_idx_begin != io_idx_end )
  {
    auto itr_str_front = m_list_string.begin() + idx0.y;
    auto itr_str_last  = m_list_string.begin() + idx1.y;
    // 選択中の始終以外の中間ライン
    if( Abs(idx0.y - idx1.y) > 1 )
    {
      itr_str_last =
        m_list_string.erase( std::next( itr_str_front ), itr_str_last );
      itr_str_front =
        m_list_string.begin() + idx0.y;
    }
    // 開始行および終了行が同じ場合
    if( itr_str_front == itr_str_last )
    {
      if( idx0.x == (*itr_str_front)->length() )
      {
        auto itr_next = std::next(itr_str_front);
        if( itr_next != m_list_string.end() )
        {
          *itr_str_front->get() = *itr_next->get();
          m_list_string.erase( itr_next );
        }
      }
      else
      {
        (*itr_str_front)->Cut
          ( idx0.x
          , (idx0.x != idx1.x) ? idx1.x - 1 : idx1.x
          );
      }
    }
    else // 開始行および終了行が違う行の場合
    {
      (*itr_str_front)->Cut( idx0.x, (*itr_str_front)->length() - 1 );
      if( idx1.x > 0 )
      {
        (*itr_str_last)->Cut( 0, idx1.x-1 );
      }
      *itr_str_front->get() += *itr_str_last->get();
      m_list_string.erase( itr_str_last );
    }
  }

  idx1 = idx0;
}



// _____________________________________________________________________________
/// @brief 補正されたインデックスを取得
/// 
/// @param[in] in_ptr_idxs
///   : 補正前のカレットインデックスをint2(row,col)のポインタで指定します。\n
///   : nullptrを渡すと現在のキャレット位置が返されます。\n
///   : rowを負数にすると指定された行または現在の行の最後尾の列インデックス、\n
///   : colを負数にすると最後尾の行インデックスが返されます。\n
/// 
/// @return in_ptr_idxsにより補正されたインデックスint2(row,col)が返ります。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2 Editor::zzf_reviseIndex_( pin<int2> in_ptr_idxs )
{
  auto& caret = sm_caret();
  int2 idxs;
  if( in_ptr_idxs == nullptr )
  {
    idxs = caret.idx1;
  }
  else
  {
    idxs.y =
      (int)Clamp<S64>( (S64)(U32)in_ptr_idxs->y, 0, (S64)m_list_string.size()-1 );
    idxs.x =
      (int)Clamp<S64>( (S64)(U32)in_ptr_idxs->x, 0, (S64)m_list_string[ idxs.y ]->length() );
  }
  return idxs;
}

// _____________________________________________________________________________
/// @brief 改行挿入
/// 
/// @param[in] in_ptr_idxs_insert
///   : 挿入位置をインデックスint2(row,col)形式で指定し、そのポインタを渡します。\n
///   : nullptrを渡すと現在のキャレット位置に挿入されます。\n
///   : rowを負数にするとその行の最後尾、\n
///   : colを負数にすると最後の行に追加されます。\n
/// 
/// @return 
/// 文字挿入後のキャレット位置インデックスを次の形式で返します。\n
/// int2( row, col )
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2 Editor::zzf_insertEnter_( pin<int2> in_ptr_idxs_insert )
{
  auto& caret = sm_caret();
  auto idxs = zzf_reviseIndex_( in_ptr_idxs_insert );
  auto sp_str_current = m_list_string[ idxs.y ];
  sdptr<td_DefString> sp_new_str = CPL_MAKE_SHARED(td_DefString);
  *sp_new_str = &(*sp_str_current)[ idxs.x ]; // 改行位置後の文字列を保存。
  sp_str_current->Cut( idxs.x, -1 ); // 改行位置後の文字列を削除。

  idxs.x = 0;
  ++idxs.y;

  // 新しい行の追加とコピー。
  m_list_string.insert( m_list_string.begin() + idxs.y, sp_new_str );
  // キャレット情報更新
  sm_caret().idx0 =
  sm_caret().idx1 = idxs;

  return idxs;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::WindowEventInterface::On_Timer
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_Timer( _in<int> in_timer_ID )
{
  if( in_timer_ID == eID::Tid_CaretSwitch )
  {
    sm_caret().width = -sm_caret().width; // １の補数を設定
                                          // マイナスのときは非表示とする
    GetComponent_RenderBox()->Update();
  }

  return eEventReturnID::SendParent_Position;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_MouseWheel
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_MouseWheel
    ( _in<sdptr<Component>> in_sp_component
    , _in<WORD            > in_fw_keys
    , _in<short           > in_z_delta  
    )
{
  if( this->CheckLinkDownClass( in_sp_component ) )
  {
    auto sp_scroll_v = this->GetComponent_ScrollV();
    sp_scroll_v->SetPos
      ( sp_scroll_v->GetPos() - sp_scroll_v->GetStep() * in_z_delta / WHEEL_DELTA * 4
      );
    //zzf_updateCaret_();
    //GetSubjectWindow()->Update();
  }

  return eEventReturnID::SendParent_Position;
}

// _____________________________________________________________________________
/// @brief 描画用カラーの取得
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  Editor::zzf_getDrawTextColor_
    ( rout<DX::RectColor4P> out_color_text
    , rout<DX::RectColor4P> out_rev_color_text
    )
{
  // テキスト：描画色
  auto sp_colorset_text = GetColorParameterText();
  auto ptr_color_text   = &sp_colorset_text->color_default;
  if( !IsEnable() )
  {
    ptr_color_text = &sp_colorset_text->color_invalid;
  }
  else
  if( sm_sp_ac_comp() == this->GetLinkMyself() )
  {
    if( WIN::Mouse::GetDownL() )
    {
      ptr_color_text = &sp_colorset_text->color_active;
    }
    else
    {
      ptr_color_text = &sp_colorset_text->color_highlight;
    }
  }
  out_color_text = *ptr_color_text;

  // 反対色作成。
  out_rev_color_text =
    DX::RectColor4P
    { kCOLOR_WHITE - ptr_color_text->color[0].rgb
    , kCOLOR_WHITE - ptr_color_text->color[1].rgb
    , kCOLOR_WHITE - ptr_color_text->color[2].rgb
    , kCOLOR_WHITE - ptr_color_text->color[3].rgb
    };

}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_DrawClient
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Editor::On_DrawClient( _in<float4> in_rect )
{
  auto& cp_d3d_IC = DX::Graphics_i_::GetImmediateContext();

  if( IsUpdate() )
  {
    //
    auto offs_sc_h    = GetComponent_ScrollH()->GetPos();
    auto offs_sc_v    = GetComponent_ScrollV()->GetPos();
    auto height_font  = m_sp_font_mng->GetFont()->GetHeight();
    auto rect_client  = GetComponent_RenderBox()->GetRectCoord();
    auto offs_x_scb_h = GetComponent_ScrollH()->GetPos();
    auto idx_offs_y   = static_cast<int>(offs_sc_v / height_font);
    // クリア
    auto& tex_render = GetComponent_RenderBox()->GetRenderTexture();
    tex_render.ClearColor( float4(0,0,0,0) );
    tex_render.ClearDepthStencil( 1.0f, 0 );

    // テキストカラー
    DX::RectColor4P color_text4p, rev_color_text4p;
    U32x4 color_text, rev_color_text;
    zzf_getDrawTextColor_( color_text4p, rev_color_text4p );
    color_text4p    .ConvertColor_to_U32x4( color_text );
    rev_color_text4p.ConvertColor_to_U32x4( rev_color_text );
    //
    bool const is_focus =
      sm_sp_focus() == GetLinkMyself()  ||
      this->CheckLinkDownClass( sm_sp_focus() );
    auto& caret = sm_caret();
    int2 idx0 = caret.idx0;
    int2 idx1 = caret.idx1;
    int2 caret_pos0{ 0, 0 };
    int2 caret_pos1{ 0, 0 };

    if( is_focus )
    {
      //
      if( idx0.y > idx1.y  ||  idx0.y == idx1.y  &&  idx0.x > idx1.x )
      {
        Swap( idx0, idx1 );
      }
      DX::FontDrawManager::CaretData cd;
      zzf_getCaretData_by_Index_( cd, idx0 );
      caret_pos0 = int2{ cd.x, cd.y };
      zzf_getCaretData_by_Index_( cd, idx1 );
      caret_pos1 = int2{ cd.x, cd.y };

      if( sm_caret().width > 0 )
      {
        // キャレット描画。
        float4 rect_craret
          { static_cast<float>(caret_pos1.x + kDRAW_OFFS_X - offs_sc_h)
          , static_cast<float>(caret_pos1.y + kDRAW_OFFS_Y - offs_sc_v)
          , static_cast<float>(caret_pos1.x + kDRAW_OFFS_X - offs_sc_h + sm_caret().width)
          , static_cast<float>(caret_pos1.y + kDRAW_OFFS_Y - offs_sc_v + height_font)
          };
        zzf_DrawRectEx( rect_craret, color_text4p.color ); // このカラーはテキストと同色でよい。
      }
    }

    // 文字列描画
    // とりあえず終端までループ（描画範囲外に達した時点で抜ける）
    float draw_y =
      static_cast<float>(kDRAW_OFFS_Y - static_cast<int>(offs_sc_v) % height_font);
    int idx_y_end = static_cast<int>(m_list_string.size());
    for( int idx_y = idx_offs_y
       ; idx_y < idx_y_end
       ; ++idx_y )
    {
      // 
      auto sp_str = m_list_string[ idx_y ];
      auto draw_size =
        m_sp_font_mng->GetStringLineSize( &(*sp_str)[0], 0, nullptr );
      // 描画範囲
      float4 rt_text
        ( static_cast<float>(kDRAW_OFFS_X - offs_x_scb_h)
        , draw_y
        , static_cast<float>(draw_size.x)
        , in_rect.w
        );
      // テキスト描画情報設定
      DX::FontDrawManager::MsgInfo
        mi{ &(*sp_str)[0]
          , WIN::Rect{ rt_text }
          , 0.0f // z
          , color_text // default text color
          , 0 // rotate z
          , int2(0,0) // rotate center
          , -1 // line height
          , DX::kFDM_X_LEFT | DX::kFDM_Y_TOP
          };

      // 
      //const int2 p0 = sm_caret().pos_x0;
      //const int2 p1 = sm_caret().pos_x1;
      //const int  idx0 = sm_caret().idx_pos0;
      //const int  idx1 = sm_caret().idx_pos1;

      if( is_focus )
      {
        // 選択範囲内の場合。
        if( idx0 != idx1  &&  (idx0.y <= idx_y  &&  idx_y <= idx1.y) )
        {
          float x0 = (idx0.y == idx_y) ? static_cast<float>(caret_pos0.x) : 0.0f;
          float x1 = (idx1.y == idx_y) ? static_cast<float>(caret_pos1.x) : draw_size.x;
          int   i0 = (idx0.y == idx_y) ? idx0.x : 0;
          int   i1 = (idx1.y == idx_y) ? idx1.x : (int)sp_str->length();
          x0 += kDRAW_OFFS_X - offs_sc_h;
          x1 += kDRAW_OFFS_X - offs_sc_h;
          if( i0 > i1 )
          {
            Swap( i0, i1 );
          }
          float4
            rt( Clamp( x0, in_rect.left, in_rect.right )
              , rt_text.y
              , Clamp( x1, rt_text.left, rt_text.right )
              , rt_text.y + draw_size.y
              );
          zzf_DrawRectEx( rt, color_text4p.color );

          // カラー変更
          for( int idx = i0; idx < i1; ++idx )
          {
            mi.SetColor_Index( idx, rev_color_text );
          }
        }
      }

      //Label::On_DrawClient( in_rect );
      m_sp_font_mng->DrawMessage( mi );

      draw_y += height_font;

      if( draw_y > rect_client.Height() )
      {
        break;
      }
    }
    m_sp_font_mng->Translate( sm_ptr_d3ddev() );
  }

  SetUpdated();
}


// _____________________________________________________________________________
/// @brief ワーキングエリアのサイズを計算
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U32x2 Editor::zzf_computeWorkingAreaSize_() const
{
  auto const height_font = m_sp_font_mng->GetFont()->GetHeight();
  U32x2 size{ 0, static_cast<U32>(m_list_string.size() * height_font) };
  for( auto& sp_str : m_list_string )
  {
    auto size_line = m_sp_font_mng->GetStringLineSize( &(*sp_str)[0], 0, nullptr );
    size.x = Max( size.x, (U32)size_line.x );
  }
  return size;
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_UserProc
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  Editor::On_UserProc
    ( _in<sdptr<Component>> in_sp_component
    , _in<long            > in_message
    , _in<WPARAM          > in_wparam
    , _in<LPARAM          > in_lparam
    )
{
  if( auto sp_menu = std::dynamic_pointer_cast<ActiveMenu>(in_sp_component) )
  {
    if( in_message == WM_COMMAND )
    {
      switch( in_wparam )
      {
      case eID::EditCut:    Cut();    return eEventReturnID::LastCall;
      case eID::EditCopy:   Copy();   return eEventReturnID::LastCall;
      case eID::EditPaste:  Paste();  return eEventReturnID::LastCall;
      case eID::EditDelete: Delete(); return eEventReturnID::LastCall;
      }
    }
  }
  return RenderOwner::On_UserProc( in_sp_component, in_message, in_wparam, in_lparam );
}




// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @copydetails CPL::GUC::Editor::Editor
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EditorWindow::EditorWindow
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<float2          > in_position_for_component
, _in<float2          > in_size_for_component
, _in<SpFontMng       > in_sp_font_mng_default
) : Window
    ( in_sp_component_for_parent
    , in_position_for_component
    , in_size_for_component
    )
{
  m_sp_editor =
    Factory<Editor>::Create
      ( GetComponent_RenderBox()
      , in_position_for_component
      , in_size_for_component
      );

  On_Size( GetLinkMyself() );
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EditorWindow::EditorWindow( cref<EditorWindow> in_edit_win )
  : Window( in_edit_win )
  , m_sp_editor
    { FindComponentT_by_UserID<Editor>( in_edit_win.m_sp_editor )
    }
{
  m_sp_editor->SetAutoUserID();
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  EditorWindow::CreateClone
    ( _in<sdptr<Component>> in_sp_component_for_parent
    , _in<float2          > in_offset_position_from_original
    ) const
{
  return
    zzz_CreateClone_Basic<td_self>
      ( in_sp_component_for_parent
      , in_offset_position_from_original
      );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::WindowEventInterface::On_Size
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  EditorWindow::On_Size( _in<sdptr<Component>> in_sp_component )
{
  td_base::On_Size( in_sp_component );

  if( in_sp_component == GetLinkMyself()  &&  m_sp_editor )
  {
    auto space = GetFrameSize();
    auto size_client = GetComponent_RenderBox()->GetSize() - float2{space*2};
    m_sp_editor->SetSize( size_client );
  }

  return eEventReturnID::SendParent_Position;
}



} // namespace GUC
} // namespace CPL
