#include "FiveWin.ch"

#define CB_ADDSTRING     ( WM_USER +  3 )
#define CB_DELETESTRING  ( WM_USER +  4 )
#define CB_GETCURSEL     ( WM_USER +  7 )
#define CB_INSERTSTRING  ( WM_USER + 10 )
#define CB_RESETCONTENT  ( WM_USER + 11 )
#define CB_SETCURSEL     ( WM_USER + 14 )
#define CB_ERR                       -1

#define COLOR_WINDOW       5
#define COLOR_WINDOWTEXT   8

//----------------------------------------------------------------------------//

CLASS TComboBox FROM TControl

   DATA   aItems
   DATA   bChange
   DATA   nAt

   METHOD New( nRow, nCol, bSetGet, aItems, nWidth, nHeight, oWnd, nHelpId,;
               bChange, bValid, nClrText, nClrBack ) CONSTRUCTOR

   METHOD ReDefine( nId, bSetGet, aItems, oWnd, nHelpId, bValid, ;
               bChange, nClrText, nClrBack ) CONSTRUCTOR

   METHOD cToChar() INLINE  Super:cToChar( "COMBOBOX" )

   METHOD Init( hDlg ) INLINE Super:Init( hDlg ),;
                              ::Default()

   METHOD Change()

   METHOD Default()

   METHOD LostFocus()

   METHOD MouseMove( nRow, nCol, nKeyFlags )

   METHOD Refresh() INLINE  ::Set( Eval( ::bSetGet ) ), Super:Refresh()

   METHOD Select( nItem ) INLINE ::SendMsg( CB_SETCURSEL, nItem, 0 )

   METHOD Set( cNewItem )

   METHOD SetItems( aItems ) INLINE ::Reset(), ::aItems := aItems,;
                                    ::Default(), ::Change()

   METHOD Add( cItem, nAt )
   METHOD Modify( cItem, nAt )
   METHOD Insert( cItem, nAt )
   METHOD Del( nAt )

   METHOD Reset() INLINE  Eval( ::bSetGet, "" ), ;
                          ASize( ::aItems, 0 ) , ;
                          ::nAt := 0, ::SendMsg( CB_RESETCONTENT ),;
                          ::Change()

ENDCLASS

//----------------------------------------------------------------------------//

METHOD New( nRow, nCol, bSetGet, aItems, nWidth, nHeight, oWnd, nHelpId,;
            bChange, bValid, nClrFore, nClrBack ) CLASS TComboBox

   if nClrFore == nil
      nClrBack := GetSysColor( COLOR_WINDOW )
   endif

   DEFAULT aItems   := {}, nWidth := 40, nHeight := 60,;
           nClrFore := GetSysColor( COLOR_WINDOWTEXT )

   ::cCaption  = ""
   ::nTop      = nRow * 14
   ::nLeft     = nCol *  8
   ::nBottom   = ::nTop  + nHeight - 1
   ::nRight    = ::nLeft + nWidth  - 1
   ::nAt       = 1
   ::aItems    = aItems
   ::bChange   = bChange
   ::bSetGet   = bSetGet
   ::oWnd      = oWnd
   ::nStyle    = nOR( LBS_NOTIFY, WS_TABSTOP, CBS_DROPDOWNLIST,;
                      LBS_DISABLENOSCROLL, WS_CHILD, WS_VISIBLE, WS_BORDER,;
                      WS_VSCROLL )
   ::nId       = ::GetNewId()
   ::nHelpId   = nHelpId
   ::bValid    = bValid
   ::lDrag     = .f.
   ::lCaptured = .f.

   ::SetColor( nClrFore, nClrBack )

   if oWnd:lVisible
      ::Create( "COMBOBOX" )
      ::Default()
      oWnd:AddControl( Self )
   else
      oWnd:DefControl( Self )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD ReDefine( nId, bSetGet, aItems, oWnd, nHelpId, bValid, ;
                 bChange, nClrFore, nClrBack ) CLASS TComboBox

   if nClrFore == nil
      nClrBack := GetSysColor( COLOR_WINDOW )
   endif

   DEFAULT aItems   := {},;
           nClrFore := GetSysColor( COLOR_WINDOWTEXT )

   ::nId     = nId
   ::hWnd    = 0
   ::aItems  = aItems
   ::bChange = bChange
   ::bSetGet = bSetGet
   ::oWnd    = oWnd
   ::nHelpId = nHelpId
   ::bValid  = bValid
   ::nAt     = 1
   ::lDrag     = .f.
   ::lCaptured = .f.

   ::SetColor( nClrFore, nClrBack )

   oWnd:DefControl( Self )

return nil

//----------------------------------------------------------------------------//

METHOD Change() CLASS TComboBox

   ::nAt = ::SendMsg( CB_GETCURSEL ) + 1

   if ::nAt != 0 .and. ::nAt <= Len( ::aItems )
      Eval( ::bSetGet, ::aItems[ ::nAt ] )
   endif

   if ::bChange != nil
      Eval( ::bChange, Self )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Set( cNewItem ) CLASS TComboBox

   local nAt := AScan( ::aItems,;
                       { | cItem | Upper( AllTrim( cItem ) ) == ;
                                   Upper( AllTrim( cNewItem ) ) } )

   if nAt != 0
      ::Select( nAt - 1 )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD LostFocus() CLASS TComboBox

   local nAt := ::SendMsg( CB_GETCURSEL )

   Super:LostFocus()
   if nAt != CB_ERR
      ::nAt = nAt
      Eval( ::bSetGet, ::aItems[ nAt + 1 ] )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Add( cItem, nAt ) CLASS TComboBox

   DEFAULT nAt := 0

   if nAt == 0
      AAdd( ::aItems, cItem )
   else
      ASize( ::aItems, Len( ::aItems ) + 1 )
      AIns( ::aItems, nAt )
      ::aItems[ nAt ] = cItem
   endif

   ::SendMsg( CB_ADDSTRING, nAt, cItem )

return nil

//----------------------------------------------------------------------------//

METHOD Modify( cItem, nAt ) CLASS TComboBox

   DEFAULT nAt := 0

   if nAt != 0
      ::aItems[ nAt ] = cItem
      ::SendMsg( CB_DELETESTRING, nAt - 1 )
      ::SendMsg( CB_INSERTSTRING, nAt - 1, cItem )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Insert( cItem, nAt ) CLASS TComboBox

   DEFAULT nAt := 0

   if nAt != 0
      ASize( ::aItems, Len( ::aItems ) + 1 )
      AIns( ::aItems, nAt )
      ::aItems[ nAt ] = cItem
      ::SendMsg( CB_INSERTSTRING, nAt - 1, cItem )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Del( nAt ) CLASS TComboBox

   DEFAULT nAt := 0

   if nAt != 0
      ADel( ::aItems, nAt )
      ASize( ::aItems, Len( ::aItems ) - 1 )
      ::SendMsg( CB_DELETESTRING, nAt - 1 )
   endif

return nil

//----------------------------------------------------------------------------//

METHOD Default() CLASS TComboBox

   local cStart := Eval( ::bSetGet )

   AEval( ::aItems, { | cItem, nAt | ::SendMsg( CB_ADDSTRING, nAt, cItem ) } )

   ::nAt = AScan( ::aItems, { | cItem | Upper( AllTrim( cItem ) ) == ;
                                        Upper( AllTrim( cStart ) ) } )

   ::Select( ::nAt - If( ::nAt > 0, 1, 0 ) )

return nil

//----------------------------------------------------------------------------//

METHOD MouseMove( nRow, nCol, nKeyFlags ) CLASS TComboBox

   local nResult := Super:MouseMove( nRow, nCol, nKeyFlags )

return If( ::lDrag, nResult, nil )    // We want standard behavior !!!

//----------------------------------------------------------------------------//
