/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2010/X/X - 2017/02/01
/// 
/// @brief  リストボックスコンポーネント
/// 
/// ****************************************************************************

#include "CPL_GUC_ListBox.h"
#include "CPL_GUC_Label.h"


namespace CPL {
namespace GUC {

const int scb_width = 16;

// リストボックス用標準ラベル
class ListLabel : public Label
{
  using td_base = Label;
  using td_self = ListLabel;
  friend Factory<td_self>;
  friend CallPrivateConstructor<td_self>;
public:
  ListLabel
  ( _in<sdptr<Component>> in_sp_component_for_parent
  , _in<float2          > in_position_for_component
  , _in<float2          > in_size_for_component
  , pin<td_DefChar      > in_string_for_label
  , _in<U32             > in_flags_draw_style = DX::kFDM_X_LEFT | DX::kFDM_Y_TOP
  ) : Label
      ( in_sp_component_for_parent
      , in_position_for_component
      , in_size_for_component
      , in_string_for_label
      , in_flags_draw_style
      )
  {
    this->zzf_setupDrawState( true, false, true );
  }
  ListLabel( cref<ListLabel> in_list_label )
    : Label( in_list_label )
  {
  }

  void On_DrawBackground( _in<float4> in_rect )
  {
    auto sp_color_bg = GetColorParameterBG();

    auto ptr_color = &sp_color_bg->color_default;
    if( !this->IsEnable() )
    {
      ptr_color = &sp_color_bg->color_invalid;
    }
    else
    if( this->IsDrawFrame() )
    {
      if( WIN::Mouse::GetDownL() )
      {
        ptr_color = &sp_color_bg->color_active;
      }
      else
      {
        ptr_color = &sp_color_bg->color_highlight;
      }
    }
    zzf_DrawRectEx( in_rect, ptr_color->color );
  }

  // _____________________________________________________________________________
  /// @copydoc CPL::GUC::MouseEventInterface::On_MouseEnter
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  eEventReturnID
    On_MouseEnter
      ( _in<sdptr<Component>> in_sp_comp_enter
      , _in<sdptr<Component>> in_sp_comp_leave
      ) override
  {
    if( in_sp_comp_enter == GetLinkMyself() )
    {
      Update();
    }
    return Label::On_MouseEnter( in_sp_comp_enter, in_sp_comp_leave );
  }

  // _____________________________________________________________________________
  /// @copydoc CPL::GUC::MouseEventInterface::On_MouseLeave
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  eEventReturnID
    On_MouseLeave
      ( _in<sdptr<Component>> in_sp_comp_leave
      , _in<sdptr<Component>> in_sp_comp_enter
      ) override
  {
    if( in_sp_comp_leave == GetLinkMyself() )
    {
      Update();
    }
    return Label::On_MouseLeave( in_sp_comp_leave, in_sp_comp_enter );
  }
    
};


// _____________________________________________________________________________
/// @brief リストボックスの標準背景カラー。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<DrawColorSet> ListBox::GetDefaultColor_ListBoxBG()
{
  static auto
    sp_color =
      CPL_MAKE_SHARED
        ( DrawColorSet
        , ColorPattern::StaticBG()
        );
  return sp_color;
}

// _____________________________________________________________________________
/// @brief リストボックスの標準フレームカラー。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<DrawColorSet> ListBox::GetDefaultColor_ListBoxFrame()
{
  static DX::RectColor4P
    gradiate
      ( DX::ColorType(1.0f, 1.0f, 1.0f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      , DX::ColorType(0.8f, 0.8f, 0.8f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      );
  static auto
    sp_color =
      CPL_MAKE_SHARED
        ( DrawColorSet
        , DX::RectColor4P( kCOLOR_BLACK     ) * gradiate
        , DX::RectColor4P( kCOLOR_BLACK     ) * gradiate
        , DX::RectColor4P( kCOLOR_WHITE     ) * gradiate
        , DX::RectColor4P( kCOLOR_LIGHTGRAY ) * gradiate
        );
  return sp_color;
}

// _____________________________________________________________________________
/// @brief リストボックスの標準テキストカラー。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<DrawColorSet> ListBox::GetDefaultColor_ListBoxText()
{
  static DX::RectColor4P
    gradiate
      ( DX::ColorType(1.0f, 1.0f, 1.0f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      , DX::ColorType(0.8f, 0.8f, 0.8f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      );
  static auto
    sp_color =
      CPL_MAKE_SHARED
        ( DrawColorSet
        , DX::RectColor4P( kCOLOR_BLACK     ) * gradiate
        , DX::RectColor4P( kCOLOR_BLACK     ) * gradiate
        , DX::RectColor4P( kCOLOR_WHITE     ) * gradiate
        , DX::RectColor4P( kCOLOR_LIGHTGRAY ) * gradiate
        );
  return sp_color;
}



// _____________________________________________________________________________
/// @brief コンストラクタ
/// 
/// @copydoc doxycomment::arg_in_sp_component_for_parent
/// @copydoc doxycomment::arg_in_position_for_component
/// @copydoc doxycomment::arg_in_size_for_component
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ListBox::ListBox
( _in<sdptr<Component>> in_sp_component_for_parent
, _in<float2          > in_position_for_component
, _in<float2          > in_size_for_component
) : RenderOwner
    ( in_sp_component_for_parent
    , in_position_for_component
    , in_size_for_component
    , float2(0,0)
    , in_size_for_component
    )
  , m_select_idx0( -1 )
  , m_select_idx1( -1 )
{
  //// アイテム配置用レイヤーの作成。
  //m_sp_item_layer = Factory<Layer>::Create( GetClient() );


  // 選択カラー設定。
  static DX::RectColor4P
    gradiate
      ( DX::ColorType(1.0f, 1.0f, 1.0f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      , DX::ColorType(0.8f, 0.8f, 0.8f, 1.0f)
      , DX::ColorType(0.9f, 0.9f, 0.9f, 1.0f)
      );
  //m_sp_active_color =
  //  CPL_MAKE_SHARED
  //    ( DrawColorSet
  //    , DX::RectColor4P( COLOr_LIGHTBLUE ) * gradiate
  //    , DX::RectColor4P( COLOr_LIGHTBLUE ) * gradiate
  //    , DX::RectColor4P( COLOr_WHITE     ) * gradiate
  //    , DX::RectColor4P( COLOr_LIGHTGRAY ) * gradiate
  //    );

  //m_sp_normal_color = GetDefaultColor_BG();

  zzf_setupDrawState( true, true, true ); // 描画ステートのデフォルト設定。
  zzf_SetInitializedLevel( zzf_GetClassLevel() );
}


// _____________________________________________________________________________
/// @brief コピーコンストラクタ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ListBox::ListBox( cref<td_self> in_listbox )
  : td_base( in_listbox )
{
  for( auto const& item_info : m_item_table )
  {
    if( auto sp_item_clone = FindComponent_by_UserID( item_info.wp_comp.lock() ) )
    {
      m_item_table.push_back( ItemInfo{ sp_item_clone, false } );
    }
  }
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::CreateClone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  ListBox::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::Component::On_DrawBackground
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ListBox::On_DrawBackground( _in<float4> in_rect )
{
  // 背景
  Component::On_DrawBackground( in_rect );
}

// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_DrawFrame
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ListBox::On_DrawFrame( _in<float4> in_rect )
{
  Component::On_DrawFrame( in_rect );
}

//// _____________________________________________________________________________
//// クライアント描画
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//void ListBox::On_DrawClient( _in<short4> in_rect )
//{
//  const int LH = GetLineHeight();
//  int index =
//    (int)m_select_idx - static_cast<int>(GetComponent_ScrollBar()->GetPos());
//
//  if( !FAILED_INDEX(index) )
//  {
//    short4 rt( in_rect.x, in_rect.y+index*LH, in_rect.z, in_rect.y+index*LH+LH );
//    //zzf_DrawRect_DefaultComponentBG( rt, m_sp_color_bg->sp_color_active );
//    zzf_DrawRectEx( rt, GetColorParameterBG()->sp_color_active->color );
//  }
//
//  // 枠
//  //zzf_DrawFrame( in_rect, COLOr_BLACK );
//
//  // アイテムリスト描画
//  RECT rt_list = { in_rect.x, in_rect.y, in_rect.z, in_rect.w };
//  short2 comp_size = GetSize();
//  const int item_num      = GetItemNum();
//  const int draw_max_num  = comp_size.y / LH + 1;
//  const int draw_idx_base = Max<int>( static_cast<int>(GetComponent_ScrollBar()->GetPos()), 0 );
//  const int draw_idx_end  = Min<int>( item_num, draw_idx_base + draw_max_num );
//
//  DX::FontDrawManager::MsgInfo mi;
//  mi.rect.left   = in_rect.x + 2;
//  mi.rect.top    = in_rect.y + 2;
//  mi.rect.right  = in_rect.z - 2;
//  mi.rect.bottom = in_rect.w - 2;
//  
//  for( int i = draw_idx_base; i < draw_idx_end; ++i )
//  {
//    mi.rect.top    = in_rect.y + LH * (i - draw_idx_base);
//    mi.rect.bottom = rt_list.top + LH;
//#ifdef CPL_UNICODE
//    mi.SetMessage( m_str_table[ i ].GetTop() );
//#else
//    mi.SetMessage(
//      Core::ChangeCodeStrings( Core::StringW(), m_str_table[ i ].GetTop() ) );
//#endif
//    if( m_select_idx == i )
//    {
//      GetColorParameterText()->
//        sp_color_active->
//          ConvertColor_to_U32x4( mi.default_color );
//    }
//    else
//    {
//      GetColorParameterText()->
//        sp_color_default->
//          ConvertColor_to_U32x4( mi.default_color );
//    }
//
//    sm_def_font_mng()->DrawMessage( mi );
//  }
//  sm_def_font_mng()->Translate( sm_ptr_d3ddev() );
//}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseDown
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ListBox::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 == GetLinkMyself() )
  {
    ActivateTopOrder();
  }

  auto sp_item_layer = GetComponent_RenderClient();
  // 属するコンポーネントのチェック。
  if( sp_item_layer->CheckLinkDownClass( in_sp_component ) )
  {
    // まず、クリックされたコンポーネントを含む、子供コンポーネントを特定する。
    auto sp_comp_check_prev = in_sp_component;
    auto sp_comp_check = in_sp_component->GetLinkParent();
    decltype(sp_comp_check) sp_comp_hit;
    while( sp_comp_check )
    {
      if( sp_comp_check == sp_item_layer )
      {
        sp_comp_hit = sp_comp_check_prev;
        break;
      }

      sp_comp_check_prev = sp_comp_check;
      sp_comp_check = sp_comp_check->GetLinkParent();
    }
    // 子供コンポーネントが特定された場合。
    if( sp_comp_hit )
    {
      // キーチェック。
      // Ctrlは追加選択、Shiftは範囲選択、クリックのみは単独選択とする。
      if( WIN::Keyboard::Control() )
      {
        AddSelectItem( sp_comp_hit );
      }
      else
      if( WIN::Keyboard::Shift() )
      {
        // 未対応。
      }
      else
      {
        SetSelectItem( sp_comp_hit );
      }
    }
  }

  Update();

  return eEventReturnID::SendParent_Position;
}

//// _____________________________________________________________________________
//// マウスアップイベント
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//eEventReturnID
//  ListBox::On_MouseUp
//    ( _in<sdptr<Component>> in_sp_component
//    , _in<int             > in_mouse_x
//    , _in<int             > in_mouse_y
//    , _in<MOUSe_BTN       > in_type_mouse_btn
//    )
//{
//  if( GetComponent_ScrollH()->CheckLinkDownClass( in_sp_component )  ||
//      GetComponent_ScrollV()->CheckLinkDownClass( in_sp_component ) )
//  {
//    m_sp_item_layer->SetPosition
//      ( short2( static_cast<short>(-GetComponent_ScrollH()->GetPos())
//              , static_cast<short>(-GetComponent_ScrollV()->GetPos()) )
//      );
//    Update();
//    return eEventReturnID::LastCall;
//  }
//
//  return eEventReturnID::SendParent;
//}

//// _____________________________________________________________________________
//// マウスムーブイベント
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//eEventReturnID
//  ListBox::On_MouseMove
//    ( _in<sdptr<Component>> in_sp_component
//    , _in<int             > in_mouse_x
//    , _in<int             > in_mouse_y
//    )
//{
//  if( in_sp_component == GetComponent_ScrollH()->GetComponent_KnobButton()  ||
//      in_sp_component == GetComponent_ScrollV()->GetComponent_KnobButton() )
//  {
//    m_sp_item_layer->SetPosition
//      ( short2( static_cast<short>(-GetComponent_ScrollH()->GetPos())
//              , static_cast<short>(-GetComponent_ScrollV()->GetPos()) )
//      );
//    Update();
//  }
//  return eEventReturnID::SendParent;
//}

// _____________________________________________________________________________
/// @copydoc CPL::GUC::MouseEventInterface::On_MouseWheel
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ListBox::On_MouseWheel
    ( _in<sdptr<Component>> in_sp_component
    , _in<WORD            > in_fw_keys
    , _in<short           > in_z_delta
    )
{
  if( CheckLinkDownClass( in_sp_component ) )
  {
    float pos = GetComponent_ScrollV()->GetPos();
    pos -= in_z_delta;
    this->GetComponent_ScrollV()->SetPos( pos );
    Update();

    return eEventReturnID::LastCall;
  }

  return CPl_GUC_LIST_BOX_BASE::On_MouseWheel( in_sp_component, in_fw_keys, in_z_delta );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::Component::On_Size
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ListBox::On_Size( _in<sdptr<Component>> in_sp_component )
{
  if( in_sp_component == this->GetLinkMyself()  &&
      GetInitialized() >= zzf_GetClassLevel() )
  {
    // ワーキングエリアの調整（スクロールバー調整）
    auto const size_frame = GetFrameSize() * IsDrawFrame();
    auto const rect_comp = GetRectCoord();
    SetupWorkingArea
      ( float4
        ( size_frame
        , size_frame
        , rect_comp.Width()  - size_frame
        , rect_comp.Height() - size_frame
        )
      , GetSize_WorkingArea()
      );

    // スクロールバーとアイテム位置の調整。
    AutoRepositionItems();

    ////
    //auto scr_h = GetComponent_ScrollH()->GetPos();
    //auto scr_v = GetComponent_ScrollV()->GetPos();
    //m_sp_item_layer->SetPosition
    //  ( short2( static_cast<short>(-scr_h)
    //          , static_cast<short>(-scr_v) )
    //  );
    Update();
  }

  return CPl_GUC_LIST_BOX_BASE::On_Size( in_sp_component );
}


// _____________________________________________________________________________
/// @copydoc CPL::GUC::KeyEventInterface::On_KeyDown
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
eEventReturnID
  ListBox::On_KeyDown
    ( _in<sdptr<Component>> in_sp_component
    , _in<int             > in_virtual_key_code
    )
{
  if( this->CheckLinkDownClass( in_sp_component ) )
  {
    switch( in_virtual_key_code )
    {
    case VK_UP:
      if( m_select_idx0 > 0 )
      {
        this->SetSelectItem( m_select_idx0 - 1 );
        Update();
      }
      break;
    case VK_DOWN:
      if( m_select_idx0 < m_item_table.size()-1 )
      {
        this->SetSelectItem( m_select_idx0 + 1 );
        Update();
      }
      break;
    }
  }

  return CPl_GUC_LIST_BOX_BASE::On_KeyDown( in_sp_component, in_virtual_key_code );
}

//// _____________________________________________________________________________
//// 拡張プロシージャ
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//eEventReturnID
//  ListBox::On_UserProc
//    ( _in<sdptr<Component>> in_sp_component
//    , _in<long            > in_message
//    , _in<WPARAM          > in_wparam
//    , _in<LPARAM          > in_lparam
//    )
//{
//  switch( in_message )
//  {
//  case eID::ScrollBarChangeValue:
//    m_sp_item_layer->SetPosition
//      ( short2
//        ( static_cast<short>(-GetComponent_ScrollH()->GetPos())
//        , static_cast<short>(-GetComponent_ScrollV()->GetPos())
//        )
//      );
//    break;
//
//  case eID::ValueChanged:
//    break;
//  }
//
//  return eEventReturnID::SendParent;
//}

// _____________________________________________________________________________
/// @brief ラベルアイテム追加
/// 
/// @param[in] in_string_for_label
///   : ラベルのテキストを指定します。
/// @param[in] in_insert_idx
///   : 挿入インデックスを指定します。\n
///   : -1を指定すると末尾を指します。
/// 
/// @return コンポーネントのスマートポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  ListBox::AddItem
    ( pin<td_DefChar> in_string_for_label
    , _in<size_t    > in_insert_idx
    )
{
  // ラベルコンポーネントの作成。
  auto sp_label_new =
    Factory<ListLabel>::Create
      ( GetComponent_RenderClient()
      , kPOS_DEFAULT
      , kSIZE_DEFAULT
      , in_string_for_label
      );

  return AddItem( sp_label_new, in_insert_idx );
}

// _____________________________________________________________________________
/// @brief コンポーネントアイテムの追加
/// 
/// @param[in] in_sp_component
///   : 追加するコンポーネントのスマートポインタを指定します。
/// @param[in] in_insert_idx
///   : 挿入位置のインデックスを指定します。\n
///   : -1を指定すると末尾を指します。
/// 
/// @return コンポーネントのスマートポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component>
  ListBox::AddItem
    ( _in<sdptr<Component>> in_sp_component
    , _in<size_t          > in_insert_idx
    )
{
  if( FAILED_INDEX(in_insert_idx) )
  {
    m_item_table.push_back( {in_sp_component, false} );
    GetComponent_RenderClient()->AddLinkChild( in_sp_component );
  }
  else
  {
    m_item_table.insert
      ( m_item_table.begin() + in_insert_idx
      , {in_sp_component, false}
      );
    if( auto sp_comp = m_item_table[ in_insert_idx + 1 ].wp_comp.lock() )
    {
      sp_comp->AddLinkPrev( in_sp_component );
    }
  }

  in_sp_component->SetBelongComponent
    ( GetComponent_RenderClient()
    , eParentType::All
    );

  //AutoRepositionItems();

  return in_sp_component;
}

// _____________________________________________________________________________
/// @brief 選択アイテムの取得
/// 
/// ※ 複数選択されている場合、先頭のアイテムを返す。
/// 
/// @return
/// 選択中のコンポーネントのスマートポインタを返します。\n
/// 非選択状態では、nullptr相当のスマートポインタを返します。\n
/// 複数選択されていた場合は先頭アイテムのスマートポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component> ListBox::GetSelectItem() const
{
  if( m_select_idx0 < m_item_table.size() )
  {
    return m_item_table[ m_select_idx0 ].wp_comp.lock();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief 選択アイテムリストの取得
/// 
/// @param[out] out_select_table
///   : コンポーネントポインタリストを取得するための配列オブジェクトを指定します。
/// 
/// @return 選択された要素数を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  ListBox::GetSelectItemList
    ( rout<std::vector<sdptr<Component>>> out_select_table
    ) const
{
  out_select_table.clear();
  for( auto& item : m_item_table )
  {
    if( item.is_select )
    {
      out_select_table.push_back( item.wp_comp.lock() );
    }
  }
  return out_select_table.size();
}

// _____________________________________________________________________________
/// @brief 選択アイテムリストの取得
/// 
/// @return 選択アイテムリストを格納した配列を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
auto ListBox::GetSelectItemList() const->std::vector<sdptr<Component>>
{
  std::vector<sdptr<Component>> arr_comp;
  GetSelectItemList( arr_comp );
  return arr_comp;
}

// _____________________________________________________________________________
/// @brief 指定したインデックスのコンポーネントを取得
/// 
/// @return
/// 指定したインデックスのアイテムコンポーネントのスマートポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<Component> ListBox::GetItem( _in<size_t> in_index ) const
{
  if( in_index < m_item_table.size() )
  {
    return m_item_table[ in_index ].wp_comp.lock();
  }
  return nullptr;
}

// _____________________________________________________________________________
/// @brief アイテム数の取得
/// 
/// @return 保持するアイテム数を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t ListBox::GetItemNum() const
{
  return m_item_table.size();
}


// _____________________________________________________________________________
// 選択補助インデックスの調整。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  ListBox::zzz_AdjustSelectIndex
    ( _in<size_t> in_index
    , _in<bool  > in_flag_select
    )
{
  if( in_flag_select )
  {
    if( in_index < m_select_idx0 )
    {
      m_select_idx0 = in_index;
    }
    if( m_select_idx1 == -1  ||  in_index > m_select_idx1 )
    {
      m_select_idx1 = in_index;
    }
  }
  else
  {
    if( m_select_idx0 == m_select_idx1 )
    {
      m_select_idx0 =
      m_select_idx1 = -1;
    }
    else
    {
      if( m_select_idx0 == in_index )
      {
        while( m_select_idx0 < m_select_idx1 )
        {
          ++m_select_idx0;
          if( m_item_table[ m_select_idx0 ].is_select )
          {
            break;
          }
        }
      }
      if( m_select_idx1 == in_index )
      {
        while( m_select_idx0 < m_select_idx1 )
        {
          --m_select_idx1;
          if( m_item_table[ m_select_idx1 ].is_select )
          {
            break;
          }
        }
      }
    }
  }
}


// _____________________________________________________________________________
/// @brief 指定したアイテムコンポーネントのインデックスを検索
/// 
/// @param[in] in_sp_component
///   : 検索するコンポーネントのスマートポインタを指定します。
/// 
/// @return
/// 該当するインデックスを返します。\n
/// 該当しない場合-1を返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t
  ListBox::FindItemComponentIndex( _in<sdptr<Component>> in_sp_component ) const
{
  CPL_LOOP2( m_item_table.size(), idx )
  {
    if( m_item_table[ idx ].wp_comp.lock() == in_sp_component )
    {
      return idx;
    }
  }
  return -1;
}


// _____________________________________________________________________________
/// @brief アイテムの選択（他の選択はクリアされる）
/// 
/// @param[in] in_index
///   : 選択するアイテムのインデックスを指定します。\n
///   : 範囲外を指定すると非選択となります。
/// 
/// @retval true  : 指定インデックスが範囲内。
/// @retval false : 指定インデックスが範囲外。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::SetSelectItem( _in<size_t> in_index )
{
  bool res = false;
  if( in_index < m_item_table.size() )
  {
    m_select_idx0 =
    m_select_idx1 = in_index;
    res = true;
  }
  else
  {
    m_select_idx0 =
    m_select_idx1 = -1;
  }

  auto num_comp = m_item_table.size();
  sdptr<Component> sp_comp_active;
  //for( auto& comp : m_item_table )
  //{
  //}

  for( size_t idx = 0; idx < num_comp; ++idx )
  {
    auto& item = m_item_table[ idx ];
    auto sp_comp = item.wp_comp.lock();
    if( idx == in_index )
    {
      sp_comp->SetStateDrawFrame( true );
      item.is_select = true;
      sp_comp_active = sp_comp;
    }
    else
    {
      sp_comp->SetStateDrawFrame( false );
      item.is_select = false;
    }
  }
  if( sp_comp_active )
  {
    auto sp_scroll = this->GetComponent_ScrollV();
    auto rt_select = sp_comp_active->GetRectCoord();
    auto rt_client = GetComponent_RenderBox()->GetRectCoord();
    MappingPosition
      ( sp_comp_active->GetBelongComponent( eParentType::Position )
      , GetLinkMyself()
      , reinterpret_cast<float2*>(&rt_select)
      , 2
      );
    if( rt_select.bottom > rt_client.bottom )
    {
      sp_scroll->SetPos( sp_scroll->GetPos() + (rt_select.bottom-rt_client.bottom) );
    }
    else
    if( rt_select.top < rt_client.top )
    {
      sp_scroll->SetPos( sp_scroll->GetPos() + (rt_select.top-rt_client.top) );
    }
  }
  Update();

  return res;
}

// _____________________________________________________________________________
/// @brief アイテムの選択（他の選択はクリアされる）
/// 
/// @param[in] in_sp_component
///   : 選択するアイテムコンポーネントのスマートポインタを指定します。\n
///   : ポインタはリストボックスの直下(子供)のポインタとしてください。\n
///   : 該当しない場合、非選択状態となります。
/// 
/// @retval true  : 指定アイテムがヒットした。
/// @retval false : 指定アイテムがヒットしなかった。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::SetSelectItem( _in<sdptr<Component>> in_sp_component )
{
  bool   res = false;
  size_t count = 0;
  for( auto& item : m_item_table )
  {
    if( auto sp_comp = item.wp_comp.lock() )
    {
      if( sp_comp == in_sp_component )
      {
        item.is_select = true;
        m_select_idx0 =
        m_select_idx1 = count;
        res = true;
        sp_comp->SetStateDrawFrame( true );
        //sp_comp->SetColorParameterBG( m_sp_active_color );
        SendParentMessage
          ( GetLinkMyself()
          , eID::ListBoxChangeItem
          , count
          , 0
          , GUC::eParentType::Position
          );
      }
      else if( !WIN::Keyboard::Control() )
      {
        sp_comp->SetStateDrawFrame( false );
        item.is_select = false;
        //sp_comp->SetColorParameterBG( m_sp_normal_color );
      }
    }
    ++count;
  }

  return res;
}


// _____________________________________________________________________________
/// @brief 選択アイテムの追加
/// 
/// @param[in] in_index
///   : 選択するアイテムのインデックスを指定します。
/// 
/// @retval true  : 指定インデックスが範囲内。
/// @retval false : 指定インデックスが範囲外。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::AddSelectItem( _in<size_t> in_index )
{
  if( in_index < m_item_table.size() )
  {
    auto& item = m_item_table[ in_index ];
    item.is_select = !item.is_select;
    if( auto sp_comp = item.wp_comp.lock() )
    {
      sp_comp->SetStateDrawFrame( item.is_select );
      if( item.is_select )
      {
        //sp_comp->SetColorParameterBG( m_sp_active_color );
        sp_comp->SetStateDrawFrame( true );
      }
      else
      {
        //sp_comp->SetColorParameterBG( m_sp_normal_color );
        sp_comp->SetStateDrawBG( false );
      }
      zzz_AdjustSelectIndex( in_index, item.is_select );
      return true;
    }
  }
  return false;
}

// _____________________________________________________________________________
/// @brief 選択アイテムの追加
/// 
/// @param[in] in_sp_component
///   : 選択するアイテムを指すスマートポインタを指定します。
/// 
/// @retval true  : 指定アイテムがヒットした。
/// @retval false : 指定アイテムがヒットしなかった。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::AddSelectItem( _in<sdptr<Component>> in_sp_component )
{
  CPL_LOOP2( m_item_table.size(), idx )
  {
    auto& item = m_item_table[ idx ];
    if( item.wp_comp.lock() == in_sp_component )
    {
      item.is_select = !item.is_select;
      in_sp_component->SetStateDrawFrame( item.is_select );
      if( item.is_select )
      {
        //in_sp_component->SetColorParameterBG( m_sp_active_color );
        in_sp_component->SetStateDrawFrame( true );
      }
      else
      {
        //in_sp_component->SetColorParameterBG( m_sp_normal_color );
        in_sp_component->SetStateDrawFrame( false );
      }
      zzz_AdjustSelectIndex( idx, item.is_select );
      return true;
    }
  }
  return false;
}

// _____________________________________________________________________________
/// @brief アイテムのリリース
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ListBox::Release()
{
  auto sp_comp_item = GetItem( 0 );
  if( !sp_comp_item )
  {
    return;
  }
  auto sp_comp_del = sp_comp_item->GetLinkEnd();
  while( sp_comp_del )
  {
    sp_comp_del = sp_comp_del->Detach( false );
  }
  
  m_select_idx0 =
  m_select_idx1 = 0;
  m_item_table.clear();

  AutoRepositionItems();
}

// _____________________________________________________________________________
/// @brief インデックス指定によるアイテムの削除。
/// 
/// @param[in] in_delete_index : 削除する要素のインデックス。
/// 
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::DeleteItem( _in<size_t> in_delete_index )
{
  if( in_delete_index >= m_item_table.size() )
  {
    return false;
  }

  // 選択インデックス補正。
  zzz_AdjustSelectIndex( in_delete_index, false );

  // 削除処理。
  if( auto sp_comp = m_item_table[ in_delete_index ].wp_comp.lock() )
  {
    sp_comp->Detach();
    m_item_table.erase( m_item_table.begin() + in_delete_index );
    return true;
  }
  else
  {
    return false;
  }
}


// _____________________________________________________________________________
/// @brief インデックス指定によるアイテムの削除
/// 
/// @param[in] in_sp_component_for_delete
///   : 削除する要素を指すコンポーネントのスマートポインタを指定します。
/// 
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::DeleteItem( _in<sdptr<Component>> in_sp_component_for_delete )
{
  if( in_sp_component_for_delete == nullptr )
  {
    return false;
  }

  size_t delete_index = FindItemComponentIndex( in_sp_component_for_delete );

  if( delete_index != -1 )
  {
    return DeleteItem( delete_index );
  }
  return false;
}


// _____________________________________________________________________________
/// @brief インデックス指定によるアイテムの削除
/// 
/// @retval true  : 削除成功。
/// @retval false : 削除失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ListBox::DeleteSelectItem()
{
  try {
    for( size_t idx = 0; idx < m_item_table.size();)
    {
      if( m_item_table[idx].is_select )
      {
        DeleteItem( idx );
        continue;
      }
      ++idx;
    }
    return true;
  }
  catch(...)
  {
    return false;
  }
}


// _____________________________________________________________________________
/// @brief インデックス指定によるアイテムの変更
/// 
/// @param[in] in_change_index
///   : 変更する要素のインデックスを指定します。
/// @param[in] in_sp_component_of_new
///   : 新しいコンポーネントのスマートポインタを指定します。
/// 
/// @retval true  : 変更成功。
/// @retval false : 変更失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  ListBox::ChangeItem
    ( _in<size_t          > in_change_index
    , _in<sdptr<Component>> in_sp_component_of_new
    )
{
  if( in_change_index >= m_item_table.size() )
  {
    return false;
  }

  if( auto sp_comp = m_item_table[ in_change_index ].wp_comp.lock() )
  {
    sp_comp->SwapLink( in_sp_component_of_new );
    return true;
  }
  return false;
}


// _____________________________________________________________________________
/// @brief アイテムポインタによるアイテムの変更
/// 
/// @param[in] in_sp_component_of_old
///   : 変更する要素を指すスマートポインタを指定します。
/// @param[in] in_sp_component_of_new
///   : 新しいコンポーネントのスマートポインタを指定します。
/// 
/// @retval true  : 変更成功。
/// @retval false : 変更失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  ListBox::ChangeItem
    ( _in<sdptr<Component>> in_sp_component_of_old
    , _in<sdptr<Component>> in_sp_component_of_new
    )
{
  auto change_index = FindItemComponentIndex( in_sp_component_of_old );
  return ChangeItem( in_sp_component_of_old, in_sp_component_of_new );
}


// _____________________________________________________________________________
/// @brief アイテム再配置。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ListBox::AutoRepositionItems()
{
  if( GetItemNum() <= 0 )
  {
    return;
  }

  // アイテムの整地と縦幅を求める。
  //auto size_client = GetComponent_RenderBox()->GetSize();
  float size_frame = this->GetFrameSize();
  auto  size_client = GetSize() - float2(size_frame*2,size_frame*2);
  float count_y = 0;
  float max_x = 0;

  for( auto& item : m_item_table )
  {
    if( auto sp_comp = item.wp_comp.lock() )
    {
      auto size = sp_comp->GetAdjustSize( true ) + float2(size_frame,size_frame);
      sp_comp->SetRectCoord
        ( float4
            ( 0
            , static_cast<float>(count_y)
            , Max<float>( size_client.x-1, size.x+2 )
            , static_cast<float>(count_y) + size.y
            )
        );
      count_y += size.y + 1;
      max_x = Max<float>( size.x, max_x );
    }
  }

  // ワーキングエリアの調整（スクロールバー調整）
  size_frame = size_frame * IsDrawFrame();
  SetupWorkingArea
    ( float4
        ( size_frame
        , size_frame
        , size_client.x + size_frame
        , size_client.y + size_frame
        )
    , float4
        ( 0, 0
        , max_x
        , count_y
        )
    );

  Update();
}



} // namespace GUC
} // namespace CPL

