+ // almost all messages use NM_LISTVIEW
+ NM_LISTVIEW *nmLV = (NM_LISTVIEW *)nmhdr;
+
+ // this is true for almost all events
+ event.m_item.m_data = nmLV->lParam;
+
+ switch ( nmhdr->code )
+ {
+ case LVN_BEGINRDRAG:
+ eventType = wxEVT_COMMAND_LIST_BEGIN_RDRAG;
+ // fall through
+
+ case LVN_BEGINDRAG:
+ if ( eventType == wxEVT_NULL )
+ {
+ eventType = wxEVT_COMMAND_LIST_BEGIN_DRAG;
+ }
+
+ event.m_itemIndex = nmLV->iItem;
+ event.m_pointDrag.x = nmLV->ptAction.x;
+ event.m_pointDrag.y = nmLV->ptAction.y;
+ break;
+
+ case LVN_BEGINLABELEDIT:
+ {
+ eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+ wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
+ event.m_itemIndex = event.m_item.m_itemId;
+ }
+ break;
+
+ case LVN_COLUMNCLICK:
+ eventType = wxEVT_COMMAND_LIST_COL_CLICK;
+ event.m_itemIndex = -1;
+ event.m_col = nmLV->iSubItem;
+ break;
+
+ case LVN_DELETEALLITEMS:
+ eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
+ event.m_itemIndex = -1;
+
+ FreeAllAttrs();
+
+ break;
+
+ case LVN_DELETEITEM:
+ eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
+ event.m_itemIndex = nmLV->iItem;
+
+ if ( m_hasAnyAttr )
+ {
+ delete (wxListItemAttr *)m_attrs.Delete(nmLV->iItem);
+ }
+ break;
+
+ case LVN_ENDLABELEDIT:
+ {
+ eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+ 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;
+
+ case LVN_SETDISPINFO:
+ {
+ eventType = wxEVT_COMMAND_LIST_SET_INFO;
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+ wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
+ }
+ break;
+
+ case LVN_INSERTITEM:
+ eventType = wxEVT_COMMAND_LIST_INSERT_ITEM;
+ event.m_itemIndex = nmLV->iItem;
+ break;
+
+ case LVN_ITEMCHANGED:
+ // This needs to be sent to wxListCtrl as a rather more concrete
+ // event. For now, just detect a selection or deselection.
+ if ( (nmLV->uNewState & LVIS_SELECTED) && !(nmLV->uOldState & LVIS_SELECTED) )
+ {
+ eventType = wxEVT_COMMAND_LIST_ITEM_SELECTED;
+ event.m_itemIndex = nmLV->iItem;
+ }
+ else if ( !(nmLV->uNewState & LVIS_SELECTED) && (nmLV->uOldState & LVIS_SELECTED) )
+ {
+ eventType = wxEVT_COMMAND_LIST_ITEM_DESELECTED;
+ event.m_itemIndex = nmLV->iItem;
+ }
+ else
+ {
+ return FALSE;
+ }
+ break;
+
+ case LVN_KEYDOWN:
+ {
+ LV_KEYDOWN *info = (LV_KEYDOWN *)lParam;
+ WORD wVKey = info->wVKey;
+
+ // get the current selection
+ long lItem = GetNextItem(-1,
+ wxLIST_NEXT_ALL,
+ wxLIST_STATE_SELECTED);
+
+ // <Enter> or <Space> activate the selected item if any (but
+ // not with Shift and/or Ctrl as then they have a predefined
+ // meaning for the list view)
+ if ( lItem != -1 &&
+ (wVKey == VK_RETURN || wVKey == VK_SPACE) &&
+ !(wxIsShiftDown() || wxIsCtrlDown()) )
+ {
+ eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
+ }
+ else
+ {
+ eventType = wxEVT_COMMAND_LIST_KEY_DOWN;
+ event.m_code = wxCharCodeMSWToWX(wVKey);
+ }
+
+ event.m_itemIndex =
+ event.m_item.m_itemId = lItem;
+
+ if ( lItem != -1 )
+ {
+ // fill the other fields too
+ event.m_item.m_text = GetItemText(lItem);
+ event.m_item.m_data = GetItemData(lItem);
+ }
+ }
+ break;
+
+ case NM_DBLCLK:
+ // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do
+ // anything else
+ if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
+ {
+ return TRUE;
+ }
+
+ // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event
+ // if it happened on an item (and not on empty place)
+ if ( nmLV->iItem == -1 )
+ {
+ // not on item
+ return FALSE;
+ }
+
+ eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
+ event.m_itemIndex = nmLV->iItem;
+ event.m_item.m_text = GetItemText(nmLV->iItem);
+ event.m_item.m_data = GetItemData(nmLV->iItem);
+ break;
+
+ case NM_RCLICK:
+ // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(),
+ // don't do anything else
+ if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
+ {
+ return TRUE;
+ }
+
+ // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event
+ LV_HITTESTINFO lvhti;
+ wxZeroMemory(lvhti);
+
+ ::GetCursorPos(&(lvhti.pt));
+ ::ScreenToClient(GetHwnd(),&(lvhti.pt));
+ if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
+ {
+ if ( lvhti.flags & LVHT_ONITEM )
+ {
+ eventType = wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK;
+ event.m_itemIndex = lvhti.iItem;
+ event.m_pointDrag.x = lvhti.pt.x;
+ event.m_pointDrag.y = lvhti.pt.y;
+ }
+ }
+ break;
+
+#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:
+ {
+ 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 )
+ {
+ event.m_oldItemIndex = cacheHint->iFrom;
+
+ long iMax = GetItemCount();
+ event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo
+ : iMax - 1;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+ 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 defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
+ && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+ if ( lvi.mask & LVIF_IMAGE )
+ {
+ lvi.iImage = OnGetItemImage(item);
+ }
+#endif
+
+ // 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);
+ }