+#endif // NM_CUSTOMDRAW supported
+
+// Necessary for drawing hrules and vrules, if specified
+void wxListCtrl::OnPaint(wxPaintEvent& event)
+{
+ const int itemCount = GetItemCount();
+ const bool drawHRules = HasFlag(wxLC_HRULES);
+ const bool drawVRules = HasFlag(wxLC_VRULES);
+
+ if (!InReportView() || !(drawHRules || drawVRules) || !itemCount)
+ {
+ event.Skip();
+ return;
+ }
+
+ wxPaintDC dc(this);
+
+ wxControl::OnPaint(event);
+
+ // Reset the device origin since it may have been set
+ dc.SetDeviceOrigin(0, 0);
+
+ wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT));
+ dc.SetPen(pen);
+ dc.SetBrush(* wxTRANSPARENT_BRUSH);
+
+ wxSize clientSize = GetClientSize();
+ wxRect itemRect;
+
+ if (drawHRules)
+ {
+ const long top = GetTopItem();
+ for ( int i = top; i < top + GetCountPerPage() + 1; i++ )
+ {
+ if (GetItemRect(i, itemRect))
+ {
+ int cy = itemRect.GetTop();
+ if (i != 0) // Don't draw the first one
+ {
+ dc.DrawLine(0, cy, clientSize.x, cy);
+ }
+ // Draw last line
+ if (i == itemCount - 1)
+ {
+ cy = itemRect.GetBottom();
+ dc.DrawLine(0, cy, clientSize.x, cy);
+ break;
+ }
+ }
+ }
+ }
+
+ if (drawVRules)
+ {
+ wxRect firstItemRect;
+ GetItemRect(0, firstItemRect);
+
+ if (GetItemRect(itemCount - 1, itemRect))
+ {
+ // this is a fix for bug 673394: erase the pixels which we would
+ // otherwise leave on the screen
+ static const int gap = 2;
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ dc.SetBrush(wxBrush(GetBackgroundColour()));
+ dc.DrawRectangle(0, firstItemRect.GetY() - gap,
+ clientSize.GetWidth(), gap);
+
+ dc.SetPen(pen);
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);
+
+ const int numCols = GetColumnCount();
+ wxVector<int> indexArray(numCols);
+ if ( !ListView_GetColumnOrderArray(GetHwnd(),
+ numCols,
+ &indexArray[0]) )
+ {
+ wxFAIL_MSG( wxT("invalid column index array in OnPaint()") );
+ return;
+ }
+
+ int x = itemRect.GetX();
+ for (int col = 0; col < numCols; col++)
+ {
+ int colWidth = GetColumnWidth(indexArray[col]);
+ x += colWidth ;
+ dc.DrawLine(x-1, firstItemRect.GetY() - gap,
+ x-1, itemRect.GetBottom());
+ }
+ }
+ }
+}
+
+WXLRESULT
+wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ switch ( nMsg )
+ {
+#ifdef WM_PRINT
+ case WM_PRINT:
+ // we should bypass our own WM_PRINT handling as we don't handle
+ // PRF_CHILDREN flag, so leave it to the native control itself
+ return MSWDefWindowProc(nMsg, wParam, lParam);
+#endif // WM_PRINT
+
+ case WM_CONTEXTMENU:
+ // because this message is propagated upwards the child-parent
+ // chain, we get it for the right clicks on the header window but
+ // this is confusing in wx as right clicking there already
+ // generates a separate wxEVT_COMMAND_LIST_COL_RIGHT_CLICK event
+ // so just ignore them
+ if ( (HWND)wParam == ListView_GetHeader(GetHwnd()) )
+ return 0;
+ //else: break
+ }
+
+ return wxControl::MSWWindowProc(nMsg, wParam, lParam);
+}
+
+// ----------------------------------------------------------------------------
+// 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( wxT("wxListCtrl::OnGetItemText not supposed to be called") );
+
+ return wxEmptyString;
+}
+
+int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const
+{
+ wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
+ -1,
+ wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden."));
+ return -1;
+}
+
+int wxListCtrl::OnGetItemColumnImage(long item, long column) const
+{
+ if (!column)
+ return OnGetItemImage(item);
+
+ return -1;
+}
+
+wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
+{
+ wxASSERT_MSG( item >= 0 && item < GetItemCount(),
+ wxT("invalid item index in OnGetItemAttr()") );
+
+ // no attributes by default
+ return NULL;
+}
+
+wxListItemAttr *wxListCtrl::DoGetItemColumnAttr(long item, long column) const
+{
+ if ( IsVirtual() )
+ return OnGetItemColumnAttr(item, column);
+
+ wxMSWListItemData * const data = MSWGetItemData(item);
+ return data ? data->attr : NULL;
+}
+
+void wxListCtrl::SetItemCount(long count)
+{
+ wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
+
+ if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count,
+ LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL) )
+ {
+ wxLogLastError(wxT("ListView_SetItemCount"));
+ }
+ m_count = count;
+ wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
+ wxT("m_count should match ListView_GetItemCount"));
+}
+
+void wxListCtrl::RefreshItem(long item)
+{
+ RefreshItems(item, item);
+}
+
+void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
+{
+ ListView_RedrawItems(GetHwnd(), itemFrom, itemTo);
+}
+
+// ----------------------------------------------------------------------------
+// wxWin <-> MSW items conversions
+// ----------------------------------------------------------------------------
+
+static void wxConvertFromMSWListItem(HWND hwndListCtrl,
+ wxListItem& info,
+ LV_ITEM& lvItem)
+{
+ wxMSWListItemData *internaldata =
+ (wxMSWListItemData *) lvItem.lParam;
+
+ if (internaldata)
+ info.m_data = internaldata->lParam;
+
+ info.m_mask = 0;
+ info.m_state = 0;
+ info.m_stateMask = 0;
+ info.m_itemId = lvItem.iItem;
+
+ long oldMask = lvItem.mask;
+
+ bool needText = false;
+ if (hwndListCtrl != 0)
+ {
+ if ( lvItem.mask & LVIF_TEXT )
+ needText = false;
+ else
+ needText = true;
+
+ if ( needText )
+ {
+ lvItem.pszText = new wxChar[513];
+ lvItem.cchTextMax = 512;
+ }
+ lvItem.mask |= LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
+ ::SendMessage(hwndListCtrl, LVM_GETITEM, 0, (LPARAM)& lvItem);
+ }
+
+ if ( lvItem.mask & LVIF_STATE )
+ {
+ info.m_mask |= wxLIST_MASK_STATE;
+
+ if ( lvItem.stateMask & LVIS_CUT)
+ {
+ info.m_stateMask |= wxLIST_STATE_CUT;
+ if ( lvItem.state & LVIS_CUT )
+ info.m_state |= wxLIST_STATE_CUT;
+ }
+ if ( lvItem.stateMask & LVIS_DROPHILITED)
+ {
+ info.m_stateMask |= wxLIST_STATE_DROPHILITED;
+ if ( lvItem.state & LVIS_DROPHILITED )
+ info.m_state |= wxLIST_STATE_DROPHILITED;
+ }
+ if ( lvItem.stateMask & LVIS_FOCUSED)
+ {
+ info.m_stateMask |= wxLIST_STATE_FOCUSED;
+ if ( lvItem.state & LVIS_FOCUSED )
+ info.m_state |= wxLIST_STATE_FOCUSED;
+ }
+ if ( lvItem.stateMask & LVIS_SELECTED)
+ {
+ info.m_stateMask |= wxLIST_STATE_SELECTED;
+ if ( lvItem.state & LVIS_SELECTED )
+ info.m_state |= wxLIST_STATE_SELECTED;
+ }
+ }
+
+ if ( lvItem.mask & LVIF_TEXT )
+ {
+ info.m_mask |= wxLIST_MASK_TEXT;
+ info.m_text = lvItem.pszText;
+ }
+ if ( lvItem.mask & LVIF_IMAGE )
+ {
+ info.m_mask |= wxLIST_MASK_IMAGE;
+ info.m_image = lvItem.iImage;
+ }
+ if ( lvItem.mask & LVIF_PARAM )
+ info.m_mask |= wxLIST_MASK_DATA;
+ if ( lvItem.mask & LVIF_DI_SETITEM )
+ info.m_mask |= wxLIST_SET_ITEM;
+ info.m_col = lvItem.iSubItem;
+
+ if (needText)
+ {
+ if (lvItem.pszText)
+ delete[] lvItem.pszText;
+ }
+ lvItem.mask = oldMask;
+}
+
+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)
+{
+ if ( ctrl->InReportView() )
+ {
+ wxASSERT_MSG( 0 <= info.m_col && info.m_col < ctrl->GetColumnCount(),
+ "wxListCtrl column index out of bounds" );
+ }
+ else // not in report view
+ {
+ wxASSERT_MSG( info.m_col == 0, "columns only exist in report view" );
+ }
+
+ lvItem.iItem = (int) info.m_itemId;
+
+ lvItem.iImage = info.m_image;
+ lvItem.stateMask = 0;
+ lvItem.state = 0;
+ lvItem.mask = 0;
+ lvItem.iSubItem = info.m_col;
+
+ if (info.m_mask & wxLIST_MASK_STATE)
+ {
+ lvItem.mask |= LVIF_STATE;
+
+ wxConvertToMSWFlags(info.m_state, info.m_stateMask, lvItem);
+ }
+
+ if (info.m_mask & wxLIST_MASK_TEXT)
+ {
+ lvItem.mask |= LVIF_TEXT;
+ if ( ctrl->HasFlag(wxLC_USER_TEXT) )
+ {
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ }
+ else
+ {
+ // pszText is not const, hence the cast
+ lvItem.pszText = (wxChar *)info.m_text.wx_str();
+ if ( lvItem.pszText )
+ lvItem.cchTextMax = info.m_text.length();
+ else
+ lvItem.cchTextMax = 0;
+ }
+ }
+ if (info.m_mask & wxLIST_MASK_IMAGE)
+ lvItem.mask |= LVIF_IMAGE;
+}
+
+static void wxConvertToMSWListCol(HWND hwndList,
+ int col,
+ const wxListItem& item,
+ LV_COLUMN& lvCol)
+{
+ wxZeroMemory(lvCol);
+
+ if ( item.m_mask & wxLIST_MASK_TEXT )
+ {
+ lvCol.mask |= LVCF_TEXT;
+ lvCol.pszText = (wxChar *)item.m_text.wx_str(); // cast is safe
+ }
+
+ if ( item.m_mask & wxLIST_MASK_FORMAT )
+ {
+ lvCol.mask |= LVCF_FMT;
+
+ if ( item.m_format == wxLIST_FORMAT_LEFT )
+ lvCol.fmt = LVCFMT_LEFT;
+ else if ( item.m_format == wxLIST_FORMAT_RIGHT )
+ lvCol.fmt = LVCFMT_RIGHT;
+ else if ( item.m_format == wxLIST_FORMAT_CENTRE )
+ lvCol.fmt = LVCFMT_CENTER;
+ }
+
+ if ( item.m_mask & wxLIST_MASK_WIDTH )
+ {
+ lvCol.mask |= LVCF_WIDTH;
+ if ( item.m_width == wxLIST_AUTOSIZE)
+ lvCol.cx = LVSCW_AUTOSIZE;
+ else if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER)
+ lvCol.cx = LVSCW_AUTOSIZE_USEHEADER;
+ else
+ lvCol.cx = item.m_width;
+ }
+
+ // see comment at the end of wxListCtrl::GetColumn()
+#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
+ if ( item.m_mask & wxLIST_MASK_IMAGE )
+ {
+ if ( wxApp::GetComCtl32Version() >= 470 )
+ {
+ lvCol.mask |= LVCF_IMAGE;
+
+ // we use LVCFMT_BITMAP_ON_RIGHT because the images on the right
+ // seem to be generally nicer than on the left and the generic
+ // version only draws them on the right (we don't have a flag to
+ // specify the image location anyhow)
+ //
+ // we don't use LVCFMT_COL_HAS_IMAGES because it doesn't seem to
+ // make any difference in my tests -- but maybe we should?
+ if ( item.m_image != -1 )
+ {
+ // as we're going to overwrite the format field, get its
+ // current value first -- unless we want to overwrite it anyhow
+ if ( !(lvCol.mask & LVCF_FMT) )
+ {
+ LV_COLUMN lvColOld;
+ wxZeroMemory(lvColOld);
+ lvColOld.mask = LVCF_FMT;
+ if ( ListView_GetColumn(hwndList, col, &lvColOld) )
+ {
+ lvCol.fmt = lvColOld.fmt;
+ }
+
+ lvCol.mask |= LVCF_FMT;
+ }
+
+ lvCol.fmt |= LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE;
+ }
+
+ lvCol.iImage = item.m_image;
+ }
+ //else: it doesn't support item images anyhow
+ }
+#endif // _WIN32_IE >= 0x0300
+}