/*
 * TreeViewItemMod.prg
 * Control TTreeViewItem moderno
 *
 * Copyright 2020 Ignacio Ortiz de Zúñiga
 * Copyright 2020 https://Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"

//------------------------------------------------------------------------------

CLASS XTreeViewItemMod FROM TComponent

PUBLISHED:
   PROPERTY cText      INIT ""  ;
                       READ METHOD GetValue ;
                       WRITE INLINE ::FcText := Value, ::Refresh()
   PROPERTY cTooltip   INIT ""
   PROPERTY nImage     INIT 0 ;
                       WRITE INLINE ::FnImage := Value, ::Refresh() ;
                       EDITOR PE_ImageIndex
   PROPERTY nSelImage  INIT 0 ;
                       WRITE INLINE ::FnSelImage := Value, ::Refresh() ;
                       EDITOR PE_ImageIndex

   PROPERTY nClrText   INIT clWindowText ;
                       WRITE INLINE ::FnClrText := Value, ::Refresh() ;
                       EDITOR PE_Color

   PROPERTY lChecked   INIT .F. ;
                       WRITE METHOD SetCheckState
   PROPERTY lExpanded  INIT .F. ;
                       WRITE INLINE ::SetExpandState( Value )

PUBLIC:
   PROPERTY lSelected  READ  INLINE ::oTreeView:ItemSelected( ::nWorkPos ) ;
                       WRITE METHOD SetSelectedState

   DATA oTreeView        READONLY AS TTreeViewMod
   DATA aItems   INIT {} READONLY AS TTreeviewItemMod
   DATA nIndex   INIT 0  READONLY // Ordinal position in its oParent:aItems array
   DATA nID      INIT 0  READONLY // Unique ID in the whole Treeview

   METHOD Create( oParent )    CONSTRUCTOR
   METHOD Free()               INLINE ( AEval( ::aItems, {|v| v:Free() } ) )

   METHOD AddItem( cItem, xImage, xSelImage, lChecked, nClrText ) EXTERN XTreeViewItemMod_InsertItem() // --> oItem
   METHOD InsertItem( cItem, xImage, xSelImage, lChecked, nClrText, nPos ) // --> oItem

   METHOD InsertInTree( oItem )
   METHOD Delete()
   METHOD HasChilds()          INLINE Len( ::aItems ) > 0
   METHOD FirstChild()         INLINE IIF( ::HasChilds(), ::aItems[ 1 ], NIL )
   METHOD LastChild()          INLINE IIF( ::HasChilds(), ATail( ::aItems ), NIL )
   METHOD DeleteChilds()       INLINE ::aItems := {}, ::lExpanded := .F., ::Rebuild()
   METHOD DeleteItems()        INLINE ::DeleteChilds()
   METHOD GetPrevSibling()
   METHOD GetNextSibling()
   METHOD MoveUp()
   METHOD MoveDown()
   METHOD MoveAfter( oItem )
   METHOD MoveInto( oItem )

   METHOD Expand( lPartial )     INLINE ::SetExpandState( .T., lPartial )
   METHOD Collapse( lPartial )   INLINE ::SetExpandState( .F., lPartial )

   METHOD Select( lFirstVisible )
   METHOD EnsureVisible( lFirstVisible )
   METHOD SortChildren( bSort, lRecursive )  // --> lSuccess


RESERVED:

   DATA nWorkPos INIT 0 // Position in the work array
   DATA nIndent  INIT 0 // Actual level of indentation

   METHOD Refresh( lValue )    INLINE ::oTreeView:Refresh( lValue )
   METHOD Rebuild()            INLINE ::oTreeView:NeedRebuild()
   METHOD GetValue( lHtml )
   METHOD SetExpandState( lExpand, lPartial )
   METHOD SetCheckState( lState )
   METHOD SetSelectedState( lState )
   METHOD GetRect()           INLINE ::aPaintRect
   METHOD SetRect( aRect )    INLINE ::aPaintRect := aRect

// For compatibility with TTreeView

   METHOD IsExpanded()        INLINE ::lExpanded

PROTECTED:
   DATA aPaintRect

   FRIEND CLASS XTreeViewMod

END CLASS

//------------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XTreeViewItemMod

   ::Super:Create( oParent )

   IF Empty( ::oTreeView )
      ::oTreeView := ::oParent
   ENDIF

   IF Empty( ::nID )
      ::nID := ::oTreeView:NewId()
   ENDIF

   ::Rebuild()

RETURN Self

//------------------------------------------------------------------------------

METHOD GetValue( lHtml ) CLASS XTreeViewItemMod

   LOCAL cText

   cText := ::FcText

   IF Empty( lHtml ) .AND. ::oTreeview:lHtmlText
      cText := XA_RemoveTags( cText )
   ENDIF

RETURN cText

//------------------------------------------------------------------------------

METHOD InsertItem( cItem, xImage, xSelImage, lChecked, nClrText, nPos ) CLASS XTreeViewItemMod

   LOCAL oItem

   IF ValType( xImage ) == "C"
      xImage := ::oTreeView:AddImage( xImage )
   ENDIF

   IF ValType( xSelImage ) == "C"
      xSelImage := ::oTreeView:AddImage( xSelImage )
   ENDIF

   DEFAULT xImage    TO 0, ;
           xSelImage TO xImage, ;
           lChecked  TO .F., ;
           nClrText  TO clWindowText

   WITH OBJECT oItem := TTreeViewItemMod():New( Self )
      :oTreeView  := ::oTreeView
      :FcText     := cItem
      :FnImage    := xImage
      :FnSelImage := xSelImage
      :FlChecked  := lChecked
      :FnClrText  := nClrText
      :Create()
   END

   ::InsertInTree( oItem, nPos )

RETURN oItem

//------------------------------------------------------------------------------

METHOD InsertInTree( oItem, nPos )  CLASS XTreeViewItemMod

   LOCAL aItems := ::aItems

   IF nPos == Nil .OR. nPos > Len( aItems )
      AAdd( aItems, oItem )
      oItem:nIndex := Len( aItems )
   ELSE
      HB_AIns( aItems, nPos, oItem, .T. )
      oItem:nIndex := nPos
      AEval( aItems, {|v,e| v:nIndex := e }, nPos  )
   ENDIF

   ::Rebuild()

RETURN NIL

//------------------------------------------------------------------------------

METHOD Delete() CLASS XTreeViewItemMod

   LOCAL aItems
   LOCAL nAt

   aItems := ::oParent:aItems

   HB_ADel( aItems, ::nIndex, .T. )
   AEval( aItems, {|v,e| v:nIndex := e }, ::nIndex  )

   ::aItems := {}
   ::nIndex := 0
   ::Rebuild()
   ::oTreeView := NIL
   ::oParent := NIL

RETURN .T.

//------------------------------------------------------------------------------

METHOD GetPrevSibling() CLASS XTreeViewItemMod

   LOCAL oItem

   IF ::nIndex > 0
      oItem := ::oParent:aItems[ ::nIndex - 1 ]
   ENDIF

RETURN oItem

//------------------------------------------------------------------------------

METHOD GetNextSibling() CLASS XTreeViewItemMod

   LOCAL oItem

   IF ::nIndex < Len( ::oParent:aItems )
      oItem := ::oParent:aItems[ ::nIndex + 1 ]
   ENDIF

RETURN oItem

//------------------------------------------------------------------------------

METHOD MoveUp() CLASS XTreeViewItemMod

   LOCAL oItem
   LOCAL nIndex

   oItem  := ::GetPrevSibling()
   nIndex := ::nIndex

   IF oItem != Nil
      oItem:nIndex := nIndex
      ::nIndex := nIndex - 1
      WITH OBJECT ::oParent
         :aItems[ nIndex ]     := oItem
         :aItems[ nIndex - 1 ] := Self
      END WITH
      ::Rebuild()
      RETURN .T.
   ENDIF

RETURN .F.

//------------------------------------------------------------------------------

METHOD MoveDown() CLASS XTreeViewItemMod

   LOCAL oItem
   LOCAL nIndex

   oItem  := ::GetNextSibling()
   nIndex := ::nIndex

   IF oItem != Nil
      oItem:nIndex := nIndex
      ::nIndex := nIndex + 1
      WITH OBJECT ::oParent
         :aItems[ nIndex ]     := oItem
         :aItems[ nIndex + 1 ] := Self
      END WITH
      ::Rebuild()
      RETURN .T.
   ENDIF

RETURN .F.

//------------------------------------------------------------------------------

METHOD MoveAfter( oItem ) CLASS XTreeViewItemMod

   IF oItem != Nil .AND. !Empty( oItem:GetNextSibling() )
      WITH OBJECT ::oParent
         HB_ADel( :aItems, ::nIndex, .T. )
         HB_AIns( :aItems, Self, oItem:nIndex + 1, .T. )
         AEval( :aItems, {|v,e| v:nIndex := e } )
      END OBJECT
      ::Rebuild()
      RETURN .T.
   ENDIF

RETURN .F.

//------------------------------------------------------------------------------

METHOD MoveInto( oItem ) CLASS XTreeViewItemMod

   IF oItem != Nil
      WITH OBJECT ::oParent
         HB_ADel( :aItems, ::nIndex, .T. )
         AEval( :aItems, {|v,e| v:nIndex := e }, ::nIndex )
         oItem:InsertInTree( Self )
      END OBJECT
      ::Rebuild()
      RETURN .T.
   ENDIF

RETURN .F.

//------------------------------------------------------------------------------

METHOD SetExpandState( lExpand, lPartial ) CLASS XTreeViewItemMod

   LOCAL lRet

   DEFAULT lPartial TO ::oTreeView:lSingleExpand

   IF !::HasChilds()
      RETURN .F.
   ENDIF

   lRet := ::oTreeView:OnItemExpanding( Self, lExpand )
   lRet := ( lRet == NIL .OR. lRet )

   IF lRet
      ::oTreeView:BeginPaint()

      IF ( lExpand != ::FlExpanded )
         ::Rebuild()
      ENDIF

      ::FlExpanded := lExpand

      IF !lPartial
         AEval( ::aItems, {|v| v:SetExpandState( lExpand, lPartial ) } )
      ENDIF

      ::oTreeView:EndPaint()
      ::oTreeView:OnItemExpanded( Self, lExpand )
   ENDIF

RETURN lRet

//------------------------------------------------------------------------------

METHOD SetCheckState( lState ) CLASS XTreeViewItemMod

   IF lState != ::FlChecked
      ::oTreeView:SwapCheckState( ::nWorkPos )
   ENDIF

RETURN ::FlChecked

//------------------------------------------------------------------------------

METHOD SetSelectedState( lState ) CLASS XTreeViewItemMod

   IF lState != ::lSelected
      ::oTreeView:SwapSelected( ::nWorkPos )
   ENDIF

RETURN lState

//------------------------------------------------------------------------------

METHOD Select( lFirstVisible ) CLASS XTreeViewItemMod

   LOCAL nWorkPos

   DEFAULT lFirstVisible TO .T.

   nWorkPos := ::oTreeView:GetItemPos( Self )

   IF !Empty( nWorkPos )
      ::oTreeView:nIndex := nWorkPos
      IF lFirstVisible
         ::oTreeView:SetFirstVisible()
      ENDIF
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD EnsureVisible( lFirstVisible ) CLASS XTreeViewItemMod

   LOCAL nWorkPos

   DEFAULT lFirstVisible TO .F.

   nWorkPos := ::oTreeView:GetItemPos( Self )

   IF !Empty( nWorkPos )
      IF lFirstVisible
         ::oTreeView:SetFirstVisible( nWorkPos )
      ELSE
         ::oTreeView:ForceItemVisible( nWorkPos )
      ENDIF
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD SortChildren( bSort, lRecursive ) CLASS XTreeViewItemMod

   LOCAL aItems

   DEFAULT lRecursive TO .F.

   ::oTreeView:BeginPaint()

   aItems := ::oParent:aItems

   ASort( aItems,,, bSort )

   AEval( aItems, {|v,e| v:nIndex := e,;
                        IIF( lRecursive, v:SortChildren( bSort, .T. ), ) } )

   ::Rebuild()
   ::oTreeView:EndPaint()

RETURN NIL

//------------------------------------------------------------------------------
