/*
 * Xailer source code:
 *
 * ListView.prg
 * Clase para manejo del control nativo ListView
 *
 * Copyright 2003, 2009 Ignacio Ortiz de Zúñiga
 * Copyright 2003, 2009 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"
#include "CommCtrl.api"

#define WM_XAILER1   WM_XAILER + 1

//------------------------------------------------------------------------------

CLASS XListView FROM TStdControl

PUBLISHED:
   // Paints via double-buffering, which reduces flicker
   PROPERTY lDoubleBuffer   INIT .F. WRITE INLINE ::FlDoubleBuffer   := Value, ::SetExStyle()
   // If a partially hidden label in any list view mode lacks tooltip text, the list-view control will unfold the label
   PROPERTY lLabeltip       INIT .F. WRITE INLINE ::FlLabeltip       := Value, ::SetExStyle()
   // Column headers are not displayed in report view
   PROPERTY lNoColumnHeader INIT .F. WRITE INLINE ::FlNoColumnHeader := Value, ::ChangeCtlStyle( LVS_NOCOLUMNHEADER, Value )
   // Column headers do not work like buttons
   PROPERTY lNoSortHeader   INIT .F. WRITE INLINE ::FlNoSortHeader   := Value, ::ChangeCtlStyle( LVS_NOSORTHEADER, Value )
   // Only one item at a time can be selected. By default, multiple items may be selected
   PROPERTY lSingleSel      INIT .F. WRITE INLINE ::FlSingleSel      := Value, ::ChangeCtlStyle( LVS_SINGLESEL, Value )

   PROPERTY oImageListSmall  AS TImageList  WRITE METHOD SetImageListSmall  EDITOR PE_ImageList
   PROPERTY oImageListLarge  AS TImageList  WRITE METHOD SetImageListLarge  EDITOR PE_ImageList
   PROPERTY oImageListHeader AS TImageList  WRITE METHOD SetImageListHeader EDITOR PE_ImageList
   PROPERTY aGroups        INIT {} EDITOR PE_StringList AS TListViewGroup[]
   PROPERTY aColumns       INIT {} EDITOR PE_StringList AS TListViewColumn[]
   PROPERTY aItems         INIT {} EDITOR PE_StringList AS TListViewItem[]
   PROPERTY nView          INIT lvICON VALUES lvICON, lvSMALLICON, lvLIST, lvTILE, lvDETAILS WRITE METHOD SetView
   PROPERTY nWidth         INIT 120
   PROPERTY nHeight        INIT 90
   PROPERTY nClrText       INIT clBtnText    WRITE SetTextColor   EDITOR PE_Color
   PROPERTY nClrPane       INIT clWindow     WRITE SetBkColor     EDITOR PE_Color
   PROPERTY lBorder        INIT .T.
   PROPERTY lAutoArrange   INIT .T. WRITE INLINE ::FlAutoArrange   := Value, ::ChangeCtlStyle( LVS_AUTOARRANGE, Value )
   PROPERTY lEditLabels    INIT .F. WRITE INLINE ::FlEditLabels    := Value, ::ChangeCtlStyle( LVS_EDITLABELS, Value )
   PROPERTY lShowSelAlways INIT .T. WRITE INLINE ::FlShowSelAlways := Value, ::ChangeCtlStyle( LVS_SHOWSELALWAYS, Value )
   PROPERTY lLabelWrap     INIT .T. WRITE INLINE ::FlLabelWrap     := Value, ::ChangeCtlStyle( LVS_NOLABELWRAP, !Value )
   PROPERTY lAlignLeft     INIT .F. WRITE INLINE ::FlAlignLeft     := Value, ::ChangeCtlStyle( LVS_ALIGNLEFT, Value )
   PROPERTY lGroupView     INIT .F. WRITE METHOD SetGroupView

   PROPERTY lFullRowSelect  INIT .F. WRITE INLINE ::FlFullRowSelect  := Value, ::SetExStyle()
   PROPERTY lCheckBoxes     INIT .F. WRITE INLINE ::FlCheckBoxes     := Value, ::SetExStyle()
   PROPERTY lGridLines      INIT .F. WRITE INLINE ::FlGridLines      := Value, ::SetExStyle()
   PROPERTY lHeaderDragDrop INIT .F. WRITE INLINE ::FlHeaderDragDrop := Value, ::SetExStyle()
   PROPERTY lTooltips       INIT .F. WRITE INLINE ::FlToolTips       := Value, ::SetExStyle()
   PROPERTY lHotTrackSelect INIT .F. WRITE INLINE ::FlHotTrackSelect := Value, ::SetExStyle()

   PROPERTY oBkImage       AS TPicture WRITE METHOD SetBkImage EDITOR PE_Picture

   EVENT OnClick( oSender, nPosX, nPosY ) // --> Nil
   EVENT OnKeyDown( oSender, nKey ) // --> Nil
   EVENT OnColumnClick( oSender, nColumn  )// --> Nil | lValue
   EVENT OnBeginLabelEdit( oSender, oItem ) // --> Nil | lValue
   EVENT OnEndLabelEdit( oSender, oItem ) // --> Nil | lValue
   EVENT OnDeleteItem( oSender, oItem ) // --> Nil
   EVENT OnCheckStateChanged( oSender, oItem ) // --> Nil
   EVENT OnItemChanged( oSender, oItem ) // --> Nil
   EVENT OnDrawCell( oSender, oItem, nColumn, BYREF nClrText, BYREF nClrPane, hDC, aRect )  // --> nResult

PUBLIC:
   PROPERTY cText

   METHOD New( oParent ) CONSTRUCTOR
   METHOD Create( oParent ) CONSTRUCTOR
   METHOD Free()

   METHOD AddItem( cText, nImage, nGroup, nIndent, aData, lChecked ) EXTERN XListView_InsertItem()
   METHOD InsertItem( cText, nImage, nGroup, nIndent, aData, lChecked, nPos )
   METHOD DeleteAllItems()
   METHOD IsEmpty() INLINE ( ::GetCount() == 0 )
   METHOD AddColumn( cText, nWidth, nAlignment, nImage )
   METHOD DelColumn( nCol )
   METHOD AddGroup( cHeader, cFooter, nHeaderAlign, nFooterAlign, nState )
   METHOD DelGroup( nGroup )

   METHOD CancelEdit() INLINE ::SendMsg( LVM_CANCELEDITLABEL, 0, 0 )
   METHOD GetSelection()
   METHOD GetFocused()

   METHOD Arrange( nAction )  INLINE ::SendMsg( LVM_ARRANGE, IIF( nAction == NIL, LVA_DEFAULT, nAction ), 0 )
   METHOD Refresh( lRedraw )
   METHOD Sort( nCol )
   METHOD HitTest( nX, nY )

   METHOD SetBkImage( oPicture, lTile, nXOffset, nYOffset ) // --> oPicture

   // Calculates the number of items that can fit vertically in the visible area of a list-view control when in list or report view
   METHOD GetVisibleCount() INLINE ::SendMsg( LVM_GETCOUNTPERPAGE, 0, 0 )

RESERVED:
   METHOD Notify()
   METHOD XANotify( nCode )
   METHOD SetView( nView )
   METHOD SetImageListSmall( oValue )
   METHOD SetImageListLarge( oValue )
   METHOD SetImageListHeader( oValue )
   METHOD GetHeader() INLINE ::SendMsg( LVM_GETHEADER, 0, 0 )
   METHOD GetCount()  INLINE ::SendMsg( LVM_GETITEMCOUNT, 0, 0 )
   METHOD SetTextColor( nColor ) INLINE ::FnClrText := nColor, ::SendMsg( LVM_SETTEXTCOLOR, 0, nColor )
   METHOD SetBkColor( nColor )   INLINE ::FnClrPane := nColor, ::SendMsg( LVM_SETBKCOLOR, 0, nColor )
   METHOD SetGroupView( lValue )
   METHOD SetExStyle()
   METHOD ColumnClick( nCol )
   METHOD WMEraseBkgnd() INLINE 1
   METHOD WMPaint() EXTERN XControl_EraseAndPaint()
   METHOD XAClick() VIRTUAL
   METHOD HandleEvent( nMsg, nWParam, nLParam )
   METHOD ReBuild()
   METHOD GetItemRect( nItem )
   METHOD WMDestroy()

   // [cItem] Searches for a list-view item with the specified string
   // [nStart] The index of the item to begin the search with or -1 to start from the beginning. By default, -1
   // Returns the index of the item if successful, or -1 otherwise.
   METHOD FindItem( cItem, nStart )

   // Causes the list-view control to allocate memory for the specified number of items
   METHOD SetCount( nCount ) INLINE ::SendMsg( LVM_SETITEMCOUNT, nCount, LVSICF_NOINVALIDATEALL )

   // Selects and focuses a specific item
   METHOD SetItemFocus( nIndex )

   // Unselects and unfocuses a specific item
   METHOD SetItemNoFocus( nIndex )

   // Enhanced with the above additional LVS_EX_
   METHOD SetExStyle()

PROTECTED:
   DATA cWinClassName INIT WC_LISTVIEW
   DATA nCtlStyle     INIT nOr( WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS )
   DATA lOnZap        INIT .F.

   METHOD UpdateBkImage()

ENDCLASS

//------------------------------------------------------------------------------

METHOD New( oParent ) CLASS XListView

   ::FoImageListSmall  := TImageList():Create( Self )
   ::FoImageListLarge  := TImageList():Create( Self )
   ::FoImageListHeader := TImageList():Create( Self )

RETURN ::Super:New( oParent )

//------------------------------------------------------------------------------

METHOD Create( oParent ) CLASS XListView

   LOCAL oItem

   InitCommonControls( ICC_LISTVIEW_CLASSES )

   ::ChangeCtlStyle( LVS_AUTOARRANGE, ::FlAutoArrange )
   ::ChangeCtlStyle( LVS_EDITLABELS, ::FlEditLabels )
   ::ChangeCtlStyle( LVS_SHOWSELALWAYS, ::FlShowSelAlways )
   ::ChangeCtlStyle( LVS_NOLABELWRAP, !::FlLabelWrap )
   ::ChangeCtlStyle( LVS_ALIGNLEFT, ::FlAlignLeft )
   ::ChangeCtlStyle( LVS_SHAREIMAGELISTS, .T. )

   ::Super:Create( oParent )

   ::SetImageListSmall()
   ::SetImageListLarge()
   ::SetImageListHeader()
   ::SetView()
   ::SetExStyle()
   ::SetGroupView()

   FOR EACH oItem IN ::aColumns
      oItem:InsertInList()
   NEXT

   IF Len( ::aGroups ) > 0
      FOR EACH oItem IN ::aGroups
         oItem:InsertInList()
      NEXT
   ENDIF

   FOR EACH oItem IN ::aItems
      oItem:InsertInList()
   NEXT

   ::UpdateBkImage()

RETURN Self

//--------------------------------------------------------------------------

METHOD Free() CLASS XListView

   LOCAL oItem

   FOR EACH oItem IN ::aItems
      IF oItem != NIL .AND. oItem:lParam > 0
         FreeItemMemory( oItem:lParam )
      ENDIF
   NEXT

   ::FaItems   := NIL
   ::FaGroups  := NIL
   ::FaColumns := NIL

   IF ::oImageListSmall:oParent == Self
      ::oImageListSmall:End()
   ENDIF

   ::oImageListSmall := NIL

   IF ::oImageListLarge:oParent == Self
      ::oImageListLarge:End()
   ENDIF

   ::oImageListLarge := NIL

   IF ::oImageListHeader:oParent == Self
      ::oImageListHeader:End()
   ENDIF

   ::oImageListHeader := NIL

RETURN ::Super:Free()

//------------------------------------------------------------------------------

METHOD SetImageListSmall( oValue ) CLASS XListView

   IF oValue != Nil
      IF ::FoImageListSmall != Nil
         ::FoImageListSmall:Destroy()
      ENDIF
      ::FoImageListSmall := oValue
   ENDIF

   WITH OBJECT ::FoImageListSmall
      IF :GetImageCount() > 0
         ::SendMsg( LVM_SETIMAGELIST, LVSIL_SMALL, :Handle )
      ELSE
         ::SendMsg( LVM_SETIMAGELIST, LVSIL_SMALL, 0 )
      ENDIF
   END WITH

RETURN oValue

//------------------------------------------------------------------------------

METHOD SetImageListLarge( oValue ) CLASS XListView

   IF oValue != Nil
      IF ::FoImageListLarge != Nil
         ::FoImageListLarge:Destroy()
      ENDIF
      ::FoImageListLarge := oValue
   ENDIF

   WITH OBJECT ::FoImageListLarge
      IF :GetImageCount() > 0
         ::SendMsg( LVM_SETIMAGELIST, LVSIL_NORMAL, :Handle )
      ELSE
         ::SendMsg( LVM_SETIMAGELIST, LVSIL_NORMAL, 0 )
      ENDIF
   END WITH

RETURN oValue

//------------------------------------------------------------------------------

METHOD SetImageListHeader( oValue ) CLASS XListView

   LOCAL hHeader

   IF oValue != Nil
      IF ::FoImageListHeader != Nil
         ::FoImageListHeader:Destroy()
      ENDIF
      ::FoImageListHeader := oValue
   ENDIF

   hHeader := ::GetHeader()

   IF !Empty( hHeader )
      WITH OBJECT ::FoImageListHeader
         IF :GetImageCount() > 0
            IF :Handle != SendMessage( hHeader, HDM_GETIMAGELIST, 0, 0 )
               SendMessage( hHeader, HDM_SETIMAGELIST, 0, :Handle )
            ENDIF
         ELSE
            SendMessage( hHeader, HDM_SETIMAGELIST, 0, :Handle )
         ENDIF
      END WITH
   ENDIF

RETURN oValue

//------------------------------------------------------------------------------

METHOD SetGroupView( lValue, lNoUpdate ) CLASS XListView

   DEFAULT lNoUpdate TO .F.

   IF !lNoUpdate
      IF lValue != NIL
         ::FlGroupView := lValue
      ELSE
         lValue := ::FlGroupView
      ENDIF
   ENDIF

   IF ::Handle != 0
      ::SendMsg( LVM_ENABLEGROUPVIEW, lValue, 0 )
   ENDIF

RETURN lValue

//------------------------------------------------------------------------------

METHOD SetView( nView, lNoUpdate ) CLASS XListView

   LOCAL nStyle

   DEFAULT lNoUpdate TO .F.

   IF !lNoUpdate
      IF nView != NIL
         ::FnView := nView
      ELSE
         nView := ::FnView
      ENDIF
   ENDIF

   SWITCH nView
   CASE lvICON
      nStyle := LV_VIEW_ICON
      EXIT
   CASE lvSMALLICON
      nStyle := LV_VIEW_SMALLICON
      EXIT
   CASE lvLIST
      nStyle := LV_VIEW_LIST
      EXIT
   CASE lvTILE
      nStyle := LV_VIEW_TILE
      EXIT
   CASE lvDETAILS
      nStyle := LV_VIEW_DETAILS
      EXIT
   END SWITCH

   ::SendMsg( LVM_SETVIEW, nStyle, 0 )
   ::SetImageListHeader()

RETURN ::FnView

//------------------------------------------------------------------------------

METHOD InsertItem( cText, nImage, nGroup, nIndent, aData, lChecked, nPos ) CLASS XListView

   LOCAL oItem

   DEFAULT nImage   TO 0,;
           nGroup   TO 0,;
           nIndent  TO 0,;
           aData    TO {},;
           lChecked TO .F.

   WITH OBJECT oItem := TListViewItem():New( Self, nPos )
      :cText    := cText
      :nImage   := nImage
      :nGroup   := nGroup
      :nIndent  := nIndent
      :aData    := aData
      :lChecked := lChecked
      :Create()
   END

   AAdd( ::aItems, oItem )

RETURN oItem

//------------------------------------------------------------------------------

METHOD DeleteAllItems() CLASS XListView

   LOCAL oItem

   IF ::SendMsg( LVM_DELETEALLITEMS, 0, 0 ) != 0
      FOR EACH oItem IN ::FaItems
         IF oItem != NIL .AND. oItem:lParam > 0
            FreeItemMemory( oItem:lParam )
         ENDIF
      NEXT
      ::FaItems := {}
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD Refresh( lRedraw ) CLASS XListView

   ::SendMsg( LVM_ARRANGE, LVA_DEFAULT, 0 )
   ::SendMsg( LVM_REDRAWITEMS, 0, ::GetCount() - 1 )

RETURN ::Super:Refresh( lRedraw )

//------------------------------------------------------------------------------

METHOD AddColumn( cText, nWidth, nAlignment, nImage ) CLASS XListView

   LOCAL oColumn

   ::SetImageListHeader()

   DEFAULT cText TO "",;
           nWidth TO 100,;
           nAlignment TO taLEFT,;
           nImage TO 0

   WITH OBJECT oColumn := TListViewColumn():New( Self )
      :cText      := cText
      :nWidth     := nWidth
      :nAlignment := nAlignment
      :nImage     := nImage
      :Create()
   END WITH

   AAdd( ::aColumns, oColumn )

RETURN oColumn

//------------------------------------------------------------------------------

METHOD DelColumn( nCol ) CLASS XListView

   LOCAL oItem
   LOCAL nLen

   nLen := Len( ::aColumns )

   IF nCol < nLen
      RETURN .F.
   ENDIF

   // Quitamos la columna

   IF !( ::SendMsg( LVM_DELETECOLUMN, nCol - 1 ) != 0 )
      RETURN .F.
   ENDIF

   Adel( ::aColumns, nCol )
   Asize( ::aColumns, nLen - 1 )

   // Quitamos elementos subitem en los items

   FOR EACH oItem IN ::aItems
      WITH OBJECT oItem
         nLen := Len( :aData )
         IF nCol <= nLen
            Adel( :aData, nCol )
            Asize( :aData, nLen - 1 )
         ENDIF
      END WITH
   NEXT

   // Volvemos a poner todos los items de nuevo

   ::ReBuild()

RETURN .T.

//------------------------------------------------------------------------------

METHOD AddGroup( cHeader, cFooter, nHeaderAlign, nFooterAlign, nState ) CLASS XListView

   LOCAL oGroup

   DEFAULT cHeader      TO "",;
           cFooter      TO "",;
           nHeaderAlign TO taLEFT,;
           nFooterAlign TO taLEFT,;
           nState       TO lsNORMAL

   WITH OBJECT oGroup := TListViewGroup():New( Self )
      :cHeader      := cHeader
      :cFooter      := cFooter
      :nHeaderAlign := nHeaderAlign
      :nFooterAlign := nFooterAlign
      :nState       := nState
      :Create()
   END WITH

   AAdd( ::aGroups, oGroup )

RETURN oGroup

//------------------------------------------------------------------------------

METHOD DelGroup( nGroup ) CLASS XListView

   LOCAL oItem
   LOCAL nAt
   LOCAL lRet

   IF ( nAt := Ascan( ::aGroups, {|v| v:nIndex == nGroup } ) ) == 0
      RETURN .F.
   ENDIF

   // Quitamos items del grupo

   FOR EACH oItem IN ::aItems
      WITH OBJECT oItem
         IF :nGroup == nGroup
            :nGroup := 0
         ENDIF
      END WITH
   NEXT

   // Quitamos el grupo

   IF ::SendMsg( LVM_REMOVEGROUP, nGroup ) == -1
      RETURN .F.
   ENDIF

   Adel( ::aGroups, nAt )
   Asize( ::aGroups, Len( ::aGroups ) - 1 )

RETURN lRet

//------------------------------------------------------------------------------

METHOD ReBuild() CLASS XListView

   LOCAL oItem

   ::lOnZap := .T.

   // Quitamos todos los Items

   IF ::Handle == 0 .OR. ::SendMsg( LVM_DELETEALLITEMS, 0, 0 ) == 0
      RETURN NIL
   ENDIF

   // Los volvemos a poner

   FOR EACH oItem IN ::aItems
      oItem:InsertInList()
   NEXT

   ::lOnZap := .F.

RETURN NIL

//------------------------------------------------------------------------------

METHOD ColumnClick( nCol ) CLASS XListView

   LOCAL lRet

   lRet := ::OnColumnClick( nCol )

   IF lRet != NIL .AND. !lRet
      RETURN NIL
   ENDIF

   ::Sort( nCol )

RETURN Nil

//------------------------------------------------------------------------------

METHOD XANotify( nCode ) CLASS XListView

   IF nCode == XANOTIFY_IMAGELIST_ADDFIRST
      ::SendMsg( LVM_SETIMAGELIST, LVSIL_NORMAL, ::FoImageListLarge:Handle )
      ::SendMsg( LVM_SETIMAGELIST, LVSIL_SMALL, ::FoImageListSmall:Handle )
   ELSEIF nCode == XANOTIFY_IMAGELIST_EMPTY
      ::SendMsg( LVM_SETIMAGELIST, LVSIL_NORMAL, 0 )
      ::SendMsg( LVM_SETIMAGELIST, LVSIL_SMALL, 0 )
      ::SendMsg( LVM_SETIMAGELIST, LVSIL_STATE, 0 )
   ENDIF

RETURN Nil

//------------------------------------------------------------------------------

METHOD HandleEvent( nMsg, nWParam, nLParam ) CLASS XListView

   LOCAL oItem
   LOCAL nAt
   LOCAL lChecked

   IF nMsg != WM_XAILER1
      RETURN Nil
   ENDIF

   IF ( nAt := Ascan( ::aItems, {|v| v != Nil .AND. v:lParam == nLParam } ) ) == 0
      RETURN Nil
   ENDIF

   oItem    := ::aItems[ nAt ]
   lChecked := oItem:lChecked

   SWITCH nWParam
   CASE 100 // Estaba sin check
      IF lChecked
         ::OnCheckStateChanged( oItem )
      ENDIF
      EXIT
   CASE 101 // Estaba con check
      IF !lChecked
         ::OnCheckStateChanged( oItem )
      ENDIF
      EXIT
   END SWITCH

RETURN NIL

//------------------------------------------------------------------------------

METHOD GetItemRect( nItem ) CLASS XListView

   LOCAL oItem
   LOCAL aData

   IF nItem > 0 .AND. nItem <= Len( ::aItems )
      oItem := ::aItems[ nItem ]
      IF oItem != NIL .AND. oItem:lParam != 0
         aData := oItem:GetItemRect()
      ENDIF
   ENDIF

RETURN aData

//--------------------------------------------------------------------------

METHOD WMDestroy() CLASS XListView

   ::Super:WMDestroy()

   ::lOnZap := .T.

RETURN Nil

//------------------------------------------------------------------------------

METHOD SetBkImage( oPicture, lTiled, nXOffset, nYOffset ) CLASS XListView

   IF ::FoBkImage != Nil
      ::FoBkImage:Destroy()
   ENDIF

   IF oPicture != Nil
      IF ValType( oPicture ) != "O"
         oPicture := TPicture():Create( oPicture )
      ENDIF
   ENDIF
   ::FoBkImage := oPicture
   ::UpdateBkImage( lTiled, nXOffset, nYOffset )

RETURN oPicture

//------------------------------------------------------------------------------

#pragma BEGINDUMP

#define HB_OS_WIN_32_USED
#define LVS_EX_DOUBLEBUFFER     0x00010000
#define _WIN32_IE 0x0600
#include <windows.h>
#include <commctrl.h>
#include <hbdate.h>
#include <xailer.h>
#include <constants.ch>

#define LVM_SETSELECTEDCOLUMN  (LVM_FIRST+140)
#define LVM_GETSELECTEDCOLUMN  (LVM_FIRST+174)

//#define LVIF_GROUPID             0x100
//#define LVIF_COLUMNS             0x200

#ifndef __GNUC__
#define ListView_GetSelectedColumn(hwnd)  (UINT)SNDMSG((hwnd),LVM_GETSELECTEDCOLUMN,0,0)
#define ListView_SetSelectedColumn(hwnd,iCol)  SNDMSG((hwnd),LVM_SETSELECTEDCOLUMN,(WPARAM)iCol,0)
#else
#define ListView_SetSelectedColumn(hwnd,iCol)  SNDMSG((hwnd),LVM_SETSELECTEDCOLUMN,(WPARAM)iCol,0)
#endif

#ifndef HDF_SORTUP
   #define HDF_SORTUP 0x0400
#endif

#ifndef HDF_SORTDOWN
   #define HDF_SORTDOWN 0x0200
#endif

#define WM_XAILER1   WM_XAILER + 1

//------------------------------------------------------------------------------

HB_FUNC_STATIC( FREEITEMMEMORY )
{
   hb_gcGripDrop( (PHB_ITEM) hb_parnl( 1 ) );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_GETSELECTION )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd = GetHandleOf( Self );
   PHB_ITEM pItems = hb_itemArrayNew( 0 );
   int nIndex = -1;
   LV_ITEM lvi;

   memset( &lvi, 0, sizeof( lvi ) );

   while( ( nIndex = ListView_GetNextItem( hWnd, nIndex,
                           MAKELPARAM( LVNI_ALL | LVNI_SELECTED, 0 ) ) ) > -1 )
      {
      lvi.iItem = nIndex;
      lvi.iSubItem = 0;
      lvi.mask = LVIF_PARAM;
      if( ListView_GetItem( hWnd, &lvi ) )
         hb_arrayAdd( pItems, (PHB_ITEM) lvi.lParam );
      }
   hb_itemReturnForward( pItems );
   hb_itemRelease( pItems );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_GETFOCUSED )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd = GetHandleOf( Self );
   int nIndex = ListView_GetNextItem( hWnd, -1, LVNI_FOCUSED );

   if( nIndex > -1 )
      {
      LV_ITEM lvi;
      lvi.iItem = nIndex;
      lvi.iSubItem = 0;
      lvi.mask = LVIF_PARAM;
      if( ListView_GetItem( hWnd, &lvi ) )
         hb_itemReturn( (PHB_ITEM) lvi.lParam );
      }
}

//------------------------------------------------------------------------------

static void CheckState( HWND hWnd, POINT pt )
{
   LVHITTESTINFO pinfo;
   int nIndex;

   memset( &pinfo, 0, sizeof( pinfo ) );
   pinfo.pt = pt;
   nIndex = ListView_HitTest( hWnd, &pinfo );
   if( nIndex > -1 )
      {
      LVITEM lvi;
      memset( &lvi, 0, sizeof( lvi ) );
      lvi.mask = LVIF_PARAM;
      lvi.iItem = nIndex;
      lvi.iSubItem = 0;
      if( ListView_GetItem( hWnd, &lvi ) && lvi.lParam )
         PostMessage( hWnd, WM_XAILER1, 100 + ListView_GetCheckState( hWnd, nIndex ), (LPARAM) lvi.lParam );
      }
}

//------------------------------------------------------------------------------

static long PaintEvent( LPNMLVCUSTOMDRAW lplvcd, PHB_ITEM Self, int nDrawStage )
{
   PHB_DYNS pMethod = hb_dynsymFindName( "OnDrawCell" );
   PHB_ITEM pclrText, pclrPane, pRect, pTemp;
   int nResult;

   if( pMethod )
   {
      pclrText = hb_itemPutNL( NULL, (long) lplvcd->clrText );
      pclrPane = hb_itemPutNL( NULL, (long) lplvcd->clrTextBk );
      pRect    = hb_itemArrayNew( 4 );

      pTemp = hb_itemPutNL( NULL, lplvcd->nmcd.rc.left );
      hb_itemArrayPut( pRect, 1, pTemp);
      hb_itemRelease( pTemp );

      pTemp = hb_itemPutNL( NULL, lplvcd->nmcd.rc.top );
      hb_itemArrayPut( pRect, 2, pTemp);
      hb_itemRelease( pTemp );

      pTemp = hb_itemPutNL( NULL, lplvcd->nmcd.rc.right );
      hb_itemArrayPut( pRect, 3, pTemp);
      hb_itemRelease( pTemp );

      pTemp = hb_itemPutNL( NULL, lplvcd->nmcd.rc.bottom );
      hb_itemArrayPut( pRect, 4, pTemp);
      hb_itemRelease( pTemp );

      hb_vmPushDynSym( pMethod );
      hb_vmPush( Self );
      hb_vmPush( (PHB_ITEM) lplvcd->nmcd.lItemlParam );
      if ( lplvcd->nmcd.dwDrawStage & CDDS_SUBITEM )
         hb_vmPushLong( lplvcd->iSubItem + 1 );
      else
         hb_vmPushLong( 0 );
      hb_vmPushItemRef( pclrText );
      hb_vmPushItemRef( pclrPane );
      hb_vmPushLong( (long) lplvcd->nmcd.hdc );
      hb_vmPush( pRect );
      hb_vmPushLong( nDrawStage );
      hb_vmSend( 7 );
      lplvcd->clrText = hb_itemGetNL( pclrText );
      lplvcd->clrTextBk = hb_itemGetNL( pclrPane );
      hb_itemRelease( pclrText );
      hb_itemRelease( pclrPane );
      hb_itemRelease( pRect );

      pTemp = hb_stackReturnItem();

      if ( HB_IS_NUMERIC ( pTemp ) )
      {
         switch( nResult = hb_itemGetNL( pTemp ) )
         {
         case 0: // Notify subitems
            return CDRF_NOTIFYSUBITEMDRAW;
         case 1: // Color changed
            return CDRF_NEWFONT;
         case 2: // Completely manual painting
            return CDRF_SKIPDEFAULT;
         default:
            return nResult;
         }
      }
   }
   return CDRF_DODEFAULT;
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_NOTIFY )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPNMHDR nmhdr = (LPNMHDR) hb_parnl( 2 );
   HWND hWnd = nmhdr->hwndFrom;

   hb_ret();

   switch ( nmhdr->code )
      {
      case NM_CLICK:
         {
         POINT pt;
         GetCursorPos( &pt );
         ScreenToClient( hWnd, &pt );
         XA_ObjSendNL2( Self, "OnClick", pt.x, pt.y );
         CheckState( hWnd, pt );
         break;
         }
//      case NM_DBLCLK:
//         {
//         POINT pt;
//         GetCursorPos( &pt );
//         ScreenToClient( hWnd, &pt );
//         XA_ObjSendNL2( Self, "OnDblClick", pt.x, pt.y );
//         CheckState( hWnd, pt );
//         break;
//         }
		case NM_RCLICK:
         {
         POINT pt;
         GetCursorPos( &pt );
         ScreenToClient( hWnd, &pt );
         XA_ObjSendNL2( Self, "OnRClick", pt.x, pt.y );
         break;
         }
      case NM_CUSTOMDRAW:
         {
         if ( XA_EventAssigned( Self, "OnDrawCell" ) )
            {
            LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW) hb_parnl( 2 );
            long nResult = CDRF_DODEFAULT;
            switch( lplvcd->nmcd.dwDrawStage )
               {
               case CDDS_PREPAINT:
                  nResult = CDRF_NOTIFYITEMDRAW;
                  break;
               case CDDS_ITEMPREPAINT:
               case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
                  nResult = PaintEvent( lplvcd, Self, CDDS_ITEMPREPAINT );
                  break;
               case CDDS_ITEMPOSTPAINT:
               case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT:
                  nResult = PaintEvent( lplvcd, Self, CDDS_ITEMPOSTPAINT );
                  break;
               }
            hb_retnl( nResult );
            }
         break;
         }
      case LVN_COLUMNCLICK:
         {
         LPNMLISTVIEW pGetInfo = (LPNMLISTVIEW) nmhdr;
         if( pGetInfo )
            {
            int nCol = pGetInfo->iSubItem;
            XA_ObjSendNL( Self, "ColumnClick", nCol + 1 );
            }
         break;
         }
      case LVN_DELETEITEM:
         {
         LPNMLISTVIEW pGetInfo = (LPNMLISTVIEW) nmhdr;
         if( pGetInfo && pGetInfo->lParam && !XA_ObjGetL( Self, "lOnZap" ))
               XA_ObjSendItem( Self, "OnDeleteItem", (PHB_ITEM) pGetInfo->lParam );
         break;
         }
      case LVN_ITEMCHANGED:
         {
         LPNMLISTVIEW pGetInfo = (LPNMLISTVIEW) nmhdr;
         if( pGetInfo && pGetInfo->lParam )
            XA_ObjSendItem( Self, "OnItemChanged", (PHB_ITEM) pGetInfo->lParam );
         break;
         }
      case LVN_BEGINLABELEDIT:
         {
         LPNMLVDISPINFOA pGetInfo = (LPNMLVDISPINFOA) nmhdr;
         if( pGetInfo  )
            {
            LVITEM item = pGetInfo->item;
            if( item.lParam )
               {
               XA_ObjSendItem( Self, "OnBeginLabelEdit", (PHB_ITEM) item.lParam );
               hb_retl( HB_ISLOG( -1 ) && hb_parl( -1 ) );
               }
            }
         break;
         }
      case LVN_ENDLABELEDIT:
         {
         LPNMLVDISPINFOA pGetInfo = (LPNMLVDISPINFOA) nmhdr;
         if( pGetInfo  )
            {
            LVITEM item = pGetInfo->item;
            if( item.lParam && item.pszText )
               {
               XA_ObjSendC( (PHB_ITEM) item.lParam, "_cText", item.pszText );
               XA_ObjSendItem( Self, "OnEndLabelEdit", (PHB_ITEM) item.lParam );
               hb_retl( TRUE );
               }
            }
         break;
         }
      case LVN_GETINFOTIP:
         {
         LPNMLVGETINFOTIP pGetInfoTip = (LPNMLVGETINFOTIP) nmhdr;
         if( pGetInfoTip )
            {
            LVITEM lvi;
            memset( &lvi, 0, sizeof( lvi ) );
            lvi.mask = LVIF_PARAM;
            lvi.iItem = pGetInfoTip->iItem;
            lvi.iSubItem = 0;
            if( ListView_GetItem( hWnd, &lvi ) && lvi.lParam )
               pGetInfoTip->pszText = XA_ObjGetC( (PHB_ITEM) lvi.lParam, "cTooltip" );
            }
         break;
         }
      case LVN_KEYDOWN:
         {
            LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN) nmhdr;
            if( pnkd->wVKey == VK_SPACE )
               {
               LVITEM lvi;
               memset( &lvi, 0, sizeof( lvi ) );
               lvi.mask = LVIF_PARAM;
               lvi.iItem = ListView_GetNextItem( hWnd, -1, LVNI_FOCUSED );
               lvi.iSubItem = 0;
               if( ListView_GetItem( hWnd, &lvi ) && lvi.lParam )
                  PostMessage( hWnd, WM_XAILER1, 100 + ListView_GetCheckState( hWnd, lvi.iItem ), (LPARAM) lvi.lParam );
               }
            XA_ObjSendNL( Self, "OnKeyDown", pnkd->wVKey );
            break;
         }
      }
}

//--------------------------------------------------------------------------

static int CALLBACK SortByColAsc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
{
   HWND hWnd = (HWND) lParamSort;
   int nCol = ListView_GetSelectedColumn( hWnd );
   char cBuffer1[ 256 ];
   char cBuffer2[ 256 ];

   memset( &cBuffer1, 0, 256 );
   memset( &cBuffer2, 0, 256 );
   ListView_GetItemText( hWnd, lParam1, nCol, (LPSTR) &cBuffer1, 255 );
   ListView_GetItemText( hWnd, lParam2, nCol, (LPSTR) &cBuffer2, 255 );
   return strcmp( cBuffer1, cBuffer2 );
}

//--------------------------------------------------------------------------

static int CALLBACK SortByColDes( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
{
   HWND hWnd = (HWND) lParamSort;
   int nCol = ListView_GetSelectedColumn( hWnd );
   char cBuffer1[ 256 ];
   char cBuffer2[ 256 ];

   memset( &cBuffer1, 0, 256 );
   memset( &cBuffer2, 0, 256 );
   ListView_GetItemText( hWnd, lParam1, nCol, (LPSTR) &cBuffer1, 255 );
   ListView_GetItemText( hWnd, lParam2, nCol, (LPSTR) &cBuffer2, 255 );
   return strcmp( cBuffer2, cBuffer1 );
}

//--------------------------------------------------------------------------

static int SetColSortInfo( HWND hWnd, int nCol )
{
   HDITEM hdi;
   int nSort;
   int nFor, nCount;

   hdi.mask = HDI_FORMAT;

   SendMessage( hWnd, HDM_GETITEM, nCol, (LPARAM) &hdi );

   if( hdi.fmt & HDF_SORTUP ) // Estaba en Ascendente
      {
      hdi.fmt &= ~HDF_SORTUP;
      hdi.fmt |= HDF_SORTDOWN;
      nSort = -1;
      }
   else if( hdi.fmt & HDF_SORTDOWN ) // Estaba en descendente
      {
      hdi.fmt &= ~HDF_SORTDOWN;
      hdi.fmt |= HDF_SORTUP;
      nSort = 1;
      }
   else
      {
      hdi.fmt |= HDF_SORTUP;
      nSort = 1;
      }

   nCount = SendMessage( hWnd, HDM_GETITEMCOUNT, 0, 0 );

   for ( nFor = 0; nFor < nCount; nFor ++ )
      if( nFor != nCol )
         {
         HDITEM hdi;
         hdi.mask = HDI_FORMAT;
         SendMessage( hWnd, HDM_GETITEM, nFor, (LPARAM) &hdi );
         hdi.fmt &= ~HDF_SORTUP;
         hdi.fmt &= ~HDF_SORTDOWN;
         SendMessage( hWnd, HDM_SETITEM, nFor, (LPARAM) &hdi );
         }

   SendMessage( hWnd, HDM_SETITEM, nCol, (LPARAM) &hdi );
   return nSort;
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_SORT )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd     = GetHandleOf( Self );
   HWND hHeader  = (HWND) SendMessage( hWnd, LVM_GETHEADER, 0, 0 );
   int nCol      = hb_parni( 1 ) - 1;
   int nSort     = 1;
   PFNLVCOMPARE pfnCompare;

   if( hHeader )
      nSort = SetColSortInfo( hHeader, nCol );

   if( nSort == 1 )
      pfnCompare = SortByColAsc;
   else
      pfnCompare = SortByColDes;

   ListView_SetSelectedColumn( hWnd, nCol );

   hb_retl( ListView_SortItemsEx( hWnd, pfnCompare, hWnd ) );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_HITTEST )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd     = GetHandleOf( Self );
   LVHITTESTINFO pinfo;
   int nItem;

   pinfo.pt.x = hb_parnl( 1 );
   pinfo.pt.y = hb_parnl( 2 );

   nItem = ListView_HitTest( hWnd, &pinfo );

   if ( ( nItem > -1 ) && ( pinfo.flags & LVHT_ONITEM ) )
      {
      LVITEM lvi;
      memset( &lvi, 0, sizeof( lvi ) );
      lvi.mask = LVIF_PARAM;
      lvi.iItem = nItem;
      lvi.iSubItem = 0;
      if( ListView_GetItem( hWnd, &lvi ) && lvi.lParam )
         hb_itemReturn( (PHB_ITEM) lvi.lParam );
      else
         hb_ret();
      }
   else
      hb_ret();

}

//------------------------------------------------------------------------------

#ifndef LVBKIF_FLAG_TILEOFFSET
#define LVBKIF_FLAG_TILEOFFSET 0x00000100
#endif

HB_FUNC_STATIC( XLISTVIEW_UPDATEBKIMAGE )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWnd     = GetHandleOf( Self );
   BOOL bSuccess;
   LVBKIMAGE lvbki;
   PHB_ITEM oPic = XA_ObjGetItemCopy( Self, "oBkImage" );

   memset( &lvbki, 0, sizeof( LVBKIMAGE ) );
   if( HB_IS_OBJECT( oPic ) )
   {
      lvbki.ulFlags = LVBKIF_SOURCE_HBITMAP;
      lvbki.hbm = (HBITMAP) GetHandleOf( oPic );
      if( hb_parldef( 1, HB_FALSE ) )
         lvbki.ulFlags |= LVBKIF_STYLE_TILE;
      else
         lvbki.ulFlags |=  LVBKIF_STYLE_NORMAL;

      lvbki.xOffsetPercent = hb_parnldef( 2, 0 );
      lvbki.yOffsetPercent = hb_parnldef( 3, 0 );
      if( lvbki.xOffsetPercent || lvbki.yOffsetPercent )
         lvbki.ulFlags |= LVBKIF_FLAG_TILEOFFSET;
   }
   else
      lvbki.ulFlags = LVBKIF_SOURCE_NONE;

   bSuccess = SendMessage( hWnd, LVM_SETBKIMAGE, 0, (LPARAM) &lvbki );
   hb_retl( bSuccess );
   hb_itemRelease( oPic );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_FINDITEM )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWndList = GetHandleOf( Self );
   HB_INT nStart = HB_ISNUM( 2 ) ? hb_parni( 2 ) : -1;
   LVFINDINFO lvfi;

   memset( &lvfi, 0, sizeof( lvfi ) );
   lvfi.flags = LVFI_PARTIAL | LVFI_STRING;
   lvfi.psz = hb_parc( 1 );

   hb_retnl( ListView_FindItem( hWndList, nStart, &lvfi ) );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_SETEXSTYLE )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWndList = GetHandleOf( Self );
   HB_BOOL lCheckBoxes     = XA_ObjGetL( Self, "FlCheckBoxes" );
   HB_BOOL lDoubleBuffer   = XA_ObjGetL( Self, "FlDoubleBuffer" );
   HB_BOOL lFullRowSelect  = XA_ObjGetL( Self, "FlFullRowSelect" );
   HB_BOOL lGridLines      = XA_ObjGetL( Self, "FlGridLines" );
   HB_BOOL lHeaderDragDrop = XA_ObjGetL( Self, "FlHeaderDragDrop" );
   HB_BOOL lHotTrackSelect = XA_ObjGetL( Self, "FlHotTrackSelect" );
   HB_BOOL lLabeltip       = XA_ObjGetL( Self, "FlLabelTip" );
   HB_BOOL lTooltips       = XA_ObjGetL( Self, "FlTooltips" );
   WPARAM dwExMask         = LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES |
                             LVS_EX_HEADERDRAGDROP | LVS_EX_TRACKSELECT | LVS_EX_LABELTIP | LVS_EX_INFOTIP;
   LPARAM dwExStyle        = 0;

   if( lCheckBoxes )
      dwExStyle |= LVS_EX_CHECKBOXES;

   if( lDoubleBuffer )
      dwExStyle |= LVS_EX_DOUBLEBUFFER;

   if( lFullRowSelect )
      dwExStyle |= LVS_EX_FULLROWSELECT;

   if( lGridLines )
      dwExStyle |= LVS_EX_GRIDLINES;

   if( lHeaderDragDrop )
      dwExStyle |= LVS_EX_HEADERDRAGDROP;

   if( lHotTrackSelect )
      dwExStyle |= LVS_EX_TRACKSELECT;

   if( lLabeltip )
      dwExStyle |= LVS_EX_LABELTIP;

   if( lTooltips )
      dwExStyle |= LVS_EX_INFOTIP;

   hb_retnl( ListView_SetExtendedListViewStyleEx( hWndList, dwExMask, dwExStyle ) );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_SETITEMFOCUS )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWndList = GetHandleOf( Self );
   HB_LONG nItem = hb_parnl( 1 );

   ListView_EnsureVisible( hWndList, nItem, HB_FALSE );
   ListView_SetItemState( hWndList, nItem, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED );

   hb_ret();
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XLISTVIEW_SETITEMNOFOCUS )
{
   PHB_ITEM Self = hb_stackSelfItem();
   HWND hWndList = GetHandleOf( Self );
   HB_LONG nItem = hb_parnl( 1 );
   HB_UINT nFlag = ListView_GetItemState( hWndList, nItem, LVIS_SELECTED );

   nFlag ^= LVNI_SELECTED;

   ListView_SetItemState( hWndList, nItem, nFlag, LVIS_SELECTED );
   hb_ret();
}

//------------------------------------------------------------------------------

#pragma ENDDUMP
