X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0e320a79f187558effb04d92020b470372bbe456..336aecf1c2f69fa10e3271ea94dac7c545bf6300:/src/os2/listctrl.cpp diff --git a/src/os2/listctrl.cpp b/src/os2/listctrl.cpp index d1d8445652..e51c44e271 100644 --- a/src/os2/listctrl.cpp +++ b/src/os2/listctrl.cpp @@ -1,334 +1,1772 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: listctrl.cpp -// Purpose: wxListCtrl. See also Robert's generic wxListCtrl -// Author: AUTHOR +// Name: src/os2/listctrl.cpp +// Purpose: wxListCtrl +// Author: David Webster // Modified by: -// Created: ??/??/98 +// Created: 01/21/03 // RCS-ID: $Id$ -// Copyright: (c) AUTHOR -// Licence: wxWindows licence +// Copyright: (c) David Webster +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "listctrl.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ -#include "wx/stubs/textctrl.h" -#include "wx/stubs/listctrl.h" +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- -#if !USE_SHARED_LIBRARY -IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) -IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) +// 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 -wxListCtrl::wxListCtrl() +#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 { - m_imageListNormal = NULL; - m_imageListSmall = NULL; - m_imageListState = NULL; - m_baseStyle = 0; - m_colCount = 0; -} + 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; -bool wxListCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, - long style, const wxValidator& validator, const wxString& name) +///////////////////////////////////////////////////////////////////////////// +// 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 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 { - m_imageListNormal = NULL; - m_imageListSmall = NULL; - m_imageListState = NULL; - m_colCount = 0; +public: - SetValidator(validator); - SetName(name); + CListItemInternalData(): m_pAttr(NULL) + ,m_lParam(0) + {} - m_windowStyle = style; + ~CListItemInternalData() + { + delete m_pAttr; + m_pAttr = NULL; + } - SetParent(parent); + 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 - m_windowId = (id == -1) ? NewControlId() : id; +///////////////////////////////////////////////////////////////////////////// +// 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 - if (parent) parent->AddChild(this); +// ---------------------------------------------------------------------------- +// private helper functions +// ---------------------------------------------------------------------------- - // TODO create list control - return TRUE; -} +///////////////////////////////////////////////////////////////////////////// +// +// 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 -wxListCtrl::~wxListCtrl() +///////////////////////////////////////////////////////////////////////////// +// +// 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 -// Add or remove a single window style -void wxListCtrl::SetSingleStyle(long style, bool add) -{ - long flag = GetWindowStyleFlag(); - - // Get rid of conflicting styles - if ( add ) - { - if ( style & wxLC_MASK_TYPE) - flag = flag & ~wxLC_MASK_TYPE ; - if ( style & wxLC_MASK_ALIGN ) - flag = flag & ~wxLC_MASK_ALIGN ; - if ( style & wxLC_MASK_SORT ) - flag = flag & ~wxLC_MASK_SORT ; +///////////////////////////////////////////////////////////////////////////// +// +// 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( _T("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 - if ( flag & style ) - { - if ( !add ) - flag -= style; - } - else - { - if ( add ) - { - flag |= style; - } - } - - m_windowStyle = flag; - - /* TODO RecreateWindow(); */ -} +///////////////////////////////////////////////////////////////////////////// +// +// 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 + + +IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) +IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) + +IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) + +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(); + delete m_pTextCtrl; + m_pTextCtrl = NULL; + } + + 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 flag) +void wxListCtrl::SetWindowStyleFlag ( + long lFlag +) +{ + m_windowStyle = lFlag; + UpdateStyle(); +} // end of wxListCtrl::SetWindowStyleFlag + +long wxListCtrl::ConvertToOS2Style ( + long& rOldStyle +, long lStyle +) const { - m_windowStyle = flag; + 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; - /* TODO RecreateWindow(); */ -} + 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 col, wxListItem& item) const +bool wxListCtrl::GetColumn ( int nCol, wxListItem& rItem ) const { - // TODO - return FALSE; -} + 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 col, wxListItem& item) +bool wxListCtrl::SetColumn ( int nCol, wxListItem& rItem ) { - // TODO - return FALSE; -} + 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 col) const +int wxListCtrl::GetColumnWidth ( int nCol ) const { - // TODO - return 0; -} + 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 col, int width) +bool wxListCtrl::SetColumnWidth ( int nCol, int nWidth ) { - // TODO - return FALSE; -} + 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 +int wxListCtrl::GetCountPerPage () const { - // TODO - return 0; -} + 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_textCtrl; + return m_pTextCtrl; } // Gets information about the item -bool wxListCtrl::GetItem(wxListItem& info) const +bool wxListCtrl::GetItem ( wxListItem& rInfo ) const { - // TODO - return FALSE; -} + 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& info) +bool wxListCtrl::SetItem ( wxListItem& rInfo ) { - // TODO - return FALSE; -} - -long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId) -{ - wxListItem info; - info.m_text = label; - info.m_mask = wxLIST_MASK_TEXT; - info.m_itemId = index; - info.m_col = col; - if ( imageId > -1 ) - { - info.m_image = imageId; - info.m_mask |= wxLIST_MASK_IMAGE; - } - return SetItem(info); -} + 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 item, long stateMask) const +int wxListCtrl::GetItemState ( + long lItem +, long lStateMask +) const { - wxListItem info; + wxListItem vInfo; - info.m_mask = wxLIST_MASK_STATE ; - info.m_stateMask = stateMask; - info.m_itemId = item; + vInfo.m_mask = wxLIST_MASK_STATE; + vInfo.m_stateMask = lStateMask; + vInfo.m_itemId = lItem; - if (!GetItem(info)) - return 0; - - return info.m_state; -} + if (!GetItem(vInfo)) + return 0; + return vInfo.m_state; +} // end of wxListCtrl::GetItemState // Sets the item state -bool wxListCtrl::SetItemState(long item, long state, long stateMask) +bool wxListCtrl::SetItemState ( long lItem, long lState, long lStateMask ) { - wxListItem info; - - info.m_mask = wxLIST_MASK_STATE ; - info.m_state = state; - info.m_stateMask = stateMask; - info.m_itemId = item; - - return SetItem(info); -} + 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 item, int image, int selImage) +bool wxListCtrl::SetItemImage ( + long lItem +, int nImage +, int WXUNUSED(nSelImage)) { - wxListItem info; + return SetItemColumnInfo(lItem, 0, nImage); +} // end of wxListCtrl::SetItemImage - info.m_mask = wxLIST_MASK_IMAGE ; - info.m_image = image; - info.m_itemId = item; +// Sets the item image +bool wxListCtrl::SetItemColumnImage ( + long lItem +, long lColumn +, int nImage +{ + wxListItem vInfo; - return SetItem(info); -} + 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 item) const +wxString wxListCtrl::GetItemText ( + long lItem +) const { - wxListItem info; + wxListItem vInfo; - info.m_mask = wxLIST_MASK_TEXT ; - info.m_itemId = item; + vInfo.m_mask = wxLIST_MASK_TEXT; + vInfo.m_itemId = lItem; - if (!GetItem(info)) - return wxString(""); - return info.m_text; -} + if (!GetItem(vInfo)) + return wxEmptyString; + return vInfo.m_text; +} // end of wxListCtrl::GetItemText // Sets the item text -void wxListCtrl::SetItemText(long item, const wxString& str) +void wxListCtrl::SetItemText ( + long lItem +, const wxString& rsStr +) { - wxListItem info; + wxListItem vInfo; - info.m_mask = wxLIST_MASK_TEXT ; - info.m_itemId = item; - info.m_text = str; - - SetItem(info); -} + 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 item) const +long wxListCtrl::GetItemData ( + long lItem +) const { - wxListItem info; + wxListItem vInfo; - info.m_mask = wxLIST_MASK_DATA ; - info.m_itemId = item; - - if (!GetItem(info)) - return 0; - return info.m_data; -} + 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::SetItemData(long item, long data) +bool wxListCtrl::SetItemPtrData ( + long lItem +, wxUIntPtr lData +) { - wxListItem info; + wxListItem vInfo; - info.m_mask = wxLIST_MASK_DATA ; - info.m_itemId = item; - info.m_data = data; - - return SetItem(info); -} + 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 item, wxRect& rect, int code) const +bool wxListCtrl::GetItemRect ( long lItem, + wxRect& rRect, + int nCode ) const { - // TODO - return FALSE; -} + 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 item, wxPoint& pos) const +bool wxListCtrl::GetItemPosition ( long lItem, wxPoint& rPos ) const { - // TODO - return FALSE; -} + 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 item, const wxPoint& pos) +bool wxListCtrl::SetItemPosition ( long lItem, const wxPoint& rPos ) { - // TODO - return FALSE; -} + // + // 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 +int wxListCtrl::GetItemCount () const { - // TODO - return FALSE; -} + 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 small is TRUE, gets the spacing for the small icon +// If bIsSmall is true, gets the spacing for the small icon // view, otherwise the large icon view. -int wxListCtrl::GetItemSpacing(bool isSmall) const +int wxListCtrl::GetItemSpacing ( bool bIsSmall ) const { - // TODO - return FALSE; -} + 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 +int wxListCtrl::GetSelectedItemCount () const { - // TODO - return FALSE; -} + 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 wxListCtrl::GetTextColour () const { - // TODO - return wxColour(); -} + 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& col) +void wxListCtrl::SetTextColour ( + const wxColour& rCol +) { - // TODO -} + 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 +long wxListCtrl::GetTopItem () const { - // TODO - return 0; -} + 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 @@ -338,259 +1776,989 @@ long wxListCtrl::GetTopItem() const // 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 item, int geom, int state) const +long wxListCtrl::GetNextItem ( + long lItem +, int WXUNUSED(nGeom) +, int WXUNUSED(nState) +) const { - // TODO - return 0; -} - -wxImageList *wxListCtrl::GetImageList(int which) 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 ( which == wxIMAGE_LIST_NORMAL ) + if (nWhich == wxIMAGE_LIST_NORMAL ) { - return m_imageListNormal; - } - else if ( which == wxIMAGE_LIST_SMALL ) + return m_pImageListNormal; + } + else if (nWhich == wxIMAGE_LIST_SMALL ) { - return m_imageListSmall; - } - else if ( which == wxIMAGE_LIST_STATE ) + return m_pImageListSmall; + } + else if (nWhich == wxIMAGE_LIST_STATE ) { - return m_imageListState; - } - return NULL; -} + return m_pImageListState; + } + return NULL; +} // end of wxListCtrl::GetImageList -void wxListCtrl::SetImageList(wxImageList *imageList, int which) +void wxListCtrl::SetImageList ( wxImageList* pImageList, + int nWhich ) { - int flags = 0; - if ( which == wxIMAGE_LIST_NORMAL ) + if (nWhich == wxIMAGE_LIST_NORMAL) { - m_imageListNormal = imageList; - } - else if ( which == wxIMAGE_LIST_SMALL ) + if (m_bOwnsImageListNormal) + delete m_pImageListNormal; + m_pImageListNormal = pImageList; + m_bOwnsImageListNormal = false; + } + else if (nWhich == wxIMAGE_LIST_SMALL) { - m_imageListSmall = imageList; - } - else if ( which == wxIMAGE_LIST_STATE ) + if (m_bOwnsImageListSmall) + delete m_pImageListSmall; + m_pImageListSmall = pImageList; + m_bOwnsImageListSmall = false; + } + else if (nWhich == wxIMAGE_LIST_STATE) { - m_imageListState = imageList; - } - // TODO set image list -} + 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 flag) +bool wxListCtrl::Arrange ( int nFlag ) { - // TODO - return FALSE; -} + 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 item) +bool wxListCtrl::DeleteItem ( long lItem ) { - // TODO - return FALSE; -} + 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() +bool wxListCtrl::DeleteAllItems () { - // TODO - return FALSE; -} + return((LONG)::WinSendMsg( GetHWND() + ,CM_REMOVERECORD + ,NULL + ,MPFROM2SHORT(0, CMA_FREE) + ) != -1L); +} // end of wxListCtrl::DeleteAllItems // Deletes all items -bool wxListCtrl::DeleteAllColumns() +bool wxListCtrl::DeleteAllColumns () { - // TODO - return FALSE; -} + 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 col) +bool wxListCtrl::DeleteColumn ( int nCol ) { - // TODO - return FALSE; -} + 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() +void wxListCtrl::ClearAll () { DeleteAllItems(); - if ( m_colCount > 0 ) + if (m_nColCount > 0) DeleteAllColumns(); -} - -// Edit the label -wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) +} // 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) +) { - // TODO - return NULL; -} - -// End label editing, optionally cancelling the edit -bool wxListCtrl::EndEditLabel(bool cancel) + 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) ) { - // TODO - return FALSE; -} + ::WinSendMsg( GetHWND() + ,CM_CLOSEEDIT + ,(MPARAM)0 + ,(MPARAM)0 + ); + return true; +} // end of wxListCtrl::EndEditLabel // Ensures this item is visible -bool wxListCtrl::EnsureVisible(long item) +bool wxListCtrl::EnsureVisible ( long lItem ) { - // TODO - return FALSE; -} + 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 start, const wxString& str, bool partial) +long wxListCtrl::FindItem ( + long lStart +, const wxString& rsStr +, bool bPartial +) { - // TODO - return FALSE; -} + 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 start, long data) +long wxListCtrl::FindItem ( + long lStart +, long lData +) { - // TODO - return 0; -} + 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 start, const wxPoint& pt, int direction) +long wxListCtrl::FindItem ( + long lStart +, const wxPoint& rPoint +, int nDirection +) { - // TODO - return 0; -} + 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& point, int& flags) +long wxListCtrl::HitTest ( + const wxPoint& rPoint +, int& WXUNUSED(rFlags) +) { - // TODO - return 0; -} + 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& info) +long wxListCtrl::InsertItem ( + wxListItem& rInfo +) { - // TODO - return 0; -} - -long wxListCtrl::InsertItem(long index, const wxString& label) + wxASSERT_MSG( !IsVirtual(), _T("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 wether 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 info; - info.m_text = label; - info.m_mask = wxLIST_MASK_TEXT; - info.m_itemId = index; - return InsertItem(info); -} + 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 index, int imageIndex) +long wxListCtrl::InsertItem ( + long lIndex +, int nImageIndex +) { - wxListItem info; - info.m_image = imageIndex; - info.m_mask = wxLIST_MASK_IMAGE; - info.m_itemId = index; - return InsertItem(info); -} + wxListItem vInfo; -// Inserts an image/string item -long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex) -{ - wxListItem info; - info.m_image = imageIndex; - info.m_text = label; - info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT; - info.m_itemId = index; - return InsertItem(info); -} + vInfo.m_image = nImageIndex; + vInfo.m_mask = wxLIST_MASK_IMAGE; + vInfo.m_itemId = lIndex; + return InsertItem(vInfo); +} // end of wxListCtrl::InsertItem -// For list view mode (only), inserts a column. -long wxListCtrl::InsertColumn(long col, wxListItem& item) +// Inserts an image/string item +long wxListCtrl::InsertItem ( + long lIndex +, const wxString& rsLabel +, int nImageIndex +) { - // TODO - return 0; -} + 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; -long wxListCtrl::InsertColumn(long col, const wxString& heading, int format, - int width) + 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 ) { - wxListItem item; - item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT; - item.m_text = heading; - if ( width > -1 ) - { - item.m_mask |= wxLIST_MASK_WIDTH; - item.m_width = width; - } - item.m_format = format; + SInternalDataSort vInternalData; - return InsertColumn(col, item); -} + 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(_T("CM_SORTRECORD failed")); + return false; + } + return true; +} // end of wxListCtrl::SortItems -// Scrolls the list control. If in icon, small icon or report view mode, -// x specifies the number of pixels to scroll. If in list view mode, x -// specifies the number of columns to scroll. -// If in icon, small icon or list view mode, y specifies the number of pixels -// to scroll. If in report view mode, y specifies the number of lines to scroll. -bool wxListCtrl::ScrollList(int dx, int dy) +// ---------------------------------------------------------------------------- +// message processing +// ---------------------------------------------------------------------------- + +bool wxListCtrl::OS2Command ( WXUINT uCmd, WXWORD wId ) { - // TODO - return FALSE; -} + if (uCmd == CN_ENDEDIT) + { + wxCommandEvent vEvent( wxEVT_COMMAND_TEXT_UPDATED, wId ); -// Sort items. + 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 -// 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. +// ---------------------------------------------------------------------------- +// virtual list controls +// ---------------------------------------------------------------------------- -// data is arbitrary data to be passed to the sort function. -bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data) +wxString wxListCtrl::OnGetItemText ( + long WXUNUSED(lItem) +, long WXUNUSED(lCol) +) const { - // TODO - return FALSE; -} + // 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( _T("not supposed to be called") ); + return wxEmptyString; +} // end of wxListCtrl::OnGetItemText + +int wxListCtrl::OnGetItemImage ( + long WXUNUSED(lItem) +) const +{ + // same as above + wxFAIL_MSG( _T("not supposed to be called") ); + return -1; +} // end of wxListCtrl::OnGetItemImage + +int wxListCtrl::OnGetItemColumnImage ( + long lItem, + long lColumn +) const +{ + if (!lColumn) + return OnGetItemImage(lItem); -// List item structure -wxListItem::wxListItem() + return -1; +} // end of wxListCtrl::OnGetItemColumnImage + +wxListItemAttr* wxListCtrl::OnGetItemAttr ( + long WXUNUSED_UNLESS_DEBUG(lItem) +) const { - m_mask = 0; - m_itemId = 0; - m_col = 0; - m_state = 0; - m_stateMask = 0; - m_image = 0; - m_data = 0; + wxASSERT_MSG( lItem >= 0 && lItem < GetItemCount(), + _T("invalid item index in OnGetItemAttr()") ); - m_format = wxLIST_FORMAT_CENTRE; - m_width = 0; -} + // + // No attributes by default + // + return NULL; +} // end of wxListCtrl::OnGetItemAttr + +void wxListCtrl::SetItemCount ( + long lCount +) +{ + wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); -// List event -IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxCommandEvent) + // + // Cannot explicitly set the record count in OS/2 + // +} // end of wxListCtrl::SetItemCount -wxListEvent::wxListEvent(wxEventType commandType, int id): - wxCommandEvent(commandType, id) +void wxListCtrl::RefreshItem ( + long lItem +) { - m_code = 0; - m_itemIndex = 0; - m_col = 0; - m_cancelled = FALSE; -} + 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_COMMAND_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_COMMAND_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_COMMAND_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_COMMAND_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