/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   200X/X/X - 2016/11/18
/// 
/// @brief  ツリービューコントロール
/// 
/// ※ 追加する再のアイテムのポインタは必ずnewで取得したポインタを渡すこととする。
/// 　 また、内部で解放するので、勝手に解放しないこと。
/// 
/// ****************************************************************************


#include "CPL_WIN_TreeView.h"
//#include "CPL_WIN_Base.h"
#include "CPL_WIN_Label.h"


namespace CPL {
namespace WIN {

namespace doxycomment {
/// @param[in] in_ptr_TVI_parent
///   : 親となるツリービューアイテムのポインタを指定します。\n
inline void arg_in_ptr_TVI_parent( pio<TreeViewItem> in_ptr_TVI_parent ){}
/// @param[in] in_ptr_TVI_add
///   : 追加するツリービューアイテムのポインタを指定します。\n
inline void arg_in_ptr_TVI_add( pio<TreeViewItem> in_ptr_TVI_add ){}
/// @param[in] in_ptr_comp_for_TVI
///   : 追加するツリービューアイテムに関連付けるコンポーネントのポインタを指定します。\n
inline void arg_in_ptr_comp_for_TVI( pio<Component> in_ptr_comp_for_TVI ){}
/// @param[in] in_string_for_TVI
///   : 追加するラベルに設定する文字列を指定します。\n
inline void arg_in_string_for_TVI( pin<str_type> in_string_for_TVI ){}
/// @param[in] in_ptr_data_for_TVI
///   : 追加するツリービューアイテムに関連付けるデータのポインタを指定します。\n
inline void arg_in_ptr_data_for_TVI( pin<void> in_ptr_data_for_TVI ){}
/// @param[in] in_ptr_TVI_base
///   : 基準となるツリービューアイテムのポインタを指定します。\n
inline void arg_in_ptr_TVI_base( pio<TreeViewItem> in_ptr_TVI_base ){}
/// @param[in] in_ptr_TVI_move
///   : 移動対象のツリービューアイテムのポインタを指定します。\n
inline void arg_in_ptr_TVI_move( pio<TreeViewItem> in_ptr_TVI_move ){}

/// @return 追加されたツリービューアイテムのポインタを返します。
inline TreeViewItem* return_addTreeViewItem(){ return nullptr; }
}


// _____________________________________________________________________________
/// @brief TreeViewItemコンストラクタ
/// 
/// @param[in] in_ptr_comp
///   : ツリービューアイテムに関連付けるコンポーネントのポインタを指定します。
/// @param[in] in_ptr_tree_view
///   : 親のツリービューアイテムのポインタを指定します。
/// @param[in] in_ptr_data_for_TVI
///   : ツリービューアイテムに関連付けるデータのポインタを指定します。\n
///   : nullptrが指定可能です。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem::TreeViewItem
( pio<Component> in_ptr_comp
, pio<TreeView > in_ptr_tree_view
, pin<void     > in_ptr_data_for_TVI
) : m_ptr_tree_view( in_ptr_tree_view )
  , m_ptr_comp( in_ptr_comp )
  , m_ptr_data( in_ptr_data_for_TVI )
  , m_flag_expand( false )
{
  in_ptr_comp->SetParent( m_ptr_tree_view->m_sp_form->GetWindowHandle() );
  // サブクラス化
  zzf_PROC_COMP = (WNDPROC)::GetWindowLongPtr( in_ptr_comp->GetWindowHandle(), GWLP_WNDPROC );
  ::SetWindowLongPtr
    ( in_ptr_comp->GetWindowHandle()
    , GWLP_WNDPROC
    , reinterpret_cast<LONG_PTR>(zzf_StaticProcTVI)
    );

  sm_ListTVI().push_back( this );

//  ::SetWindowPos( in_ptr_comp->GetWindowHandle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
}


// _____________________________________________________________________________
/// @brief TreeViewItem デストラクタ
/// 
/// コンポーネントと子供アイテムを削除する。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem::~TreeViewItem()
{
  sm_ListTVI().remove( this );
  // コンポーネント破棄
  DEL( GetComponent() );
  // 子供を削除
  if( m_ptr_child ) // 子供があれば全て削除する
  {
    TreeViewItem* in_ptr_del_node = GetNodeChild();
    while( in_ptr_del_node )
    {
      DEL<TreeViewItem*>( in_ptr_del_node, in_ptr_del_node->GetNodeNext() );
    }
  }
}


// _____________________________________________________________________________
/// @brief 指定したコンポーネントを持つアイテムの検索
/// 
/// @param[in] in_ptr_comp : 比較するコンポーネントのポインタを指定します。
/// 
/// @return
/// 該当したコンポーネントのポインタを返します。\n
/// 該当なしの場合nullptrを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cptr<TreeViewItem>
  TreeViewItem::FindTreeViewItem( pin<Component> in_ptr_comp ) const
{
  auto ptr_item = this;
  while( ptr_item )
  {
    if( ptr_item->m_ptr_comp == in_ptr_comp )
    {
      return ptr_item;
    }
    ptr_item = ptr_item->GetNodeNextAll();
  }
  return nullptr;
}


// _____________________________________________________________________________
/// @brief 指定したデータポインタを持つアイテムの検索
/// 
/// @param[in] in_ptr_data_for_TVI : 比較するデータポインタを指定します。
/// 
/// @return 
/// 該当したコンポーネントのポインタを返します。\n
/// 該当なしの場合nullptrを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cptr<TreeViewItem>
  TreeViewItem::FindTreeViewItem( pin<void> in_ptr_data_for_TVI ) const
{
  auto ptr_item = this;
  while( ptr_item )
  {
    if( ptr_item->m_ptr_data == in_ptr_data_for_TVI )
    {
      return ptr_item;
    }
    ptr_item = ptr_item->GetNodeNextAll();
  }
  return nullptr;
}


// _____________________________________________________________________________
/// @brief ツリーの展開設定
/// 
/// @param[in] in_flag_expand
///   : 展開フラグを指定します。\n
///   : false - 子供ツリーを閉じます。
///   : true  - 子供ツリーを開きます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeViewItem::SetExpand( _in<U32> in_flag_expand )
{
  m_flag_expand = in_flag_expand;
  if( m_ptr_tree_view )
  {
    m_ptr_tree_view->SetupViewComponent();
  }
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ツリービューアイテム用拡張プロシージャ
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LRESULT
  CALLBACK TreeViewItem::zzf_StaticProcTVI
    ( HWND   in_hdl_window
    , UINT   in_message
    , WPARAM in_wparam
    , LPARAM in_lparam
    )
{
  Component* in_ptr_comp = Component::GetHandleToPointer( in_hdl_window );
  if( in_ptr_comp )
  {
    // ツリービューアイテムクラスをコンポーネントポインタから検索
    std::list<TreeViewItem*>::iterator
      itr_tree_view_item = sm_ListTVI().begin();

    //TreeViewItem* p = *itr_tree_view_item;

    while( itr_tree_view_item != sm_ListTVI().end() )
    {
      if( (*itr_tree_view_item)->GetComponent() == in_ptr_comp )
      {
        break;
      }
      ++itr_tree_view_item;
    }
    TreeViewItem* ptr_tree_view_item = nullptr;
    if( itr_tree_view_item != sm_ListTVI().end() )
    {
      ptr_tree_view_item = *itr_tree_view_item;
    }

    if( ptr_tree_view_item )
    {
      TreeView* ptr_tree_view = ptr_tree_view_item->m_ptr_tree_view;
      switch( in_message )
      {
      case WM_NCHITTEST:
        ::SendMessage( ptr_tree_view->GetWindowHandle(), in_message, in_wparam, in_lparam );
//        return HTNOWHERE;

/*      case WM_SETCURSOR:
        if( HIWORD(in_wparam) == WM_LBUTTONDOWN ){
  //        POINT pos = { LOWORD(in_wparam), HIWORD(in_wparam) };
  //        ::MapWindowPoints( in_ptr_comp->GetWindowHandle(), ptr_tree_view->GetWindowHandle(), &pos, 1 );
  //        LPARAM lp = (pos.y<<16) + pos.x;
  //        Mouse::SetPos( ptr_tree_view->GetWindowHandle(), lp );
          ptr_tree_view->On_MouseDown( Component::eMouseButton::Left );
//          return 1;
        }*/
        break;
      }
      return ::CallWindowProc( ptr_tree_view_item->zzf_PROC_COMP, in_hdl_window, in_message, in_wparam, in_lparam );
    }
  }
  return 0;
}







// =============================================================================
// ツリービューコントロールクラス
// =============================================================================

const int bar_size = 15;
const int edge_range = 5;
// _____________________________________________________________________________
/// @brief TreeView コンストラクタ
/// 
/// @copydetails doxycomment::arg_in_hdl_win_parent
/// @copydetails doxycomment::arg_in_pos
/// @copydetails doxycomment::arg_in_size
/// @param[in] in_ID : コンポーネントのIDを指定します。
/// @param[in] in_win_style
///   : ウィンドウスタイルを指定します。\n
///   : 詳しくは
/// 
/// @return 
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeView::TreeView
( _in<HWND     > in_hdl_win_parent
, _in<td_Coord2> in_pos
, _in<td_Coord2> in_size
, _in<int      > in_ID
, _in<LONG_PTR > in_win_style
) : m_ptr_root( nullptr )
  , m_flag_scroll_show(false)
{
  m_ptr_select      = nullptr;
  m_ptr_moving_item = nullptr;

  bool flag_create =
    zzf_Create
      ( in_hdl_win_parent
      , SwS("")
      , in_pos
      , in_size
      , in_ID
      , in_win_style
      , 0
      , this
      );
  if( !flag_create )
  {
    CPL_THROW( CPL_WIN_MSG_NOT_CREATE_TREEVIEW );
  }
  ::SetFocus( m_hdl_window );

  auto size_client = GetClientSize();
  // アイテム配置コンテナの作成
  m_sp_form = CPL_MAKE_SHARED( ItemForm, m_hdl_window, size_client );
  m_sp_form->SetBgColor( RGB(255,255,255) );
  // 縦スクロールバーの作成
  m_sp_scroll_v =
    CPL_MAKE_SHARED
      ( ScrollBar
      , m_hdl_window
      , td_Coord2(size_client.x - bar_size, 0)
      , td_Coord2(bar_size, size_client.y)
      , false
      );
  m_sp_scroll_v->Show( false );
  m_sp_scroll_v->SetMin( 0 );
  // 横スクロールバーの作成
  m_sp_scroll_h =
    CPL_MAKE_SHARED
      ( ScrollBar
      , m_hdl_window
      , td_Coord2(0, size_client.y - bar_size)
      , td_Coord2(size_client.x, bar_size)
      , true
      );
  m_sp_scroll_h->Show( false );
  m_sp_scroll_h->SetMin( 0 );

  SetBgColor( RGB(255,255,255) );

  SetupViewComponent();
//  On_Create();
}

  
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// デストラクタ
// アイテムを全て解放する
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeView::~TreeView()
{
  ReleaseItem();
//  if( m_ptr_root ){
//    TreeViewItem* pNode = m_ptr_root->GetNodeEnd();
//    TreeViewItem* in_ptr_del_node;
//    while( pNode ){
//      in_ptr_del_node = pNode;
//      pNode = pNode->GetNodePrev();
//      DEL( in_ptr_del_node );
//    }
//  }
}


// _____________________________________________________________________________
/// @brief アイテムの選択
/// 
/// @param[in] in_ptr_item
///   : 選択状態にしたいツリービューアイテムのポインタを指定します。\n
///   : 該当しない場合は非選択状態になります。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::SetSelectItem( pin<TreeViewItem> in_ptr_item )
{
  if( m_ptr_root->CheckNodeAllClass( in_ptr_item ) )
  {
    this->m_ptr_select = const_cast<TreeViewItem*>(in_ptr_item);
  }
  else
  {
    this->m_ptr_select = nullptr;
  }
}


// _____________________________________________________________________________
/// @brief アイテムの選択
/// 
/// @param[in] in_ptr_comp
///   : 選択状態にしたいツリービューアイテムに関連付けられているコンポーネントのポインタを指定します。\n
///   : 該当しない場合は非選択状態になります。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::SetSelectItem( pio<Component> in_ptr_comp )
{
  SetSelectItem( m_ptr_root->FindTreeViewItem( in_ptr_comp ) );
}


// _____________________________________________________________________________
/// @brief アイテムの選択
/// 
/// @param[in] in_ptr_data_for_TVI
///   : 選択状態にしたいツリービューアイテムに関連付けられているデータのポインタを指定します。\n
///   : 該当しない場合はnullptrが設定されます。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::SetSelectItem( pin<void> in_ptr_data_for_TVI )
{
  TreeViewItem* in_ptr_node = this->m_ptr_root;
  while( in_ptr_node )
  {
    if( in_ptr_node->GetData<void_cptr>() == in_ptr_data_for_TVI )
    {
      this->m_ptr_select = in_ptr_node;
      return;
    }
    in_ptr_node = in_ptr_node->GetNodeNextAll();
  }
  this->m_ptr_select = nullptr;
}


// _____________________________________________________________________________
/// @brief 子供アイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_parent
/// @copydetails doxycomment::arg_in_ptr_TVI_add
/// @param[in] in_index
///   : 子供の挿入したい位置をインデックスで指定します。
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemChild
    ( pio<TreeViewItem> in_ptr_TVI_parent
    , pio<TreeViewItem> in_ptr_TVI_add
    , _in<int         > in_index
    )
{
  if( in_ptr_TVI_add == nullptr )
  {
    return nullptr;
  }
  auto index = in_index;

  if( m_ptr_root )
  {
    if( in_ptr_TVI_parent )
    {
      if( FAILED_INDEX(index) ) // 無効インデックスならば最後尾に追加
      {
        in_ptr_TVI_parent->AddNodeChild( in_ptr_TVI_add );
      }
      else
      { // インデックスが有効なら、インデックス位置にあるアイテムの前に追加
        TreeViewItem* in_ptr_node = in_ptr_TVI_parent->GetNodeChild();
        // 指定インデックス位置のアイテムを検索
        while( --index >= 0  &&  in_ptr_node )
        {
          in_ptr_node = in_ptr_node->GetNodeNext();
        }
        // 検索対象があればその前方に追加し、なければ最後尾に追加される
        if( in_ptr_node )
        {
          in_ptr_node->AddNodePrev( in_ptr_TVI_add );
        }
        else
        {
          in_ptr_TVI_parent->AddNodeChild( in_ptr_TVI_add );
        }
      }
    }
    else // 親が居ない場合はルートクラスに追加
    {
      if( FAILED_INDEX(index) )
      {
        m_ptr_root->AddNodeEnd( in_ptr_TVI_add );
      }
      else
      {
        TreeViewItem* in_ptr_node = m_ptr_root;
        // 指定インデックス位置のアイテムを検索
        while( in_ptr_node  &&  --index >= 0 )
        {
          in_ptr_node = in_ptr_node->GetNodeNext();
        }
        // 検索対象があればその前方に追加し、なければ最後尾に追加
        if( in_ptr_node )
        {
          in_ptr_node->AddNodePrev( in_ptr_TVI_add );
        }
        else
        {
          m_ptr_root->AddNodeEnd( in_ptr_TVI_add );
        }
      }
    }
  }
  else // ルートがない場合は、ルートに設定
  {
    m_ptr_root = in_ptr_TVI_add;
  }
  SetupViewComponent();
  return in_ptr_TVI_add;
}


// _____________________________________________________________________________
/// @brief 子供アイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_parent
/// @copydetails doxycomment::arg_in_ptr_comp_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// @param[in] in_index
///   : 子供の挿入したい位置をインデックスで指定します。
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemChild
    ( pio<TreeViewItem> in_ptr_TVI_parent
    , pio<Component   > in_ptr_comp_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    , _in<int         > in_index
    )
{
  return
    AddItemChild
      ( in_ptr_TVI_parent
      , CPL_NEW(TreeViewItem)( in_ptr_comp_for_TVI, this, in_ptr_data_for_TVI )
      , in_index
      );
}


// _____________________________________________________________________________
/// @brief 子供アイテムの追加
/// 
/// 文字列を指定し、ラベルコンポーネントを自動で生成します。
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_parent
/// @copydetails doxycomment::arg_in_string_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// @param[in] in_index
///   : 子供の挿入したい位置をインデックスで指定します。
/// 
/// @return 追加されたツリービューアイテムのポインタを返します。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemChild
    ( pio<TreeViewItem> in_ptr_TVI_parent
    , pin<str_type    > in_string_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    , _in<int         > in_index
    )
{
  TreeViewItem*
    ptr_item =
      AddItemChild
        ( in_ptr_TVI_parent
        , (Component*)CPL_NEW(Label)( m_hdl_window, in_string_for_TVI )
        , in_ptr_data_for_TVI
        , in_index
        );
//  ptr_item->GetComponent()->SetBgColor( RGB(255,255,255) );
  return ptr_item;
}


// _____________________________________________________________________________
/// @brief 前方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_ptr_TVI_add
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemPrev
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pio<TreeViewItem> in_ptr_TVI_add
    )
{
  if( in_ptr_TVI_base == m_ptr_root )
  {
    m_ptr_root = in_ptr_TVI_add;
  }
  in_ptr_TVI_base->AddNodePrev( in_ptr_TVI_add );
  SetupViewComponent();
  return in_ptr_TVI_add;
}


// _____________________________________________________________________________
/// @brief 前方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_ptr_comp_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemPrev
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pio<Component   > in_ptr_comp_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    )
{
  return
    AddItemPrev
      ( in_ptr_TVI_base
      , CPL_NEW(TreeViewItem)( in_ptr_comp_for_TVI, this, in_ptr_data_for_TVI )
      );
}


// _____________________________________________________________________________
/// @brief 前方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_string_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemPrev
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pin<str_type    > in_string_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    )
{
  return
    AddItemPrev
      ( in_ptr_TVI_base
      , (Component*)CPL_NEW(Label)( m_hdl_window, in_string_for_TVI )
      , in_ptr_data_for_TVI
      );
}


// _____________________________________________________________________________
/// @brief 後方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_ptr_TVI_add
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemNext
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pio<TreeViewItem> in_ptr_TVI_add
    )
{
  in_ptr_TVI_base->AddNodeNext( in_ptr_TVI_add );
  SetupViewComponent();
  return in_ptr_TVI_add;
}


// _____________________________________________________________________________
/// @brief 後方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_ptr_comp_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemNext
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pio<Component   > in_ptr_comp_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    )
{
  return
    AddItemNext
      ( in_ptr_TVI_base
      , CPL_NEW(TreeViewItem)( in_ptr_comp_for_TVI, this, in_ptr_data_for_TVI )
      );
}


// _____________________________________________________________________________
/// @brief 後方にアイテムの追加
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_base
/// @copydetails doxycomment::arg_in_string_for_TVI
/// @copydetails doxycomment::arg_in_ptr_data_for_TVI
/// 
/// @copydetails doxycomment::return_addTreeViewItem
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TreeViewItem*
  TreeView::AddItemNext
    ( pio<TreeViewItem> in_ptr_TVI_base
    , pin<str_type    > in_string_for_TVI
    , pin<void        > in_ptr_data_for_TVI
    )
{
  return
    AddItemNext
      ( in_ptr_TVI_base
      , (Component*)CPL_NEW(Label)( m_hdl_window, in_string_for_TVI )
      , in_ptr_data_for_TVI
      );
}


// _____________________________________________________________________________
/// @brief アイテムを前に移動
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_move
/// @copydetails doxycomment::arg_in_ptr_TVI_base
///   : また、nullptrを指定可能です。その場合、移動対象を１つ前に移動します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  TreeView::MoveItemPrev
    ( pio<TreeViewItem> in_ptr_TVI_move
    , pio<TreeViewItem> in_ptr_TVI_base
    )
{
  // ツリー内のアイテムかどうか検査
  if( in_ptr_TVI_move )
  {
    if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_move ) )
    {
      return;
    }
  }
  else
  {
    return;
  }
  // 移動処理
  if( in_ptr_TVI_base ) // 移動先アイテムが指定されている場合
  {
    if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_base ) )
    {
      // 移動先の指すアイテムポインタが自身のツリー内に無い場合は無効
      return;
    }
    else
    if( in_ptr_TVI_move->CheckNodeDownClass( in_ptr_TVI_base ) )
    {
      // 移動先が子供の場合は無効
      return;
    }
    in_ptr_TVI_move->MoveNodePrev( in_ptr_TVI_base );
  }
  else // 移動先なしの場合は
  {
    in_ptr_TVI_move->MoveNodePrev();
  }
  m_ptr_root = in_ptr_TVI_move->GetNodeRoot();
}


// _____________________________________________________________________________
/// @brief アイテムを後ろに移動
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_move
/// @copydetails doxycomment::arg_in_ptr_TVI_base
///   : また、nullptrを指定可能です。その場合、移動対象を１つ後ろに移動します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  TreeView::MoveItemNext
    ( pio<TreeViewItem> in_ptr_TVI_move
    , pio<TreeViewItem> in_ptr_TVI_base
    )
{
  // ツリー内のアイテムかどうか検査
  if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_move ) )
  {
    return;
  }
  // ツリー内のアイテムかどうか検査
  if( in_ptr_TVI_base ) // 
  {
    if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_base ) ) // ツリー内チェック
    {
      return;
    }
    in_ptr_TVI_move->MoveNodeNext( in_ptr_TVI_base );
  }
  else
  {
    in_ptr_TVI_move->MoveNodeNext();
  }
  m_ptr_root = in_ptr_TVI_move->GetNodeRoot();
}


// _____________________________________________________________________________
/// @brief アイテムを１段階上に移動
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_move
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::MoveItemParent( pio<TreeViewItem> in_ptr_TVI_move )
{
  if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_move ) )
  {
    return;
  }
  in_ptr_TVI_move->MoveNodeParent();
  m_ptr_root = in_ptr_TVI_move->GetNodeRoot();
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// アイテムのクラスを指定したアイテムの下層に移動
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// _____________________________________________________________________________
/// @brief アイテムを指定した親の下に移動
/// 
/// @copydetails doxycomment::arg_in_ptr_TVI_move
/// @copydetails doxycomment::arg_in_ptr_TVI_parent
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void
  TreeView::MoveItemChild
  ( pio<TreeViewItem> in_ptr_TVI_move
  , pio<TreeViewItem> in_ptr_TVI_parent
  )
{
  if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_move ) )
  {
    return;
  }
  if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_parent ) )
  {
    return;
  }
  in_ptr_TVI_parent->AddNodeChild( in_ptr_TVI_move );
  m_ptr_root = in_ptr_TVI_parent->GetNodeRoot();
}


// _____________________________________________________________________________
/// @brief アイテム交換
/// 
/// @param[in] in_ptr_TVI0
///   : 交換元のツリービューアイテムを指定します。\n
///   : 指定するツリービューアイテムはツリー内のノードでなければいけません。
/// @param[in] in_ptr_TVI1 
///   : 交換先のツリービューアイテムを指定します。\n
///   : 指定するツリービューアイテムはツリー内のノードでなければいけません。
/// 
/// @retval true  : 交換成功。
/// @retval false : 交換失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  TreeView::SwapItem
    ( pio<TreeViewItem> in_ptr_TVI0
    , pio<TreeViewItem> in_ptr_TVI1
    )
{
  if( in_ptr_TVI0->SwapNode( in_ptr_TVI1 ) )
  {
    return false;
  }
  SetupViewComponent();
  Update();
  return true;
}


// _____________________________________________________________________________
/// @brief アイテムの削除
/// 
/// @param[in] in_ptr_TVI_del
///   : 削除するツリービューアイテムのポインタを指定します。
/// 
/// @return なし。
/// @retval true  : 削除成功。
/// @retval false : 削除失敗（指定したツリービューアイテムが該当しない等）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool TreeView::DeleteItem( pio<TreeViewItem> in_ptr_TVI_del )
{
  if( !m_ptr_root->CheckNodeAllClass( in_ptr_TVI_del ) )
  {
    return false;
  }

  try {
    // 選択候補の修正
    if( in_ptr_TVI_del->GetNodeNext() )
    {
      m_ptr_select = in_ptr_TVI_del->GetNodeNext();
    }
    else
    {
      m_ptr_select = in_ptr_TVI_del->GetNodePrev();
    }

    if( in_ptr_TVI_del == m_ptr_root )
    {
      m_ptr_root = m_ptr_root->GetNodeNext();
    }
    DEL( in_ptr_TVI_del );
    SetupViewComponent();
    Update();
  }
  catch(...)
  {
    return false;
  }

  return true;
}


// _____________________________________________________________________________
/// @brief 全アイテムの解放
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::ReleaseItem()
{
  if( !m_ptr_root )
  {
    return;
  }

  TreeViewItem* ptr_del_item = m_ptr_root->GetNodeEnd();
  while( ptr_del_item )
  {
    DEL( ptr_del_item, ptr_del_item->GetNodePrev() );
  }
  m_ptr_root   = nullptr;
  m_ptr_select = nullptr;
  m_ptr_moving_item = nullptr;

  SetupViewComponent();
  Update();
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 描画処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_Paint()
{
  WIN::PaintDC pdcTV( m_hdl_window );
  if( m_sp_scroll_v->IsVisible() )
  {
    Rect rt;
    m_sp_scroll_v->GetWindowRect( rt );
    ::ValidateRect( m_hdl_window, &rt );
    m_sp_scroll_v->Update( false );
  }

  TreeViewItem* ptr_node = m_ptr_root;
  Component*    ptr_comp;
  //HDC           hdc;
  //PAINTSTRUCT   ps;
  Rect          rect_client;
  Rect          rect_item;
  GetClientRect( rect_client );

//  hdc = ::BeginPaint( m_sp_form->GetWindowHandle(), &ps );
  WIN::PaintDC form_PDC( m_sp_form->GetWindowHandle() );
  WIN::DrawPen gray_pen( form_PDC.GetDC(), 128,128,128 );

  while( ptr_node )
  {
    ptr_comp = ptr_node->GetComponent();
    if( ptr_comp )
    {
      if( ptr_comp->IsVisible() ) // 表示されていれば
      {
//        short2 pos = ptr_comp->GetWindowPos();
        ptr_comp->GetWindowRect( rect_item );
        // 選択中のアイテムライン表示
        if( ptr_node == m_ptr_select )
        {
          WIN::DrawPen pen_red( form_PDC.GetDC(), 255,0,0 );
          ::MoveToEx( form_PDC.GetDC(), rect_item.left-1, rect_item.top-1, nullptr );
          ::LineTo( form_PDC.GetDC(), rect_item.right, rect_item.top-1 );
          ::LineTo( form_PDC.GetDC(), rect_item.right, rect_item.bottom );
          ::LineTo( form_PDC.GetDC(), rect_item.left-1, rect_item.bottom );
          ::LineTo( form_PDC.GetDC(), rect_item.left-1, rect_item.top-1 );
        }
        else
        if( m_ptr_moving_item == ptr_node )
        { // D&Dライン表示
          if( rect_item.top + edge_range > Mouse::GetY() )
          {
            ::MoveToEx( form_PDC.GetDC(), rect_item.left-20, rect_item.top-1, nullptr );
            ::LineTo( form_PDC.GetDC(), rect_client.right, rect_item.top-1 );
          }
          else
          if( rect_item.bottom - edge_range < Mouse::GetY() )
          {
            ::MoveToEx( form_PDC.GetDC(), rect_item.left-20, rect_item.bottom, nullptr );
            ::LineTo( form_PDC.GetDC(), rect_client.right, rect_item.bottom );
          }
          else
          {
            ::MoveToEx( form_PDC.GetDC(), rect_item.left-1, rect_item.top-1, nullptr );
            ::LineTo( form_PDC.GetDC(), rect_item.right, rect_item.top-1 );
            ::LineTo( form_PDC.GetDC(), rect_item.right, rect_item.bottom );
            ::LineTo( form_PDC.GetDC(), rect_item.left-1, rect_item.bottom );
            ::LineTo( form_PDC.GetDC(), rect_item.left-1, rect_item.top-1 );
          }
        }
        // ラインの描画
        if( ptr_node->GetNodeParent()  ||  ptr_node->GetNodeNext()  ||  ptr_node->GetNodePrev() )
        { //
          ::MoveToEx( form_PDC.GetDC(), rect_item.left, rect_item.top+2+5, nullptr );
          ::LineTo( form_PDC.GetDC(), rect_item.left-11, rect_item.top+2+5 );
          WIN::Rect rtChild;
          if( ptr_node->GetNodePrev() )
          {
            ptr_node->GetNodePrev()->GetComponent()->GetWindowRect( rtChild );
            ::LineTo( form_PDC.GetDC(),
              rect_item.left-11,
              rtChild.top +2+5 + (ptr_node->GetNodePrev()->GetNodeChild() ? 3 : 0) );
          }
          else
          if( ptr_node->GetNodeParent() )
          {
            ptr_node->GetNodeParent()->GetComponent()->GetWindowRect( rtChild );
            ::LineTo( form_PDC.GetDC(), rect_item.left-11, rtChild.top +2+5 );
          }
        }
        if( ptr_node->GetNodeChild() ) // 子供があれば
        {
          if( ptr_node->GetExpand() ) // 展開されていれば
          {
            // 下向きボタン？
            sm_bitmap().Draw
              ( form_PDC.GetDC()
              , sem::t_Position<td_Coord2>( {rect_item.left-15, rect_item.top+2} )
              , sem::t_Rect<Rect>( {10,0,20,10} )
              );
          }
          else
          {
            // 右向きボタン？
            sm_bitmap().Draw
              ( form_PDC.GetDC()
              , sem::t_Position<td_Coord2>( {rect_item.left-15, rect_item.top+2} )
              , sem::t_Rect<Rect>( {0,0,10,10} )
              );
          }
        }

      }
    }
    // 次のアイテムを検索
    if( ptr_node->GetNodeChild()  &&  ptr_node->GetExpand() )
    {
      ptr_node = ptr_node->GetNodeChild();
    }
    else
    if( ptr_node->GetNodeNext() )
    {
      ptr_node = ptr_node->GetNodeNext();
    }
    else
    {
      while( ptr_node )
      {
        if( ptr_node->GetNodeParent()  &&  ptr_node->GetNodeParent()->GetNodeNext() )
        {
          ptr_node = ptr_node->GetNodeParent()->GetNodeNext();
          break;
        }
        ptr_node = ptr_node->GetNodeParent();
      }
    }
  }

  //::EndPaint( m_hdl_window, &ps );
  return true;
}


// _____________________________________________________________________________
/// @brief 表示コンポーネントの更新
/// 
/// 保持するコンポーネントの座標や表示状態およびスクロールバー等を更新します。
/// 
/// @return なし。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TreeView::SetupViewComponent()
{
  if( m_sp_scroll_h == nullptr ){ return; }
  if( m_sp_scroll_v == nullptr ){ return; }

  long  left        = -m_sp_scroll_h->GetPos();
  long  top         = -m_sp_scroll_v->GetPos() + 1;
  long2 size_client = GetClientSize();
  Rect  clt_rect( left, top, left + size_client.x, top + size_client.y );
  if( size_client == long2(0,0) )
  {
    return;
  }

  // スクロールバーのスタイルを取得
//  DWORD vsc_style = ::GetWindowLongPtr( m_sp_scroll_v->GetWindowHandle(), GWL_STYLE );
//  DWORD hsc_style = ::GetWindowLongPtr( m_sp_scroll_h->GetWindowHandle(), GWL_STYLE );
  // 
  int2 point2 = SetupViewComponentItem( m_ptr_root, left + 20, top, true );
  int2 size = point2 + int2( -short(left), -short(top) );

  //
  WIN::Rect form_rect;
  m_sp_form->GetWindowRect( form_rect );
  // スクロールバーの表示フラグを設定
  int f_hsc_show_size =
      (m_flag_scroll_show || (size.x > size_client.x))
        ? m_sp_scroll_v->GetWindowSize().x
        : 0;
  int f_vsc_show_size =
      (m_flag_scroll_show || (size.y > size_client.y-f_hsc_show_size))
        ? m_sp_scroll_h->GetWindowSize().y
        : 0;
  if( f_vsc_show_size  &&  size.x > size_client.x - f_vsc_show_size )
  {
    // hの補正
    f_hsc_show_size = m_sp_scroll_v->GetWindowSize().x;
  }
  // スクロールバーの表示判定
  if( f_hsc_show_size )
  {
    m_sp_scroll_h->SetMax( size.x );
    m_sp_scroll_h->SetPage( size_client.x - f_vsc_show_size );
    m_sp_scroll_h->Show( true );
//    ::SetWindowPos( m_sp_scroll_h->GetWindowHandle(), HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE );
//    form_rect.left = -m_sp_scroll_h->GetPos();
  }
  else
  {
    m_sp_scroll_h->Show( false );
//    form_rect.left = 0;
  }
  if( f_vsc_show_size ) // V判定
  {
    m_sp_scroll_v->SetMax( size.y );
    m_sp_scroll_v->SetPage( size_client.y - f_hsc_show_size );
    m_sp_scroll_v->Show( true );
//    ::SetWindowPos( m_sp_scroll_v->GetWindowHandle(), HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE );
//    form_rect.top = -m_sp_scroll_v->GetPos();
  }
  else
  {
    m_sp_scroll_v->Show( false );
//    form_rect.top = 0;
  }

  m_sp_scroll_h->SetWindowSize
    ( { int(size_client.x - f_vsc_show_size)
      , m_sp_scroll_h->GetWindowSize().y }
    );
  m_sp_scroll_v->SetWindowSize
    ( { m_sp_scroll_v->GetWindowSize().x
      , int(size_client.y - f_hsc_show_size) }
    );
  //
  form_rect.right  = size_client.x - f_vsc_show_size;
  form_rect.bottom = size_client.y - f_hsc_show_size;
  m_sp_form->SetWindowRect( form_rect );

}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 表示コンポーネントアイテムの設定
// 戻り値：
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// _____________________________________________________________________________
/// @brief 表示コンポーネントアイテムの設定
/// 
/// この関数は基本的には内部的に呼ばれます。
/// 
/// @param[in] io_ptr_item_node
///   : 設定するツリービューアイテムのポインタを指定します。
/// @param[in] in_left : 設定するｘ座標。
/// @param[in] in_top  : 設定するｙ座標。
/// @param[in] in_flag_view : true - 表示\n : false - 非表示
/// 
/// @return
/// .x == 横方向の最大値\n
/// .y == 次のコンポーネントのｙ座標
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int2
  TreeView::SetupViewComponentItem
    ( pio<TreeViewItem> io_ptr_item_node
    , _in<long        > in_left
    , _in<long        > in_top
    , _in<BOOL        > in_flag_view
    )
{
  const int indent_x = 20;
  int max_x = in_left;
  auto ptr_item_node = io_ptr_item_node;
  auto top = in_top;

  while( ptr_item_node )
  {
    auto ptr_comp = ptr_item_node->GetComponent();
    if( ptr_comp )
    {
      if( in_flag_view )
      {
  //      item_size = pNode->GetHandle()->GetClientSize();
        // クライアントサイズ
        auto item_size = ptr_comp->GetWindowSize();
        // コンポーネント座標を設定
        ptr_comp->SetWindowPos
          ( { static_cast<int>(in_left)
            , static_cast<int>(top) }
          );
        ptr_comp->Show( true );

        if( max_x < in_left + item_size.x )
        {
          max_x = in_left + item_size.x;
        }
        top += item_size.y + 1;
      }
      else
      {
        ptr_comp->Show( false );
      }

      // インデント設定
      if( ptr_item_node->GetNodeChild() ) // 子供があれば
      {
        short2 ret =
          SetupViewComponentItem
            ( ptr_item_node->GetNodeChild()
            , in_left + indent_x
            , top
            , (in_flag_view & ptr_item_node->GetExpand())
            );
        if( max_x < ret.x )
        {
          max_x = ret.x;
        }
        top = ret.y;
      }
      // 次の要素へ
      ptr_item_node = ptr_item_node->GetNodeNext();
    }
  }
  return int2( int(max_x), int(top) );
}



// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// リサイズ処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_Size( _in<eSizeMode> in_size_mode )
{
  int2 size_client = GetClientSize();
  if( m_sp_scroll_h )
  {
    ::SetWindowPos
      ( m_sp_scroll_h->GetWindowHandle()
      , HWND_TOP
      , 0
      , size_client.y - bar_size
      , size_client.x
      , bar_size
      , 0
      );
    m_sp_scroll_h->Update( false );
  }
  if( m_sp_scroll_v )
  {
    ::SetWindowPos
      ( m_sp_scroll_v->GetWindowHandle()
      , HWND_TOP
      , size_client.x - bar_size
      , 0
      , bar_size
      , size_client.y
      , 0
      );
    m_sp_scroll_v->Update( false );
  }
  SetupViewComponent();
  return false;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスダウン時の処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_MouseDown( _in<eMouseButton> in_mouse_btn )
{
  if( in_mouse_btn == eMouseButton::Left )
  {
    // 
    TreeViewItem* in_ptr_node = m_ptr_root;
    Component*    in_ptr_comp;
    Rect          rt;
    U32           f_select = false;
    int mx = WIN::Mouse::GetX();
    int my = WIN::Mouse::GetY();

    while( in_ptr_node )
    {
      in_ptr_comp = in_ptr_node->GetComponent();
      if( in_ptr_comp )
      {
        // アイテムマウスダウンチェック
        in_ptr_comp->GetWindowRect( rt );
        if( rt.Hit( mx, my ) )
        {
          On_ItemSelect( in_ptr_node );
          f_select = true;
          break;
        }
        else
        if( in_ptr_node->GetNodeChild() )
        {
          // 展開ボタンチェック
          rt.left   -= 15;
          rt.top    += 2;
          rt.right  = rt.left + 10;
          rt.bottom = rt.top + 10;
          if( rt.Hit( Mouse::GetX(), Mouse::GetY() ) )
          {
            in_ptr_node->SetExpand( !in_ptr_node->GetExpand() );
            Update( true );

            On_ItemExpand( in_ptr_node );
            break;
          }
        }
      }

      // 次のアイテムを検索
      if( in_ptr_node->GetNodeChild()  &&  in_ptr_node->GetExpand() )
      {
        in_ptr_node = in_ptr_node->GetNodeChild();
      }
      else
      if( in_ptr_node->GetNodeNext() )
      {
        in_ptr_node = in_ptr_node->GetNodeNext();
      }
      else
      {
        while( in_ptr_node )
        {
          if( in_ptr_node->GetNodeParent()  &&  in_ptr_node->GetNodeParent()->GetNodeNext() )
          {
            in_ptr_node = in_ptr_node->GetNodeParent()->GetNodeNext();
            break;
          }
          in_ptr_node = in_ptr_node->GetNodeParent();
        }
      }
    }
    if( !f_select ) // 選択項目がない場合
    {
      m_ptr_select = nullptr;
      On_ItemSelect( nullptr );
    }
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウスアップ時の処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_MouseUp( _in<eMouseButton> in_mouse_btn )
{
  if( in_mouse_btn == eMouseButton::Left )
  {
    Core::EventCatcher ec;

    if( m_ptr_moving_item )
    {
      // 
      //TreeViewItem* in_ptr_node = m_ptr_root;
      Component*    ptr_comp;
      Rect          rt;

      ptr_comp = m_ptr_moving_item->GetComponent();
      if( m_ptr_moving_item  &&  m_ptr_moving_item != m_ptr_select )
      {
        ptr_comp->GetWindowRect( rt );
        int flag;
        if( rt.top + edge_range > Mouse::GetY() )
        {
          flag = -1;
        }
        else
        if( rt.bottom - edge_range < Mouse::GetY() )
        {
          flag = 1;
        }
        else
        {
          flag = 0;
        }
        On_ItemDrop( m_ptr_moving_item, flag );
      }

      m_ptr_moving_item = nullptr;
    }
    SetupViewComponent();
    Update( true );
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウス移動処理
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_MouseMove()
{
  if( Mouse::GetDownL()  &&  m_ptr_moving_item )
  {
    TreeViewItem* ptr_node = m_ptr_root;
    Component*    ptr_comp;
    Rect          item_rt;
    Rect          clt_rt;
    GetClientRect( clt_rt );
    while( ptr_node )
    {
      ptr_comp = ptr_node->GetComponent();
      if( ptr_comp )
      {
        ptr_comp->GetWindowRect( item_rt );
        item_rt.left  = 0;
        item_rt.right = clt_rt.right;
        if( item_rt.Hit( Mouse::GetX(), Mouse::GetY() ) )
        {
          m_ptr_moving_item = ptr_node;
          break;
        }
      }
      // 次のアイテムを検索
      if( ptr_node->GetNodeChild()  &&  ptr_node->GetExpand() )
      {
        ptr_node = ptr_node->GetNodeChild();
      }
      else
      if( ptr_node->GetNodeNext() )
      {
        ptr_node = ptr_node->GetNodeNext();
      }
      else
      {
        while( ptr_node )
        {
          if( ptr_node->GetNodeParent()  &&  ptr_node->GetNodeParent()->GetNodeNext() )
          {
            ptr_node = ptr_node->GetNodeParent()->GetNodeNext();
            break;
          }
          ptr_node = ptr_node->GetNodeParent();
        }
      }
    }
    static TreeViewItem* s_ptr_dd_old = nullptr;
    if( s_ptr_dd_old != m_ptr_moving_item )
    {
      Update( true );
      s_ptr_dd_old = m_ptr_moving_item;
    }
    else
    if( m_ptr_moving_item )
    {
//      Update( false );
      if( m_ptr_moving_item->GetComponent() )
      {
        Rect rt;
        m_ptr_moving_item->GetComponent()->GetWindowRect( rt );
        rt.top--;
        rt.bottom++;
        rt.left = 0;
        rt.right = clt_rt.right;
        ::InvalidateRect( m_hdl_window, &rt, true );
      }
    }

    if( 5 > Mouse::GetY() )
    {
      m_sp_scroll_v->AddPos( Mouse::GetY()-5 );
      SetupViewComponent();
      Update();
      ::UpdateWindow( m_hdl_window );
      CPL_Sleep( td_time_msec(30) );
    }
    else
    if( clt_rt.bottom - 5 < Mouse::GetY() )
    {
      m_sp_scroll_v->AddPos( Mouse::GetY() - (clt_rt.bottom-5) );
      SetupViewComponent();
      Update( true );
      ::UpdateWindow( m_hdl_window );
      CPL_Sleep( td_time_msec(30) );
    }
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// マウススクロール
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_MouseWheel( _in<WORD> in_fw_keys, _in<short> in_z_delta )
{
  if( m_sp_scroll_v )
  {
    m_sp_scroll_v->AddPos( -in_z_delta / 10 );
    SetupViewComponent();
    Update( true );
    m_sp_scroll_v->Update( true );
  }
  return true;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// スクロールイベント
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_Scroll( pio<ScrollBar> in_ptr_scroll, _in<int> in_scroll_flag )
{
//  ScrollBar* in_ptr_scroll = dynamic_cast<ScrollBar*>(GetHandleToPointer(hScrollWnd));

  if( m_sp_scroll_h.get() == in_ptr_scroll  ||
      m_sp_scroll_v.get() == in_ptr_scroll )
  {
    SetupViewComponent();
//    Update( true );
    // 
    WIN::Rect rt;
    GetClientRect( rt );
    if( m_sp_scroll_h->IsVisible() ){ rt.bottom -= m_sp_scroll_h->GetWindowSize().y; }
    if( m_sp_scroll_v->IsVisible() ){ rt.left -= m_sp_scroll_v->GetWindowSize().x; }
    ::InvalidateRect( m_hdl_window, &rt, true );
    in_ptr_scroll->Update( true );
    return true;
  }
  return false;
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// アイテム選択
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SDEF TreeView::On_ItemSelect( TreeViewItem* in_ptr_item )
{
  m_ptr_select      = in_ptr_item;
  m_ptr_moving_item = in_ptr_item;
  Update( true );
  return false;
}




} // namespace WIN
} // namespace CPL

