/*
 * BrwColMod.prg
 * Control Columnas del TBrowse moderno
 *
 * Copyright 2020 Ignacio Ortiz de Zúñiga
 * Copyright 2020 https://Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"

#xtranslate PROPERTY <Property> ;
               INIT <_Init> ;
               [ _VALUES_ <_Values> ] ;
               [ EDITOR <_Editor> ] ;
               AUTO => ;
            PROPERTY < Property > ;
               INIT <_Init> ;
               [ _VALUES_ <_Values> ] ;
               [ EDITOR <_Editor> ] ;
               WRITE INLINE ::SetHighValue( < 'Property' >, Value ) ;
               READ INLINE  ::GetHighValue( < 'Property' > )

//------------------------------------------------------------------------------

CLASS XBrwColMod FROM TComponent

   ACCESS oBrowse   IS oParent
   ACCESS oHeader   IS oParent:oHeader
   ACCESS oFooter   IS oParent:oFooter
   ACCESS oSupra    IS oParent:oSupra
   ACCESS oDataGrid IS oParent:oDataGrid
   ACCESS aGridData IS oParent:aGridData

   //DELEGATE IsVisible   TO oHeaderCol
   DELEGATE RightPos    TO oHeaderCol
   DELEGATE LeftPos     TO oHeaderCol
   DELEGATE nPos        TO oHeaderCol
   DELEGATE oEditFilter TO oHeaderCol

   FRIEND CLASS XBrowseMod

PUBLISHED:
   PROPERTY FilterEval        INIT "" EDITOR PE_ExtendedString
   PROPERTY cHeader           INIT "" WRITE METHOD SetHeader
   PROPERTY cFooter           INIT "" WRITE METHOD SetFooter
   PROPERTY cPicture          INIT ""
   PROPERTY cFilter           INIT ""  WRITE METHOD SetFilter ;
                                       READ  METHOD GetFilter
   PROPERTY nImage            INIT 0   AUTO  EDITOR PE_ImageIndex
   PROPERTY nWidth            INIT 100 WRITE METHOD SetWidth
   PROPERTY nVAlignment       INIT vaCENTER   AUTO ;
         VALUES vaTOP, vaCENTER, vaBOTTOM
   PROPERTY nAlignment        INIT taCENTER ;
         VALUES taLEFT, taRIGHT, taCENTER ;
         WRITE METHOD SetAlignment
   PROPERTY nBmpAlignment     INIT taLEFT     AUTO ;
         VALUES taLEFT, taRIGHT
   PROPERTY nFilterEditStyle  INIT esSEARCH AUTO ;
         VALUES esNONE, esSEARCH, esCLEAR, esVERTDOTS, ;
         esHORZDOTS, esCOMBO, esARROW

   PROPERTY nDroppedHeight    INIT 300
   PROPERTY nDroppedWidth     INIT 0
   PROPERTY cTooltip          INIT ""  AUTO EDITOR PE_ExtendedString
   PROPERTY lMultiline        INIT .F. AUTO
   PROPERTY lCanDrag          INIT .T. AUTO
   PROPERTY lCanResize        INIT .T. AUTO
   PROPERTY lCanFilter        INIT .T. AUTO
   PROPERTY lCanSort          INIT .T. AUTO
   PROPERTY lCanHot           INIT .T. AUTO
   PROPERTY lCanHide          INIT .T. AUTO
   PROPERTY lVisible          INIT .T. WRITE METHOD SetVisible
   PROPERTY lTransparent      INIT .T.
   PROPERTY nClrPane          INIT NIL EDITOR PE_ColorOrNil ;

   PROPERTY nDataCol          INIT 0 ;
         WRITE INLINE ( ::FnDataCol := Value, Value )
   PROPERTY nDataAlignment    INIT taLEFT ;
         VALUES taLEFT, taRIGHT, taCENTER ;
         WRITE METHOD SetDataAlignment
   PROPERTY nDataVAlignment   INIT vaCENTER ;
         VALUES vaTOP, vaCENTER, vaBOTTOM ;
         WRITE INLINE ( ::FnDataVAlignment := Value, ::Refresh(), Value )
   PROPERTY nDataBmpAlignment INIT taLEFT ;
         VALUES taLEFT, taRIGHT, taCENTER  ;
         WRITE INLINE ( ::FnDataBmpAlignment := Value, ::Refresh(), Value )
   PROPERTY lDataMultiline    INIT .F. ;
         WRITE INLINE ( ::FlDataMultiline := Value, ::Refresh(), Value )

   PROPERTY aEditListText  INIT {} EDITOR PE_StringList
   PROPERTY aEditListBound INIT {} EDITOR PE_MultiList
   PROPERTY lCanEdit       INIT .T.
   PROPERTY nEditType      INIT beGET ;
                           VALUES beNONE, beGET, beGET_BUTTON, beGET_LISTBOX,;
                                  beBUTTON, beLISTBOX, beGET_CALENDAR,;
                                  beCALENDAR, beGET_MEMO, beMEMO

   PROPERTY nEditLength    INIT -1
   PROPERTY uDefaultValue  EDITOR PE_EDIT

PUBLIC:
   PROPERTY oGridEdit  AS TEditForGrid
   PROPERTY oHeaderCol AS THeaderItemMod
   PROPERTY oFooterCol AS THeaderItemMod
   PROPERTY oSupraCol  AS THeaderItemMod
   PROPERTY nIndex   INIT 0 READONLY

   METHOD Value( nRecord )
   METHOD SetFilter( Value, lRun )

RESERVED:
   DATA lDataImageUsed INIT .f.

   METHOD Create( oParent )   CONSTRUCTOR
   METHOD Free()
   METHOD CreateEdit()

   METHOD UpdateChilds()
   METHOD UpdateWidth() INLINE IIF( ::oHeaderCol != NIL,;
                                    ::FnWidth := ::oHeaderCol:nWidth, )
   METHOD Insert( nPos )
   METHOD Refresh( lValue )

   METHOD SetEditProperties()
   METHOD SetEditCoors()
   METHOD SetVisible( Value, lUpdHeader )
   METHOD IsVisible()         INLINE ::FlVisible
   METHOD DefaultValue( BYREF cType )
   METHOD cValue( lList )
   METHOD Edit()
   METHOD EditValue( lChanged )
   METHOD CancelEdit()

   METHOD Sort( nSort ) INLINE ::oBrowse:Sort( Self, nSort )
   METHOD SetWidth( Value, lUpdHeaderItem )
   METHOD IsLastOnEdit()
   METHOD SetAlignment( Value )
   METHOD SetDataAlignment( Value )

PROTECTED:
   METHOD SetHighValue( cProperty, Value )
   METHOD SetLowValue( cProperty, Value )
   METHOD GetHighValue( cProperty )
   METHOD GetLowValue( cProperty )
   METHOD SetHeader( Value )
   METHOD SetFooter( Value )
   METHOD GetFilter( Value )

END CLASS

//------------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XBrwColMod

   ::Super:Create( oParent )

   IF ::oBrowse != Nil
      ::oBrowse:AddCol( Self )
      IF ::oDataGrid != NIL
         ::CreateEdit()
      ENDIF
   ENDIF

RETURN Self

//------------------------------------------------------------------------------

METHOD Free() CLASS XBrwColMod

   IF ::oGridEdit != NIL
      ::oGridEdit:End()
      ::oGridEdit := NIL
   ENDIF

   IF ::oHeaderCol != nil
      ::oHeaderCol:End()
      ::oHeaderCol := NIL
   ENDIF

   IF ::oFooterCol != nil
      ::oFooterCol:End()
      ::oFooterCol := NIL
   ENDIF

   IF ::oSupraCol != nil
      ::oSupraCol:End()
      ::oSupraCol := NIL
   ENDIF

RETURN ::Super:Free()

//------------------------------------------------------------------------------

METHOD CreateEdit() CLASS XBrwColMod

   IF ::oGridEdit == NIL .AND. !Empty( ::oDataGrid )
      WITH OBJECT ::oGridEdit := TEditForGrid():New( ::oDataGrid )
         :oCol     := Self
         :lVisible := .F.
         :Create()
      END WITH
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD Insert( nPos ) CLASS XBrwColMod

   ::oHeaderCol := ::oHeader:InsertItem( nPos, ::cHeader )
   ::oFooterCol := ::oFooter:InsertItem( nPos, ::cFooter )

   ::oHeaderCol:oGridCol := Self
   ::oFooterCol:oGridCol := Self

   ::oHeaderCol:nFilterEditStyle := ::nFilterEditStyle

   ::UpdateChilds()
   ::oBrowse:HeaderChanged()

RETURN NIL

//------------------------------------------------------------------------------

METHOD UpdateChilds() CLASS XBrwColMod

   LOCAL aMember
   LOCAL Value, cProp

   IF ::oHeaderCol == NIL .OR. ::oFooterCol == NIL
      RETURN NIL
   ENDIF

   ::oHeader:BeginPaint()
   ::oFooter:BeginPaint()

   FOR EACH aMember IN ::aProperties
      cProp := aMember[ 1 ]

      IF !Empty( aMember[ 7 ] ) // tiene un WRITE METHOD
         cProp := aMember[ 1 ]
         IF cProp == "cHeader"
            LOOP
         ENDIF
         IF __objHasData( ::oHeaderCol, cProp )
            Value := __objSendMsg( Self, cProp )
            cProp := "_" + cProp
            __objSendMsg( ::oHeaderCol, cProp, Value )
            IF cProp != "_nAlignment"
               __objSendMsg( ::oFooterCol, cProp, Value )
            ELSE
               __objSendMsg( ::oFooterCol, cProp, ::nDataAlignment )
            ENDIF
         ENDIF
      ENDIF
   NEXT

   ::oHeader:EndPaint()
   ::oFooter:EndPaint()

RETURN NIL

//------------------------------------------------------------------------------

METHOD SetHighValue( cProperty, Value ) CLASS XBrwColMod

   IF ::SetLowValue( "_F" + cProperty, Value )
      IF ::oHeaderCol != NIL
         __objSendMsg( ::oHeaderCol, "_" + cProperty, Value )
      ENDIF

      IF ::oFooterCol != NIL
         __objSendMsg( ::oFooterCol, "_" + cProperty, Value )
      ENDIF
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD GetHighValue( cProperty ) CLASS XBrwColMod

RETURN ::GetLowValue( "F" + cProperty )

//------------------------------------------------------------------------------

METHOD SetHeader( Value ) CLASS XBrwColMod

   ::FcHeader := Value

   IF ::oHeaderCol != NIL
      ::oHeaderCol:cHeader := Value
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD SetFooter( Value ) CLASS XBrwColMod

   ::FcFooter := Value

   IF ::oFooterCol != NIL
      ::oFooterCol:cHeader := Value
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD SetVisible( Value, lUpdHeader ) CLASS XBrwColMod

   DEFAULT lUpdHeader TO .T.

   ::FlVisible := Value

   IF lUpdHeader
      IF ::oHeaderCol != NIL
         ::oHeaderCol:lVisible := Value
      ENDIF

      IF ::oFooterCol != NIL
         ::oFooterCol:lVisible := Value
      ENDIF
      ::oBrowse:Refresh()
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD SetWidth( Value, lUpdHeaderItem ) CLASS XBrwColMod

   DEFAULT lUpdHeaderItem TO .T.

   ::FnWidth := Value

   IF lUpdHeaderItem .AND. ::oHeaderCol != NIL
      ::oHeaderCol:nWidth := Value
      ::oHeader:ItemChanged( ::oHeaderCol )
   ENDIF

   IF ::oFooterCol != NIL
      ::oFooterCol:nWidth := Value
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD SetFilter( Value, lRun ) CLASS XBrwColMod

   DEFAULT lRun TO .T.

   IF ::oHeaderCol != NIL
      ::oHeaderCol:oEditFilter:Value := Value
      IF lRun
         ::oBrowse:Filter( Self )
      ENDIF
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD GetFilter() CLASS XBrwColMod

   LOCAL Value

   IF ::oHeaderCol != NIL .AND. ::oHeaderCol:oEditFilter != NIL
      Value := ::oHeaderCol:oEditFilter:Value
   ENDIF

RETURN Value
//------------------------------------------------------------------------------

METHOD Value( nRecord ) CLASS XBrwColMod

   LOCAL aWork
   LOCAL xVal

   aWork := ::oDataGrid:aWork

   WITH OBJECT ::oBrowse
      DEFAULT nRecord TO aWork[ :nIndex ]
      xVal := :aGridData[ nRecord, ::nDataCol ]
   END WITH

RETURN xVal

//------------------------------------------------------------------------------

METHOD cValue( lList ) CLASS XBrwColMod

   LOCAL xValue := ::Value()
   LOCAL cValue := "" //ToString( xValue )
   LOCAL nIdx

   lList := .F.

   IF ValType( xValue ) == "N" .AND. !Empty( ::aEditListBound )
      IF ( nIdx := AScan( ::aEditListBound, xValue ) ) > 0
         cValue := ::aEditListText[ nIdx ]
      ENDIF
      lList  := .T.
   ENDIF

RETURN cValue

//------------------------------------------------------------------------------

METHOD EditValue( lChanged ) CLASS XBrwColMod

   LOCAL xValue
   LOCAL cValue

   xValue := ::Value()
   cValue := ::cValue( @lChanged )

   IF lChanged
      xValue := cValue
   ENDIF

RETURN xValue

//------------------------------------------------------------------------------

METHOD DefaultValue( cType ) CLASS XBrwColMod

   LOCAL Value

   IF ::uDefaultValue != Nil
      Value := ::uDefaultValue
   ELSE
      IF Len( ::aGridData ) > 0
         cType := ValType( ::aGridData[ 1, ::nDataCol ] )
      ELSE
         cType := "C"
      ENDIF

      SWITCH cType
      CASE "C"
      CASE "M"
      CASE "B"
         Value := ""
         EXIT
      CASE "N"
         Value := 0
         EXIT
      CASE "L"
         Value := .F.
         EXIT
      CASE "D"
         Value := Ctod( "" )
         EXIT
      CASE "T"
         Value := hb_Ctot( "" )
         EXIT
      OTHERWISE
         Value := ""
      END SWITCH
   ENDIF

RETURN Value

//------------------------------------------------------------------------------

METHOD Refresh( lValue ) CLASS XBrwColMod

   IF ::oBrowse != NIL
      ::oBrowse:Refresh( lValue )
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD Edit() CLASS XBrwColMod

   LOCAL lChanged := .F.

   ::SetEditProperties()

   WITH OBJECT ::oGridEdit
      :lVisible := ::lVisible .AND. ::lCanEdit
      IF ::oBrowse:nEditMode == beEDIT .AND. :Value() == ::EditValue( @lChanged )
         :SetSel( 0, 0 )
         :SelectAll()
      ELSE
         :SetSel( -1, -1)
      ENDIF
      :lTypeChanged := lChanged
   END WITH

RETURN NIL

//------------------------------------------------------------------------------

METHOD CancelEdit() CLASS xBrwColMod

   WITH OBJECT ::oGridEdit
      :lVisible := .f.
   END WITH

RETURN NIL

//------------------------------------------------------------------------------

METHOD SetEditProperties() CLASS XBrwColMod

   ::SetEditCoors()

   WITH OBJECT ::oGridEdit
      :nMaxLength  := ::nEditLength
      :lModify     := .F.
      :lReadOnly   := .F.
      :nAlignment  := ::nDataAlignment
   END

RETURN NIL

//------------------------------------------------------------------------------

METHOD SetEditCoors() CLASS XBrwColMod

   LOCAL aRect
   LOCAL nTop, nHeight

   aRect   := ::oHeaderCol:GetRect()
   nTop    := ::oDataGrid:nActiveRowTop
   nHeight := ::oDataGrid:nItemHeight

   ::oGridEdit:SetBounds( aRect[ rtLEFT], nTop, aRect[ rtRIGHT ] - aRect[ rtLEFT], nHeight )

RETURN NIL

//------------------------------------------------------------------------------

METHOD IsLastOnEdit() CLASS XBrwColMod

RETURN ( ::oDataGrid:LastColEditable() == Self )

//------------------------------------------------------------------------------

METHOD SetAlignment( Value ) CLASS XBrwColMod

   ::FnAlignment := Value

   IF ::oHeaderCol != NIL
      ::oHeaderCol:nAlignment := Value
   ENDIF

   ::Refresh()

RETURN Value

//------------------------------------------------------------------------------

METHOD SetDataAlignment( Value ) CLASS XBrwColMod

   ::FnDataAlignment := Value

   IF ::oFooterCol != NIL
      ::oFooterCol:nAlignment := Value
   ENDIF

   ::Refresh()

RETURN Value

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

#pragma BEGINDUMP

#include <windows.h>
#include <xailer.h>

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XBRWCOLMOD_SETLOWVALUE ) // ( cProperty, Value ) --> NIL
{
   PHB_ITEM Self = hb_stackSelfItem();
   BOOL lSuccess = FALSE;

   switch( HB_ITEM_TYPE( hb_param( 2, HB_IT_ANY ) ) )
   {
   case HB_IT_ARRAY:
      XA_ObjSendItem( Self, hb_parc( 1 ), hb_param( 2, HB_IT_ARRAY ) );
      lSuccess = TRUE;
      break;
   case HB_IT_STRING:
      XA_ObjSendC( Self, hb_parc( 1 ), hb_parc( 2 ) );
      lSuccess = TRUE;
      break;
   case HB_IT_INTEGER:
   case HB_IT_LONG:
      XA_ObjSendNL( Self, hb_parc( 1 ), hb_parnl( 2 ) );
      lSuccess = TRUE;
      break;
   case HB_IT_DOUBLE:
      XA_ObjSendND( Self, hb_parc( 1 ), hb_parnd( 2 ) );
      lSuccess = TRUE;
      break;
   case HB_IT_LOGICAL:
      XA_ObjSendL( Self, hb_parc( 1 ), hb_parl( 2 ) );
      lSuccess = TRUE;
      break;
   }
   hb_retl( lSuccess );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XBRWCOLMOD_GETLOWVALUE ) // ( cProperty ) --> Value
{
   PHB_ITEM Self  = hb_stackSelfItem();
   PHB_ITEM oItem = XA_ObjGetItemCopy( Self, hb_parc( 1 ) );

   hb_ret();

   switch( HB_ITEM_TYPE( oItem ) )
   {
   case HB_IT_ARRAY: // Siempre tiene que ser de tipo string completo
      {
      int nLen = hb_arrayLen( oItem );
      int nFor;
      hb_reta( nLen );
      for( nFor = 1; nFor <= nLen; nFor ++ )
         hb_storvc( hb_arrayGetCPtr( oItem, nFor ), -1, nFor );
      }
      break;
   case HB_IT_STRING:
      hb_retc_buffer(  hb_itemGetC( oItem ) );
      break;
   case HB_IT_INTEGER:
   case HB_IT_LONG:
      hb_retnl(  hb_itemGetNL( oItem ) );
      break;
   case HB_IT_DOUBLE:
      hb_retnd(  hb_itemGetND( oItem ) );
      break;
   case HB_IT_LOGICAL:
      hb_retl(  hb_itemGetL( oItem ) );
      break;
   }
   hb_itemRelease( oItem );
}

#pragma ENDDUMP

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

#define DEFAULT_DROP_SIZE   250

CLASS TEditForGrid FROM TEditMod

   ACCESS oBrowse             IS oParent:oParent
   ACCESS aEditListText       IS oCol:aEditListText
   ACCESS aEditListBound      IS oCol:aEditListBound

   PROPERTY oCol              AS TBrwColMod READONLY
   PROPERTY lDropped          INIT .F. READONLY
   PROPERTY lDropAnimation    INIT .T.
   PROPERTY nClrDropPane      INIT clWindow        EDITOR PE_Color
   PROPERTY nClrDropText      INIT clWindowText    EDITOR PE_Color
   PROPERTY nClrDropHotPane   INIT clActiveCaption EDITOR PE_Color
   PROPERTY nClrDropHotText   INIT clWindowText    EDITOR PE_Color
   PROPERTY nClrDropSelPane   INIT clHighlight     EDITOR PE_Color
   PROPERTY nClrDropSelText   INIT clWindowText    EDITOR PE_Color
   PROPERTY nDropMargin       INIT 10
   PROPERTY nDropItemHeight   INIT 25


RESERVED:
   PROPERTY nDroppedWidth     INIT 0
   PROPERTY nDroppedHeight    INIT 300

   DATA     lTypeChanged      INIT .F.

   EVENT OnShow( oSender )
   EVENT OnDropCreate( oSender )   // --> lValue
   EVENT OnDropDown( oSender, oDropForm, oControl )   // --> Nil
   EVENT OnCloseUp( oSender )   // --> Nil

   METHOD GoNextControl() INLINE ::oParent:GoNextControl( Self, .F. )
   METHOD GoPrevControl() INLINE ::oParent:GoNextControl( Self, .T. )

   METHOD Show( nCmdShow )
   METHOD WMKeyDown( nKey, nFlags, hWnd )
   METHOD WMChar( nKey, nFlags, hWnd )
   METHOD WMKillFocus( hCtl )

   METHOD BtnClick()
   METHOD DropdownDate()
   METHOD DropdownMemo()
   METHOD DropdownList()
   METHOD SetListValue( cValue )
   METHOD SaveValue()

   METHOD BtnClick( nX, nY )
   METHOD CloseUp()

PROTECTED:
   PROPERTY nStyle
   PROPERTY DropHandle INIT 0

END CLASS

//--------------------------------------------------------------------------

METHOD SetListValue( cValue ) CLASS TEditForGrid

   LOCAL nIndex
   LOCAL xRet

   cValue := Trim( Upper( cValue ) )

//   nIndex := AScan( ::oCol:aEditListText, {|v,e| cValue  = Trim( Upper( v ) ) } )

   nIndex := AScan( ::oCol:aEditListText, {|v,e| Trim( Upper( v ) ) = cValue } )

   IF nIndex > 0
      IF nIndex <= Len( ::oCol:aEditListBound ) .AND. !::lTypeChanged
         xRet := ::oCol:aEditListBound[ nIndex ]
      ELSE
         xRet := ::oCol:aEditListText[ nIndex ]
      ENDIF
      ::SetValue( xRet )
   ENDIF

RETURN ( nIndex > 0 )

//--------------------------------------------------------------------------

METHOD SaveValue() CLASS TEditForGrid

   LOCAL xValue, nIndex

   xValue := ::Value()

   IF ::lTypeChanged .AND. ValType( xValue ) == "C"
      xValue := Trim( Upper( xValue ) )
      nIndex := AScan( ::oCol:aEditListText, {|v,e| xValue = Trim( Upper( v ) ) } )
      IF nIndex > 0 .AND. nIndex <= Len( ::oCol:aEditListBound )
         xValue := ::oCol:aEditListBound[ nIndex ]
      ELSE
         xValue := 0
      ENDIF
   ENDIF

RETURN xValue

//--------------------------------------------------------------------------

METHOD BtnClick( nX, nY ) CLASS TEditForGrid

   LOCAL xRet, xVal

   SWITCH ::oCol:nEditType
   CASE beGET
   CASE beGET_BUTTON
   CASE beBUTTON
      IF GetFocus() == ::Handle
         IF ::EventAssigned( "OnBtnClick" )
            xRet := ::OnBtnClick( nX, nY )
         ELSE
            xVal := ::Value()
            xRet := ::oBrowse:OnEditColBtnClick( ::oCol, @xVal )
            IF !Empty( xRet )
               ::SetValue( xVal )
            ENDIF
         ENDIF
         IF !Empty( xRet )
            ::SetSel( 0, -1 )
         ENDIF
      ENDIF
   CASE beGET_LISTBOX
   CASE beLISTBOX
      ::DropdownList()
      EXIT
   CASE beGET_CALENDAR
   CASE beCALENDAR
      ::DropdownDate()
      EXIT
   CASE beGET_MEMO
   CASE beMEMO
      ::DropdownMemo()
      EXIT
   END SWITCH

RETURN nil

//--------------------------------------------------------------------------

METHOD CloseUp() CLASS TEditForGrid

   ::OnCloseUp()

   WITH OBJECT ::oCol:oBrowse
      IF :IsSingleEdit()
         :CancelEdit( .T. )
      ENDIF
   END WITH

RETURN NIL

//--------------------------------------------------------------------------

METHOD Show( nCmdShow ) CLASS TEditForGrid

   ::nDroppedHeight := ::oCol:nDroppedHeight
   ::nDroppedWidth  := ::oCol:nDroppedWidth
   ::cPicture       := ::oCol:cPicture

   SWITCH ::oCol:nEditType
   CASE beNONE
      ::nStyle := esNONE
      ::lReadOnly := .T.
      EXIT
   CASE beGET
      ::nStyle := esNONE
      EXIT
   CASE beGET_BUTTON
   CASE beBUTTON
      ::nStyle := esVERTDOTS
      EXIT
   CASE beGET_LISTBOX
   CASE beLISTBOX
      ::nStyle := esCOMBO
      EXIT
   CASE beGET_CALENDAR
   CASE beCALENDAR
      ::nStyle := esCOMBO
      EXIT
   CASE beGET_MEMO
   CASE beMEMO
      ::nStyle := esVERTDOTS
      EXIT
   OTHERWISE
      ::nStyle := esNONE
      EXIT
   END SWITCH

   ::OnShow()

RETURN ::Super:Show( nCmdShow )

//------------------------------------------------------------------------------

METHOD WMKeyDown( nKey, nFlags, hWnd ) CLASS TEditForGrid

   LOCAL aTxt
   LOCAL nEditType, xVal, nPos

   IF hWnd != ::Handle
      RETURN ::Super:WMKeyDown( nKey, nFlags, hWnd )
   ENDIF

   nEditType := ::oCol:nEditType

   SWITCH nKey
   CASE VK_ESCAPE
      ::oCol:oBrowse:CancelEdit( .F., nKey )
      RETURN 0
   CASE VK_RETURN
      WITH OBJECT ::oBrowse
         IF !:lOnEditUseReturn .OR. :IsSingleEdit() .OR. ;
            ::oCol:IsLastOnEdit()
//            ::oCol:oHeaderCol:GetNextSibling == NIL
            ::OnExit( ::oCol:oHeaderCol:GetNextSibling )
            :CancelEdit( .T., nKey )
         ELSE
            ::GoNextControl()
         ENDIF
         RETURN 0
      END WITH
      EXIT
   CASE VK_F4
      IF !::lReadOnly .AND. !lGetKeyState( VK_CONTROL )
         ::OnBtnClick()
         RETURN 0
      ENDIF
      EXIT
   CASE VK_DELETE
   CASE VK_BACK
      SWITCH nEditType
      CASE beBUTTON
      CASE beLISTBOX
      CASE beCALENDAR
      CASE beMEMO
         RETURN 0
         EXIT
      END SWITCH
   END SWITCH

   IF nKey == VK_UP .OR. nKey == VK_DOWN
      SWITCH nEditType
      CASE beLISTBOX
         aTxt := ::oCol:aEditListText
         nPos := AScan( aTxt, ::Value() )
         IF nPos > 0
            IF nKey == VK_UP
               nPos := Max( 1, nPos - 1 )
            ELSE
               nPos := Min( Len( aTxt ), nPos + 1 )
            ENDIF
            ::Value := aTxt[ nPos ]
            ::SelectAll()
            RETURN 0
         ENDIF
         EXIT
      CASE beCALENDAR
         IF nKey == VK_UP
            ::Value++
         ELSE
            ::Value--
         ENDIF
         ::SelectAll()
         RETURN 0
      END SWITCH
   ENDIF

   IF nEditType == beLISTBOX .AND. IsCharAlphaNumeric( nKey )
      aTxt := ::oCol:aEditListText
      nPos := AScan( aTxt, {|v| Upper( Left( v, 1 ) ) == Upper( Chr( nKey ) ) } )
      IF nPos > 0
         ::Value := aTxt[ nPos ]
         ::SelectAll()
         RETURN 0
      ENDIF
   ENDIF

RETURN ::Super:WMKeyDown( nKey, nFlags, hWnd )

//------------------------------------------------------------------------------

# xtranslate IsSpecialChar( <nKey> ) => ;
             Chr( <nKey> ) $ ( " .:,;-_¿¡'?=)(/&%$!ºª*Çç¨{}\|@#[]+<>" + Chr( 32 ) )

METHOD WMChar( nKey, nFlags, hWnd ) CLASS TEditForGrid

   LOCAL aTxt
   LOCAL nPos

   IF hWnd != ::Handle
      RETURN ::Super:WMChar( nKey, nFlags, hWnd )
   ENDIF

   SWITCH ::oCol:nEditType
   CASE beLISTBOX
   CASE beBUTTON
   CASE beCALENDAR
   CASE beMEMO
      IF IsCharAlphaNumeric( nKey ) .OR. IsSpecialChar( nKey ) .OR. nKey == VK_BACK
         RETURN 0
      ENDIF
      EXIT
   END SWITCH

RETURN ::Super:WMChar( nKey, nFlags, hWnd )

//------------------------------------------------------------------------------

METHOD DropdownList() CLASS TEditForGrid

   LOCAL oDrop
   LOCAL lRet

   IF !::lDropped .AND. Len( ::oCol:aEditListText ) > 0
      lRet := ::OnDropCreate()
      IF lRet == NIL .OR. lRet

         WITH OBJECT oDrop := TDropControl():New( Self )
            :Create()
            ::DropHandle := :Handle
            WITH OBJECT :oControl := TListBoxMod():New( oDrop )
               :oFont        := ::oFont:Clone()
               :aItems       := ::oCol:aEditListText
               :nBorderStyle := bvFLAT
               :cText        := ToString( ::Value )
               :nClrPane     := ::nClrDropPane
               :nClrText     := ::nClrDropText
               :nClrHotPane  := ::nClrDropHotPane
               :nClrHotText  := ::nClrDropHotText
               :nClrSelPane  := ::nClrDropSelPane
               :nClrSelText  := ::nClrDropSelText
               :nMargin      := ::nDropMargin
               :nItemHeight  := ::nDropItemHeight
               IF ::EventAssigned( "OnDrawItem" )
                  :OnDrawItem  := {|o,id,txt,img,clt,clp,hdc,aRec| ;
                                    ::OnDrawItem( id,@txt,@img,@clt,@clp,hdc,aRec ) }
               ENDIF
               :Create()
            END WITH

            :OnSelect   := {|oForm, oControl| ::SetListValue( oControl:cText ), ::SetSel( 0, -1 ) }
            :OnReturn   := {|| ::lDropped := .F., ::CloseUp(), ::Refresh() }
            :nHeight    := Min( ::nDroppedHeight, ::nDropItemHeight * Len( :oControl:aItems ) )
            :nWidth     := IIF( ::nDroppedWidth = 0, ::nWidth, ::nDroppedWidth )
            :lAnimation := ::lDropAnimation

            ::OnDropDown( oDrop, oDrop:oControl )

            :Show()
         END WITH

         ::lDropped := .T.
         ::Refresh()
      ENDIF
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD DropdownDate() CLASS TEditForGrid

   LOCAL oDrop
   LOCAL lRet

   IF !::lReadOnly .AND. !::lDropped
      lRet := ::OnDropCreate()
      IF lRet == NIL .OR. lRet
         WITH OBJECT oDrop := TDropControl():New( Self )
            :Create()
            ::DropHandle := :Handle
            WITH OBJECT :oControl := TCalendarMod():New( oDrop )
               :dValue            := IIF( Empty( ::Value ), Date(), ::Value )
               :lTransparent      := .f.

               IF ::EventAssigned( "OnDrawItem" )
                  :OnDrawItem  := {|o, dDay, nClrText, nClrPane, hDC, aRect| ;
                                    ::OnDrawItem( @dDay, @nClrText, @nClrPane, hDC, aRect ) }
               ENDIF
               :lShowDOW          := .T.
               :lShowLines        := .F.
               :lShowFirstOfGroup := .F.
               :Create()
            END WITH

            :OnSelect   := {|oForm, oControl| ::SetValue( oControl:dValue ), ::SetSel( 0, -1 ) }
            :OnReturn   := {|| ::lDropped := .F., ::CloseUp(), ::Refresh() }
            :nHeight    := IIF( ::nDroppedHeight == 0, DEFAULT_DROP_SIZE, ::nDroppedHeight )
            :nWidth     := IIF( ::nDroppedWidth  == 0, DEFAULT_DROP_SIZE, ::nDroppedWidth )
            :lAnimation := ::lDropAnimation

            ::OnDropDown( oDrop, oDrop:oControl )

            :Show()
         END WITH

         ::lDropped := .T.
         ::Refresh()
      ENDIF
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD DropdownMemo() CLASS TEditForGrid

   LOCAL oDrop
   LOCAL lRet

   IF !::lReadOnly .AND. !::lDropped
      lRet := ::OnDropCreate()
      IF lRet == NIL .OR. lRet
         WITH OBJECT oDrop := TDropControl():New( Self )
            :nBorderStyle := bsSPLASH
            :Create()
            ::DropHandle := :Handle
            WITH OBJECT :oControl := TMemoMod():New( oDrop )
               :oFont    := ::oFont:Clone()
               :Value    := ::Value
               :nClrPane := ::nClrPaneFocus
               :nClrText := ::nClrTextFocus
               :lModBorder := .f.
               :Create()
            END WITH

            :OnSelect   := {|oForm, oControl| ::SetValue( oControl:Value ) }
            :OnReturn   := {|| ::lDropped := .F., ::CloseUp(), ::Refresh() }
            :nHeight    := IIF( ::nDroppedHeight == 0, DEFAULT_DROP_SIZE, ::nDroppedHeight )
            :nWidth     := IIF( ::nDroppedWidth  == 0, ::nWidth, ::nDroppedWidth )
            :lAnimation := ::lDropAnimation

            ::OnDropDown( oDrop, oDrop:oControl )

            :Show()
         END WITH

         ::lDropped := .T.
         ::Refresh()
      ENDIF
   ENDIF

RETURN NIL

//------------------------------------------------------------------------------

METHOD WMKillFocus( hCtl ) CLASS TEditForGrid

   ::Super:WMKillFocus( hCtl )

   IF GetParent( hCtl ) != ::oParent:Handle .AND. hCtl != ::DropHandle ;
      .AND. GetParent( hCtl ) != ::DropHandle .AND. hCtl != ::oParent:Handle
      ::oBrowse:CancelEdit()
   ENDIF

RETURN nil

//------------------------------------------------------------------------------
