/*
 * Xailer source code:
 *
 * TabsItem.prg
 * Clase TabsItem
 *
 * Copyright 2003, 2015 Ignacio Ortiz de Zuņiga
 * Copyright 2003, 2009 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"

#define BMP_PADDING        2
#define TXT_PADDING        2

//------------------------------------------------------------------------------

CLASS XTabsItem FROM TComponent

PUBLISHED:
   PROPERTY cText         INIT ""            WRITE INLINE ( ::FcText := Value, ::UpdateTabs(.t.), Value ) ;
                                             READ  INLINE ::FcText
   PROPERTY cTooltip      INIT ""            WRITE INLINE ( ::FcTooltip := Value, ::oParent:SetTooltipZones(), Value )
   PROPERTY nImage        INIT 0             WRITE INLINE ( ::FnImage := Value, ::UpdateTabs(.t.), Value );
                                             EDITOR PE_ImageIndex
   PROPERTY nSize         INIT 0             WRITE INLINE ( ::FnSize := Value, ::UpdateTabs(.t.), Value );
                                             READ METHOD GetSize
   PROPERTY nAlignment    INIT taCENTER      WRITE INLINE ( ::FnAlignment := Value, ::UpdateTabs(), Value );
                                             VALUES taLEFT, taRIGHT, taCENTER
   PROPERTY nBmpAlignment INIT taLEFT        WRITE INLINE ( ::FnBmpAlignment := Value, ::UpdateTabs(), Value );
                                             VALUES taLEFT, taRIGHT
   PROPERTY lVisible      INIT .T.           WRITE INLINE ( ::FlVisible := Value, ::UpdateTabs(.t.), Value )
   PROPERTY lEnabled      INIT .T.
   PROPERTY nClrText      INIT Nil           EDITOR PE_ColorOrNil
   PROPERTY nClrTextHot   INIT Nil           EDITOR PE_ColorOrNil

   EVENT OnCreate( oSender ) // --> Nil
   EVENT OnClick( oSender, nX, nY ) // --> Nil
   EVENT OnRClick( oSender, nX, nY ) // --> Nil
   EVENT OnClose( oSender, nX, nY ) // --> lResult

PUBLIC:
   PROPERTY oParent READONLY AS TTabs
   PROPERTY nItem   READONLY INIT 0

   METHOD New( oParent ) CONSTRUCTOR
   METHOD Create( oParent, nItem, cText, nImage, cTooltip, nSize, nAlignment, nBmpAlignment, lVisible, lEnabled ) CONSTRUCTOR

   METHOD Select()   INLINE ::oParent:nIndex := ::nItem // --> Nil
   METHOD Selected() INLINE (::oParent:nIndex == ::nItem ) // --> lValue
   METHOD Delete()   INLINE ::oParent:DeleteItem( ::nItem ) // --> Nil

RESERVED:
   DATA nOrgPos    INIT 0
   DATA lCloseHot  INIT .F.
   DATA lCanShow   INIT .F.

   METHOD Paint( hDC, aPoint )
   METHOD PaintFrame( hDC, BYREF aRect, lSelect )
   METHOD CalcRect( BYREF nOffset, nSize, nMax ) // --> lVisible
   METHOD GetRect( lClient, aPoint )
   METHOD SetRect( aRect )   INLINE ::aPaintRect := aRect
   METHOD IsFirst()  INLINE ::oParent:TabIsFirst( ::nItem ) // --> lValue
   METHOD IsLast()   INLINE ::oParent:TabIsLast( ::nItem ) // --> lValue
   METHOD IsCursorOnClose( aPoint, aRect )
   METHOD IsVisible() INLINE ::lVisible // --> lValue (needed on the IDE)
   METHOD UpdateTabs( lFull ) INLINE ::oParent:Refresh( lFull )

PROTECTED:
   DATA aPaintRect INIT {0,0,0,0}

   METHOD GetSize()

ENDCLASS

//------------------------------------------------------------------------------

METHOD New( oParent ) CLASS XTabsItem

   ::oParent := oParent

RETURN ::Super:New( ::oParent )

//------------------------------------------------------------------------------

METHODPUB Create( oParent, nItem, cText, nImage, cTooltip, nSize, nAlignment, nBmpAlignment, lVisible, lEnabled ) CLASS XTabsItem

   UPDATE ::oParent        TO oParent
   UPDATE ::nItem          TO nItem
   UPDATE ::FcText         TO cText
   UPDATE ::FnImage        TO nImage
   UPDATE ::FcTooltip      TO cTooltip
   UPDATE ::FnSize         TO nSize
   UPDATE ::FnAlignment    TO nAlignment
   UPDATE ::FnBmpAlignment TO nBmpAlignment
   UPDATE ::FlVisible      TO lVisible
   UPDATE ::FlEnabled      TO lEnabled

   ::Super:Create( ::oParent )

   IF Empty( ::nItem ) .OR. ::nItem > Len( ::oParent:aItems )
      AAdd( ::oParent:aItems, Self )
      ::nItem := Len( ::oParent:aItems )
   ELSE
      AAdd( ::oParent:aItems, Nil )
      AIns( ::oParent:aItems, ::nItem )
      ::oParent:aItems[ ::nItem ] := Self
      nItem := ::nItem + 1
      WHILE nItem <= Len( ::oParent:aItems )
         ::oParent:aItems[ nItem ]:nItem := nItem++
      ENDDO
   ENDIF

   WITH OBJECT ::oParent
      IF ! Empty( :Handle )
         :CalcItemsRect()
         :SetShortCuts()
         :SetTooltipZones()
         //::Select()
      ENDIF
   END WITH

   ::nOrgPos := ::nItem

   ::OnCreate()

RETURN Self

//------------------------------------------------------------------------------

METHODPUB GetSize() CLASS XTabsItem

   LOCAL nSize

   IF Empty( ::FnSize )
      WITH OBJECT ::oParent
         IF :nOrientation == orBOTTOM .OR. :nOrientation == orTOP
            nSize := :GetFont():GetTextWidth( ::cText, ::oParent ) + 2
            IF ::nImage > 0
               nSize += :oImageList:nWidth + BMP_PADDING + BMP_PADDING
            ENDIF
            IF :lTabCloseButtons
               nSize += ( 8 + BMP_PADDING + BMP_PADDING ) // pad - 8 bmp - pad
            ENDIF
         ELSE
            nSize := :GetFont():GetTextHeight( ::cText, ::oParent ) + 2
            IF ::nImage > 0
               nSize := Max( :oImageList:nHeight + BMP_PADDING + BMP_PADDING, nSize )
            ENDIF
         ENDIF
         IF :nFrame > tfNONE
            nSize += 2
         ENDIF
         nSize += :nItemPadding
      END WITH
   ELSE
      nSize := ::FnSize
   ENDIF

RETURN nSize

//------------------------------------------------------------------------------

METHODPUB CalcRect( nOffset, nSize, nMax ) CLASS XTabsItem

   LOCAL aRect := Array( 4 )
   LOCAL lVisible

   DEFAULT nSize TO ::GetSize()

   WITH OBJECT ::oParent
      IF :nOrientation == orBOTTOM .OR. :nOrientation == orTOP
         aRect[ rtTOP ]    := :aPaintRect[ rtTOP ]
         aRect[ rtBOTTOM ] := :aPaintRect[ rtBOTTOM ]
         aRect[ rtLEFT ]   := nOffset
         aRect[ rtRIGHT ]  :=  nOffset + nSize
         //aRect[ rtRIGHT ]  := Min( nOffset + nSize, nMax )
         :OnCalcRect( ::nItem, @aRect )
         IF ( lVisible := ( aRect[ rtRIGHT ] <= nMax ) )
            nOffset := aRect[ rtRIGHT ]
         ENDIF
      ELSE
         aRect[ rtLEFT ]   := :aPaintRect[ rtLEFT ]
         aRect[ rtRIGHT ]  := :aPaintRect[ rtRIGHT ]
         aRect[ rtTOP ]    := nOffset
         aRect[ rtBOTTOM ] := nOffset + nSize
         //aRect[ rtBOTTOM ] := Min( nOffset + nSize, nMax )
         :OnCalcRect( ::nItem, @aRect )
         IF ( lVisible := ( aRect[ rtBOTTOM ] <= nMax ) )
            nOffset := aRect[ rtBOTTOM ]
         ENDIF
      ENDIF
   END WITH

   ::aPaintRect := aRect

RETURN lVisible

//------------------------------------------------------------------------------

METHODPUB GetRect( lClient, aPoint ) CLASS XTabsItem

   LOCAL aRect
   LOCAL nGap

   DEFAULT lClient TO .F.

   aRect := AClone( ::aPaintRect )

   IF lClient
      nGap  := ::oParent:nTabsSize
      SWITCH ::oParent:nOrientation
      case orTOP
         aRect[ rtTOP ]    -= nGap
         aRect[ rtBOTTOM ] -= nGap
         EXIT
      case orLEFT
         aRect[ rtLEFT ]  -= nGap
         aRect[ rtRIGHT ] -= nGap
         EXIT
      END SWITCH
   ELSEIF ::oParent:nFloat == ::nItem .AND. aPoint != NIL
      SWITCH ::oParent:nOrientation
      case orTOP
      CASE orBOTTOM
         nGap  := aPoint[ 1 ] - aRect[ rtLEFT ]
         aRect[ rtLEFT ]  += nGap
         aRect[ rtRIGHT ] += nGap
         EXIT
      case orLEFT
      case orRIGHT
         nGap  := aPoint[ 2 ] - aRect[ rtTOP ]
         aRect[ rtTOP ]    += nGap
         aRect[ rtBOTTOM ] += nGap
         EXIT
      END SWITCH
   ENDIF

RETURN aRect

//------------------------------------------------------------------------------

METHODPUB IsCursorOnClose( aPoint, aRect ) CLASS XTabsItem

   LOCAL nX, nY

   nX := aRect[ rtRIGHT ] - 8 - BMP_PADDING - BMP_PADDING
   nY := aRect[ rtTOP ] + BMP_PADDING + BMP_PADDING

   IF ::oParent:lTabCloseButtons
      RETURN PtInRect( {nX, nY, nX + 8, nY + 8 }, aPoint )
   ENDIF

RETURN .F.

//------------------------------------------------------------------------------

METHODPUB Paint( hDC, aPoint ) CLASS XTabsItem

   LOCAL aRect, aTmp, aColors
   LOCAL cText
   LOCAL nClrText, nClrPane, nClrCross, nImage, nOldColor, nOldObj, nX, nY, nState, nFlags
   LOCAL hPen, hOld
   LOCAL lResult, lDelBrh, lDelPen, lSelect

   IF !::IsVisible() .OR. !::lCanShow
      RETURN NIL
   ENDIF

   aRect    := ::GetRect( .f., aPoint )
   cText    := ::cText
   nImage   := ::nImage
   lSelect  := ::Selected() .AND. ::oParent:lShowActive

   WITH OBJECT ::oParent
      // To force printing zero based due the use of double-buffering
      IF :nOrientation == orBOTTOM
         aRect[ rtBOTTOM ] -= aRect[ rtTOP  ]
         aRect[ rtTOP    ] := 0
      ELSEIF :nOrientation == orRIGHT
         aRect[ rtRIGHT  ] -= aRect[ rtLEFT ]
         aRect[ rtLEFT   ] := 0
      ENDIF
      IF !lSelect
         IF :nFrame > tfNONE // Avoid deleting frame line on NON selected items
            SWITCH :nOrientation
            CASE orTOP
               aRect[ rtBOTTOM ] --
               EXIT
            CASE orBOTTOM
               aRect[ rtTOP ] ++
               EXIT
            CASE orLEFT
               aRect[ rtRIGHT ] --
               EXIT
            CASE orRIGHT
               aRect[ rtLEFT ] ++
               EXIT
            END SWITCH
         ENDIF
         nState   := IIF( ::lEnabled, IIF( :nHot == ::nItem, tsHOT, tsNORMAL ), tsDISABLED )
         IF ( nClrText := IIF( nState != tsHOT, ::nClrText, ::nClrTextHot ) ) == Nil
            nClrText := IIF( nState != tsHOT, :nTabsClrText, :nTabsClrTextHot )
         ENDIF
         nClrPane := IIF( nState != tsHOT, :nTabsClrPane, :nTabsClrPaneHot )
      ELSE
         IF ( nClrText := ::nClrText ) == Nil
            nClrText := :nTabsClrTextSel
         ENDIF
         nClrPane := :nTabsClrPaneSel
         nState   := tsSELECTED
      ENDIF
      lResult := :OnDrawTab( ::nItem, @cText, @nImage, @nClrText, @nClrPane, nState, hDC, aRect )
   END WITH

   IF !Empty( lResult )
      RETURN NIL
   ENDIF

   WITH OBJECT ::oParent
      IF nClrPane != IIF( lSelect, :nTabsClrPaneSel, IIF( nState == tsHOT, :nTabsClrPaneHot, :nTabsClrPane ) )
         nClrPane := CreateSolidBrush( nClrPane )
         lDelBrh  := .T.
      ELSE
         nClrPane := IIF( lSelect, :oTabsClrPaneSel:Handle, IIF( nState == tsHOT, :oTabsClrPaneHot:Handle, :oTabsClrPane:Handle ) )
         lDelBrh  := .F.
      ENDIF
      IF :nFrame > tfNONE
         ::PaintFrame( hDC, @aRect, lSelect )
      ENDIF
      FillRect( hDC, aRect, nClrPane )
      IF lSelect .AND. :lTabStop .AND. :nFloat == 0 .AND. GetFocus() == :Handle
         aTmp := AClone( aRect )
         InflateRect( aTmp, -2, -2 )
         DrawFocusRect( hDC, aTmp )
      ENDIF
      ::lCloseHot := .F.
      IF :lTabCloseButtons
         IF !::lEnabled
            nClrCross := clLightGray
         ELSE
            IF ::IsCursorOnClose( aPoint, aRect )
               ::lCloseHot := .T.
               nClrCross := clLightCoral
            ELSEIF lSelect
               nClrCross := clRed
            ELSE
               nClrCross := clWhite
            ENDIF
         ENDIF
         nX := aRect[ rtRIGHT ] - 8 - BMP_PADDING - BMP_PADDING
         nY := aRect[ rtTOP ] + BMP_PADDING + BMP_PADDING

         XA_PaintCross( hDC, {nX, nY, nX + 8, nY + 8 }, nClrCross, 255 )
         aRect[ rtRIGHT ] := nX - BMP_PADDING - BMP_PADDING
      ENDIF
   END WITH

   IF nImage > 0
      WITH OBJECT ::oParent:oImageList
         nY := aRect[ rtTOP ] + Int( ( aRect[ rtBOTTOM ] - aRect[ rtTOP ] - :nHeight ) / 2 )
         IF ::nBmpAlignment = taRIGHT
            nX := aRect[ rtRIGHT ] - :nWidth - BMP_PADDING - BMP_PADDING
            DrawImage( :Handle, nImage, hDC, nX, nY, !::lEnabled )
            aRect[ rtRIGHT ] := nX - BMP_PADDING
         ELSE
            nX := aRect[ rtLEFT ] + BMP_PADDING + BMP_PADDING
            DrawImage( :Handle, nImage, hDC, nX, nY, !::lEnabled )
            aRect[ rtLEFT ] := nX + :nWidth + BMP_PADDING + BMP_PADDING
         ENDIF
      END WITH
   ENDIF

   IF !Empty( cText )
      nFlags := nOr( DT_END_ELLIPSIS, DT_VCENTER, DT_SINGLELINE )
      SWITCH ::nAlignment
      case taCENTER
         nFlags := nOr( nFlags, DT_CENTER )
         EXIT
      CASE taLEFT
         nFlags := nOr( nFlags, DT_LEFT )
         aRect[ rtLEFT ] += TXT_PADDING
         EXIT
      CASE taRIGHT
         nFlags := nOr( nFlags, DT_RIGHT )
         aRect[ rtRIGHT ] -= TXT_PADDING
         EXIT
      END SWITCH

      SetbkMode( hDC, TRANSPARENT )
      IF ::lEnabled
         nOldColor := SetTextColor( hDC, nClrText )
         nOldObj   := SelectObject( hDC, ::oParent:oFont:Handle )
         DrawText( hDC, cText, aRect, nFlags )
         SetTextColor( hDC, nOldColor )
         SelectObject( hDC, nOldObj )
      ELSE
         nOldColor := SetTextColor( hDC, GetSysColor( COLOR_3DSHADOW ) )
         nOldObj   := SelectObject( hDC, ::oParent:oFont:Handle )
         //AEval( aRect, {|v,e| aRect[ e ]++ } )
         //DrawText( hDC, cText, aRect, nFlags )
         //AEval( aRect, {|v,e| aRect[ e ]-- } )
         //SetTextColor( hDC, GetSysColor( COLOR_3DSHADOW ) )
         DrawText( hDC, cText, aRect, nFlags )
         SetTextColor( hDC, nOldColor )
         SelectObject( hDC, nOldObj )
      ENDIF
   ENDIF

   IF lDelBrh
      DeleteObject( nClrPane )
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHODPUB PaintFrame( hDC, aRect, lSelect ) CLASS XTabsItem

   LOCAL nGap, nOld

   nGap := Int( ::oParent:nTabsSize * 0.1 )
   nOld := SelectObject( hDC, ::oParent:oTabsPen:Handle )

   SWITCH ::oParent:nOrientation
   CASE orTOP
      IF !lSelect .AND. !::oParent:lSameFrameSize
         aRect[ rtTOP ]  += nGap
         IF ::IsFirst()
            aRect[ rtLEFT ] += 3
         ENDIF
      ENDIF
      MoveTo( hDC, aRect[ rtLEFT ], aRect[ rtBOTTOM ] -1)
      LineTo( hDC, aRect[ rtLEFT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtBOTTOM ]  )
      aRect[ rtLEFT ]++; aRect[ rtTOP ]++
      EXIT
   CASE orBOTTOM
      IF !lSelect .AND. !::oParent:lSameFrameSize
         aRect[ rtBOTTOM ] -= nGap
         IF ::IsFirst()
            aRect[ rtLEFT ] += 3
         ENDIF
      ENDIF
      MoveTo( hDC, aRect[ rtLEFT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtLEFT ], aRect[ rtBOTTOM ] -1 )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtBOTTOM ] -1 )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtTOP ] )
      aRect[ rtLEFT ]++; aRect[ rtBOTTOM ]--
      EXIT
   CASE orLEFT
      IF !lSelect .AND. !::oParent:lSameFrameSize
         aRect[ rtLEFT ] += nGap
         IF ::IsFirst()
            aRect[ rtTOP ] += 3
         ENDIF
      ENDIF
      MoveTo( hDC, aRect[ rtRIGHT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtLEFT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtLEFT ], aRect[ rtBOTTOM ]  )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtBOTTOM ] )
      aRect[ rtLEFT ]++; aRect[ rtTOP ]++
      EXIT
   CASE orRIGHT
      aRect[ rtLEFT ]--
      IF !lSelect .AND. !::oParent:lSameFrameSize
         aRect[ rtRIGHT ] -= nGap
         IF ::IsFirst()
            aRect[ rtTOP ] += 3
         ENDIF
      ELSE
         aRect[ rtRIGHT ] --
      ENDIF
      MoveTo( hDC, aRect[ rtLEFT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtTOP ] )
      LineTo( hDC, aRect[ rtRIGHT ], aRect[ rtBOTTOM ]  )
      LineTo( hDC, aRect[ rtLEFT ], aRect[ rtBOTTOM ] )
      aRect[ rtLEFT ]++; aRect[ rtTOP ]++
      EXIT
   END SWITCH

   SelectObject( hDC, nOld )

RETURN NIL

//------------------------------------------------------------------------------

#pragma BEGINDUMP

#include <windows.h>
#include <commctrl.h>
#include <xailer.h>
#include <constants.ch>

//------------------------------------------------------------------------------

HB_FUNC_STATIC( DRAWIMAGE ) // Imagelist, nImage, hDC, nX, nY, lEnabled --> lValue
{
   if( !hb_parl( 6 ) )
      {
      hb_retl( ImageList_Draw( (HIMAGELIST) hb_parnl( 1 ) , hb_parnl( 2 ) - 1,
               (HDC) hb_parnl( 3 ), hb_parni( 4 ), hb_parni( 5 ), ILD_TRANSPARENT ) );
      }
   else
      {
      HICON hIcon = ImageList_GetIcon( (HIMAGELIST) hb_parnl( 1 ), hb_parnl( 2 ) - 1, ILD_TRANSPARENT );
      hb_retl( DrawState( (HDC) hb_parnl( 3 ), NULL, NULL, (long) hIcon, 0, hb_parni( 4 ), hb_parni( 5 ), 0, 0, DST_ICON | DSS_DISABLED ) );
      DestroyIcon( hIcon );
      }
}

//--------------------------------------------------------------------------

#pragma ENDDUMP



