/*
 * Xailer source code:
 *
 * Label.prg
 * Clase TLabelEx()
 *
 * Copyright 2012 Jose F. Gimenez
 * Copyright 2020 Ignacio Ortiz de Zúñiga (soporte de alineaciones)
 * Copyright 2012 Xailer.com
 * All rights reserved
 *
 */


#include "Xailer.ch"

CLASS XLabelEx FROM TScrollingWinControl

PUBLISHED:
   PROPERTY cText          EDITOR PE_LabelEx WRITE METHOD SetText
   PROPERTY nWidth         INIT 120
   PROPERTY nHeight        INIT 40
   PROPERTY lTransparent   INIT .T.
   PROPERTY nOpacity       INIT 0
   PROPERTY nClrLink       INIT clBlue EDITOR PE_Color
   PROPERTY nBorderStyle   INIT bvNONE
   PROPERTY nBorderSides
   PROPERTY nLineSpacing   INIT 100
   PROPERTY nMargins       INIT 0
   PROPERTY lTabStop       INIT .F.
   PROPERTY nAlignment     INIT taLEFT   WRITE INLINE ( ::FnAlignment := Value, ::CheckAlignment() )  VALUES taLEFT, taRIGHT, taCENTER
   PROPERTY nVAlignment    INIT vaTOP    WRITE INLINE ( ::FnVAlignment := Value, ::CheckAlignment() ) VALUES vaTOP, vaBOTTOM, vaCENTER
   PROPERTY lMultiLine     INIT .T.      WRITE INLINE ( ::FlMultiLine := Value, ::CheckAlignment() )

   EVENT OnLinkClick( oSender, cText, nLink )

   PROPERTY nScrollIncrement  INIT 8
   PROPERTY lAutoSize         INIT .f. WRITE METHOD SetAutoSize
   PROPERTY lHideScrollBars   INIT .F. // Must be set before Create().

   EVENT OnScrollUp( oSender, nPos ) // --> Nil
   EVENT OnScrollDown( oSender, nPos ) // --> Nil
   EVENT OnScrollPageUp( oSender, nPos ) // --> Nil
   EVENT OnScrollPageDown( oSender, nPos ) // --> Nil
   EVENT OnThumbTrackV( oSender, nPos ) // --> Nil
   EVENT OnThumbPosV( oSender, nPos ) // --> Nil

PUBLIC:
   PROPERTY nClientTop     INIT 0 WRITE METHOD SetScroll
   PROPERTY nVirtualHeight INIT 0 WRITE INLINE ::SetScroll( , Value )

   METHOD Create( oParent ) CONSTRUCTOR

PROTECTED:
   PROPERTY lBorder  INIT .f.
   DATA nClassStyle  INIT nOr( CS_HREDRAW, CS_VREDRAW )

   METHOD CheckAlignment()

RESERVED:
   METHOD WMLButtonDown( nWParam, nLParam )
   METHOD WMSetCursor( nWParam, nLParam )
   METHOD WMMouseWheel( nWParam, nLParam )
   METHOD WMPaint()

   METHOD WMEraseBkgnd()      INLINE 1
   METHOD SetScroll( nClientTop, nVirtualHeight )
   METHOD WMVScroll()         EXTERN XScrollingWinControl_WMVScroll()
   METHOD WMWindowPosChanged()
   METHOD SetAutoSize( lAutoSize )
   METHOD GetHeight()
   METHOD SetText( Value )

ENDCLASS

//--------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XLabelEx

   ::Super:Create( oParent )
   ::SetAutoSize()

RETURN Self

//--------------------------------------------------------------------------

METHOD SetText( Value ) CLASS XLabelEx

   ::Super:SetText( Value )
   ::SetAutoSize()

RETURN ::FcText

//--------------------------------------------------------------------------

METHOD SetAutoSize( lAutoSize ) CLASS XLabelEx

   LOCAL nHeight

   UPDATE ::FlAutoSize TO lAutoSize

   IF !Empty( ::Handle ) .AND. ::lMultiLine .AND. ( nHeight := ::GetHeight() ) > 0
      IF ::FlAutoSize
         ::nHeight := nHeight
         ::oParent:AlignControls()
      ELSE
         ::nVirtualHeight := nHeight
      ENDIF
      ::Refresh()
   ENDIF

RETURN ::FlAutoSize

//--------------------------------------------------------------------------

METHOD SetScroll( nClientTop, nVirtualHeight ) CLASS XLabelEx

   UPDATE ::FnClientTop TO nClientTop
   UPDATE ::FnVirtualHeight TO nVirtualHeight

   IF ! Empty( ::Handle )
      IF ::FnClientTop + ::FnVirtualHeight < ::nClientHeight
         ::FnClientTop := Min( ::nClientHeight - ::FnVirtualHeight, 0 )
      ENDIF
      IF !::IsHideScrollBars()
         SetScrollInfo( ::Handle, SB_VERT, 0, ::FnVirtualHeight - 1, ::nClientHeight, -::FnClientTop, .F., .T. )
      ENDIF
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD CheckAlignment() CLASS XLabelEx

   IF ::FnAlignment != taLEFT .OR. ::FnVAlignment != vaTOP
      ::FlMultiLine := .F.
   ENDIF

   ::Refresh()

RETURN NIL

//------------------------------------------------------------------------------

METHOD WMMouseWheel( nWParam, nLParam ) CLASS XLabelEx

   IF !lGetKeyState( VK_SHIFT ) .AND. !lGetKeyState( VK_CONTROL )
      ::nClientTop := Min( 0, ::nClientTop + ::nScrollIncrement * IIF( HiInt( nWParam ) > 0, 1, -1 ) )
      ::Refresh()
      RETURN 0
   ENDIF

RETURN ::Super:WMMouseWheel( nWParam, nLParam )

//--------------------------------------------------------------------------

METHOD WMWindowPosChanged() CLASS XLabelEx

   ::UpdateBounds()
   ::SetAutoSize()

RETURN Nil

//------------------------------------------------------------------------------

#pragma BEGINDUMP

#include "Windows.h"
#include "Xailer.h"
#include "Constants.ch"

//------------------------------------------------------------------------------

static void GetPaintRect( HDC hDC, PHB_ITEM Self, PRECT rect, char * cText, UINT nTextLen )
{
   int nVAlign, nAlign, nSpacing, nHeight, nWidth;
   BOOL lMultiLine;

   lMultiLine = XA_ObjGetL( Self, "lMultiLine" );
   nVAlign = XA_ObjGetNL( Self, "nVAlignment" );
   nAlign = XA_ObjGetNL( Self, "nAlignment" );
   nSpacing = XA_ObjGetNL( Self, "nLineSpacing" );

   if ( lMultiLine )
      {
      if ( nVAlign != vaTOP )
         {
         nHeight = XA_DrawTextEx( hDC, cText, nTextLen, rect, DT_CALCRECT | nSpacing, 0, NULL, NULL, NULL );
         if ( nVAlign == vaCENTER )
            rect->top = ( rect->bottom - rect->top - nHeight ) >>1;
         else
            rect->top = rect->bottom - nHeight;
         }
      }
   else
      {
      nHeight = XA_DrawTextEx( hDC, cText, nTextLen, rect, DT_CALCRECT | -1, 0, NULL, NULL, &nWidth );
      switch ( nAlign )
         {
         case taLEFT: break;
         case taCENTER: rect->left += ( rect->right - rect->left - nWidth ) >> 1; break;
         case taRIGHT: rect->left = rect->right - nWidth; break;
         }
      switch ( nVAlign )
         {
         case vaTOP: break;
         case vaCENTER: rect->top += ( rect->bottom - rect->top - nHeight ) >>1; break;
         case vaBOTTOM: rect->top = rect->bottom - nHeight; break;
         }
      }
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLABELEX_WMLBUTTONDOWN )
{
   PHB_ITEM Self = hb_stackSelfItem(), tControl;
   HWND hWnd = GetHandleOf( Self );
   int wParam = hb_parnl( 1 ), lParam = hb_parnl( 2 );
   int bRet = FALSE;
   HDC hdc = CreateCompatibleDC( NULL );
   POINT pt;
   UINT nTextLen = 0;
   char *cText = XA_ObjGetCLen( Self, "cText", &nTextLen );
   int nLineSpacing = XA_ObjGetNL( Self, "nLineSpacing" ) & 1023;
   DT_LINK *aLink;
   int nCount = 0, n;
   RECT rect;

   if( XA_ObjGetL( Self, "lTabStop" ) )
      SetFocus( hWnd );

   GetClientRect( hWnd, &rect );
   XA_DrawTextEx( hdc, cText, nTextLen, &rect, DT_CALCRECT | DT_NOPREFIX | nLineSpacing, 0, NULL, &nCount, NULL );

   if( nCount )
      {
      pt.x = LOWORD( lParam );
      pt.y = HIWORD( lParam );
      SelectObject( hdc, GetHandleOf( XA_ObjGetItem( Self, "GetFont" ) ) );
      GetPaintRect( hdc, Self, &rect, cText, nTextLen );
      aLink = hb_xgrab( sizeof( DT_LINK ) * nCount );
      XA_DrawTextEx( hdc, cText, nTextLen, &rect, DT_CALCRECT | DT_NOPREFIX | nLineSpacing, 0, aLink, &nCount, NULL );

      if ( XA_ObjGetL( Self, "lMultiLine" ) && ( XA_ObjGetNL( Self, "nVAlignment" ) == vaTOP ) )
         pt.y -= XA_ObjGetNL( Self, "nClientTop" );
      for( n = 0; n < nCount; n++ )
         {
         if( PtInRect( &aLink[ n ].rect, pt ) )
            {
            PHB_DYNS pMethod = hb_dynsymFindName( "OnLinkClick" );

            if( pMethod )
               {
               cText = hb_xgrab( aLink[ n ].nTextLen + 1 );
               strncpy( cText, aLink[ n ].cText, aLink[ n ].nTextLen );
               cText[ aLink[ n ].nTextLen ] = 0;
               hb_vmPushDynSym( pMethod );
               hb_vmPush( Self );
               hb_vmPushString( cText, aLink[ n ].nTextLen );
               hb_vmPushInteger( n + 1 );
               hb_vmSend( 2 );
               hb_xfree( cText );
               }
            bRet = TRUE;
            break;
            }
         }

      hb_xfree( aLink );
      }

   DeleteDC( hdc );

   if( !bRet || XA_ObjGetL( Self, "lHideScrollBars" ) )
      {
      tControl = XA_ObjGetItemCopy( Self, "TScrollingWinControl" );
      XA_ObjSendNL2( tControl, "WMLButtonDown", wParam, lParam );
      hb_itemRelease( tControl );
      if( !bRet )
         XA_ObjSendNL2( Self, "OnClick", pt.x, pt.y );
      }
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLABELEX_WMSETCURSOR )
{
   PHB_ITEM Self = hb_stackSelfItem(), tControl;
   HWND hWnd = GetHandleOf( Self );
   int wParam = hb_parnl( 1 ), lParam = hb_parnl( 2 );
   int bRet = FALSE;
   int nLineSpacing = XA_ObjGetNL( Self, "nLineSpacing" ) & 1023;

   if( (HWND) wParam == hWnd )
      {
      if( XA_ObjGetL( Application(), "lBusy" ) )
         {
         CursorWait();
         bRet = TRUE;
         }
      else if( LOWORD( lParam ) == HTCLIENT )
         {
         HDC hdc = CreateCompatibleDC( NULL );
         POINT pt;
         UINT nTextLen = 0;
         char *cText = XA_ObjGetCLen( Self, "cText", &nTextLen );
         DT_LINK *aLink;
         int nCount = 0, n;
         RECT rect;

         GetClientRect( hWnd, &rect );
         XA_DrawTextEx( hdc, cText, nTextLen, &rect, DT_CALCRECT | nLineSpacing, 0, NULL, &nCount, NULL );
         if( nCount )
            {
            SelectObject( hdc, GetHandleOf( XA_ObjGetItem( Self, "GetFont" ) ) );
            GetPaintRect( hdc, Self, &rect, cText, nTextLen );
            cText = XA_ObjGetCLen( Self, "cText", &nTextLen );
            aLink = hb_xgrab( sizeof( DT_LINK ) * nCount );
            XA_DrawTextEx( hdc, cText, nTextLen, &rect, DT_CALCRECT | nLineSpacing, 0, aLink, &nCount, NULL );
            GetCursorPos( &pt );
            ScreenToClient( hWnd, &pt );

            if ( XA_ObjGetL( Self, "lMultiLine" ) && ( XA_ObjGetNL( Self, "nVAlignment" ) == vaTOP ) )
               pt.y -= XA_ObjGetNL( Self, "nClientTop" );
            for( n = 0; n < nCount; n++ )
               {
               if( PtInRect( &aLink[ n ].rect, pt ) )
                  {
                  CursorHand();
                  bRet = TRUE;
                  break;
                  }
               }

            hb_xfree( aLink );
            }
         DeleteDC( hdc );
         }

      if( !bRet )
         {
         tControl = XA_ObjGetItemCopy( Self, "TScrollingWinControl" );
         XA_ObjSendNL2( tControl, "WMSetCursor", wParam, lParam );
         hb_itemRelease( tControl );
         }
      }
   else
      hb_ret();
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLABELEX_WMPAINT )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd = GetHandleOf( Self );
   CONTROLPAINT cp;
   RECT rect;
   char * cText;
   int nSpacing, nClrLink, nVAlign, nMargins;
   BOOL lMultiLine;
   UINT nTextLen;

   if ( XA_ControlBeginPaint( Self, hWnd, &cp, (HDC) hb_parnl( 1 ) ) )
      {
      GetClientRect( hWnd, &rect );
      XA_DrawControlBackground( Self, cp.hDC, &rect );

      lMultiLine = XA_ObjGetL( Self, "lMultiLine" );
      nSpacing = lMultiLine ? XA_ObjGetNL( Self, "nLineSpacing" ) : -1;
      nMargins = XA_ObjGetNL( Self, "nMargins" );
      nClrLink = XA_ObjGetNL( Self, "nClrLink" );
      nVAlign = XA_ObjGetNL( Self, "nVAlignment" );
      cText = XA_ObjGetCLen( Self, "cText", &nTextLen );

      SelectObject( cp.hDC, GetHandleOf( XA_ObjGetItem( Self, "GetFont" ) ) );
      SetTextColor( cp.hDC, XA_ObjGetNL( Self, "nClrText" ) );
      SetBkMode( cp.hDC, TRANSPARENT );

      // IOZ: Es importante llamarlo después de establecer el font
      GetPaintRect( cp.hDC, Self, &rect, cText, nTextLen );
      if( nMargins )
         InflateRect( &rect, -nMargins, -nMargins );

      if ( lMultiLine && ( nVAlign == vaTOP ) )
         rect.top = XA_ObjGetNL( Self, "nClientTop" ) + nMargins;

      XA_DrawTextEx( cp.hDC, cText, nTextLen, &rect, DT_NOPREFIX | (nSpacing & 1023), nClrLink, NULL, NULL, NULL );

      if ( XA_ObjGetL( Self, "IsHideScrollBars" ) )
         XA_ObjSendNL( Self, "PaintScrollBars", (long) cp.hDC );

      }
   XA_ControlEndPaint( Self, hWnd, &cp );

   hb_retnl( 0 );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLABELEX_GETHEIGHT )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd = GetHandleOf( Self );
   HDC hdc = CreateCompatibleDC( NULL );
   UINT nTextLen = 0;
   int nLineSpacing = XA_ObjGetNL( Self, "nLineSpacing" ) & 1023;
   int nMargins = XA_ObjGetNL( Self, "nMargins" );
   char *cText = XA_ObjGetCLen( Self, "cText", &nTextLen );
   RECT rect;
   HFONT hFont = (HFONT) GetHandleOf( XA_ObjGetItem( Self, "GetFont" ) );
   HFONT hOldFont = SelectObject( hdc, hFont );

   GetClientRect( hWnd, &rect );
   if( nMargins )
      InflateRect( &rect, -nMargins, -nMargins );

   hb_retnl( XA_DrawTextEx( hdc, cText, nTextLen, &rect, DT_CALCRECT | nLineSpacing, 0, NULL, NULL, NULL ) + nMargins );
   SelectObject( hdc, hOldFont );
   DeleteDC( hdc );
}

//------------------------------------------------------------------------------

#pragma ENDDUMP
