#pragma hdrstop
#endif
-#ifdef __WIN95__
+#if wxUSE_LISTCTRL && defined(__WIN95__)
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/msw/private.h"
-#ifdef __GNUWIN32_OLD__
+#if ((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__))
#include "wx/msw/gnuwin32/extra.h"
#else
#include <commctrl.h>
#define LVS_EX_FULLROWSELECT 0x00000020
#endif
+#ifndef LVS_OWNERDATA
+ #define LVS_OWNERDATA 0x1000
+#endif
+
+// mingw32/cygwin don't have declarations for comctl32.dll 4.70+ stuff
+#ifndef NM_CACHEHINT
+ typedef struct tagNMLVCACHEHINT
+ {
+ NMHDR hdr;
+ int iFrom;
+ int iTo;
+ } NMLVCACHEHINT;
+
+ #define NM_CACHEHINT NMLVCACHEHINT
+#endif
+
+#ifndef LVN_ODCACHEHINT
+ #define LVN_ODCACHEHINT (-113)
+#endif
+
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
-static void wxConvertToMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& tvItem);
-static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& tvItem, HWND getFullInfo = 0);
+// convert our state and mask flags to LV_ITEM constants
+static void wxConvertToMSWFlags(long state, long mask, LV_ITEM& lvItem);
+
+// convert wxListItem to LV_ITEM
+static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
+ const wxListItem& info, LV_ITEM& lvItem);
+
+// convert LV_ITEM to wxListItem
+static void wxConvertFromMSWListItem(HWND hwndListCtrl,
+ wxListItem& info,
+ /* const */ LV_ITEM& lvItem);
// ----------------------------------------------------------------------------
// events
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT)
IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
+IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
+
+ if ( m_windowStyle & wxCLIP_SIBLINGS )
+ wstyle |= WS_CLIPSIBLINGS;
+
if ( wxStyleHasBorder(m_windowStyle) )
wstyle |= WS_BORDER;
m_baseStyle = wstyle;
if ( m_textCtrl )
{
- m_textCtrl->UnsubclassWin();
m_textCtrl->SetHWND(0);
+ m_textCtrl->UnsubclassWin();
delete m_textCtrl;
m_textCtrl = NULL;
}
if ( style & wxLC_AUTOARRANGE )
wstyle |= LVS_AUTOARRANGE;
- // Apparently, no such style (documentation wrong?)
- /*
- if ( style & wxLC_BUTTON )
- wstyle |= LVS_BUTTON;
- */
-
if ( style & wxLC_NO_SORT_HEADER )
wstyle |= LVS_NOSORTHEADER;
wstyle |= LVS_SORTDESCENDING;
}
+#if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+ if ( style & wxLC_VIRTUAL )
+ {
+ int ver = wxTheApp->GetComCtl32Version();
+ if ( ver < 470 )
+ {
+ wxLogWarning(_("Please install a newer version of comctl32.dll\n(at least version 4.70 is required but you have %d.%02d)\nor this program won't operate correctly."),
+ ver / 100, ver % 100);
+ }
+
+ wstyle |= LVS_OWNERDATA;
+ }
+#endif
+
return wstyle;
}
}
else
{
- wxConvertFromMSWListItem(this, info, lvItem);
+ // give NULL as hwnd as we already have everything we need
+ wxConvertFromMSWListItem(NULL, info, lvItem);
}
if (lvItem.pszText)
// Sets the item state
bool wxListCtrl::SetItemState(long item, long state, long stateMask)
{
- wxListItem info;
+ // NB: don't use SetItem() here as it doesn't work with the virtual list
+ // controls
+ LV_ITEM lvItem;
+ wxZeroMemory(lvItem);
- info.m_mask = wxLIST_MASK_STATE;
- info.m_state = state;
- info.m_stateMask = stateMask;
- info.m_itemId = item;
+ wxConvertToMSWFlags(state, stateMask, lvItem);
- return SetItem(info);
+ if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE,
+ (WPARAM)item, (LPARAM)&lvItem) )
+ {
+ wxLogLastError(_T("ListView_SetItemState"));
+
+ return FALSE;
+ }
+
+ return TRUE;
}
// Sets the item image
{
wxASSERT( (textControlClass->IsKindOf(CLASSINFO(wxTextCtrl))) );
+ // VS: ListView_EditLabel requires that the list has focus.
+ SetFocus();
HWND hWnd = (HWND) ListView_EditLabel(GetHwnd(), item);
if (m_textCtrl)
{
- m_textCtrl->UnsubclassWin();
m_textCtrl->SetHWND(0);
+ m_textCtrl->UnsubclassWin();
delete m_textCtrl;
m_textCtrl = NULL;
}
// End label editing, optionally cancelling the edit
bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel))
{
- wxFAIL;
-
- /* I don't know how to implement this: there's no such macro as ListView_EndEditLabelNow.
- * ???
- bool success = (ListView_EndEditLabelNow(GetHwnd(), cancel) != 0);
-
- if (m_textCtrl)
- {
- m_textCtrl->UnsubclassWin();
- m_textCtrl->SetHWND(0);
- delete m_textCtrl;
- m_textCtrl = NULL;
- }
- return success;
- */
+ wxFAIL_MSG( _T("not implemented") );
+
return FALSE;
}
-
// Ensures this item is visible
bool wxListCtrl::EnsureVisible(long item)
{
- return (ListView_EnsureVisible(GetHwnd(), (int) item, FALSE) != 0);
+ return ListView_EnsureVisible(GetHwnd(), (int) item, FALSE) != 0;
}
// Find an item whose label matches this string, starting from the item after 'start'
// -1 otherwise.
long wxListCtrl::InsertItem(wxListItem& info)
{
+ wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") );
+
LV_ITEM item;
wxConvertToMSWListItem(this, info, item);
{
eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
+ wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
+ event.m_itemIndex = event.m_item.m_itemId;
}
break;
{
eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item);
+ wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
if ( info->item.pszText == NULL || info->item.iItem == -1 )
return FALSE;
+
+ event.m_itemIndex = event.m_item.m_itemId;
}
break;
{
eventType = wxEVT_COMMAND_LIST_SET_INFO;
LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
+ wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
}
break;
- case LVN_GETDISPINFO:
- // this provokes stack overflow: indeed, wxConvertFromMSWListItem()
- // sends us WM_NOTIFY! As it doesn't do anything for now, just leave
- // it out.
-#if 0
- {
- // TODO: some text buffering here, I think
- // TODO: API for getting Windows to retrieve values
- // on demand.
- eventType = wxEVT_COMMAND_LIST_GET_INFO;
- LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
- break;
- }
-#endif // 0
- return FALSE;
-
-
case LVN_INSERTITEM:
eventType = wxEVT_COMMAND_LIST_INSERT_ITEM;
event.m_itemIndex = nmLV->iItem;
!(wxIsShiftDown() || wxIsCtrlDown()) )
{
eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
- event.m_itemIndex = lItem;
}
else
{
event.m_code = wxCharCodeMSWToWX(wVKey);
}
+ event.m_itemIndex =
+ event.m_item.m_itemId = lItem;
+
if ( lItem != -1 )
{
// fill the other fields too
break;
#endif // 0
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300
+#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
+ && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
case NM_CUSTOMDRAW:
+ *result = OnCustomDraw(lParam);
+
+ return TRUE;
+#endif // _WIN32_IE >= 0x300
+
+ case LVN_ODCACHEHINT:
{
- LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
- NMCUSTOMDRAW& nmcd = lplvcd->nmcd;
- switch( nmcd.dwDrawStage )
+ const NM_CACHEHINT *cacheHint = (NM_CACHEHINT *)lParam;
+
+ eventType = wxEVT_COMMAND_LIST_CACHE_HINT;
+
+ // we get some really stupid cache hints like ones for items in
+ // range 0..0 for an empty control or, after deleting an item,
+ // for items in invalid range - filter this garbage out
+ if ( cacheHint->iFrom < cacheHint->iTo )
{
- case CDDS_PREPAINT:
- // if we've got any items with non standard attributes,
- // notify us before painting each item
- *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
- : CDRF_DODEFAULT;
- return TRUE;
-
- case CDDS_ITEMPREPAINT:
- {
- wxListItemAttr *attr =
- (wxListItemAttr *)m_attrs.Get(nmcd.dwItemSpec);
-
- if ( !attr )
- {
- // nothing to do for this item
- return CDRF_DODEFAULT;
- }
-
- HFONT hFont;
- wxColour colText, colBack;
- if ( attr->HasFont() )
- {
- wxFont font = attr->GetFont();
- hFont = (HFONT)font.GetResourceHandle();
- }
- else
- {
- hFont = 0;
- }
-
- if ( attr->HasTextColour() )
- {
- colText = attr->GetTextColour();
- }
- else
- {
- colText = GetTextColour();
- }
-
- if ( attr->HasBackgroundColour() )
- {
- colBack = attr->GetBackgroundColour();
- }
- else
- {
- colBack = GetBackgroundColour();
- }
-
- // note that if we wanted to set colours for
- // individual columns (subitems), we would have
- // returned CDRF_NOTIFYSUBITEMREDRAW from here
- if ( hFont )
- {
- ::SelectObject(nmcd.hdc, hFont);
-
- *result = CDRF_NEWFONT;
- }
- else
- {
- *result = CDRF_DODEFAULT;
- }
-
- lplvcd->clrText = wxColourToRGB(colText);
- lplvcd->clrTextBk = wxColourToRGB(colBack);
-
- return TRUE;
- }
-
- default:
- *result = CDRF_DODEFAULT;
- return TRUE;
+ event.m_oldItemIndex = cacheHint->iFrom;
+
+ long iMax = GetItemCount();
+ event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo
+ : iMax - 1;
+ }
+ else
+ {
+ return FALSE;
}
}
-// break; // can never be reached
-#endif // _WIN32_IE >= 0x300
+ break;
+
+ case LVN_GETDISPINFO:
+ if ( IsVirtual() )
+ {
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+
+ LV_ITEM& lvi = info->item;
+ long item = lvi.iItem;
+
+ if ( lvi.mask & LVIF_TEXT )
+ {
+ wxString text = OnGetItemText(item, lvi.iSubItem);
+ wxStrncpy(lvi.pszText, text, lvi.cchTextMax);
+ }
+
+ if ( lvi.mask & LVIF_IMAGE )
+ {
+ lvi.iImage = OnGetItemImage(item);
+ }
+
+ // a little dose of healthy paranoia: as we never use
+ // LVM_SETCALLBACKMASK we're not supposed to get these ones
+ wxASSERT_MSG( !(lvi.mask & LVIF_STATE),
+ _T("we don't support state callbacks yet!") );
+
+ return TRUE;
+ }
+ // fall through
default:
return wxControl::MSWOnNotify(idCtrl, lParam, result);
return TRUE;
- case LVN_GETDISPINFO:
- {
- LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- if ( info->item.mask & LVIF_TEXT )
- {
- if ( !event.m_item.m_text.IsNull() )
- {
- info->item.pszText = AddPool(event.m_item.m_text);
- info->item.cchTextMax = wxStrlen(info->item.pszText) + 1;
- }
- }
- // wxConvertToMSWListItem(this, event.m_item, info->item);
- break;
- }
case LVN_ENDLABELEDIT:
- {
- *result = event.IsAllowed();
- return TRUE;
- }
+ // logic here is inversed compared to all the other messages
+ *result = event.IsAllowed();
+
+ return TRUE;
}
*result = !event.IsAllowed();
return TRUE;
}
-wxChar *wxListCtrl::AddPool(const wxString& str)
+#if defined(_WIN32_IE) && _WIN32_IE >= 0x300
+
+WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
{
- // Remove the first element if 3 strings exist
- if ( m_stringPool.Number() == 3 )
+ LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
+ NMCUSTOMDRAW& nmcd = lplvcd->nmcd;
+ switch ( nmcd.dwDrawStage )
{
- wxNode *node = m_stringPool.First();
- delete[] (char *)node->Data();
- delete node;
+ case CDDS_PREPAINT:
+ // if we've got any items with non standard attributes,
+ // notify us before painting each item
+ //
+ // for virtual controls, always suppose that we have attributes as
+ // there is no way to check for this
+ return IsVirtual() || m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
+ : CDRF_DODEFAULT;
+
+ case CDDS_ITEMPREPAINT:
+ {
+ size_t item = (size_t)nmcd.dwItemSpec;
+ if ( item >= (size_t)GetItemCount() )
+ {
+ // we get this message with item == 0 for an empty control,
+ // we must ignore it as calling OnGetItemAttr() would be
+ // wrong
+ return CDRF_DODEFAULT;
+ }
+
+ wxListItemAttr *attr =
+ IsVirtual() ? OnGetItemAttr(item)
+ : (wxListItemAttr *)m_attrs.Get(item);
+
+ if ( !attr )
+ {
+ // nothing to do for this item
+ return CDRF_DODEFAULT;
+ }
+
+ HFONT hFont;
+ wxColour colText, colBack;
+ if ( attr->HasFont() )
+ {
+ wxFont font = attr->GetFont();
+ hFont = (HFONT)font.GetResourceHandle();
+ }
+ else
+ {
+ hFont = 0;
+ }
+
+ if ( attr->HasTextColour() )
+ {
+ colText = attr->GetTextColour();
+ }
+ else
+ {
+ colText = GetTextColour();
+ }
+
+ if ( attr->HasBackgroundColour() )
+ {
+ colBack = attr->GetBackgroundColour();
+ }
+ else
+ {
+ colBack = GetBackgroundColour();
+ }
+
+ lplvcd->clrText = wxColourToRGB(colText);
+ lplvcd->clrTextBk = wxColourToRGB(colBack);
+
+ // note that if we wanted to set colours for
+ // individual columns (subitems), we would have
+ // returned CDRF_NOTIFYSUBITEMREDRAW from here
+ if ( hFont )
+ {
+ ::SelectObject(nmcd.hdc, hFont);
+
+ return CDRF_NEWFONT;
+ }
+ }
+ // fall through to return CDRF_DODEFAULT
+
+ default:
+ return CDRF_DODEFAULT;
}
- wxNode *node = m_stringPool.Add(WXSTRINGCAST str);
- return (wxChar *)node->Data();
}
+#endif // NM_CUSTOMDRAW supported
+
// Necessary for drawing hrules and vrules, if specified
void wxListCtrl::OnPaint(wxPaintEvent& event)
{
}
}
+// ----------------------------------------------------------------------------
+// virtual list controls
+// ----------------------------------------------------------------------------
+
+wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) 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( _T("not supposed to be called") );
+
+ return wxEmptyString;
+}
+
+int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const
+{
+ // same as above
+ wxFAIL_MSG( _T("not supposed to be called") );
+
+ return -1;
+}
+
+wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
+{
+ wxASSERT_MSG( item >= 0 && item < GetItemCount(),
+ _T("invalid item index in OnGetItemAttr()") );
+
+ // no attributes by default
+ return NULL;
+}
+
+void wxListCtrl::SetItemCount(long count)
+{
+ wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
+
+ if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count, 0) )
+ {
+ wxLogLastError(_T("ListView_SetItemCount"));
+ }
+}
+
+void wxListCtrl::RefreshItem(long item)
+{
+ if ( !ListView_Update(GetHwnd(), item) )
+ {
+ wxLogLastError(_T("ListView_Update"));
+ }
+}
+
+void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
+{
+ wxRect rect1, rect2;
+ GetItemRect(itemFrom, rect1);
+ GetItemRect(itemTo, rect2);
+
+ wxRect rect = rect1;
+ rect.height = rect2.GetBottom() - rect1.GetTop();
+
+ RefreshRect(rect);
+}
+
// ----------------------------------------------------------------------------
// wxListItem
// ----------------------------------------------------------------------------
m_attr = NULL;
}
-static void wxConvertFromMSWListItem(const wxListCtrl *WXUNUSED(ctrl), wxListItem& info, LV_ITEM& lvItem, HWND getFullInfo)
+static void wxConvertFromMSWListItem(HWND hwndListCtrl,
+ wxListItem& info,
+ LV_ITEM& lvItem)
{
info.m_data = lvItem.lParam;
info.m_mask = 0;
long oldMask = lvItem.mask;
bool needText = FALSE;
- if (getFullInfo != 0)
+ if (hwndListCtrl != 0)
{
if ( lvItem.mask & LVIF_TEXT )
needText = FALSE;
lvItem.pszText = new wxChar[513];
lvItem.cchTextMax = 512;
}
- // lvItem.mask |= TVIF_HANDLE | TVIF_STATE | TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_PARAM;
lvItem.mask |= LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
- ::SendMessage(getFullInfo, LVM_GETITEM, 0, (LPARAM)& lvItem);
+ ::SendMessage(hwndListCtrl, LVM_GETITEM, 0, (LPARAM)& lvItem);
}
if ( lvItem.mask & LVIF_STATE )
lvItem.mask = oldMask;
}
-static void wxConvertToMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& lvItem)
+static void wxConvertToMSWFlags(long state, long stateMask, LV_ITEM& lvItem)
+{
+ if (stateMask & wxLIST_STATE_CUT)
+ {
+ lvItem.stateMask |= LVIS_CUT;
+ if (state & wxLIST_STATE_CUT)
+ lvItem.state |= LVIS_CUT;
+ }
+ if (stateMask & wxLIST_STATE_DROPHILITED)
+ {
+ lvItem.stateMask |= LVIS_DROPHILITED;
+ if (state & wxLIST_STATE_DROPHILITED)
+ lvItem.state |= LVIS_DROPHILITED;
+ }
+ if (stateMask & wxLIST_STATE_FOCUSED)
+ {
+ lvItem.stateMask |= LVIS_FOCUSED;
+ if (state & wxLIST_STATE_FOCUSED)
+ lvItem.state |= LVIS_FOCUSED;
+ }
+ if (stateMask & wxLIST_STATE_SELECTED)
+ {
+ lvItem.stateMask |= LVIS_SELECTED;
+ if (state & wxLIST_STATE_SELECTED)
+ lvItem.state |= LVIS_SELECTED;
+ }
+}
+
+static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
+ const wxListItem& info,
+ LV_ITEM& lvItem)
{
lvItem.iItem = (int) info.m_itemId;
if (info.m_mask & wxLIST_MASK_STATE)
{
lvItem.mask |= LVIF_STATE;
- if (info.m_stateMask & wxLIST_STATE_CUT)
- {
- lvItem.stateMask |= LVIS_CUT;
- if (info.m_state & wxLIST_STATE_CUT)
- lvItem.state |= LVIS_CUT;
- }
- if (info.m_stateMask & wxLIST_STATE_DROPHILITED)
- {
- lvItem.stateMask |= LVIS_DROPHILITED;
- if (info.m_state & wxLIST_STATE_DROPHILITED)
- lvItem.state |= LVIS_DROPHILITED;
- }
- if (info.m_stateMask & wxLIST_STATE_FOCUSED)
- {
- lvItem.stateMask |= LVIS_FOCUSED;
- if (info.m_state & wxLIST_STATE_FOCUSED)
- lvItem.state |= LVIS_FOCUSED;
- }
- if (info.m_stateMask & wxLIST_STATE_SELECTED)
- {
- lvItem.stateMask |= LVIS_SELECTED;
- if (info.m_state & wxLIST_STATE_SELECTED)
- lvItem.state |= LVIS_SELECTED;
- }
+
+ wxConvertToMSWFlags(info.m_state, info.m_stateMask, lvItem);
}
if (info.m_mask & wxLIST_MASK_TEXT)
}
else
{
- lvItem.pszText = WXSTRINGCAST info.m_text;
+ // pszText is not const, hence the cast
+ lvItem.pszText = (wxChar *)info.m_text.c_str();
if ( lvItem.pszText )
lvItem.cchTextMax = info.m_text.Length();
else
m_cancelled = FALSE;
}
-#endif // __WIN95__
-
+#endif // wxUSE_LISTCTRL