/////////////////////////////////////////////////////////////////////////////
// Name:        src/os2/listctrl.cpp
// Purpose:     wxListCtrl
// Author:      David Webster
// Modified by:
// Created:     01/21/03
// Copyright:   (c) David Webster
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ============================================================================
// declarations
// ============================================================================

// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_LISTCTRL

#ifndef WX_PRECOMP
    #include "wx/app.h"
    #include "wx/intl.h"
    #include "wx/log.h"
    #include "wx/settings.h"
    #include "wx/dcclient.h"
    #include "wx/textctrl.h"
#endif

#include "wx/imaglist.h"
#include "wx/listctrl.h"

#include "wx/os2/private.h"

//
// FIELDOFFSET in DETAIL view as defined in the OS2TK45 simply doesn't work
// We use this, which does!
//
#undef  FIELDOFFSET
#define FIELDOFFSET(type, field)    ((ULONG)&(((type *)0)->field))

// ----------------------------------------------------------------------------
// private helper classes
// ----------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////////
// STRUCT SMYRECORD
//   Under OS/2 we have to use our own RECORDCORE based struct if we have
//   user data to store in a PM Container Control (and wxListCtrl is a PM
//   Container in ICON, NAME, TEXT or DETAIL view). m_ulUserData is a four
//   byte value containing a pointer to our CListIntemInternalData class
//   instance.
//
//   And now for the big time OS/2 Kludge.  In traditional OS/2 PM
//   applications using containers, programmers determine BEFORE creation
//   how many records the view will have, initially, and how many columns
//   the detail view of the container will have, as the container represents
//   a known data block.  Thus the OS/2 PM CV_DETAIL view, i.e.
//   the wxWidgets wxLC_REPORT view, relies on STATIC structure for its
//   columnar data.  It gets the data to display by telling it the specific
//   offset of the field in the struct containing the displayable data.  That
//   data has be of OS/2 Types, PSZ (char string), CDATE or CTIME format.
//   wxWidgets is dynamic in nature, however.  We insert columns, one at a
//   time and do not know how many until the app is done inserting them. So
//   for OS/2 I have to set a max allowable since they are fixed.  We return
//   an error to the app if they include more than we can handle.
//
//   For example to display the data "Col 4 of Item 6" in a report view, I'd
//   have to do:
//   pRecord->m_pzColumn4 = "Col 4 of Item 6";
//   pField->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn4);
//   and then call the PM API to set it.
//
//   This really stinks but I can't use a pointer to another struct as the
//   FIELDOFFSET call could only tell OS/2 the four byte value offset of
//   pointer field and it would display giberish in the column.
/////////////////////////////////////////////////////////////////////////////
typedef struct _MYRECORD
{
    RECORDCORE                      m_vRecord;
    unsigned long                   m_ulItemId;
    unsigned long                   m_ulUserData; //actually a pointer value to real data (a CListItemInternalData class instance)
    PSZ                             m_pzColumn1;
    PSZ                             m_pzColumn2;
    PSZ                             m_pzColumn3;
    PSZ                             m_pzColumn4;
    PSZ                             m_pzColumn5;
    PSZ                             m_pzColumn6;
    PSZ                             m_pzColumn7;
    PSZ                             m_pzColumn8;
    PSZ                             m_pzColumn9;
    PSZ                             m_pzColumn10;
} MYRECORD, *PMYRECORD;

/////////////////////////////////////////////////////////////////////////////
// CLASS CListItemInternalData
//
// Problem:
//  The MSW version had problems with SetTextColour() et al as the
//  CListItemAttr's were stored keyed on the item index. If a item was
//  inserted anywhere but the end of the list the text attributes
//  (colour etc) for the following items were out of sync.
//
// Solution:
//  Under MSW the only way to associate data with a
//  List item independent of its position in the list is to store a pointer
//  to it in its lParam attribute. However user programs are already using
//  this (via the SetItemData() GetItemData() calls).
//
//  However what we can do is store a pointer to a structure which contains
//  the attributes we want *and* a lParam for the users data, e.g.
//
//  class CListItemInternalData
//  {
//  public:
//       GuiAdvCtrl_CListItemAttr* pAttr;
//       long                      lParam; // user data
//  };
//
//  To conserve memory, a CListItemInternalData is only allocated for a
//  LV_ITEM if text attributes or user data(lparam) are being set.
//
//  For OS/2, the lParam value points to whatever actual data we have
/////////////////////////////////////////////////////////////////////////////
class CListItemInternalData
{
public:

    CListItemInternalData(): m_pAttr(NULL)
                            ,m_lParam(0)
    {}

    ~CListItemInternalData()
    {
        wxDELETE(m_pAttr);
    }

    wxListItemAttr*                 m_pAttr;
    WXLPARAM                        m_lParam; // user data
    PMYRECORD                       m_pMyRecord; // so we can set the m_ulUserData to 0 when this is deleted
}; // end of CLASS CListItemInternalData

/////////////////////////////////////////////////////////////////////////////
// STRUCT SInternalDataSort
//
// Sort items.
//
// fn is a function which takes 3 long arguments: item1, item2, data.
// item1 is the long data associated with a first item (NOT the index).
// item2 is the long data associated with a second item (NOT the index).
// data is the same value as passed to SortItems.
//
// The return value is a negative number if the first item should precede the
// second item, a positive number of the second item should precede the first,
// or zero if the two items are equivalent.
//
// data is arbitrary data to be passed to the sort function.
//
// Internal structures for proxying the user compare function
// so that we can pass it the *real* user data
/////////////////////////////////////////////////////////////////////////////
typedef struct internalDataSort
{
    wxListCtrlCompare               m_fnUser;
    long                            m_lData;
} SInternalDataSort; // end of STRUCT SInternalDataSort

// ----------------------------------------------------------------------------
// private helper functions
// ----------------------------------------------------------------------------

/////////////////////////////////////////////////////////////////////////////
//
// FindOS2ListFieldByColNum
//
//  There is no way, under OS/2 to get a field in a container by index,
//  directly, so you must get the first one, then cycle through the list
//  until you get to where you want to be.
//
// PARAMETERS
//  hWnd   -- window handle of container to search
//  lIndex -- index to set
//
// RETURN VALUE
//  pointer to the FIELDINFO struct at the index in the container record
//
/////////////////////////////////////////////////////////////////////////////
PFIELDINFO FindOS2ListFieldByColNum (
  HWND                              hWnd
, long                              lIndex
)
{
    PFIELDINFO                      pFieldInfo = NULL;
    CNRINFO                         vCnrInfo;
    ULONG                           i;

    if (!::WinSendMsg( hWnd
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return NULL;
    for (i = 0; i < vCnrInfo.cFields; i++)
    {
        if (i == 0)
            pFieldInfo = (PFIELDINFO)PVOIDFROMMR(::WinSendMsg( hWnd
                                                              ,CM_QUERYDETAILFIELDINFO
                                                              ,MPFROMP(pFieldInfo)
                                                              ,(MPARAM)CMA_FIRST
                                                             ));
        else
            pFieldInfo = (PFIELDINFO)PVOIDFROMMR(::WinSendMsg( hWnd
                                                              ,CM_QUERYDETAILFIELDINFO
                                                              ,MPFROMP(pFieldInfo)
                                                              ,(MPARAM)CMA_NEXT
                                                             ));
        if (!pFieldInfo)
            return NULL;
        if (i == (ULONG)lIndex)
            break;
    }
    if (!pFieldInfo)
        return NULL;
    return pFieldInfo;
} // end of FindOS2ListFieldByColNum

/////////////////////////////////////////////////////////////////////////////
//
// FindOS2ListRecordByID
//
//  There is no way, under OS/2 to get a record in a container by index,
//  directly, so you must get the first one, then cycle through the list
//  until you get to where you want to be.
//
// PARAMETERS
//  hWnd    -- window handle of container to search
//  lItemId -- index to set
//
// RETURN VALUE
//  pointer to the internal RECORDCORE struct at the index in the container
//
/////////////////////////////////////////////////////////////////////////////
PMYRECORD FindOS2ListRecordByID (
  HWND                              hWnd
, long                              lItemId
)
{
    PMYRECORD                       pRecord = NULL;
    CNRINFO                         vCnrInfo;
    unsigned long                   i;

    if (!::WinSendMsg( hWnd
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return NULL;
    for (i = 0; i < vCnrInfo.cRecords; i++)
    {
        if (i == 0)
            pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( hWnd
                                                          ,CM_QUERYRECORD
                                                          ,MPFROMP(pRecord)
                                                          ,MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER)
                                                         ));
        else
            pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( hWnd
                                                          ,CM_QUERYRECORD
                                                          ,MPFROMP(pRecord)
                                                          ,MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER)
                                                         ));
        if (!pRecord)
            return NULL;
        if (pRecord->m_ulItemId == (ULONG)lItemId)
            break;
    }
    return pRecord;
} // end of FindOS2ListRecordByID

/////////////////////////////////////////////////////////////////////////////
//
// BumpRecordIds
//
//  Since OS/2 does not keep native record id's but wx insists on inserting
//  and selecting via ID's, when we insert a record in the middle we need
//  to bump the id's of each record after the one we just inserted.
//
// PARAMETERS
//  hWnd    -- window handle of container to search
//  pRecord -- record after which we starting bumping id's
//
// RETURN VALUE
//  none
//
/////////////////////////////////////////////////////////////////////////////
void BumpRecordIds (
  HWND                              hWnd
, PMYRECORD                         pRecord
)
{
    while(pRecord)
    {
        pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( hWnd
                                                      ,CM_QUERYRECORD
                                                      ,MPFROMP(pRecord)
                                                      ,MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER)
                                                     ));
        if (pRecord)
            pRecord->m_ulItemId++;
    }
} // end of BumpRecordIds

/////////////////////////////////////////////////////////////////////////////
//
// GetInternalData
//
//  Get the internal data given a handle and an id
//
// PARAMETERS
//  hWnd    -- window handle to the control in which item is located
//  lItemId -- ID to get
//
// RETURN VALUE
//  pointer to the internal data
//
// Note:
//  Under OS/2 PM a container item cannot be obtained via a simple index or
//  id retrieval.  We have to walk the record list if we are looking for
//  a record at a specific index location
/////////////////////////////////////////////////////////////////////////////
CListItemInternalData* GetInternalData (
  HWND                              hWnd
, long                              lItemId
)
{
    PMYRECORD                       pRecord = FindOS2ListRecordByID( hWnd
                                                                    ,lItemId
                                                                   );
    //
    // Internal user data is stored AFTER the last field of the RECORDCORE
    //
    if (!pRecord)
        return NULL;
    return((CListItemInternalData *)(pRecord->m_ulUserData));
} // end of GetInternalData

/////////////////////////////////////////////////////////////////////////////
//
// GetInternalData
//
//  Get the internal data given a pointer to a list control and an id
//
// PARAMETERS
//  pCtl    -- pointer to control inwhich item is located
//  lItemId -- ID to get
//
// RETURN VALUE
//  pointer to the internal data
//
/////////////////////////////////////////////////////////////////////////////
CListItemInternalData* GetInternalData (
  wxListCtrl*                       pCtl
, long                              lItemId
)
{
    return(GetInternalData( (HWND)pCtl->GetHWND()
                           ,lItemId
                          ));
} // end of GetInternalData

/////////////////////////////////////////////////////////////////////////////
//
// DeleteInternalData
//
//  Delete the internal data for a record
//
// PARAMETERS
//  pCtl    -- pointer to the list control containing the record
//  lItemId -- the record index to delete the internal data from
//
// RETURN VALUE
//  pointer to the internal data attribute
//
/////////////////////////////////////////////////////////////////////////////
void DeleteInternalData (
  wxListCtrl*                       pCtl
, long                              lItemId
)
{
    CListItemInternalData*          pData = GetInternalData( pCtl
                                                            ,lItemId
                                                           );
    if (pData)
    {
        if (pData->m_pMyRecord)
            pData->m_pMyRecord->m_ulUserData = 0;
        delete pData;
    }
} // end of DeleteInternalData

// #pragma page   "GetInternalDataAttr"
/////////////////////////////////////////////////////////////////////////////
//
// GetInternalDataAttr
//
//  Get the internal data item attribute given a pointer to a list control
//  and an id
//
// PARAMETERS
//  pCtl    -- pointer to control to set
//  lItemId -- ID to set
//
// RETURN VALUE
//  pointer to the internal data attribute
//
/////////////////////////////////////////////////////////////////////////////
wxListItemAttr* GetInternalDataAttr (
  wxListCtrl*                       pCtl
, long                              lItemId
)
{
    CListItemInternalData*          pData = GetInternalData( pCtl
                                                            ,lItemId
                                                           );

    if (pData)
        return(pData->m_pAttr);
    else
        return NULL;
} // end of GetInternalDataAttr

/////////////////////////////////////////////////////////////////////////////
//
// InternalDataCompareFunc
//
//  This is compare function we pass to PM.  It wraps the real compare
//  function in SInternalDataSort
//
// PARAMETERS
//  p1       -- is the first record structure to compare
//  p2       -- is the second record structure to compare
//  lStorage -- is the same value as passed to SortItems.
//
// RETURN VALUE
//  pointer to the internal data attribute
//
/////////////////////////////////////////////////////////////////////////////
SHORT EXPENTRY InternalDataCompareFunc (
  PMYRECORD                         p1
, PMYRECORD                         p2
, PVOID                             pStorage
)
{
    SInternalDataSort*              pInternalData = (SInternalDataSort *)pStorage;
    CListItemInternalData*          pData1 = (CListItemInternalData *)p1->m_ulUserData;
    CListItemInternalData*          pData2 = (CListItemInternalData *)p2->m_ulUserData;
    long                            lD1 = (pData1 == NULL ? 0 : (long)pData1->m_lParam);
    long                            lD2 = (pData2 == NULL ? 0 : (long)pData2->m_lParam);

    return(pInternalData->m_fnUser( lD1
                                   ,lD2
                                   ,pInternalData->m_lData
                                  ));
} // end of InternalDataCompareFunc

/////////////////////////////////////////////////////////////////////////////
//
// ConvertFromOS2ListItem
//
//  Convert from an internal PM List item to a Toolkit List item
//
// PARAMETERS
//  hWndListCtrl -- the control's windows handle
//  rInfo        -- the library list control to convert to
//  pRecord      -- the OS list control to convert from
//
// RETURN VALUE
//  none
//
/////////////////////////////////////////////////////////////////////////////
void ConvertFromOS2ListItem ( HWND hWndListCtrl,
                              wxListItem& rInfo,
                              PMYRECORD pRecord )
{
    CListItemInternalData* pInternaldata = (CListItemInternalData *)pRecord->m_ulUserData;
    bool bNeedText = false;

    if (pInternaldata)
        rInfo.SetData(pInternaldata->m_lParam);

    rInfo.SetMask(0);
    rInfo.SetState(0);
    rInfo.SetStateMask(0);
    rInfo.SetId((long)pRecord->m_ulItemId);
    if (hWndListCtrl != 0)
    {
        pRecord = FindOS2ListRecordByID( hWndListCtrl
                                        ,rInfo.GetId()
                                       );
    }

    //
    // The wxListItem class is really set up to handle the WIN32 list item
    // and OS/2 are not as complicated.  Just set both state members to the
    // same thing under OS/2
    //
    if (pRecord->m_vRecord.flRecordAttr & CRA_DROPONABLE)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_DROPHILITED);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_DROPHILITED);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_SELECTED)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_SELECTED);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_SELECTED);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_DISABLED)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_DISABLED);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_DISABLED);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_FILTERED)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_FILTERED);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_FILTERED);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_INUSE)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_INUSE);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_INUSE);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_PICKED)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_PICKED);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_PICKED);
    }
    if (pRecord->m_vRecord.flRecordAttr & CRA_SOURCE)
    {
        rInfo.SetStateMask(rInfo.m_stateMask | wxLIST_STATE_SOURCE);
        rInfo.SetState(rInfo.m_state | wxLIST_STATE_SOURCE);
    }

    if (pRecord->m_vRecord.pszText != (PSZ)NULL)
    {
        rInfo.SetMask(rInfo.GetMask() | wxLIST_MASK_TEXT);
        rInfo.SetText(pRecord->m_vRecord.pszText);
    }
    if (pRecord->m_vRecord.pszIcon != (PSZ)NULL ||
        pRecord->m_vRecord.pszName != (PSZ)NULL)
    {
        rInfo.SetMask(rInfo.GetMask() | wxLIST_MASK_IMAGE);
        rInfo.SetImage(pRecord->m_vRecord.hptrIcon);
    }
    if (pRecord->m_ulUserData)
        rInfo.SetMask(rInfo.GetMask() | wxLIST_MASK_DATA);
} // end of ConvertFromOS2ListItem

/////////////////////////////////////////////////////////////////////////////
//
// ConvertToOS2Flags
//
//  Convert from an library states to OS states
//
// PARAMETERS
//  lState       -- the state
//  pRecord      -- the OS list control to use
//
// RETURN VALUE
//  none
//
/////////////////////////////////////////////////////////////////////////////
void ConvertToOS2Flags (
  long                              lState
, PMYRECORD                         pRecord
)
{
    if (lState & wxLIST_STATE_DROPHILITED)
        pRecord->m_vRecord.flRecordAttr |= CRA_DROPONABLE;
    if (lState & wxLIST_STATE_SELECTED)
        pRecord->m_vRecord.flRecordAttr |= CRA_SELECTED;
    if (lState & wxLIST_STATE_DISABLED)
        pRecord->m_vRecord.flRecordAttr |= CRA_DISABLED;
    if (lState & wxLIST_STATE_FILTERED)
        pRecord->m_vRecord.flRecordAttr |= CRA_FILTERED;
    if (lState & wxLIST_STATE_INUSE)
        pRecord->m_vRecord.flRecordAttr |= CRA_INUSE;
    if (lState & wxLIST_STATE_PICKED)
        pRecord->m_vRecord.flRecordAttr |= CRA_PICKED;
    if (lState & wxLIST_STATE_SOURCE)
        pRecord->m_vRecord.flRecordAttr |= CRA_SOURCE;
} // end of ConvertToOS2Flags

/////////////////////////////////////////////////////////////////////////////
//
// ConvertToOS2ListItem
//
//  Convert from a library List item to an internal OS2 List item. We set
//  only the fields we need to set.  Some of them are set by the API when
//  they are added to the container.
//
// PARAMETERS
//  pCtrl      -- the control to use
//  rInfo      -- the item to convert
//  pRecord    -- the OS list control to use, should be zeroed out
//  pFieldinfo -- a field struct that may contain columnar data for detail view
//
// RETURN VALUE
//  none
//
/////////////////////////////////////////////////////////////////////////////
void ConvertToOS2ListItem (
  const wxListCtrl*                 pCtrl
, const wxListItem&                 rInfo
, PMYRECORD                         pRecord
, PFIELDINFO                        pFieldInfo
)
{
    pRecord->m_ulItemId    = (ULONG)rInfo.GetId();
    pRecord->m_vRecord.cb = sizeof(RECORDCORE);
    if (rInfo.GetMask() & wxLIST_MASK_STATE)
    {
        ConvertToOS2Flags( rInfo.m_state
                          ,pRecord
                         );
    }
    if (pCtrl->GetWindowStyleFlag() & wxLC_ICON ||
        pCtrl->GetWindowStyleFlag() & wxLC_SMALL_ICON)
    {
        pRecord->m_vRecord.pszIcon = (char*)rInfo.GetText().c_str();
    }
    if (pCtrl->GetWindowStyleFlag() & wxLC_LIST) // PM TEXT view
    {
        pRecord->m_vRecord.pszText = (char*)rInfo.GetText().c_str();
    }
    //
    // In the case of a report view the text will be the data in the lead column
    // ???? Don't know why, but that is how it works in other ports.
    //
    if (pCtrl->GetWindowStyleFlag() & wxLC_REPORT)
    {
        if (pFieldInfo)
        {
            switch(rInfo.GetColumn())
            {
                case 0:
                    pRecord->m_pzColumn1 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn1);
                    break;

                case 1:
                    pRecord->m_pzColumn2 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn2);
                    break;

                case 2:
                    pRecord->m_pzColumn3 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn3);
                    break;

                case 3:
                    pRecord->m_pzColumn4 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn4);
                    break;

                case 4:
                    pRecord->m_pzColumn5 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn5);
                    break;

                case 5:
                    pRecord->m_pzColumn6 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn6);
                    break;

                case 6:
                    pRecord->m_pzColumn7 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn7);
                    break;

                case 7:
                    pRecord->m_pzColumn8 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn8);
                    break;

                case 8:
                    pRecord->m_pzColumn9 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn9);
                    break;

                case 9:
                    pRecord->m_pzColumn10 = (char*)rInfo.GetText().c_str();
                    pFieldInfo->offStruct = FIELDOFFSET(MYRECORD, m_pzColumn10);
                    break;

                default:
                    wxFAIL_MSG( wxT("wxOS2 does not support more than 10 columns in REPORT view") );
                    break;
            }
        }
    }
    if (rInfo.GetMask() & wxLIST_MASK_IMAGE)
    {
        pRecord->m_vRecord.hptrIcon      = (HPOINTER)rInfo.GetImage();
        pRecord->m_vRecord.hptrMiniIcon  = (HPOINTER)rInfo.m_miniImage;
    }
} // end of ConvertToOS2ListItem

/////////////////////////////////////////////////////////////////////////////
//
// ConvertToOS2ListCol
//
//  Convert from a library List column to an internal PM List column
//
// PARAMETERS
//  lCol   -- the columnd to convert
//  rItem  -- the item to convert
//  pField -- the OS list column to use
//
// RETURN VALUE
//  none
//
/////////////////////////////////////////////////////////////////////////////
void ConvertToOS2ListCol (
  long                              lCol
, const wxListItem&                 rItem
, PFIELDINFO                        pField
)
{
    memset(pField, '\0', sizeof(FIELDINFO));
    pField->cb = sizeof(FIELDINFO);

    //
    // Default some settings
    //
    pField->flData  = CFA_HORZSEPARATOR | CFA_SEPARATOR;
    pField->flTitle = CFA_CENTER;

    if (rItem.GetMask() & wxLIST_MASK_TEXT)
    {
        pField->flData |= CFA_STRING;
        pField->pTitleData = (PVOID)rItem.GetText().c_str(); // text is column title not data
    }
    if (rItem.GetMask() & wxLIST_MASK_FORMAT)
    {
        if (rItem.m_format == wxLIST_FORMAT_LEFT)
            pField->flData |= CFA_LEFT;
        else if (rItem.m_format == wxLIST_FORMAT_RIGHT)
            pField->flData |= CFA_RIGHT;
        else if (rItem.m_format == wxLIST_FORMAT_CENTRE)
            pField->flData |= CFA_CENTER;
    }
    else
        pField->flData |= CFA_CENTER;  // Just ensure the default is centered
    if (rItem.GetMask() & wxLIST_MASK_WIDTH)
    {
        if (!(rItem.GetWidth() == wxLIST_AUTOSIZE ||
             rItem.GetWidth() == wxLIST_AUTOSIZE_USEHEADER))
            pField->cxWidth = rItem.GetWidth();
        // else: OS/2 automatically sets the width if created with the approppriate style
    }

    //
    // Still need to set the actual data
    //
    pField->offStruct = 0;
} // end of ConvertToOS2ListCol

BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
    EVT_PAINT(wxListCtrl::OnPaint)
END_EVENT_TABLE()

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// wxListCtrl construction
// ----------------------------------------------------------------------------

void wxListCtrl::Init ()
{
    m_pImageListNormal     = NULL;
    m_pImageListSmall      = NULL;
    m_pImageListState      = NULL;
    m_bOwnsImageListNormal = false;
    m_bOwnsImageListSmall  = false;
    m_bOwnsImageListState  = false;
    m_lBaseStyle           = 0L;
    m_nColCount            = 0;
    m_pTextCtrl            = NULL;
    m_bAnyInternalData     = false;
    m_bHasAnyAttr          = false;
} // end of wxListCtrl::Init

bool wxListCtrl::Create ( wxWindow* pParent,
                          wxWindowID vId,
                          const wxPoint& rPos,
                          const wxSize& rSize,
                          long lStyle,
                          const wxValidator& rValidator,
                          const wxString& rsName )
{
    int nX = rPos.x;
    int nY = rPos.y;
    int nWidth = rSize.x;
    int nHeight = rSize.y;

#if wxUSE_VALIDATORS
    SetValidator(rValidator);
#endif // wxUSE_VALIDATORS

    SetName(rsName);
    SetWindowStyleFlag(lStyle);
    SetParent(pParent);
    if (nWidth <= 0)
        nWidth = 100;
    if (nHeight <= 0)
        nHeight = 30;
    if (nX < 0)
        nX = 0;
    if (nY < 0)
        nY = 0;

    m_windowId = (vId == -1) ? NewControlId() : vId;

    long lSstyle = WS_VISIBLE | WS_TABSTOP;

    if (GetWindowStyleFlag() & wxCLIP_SIBLINGS)
        lSstyle |= WS_CLIPSIBLINGS;
    m_lBaseStyle = lSstyle;
    if (!DoCreateControl( nX
                         ,nY
                         ,nWidth
                         ,nHeight
                        ))
        return false;
    if (pParent)
        pParent->AddChild(this);
   return true;
} // end of wxListCtrl::Create

bool wxListCtrl::DoCreateControl ( int nX, int nY,
                                   int nWidth, int nHeight )
{
    DWORD lWstyle = m_lBaseStyle;
    long lOldStyle = 0; // Dummy

    CNRINFO vCnrInfo;

    lWstyle |= ConvertToOS2Style( lOldStyle
                                 ,GetWindowStyleFlag()
                                );

    m_hWnd = (WXHWND)::WinCreateWindow( GetParent()->GetHWND()
                                       ,WC_CONTAINER
                                       ,NULL
                                       ,m_lBaseStyle
                                       ,0, 0, 0, 0
                                       ,GetParent()->GetHWND()
                                       ,HWND_BOTTOM
                                       ,(ULONG)m_windowId
                                       ,NULL
                                       ,NULL
                                      );
    if (!m_hWnd)
    {
        return false;
    }

    //
    // Now set the display attributes of the container
    //
    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return false;
    lWstyle = ConvertViewToOS2Style(GetWindowStyleFlag());
    vCnrInfo.flWindowAttr |= lWstyle;
    if (!::WinSendMsg( GetHWND()
                      ,CM_SETCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)CMA_FLWINDOWATTR
                     ))
        return false;

    //
    // And now set needed arrangement flags
    //
    lWstyle = ConvertArrangeToOS2Style(GetWindowStyleFlag());
    if (!::WinSendMsg( GetHWND()
                      ,CM_ARRANGE
                      ,(MPARAM)CMA_ARRANGEGRID
                      ,(MPARAM)lWstyle
                     ))
        return false;
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    SetForegroundColour(GetParent()->GetForegroundColour());
    SubclassWin(m_hWnd);
    SetFont(*wxSMALL_FONT);
    SetXComp(0);
    SetYComp(0);
    SetSize( nX, nY, nWidth, nHeight );
    return true;
} // end of wxListCtrl::DoCreateControl

void wxListCtrl::UpdateStyle ()
{
    if (GetHWND())
    {
        long lDummy;
        DWORD dwStyleNew = ConvertToOS2Style( lDummy, GetWindowStyleFlag() );

        dwStyleNew |= m_lBaseStyle;

        //
        // Get the current window style.
        //
        ULONG dwStyleOld = ::WinQueryWindowULong(GetHWND(), QWL_STYLE);

        //
        // Only set the window style if the view bits have changed.
        //
        if (dwStyleOld != dwStyleNew)
        {
            ::WinSetWindowULong(GetHWND(), QWL_STYLE, dwStyleNew);
        }
    }
} // end of wxListCtrl::UpdateStyle

void wxListCtrl::FreeAllInternalData ()
{
    if (m_bAnyInternalData)
    {
        int n = GetItemCount();
        int i = 0;

        for (i = 0; i < n; i++)
            DeleteInternalData(this, (long)i);
        m_bAnyInternalData = false;
    }
} // end of wxListCtrl::FreeAllInternalData

wxListCtrl::~wxListCtrl ()
{
    FreeAllInternalData();
    if (m_pTextCtrl )
    {
        m_pTextCtrl->SetHWND(0);
        m_pTextCtrl->UnsubclassWin();
        wxDELETE(m_pTextCtrl);
    }

    if (m_bOwnsImageListNormal)
        delete m_pImageListNormal;
    if (m_bOwnsImageListSmall)
        delete m_pImageListSmall;
    if (m_bOwnsImageListState)
        delete m_pImageListState;
} // end of wxListCtrl::~wxListCtrl

// ----------------------------------------------------------------------------
// set/get/change style
// ----------------------------------------------------------------------------

// Add or remove a single window style
void wxListCtrl::SetSingleStyle (
  long                              lStyle
, bool                              bAdd
)
{
    long                            lFlag = GetWindowStyleFlag();

    //
    // Get rid of conflicting styles
    //
    if (bAdd)
    {
        if (lStyle & wxLC_MASK_TYPE)
            lFlag = lFlag & ~wxLC_MASK_TYPE;
        if (lStyle & wxLC_MASK_ALIGN )
            lFlag = lFlag & ~wxLC_MASK_ALIGN;
        if (lStyle & wxLC_MASK_SORT )
            lFlag = lFlag & ~wxLC_MASK_SORT;
    }
    if (lFlag & lStyle)
    {
        if (!bAdd)
            lFlag -= lStyle;
    }
    else
    {
        if (bAdd)
        {
            lFlag |= lStyle;
        }
    }
    m_windowStyle = lFlag;
    UpdateStyle();
} // end of wxListCtrl::SetSingleStyle

// Set the whole window style
void wxListCtrl::SetWindowStyleFlag (
  long                              lFlag
)
{
    m_windowStyle = lFlag;
    UpdateStyle();
} // end of wxListCtrl::SetWindowStyleFlag

long wxListCtrl::ConvertToOS2Style (
  long&                             rOldStyle
, long                              lStyle
) const
{
    long                            lWstyle = 0L;

    //
    // The only styles OS2 uses on creation are auto arrange, read only, and
    // and selection styles.  This lib does not support OS/2 MINIRECORDCORE
    // or VERIFYPOINTER styles
    //
    if (lStyle & wxLC_AUTOARRANGE)
        lWstyle |= CCS_AUTOPOSITION;
    if (lStyle & wxLC_SINGLE_SEL)
        lWstyle |= CCS_SINGLESEL;
    else
        lWstyle |= CCS_EXTENDSEL;
    if (!(lStyle & wxLC_EDIT_LABELS))
        lWstyle |= CCS_READONLY;
    return lWstyle;
} // end of wxListCtrl::ConvertToOS2Style

long wxListCtrl::ConvertArrangeToOS2Style (
  long                              lStyle
)
{
    long                            lWstyle = 0;

    if (lStyle & wxLC_ALIGN_LEFT)
    {
        lWstyle |= CMA_LEFT;
    }

    if (lStyle & wxLC_ALIGN_TOP)
    {
        lWstyle |= CMA_TOP;
    }
    return lWstyle;
} // end of wxListCtrl::ConvertArrangeToOS2Style

long wxListCtrl::ConvertViewToOS2Style (
  long                              lStyle
)
{
    long                            lWstyle = CA_DRAWICON; // we will only use icons

    if (lStyle & wxLC_ICON)
    {
        lWstyle |= CV_ICON;
    }
    if (lStyle & wxLC_SMALL_ICON)
    {
        lWstyle |= (CV_ICON | CV_MINI);
    }
    if (lStyle & wxLC_LIST)
    {
        lWstyle |= CV_TEXT;
    }
    if (lStyle & wxLC_REPORT)
    {
        lWstyle |= CV_DETAIL;
    }
    if (lStyle & wxLC_VIRTUAL)
    {
        lWstyle |= CA_OWNERDRAW;
    }
    if (lStyle & wxLC_AUTOARRANGE)
    {
        lWstyle |= CV_FLOW;
    }
    if (!(lStyle & wxLC_NO_HEADER))
    {
        lWstyle |= CA_DETAILSVIEWTITLES;
    }
    return lWstyle;
} // end of wxListCtrl::ConvertViewToOS2Style

// ----------------------------------------------------------------------------
// accessors
// ----------------------------------------------------------------------------

// Sets the foreground, i.e. text, colour
bool wxListCtrl::SetForegroundColour (const wxColour& rCol)
{
    ULONG ulColor = wxColourToRGB(rCol);

    if (!wxWindow::SetForegroundColour(rCol))
        return false;

    ::WinSetPresParam( GetHWND()
                      ,PP_FOREGROUNDCOLOR
                      ,sizeof(ULONG)
                      ,&ulColor
                     );
    return true;
} // end of wxListCtrl::SetForegroundColour

// Sets the background colour
bool wxListCtrl::SetBackgroundColour ( const wxColour& rCol )
{
    if (!wxWindow::SetBackgroundColour(rCol))
        return false;

    //
    // We set the same colour for both the "empty" background and the items
    // background
    //
    ULONG ulColor = wxColourToRGB(rCol);

    ::WinSetPresParam( GetHWND()
                      ,PP_BACKGROUNDCOLOR
                      ,sizeof(ULONG)
                      ,&ulColor
                     );
    return true;
} // end of wxListCtrl::SetBackgroundColour

// Gets information about this column
bool wxListCtrl::GetColumn ( int nCol, wxListItem& rItem ) const
{
    PFIELDINFO pFieldInfo = FindOS2ListFieldByColNum ( GetHWND(), nCol );

    if (!pFieldInfo)
        return false;
    rItem.SetWidth(pFieldInfo->cxWidth);
    if ((rItem.GetMask() & wxLIST_MASK_TEXT) &&
        (pFieldInfo->flData & CFA_STRING) &&
        (pFieldInfo->pUserData != NULL))
    {
        rItem.SetText((char*)pFieldInfo->pUserData);
    }
    if (rItem.GetMask() & wxLIST_MASK_FORMAT )
    {
        if (pFieldInfo->flData & CFA_LEFT)
            rItem.m_format = wxLIST_FORMAT_LEFT;
        else if (pFieldInfo->flData & CFA_RIGHT)
            rItem.m_format = wxLIST_FORMAT_RIGHT;
        else if (pFieldInfo->flData & CFA_CENTER)
            rItem.m_format = wxLIST_FORMAT_CENTRE;
    }
    return true;
} // end of wxListCtrl::GetColumn

// Sets information about this column
bool wxListCtrl::SetColumn ( int nCol, wxListItem& rItem )
{
    PFIELDINFO pFieldInfo = FindOS2ListFieldByColNum( GetHWND(), nCol );
    ConvertToOS2ListCol( nCol, rItem, pFieldInfo );
    //
    // Since we changed the field pointed to, we invalidate to see the result
    //
    ::WinSendMsg(GetHWND(), CM_INVALIDATEDETAILFIELDINFO, NULL, NULL);
    return true;
} // end of wxListCtrl::SetColumn

// Gets the column width
int wxListCtrl::GetColumnWidth ( int nCol ) const
{
    PFIELDINFO pFieldInfo = FindOS2ListFieldByColNum ( GetHWND(), nCol );

    if (!pFieldInfo)
        return 0;
    return((int)pFieldInfo->cxWidth);
} // end of wxListCtrl::GetColumnWidth

// Sets the column width
bool wxListCtrl::SetColumnWidth ( int nCol, int nWidth )
{
    int nCol2 = nCol;
    int nWidth2 = nWidth;

    if (GetWindowStyleFlag() & wxLC_LIST)
        nCol2 = -1;

    PFIELDINFO pFieldInfo = FindOS2ListFieldByColNum( GetHWND(), nCol );
    pFieldInfo->cxWidth = nWidth;
    ::WinSendMsg(GetHWND(), CM_INVALIDATEDETAILFIELDINFO, NULL, NULL);
    return true;
} // end of wxListCtrl::SetColumnWidth

// Gets the number of items that can fit vertically in the
// visible area of the list control (list or report view)
// or the total number of items in the list control (icon
// or small icon view)
int wxListCtrl::GetCountPerPage () const
{
    QUERYRECORDRECT                 vQueryRect;
    CNRINFO                         vCnrInfo;
    RECTL                           vRectRecord;
    RECTL                           vRectControl;
    int                             nCount;

    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return 0;
    memset(&vQueryRect, '\0', sizeof(QUERYRECORDRECT));
    vQueryRect.cb = sizeof(QUERYRECORDRECT);
    if (vCnrInfo.flWindowAttr & CV_ICON)
        vQueryRect.fsExtent = CMA_ICON | CMA_TEXT;
    else if (vCnrInfo.flWindowAttr & CV_NAME)
        vQueryRect.fsExtent = CMA_ICON | CMA_TEXT;
    else if (vCnrInfo.flWindowAttr & CV_TEXT)
        vQueryRect.fsExtent = CMA_TEXT;
    else if (vCnrInfo.flWindowAttr & CV_DETAIL)
        vQueryRect.fsExtent = CMA_TEXT;
    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYRECORDRECT
                      ,MPFROMP(&vRectRecord)
                      ,MPFROMP(&vQueryRect)
                     ))
        return 0;
    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYVIEWPORTRECT
                      ,MPFROMP(&vRectControl)
                      ,MPFROM2SHORT(CMA_WINDOW, (USHORT)FALSE)
                     ))
        return 0;
    nCount = (int)((int)((vRectControl.xRight - vRectControl.xLeft) / (vRectRecord.xRight - vRectRecord.xLeft)) *
                   (int)((vRectControl.yTop - vRectControl.yBottom) / (vRectRecord.yTop - vRectRecord.yBottom))
                     );
    if (nCount > (int)vCnrInfo.cFields)
        nCount = (int)vCnrInfo.cFields;
    return nCount;
} // end of wxListCtrl::GetCountPerPage

// Gets the edit control for editing labels.
wxTextCtrl* wxListCtrl::GetEditControl() const
{
    return m_pTextCtrl;
}

// Gets information about the item
bool wxListCtrl::GetItem ( wxListItem& rInfo ) const
{
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), rInfo.GetId() );

    //
    // Give NULL as hwnd as we already have everything we need
    //
    ConvertFromOS2ListItem( NULL, rInfo, pRecord );
    return true;
} // end of wxListCtrl::GetItem

// Sets information about the item
bool wxListCtrl::SetItem ( wxListItem& rInfo )
{
    PFIELDINFO pFieldInfo = FindOS2ListFieldByColNum ( GetHWND(), rInfo.GetColumn() );
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), rInfo.GetId() );

    ConvertToOS2ListItem( this
                         ,rInfo
                         ,pRecord
                         ,pFieldInfo
                        );

    //
    // Check if setting attributes or lParam
    //
    if (rInfo.HasAttributes() || (rInfo.GetMask()  & wxLIST_MASK_DATA))
    {
        //
        // Get internal item data
        // perhaps a cache here ?
        //
        CListItemInternalData*      pData = GetInternalData( this
                                                            ,rInfo.GetId()
                                                           );

        if (!pData)
        {
            //
            // Need to set it
            //
            m_bAnyInternalData    = true;
            pData                 = new CListItemInternalData();
            pRecord->m_ulUserData = (unsigned long)pData;
        };

        //
        // User data
        //
        if (rInfo.GetMask()  & wxLIST_MASK_DATA)
            pData->m_lParam = (WXLPARAM)rInfo.GetData();

        // attributes
        if (rInfo.HasAttributes())
        {
            if (pData->m_pAttr)
                *pData->m_pAttr = *rInfo.GetAttributes();
            else
                pData->m_pAttr = new wxListItemAttr(*rInfo.GetAttributes());
        }
        pData->m_pMyRecord = pRecord;  // they point to each other
    }

    //
    // We need to update the item immediately to show the new image
    //
    bool bUpdateNow = (rInfo.GetMask() & wxLIST_MASK_IMAGE) != 0;

    //
    // Check whether it has any custom attributes
    //
    if (rInfo.HasAttributes())
    {
        m_bHasAnyAttr = true;

        //
        // If the colour has changed, we must redraw the item
        //
        bUpdateNow = true;
    }
    if (::WinIsWindowVisible(GetHWND()))
    {
        ::WinSendMsg( GetHWND()
                     ,CM_INVALIDATERECORD
                     ,MPFROMP(pRecord)
                     ,MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION | CMA_TEXTCHANGED)
                    );
        RefreshItem(pRecord->m_ulItemId);
    }
    ::WinSendMsg( GetHWND()
                 ,CM_INVALIDATEDETAILFIELDINFO
                 ,NULL
                 ,NULL
                );
    return true;
} // end of wxListCtrl::SetItem

long wxListCtrl::SetItem (
  long                              lIndex
, int                               nCol
, const wxString&                   rsLabel
, int                               nImageId
)
{
    wxListItem                      vInfo;

    vInfo.m_text   = rsLabel;
    vInfo.m_mask   = wxLIST_MASK_TEXT;
    vInfo.m_itemId = lIndex;
    vInfo.m_col    = nCol;
    if (nImageId > -1)
    {
        vInfo.m_image = nImageId;
        vInfo.m_mask |= wxLIST_MASK_IMAGE;
    }
    return SetItem(vInfo);
} // end of wxListCtrl::SetItem

// Gets the item state
int wxListCtrl::GetItemState (
  long                              lItem
, long                              lStateMask
) const
{
    wxListItem                      vInfo;

    vInfo.m_mask      = wxLIST_MASK_STATE;
    vInfo.m_stateMask = lStateMask;
    vInfo.m_itemId    = lItem;

    if (!GetItem(vInfo))
        return 0;
    return vInfo.m_state;
} // end of wxListCtrl::GetItemState

// Sets the item state
bool wxListCtrl::SetItemState ( long lItem, long lState, long lStateMask )
{
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), lItem );

    //
    // Don't use SetItem() here as it doesn't work with the virtual list
    // controls
    //
    ConvertToOS2Flags( lState, pRecord );

    //
    // for the virtual list controls we need to refresh the previously focused
    // item manually when changing focus without changing selection
    // programmatically because otherwise it keeps its focus rectangle until
    // next repaint (yet another comctl32 bug)
    //
    long                            lFocusOld;

    if (IsVirtual() &&
        (lStateMask & wxLIST_STATE_FOCUSED) &&
         (lState & wxLIST_STATE_FOCUSED) )
    {
        lFocusOld = GetNextItem( -1
                                ,wxLIST_NEXT_ALL
                                ,wxLIST_STATE_FOCUSED
                               );
    }
    else
    {
        lFocusOld = -1;
    }
    ::WinSendMsg( GetHWND()
                 ,CM_INVALIDATERECORD
                 ,MPFROMP(pRecord)
                 ,MPFROM2SHORT(1, CMA_ERASE | CMA_REPOSITION | CMA_TEXTCHANGED)
                );

    if (lFocusOld != -1)
    {
        //
        // No need to refresh the item if it was previously selected, it would
        // only result in annoying flicker
        //
        if (!(GetItemState( lFocusOld
                           ,wxLIST_STATE_SELECTED
                          ) & wxLIST_STATE_SELECTED))
        {
            RefreshItem(lFocusOld);
        }
    }
    return true;
} // end of wxListCtrl::SetItemState

// Sets the item image
bool wxListCtrl::SetItemImage (
  long                              lItem
, int                               nImage
, int                               WXUNUSED(nSelImage))
{
    return SetItemColumnInfo(lItem, 0, nImage);
} // end of wxListCtrl::SetItemImage

// Sets the item image
bool wxListCtrl::SetItemColumnImage (
  long                              lItem
, long                              lColumn
, int                               nImage)
{
    wxListItem                      vInfo;

    vInfo.m_mask   = wxLIST_MASK_IMAGE;
    vInfo.m_image  = nImage;
    vInfo.m_itemId = lItem;
    vInfo.m_col    = lColumn;
    return SetItem(vInfo);
} // end of wxListCtrl::SetItemColumnImage

// Gets the item text
wxString wxListCtrl::GetItemText (
  long                              lItem
) const
{
    wxListItem                      vInfo;

    vInfo.m_mask   = wxLIST_MASK_TEXT;
    vInfo.m_itemId = lItem;

    if (!GetItem(vInfo))
        return wxEmptyString;
    return vInfo.m_text;
} // end of wxListCtrl::GetItemText

// Sets the item text
void wxListCtrl::SetItemText (
  long                              lItem
, const wxString&                   rsStr
)
{
    wxListItem                      vInfo;

    vInfo.m_mask   = wxLIST_MASK_TEXT;
    vInfo.m_itemId = lItem;
    vInfo.m_text   = rsStr;
    SetItem(vInfo);
} // end of wxListCtrl::SetItemText

// Gets the item data
long wxListCtrl::GetItemData (
  long                              lItem
) const
{
    wxListItem                      vInfo;

    vInfo.m_mask   = wxLIST_MASK_DATA;
    vInfo.m_itemId = lItem;
    if (!GetItem(vInfo))
        return 0;
    return vInfo.m_data;
} // end of wxListCtrl::GetItemData

// Sets the item data
bool wxListCtrl::SetItemPtrData (
  long                              lItem
, wxUIntPtr                         lData
)
{
    wxListItem                      vInfo;

    vInfo.m_mask   = wxLIST_MASK_DATA;
    vInfo.m_itemId = lItem;
    vInfo.m_data   = lData;
    return SetItem(vInfo);
} // end of wxListCtrl::SetItemPtrData

// Gets the item rectangle
bool wxListCtrl::GetItemRect ( long lItem,
                               wxRect& rRect,
                               int nCode ) const
{
    bool bSuccess;
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), lItem );
    QUERYRECORDRECT vQueryRect;
    RECTL vRect;
    int nHeight;

    if (!pRecord)
        return false;
    vQueryRect.cb                = sizeof(QUERYRECORDRECT);
    vQueryRect.pRecord           = &pRecord->m_vRecord;
    vQueryRect.fRightSplitWindow = TRUE;
    vQueryRect.fsExtent          = CMA_ICON | CMA_TEXT;
    ::WinSendMsg( GetHWND()
                 ,CM_QUERYRECORDRECT
                 ,MPFROMP(&vRect)
                 ,MPFROMP(&vQueryRect)
                );
    //
    // remember OS/2 is backwards
    //
    GetClientSize( NULL, &nHeight );
    rRect.x      = vRect.xLeft;
    rRect.y      = nHeight - vRect.yTop;
    rRect.width  = vRect.xRight;
    rRect.height = nHeight - vRect.yBottom;
    bSuccess = true;
    return bSuccess;
} // end of wxListCtrl::GetItemRect

// Gets the item position
bool wxListCtrl::GetItemPosition ( long lItem, wxPoint& rPos ) const
{
    bool bSuccess;
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND() , lItem );
    QUERYRECORDRECT vQueryRect;
    RECTL vRect;
    int nHeight;

    if (!pRecord)
        return false;
    vQueryRect.cb                = sizeof(QUERYRECORDRECT);
    vQueryRect.pRecord           = &pRecord->m_vRecord;
    vQueryRect.fRightSplitWindow = TRUE;
    vQueryRect.fsExtent          = CMA_ICON | CMA_TEXT;
    ::WinSendMsg( GetHWND()
                 ,CM_QUERYRECORDRECT
                 ,MPFROMP(&vRect)
                 ,MPFROMP(&vQueryRect)
                );
    //
    // remember OS/2 is backwards
    //
    GetClientSize( NULL, &nHeight );
    rPos.x   = vRect.xLeft;
    rPos.y   = nHeight - vRect.yTop;
    bSuccess = true;
    return bSuccess;
} // end of wxListCtrl::GetItemPosition

// Sets the item position.
bool wxListCtrl::SetItemPosition ( long lItem, const wxPoint& rPos )
{
    //
    // Items cannot be positioned in X/Y coord in OS/2
    //
    return false;
} // end of wxListCtrl::SetItemPosition

// Gets the number of items in the list control
int wxListCtrl::GetItemCount () const
{
    CNRINFO                         vCnrInfo;

    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return -1;
    return vCnrInfo.cRecords;
} // end of wxListCtrl::GetItemCount

// Retrieves the spacing between icons in pixels.
// If bIsSmall is true, gets the spacing for the small icon
// view, otherwise the large icon view.
int wxListCtrl::GetItemSpacing ( bool bIsSmall ) const
{
    CNRINFO                         vCnrInfo;

    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return -1;
    return vCnrInfo.cyLineSpacing;
} // end of wxListCtrl::GetItemSpacing

void wxListCtrl::SetItemTextColour (
  long                              lItem
, const wxColour&                   rCol
)
{
    wxListItem                      vInfo;

    vInfo.m_itemId = lItem;
    vInfo.SetTextColour(rCol);
    SetItem(vInfo);
} // end of wxListCtrl::SetItemTextColour

wxColour wxListCtrl::GetItemTextColour (
  long                              lItem
) const
{
    wxListItem                      vInfo;

    vInfo.m_itemId = lItem;
    GetItem(vInfo);
    return vInfo.GetTextColour();
} // end of wxListCtrl::GetItemTextColour

void wxListCtrl::SetItemBackgroundColour (
  long                              lItem
, const wxColour&                   rCol
)
{
    wxListItem                      vInfo;

    vInfo.m_itemId = lItem;
    vInfo.SetBackgroundColour(rCol);
    SetItem(vInfo);
} // end of wxListCtrl::SetItemBackgroundColour

wxColour wxListCtrl::GetItemBackgroundColour (
  long                              lItem
) const
{
    wxListItem                      vInfo;

    vInfo.m_itemId = lItem;
    GetItem(vInfo);
    return vInfo.GetBackgroundColour();
} // end of wxListCtrl::GetItemBackgroundColour

// Gets the number of selected items in the list control
int wxListCtrl::GetSelectedItemCount () const
{
    PMYRECORD                       pRecord = NULL;
    int                             nCount = 0;
    pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( GetHWND()
                                                  ,CM_QUERYRECORDEMPHASIS
                                                  ,(MPARAM)CMA_FIRST
                                                  ,(MPARAM)CRA_SELECTED
                                                 ));
    if (pRecord)
        nCount++;
    else
        return 0;
    while (pRecord)
    {
        pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( GetHWND()
                                                      ,CM_QUERYRECORDEMPHASIS
                                                      ,MPFROMP(pRecord)
                                                      ,(MPARAM)CRA_SELECTED
                                                     ));
        if (pRecord)
            nCount++;
    }
    return nCount;
} // end of wxListCtrl::GetSelectedItemCount

// Gets the text colour of the listview
wxColour wxListCtrl::GetTextColour () const
{
    wxColour                        vCol;
    ULONG                           ulColor;

    ::WinQueryPresParam( GetHWND()
                        ,PP_FOREGROUNDCOLOR
                        ,0
                        ,NULL
                        ,sizeof(ULONG)
                        ,&ulColor
                        ,QPF_PURERGBCOLOR
                       );
    vCol.Set(ulColor);
    return vCol;
} // end of wxListCtrl::GetTextColour

// Sets the text colour of the listview
void wxListCtrl::SetTextColour (
  const wxColour&                   rCol
)
{
    ULONG                           ulColor = wxColourToRGB(rCol);

    ::WinSetPresParam( GetHWND()
                      ,PP_FOREGROUNDCOLOR
                      ,sizeof(ULONG)
                      ,&ulColor
                     );
} // end of wxListCtrl::SetTextColour

// Gets the index of the topmost visible item when in
// list or report view
long wxListCtrl::GetTopItem () const
{
    PMYRECORD                       pRecord = NULL;
    QUERYRECFROMRECT                vQueryRect;
    RECTL                           vRect;

    ::WinSendMsg( GetHWND()
                 ,CM_QUERYVIEWPORTRECT
                 ,MPFROMP(&vRect)
                 ,MPFROM2SHORT(CMA_WINDOW, TRUE)
                );
    vQueryRect.cb        = sizeof(QUERYRECFROMRECT);
    vQueryRect.rect      = vRect;
    vQueryRect.fsSearch = CMA_PARTIAL;

    pRecord = (PMYRECORD)::WinSendMsg( GetHWND()
                                      ,CM_QUERYRECORDFROMRECT
                                      ,(MPARAM)CMA_FIRST
                                      ,MPFROMP(&vQueryRect)
                                    );

    if (!pRecord)
        return -1L;
    return (long)pRecord->m_ulItemId;
} // end of wxListCtrl::GetTopItem

// Searches for an item, starting from 'item'.
// 'geometry' is one of
// wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT.
// 'state' is a state bit flag, one or more of
// wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT.
// item can be -1 to find the first item that matches the
// specified flags.
// Returns the item or -1 if unsuccessful.
long wxListCtrl::GetNextItem (
  long                              lItem
, int                               WXUNUSED(nGeom)
, int                               WXUNUSED(nState)
) const
{
    PMYRECORD                       pRecord = FindOS2ListRecordByID( GetHWND()
                                                                    ,lItem
                                                                   );

    pRecord = (PMYRECORD)pRecord->m_vRecord.preccNextRecord;
    if (pRecord)
        return((long)pRecord->m_ulItemId);
    return -1L;
} // end of wxListCtrl::GetNextItem

wxImageList* wxListCtrl::GetImageList (
  int                               nWhich
) const
{
    if (nWhich == wxIMAGE_LIST_NORMAL )
    {
        return m_pImageListNormal;
    }
    else if (nWhich == wxIMAGE_LIST_SMALL )
    {
        return m_pImageListSmall;
    }
    else if (nWhich == wxIMAGE_LIST_STATE )
    {
        return m_pImageListState;
    }
    return NULL;
} // end of wxListCtrl::GetImageList

void wxListCtrl::SetImageList ( wxImageList* pImageList,
                                int nWhich )
{
    if (nWhich == wxIMAGE_LIST_NORMAL)
    {
        if (m_bOwnsImageListNormal)
            delete m_pImageListNormal;
        m_pImageListNormal     = pImageList;
        m_bOwnsImageListNormal = false;
    }
    else if (nWhich == wxIMAGE_LIST_SMALL)
    {
        if (m_bOwnsImageListSmall)
            delete m_pImageListSmall;
        m_pImageListSmall    = pImageList;
        m_bOwnsImageListSmall = false;
    }
    else if (nWhich == wxIMAGE_LIST_STATE)
    {
        if (m_bOwnsImageListState)
            delete m_pImageListState;
        m_pImageListState     = pImageList;
        m_bOwnsImageListState = false;
    }
} // end of wxListCtrl::SetImageList

void wxListCtrl::AssignImageList ( wxImageList* pImageList, int nWhich )
{
    SetImageList( pImageList, nWhich );

    if (nWhich == wxIMAGE_LIST_NORMAL )
        m_bOwnsImageListNormal = true;
    else if (nWhich == wxIMAGE_LIST_SMALL )
        m_bOwnsImageListSmall = true;
    else if (nWhich == wxIMAGE_LIST_STATE )
        m_bOwnsImageListState = true;
} // end of wxListCtrl::AssignImageList

// ----------------------------------------------------------------------------
// Operations
// ----------------------------------------------------------------------------

// Arranges the items
bool wxListCtrl::Arrange ( int nFlag )
{
    ULONG ulType = 0L;
    ULONG ulFlags = 0L;

    if (nFlag == wxLIST_ALIGN_SNAP_TO_GRID)
    {
        ulType = CMA_ARRANGEGRID;
        if (nFlag == wxLIST_ALIGN_LEFT)
            ulFlags |= CMA_LEFT;
        else if (nFlag == wxLIST_ALIGN_TOP)
            ulFlags |= CMA_TOP;
        else if (nFlag == wxLIST_ALIGN_DEFAULT)
            ulFlags |= CMA_LEFT;
    }
    else
        ulType = CMA_ARRANGESTANDARD;
    ::WinSendMsg( GetHWND()
                 ,CM_ARRANGE
                 ,(MPARAM)ulType
                 ,(MPARAM)ulFlags
                );
    //
    // We do not support CMA_ARRANGESELECTED
    //
    return true;
} // end of wxListCtrl::Arrange

// Deletes an item
bool wxListCtrl::DeleteItem ( long lItem )
{
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), lItem );
    if (LONGFROMMR(::WinSendMsg( GetHWND()
                                ,CM_REMOVERECORD
                                ,(MPARAM)pRecord
                                ,MPFROM2SHORT(1, CMA_FREE)
                               )) == -1L)
    {
        return false;
    }

    //
    // The virtual list control doesn't refresh itself correctly, help it
    //
    if (IsVirtual())
    {
        //
        // We need to refresh all the lines below the one which was deleted
        //
        wxRect                      vRectItem;

        if (lItem > 0 && GetItemCount())
        {
            GetItemRect( lItem - 1
                        ,vRectItem
                       );
        }
        else
        {
            vRectItem.y = vRectItem.height = 0;
        }
        wxRect                      vRectWin = GetRect();

        vRectWin.height = vRectWin.GetBottom() - vRectItem.GetBottom();
        vRectWin.y      = vRectItem.GetBottom();
        RefreshRect(vRectWin);
    }
    return true;
} // end of wxListCtrl::DeleteItem

// Deletes all items
bool wxListCtrl::DeleteAllItems ()
{
    return((LONG)::WinSendMsg( GetHWND()
                              ,CM_REMOVERECORD
                              ,NULL
                              ,MPFROM2SHORT(0, CMA_FREE)
                             ) != -1L);
} // end of wxListCtrl::DeleteAllItems

// Deletes all items
bool wxListCtrl::DeleteAllColumns ()
{
    while (m_nColCount > 0)
    {
        DeleteColumn(m_nColCount - 1);
        m_nColCount--;
    }

    wxASSERT_MSG(m_nColCount == 0, wxT("no columns should be left"));
    return true;
} // end of wxListCtrl::DeleteAllColumns

// Deletes a column
bool wxListCtrl::DeleteColumn ( int nCol )
{
    bool bSuccess = false;
    PFIELDINFO pField = FindOS2ListFieldByColNum( GetHWND(), nCol );
    bSuccess = ((LONG)::WinSendMsg( GetHWND()
                                   ,CM_REMOVEDETAILFIELDINFO
                                   ,MPFROMP(pField)
                                   ,MPFROM2SHORT((SHORT)1, CMA_FREE)
                                  ) == -1L);
    if (bSuccess && (m_nColCount > 0))
        m_nColCount--;
    return bSuccess;
} // end of wxListCtrl::DeleteColumn

// Clears items, and columns if there are any.
void wxListCtrl::ClearAll ()
{
    DeleteAllItems();
    if (m_nColCount > 0)
        DeleteAllColumns();
} // end of wxListCtrl::ClearAll

//
// OS/2 does not use a text control for its container labels.  You merely
// "open" a record for editting.
//
wxTextCtrl* wxListCtrl::EditLabel (
  long                              lItem
, wxClassInfo*                      WXUNUSED(pTextControlClass)
)
{
    CNREDITDATA                     vEdit;
    PMYRECORD                       pRecord = FindOS2ListRecordByID( GetHWND()
                                                                    ,lItem
                                                                   );

    vEdit.cb         = sizeof(CNREDITDATA);
    vEdit.hwndCnr    = GetHWND();
    vEdit.pRecord    = &pRecord->m_vRecord;
    vEdit.pFieldInfo = NULL;
    vEdit.ppszText   = NULL;
    vEdit.cbText     = 0;
    vEdit.id         = 0;

    ::WinSendMsg( GetHWND()
                 ,CM_OPENEDIT
                 ,MPFROMP(&vEdit)
                 ,(MPARAM)0
                );
    return m_pTextCtrl;
} // end of wxListCtrl::EditLabel

// End label editing, optionally cancelling the edit.  Under OS/2 you close
// the record for editting
bool wxListCtrl::EndEditLabel ( bool WXUNUSED(bCancel) )
{
    ::WinSendMsg( GetHWND()
                 ,CM_CLOSEEDIT
                 ,(MPARAM)0
                 ,(MPARAM)0
                );
    return true;
} // end of wxListCtrl::EndEditLabel

// Ensures this item is visible
bool wxListCtrl::EnsureVisible ( long lItem )
{
    PMYRECORD pRecord = FindOS2ListRecordByID( GetHWND(), lItem );
    ::WinSendMsg( GetHWND()
                 ,CM_INVALIDATERECORD
                 ,MPFROMP(pRecord)
                 ,MPFROM2SHORT((SHORT)1, CMA_NOREPOSITION)
                );
    return true;
} // end of wxListCtrl::EnsureVisible

// Find an item whose label matches this string, starting from the item after 'start'
// or the beginning if 'start' is -1.
long wxListCtrl::FindItem (
  long                              lStart
, const wxString&                   rsStr
, bool                              bPartial
)
{
    CNRINFO                         vCnrInfo;
    SEARCHSTRING                    vSearch;
    PMYRECORD                       pRecord = FindOS2ListRecordByID( GetHWND()
                                                                    ,lStart
                                                                   );
    ULONG                           ulFlag;


    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return -1L;

    if (vCnrInfo.flWindowAttr & CV_ICON)
        ulFlag = CV_ICON;
    if (vCnrInfo.flWindowAttr & CV_NAME)
        ulFlag = CV_NAME;
    if (vCnrInfo.flWindowAttr & CV_TEXT)
        ulFlag = CV_TEXT;
    if (vCnrInfo.flWindowAttr & CV_DETAIL)
        ulFlag = CV_DETAIL;
    if (!bPartial)
        ulFlag |= CV_EXACTLENGTH;

    vSearch.cb              = sizeof(SEARCHSTRING);
    vSearch.pszSearch       = (char*)rsStr.c_str();
    vSearch.fsPrefix        = TRUE;
    vSearch.fsCaseSensitive = TRUE;
    vSearch.usView          = ulFlag;

    if (lStart == -1)
    {
        pRecord = (PMYRECORD)::WinSendMsg( GetHWND()
                                          ,CM_SEARCHSTRING
                                          ,MPFROMP(&vSearch)
                                          ,(MPARAM)CMA_FIRST
                                         );
    }
    else
    {
        pRecord = (PMYRECORD)::WinSendMsg( GetHWND()
                                          ,CM_SEARCHSTRING
                                          ,MPFROMP(&vSearch)
                                          ,MPFROMP(pRecord)
                                         );
    }
    if (!pRecord)
        return -1L;
    return pRecord->m_ulItemId;
} // end of wxListCtrl::FindItem

// Find an item whose data matches this data, starting from the item after 'start'
// or the beginning if 'start' is -1.
long wxListCtrl::FindItem (
  long                              lStart
, long                              lData
)
{
    long                            lIdx = lStart + 1;
    long                            lCount = GetItemCount();

    while (lIdx < lCount)
    {
        if (GetItemData(lIdx) == lData)
            return lIdx;
        lIdx++;
    };
    return -1;
} // end of wxListCtrl::FindItem

// Find an item nearest this position in the specified direction, starting from
// the item after 'start' or the beginning if 'start' is -1.
long wxListCtrl::FindItem (
  long                              lStart
, const wxPoint&                    rPoint
, int                               nDirection
)
{
    RECTL                           vRect;
    QUERYRECORDRECT                 vQueryRect;
    PMYRECORD                       pRecord = FindOS2ListRecordByID( GetHWND()
                                                                    ,lStart
                                                                   );
    CNRINFO                         vCnrInfo;
    ULONG                           i;
    wxRect                          vLibRect;

    if (!::WinSendMsg( GetHWND()
                      ,CM_QUERYCNRINFO
                      ,MPFROMP(&vCnrInfo)
                      ,(MPARAM)(USHORT)sizeof(CNRINFO)
                     ))
        return -1L;

    vQueryRect.cb                = sizeof(QUERYRECORDRECT);
    vQueryRect.pRecord           = &pRecord->m_vRecord;
    vQueryRect.fRightSplitWindow = TRUE;
    vQueryRect.fsExtent          = CMA_ICON | CMA_TEXT;

    ::WinSendMsg( GetHWND()
                 ,CM_QUERYRECORDRECT
                 ,MPFROMP(&vRect)
                 ,MPFROMP(&vQueryRect)
                );
    vLibRect.SetLeft(vRect.xLeft);
    vLibRect.SetTop(vRect.yTop);
    vLibRect.SetRight(vRect.xRight);
    vLibRect.SetBottom(vRect.yBottom);
    if (vLibRect.Contains(rPoint))
        return pRecord->m_ulItemId;

    for (i = lStart + 1; i < vCnrInfo.cRecords; i++)
    {
        pRecord = (PMYRECORD)PVOIDFROMMR(::WinSendMsg( GetHWND()
                                                      ,CM_QUERYRECORD
                                                      ,MPFROMP(pRecord)
                                                      ,MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER)
                                                     ));
        vQueryRect.pRecord = (PRECORDCORE)pRecord;
        ::WinSendMsg( GetHWND()
                     ,CM_QUERYRECORDRECT
                     ,MPFROMP(&vRect)
                     ,MPFROMP(&vQueryRect)
                    );
        vLibRect.SetLeft(vRect.xLeft);
        vLibRect.SetTop(vRect.yTop);
        vLibRect.SetRight(vRect.xRight);
        vLibRect.SetBottom(vRect.yBottom);
        if (vLibRect.Contains(rPoint))
            return pRecord->m_ulItemId;
    }
    return -1L;
} // end of wxListCtrl::FindItem

// Determines which item (if any) is at the specified point,
// giving details in 'flags' (see wxLIST_HITTEST_... flags above)
long wxListCtrl::HitTest (
  const wxPoint&                    rPoint
, int&                              WXUNUSED(rFlags)
)
{
    PMYRECORD                       pRecord = NULL;
    QUERYRECFROMRECT                vQueryRect;
    RECTL                           vRect;
    long                            lHeight;

    //
    // Get height for OS/2 point conversion
    //
    ::WinSendMsg( GetHWND()
                 ,CM_QUERYVIEWPORTRECT
                 ,MPFROMP(&vRect)
                 ,MPFROM2SHORT(CMA_WINDOW, TRUE)
                );
    lHeight = vRect.yTop - vRect.yBottom;

    //
    // For now just try and get a record in the general vicinity and forget
    // the flag
    //
    vRect.xLeft   = rPoint.x - 2;
    vRect.xRight  = rPoint.x + 2;
    vRect.yTop    = (lHeight - rPoint.y) + 2;
    vRect.yBottom = (lHeight - rPoint.y) - 2;

    vQueryRect.cb = sizeof(QUERYRECFROMRECT);
    vQueryRect.rect = vRect;
    vQueryRect.fsSearch = CMA_PARTIAL;

    pRecord = (PMYRECORD)::WinSendMsg( GetHWND()
                                      ,CM_QUERYRECORDFROMRECT
                                      ,(MPARAM)CMA_FIRST
                                      ,MPFROMP(&vQueryRect)
                                    );

    if (!pRecord)
        return -1L;
    return pRecord->m_ulItemId;
} // end of wxListCtrl::HitTest

// Inserts an item, returning the index of the new item if successful,
// -1 otherwise.
long wxListCtrl::InsertItem (
  wxListItem&                       rInfo
)
{
    wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual controls") );

    PFIELDINFO                      pFieldInfo = FindOS2ListFieldByColNum ( GetHWND()
                                                                           ,rInfo.GetColumn()
                                                                          );
    PMYRECORD                       pRecordAfter = NULL;
    PMYRECORD                       pRecord = (PMYRECORD)::WinSendMsg( GetHWND()
                                                                      ,CM_ALLOCRECORD
                                                                      ,MPFROMLONG(sizeof(MYRECORD) - sizeof(RECORDCORE))
                                                                      ,MPFROMSHORT(1)
                                                                     );

    ConvertToOS2ListItem( this
                         ,rInfo
                         ,pRecord
                         ,pFieldInfo
                        );

    if (rInfo.GetId() > 0)
        pRecordAfter = FindOS2ListRecordByID( GetHWND()
                                             ,rInfo.GetId() - 1
                                            );

    RECORDINSERT                    vInsert;

    vInsert.cb                = sizeof(RECORDINSERT);
    vInsert.pRecordParent     = NULL;
    if (!pRecordAfter)
        vInsert.pRecordOrder  = (PRECORDCORE)CMA_FIRST;
    else
        vInsert.pRecordOrder  = (PRECORDCORE)pRecordAfter;
    vInsert.zOrder            = CMA_TOP;
    vInsert.cRecordsInsert    = 1;
    vInsert.fInvalidateRecord = TRUE;

    //
    // Check whether we need to allocate our internal data
    //
    bool                            bNeedInternalData = ((rInfo.GetMask() & wxLIST_MASK_DATA) ||
                                                          rInfo.HasAttributes()
                                                        );
    if (bNeedInternalData)
    {
        m_bAnyInternalData = true;

        //
        // Internal stucture that manages data
        //
        CListItemInternalData*      pData = new CListItemInternalData();

        pRecord->m_ulUserData = (unsigned long)pData;
        if (rInfo.GetMask() & wxLIST_MASK_DATA)
            pData->m_lParam = (WXLPARAM)rInfo.GetData();

        //
        // Check whether it has any custom attributes
        //
        if (rInfo.HasAttributes())
        {
            //
            // Take copy of attributes
            //
            pData->m_pAttr = new wxListItemAttr(*rInfo.GetAttributes());
        }
    }
    if (!::WinSendMsg( GetHWND()
                      ,CM_INSERTRECORD
                      ,MPFROMP(pRecord)
                      ,MPFROMP(&vInsert)
                     ))
        return -1;
    //
    // OS/2 must mannually bump the index's of following records
    //
    BumpRecordIds( GetHWND()
                  ,pRecord
                 );
    ::WinSendMsg( GetHWND()
                 ,CM_INVALIDATEDETAILFIELDINFO
                 ,NULL
                 ,NULL
                );
    return pRecord->m_ulItemId;
} // end of wxListCtrl::InsertItem

long wxListCtrl::InsertItem (
  long                              lIndex
, const wxString&                   rsLabel
)
{
    wxListItem                      vInfo;

    memset(&vInfo, '\0', sizeof(wxListItem));
    vInfo.m_text   = rsLabel;
    vInfo.m_mask   = wxLIST_MASK_TEXT;
    vInfo.m_itemId = lIndex;
    return InsertItem(vInfo);
} // end of wxListCtrl::InsertItem

// Inserts an image item
long wxListCtrl::InsertItem (
  long                              lIndex
, int                               nImageIndex
)
{
    wxListItem                      vInfo;

    vInfo.m_image  = nImageIndex;
    vInfo.m_mask   = wxLIST_MASK_IMAGE;
    vInfo.m_itemId = lIndex;
    return InsertItem(vInfo);
} // end of wxListCtrl::InsertItem

// Inserts an image/string item
long wxListCtrl::InsertItem (
  long                              lIndex
, const wxString&                   rsLabel
, int                               nImageIndex
)
{
    wxListItem                      vInfo;

    vInfo.m_image  = nImageIndex;
    vInfo.m_text   = rsLabel;
    vInfo.m_mask   = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT;
    vInfo.m_itemId = lIndex;
    return InsertItem(vInfo);
} // end of wxListCtrl::InsertItem

// For details view mode (only), inserts a column.
long wxListCtrl::InsertColumn (
  long                              lCol
, wxListItem&                       rItem
)
{
    bool                            bSuccess;
    PFIELDINFO                      pField = (PFIELDINFO)::WinSendMsg( GetHWND()
                                                                      ,CM_ALLOCDETAILFIELDINFO
                                                                      ,MPFROMLONG(1)
                                                                      ,NULL
                                                                     );
    PFIELDINFO                      pFieldAfter = FindOS2ListFieldByColNum ( GetHWND()
                                                                            ,lCol - 1
                                                                           );
    FIELDINFOINSERT                 vInsert;

    ConvertToOS2ListCol ( lCol
                         ,rItem
                         ,pField
                        );

    vInsert.cb                   = sizeof(FIELDINFOINSERT);
    vInsert.pFieldInfoOrder      = pFieldAfter;
    vInsert.fInvalidateFieldInfo = TRUE;
    vInsert.cFieldInfoInsert     = 1;

    bSuccess = ::WinSendMsg( GetHWND()
                            ,CM_INSERTDETAILFIELDINFO
                            ,MPFROMP(pField)
                            ,MPFROMP(&vInsert)
                           ) != (MRESULT)0;
    return bSuccess;
} // end of wxListCtrl::InsertColumn

long wxListCtrl::InsertColumn (
  long                              lCol
, const wxString&                   rsHeading
, int                               nFormat
, int                               nWidth
)
{
    wxListItem                      vItem;

    vItem.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
    vItem.m_text = rsHeading;
    if (nWidth > -1)
    {
        vItem.m_mask |= wxLIST_MASK_WIDTH;
        vItem.m_width = nWidth;
    }
    vItem.m_format = nFormat;

    return InsertColumn( lCol
                        ,vItem
                       );
} // end of wxListCtrl::InsertColumn

// scroll the control by the given number of pixels (exception: in list view,
// dx is interpreted as number of columns)
bool wxListCtrl::ScrollList ( int nDx, int nDy )
{
    if (nDx > 0)
        ::WinSendMsg( GetHWND()
                     ,CM_SCROLLWINDOW
                     ,(MPARAM)CMA_HORIZONTAL
                     ,(MPARAM)nDx
                    );
    if (nDy > 0)
        ::WinSendMsg( GetHWND()
                     ,CM_SCROLLWINDOW
                     ,(MPARAM)CMA_VERTICAL
                     ,(MPARAM)nDy
                    );
    return true;
} // end of wxListCtrl::ScrollList

bool wxListCtrl::SortItems ( wxListCtrlCompare fn, long lData )
{
    SInternalDataSort vInternalData;

    vInternalData.m_fnUser = fn;
    vInternalData.m_lData  = lData;

    // WPARAM cast is needed for mingw/cygwin
    if (!::WinSendMsg( GetHWND()
                      ,CM_SORTRECORD
                      ,(PFN)InternalDataCompareFunc
                      ,(PVOID)&vInternalData
                     ))
    {
        wxLogDebug(wxT("CM_SORTRECORD failed"));
        return false;
    }
    return true;
} // end of wxListCtrl::SortItems

// ----------------------------------------------------------------------------
// message processing
// ----------------------------------------------------------------------------

bool wxListCtrl::OS2Command ( WXUINT uCmd, WXWORD wId )
{
    if (uCmd == CN_ENDEDIT)
    {
        wxCommandEvent vEvent( wxEVT_TEXT, wId );

        vEvent.SetEventObject( this );
        ProcessCommand(vEvent);
        return true;
    }
    else if (uCmd == CN_KILLFOCUS)
    {
        wxCommandEvent vEvent( wxEVT_KILL_FOCUS, wId );
        vEvent.SetEventObject( this );
        ProcessCommand(vEvent);
        return true;
    }
    else
        return false;
} // end of wxListCtrl::OS2Command

// Necessary for drawing hrules and vrules, if specified
void wxListCtrl::OnPaint ( wxPaintEvent& rEvent )
{
    wxPaintDC                       vDc(this);
    wxPen                           vPen(wxSystemSettings::GetColour( wxSYS_COLOUR_3DLIGHT)
                                                                     ,1
                                                                     ,wxSOLID
                                                                    );
    wxSize                          vClientSize = GetClientSize();
    wxRect                          vItemRect;
    int                             nItemCount = GetItemCount();
    int                             nCy = 0;
    int                             i;
    bool                            bDrawHRules = ((GetWindowStyle() & wxLC_HRULES) != 0);
    bool                            bDrawVRules = ((GetWindowStyle() & wxLC_VRULES) != 0);

    wxControl::OnPaint(rEvent);

    //
    // Reset the device origin since it may have been set
    //
    vDc.SetDeviceOrigin(0, 0);
    if (!bDrawHRules && !bDrawVRules)
        return;
    if ((GetWindowStyle() & wxLC_REPORT) == 0)
        return;
    vDc.SetPen(vPen);
    vDc.SetBrush(*wxTRANSPARENT_BRUSH);

    if (bDrawHRules)
    {
        long                        lTop = GetTopItem();

        for (i = lTop; i < lTop + GetCountPerPage() + 1; i++)
        {
            if (GetItemRect( i
                            ,vItemRect
                           ))
            {
                nCy = vItemRect.GetTop();
                if (i != 0) // Don't draw the first one
                {
                    vDc.DrawLine( 0
                                 ,nCy
                                 ,vClientSize.x
                                 ,nCy
                                );
                }
                // Draw last line
                if (i == nItemCount - 1)
                {
                    nCy = vItemRect.GetBottom();
                    vDc.DrawLine( 0
                                 ,nCy
                                 ,vClientSize.x
                                 ,nCy
                                );
                }
            }
        }
    }
    i = nItemCount - 1;
    if (bDrawVRules && (i > -1))
    {
        wxRect                      vFirstItemRect;

        GetItemRect( 0
                    ,vFirstItemRect
                   );
        if (GetItemRect( i
                        ,vItemRect
                       ))
        {
            int                     nCol;
            int                     nX = vItemRect.GetX();

            for (nCol = 0; nCol < GetColumnCount(); nCol++)
            {
                int                 nColWidth = GetColumnWidth(nCol);

                nX += nColWidth ;
                vDc.DrawLine( nX - 1
                             ,vFirstItemRect.GetY() - 2
                             ,nX - 1
                             ,vItemRect.GetBottom()
                            );
            }
        }
    }
} // end of wxListCtrl::OnPaint

// ----------------------------------------------------------------------------
// virtual list controls
// ----------------------------------------------------------------------------

wxString wxListCtrl::OnGetItemText (
  long                              WXUNUSED(lItem)
, long                              WXUNUSED(lCol)
) const
{
    // this is a pure virtual function, in fact - which is not really pure
    // because the controls which are not virtual don't need to implement it
    wxFAIL_MSG( wxT("not supposed to be called") );
    return wxEmptyString;
} // end of wxListCtrl::OnGetItemText

int wxListCtrl::OnGetItemImage (
  long                              WXUNUSED(lItem)
) const
{
    // same as above
    wxFAIL_MSG( wxT("not supposed to be called") );
    return -1;
} // end of wxListCtrl::OnGetItemImage

int wxListCtrl::OnGetItemColumnImage (
  long                              lItem,
  long                              lColumn
) const
{
    if (!lColumn)
        return OnGetItemImage(lItem);

    return -1;
} // end of wxListCtrl::OnGetItemColumnImage

void wxListCtrl::SetItemCount (
  long                              lCount
)
{
    wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );

    //
    // Cannot explicitly set the record count in OS/2
    //
} // end of wxListCtrl::SetItemCount

void wxListCtrl::RefreshItem (
  long                              lItem
)
{
    wxRect                          vRect;

    GetItemRect( lItem
                ,vRect
               );
    RefreshRect(vRect);
} // end of wxListCtrl::RefreshItem

void wxListCtrl::RefreshItems ( long lItemFrom, long lItemTo )
{
    wxRect vRect1;
    wxRect vRect2;

    GetItemRect( lItemFrom , vRect1 );
    GetItemRect( lItemTo , vRect2 );

    wxRect vRect = vRect1;

    vRect.height = vRect2.GetBottom() - vRect1.GetTop();
    RefreshRect(vRect);
} // end of wxListCtrl::RefreshItems

MRESULT wxListCtrl::OS2WindowProc( WXUINT uMsg,
                                   WXWPARAM wParam,
                                   WXLPARAM lParam )
{
    bool                            bProcessed = false;
    MRESULT                         lRc;
    wxListEvent                     vEvent( wxEVT_NULL
                                           ,m_windowId
                                          );
    wxEventType                     vEventType = wxEVT_NULL;
    PCNRDRAGINIT                    pDragInit = NULL;
    PCNREDITDATA                    pEditData = NULL;
    PNOTIFYRECORDENTER              pNotifyEnter = NULL;

    vEvent.SetEventObject(this);
    switch (uMsg)
    {
        case WM_CONTROL:
            //
            // First off let's set some internal data
            //
            switch(SHORT2FROMMP(wParam))
            {
                case CN_INITDRAG:
                case CN_DRAGOVER:
                case CN_DRAGAFTER:
                    {
                        CListItemInternalData*  pInternaldata = (CListItemInternalData *)lParam;

                        if (pInternaldata)
                        {
                            wxListItem*     pItem = (wxListItem*)&vEvent.GetItem();

                            pItem->SetData((long)pInternaldata->m_lParam);
                        }
                    }
                    break;
            }
            //
            // Now let's go through the codes we're interested in
            //
            switch(SHORT2FROMMP(wParam))
            {
                case CN_INITDRAG:
                    pDragInit = (PCNRDRAGINIT)lParam;
                    if (pDragInit)
                    {
                        PMYRECORD       pRecord = (PMYRECORD)pDragInit->pRecord;

                        vEventType = wxEVT_LIST_BEGIN_RDRAG;
                        vEvent.m_itemIndex   = pRecord->m_ulItemId;
                        vEvent.m_pointDrag.x = pDragInit->x;
                        vEvent.m_pointDrag.y = pDragInit->y;
                    }
                    break;

                case CN_BEGINEDIT:
                    pEditData = (PCNREDITDATA)lParam;
                    if (pEditData)
                    {
                        vEventType = wxEVT_LIST_BEGIN_LABEL_EDIT;
                        ConvertFromOS2ListItem( GetHWND()
                                               ,(wxListItem &)vEvent.GetItem()
                                               ,(PMYRECORD)pEditData->pRecord
                                              );
                        vEvent.m_itemIndex = vEvent.GetItem().GetId();
                    }
                    break;

                case CN_ENDEDIT:
                    pEditData = (PCNREDITDATA)lParam;
                    if (pEditData)
                    {
                        vEventType = wxEVT_LIST_END_LABEL_EDIT;
                        ConvertFromOS2ListItem( GetHWND()
                                               ,(wxListItem &)vEvent.GetItem()
                                               ,(PMYRECORD)pEditData->pRecord
                                              );
                        if (pEditData->cbText == 0)
                            return (MRESULT)FALSE;
                        vEvent.m_itemIndex = vEvent.GetItem().GetId();
                    }
                    break;

                case CN_ENTER:
                    pNotifyEnter = (PNOTIFYRECORDENTER)lParam;
                    if (pNotifyEnter)
                    {
                        wxListItem*     pItem = (wxListItem*)&vEvent.GetItem();
                        PMYRECORD       pMyRecord = (PMYRECORD)pNotifyEnter->pRecord;

                        vEventType             = wxEVT_LIST_ITEM_ACTIVATED;
                        vEvent.m_itemIndex = pMyRecord->m_ulItemId;
                        pItem->SetText(GetItemText(pMyRecord->m_ulItemId));
                        pItem->SetData(GetItemData(pMyRecord->m_ulItemId));
                    }
                    break;

                    //
                    // Add the CN_DROP messages for Direct Manipulation
                    //
            }
            vEvent.SetEventType(vEventType);
            bProcessed = HandleWindowEvent(vEvent);
            break;
    }
    if (!bProcessed)
        lRc = wxControl::OS2WindowProc( uMsg
                                       ,wParam
                                       ,lParam
                                      );
    return lRc;
} // end of wxListCtrl::WindowProc

#endif // wxUSE_LISTCTRL
