1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/listctrl.cpp 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "listctrl.h" 
  22     #pragma implementation "listctrlbase.h" 
  25 // For compilers that support precompilation, includes "wx.h". 
  26 #include "wx/wxprec.h" 
  32 #if wxUSE_LISTCTRL && defined(__WIN95__) 
  38     #include "wx/settings.h" 
  41 #include "wx/textctrl.h" 
  42 #include "wx/imaglist.h" 
  43 #include "wx/listctrl.h" 
  44 #include "wx/dcclient.h" 
  46 #include "wx/msw/private.h" 
  48 #if defined(__WXWINCE__) 
  56 // include <commctrl.h> "properly" 
  57 #include "wx/msw/wrapcctl.h" 
  59 // ---------------------------------------------------------------------------- 
  61 // ---------------------------------------------------------------------------- 
  63 // convert our state and mask flags to LV_ITEM constants 
  64 static void wxConvertToMSWFlags(long state
, long mask
, LV_ITEM
& lvItem
); 
  66 // convert wxListItem to LV_ITEM 
  67 static void wxConvertToMSWListItem(const wxListCtrl 
*ctrl
, 
  68                                    const wxListItem
& info
, LV_ITEM
& lvItem
); 
  70 // convert LV_ITEM to wxListItem 
  71 static void wxConvertFromMSWListItem(HWND hwndListCtrl
, 
  73                                      /* const */ LV_ITEM
& lvItem
); 
  75 // convert our wxListItem to LV_COLUMN 
  76 static void wxConvertToMSWListCol(int col
, const wxListItem
& item
, 
  79 // ---------------------------------------------------------------------------- 
  80 // private helper classes 
  81 // ---------------------------------------------------------------------------- 
  83 // We have to handle both fooW and fooA notifications in several cases 
  84 // because of broken comctl32.dll and/or unicows.dll. This class is used to 
  85 // convert LV_ITEMA and LV_ITEMW to LV_ITEM (which is either LV_ITEMA or 
  86 // LV_ITEMW depending on wxUSE_UNICODE setting), so that it can be processed 
  87 // by wxConvertToMSWListItem(). 
  89     #define LV_ITEM_NATIVE  LV_ITEMW 
  90     #define LV_ITEM_OTHER   LV_ITEMA 
  92     #define LV_CONV_TO_WX   cMB2WX 
  93     #define LV_CONV_BUF     wxMB2WXbuf 
  95     #define LV_ITEM_NATIVE  LV_ITEMA 
  96     #define LV_ITEM_OTHER   LV_ITEMW 
  98     #define LV_CONV_TO_WX   cWC2WX 
  99     #define LV_CONV_BUF     wxWC2WXbuf 
 100 #endif // Unicode/ANSI 
 105     // default ctor, use Init() later 
 106     wxLV_ITEM() { m_buf 
= NULL
; m_pItem 
= NULL
; } 
 108     // init without conversion 
 109     void Init(LV_ITEM_NATIVE
& item
) 
 111         wxASSERT_MSG( !m_pItem
, _T("Init() called twice?") ); 
 116     // init with conversion 
 117     void Init(LV_ITEM_OTHER
& item
) 
 119         // avoid unnecessary dynamic memory allocation, jjust make m_pItem 
 120         // point to our own m_item 
 122         // memcpy() can't work if the struct sizes are different 
 123         wxCOMPILE_TIME_ASSERT( sizeof(LV_ITEM_OTHER
) == sizeof(LV_ITEM_NATIVE
), 
 124                                 CodeCantWorkIfDiffSizes
); 
 126         memcpy(&m_item
, &item
, sizeof(LV_ITEM_NATIVE
)); 
 128         // convert text from ANSI to Unicod if necessary 
 129         if ( (item
.mask 
& LVIF_TEXT
) && item
.pszText 
) 
 131             m_buf 
= new LV_CONV_BUF(wxConvLocal
.LV_CONV_TO_WX(item
.pszText
)); 
 132             m_item
.pszText 
= (wxChar 
*)m_buf
->data(); 
 136     // ctor without conversion 
 137     wxLV_ITEM(LV_ITEM_NATIVE
& item
) : m_buf(NULL
), m_pItem(&item
) { } 
 139     // ctor with conversion 
 140     wxLV_ITEM(LV_ITEM_OTHER
& item
) : m_buf(NULL
) 
 145     ~wxLV_ITEM() { delete m_buf
; } 
 147     // conversion to the real LV_ITEM 
 148     operator LV_ITEM_NATIVE
&() const { return *m_pItem
; } 
 153     LV_ITEM_NATIVE 
*m_pItem
; 
 154     LV_ITEM_NATIVE m_item
; 
 156     DECLARE_NO_COPY_CLASS(wxLV_ITEM
) 
 159 /////////////////////////////////////////////////////// 
 161 // The MSW version had problems with SetTextColour() et 
 162 // al as the wxListItemAttr's were stored keyed on the 
 163 // item index. If a item was inserted anywhere but the end 
 164 // of the list the the text attributes (colour etc) for 
 165 // the following items were out of sync. 
 168 // Under MSW the only way to associate data with a List 
 169 // item independant of its position in the list is to 
 170 // store a pointer to it in its lParam attribute. However 
 171 // user programs are already using this (via the 
 172 // SetItemData() GetItemData() calls). 
 174 // However what we can do is store a pointer to a 
 175 // structure which contains the attributes we want *and* 
 176 // a lParam for the users data, e.g. 
 178 // class wxListItemInternalData 
 181 //   wxListItemAttr *attr; 
 182 //   long lParam; // user data 
 185 // To conserve memory, a wxListItemInternalData is 
 186 // only allocated for a LV_ITEM if text attributes or 
 187 // user data(lparam) are being set. 
 190 // class wxListItemInternalData 
 191 class wxListItemInternalData
 
 194    wxListItemAttr 
*attr
; 
 195    LPARAM lParam
; // user data 
 197    wxListItemInternalData() : attr(NULL
), lParam(0) {} 
 198    ~wxListItemInternalData() 
 204     DECLARE_NO_COPY_CLASS(wxListItemInternalData
) 
 207 // Get the internal data structure 
 208 static wxListItemInternalData 
*wxGetInternalData(HWND hwnd
, long itemId
); 
 209 static wxListItemInternalData 
*wxGetInternalData(const wxListCtrl 
*ctl
, long itemId
); 
 210 static wxListItemAttr 
*wxGetInternalDataAttr(wxListCtrl 
*ctl
, long itemId
); 
 211 static void wxDeleteInternalData(wxListCtrl
* ctl
, long itemId
); 
 214 // ---------------------------------------------------------------------------- 
 216 // ---------------------------------------------------------------------------- 
 218 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_DRAG
) 
 219 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_RDRAG
) 
 220 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
) 
 221 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_END_LABEL_EDIT
) 
 222 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ITEM
) 
 223 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS
) 
 224 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_GET_INFO
) 
 225 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_SET_INFO
) 
 226 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_SELECTED
) 
 227 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED
) 
 228 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN
) 
 229 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_INSERT_ITEM
) 
 230 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_CLICK
) 
 231 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_RIGHT_CLICK
) 
 232 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG
) 
 233 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_DRAGGING
) 
 234 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_END_DRAG
) 
 235 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK
) 
 236 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK
) 
 237 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED
) 
 238 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_FOCUSED
) 
 239 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT
) 
 241 #if wxUSE_EXTENDED_RTTI 
 242 WX_DEFINE_FLAGS( wxListCtrlStyle 
) 
 244 wxBEGIN_FLAGS( wxListCtrlStyle 
) 
 245     // new style border flags, we put them first to 
 246     // use them for streaming out 
 247     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
 248     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
 249     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
 250     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
 251     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
 252     wxFLAGS_MEMBER(wxBORDER_NONE
) 
 254     // old style border flags 
 255     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
 256     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
 257     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
 258     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
 259     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
 260     wxFLAGS_MEMBER(wxBORDER
) 
 262     // standard window styles 
 263     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 264     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 265     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 266     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 267     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
 268     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 269     wxFLAGS_MEMBER(wxVSCROLL
) 
 270     wxFLAGS_MEMBER(wxHSCROLL
) 
 272     wxFLAGS_MEMBER(wxLC_LIST
) 
 273     wxFLAGS_MEMBER(wxLC_REPORT
) 
 274     wxFLAGS_MEMBER(wxLC_ICON
) 
 275     wxFLAGS_MEMBER(wxLC_SMALL_ICON
) 
 276     wxFLAGS_MEMBER(wxLC_ALIGN_TOP
) 
 277     wxFLAGS_MEMBER(wxLC_ALIGN_LEFT
) 
 278     wxFLAGS_MEMBER(wxLC_AUTOARRANGE
) 
 279     wxFLAGS_MEMBER(wxLC_USER_TEXT
) 
 280     wxFLAGS_MEMBER(wxLC_EDIT_LABELS
) 
 281     wxFLAGS_MEMBER(wxLC_NO_HEADER
) 
 282     wxFLAGS_MEMBER(wxLC_SINGLE_SEL
) 
 283     wxFLAGS_MEMBER(wxLC_SORT_ASCENDING
) 
 284     wxFLAGS_MEMBER(wxLC_SORT_DESCENDING
) 
 285     wxFLAGS_MEMBER(wxLC_VIRTUAL
) 
 287 wxEND_FLAGS( wxListCtrlStyle 
) 
 289 IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl
, wxControl
,"wx/listctrl.h") 
 291 wxBEGIN_PROPERTIES_TABLE(wxListCtrl
) 
 292     wxEVENT_PROPERTY( TextUpdated 
, wxEVT_COMMAND_TEXT_UPDATED 
, wxCommandEvent 
)  
 294     wxPROPERTY_FLAGS( WindowStyle 
, wxListCtrlStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 295 wxEND_PROPERTIES_TABLE() 
 297 wxBEGIN_HANDLERS_TABLE(wxListCtrl
) 
 298 wxEND_HANDLERS_TABLE() 
 300 wxCONSTRUCTOR_5( wxListCtrl 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle 
)  
 303  TODO : Expose more information of a list's layout etc. via appropriate objects (à la NotebookPageInfo) 
 306 IMPLEMENT_DYNAMIC_CLASS(wxListCtrl
, wxControl
) 
 309 IMPLEMENT_DYNAMIC_CLASS(wxListView
, wxListCtrl
) 
 310 IMPLEMENT_DYNAMIC_CLASS(wxListItem
, wxObject
) 
 312 IMPLEMENT_DYNAMIC_CLASS(wxListEvent
, wxNotifyEvent
) 
 314 BEGIN_EVENT_TABLE(wxListCtrl
, wxControl
) 
 315     EVT_PAINT(wxListCtrl::OnPaint
) 
 318 // ============================================================================ 
 320 // ============================================================================ 
 322 // ---------------------------------------------------------------------------- 
 323 // wxListCtrl construction 
 324 // ---------------------------------------------------------------------------- 
 326 void wxListCtrl::Init() 
 328     m_imageListNormal 
= NULL
; 
 329     m_imageListSmall 
= NULL
; 
 330     m_imageListState 
= NULL
; 
 331     m_ownsImageListNormal 
= m_ownsImageListSmall 
= m_ownsImageListState 
= FALSE
; 
 334     m_ignoreChangeMessages 
= FALSE
; 
 336     m_AnyInternalData 
= FALSE
; 
 337     m_hasAnyAttr 
= FALSE
; 
 340 bool wxListCtrl::Create(wxWindow 
*parent
, 
 345                         const wxValidator
& validator
, 
 346                         const wxString
& name
) 
 348     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 351     if ( !MSWCreateControl(WC_LISTVIEW
, _T(""), pos
, size
) ) 
 354     // explicitly say that we want to use Unicode because otherwise we get ANSI 
 355     // versions of _some_ messages (notably LVN_GETDISPINFOA) in MSLU build 
 356     wxSetCCUnicodeFormat(GetHwnd()); 
 358     // for comctl32.dll v 4.70+ we want to have this attribute because it's 
 359     // prettier (and also because wxGTK does it like this) 
 360     if ( InReportView() && wxTheApp
->GetComCtl32Version() >= 470 ) 
 362         ::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE
, 
 363                       0, LVS_EX_FULLROWSELECT
); 
 369 WXDWORD 
wxListCtrl::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 371     WXDWORD wstyle 
= wxControl::MSWGetStyle(style
, exstyle
); 
 373     wstyle 
|= LVS_SHAREIMAGELISTS 
| LVS_SHOWSELALWAYS
; 
 378     #define MAP_MODE_STYLE(wx, ms)                                            \ 
 379         if ( style & (wx) ) { wstyle |= (ms); nModes++; } 
 380 #else // !__WXDEBUG__ 
 381     #define MAP_MODE_STYLE(wx, ms)                                            \ 
 382         if ( style & (wx) ) wstyle |= (ms); 
 383 #endif // __WXDEBUG__ 
 385     MAP_MODE_STYLE(wxLC_ICON
, LVS_ICON
) 
 386     MAP_MODE_STYLE(wxLC_SMALL_ICON
, LVS_SMALLICON
) 
 387     MAP_MODE_STYLE(wxLC_LIST
, LVS_LIST
) 
 388     MAP_MODE_STYLE(wxLC_REPORT
, LVS_REPORT
) 
 390     wxASSERT_MSG( nModes 
== 1, 
 391                   _T("wxListCtrl style should have exactly one mode bit set") ); 
 393 #undef MAP_MODE_STYLE 
 395     if ( style 
& wxLC_ALIGN_LEFT 
) 
 396         wstyle 
|= LVS_ALIGNLEFT
; 
 398     if ( style 
& wxLC_ALIGN_TOP 
) 
 399         wstyle 
|= LVS_ALIGNTOP
; 
 401     if ( style 
& wxLC_AUTOARRANGE 
) 
 402         wstyle 
|= LVS_AUTOARRANGE
; 
 404     if ( style 
& wxLC_NO_SORT_HEADER 
) 
 405         wstyle 
|= LVS_NOSORTHEADER
; 
 407     if ( style 
& wxLC_NO_HEADER 
) 
 408         wstyle 
|= LVS_NOCOLUMNHEADER
; 
 410     if ( style 
& wxLC_EDIT_LABELS 
) 
 411         wstyle 
|= LVS_EDITLABELS
; 
 413     if ( style 
& wxLC_SINGLE_SEL 
) 
 414         wstyle 
|= LVS_SINGLESEL
; 
 416     if ( style 
& wxLC_SORT_ASCENDING 
) 
 418         wstyle 
|= LVS_SORTASCENDING
; 
 420         wxASSERT_MSG( !(style 
& wxLC_SORT_DESCENDING
), 
 421                       _T("can't sort in ascending and descending orders at once") ); 
 423     else if ( style 
& wxLC_SORT_DESCENDING 
) 
 424         wstyle 
|= LVS_SORTDESCENDING
; 
 426 #if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) 
 427     if ( style 
& wxLC_VIRTUAL 
) 
 429         int ver 
= wxTheApp
->GetComCtl32Version(); 
 432             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."), 
 433                         ver 
/ 100, ver 
% 100); 
 436         wstyle 
|= LVS_OWNERDATA
; 
 438 #endif // ancient cygwin 
 443 void wxListCtrl::UpdateStyle() 
 447         // The new window view style 
 448         DWORD dwStyleNew 
= MSWGetStyle(m_windowStyle
, NULL
); 
 450         // Get the current window style. 
 451         DWORD dwStyleOld 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
 453         // Only set the window style if the view bits have changed. 
 454         if ( dwStyleOld 
!= dwStyleNew 
) 
 456             ::SetWindowLong(GetHwnd(), GWL_STYLE
, dwStyleNew
); 
 461 void wxListCtrl::FreeAllInternalData() 
 463     if (m_AnyInternalData
) 
 465         int n 
= GetItemCount(); 
 468         m_ignoreChangeMessages 
= TRUE
; 
 469         for (i 
= 0; i 
< n
; i
++) 
 470             wxDeleteInternalData(this, i
); 
 471         m_ignoreChangeMessages 
= FALSE
; 
 473         m_AnyInternalData 
= FALSE
; 
 477 wxListCtrl::~wxListCtrl() 
 479     FreeAllInternalData(); 
 483         m_textCtrl
->UnsubclassWin(); 
 484         m_textCtrl
->SetHWND(0); 
 489     if (m_ownsImageListNormal
) delete m_imageListNormal
; 
 490     if (m_ownsImageListSmall
) delete m_imageListSmall
; 
 491     if (m_ownsImageListState
) delete m_imageListState
; 
 494 // ---------------------------------------------------------------------------- 
 495 // set/get/change style 
 496 // ---------------------------------------------------------------------------- 
 498 // Add or remove a single window style 
 499 void wxListCtrl::SetSingleStyle(long style
, bool add
) 
 501     long flag 
= GetWindowStyleFlag(); 
 503     // Get rid of conflicting styles 
 506         if ( style 
& wxLC_MASK_TYPE
) 
 507             flag 
= flag 
& ~wxLC_MASK_TYPE
; 
 508         if ( style 
& wxLC_MASK_ALIGN 
) 
 509             flag 
= flag 
& ~wxLC_MASK_ALIGN
; 
 510         if ( style 
& wxLC_MASK_SORT 
) 
 511             flag 
= flag 
& ~wxLC_MASK_SORT
; 
 527     m_windowStyle 
= flag
; 
 532 // Set the whole window style 
 533 void wxListCtrl::SetWindowStyleFlag(long flag
) 
 535     m_windowStyle 
= flag
; 
 540 // ---------------------------------------------------------------------------- 
 542 // ---------------------------------------------------------------------------- 
 544 /* static */ wxVisualAttributes
 
 545 wxListCtrl::GetClassDefaultAttributes(wxWindowVariant variant
) 
 547     wxVisualAttributes attrs 
= GetCompositeControlsDefaultAttributes(variant
); 
 549     // common controls have their own default font 
 550     attrs
.font 
= wxGetCCDefaultFont(); 
 555 // Sets the foreground, i.e. text, colour 
 556 bool wxListCtrl::SetForegroundColour(const wxColour
& col
) 
 558     if ( !wxWindow::SetForegroundColour(col
) ) 
 561     ListView_SetTextColor(GetHwnd(), wxColourToRGB(col
)); 
 566 // Sets the background colour 
 567 bool wxListCtrl::SetBackgroundColour(const wxColour
& col
) 
 569     if ( !wxWindow::SetBackgroundColour(col
) ) 
 572     // we set the same colour for both the "empty" background and the items 
 574     COLORREF color 
= wxColourToRGB(col
); 
 575     ListView_SetBkColor(GetHwnd(), color
); 
 576     ListView_SetTextBkColor(GetHwnd(), color
); 
 581 // Gets information about this column 
 582 bool wxListCtrl::GetColumn(int col
, wxListItem
& item
) const 
 587     lvCol
.mask 
= LVCF_WIDTH
; 
 589     if ( item
.m_mask 
& wxLIST_MASK_TEXT 
) 
 591         lvCol
.mask 
|= LVCF_TEXT
; 
 592         lvCol
.pszText 
= new wxChar
[513]; 
 593         lvCol
.cchTextMax 
= 512; 
 596     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
 598         lvCol
.mask 
|= LVCF_FMT
; 
 601     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
 603         lvCol
.mask 
|= LVCF_IMAGE
; 
 606     bool success 
= ListView_GetColumn(GetHwnd(), col
, &lvCol
) != 0; 
 608     //  item.m_subItem = lvCol.iSubItem; 
 609     item
.m_width 
= lvCol
.cx
; 
 611     if ( (item
.m_mask 
& wxLIST_MASK_TEXT
) && lvCol
.pszText 
) 
 613         item
.m_text 
= lvCol
.pszText
; 
 614         delete[] lvCol
.pszText
; 
 617     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
 619         switch (lvCol
.fmt 
& LVCFMT_JUSTIFYMASK
) { 
 621                 item
.m_format 
= wxLIST_FORMAT_LEFT
; 
 624                 item
.m_format 
= wxLIST_FORMAT_RIGHT
; 
 627                 item
.m_format 
= wxLIST_FORMAT_CENTRE
; 
 630                 item
.m_format 
= -1;  // Unknown? 
 635     // the column images were not supported in older versions but how to check 
 636     // for this? we can't use _WIN32_IE because we always define it to a very 
 637     // high value, so see if another symbol which is only defined starting from 
 638     // comctl32.dll 4.70 is available 
 639 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
 640     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
 642         item
.m_image 
= lvCol
.iImage
; 
 644 #endif // LVCOLUMN::iImage exists 
 649 // Sets information about this column 
 650 bool wxListCtrl::SetColumn(int col
, wxListItem
& item
) 
 653     wxConvertToMSWListCol(col
, item
, lvCol
); 
 655     return ListView_SetColumn(GetHwnd(), col
, &lvCol
) != 0; 
 658 // Gets the column width 
 659 int wxListCtrl::GetColumnWidth(int col
) const 
 661     return ListView_GetColumnWidth(GetHwnd(), col
); 
 664 // Sets the column width 
 665 bool wxListCtrl::SetColumnWidth(int col
, int width
) 
 668     if ( m_windowStyle 
& wxLC_LIST 
) 
 672     if ( width2 
== wxLIST_AUTOSIZE
) 
 673         width2 
= LVSCW_AUTOSIZE
; 
 674     else if ( width2 
== wxLIST_AUTOSIZE_USEHEADER
) 
 675         width2 
= LVSCW_AUTOSIZE_USEHEADER
; 
 677     return ListView_SetColumnWidth(GetHwnd(), col2
, width2
) != 0; 
 680 // Gets the number of items that can fit vertically in the 
 681 // visible area of the list control (list or report view) 
 682 // or the total number of items in the list control (icon 
 683 // or small icon view) 
 684 int wxListCtrl::GetCountPerPage() const 
 686     return ListView_GetCountPerPage(GetHwnd()); 
 689 // Gets the edit control for editing labels. 
 690 wxTextCtrl
* wxListCtrl::GetEditControl() const 
 695 // Gets information about the item 
 696 bool wxListCtrl::GetItem(wxListItem
& info
) const 
 699     wxZeroMemory(lvItem
); 
 701     lvItem
.iItem 
= info
.m_itemId
; 
 702     lvItem
.iSubItem 
= info
.m_col
; 
 704     if ( info
.m_mask 
& wxLIST_MASK_TEXT 
) 
 706         lvItem
.mask 
|= LVIF_TEXT
; 
 707         lvItem
.pszText 
= new wxChar
[513]; 
 708         lvItem
.cchTextMax 
= 512; 
 712         lvItem
.pszText 
= NULL
; 
 715     if (info
.m_mask 
& wxLIST_MASK_DATA
) 
 716         lvItem
.mask 
|= LVIF_PARAM
; 
 718     if (info
.m_mask 
& wxLIST_MASK_IMAGE
) 
 719         lvItem
.mask 
|= LVIF_IMAGE
; 
 721     if ( info
.m_mask 
& wxLIST_MASK_STATE 
) 
 723         lvItem
.mask 
|= LVIF_STATE
; 
 724         // the other bits are hardly interesting anyhow 
 725         lvItem
.stateMask 
= LVIS_SELECTED 
| LVIS_FOCUSED
; 
 728     bool success 
= ListView_GetItem((HWND
)GetHWND(), &lvItem
) != 0; 
 731         wxLogError(_("Couldn't retrieve information about list control item %d."), 
 736         // give NULL as hwnd as we already have everything we need 
 737         wxConvertFromMSWListItem(NULL
, info
, lvItem
); 
 741         delete[] lvItem
.pszText
; 
 746 // Sets information about the item 
 747 bool wxListCtrl::SetItem(wxListItem
& info
) 
 750     wxConvertToMSWListItem(this, info
, item
); 
 752     // we never update the lParam if it contains our pointer 
 753     // to the wxListItemInternalData structure 
 754     item
.mask 
&= ~LVIF_PARAM
; 
 756     // check if setting attributes or lParam 
 757     if (info
.HasAttributes() || (info
.m_mask  
& wxLIST_MASK_DATA
)) 
 759         // get internal item data 
 760         // perhaps a cache here ? 
 761         wxListItemInternalData 
*data 
= wxGetInternalData(this, info
.m_itemId
); 
 766             m_AnyInternalData 
= TRUE
; 
 767             data 
= new wxListItemInternalData(); 
 768             item
.lParam 
= (LPARAM
) data
; 
 769             item
.mask 
|= LVIF_PARAM
; 
 774         if (info
.m_mask  
& wxLIST_MASK_DATA
) 
 775             data
->lParam 
= info
.m_data
; 
 778         if (info
.HasAttributes()) 
 781                 *data
->attr 
= *info
.GetAttributes(); 
 783                 data
->attr 
= new wxListItemAttr(*info
.GetAttributes()); 
 788     // we could be changing only the attribute in which case we don't need to 
 789     // call ListView_SetItem() at all 
 793         if ( !ListView_SetItem(GetHwnd(), &item
) ) 
 795             wxLogDebug(_T("ListView_SetItem() failed")); 
 801     // we need to update the item immediately to show the new image 
 802     bool updateNow 
= (info
.m_mask 
& wxLIST_MASK_IMAGE
) != 0; 
 804     // check whether it has any custom attributes 
 805     if ( info
.HasAttributes() ) 
 809         // if the colour has changed, we must redraw the item 
 815         // we need this to make the change visible right now 
 816         RefreshItem(item
.iItem
); 
 822 long wxListCtrl::SetItem(long index
, int col
, const wxString
& label
, int imageId
) 
 826     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 827     info
.m_itemId 
= index
; 
 831         info
.m_image 
= imageId
; 
 832         info
.m_mask 
|= wxLIST_MASK_IMAGE
; 
 834     return SetItem(info
); 
 838 // Gets the item state 
 839 int wxListCtrl::GetItemState(long item
, long stateMask
) const 
 843     info
.m_mask 
= wxLIST_MASK_STATE
; 
 844     info
.m_stateMask 
= stateMask
; 
 845     info
.m_itemId 
= item
; 
 853 // Sets the item state 
 854 bool wxListCtrl::SetItemState(long item
, long state
, long stateMask
) 
 856     // NB: don't use SetItem() here as it doesn't work with the virtual list 
 859     wxZeroMemory(lvItem
); 
 861     wxConvertToMSWFlags(state
, stateMask
, lvItem
); 
 863     // for the virtual list controls we need to refresh the previously focused 
 864     // item manually when changing focus without changing selection 
 865     // programmatically because otherwise it keeps its focus rectangle until 
 866     // next repaint (yet another comctl32 bug) 
 869          (stateMask 
& wxLIST_STATE_FOCUSED
) && 
 870          (state 
& wxLIST_STATE_FOCUSED
) ) 
 872         focusOld 
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_FOCUSED
); 
 879     if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE
, 
 880                         (WPARAM
)item
, (LPARAM
)&lvItem
) ) 
 882         wxLogLastError(_T("ListView_SetItemState")); 
 887     if ( focusOld 
!= -1 ) 
 889         // no need to refresh the item if it was previously selected, it would 
 890         // only result in annoying flicker 
 891         if ( !(GetItemState(focusOld
, 
 892                             wxLIST_STATE_SELECTED
) & wxLIST_STATE_SELECTED
) ) 
 894             RefreshItem(focusOld
); 
 901 // Sets the item image 
 902 bool wxListCtrl::SetItemImage(long item
, int image
, int WXUNUSED(selImage
)) 
 906     info
.m_mask 
= wxLIST_MASK_IMAGE
; 
 907     info
.m_image 
= image
; 
 908     info
.m_itemId 
= item
; 
 910     return SetItem(info
); 
 913 // Gets the item text 
 914 wxString 
wxListCtrl::GetItemText(long item
) const 
 918     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 919     info
.m_itemId 
= item
; 
 922         return wxEmptyString
; 
 926 // Sets the item text 
 927 void wxListCtrl::SetItemText(long item
, const wxString
& str
) 
 931     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 932     info
.m_itemId 
= item
; 
 938 // Gets the item data 
 939 long wxListCtrl::GetItemData(long item
) const 
 943     info
.m_mask 
= wxLIST_MASK_DATA
; 
 944     info
.m_itemId 
= item
; 
 951 // Sets the item data 
 952 bool wxListCtrl::SetItemData(long item
, long data
) 
 956     info
.m_mask 
= wxLIST_MASK_DATA
; 
 957     info
.m_itemId 
= item
; 
 960     return SetItem(info
); 
 963 wxRect 
wxListCtrl::GetViewRect() const 
 965     wxASSERT_MSG( !HasFlag(wxLC_REPORT 
| wxLC_LIST
), 
 966                     _T("wxListCtrl::GetViewRect() only works in icon mode") ); 
 969     if ( !ListView_GetViewRect(GetHwnd(), &rc
) ) 
 971         wxLogDebug(_T("ListView_GetViewRect() failed.")); 
 977     wxCopyRECTToRect(rc
, rect
); 
 982 // Gets the item rectangle 
 983 bool wxListCtrl::GetItemRect(long item
, wxRect
& rect
, int code
) const 
 988     if ( code 
== wxLIST_RECT_BOUNDS 
) 
 989         codeWin 
= LVIR_BOUNDS
; 
 990     else if ( code 
== wxLIST_RECT_ICON 
) 
 992     else if ( code 
== wxLIST_RECT_LABEL 
) 
 993         codeWin 
= LVIR_LABEL
; 
 996         wxFAIL_MSG( _T("incorrect code in GetItemRect()") ); 
 998         codeWin 
= LVIR_BOUNDS
; 
1001     bool success 
= ListView_GetItemRect(GetHwnd(), (int) item
, &rectWin
, codeWin
) != 0; 
1003     rect
.x 
= rectWin
.left
; 
1004     rect
.y 
= rectWin
.top
; 
1005     rect
.width 
= rectWin
.right 
- rectWin
.left
; 
1006     rect
.height 
= rectWin
.bottom 
- rectWin
.top
; 
1011 // Gets the item position 
1012 bool wxListCtrl::GetItemPosition(long item
, wxPoint
& pos
) const 
1016     bool success 
= (ListView_GetItemPosition(GetHwnd(), (int) item
, &pt
) != 0); 
1018     pos
.x 
= pt
.x
; pos
.y 
= pt
.y
; 
1022 // Sets the item position. 
1023 bool wxListCtrl::SetItemPosition(long item
, const wxPoint
& pos
) 
1025     return (ListView_SetItemPosition(GetHwnd(), (int) item
, pos
.x
, pos
.y
) != 0); 
1028 // Gets the number of items in the list control 
1029 int wxListCtrl::GetItemCount() const 
1034 wxSize 
wxListCtrl::GetItemSpacing() const 
1036     const int spacing 
= ListView_GetItemSpacing(GetHwnd(), (BOOL
)HasFlag(wxLC_SMALL_ICON
)); 
1038     return wxSize(LOWORD(spacing
), HIWORD(spacing
)); 
1041 int wxListCtrl::GetItemSpacing(bool isSmall
) const 
1043     return ListView_GetItemSpacing(GetHwnd(), (BOOL
) isSmall
); 
1046 void wxListCtrl::SetItemTextColour( long item
, const wxColour 
&col 
) 
1049     info
.m_itemId 
= item
; 
1050     info
.SetTextColour( col 
); 
1054 wxColour 
wxListCtrl::GetItemTextColour( long item 
) const 
1057     wxListItemInternalData 
*data 
= wxGetInternalData(this, item
); 
1058     if ( data 
&& data
->attr 
) 
1059         col 
= data
->attr
->GetTextColour(); 
1064 void wxListCtrl::SetItemBackgroundColour( long item
, const wxColour 
&col 
) 
1067     info
.m_itemId 
= item
; 
1068     info
.SetBackgroundColour( col 
); 
1072 wxColour 
wxListCtrl::GetItemBackgroundColour( long item 
) const 
1075     wxListItemInternalData 
*data 
= wxGetInternalData(this, item
); 
1076     if ( data 
&& data
->attr 
) 
1077         col 
= data
->attr
->GetBackgroundColour(); 
1082 // Gets the number of selected items in the list control 
1083 int wxListCtrl::GetSelectedItemCount() const 
1085     return ListView_GetSelectedCount(GetHwnd()); 
1088 // Gets the text colour of the listview 
1089 wxColour 
wxListCtrl::GetTextColour() const 
1091     COLORREF ref 
= ListView_GetTextColor(GetHwnd()); 
1092     wxColour 
col(GetRValue(ref
), GetGValue(ref
), GetBValue(ref
)); 
1096 // Sets the text colour of the listview 
1097 void wxListCtrl::SetTextColour(const wxColour
& col
) 
1099     ListView_SetTextColor(GetHwnd(), PALETTERGB(col
.Red(), col
.Green(), col
.Blue())); 
1102 // Gets the index of the topmost visible item when in 
1103 // list or report view 
1104 long wxListCtrl::GetTopItem() const 
1106     return (long) ListView_GetTopIndex(GetHwnd()); 
1109 // Searches for an item, starting from 'item'. 
1110 // 'geometry' is one of 
1111 // wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT. 
1112 // 'state' is a state bit flag, one or more of 
1113 // wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT. 
1114 // item can be -1 to find the first item that matches the 
1116 // Returns the item or -1 if unsuccessful. 
1117 long wxListCtrl::GetNextItem(long item
, int geom
, int state
) const 
1121     if ( geom 
== wxLIST_NEXT_ABOVE 
) 
1122         flags 
|= LVNI_ABOVE
; 
1123     if ( geom 
== wxLIST_NEXT_ALL 
) 
1125     if ( geom 
== wxLIST_NEXT_BELOW 
) 
1126         flags 
|= LVNI_BELOW
; 
1127     if ( geom 
== wxLIST_NEXT_LEFT 
) 
1128         flags 
|= LVNI_TOLEFT
; 
1129     if ( geom 
== wxLIST_NEXT_RIGHT 
) 
1130         flags 
|= LVNI_TORIGHT
; 
1132     if ( state 
& wxLIST_STATE_CUT 
) 
1134     if ( state 
& wxLIST_STATE_DROPHILITED 
) 
1135         flags 
|= LVNI_DROPHILITED
; 
1136     if ( state 
& wxLIST_STATE_FOCUSED 
) 
1137         flags 
|= LVNI_FOCUSED
; 
1138     if ( state 
& wxLIST_STATE_SELECTED 
) 
1139         flags 
|= LVNI_SELECTED
; 
1141     return (long) ListView_GetNextItem(GetHwnd(), item
, flags
); 
1145 wxImageList 
*wxListCtrl::GetImageList(int which
) const 
1147     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1149         return m_imageListNormal
; 
1151     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1153         return m_imageListSmall
; 
1155     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1157         return m_imageListState
; 
1162 void wxListCtrl::SetImageList(wxImageList 
*imageList
, int which
) 
1165     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1167         flags 
= LVSIL_NORMAL
; 
1168         if (m_ownsImageListNormal
) delete m_imageListNormal
; 
1169         m_imageListNormal 
= imageList
; 
1170         m_ownsImageListNormal 
= FALSE
; 
1172     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1174         flags 
= LVSIL_SMALL
; 
1175         if (m_ownsImageListSmall
) delete m_imageListSmall
; 
1176         m_imageListSmall 
= imageList
; 
1177         m_ownsImageListSmall 
= FALSE
; 
1179     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1181         flags 
= LVSIL_STATE
; 
1182         if (m_ownsImageListState
) delete m_imageListState
; 
1183         m_imageListState 
= imageList
; 
1184         m_ownsImageListState 
= FALSE
; 
1186     ListView_SetImageList(GetHwnd(), (HIMAGELIST
) imageList 
? imageList
->GetHIMAGELIST() : 0, flags
); 
1189 void wxListCtrl::AssignImageList(wxImageList 
*imageList
, int which
) 
1191     SetImageList(imageList
, which
); 
1192     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1193         m_ownsImageListNormal 
= TRUE
; 
1194     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1195         m_ownsImageListSmall 
= TRUE
; 
1196     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1197         m_ownsImageListState 
= TRUE
; 
1200 // ---------------------------------------------------------------------------- 
1202 // ---------------------------------------------------------------------------- 
1204 // Arranges the items 
1205 bool wxListCtrl::Arrange(int flag
) 
1208     if ( flag 
== wxLIST_ALIGN_LEFT 
) 
1209         code 
= LVA_ALIGNLEFT
; 
1210     else if ( flag 
== wxLIST_ALIGN_TOP 
) 
1211         code 
= LVA_ALIGNTOP
; 
1212     else if ( flag 
== wxLIST_ALIGN_DEFAULT 
) 
1214     else if ( flag 
== wxLIST_ALIGN_SNAP_TO_GRID 
) 
1215         code 
= LVA_SNAPTOGRID
; 
1217     return (ListView_Arrange(GetHwnd(), code
) != 0); 
1221 bool wxListCtrl::DeleteItem(long item
) 
1223     if ( !ListView_DeleteItem(GetHwnd(), (int) item
) ) 
1225         wxLogLastError(_T("ListView_DeleteItem")); 
1230     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
1231                   wxT("m_count should match ListView_GetItemCount")); 
1233     // the virtual list control doesn't refresh itself correctly, help it 
1236         // we need to refresh all the lines below the one which was deleted 
1238         if ( item 
> 0 && GetItemCount() ) 
1240             GetItemRect(item 
- 1, rectItem
); 
1245             rectItem
.height 
= 0; 
1248         wxRect rectWin 
= GetRect(); 
1249         rectWin
.height 
= rectWin
.GetBottom() - rectItem
.GetBottom(); 
1250         rectWin
.y 
= rectItem
.GetBottom(); 
1252         RefreshRect(rectWin
); 
1258 // Deletes all items 
1259 bool wxListCtrl::DeleteAllItems() 
1261     FreeAllInternalData(); 
1262     return ListView_DeleteAllItems(GetHwnd()) != 0; 
1265 // Deletes all items 
1266 bool wxListCtrl::DeleteAllColumns() 
1268     while ( m_colCount 
> 0 ) 
1270         if ( ListView_DeleteColumn(GetHwnd(), 0) == 0 ) 
1272             wxLogLastError(wxT("ListView_DeleteColumn")); 
1280     wxASSERT_MSG( m_colCount 
== 0, wxT("no columns should be left") ); 
1286 bool wxListCtrl::DeleteColumn(int col
) 
1288     bool success 
= (ListView_DeleteColumn(GetHwnd(), col
) != 0); 
1290     if ( success 
&& (m_colCount 
> 0) ) 
1295 // Clears items, and columns if there are any. 
1296 void wxListCtrl::ClearAll() 
1299     if ( m_colCount 
> 0 ) 
1303 wxTextCtrl
* wxListCtrl::EditLabel(long item
, wxClassInfo
* textControlClass
) 
1305     wxASSERT( (textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
))) ); 
1307     // ListView_EditLabel requires that the list has focus. 
1310     WXHWND hWnd 
= (WXHWND
) ListView_EditLabel(GetHwnd(), item
); 
1313         // failed to start editing 
1317     // [re]create the text control wrapping the HWND we got 
1320         m_textCtrl
->UnsubclassWin(); 
1321         m_textCtrl
->SetHWND(0); 
1325     m_textCtrl 
= (wxTextCtrl 
*)textControlClass
->CreateObject(); 
1326     m_textCtrl
->SetHWND(hWnd
); 
1327     m_textCtrl
->SubclassWin(hWnd
); 
1328     m_textCtrl
->SetParent(this); 
1330     // we must disallow TABbing away from the control while the edit contol is 
1331     // shown because this leaves it in some strange state (just try removing 
1332     // this line and then pressing TAB while editing an item in  listctrl 
1334     m_textCtrl
->SetWindowStyle(m_textCtrl
->GetWindowStyle() | wxTE_PROCESS_TAB
); 
1339 // End label editing, optionally cancelling the edit 
1340 bool wxListCtrl::EndEditLabel(bool WXUNUSED(cancel
)) 
1342     wxFAIL_MSG( _T("not implemented") ); 
1347 // Ensures this item is visible 
1348 bool wxListCtrl::EnsureVisible(long item
) 
1350     return ListView_EnsureVisible(GetHwnd(), (int) item
, FALSE
) != 0; 
1353 // Find an item whose label matches this string, starting from the item after 'start' 
1354 // or the beginning if 'start' is -1. 
1355 long wxListCtrl::FindItem(long start
, const wxString
& str
, bool partial
) 
1357     LV_FINDINFO findInfo
; 
1359     findInfo
.flags 
= LVFI_STRING
; 
1361         findInfo
.flags 
|= LVFI_PARTIAL
; 
1364     // ListView_FindItem() excludes the first item from search and to look 
1365     // through all the items you need to start from -1 which is unnatural and 
1366     // inconsistent with the generic version - so we adjust the index 
1369     return ListView_FindItem(GetHwnd(), (int) start
, &findInfo
); 
1372 // Find an item whose data matches this data, starting from the item after 'start' 
1373 // or the beginning if 'start' is -1. 
1374 // NOTE : Lindsay Mathieson - 14-July-2002 
1375 //        No longer use ListView_FindItem as the data attribute is now stored 
1376 //        in a wxListItemInternalData structure refernced by the actual lParam 
1377 long wxListCtrl::FindItem(long start
, long data
) 
1379     long  idx 
= start 
+ 1; 
1380     long count 
= GetItemCount(); 
1384         if (GetItemData(idx
) == data
) 
1392 // Find an item nearest this position in the specified direction, starting from 
1393 // the item after 'start' or the beginning if 'start' is -1. 
1394 long wxListCtrl::FindItem(long start
, const wxPoint
& pt
, int direction
) 
1396     LV_FINDINFO findInfo
; 
1398     findInfo
.flags 
= LVFI_NEARESTXY
; 
1399     findInfo
.pt
.x 
= pt
.x
; 
1400     findInfo
.pt
.y 
= pt
.y
; 
1401     findInfo
.vkDirection 
= VK_RIGHT
; 
1403     if ( direction 
== wxLIST_FIND_UP 
) 
1404         findInfo
.vkDirection 
= VK_UP
; 
1405     else if ( direction 
== wxLIST_FIND_DOWN 
) 
1406         findInfo
.vkDirection 
= VK_DOWN
; 
1407     else if ( direction 
== wxLIST_FIND_LEFT 
) 
1408         findInfo
.vkDirection 
= VK_LEFT
; 
1409     else if ( direction 
== wxLIST_FIND_RIGHT 
) 
1410         findInfo
.vkDirection 
= VK_RIGHT
; 
1412     return ListView_FindItem(GetHwnd(), (int) start
, & findInfo
); 
1415 // Determines which item (if any) is at the specified point, 
1416 // giving details in 'flags' (see wxLIST_HITTEST_... flags above) 
1417 long wxListCtrl::HitTest(const wxPoint
& point
, int& flags
) 
1419     LV_HITTESTINFO hitTestInfo
; 
1420     hitTestInfo
.pt
.x 
= (int) point
.x
; 
1421     hitTestInfo
.pt
.y 
= (int) point
.y
; 
1423     ListView_HitTest(GetHwnd(), & hitTestInfo
); 
1426     if ( hitTestInfo
.flags 
& LVHT_ABOVE 
) 
1427         flags 
|= wxLIST_HITTEST_ABOVE
; 
1428     if ( hitTestInfo
.flags 
& LVHT_BELOW 
) 
1429         flags 
|= wxLIST_HITTEST_BELOW
; 
1430     if ( hitTestInfo
.flags 
& LVHT_NOWHERE 
) 
1431         flags 
|= wxLIST_HITTEST_NOWHERE
; 
1432     if ( hitTestInfo
.flags 
& LVHT_ONITEMICON 
) 
1433         flags 
|= wxLIST_HITTEST_ONITEMICON
; 
1434     if ( hitTestInfo
.flags 
& LVHT_ONITEMLABEL 
) 
1435         flags 
|= wxLIST_HITTEST_ONITEMLABEL
; 
1436     if ( hitTestInfo
.flags 
& LVHT_ONITEMSTATEICON 
) 
1437         flags 
|= wxLIST_HITTEST_ONITEMSTATEICON
; 
1438     if ( hitTestInfo
.flags 
& LVHT_TOLEFT 
) 
1439         flags 
|= wxLIST_HITTEST_TOLEFT
; 
1440     if ( hitTestInfo
.flags 
& LVHT_TORIGHT 
) 
1441         flags 
|= wxLIST_HITTEST_TORIGHT
; 
1443     return (long) hitTestInfo
.iItem
; 
1446 // Inserts an item, returning the index of the new item if successful, 
1448 long wxListCtrl::InsertItem(wxListItem
& info
) 
1450     wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") ); 
1453     wxConvertToMSWListItem(this, info
, item
); 
1454     item
.mask 
&= ~LVIF_PARAM
; 
1456     // check wether we need to allocate our internal data 
1457     bool needInternalData 
= ((info
.m_mask 
& wxLIST_MASK_DATA
) || info
.HasAttributes()); 
1458     if (needInternalData
) 
1460         m_AnyInternalData 
= TRUE
; 
1461         item
.mask 
|= LVIF_PARAM
; 
1463         // internal stucture that manages data 
1464         wxListItemInternalData 
*data 
= new wxListItemInternalData(); 
1465         item
.lParam 
= (LPARAM
) data
; 
1467         if (info
.m_mask 
& wxLIST_MASK_DATA
) 
1468             data
->lParam 
= info
.m_data
; 
1470         // check whether it has any custom attributes 
1471         if ( info
.HasAttributes() ) 
1473             // take copy of attributes 
1474             data
->attr 
= new wxListItemAttr(*info
.GetAttributes()); 
1476             // and remember that we have some now... 
1477             m_hasAnyAttr 
= TRUE
; 
1481     long rv 
= ListView_InsertItem(GetHwnd(), & item
); 
1484     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
1485                   wxT("m_count should match ListView_GetItemCount")); 
1490 long wxListCtrl::InsertItem(long index
, const wxString
& label
) 
1493     info
.m_text 
= label
; 
1494     info
.m_mask 
= wxLIST_MASK_TEXT
; 
1495     info
.m_itemId 
= index
; 
1496     return InsertItem(info
); 
1499 // Inserts an image item 
1500 long wxListCtrl::InsertItem(long index
, int imageIndex
) 
1503     info
.m_image 
= imageIndex
; 
1504     info
.m_mask 
= wxLIST_MASK_IMAGE
; 
1505     info
.m_itemId 
= index
; 
1506     return InsertItem(info
); 
1509 // Inserts an image/string item 
1510 long wxListCtrl::InsertItem(long index
, const wxString
& label
, int imageIndex
) 
1513     info
.m_image 
= imageIndex
; 
1514     info
.m_text 
= label
; 
1515     info
.m_mask 
= wxLIST_MASK_IMAGE 
| wxLIST_MASK_TEXT
; 
1516     info
.m_itemId 
= index
; 
1517     return InsertItem(info
); 
1520 // For list view mode (only), inserts a column. 
1521 long wxListCtrl::InsertColumn(long col
, wxListItem
& item
) 
1524     wxConvertToMSWListCol(col
, item
, lvCol
); 
1526     if ( !(lvCol
.mask 
& LVCF_WIDTH
) ) 
1528         // always give some width to the new column: this one is compatible 
1529         // with the generic version 
1530         lvCol
.mask 
|= LVCF_WIDTH
; 
1534     long n 
= ListView_InsertColumn(GetHwnd(), col
, &lvCol
); 
1539     else // failed to insert? 
1541         wxLogDebug(wxT("Failed to insert the column '%s' into listview!"), 
1548 long wxListCtrl::InsertColumn(long col
, 
1549                               const wxString
& heading
, 
1554     item
.m_mask 
= wxLIST_MASK_TEXT 
| wxLIST_MASK_FORMAT
; 
1555     item
.m_text 
= heading
; 
1558         item
.m_mask 
|= wxLIST_MASK_WIDTH
; 
1559         item
.m_width 
= width
; 
1561     item
.m_format 
= format
; 
1563     return InsertColumn(col
, item
); 
1566 // scroll the control by the given number of pixels (exception: in list view, 
1567 // dx is interpreted as number of columns) 
1568 bool wxListCtrl::ScrollList(int dx
, int dy
) 
1570     if ( !ListView_Scroll(GetHwnd(), dx
, dy
) ) 
1572         wxLogDebug(_T("ListView_Scroll(%d, %d) failed"), dx
, dy
); 
1582 // fn is a function which takes 3 long arguments: item1, item2, data. 
1583 // item1 is the long data associated with a first item (NOT the index). 
1584 // item2 is the long data associated with a second item (NOT the index). 
1585 // data is the same value as passed to SortItems. 
1586 // The return value is a negative number if the first item should precede the second 
1587 // item, a positive number of the second item should precede the first, 
1588 // or zero if the two items are equivalent. 
1590 // data is arbitrary data to be passed to the sort function. 
1592 // Internal structures for proxying the user compare function 
1593 // so that we can pass it the *real* user data 
1595 // translate lParam data and call user func 
1596 struct wxInternalDataSort
 
1598     wxListCtrlCompare user_fn
; 
1602 int CALLBACK 
wxInternalDataCompareFunc(LPARAM lParam1
, LPARAM lParam2
,  LPARAM lParamSort
) 
1604     struct wxInternalDataSort 
*internalData 
= (struct wxInternalDataSort 
*) lParamSort
; 
1606     wxListItemInternalData 
*data1 
= (wxListItemInternalData 
*) lParam1
; 
1607     wxListItemInternalData 
*data2 
= (wxListItemInternalData 
*) lParam2
; 
1609     long d1 
= (data1 
== NULL 
? 0 : data1
->lParam
); 
1610     long d2 
= (data2 
== NULL 
? 0 : data2
->lParam
); 
1612     return internalData
->user_fn(d1
, d2
, internalData
->data
); 
1616 bool wxListCtrl::SortItems(wxListCtrlCompare fn
, long data
) 
1618     struct wxInternalDataSort internalData
; 
1619     internalData
.user_fn 
= fn
; 
1620     internalData
.data 
= data
; 
1622     // WPARAM cast is needed for mingw/cygwin 
1623     if ( !ListView_SortItems(GetHwnd(), 
1624                              wxInternalDataCompareFunc
, 
1625                              (WPARAM
) &internalData
) ) 
1627         wxLogDebug(_T("ListView_SortItems() failed")); 
1637 // ---------------------------------------------------------------------------- 
1638 // message processing 
1639 // ---------------------------------------------------------------------------- 
1641 bool wxListCtrl::MSWCommand(WXUINT cmd
, WXWORD id
) 
1643     if (cmd 
== EN_UPDATE
) 
1645         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, id
); 
1646         event
.SetEventObject( this ); 
1647         ProcessCommand(event
); 
1650     else if (cmd 
== EN_KILLFOCUS
) 
1652         wxCommandEvent 
event(wxEVT_KILL_FOCUS
, id
); 
1653         event
.SetEventObject( this ); 
1654         ProcessCommand(event
); 
1661 bool wxListCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
1664     // prepare the event 
1665     // ----------------- 
1667     wxListEvent 
event(wxEVT_NULL
, m_windowId
); 
1668     event
.SetEventObject(this); 
1670     wxEventType eventType 
= wxEVT_NULL
; 
1672     NMHDR 
*nmhdr 
= (NMHDR 
*)lParam
; 
1674     // if your compiler is as broken as this, you should really change it: this 
1675     // code is needed for normal operation! #ifdef below is only useful for 
1676     // automatic rebuilds which are done with a very old compiler version 
1677 #ifdef HDN_BEGINTRACKA 
1679     // check for messages from the header (in report view) 
1680     HWND hwndHdr 
= ListView_GetHeader(GetHwnd()); 
1682     // is it a message from the header? 
1683     if ( nmhdr
->hwndFrom 
== hwndHdr 
) 
1685         HD_NOTIFY 
*nmHDR 
= (HD_NOTIFY 
*)nmhdr
; 
1687         event
.m_itemIndex 
= -1; 
1689         switch ( nmhdr
->code 
) 
1691             // yet another comctl32.dll bug: under NT/W2K it sends Unicode 
1692             // TRACK messages even to ANSI programs: on my system I get 
1693             // HDN_BEGINTRACKW and HDN_ENDTRACKA and no HDN_TRACK at all! 
1695             // work around is to simply catch both versions and hope that it 
1696             // works (why should this message exist in ANSI and Unicode is 
1697             // beyond me as it doesn't deal with strings at all...) 
1699             // note that fr HDN_TRACK another possibility could be to use 
1700             // HDN_ITEMCHANGING but it is sent even after HDN_ENDTRACK and when 
1701             // something other than the item width changes so we'd have to 
1702             // filter out the unwanted events then 
1703             case HDN_BEGINTRACKA
: 
1704             case HDN_BEGINTRACKW
: 
1705                 eventType 
= wxEVT_COMMAND_LIST_COL_BEGIN_DRAG
; 
1710                 if ( eventType 
== wxEVT_NULL 
) 
1711                     eventType 
= wxEVT_COMMAND_LIST_COL_DRAGGING
; 
1716                 if ( eventType 
== wxEVT_NULL 
) 
1717                     eventType 
= wxEVT_COMMAND_LIST_COL_END_DRAG
; 
1719                 event
.m_item
.m_width 
= nmHDR
->pitem
->cxy
; 
1720                 event
.m_col 
= nmHDR
->iItem
; 
1723 #if defined(__WXWINCE__) && _WIN32_WCE < 400 
1724             case GN_CONTEXTMENU
: 
1725 #endif //__WXWINCE__ 
1728                     eventType 
= wxEVT_COMMAND_LIST_COL_RIGHT_CLICK
; 
1731                     // find the column clicked: we have to search for it 
1732                     // ourselves as the notification message doesn't provide 
1735                     // where did the click occur? 
1737 #if defined(__WXWINCE__) && _WIN32_WCE < 400 
1738                   if(nmhdr
->code 
== GN_CONTEXTMENU
) { 
1739                       ptClick 
= ((NMRGINFO
*)nmhdr
)->ptAction
; 
1741 #endif //__WXWINCE__ 
1742                     if ( !::GetCursorPos(&ptClick
) ) 
1744                         wxLogLastError(_T("GetCursorPos")); 
1747                     if ( !::ScreenToClient(hwndHdr
, &ptClick
) ) 
1749                         wxLogLastError(_T("ScreenToClient(listctrl header)")); 
1752                     event
.m_pointDrag
.x 
= ptClick
.x
; 
1753                     event
.m_pointDrag
.y 
= ptClick
.y
; 
1755                     int colCount 
= Header_GetItemCount(hwndHdr
); 
1758                     for ( int col 
= 0; col 
< colCount
; col
++ ) 
1760                         if ( Header_GetItemRect(hwndHdr
, col
, &rect
) ) 
1762                             if ( ::PtInRect(&rect
, ptClick
) ) 
1772             case HDN_GETDISPINFOW
: 
1774                     LPNMHDDISPINFOW info 
= (LPNMHDDISPINFOW
) lParam
; 
1775                     // This is a fix for a strange bug under XP. 
1776                     // Normally, info->iItem is a valid index, but 
1777                     // sometimes this is a silly (large) number 
1778                     // and when we return FALSE via wxControl::MSWOnNotify 
1779                     // to indicate that it hasn't yet been processed, 
1780                     // there's a GPF in Windows. 
1781                     // By returning TRUE here, we avoid further processing 
1782                     // of this strange message. 
1783                     if ( info
->iItem 
>= GetColumnCount() ) 
1789                 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
1793 #endif // defined(HDN_BEGINTRACKA) 
1794         if ( nmhdr
->hwndFrom 
== GetHwnd() ) 
1796         // almost all messages use NM_LISTVIEW 
1797         NM_LISTVIEW 
*nmLV 
= (NM_LISTVIEW 
*)nmhdr
; 
1799         const int iItem 
= nmLV
->iItem
; 
1802         // FreeAllInternalData will cause LVN_ITEMCHANG* messages, which can be 
1803         // ignored for efficiency.  It is done here because the internal data is in the 
1804         // process of being deleted so we don't want to try and access it below. 
1805         if ( m_ignoreChangeMessages 
&& 
1806              ( (nmLV
->hdr
.code 
== LVN_ITEMCHANGED
) || (nmLV
->hdr
.code 
== LVN_ITEMCHANGING
))) 
1812         // If we have a valid item then check if there is a data value 
1813         // associated with it and put it in the event. 
1814         if ( iItem 
>= 0 && iItem 
< GetItemCount() ) 
1816             wxListItemInternalData 
*internaldata 
= 
1817                 wxGetInternalData(GetHwnd(), iItem
); 
1820                 event
.m_item
.m_data 
= internaldata
->lParam
; 
1824         switch ( nmhdr
->code 
) 
1826             case LVN_BEGINRDRAG
: 
1827                 eventType 
= wxEVT_COMMAND_LIST_BEGIN_RDRAG
; 
1831                 if ( eventType 
== wxEVT_NULL 
) 
1833                     eventType 
= wxEVT_COMMAND_LIST_BEGIN_DRAG
; 
1836                 event
.m_itemIndex 
= iItem
; 
1837                 event
.m_pointDrag
.x 
= nmLV
->ptAction
.x
; 
1838                 event
.m_pointDrag
.y 
= nmLV
->ptAction
.y
; 
1841             // NB: we have to handle both *A and *W versions here because some 
1842             //     versions of comctl32.dll send ANSI messages even to the 
1844             case LVN_BEGINLABELEDITA
: 
1845             case LVN_BEGINLABELEDITW
: 
1848                     if ( nmhdr
->code 
== LVN_BEGINLABELEDITA 
) 
1850                         item
.Init(((LV_DISPINFOA 
*)lParam
)->item
); 
1852                     else // LVN_BEGINLABELEDITW 
1854                         item
.Init(((LV_DISPINFOW 
*)lParam
)->item
); 
1857                     eventType 
= wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
; 
1858                     wxConvertFromMSWListItem(GetHwnd(), event
.m_item
, item
); 
1859                     event
.m_itemIndex 
= event
.m_item
.m_itemId
; 
1863             case LVN_ENDLABELEDITA
: 
1864             case LVN_ENDLABELEDITW
: 
1867                     if ( nmhdr
->code 
== LVN_ENDLABELEDITA 
) 
1869                         item
.Init(((LV_DISPINFOA 
*)lParam
)->item
); 
1871                     else // LVN_ENDLABELEDITW 
1873                         item
.Init(((LV_DISPINFOW 
*)lParam
)->item
); 
1876                     // was editing cancelled? 
1877                     const LV_ITEM
& lvi 
= (LV_ITEM
)item
; 
1878                     if ( !lvi
.pszText 
|| lvi
.iItem 
== -1 ) 
1880                         // don't keep a stale wxTextCtrl around 
1883                             // EDIT control will be deleted by the list control itself so 
1884                             // prevent us from deleting it as well 
1885                             m_textCtrl
->UnsubclassWin(); 
1886                             m_textCtrl
->SetHWND(0); 
1891                         event
.SetEditCanceled(true); 
1894                     eventType 
= wxEVT_COMMAND_LIST_END_LABEL_EDIT
; 
1895                     wxConvertFromMSWListItem(NULL
, event
.m_item
, item
); 
1896                     event
.m_itemIndex 
= event
.m_item
.m_itemId
; 
1900             case LVN_COLUMNCLICK
: 
1901                 eventType 
= wxEVT_COMMAND_LIST_COL_CLICK
; 
1902                 event
.m_itemIndex 
= -1; 
1903                 event
.m_col 
= nmLV
->iSubItem
; 
1906             case LVN_DELETEALLITEMS
: 
1908                 eventType 
= wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS
; 
1909                 event
.m_itemIndex 
= -1; 
1912             case LVN_DELETEITEM
: 
1914                     // this should be prevented by the post-processing code below, 
1915                     // but "just in case" 
1918                 eventType 
= wxEVT_COMMAND_LIST_DELETE_ITEM
; 
1919                 event
.m_itemIndex 
= iItem
; 
1920                 // delete the assoicated internal data 
1921                 wxDeleteInternalData(this, iItem
); 
1924             case LVN_SETDISPINFO
: 
1926                     eventType 
= wxEVT_COMMAND_LIST_SET_INFO
; 
1927                     LV_DISPINFO 
*info 
= (LV_DISPINFO 
*)lParam
; 
1928                     wxConvertFromMSWListItem(GetHwnd(), event
.m_item
, info
->item
); 
1932             case LVN_INSERTITEM
: 
1933                 eventType 
= wxEVT_COMMAND_LIST_INSERT_ITEM
; 
1934                 event
.m_itemIndex 
= iItem
; 
1937             case LVN_ITEMCHANGED
: 
1938                 // we translate this catch all message into more interesting 
1939                 // (and more easy to process) wxWindows events 
1941                 // first of all, we deal with the state change events only and 
1942                 // only for valid items (item == -1 for the virtual list 
1944                 if ( nmLV
->uChanged 
& LVIF_STATE 
&& iItem 
!= -1 ) 
1946                     // temp vars for readability 
1947                     const UINT stOld 
= nmLV
->uOldState
; 
1948                     const UINT stNew 
= nmLV
->uNewState
; 
1950                     event
.m_item
.SetId(iItem
); 
1951                     event
.m_item
.SetMask(wxLIST_MASK_TEXT 
| 
1954                     GetItem(event
.m_item
); 
1956                     // has the focus changed? 
1957                     if ( !(stOld 
& LVIS_FOCUSED
) && (stNew 
& LVIS_FOCUSED
) ) 
1959                         eventType 
= wxEVT_COMMAND_LIST_ITEM_FOCUSED
; 
1960                         event
.m_itemIndex 
= iItem
; 
1963                     if ( (stNew 
& LVIS_SELECTED
) != (stOld 
& LVIS_SELECTED
) ) 
1965                         if ( eventType 
!= wxEVT_NULL 
) 
1967                             // focus and selection have both changed: send the 
1968                             // focus event from here and the selection one 
1970                             event
.SetEventType(eventType
); 
1971                             (void)GetEventHandler()->ProcessEvent(event
); 
1973                         else // no focus event to send 
1975                             // then need to set m_itemIndex as it wasn't done 
1977                             event
.m_itemIndex 
= iItem
; 
1980                         eventType 
= stNew 
& LVIS_SELECTED
 
1981                                         ? wxEVT_COMMAND_LIST_ITEM_SELECTED
 
1982                                         : wxEVT_COMMAND_LIST_ITEM_DESELECTED
; 
1986                 if ( eventType 
== wxEVT_NULL 
) 
1988                     // not an interesting event for us 
1996                     LV_KEYDOWN 
*info 
= (LV_KEYDOWN 
*)lParam
; 
1997                     WORD wVKey 
= info
->wVKey
; 
1999                     // get the current selection 
2000                     long lItem 
= GetNextItem(-1, 
2002                                              wxLIST_STATE_SELECTED
); 
2004                     // <Enter> or <Space> activate the selected item if any (but 
2005                     // not with Shift and/or Ctrl as then they have a predefined 
2006                     // meaning for the list view) 
2008                          (wVKey 
== VK_RETURN 
|| wVKey 
== VK_SPACE
) && 
2009                          !(wxIsShiftDown() || wxIsCtrlDown()) ) 
2011                         eventType 
= wxEVT_COMMAND_LIST_ITEM_ACTIVATED
; 
2015                         eventType 
= wxEVT_COMMAND_LIST_KEY_DOWN
; 
2017                         // wxCharCodeMSWToWX() returns 0 if the key is an ASCII 
2018                         // value which should be used as is 
2019                         int code 
= wxCharCodeMSWToWX(wVKey
); 
2020                         event
.m_code 
= code 
? code 
: wVKey
; 
2024                     event
.m_item
.m_itemId 
= lItem
; 
2028                         // fill the other fields too 
2029                         event
.m_item
.m_text 
= GetItemText(lItem
); 
2030                         event
.m_item
.m_data 
= GetItemData(lItem
); 
2036                 // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do 
2038                 if ( wxControl::MSWOnNotify(idCtrl
, lParam
, result
) ) 
2043                 // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event 
2044                 // if it happened on an item (and not on empty place) 
2051                 eventType 
= wxEVT_COMMAND_LIST_ITEM_ACTIVATED
; 
2052                 event
.m_itemIndex 
= iItem
; 
2053                 event
.m_item
.m_text 
= GetItemText(iItem
); 
2054                 event
.m_item
.m_data 
= GetItemData(iItem
); 
2057 #if defined(__WXWINCE__) && _WIN32_WCE < 400 
2058             case GN_CONTEXTMENU
: 
2059 #endif //__WXWINCE__ 
2061                 // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(), 
2062                 // don't do anything else 
2063                 if ( wxControl::MSWOnNotify(idCtrl
, lParam
, result
) ) 
2068                 // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event 
2069                 LV_HITTESTINFO lvhti
; 
2070                 wxZeroMemory(lvhti
); 
2072 #if defined(__WXWINCE__) && _WIN32_WCE < 400 
2073               if(nmhdr
->code 
== GN_CONTEXTMENU
) { 
2074                   lvhti
.pt 
= ((NMRGINFO
*)nmhdr
)->ptAction
; 
2076 #endif //__WXWINCE__ 
2077                 ::GetCursorPos(&(lvhti
.pt
)); 
2078                 ::ScreenToClient(GetHwnd(),&(lvhti
.pt
)); 
2079                 if ( ListView_HitTest(GetHwnd(),&lvhti
) != -1 ) 
2081                     if ( lvhti
.flags 
& LVHT_ONITEM 
) 
2083                         eventType 
= wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK
; 
2084                         event
.m_itemIndex 
= lvhti
.iItem
; 
2085                         event
.m_pointDrag
.x 
= lvhti
.pt
.x
; 
2086                         event
.m_pointDrag
.y 
= lvhti
.pt
.y
; 
2091 #ifdef NM_CUSTOMDRAW 
2093                 *result 
= OnCustomDraw(lParam
); 
2096 #endif // _WIN32_IE >= 0x300 
2098             case LVN_ODCACHEHINT
: 
2100                     const NM_CACHEHINT 
*cacheHint 
= (NM_CACHEHINT 
*)lParam
; 
2102                     eventType 
= wxEVT_COMMAND_LIST_CACHE_HINT
; 
2104                     // we get some really stupid cache hints like ones for 
2105                     // items in range 0..0 for an empty control or, after 
2106                     // deleting an item, for items in invalid range -- filter 
2108                     if ( cacheHint
->iFrom 
> cacheHint
->iTo 
) 
2111                     event
.m_oldItemIndex 
= cacheHint
->iFrom
; 
2113                     const long iMax 
= GetItemCount(); 
2114                     event
.m_itemIndex 
= cacheHint
->iTo 
< iMax 
? cacheHint
->iTo
 
2119             case LVN_GETDISPINFO
: 
2122                     LV_DISPINFO 
*info 
= (LV_DISPINFO 
*)lParam
; 
2124                     LV_ITEM
& lvi 
= info
->item
; 
2125                     long item 
= lvi
.iItem
; 
2127                     if ( lvi
.mask 
& LVIF_TEXT 
) 
2129                         wxString text 
= OnGetItemText(item
, lvi
.iSubItem
); 
2130                         wxStrncpy(lvi
.pszText
, text
, lvi
.cchTextMax
); 
2133                     // see comment at the end of wxListCtrl::GetColumn() 
2134 #ifdef NM_CUSTOMDRAW 
2135                     if ( lvi
.mask 
& LVIF_IMAGE 
) 
2137                         lvi
.iImage 
= OnGetItemImage(item
); 
2139 #endif // NM_CUSTOMDRAW 
2141                     // a little dose of healthy paranoia: as we never use 
2142                     // LVM_SETCALLBACKMASK we're not supposed to get these ones 
2143                     wxASSERT_MSG( !(lvi
.mask 
& LVIF_STATE
), 
2144                                   _T("we don't support state callbacks yet!") ); 
2151                 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
2156         // where did this one come from? 
2160     // process the event 
2161     // ----------------- 
2163     event
.SetEventType(eventType
); 
2165     bool processed 
= GetEventHandler()->ProcessEvent(event
); 
2169     switch ( nmhdr
->code 
) 
2171         case LVN_DELETEALLITEMS
: 
2172             // always return TRUE to suppress all additional LVN_DELETEITEM 
2173             // notifications - this makes deleting all items from a list ctrl 
2178         case LVN_ENDLABELEDITA
: 
2179         case LVN_ENDLABELEDITW
: 
2180             // logic here is inversed compared to all the other messages 
2181             *result 
= event
.IsAllowed(); 
2183             // don't keep a stale wxTextCtrl around 
2186                 // EDIT control will be deleted by the list control itself so 
2187                 // prevent us from deleting it as well 
2188                 m_textCtrl
->UnsubclassWin(); 
2189                 m_textCtrl
->SetHWND(0); 
2198         *result 
= !event
.IsAllowed(); 
2203 // see comment at the end of wxListCtrl::GetColumn() 
2204 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
2206 WXLPARAM 
wxListCtrl::OnCustomDraw(WXLPARAM lParam
) 
2208     LPNMLVCUSTOMDRAW lplvcd 
= (LPNMLVCUSTOMDRAW
)lParam
; 
2209     NMCUSTOMDRAW
& nmcd 
= lplvcd
->nmcd
; 
2210     switch ( nmcd
.dwDrawStage 
) 
2213             // if we've got any items with non standard attributes, 
2214             // notify us before painting each item 
2216             // for virtual controls, always suppose that we have attributes as 
2217             // there is no way to check for this 
2218             return IsVirtual() || m_hasAnyAttr 
? CDRF_NOTIFYITEMDRAW
 
2221         case CDDS_ITEMPREPAINT
: 
2223                 size_t item 
= (size_t)nmcd
.dwItemSpec
; 
2224                 if ( item 
>= (size_t)GetItemCount() ) 
2226                     // we get this message with item == 0 for an empty control, 
2227                     // we must ignore it as calling OnGetItemAttr() would be 
2229                     return CDRF_DODEFAULT
; 
2232                 wxListItemAttr 
*attr 
= 
2233                     IsVirtual() ? OnGetItemAttr(item
) 
2234                                 : wxGetInternalDataAttr(this, item
); 
2238                     // nothing to do for this item 
2239                     return CDRF_DODEFAULT
; 
2243                 wxColour colText
, colBack
; 
2244                 if ( attr
->HasFont() ) 
2246                     wxFont font 
= attr
->GetFont(); 
2247                     hFont 
= (HFONT
)font
.GetResourceHandle(); 
2254                 if ( attr
->HasTextColour() ) 
2256                     colText 
= attr
->GetTextColour(); 
2260                     colText 
= GetTextColour(); 
2263                 if ( attr
->HasBackgroundColour() ) 
2265                     colBack 
= attr
->GetBackgroundColour(); 
2269                     colBack 
= GetBackgroundColour(); 
2272                 lplvcd
->clrText 
= wxColourToRGB(colText
); 
2273                 lplvcd
->clrTextBk 
= wxColourToRGB(colBack
); 
2275                 // note that if we wanted to set colours for 
2276                 // individual columns (subitems), we would have 
2277                 // returned CDRF_NOTIFYSUBITEMREDRAW from here 
2280                     ::SelectObject(nmcd
.hdc
, hFont
); 
2282                     return CDRF_NEWFONT
; 
2285             // fall through to return CDRF_DODEFAULT 
2288             return CDRF_DODEFAULT
; 
2292 #endif // NM_CUSTOMDRAW supported 
2294 // Necessary for drawing hrules and vrules, if specified 
2295 void wxListCtrl::OnPaint(wxPaintEvent
& event
) 
2299     wxControl::OnPaint(event
); 
2301     // Reset the device origin since it may have been set 
2302     dc
.SetDeviceOrigin(0, 0); 
2304     bool drawHRules 
= HasFlag(wxLC_HRULES
); 
2305     bool drawVRules 
= HasFlag(wxLC_VRULES
); 
2307     if (!InReportView() || !drawHRules 
&& !drawVRules
) 
2310     wxPen 
pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
), 1, wxSOLID
); 
2312     dc
.SetBrush(* wxTRANSPARENT_BRUSH
); 
2314     wxSize clientSize 
= GetClientSize(); 
2318     int itemCount 
= GetItemCount(); 
2322         long top 
= GetTopItem(); 
2323         for (i 
= top
; i 
< top 
+ GetCountPerPage() + 1; i
++) 
2325             if (GetItemRect(i
, itemRect
)) 
2327                 cy 
= itemRect
.GetTop(); 
2328                 if (i 
!= 0) // Don't draw the first one 
2330                     dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
2333                 if (i 
== itemCount 
- 1) 
2335                     cy 
= itemRect
.GetBottom(); 
2336                     dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
2342     if (drawVRules 
&& (i 
> -1)) 
2344         wxRect firstItemRect
; 
2345         GetItemRect(0, firstItemRect
); 
2347         if (GetItemRect(i
, itemRect
)) 
2350             int x 
= itemRect
.GetX(); 
2351             for (col 
= 0; col 
< GetColumnCount(); col
++) 
2353                 int colWidth 
= GetColumnWidth(col
); 
2355                 dc
.DrawLine(x
-1, firstItemRect
.GetY() - 2, x
-1, itemRect
.GetBottom()); 
2361 // ---------------------------------------------------------------------------- 
2362 // virtual list controls 
2363 // ---------------------------------------------------------------------------- 
2365 wxString 
wxListCtrl::OnGetItemText(long WXUNUSED(item
), long WXUNUSED(col
)) const 
2367     // this is a pure virtual function, in fact - which is not really pure 
2368     // because the controls which are not virtual don't need to implement it 
2369     wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") ); 
2371     return wxEmptyString
; 
2374 int wxListCtrl::OnGetItemImage(long WXUNUSED(item
)) const 
2377     wxFAIL_MSG( _T("wxListCtrl::OnGetItemImage not supposed to be called") ); 
2382 wxListItemAttr 
*wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item
)) const 
2384     wxASSERT_MSG( item 
>= 0 && item 
< GetItemCount(), 
2385                   _T("invalid item index in OnGetItemAttr()") ); 
2387     // no attributes by default 
2391 void wxListCtrl::SetItemCount(long count
) 
2393     wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); 
2395     if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT
, (WPARAM
)count
, 
2396                         LVSICF_NOSCROLL 
| LVSICF_NOINVALIDATEALL
) ) 
2398         wxLogLastError(_T("ListView_SetItemCount")); 
2401     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
2402                   wxT("m_count should match ListView_GetItemCount")); 
2405 void wxListCtrl::RefreshItem(long item
) 
2407     // strangely enough, ListView_Update() results in much more flicker here 
2408     // than a dumb Refresh() -- why? 
2410     if ( !ListView_Update(GetHwnd(), item
) ) 
2412         wxLogLastError(_T("ListView_Update")); 
2416     GetItemRect(item
, rect
); 
2421 void wxListCtrl::RefreshItems(long itemFrom
, long itemTo
) 
2423     wxRect rect1
, rect2
; 
2424     GetItemRect(itemFrom
, rect1
); 
2425     GetItemRect(itemTo
, rect2
); 
2427     wxRect rect 
= rect1
; 
2428     rect
.height 
= rect2
.GetBottom() - rect1
.GetTop(); 
2433 // ---------------------------------------------------------------------------- 
2434 // internal data stuff 
2435 // ---------------------------------------------------------------------------- 
2437 static wxListItemInternalData 
*wxGetInternalData(HWND hwnd
, long itemId
) 
2440     it
.mask 
= LVIF_PARAM
; 
2443     if ( !ListView_GetItem(hwnd
, &it
) ) 
2446     return (wxListItemInternalData 
*) it
.lParam
; 
2450 wxListItemInternalData 
*wxGetInternalData(const wxListCtrl 
*ctl
, long itemId
) 
2452     return wxGetInternalData(GetHwndOf(ctl
), itemId
); 
2455 static wxListItemAttr 
*wxGetInternalDataAttr(wxListCtrl 
*ctl
, long itemId
) 
2457     wxListItemInternalData 
*data 
= wxGetInternalData(ctl
, itemId
); 
2459     return data 
? data
->attr 
: NULL
; 
2462 static void wxDeleteInternalData(wxListCtrl
* ctl
, long itemId
) 
2464     wxListItemInternalData 
*data 
= wxGetInternalData(ctl
, itemId
); 
2468         memset(&item
, 0, sizeof(item
)); 
2469         item
.iItem 
= itemId
; 
2470         item
.mask 
= LVIF_PARAM
; 
2471         item
.lParam 
= (LPARAM
) 0; 
2472         ListView_SetItem((HWND
)ctl
->GetHWND(), &item
); 
2477 // ---------------------------------------------------------------------------- 
2478 // wxWin <-> MSW items conversions 
2479 // ---------------------------------------------------------------------------- 
2481 static void wxConvertFromMSWListItem(HWND hwndListCtrl
, 
2485     wxListItemInternalData 
*internaldata 
= 
2486         (wxListItemInternalData 
*) lvItem
.lParam
; 
2489         info
.m_data 
= internaldata
->lParam
; 
2493     info
.m_stateMask 
= 0; 
2494     info
.m_itemId 
= lvItem
.iItem
; 
2496     long oldMask 
= lvItem
.mask
; 
2498     bool needText 
= FALSE
; 
2499     if (hwndListCtrl 
!= 0) 
2501         if ( lvItem
.mask 
& LVIF_TEXT 
) 
2508             lvItem
.pszText 
= new wxChar
[513]; 
2509             lvItem
.cchTextMax 
= 512; 
2511         lvItem
.mask 
|= LVIF_TEXT 
| LVIF_IMAGE 
| LVIF_PARAM
; 
2512         ::SendMessage(hwndListCtrl
, LVM_GETITEM
, 0, (LPARAM
)& lvItem
); 
2515     if ( lvItem
.mask 
& LVIF_STATE 
) 
2517         info
.m_mask 
|= wxLIST_MASK_STATE
; 
2519         if ( lvItem
.stateMask 
& LVIS_CUT
) 
2521             info
.m_stateMask 
|= wxLIST_STATE_CUT
; 
2522             if ( lvItem
.state 
& LVIS_CUT 
) 
2523                 info
.m_state 
|= wxLIST_STATE_CUT
; 
2525         if ( lvItem
.stateMask 
& LVIS_DROPHILITED
) 
2527             info
.m_stateMask 
|= wxLIST_STATE_DROPHILITED
; 
2528             if ( lvItem
.state 
& LVIS_DROPHILITED 
) 
2529                 info
.m_state 
|= wxLIST_STATE_DROPHILITED
; 
2531         if ( lvItem
.stateMask 
& LVIS_FOCUSED
) 
2533             info
.m_stateMask 
|= wxLIST_STATE_FOCUSED
; 
2534             if ( lvItem
.state 
& LVIS_FOCUSED 
) 
2535                 info
.m_state 
|= wxLIST_STATE_FOCUSED
; 
2537         if ( lvItem
.stateMask 
& LVIS_SELECTED
) 
2539             info
.m_stateMask 
|= wxLIST_STATE_SELECTED
; 
2540             if ( lvItem
.state 
& LVIS_SELECTED 
) 
2541                 info
.m_state 
|= wxLIST_STATE_SELECTED
; 
2545     if ( lvItem
.mask 
& LVIF_TEXT 
) 
2547         info
.m_mask 
|= wxLIST_MASK_TEXT
; 
2548         info
.m_text 
= lvItem
.pszText
; 
2550     if ( lvItem
.mask 
& LVIF_IMAGE 
) 
2552         info
.m_mask 
|= wxLIST_MASK_IMAGE
; 
2553         info
.m_image 
= lvItem
.iImage
; 
2555     if ( lvItem
.mask 
& LVIF_PARAM 
) 
2556         info
.m_mask 
|= wxLIST_MASK_DATA
; 
2557     if ( lvItem
.mask 
& LVIF_DI_SETITEM 
) 
2558         info
.m_mask 
|= wxLIST_SET_ITEM
; 
2559     info
.m_col 
= lvItem
.iSubItem
; 
2564             delete[] lvItem
.pszText
; 
2566     lvItem
.mask 
= oldMask
; 
2569 static void wxConvertToMSWFlags(long state
, long stateMask
, LV_ITEM
& lvItem
) 
2571     if (stateMask 
& wxLIST_STATE_CUT
) 
2573         lvItem
.stateMask 
|= LVIS_CUT
; 
2574         if (state 
& wxLIST_STATE_CUT
) 
2575             lvItem
.state 
|= LVIS_CUT
; 
2577     if (stateMask 
& wxLIST_STATE_DROPHILITED
) 
2579         lvItem
.stateMask 
|= LVIS_DROPHILITED
; 
2580         if (state 
& wxLIST_STATE_DROPHILITED
) 
2581             lvItem
.state 
|= LVIS_DROPHILITED
; 
2583     if (stateMask 
& wxLIST_STATE_FOCUSED
) 
2585         lvItem
.stateMask 
|= LVIS_FOCUSED
; 
2586         if (state 
& wxLIST_STATE_FOCUSED
) 
2587             lvItem
.state 
|= LVIS_FOCUSED
; 
2589     if (stateMask 
& wxLIST_STATE_SELECTED
) 
2591         lvItem
.stateMask 
|= LVIS_SELECTED
; 
2592         if (state 
& wxLIST_STATE_SELECTED
) 
2593             lvItem
.state 
|= LVIS_SELECTED
; 
2597 static void wxConvertToMSWListItem(const wxListCtrl 
*ctrl
, 
2598                                    const wxListItem
& info
, 
2601     lvItem
.iItem 
= (int) info
.m_itemId
; 
2603     lvItem
.iImage 
= info
.m_image
; 
2604     lvItem
.stateMask 
= 0; 
2607     lvItem
.iSubItem 
= info
.m_col
; 
2609     if (info
.m_mask 
& wxLIST_MASK_STATE
) 
2611         lvItem
.mask 
|= LVIF_STATE
; 
2613         wxConvertToMSWFlags(info
.m_state
, info
.m_stateMask
, lvItem
); 
2616     if (info
.m_mask 
& wxLIST_MASK_TEXT
) 
2618         lvItem
.mask 
|= LVIF_TEXT
; 
2619         if ( ctrl
->HasFlag(wxLC_USER_TEXT
) ) 
2621             lvItem
.pszText 
= LPSTR_TEXTCALLBACK
; 
2625             // pszText is not const, hence the cast 
2626             lvItem
.pszText 
= (wxChar 
*)info
.m_text
.c_str(); 
2627             if ( lvItem
.pszText 
) 
2628                 lvItem
.cchTextMax 
= info
.m_text
.Length(); 
2630                 lvItem
.cchTextMax 
= 0; 
2633     if (info
.m_mask 
& wxLIST_MASK_IMAGE
) 
2634         lvItem
.mask 
|= LVIF_IMAGE
; 
2637 static void wxConvertToMSWListCol(int WXUNUSED(col
), const wxListItem
& item
, 
2640     wxZeroMemory(lvCol
); 
2642     if ( item
.m_mask 
& wxLIST_MASK_TEXT 
) 
2644         lvCol
.mask 
|= LVCF_TEXT
; 
2645         lvCol
.pszText 
= (wxChar 
*)item
.m_text
.c_str(); // cast is safe 
2648     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
2650         lvCol
.mask 
|= LVCF_FMT
; 
2652         if ( item
.m_format 
== wxLIST_FORMAT_LEFT 
) 
2653             lvCol
.fmt 
= LVCFMT_LEFT
; 
2654         else if ( item
.m_format 
== wxLIST_FORMAT_RIGHT 
) 
2655             lvCol
.fmt 
= LVCFMT_RIGHT
; 
2656         else if ( item
.m_format 
== wxLIST_FORMAT_CENTRE 
) 
2657             lvCol
.fmt 
= LVCFMT_CENTER
; 
2660     if ( item
.m_mask 
& wxLIST_MASK_WIDTH 
) 
2662         lvCol
.mask 
|= LVCF_WIDTH
; 
2663         if ( item
.m_width 
== wxLIST_AUTOSIZE
) 
2664             lvCol
.cx 
= LVSCW_AUTOSIZE
; 
2665         else if ( item
.m_width 
== wxLIST_AUTOSIZE_USEHEADER
) 
2666             lvCol
.cx 
= LVSCW_AUTOSIZE_USEHEADER
; 
2668             lvCol
.cx 
= item
.m_width
; 
2671     // see comment at the end of wxListCtrl::GetColumn() 
2672 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
2673     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
2675         if ( wxTheApp
->GetComCtl32Version() >= 470 ) 
2677             lvCol
.mask 
|= LVCF_IMAGE 
| LVCF_FMT
; 
2679             // we use LVCFMT_BITMAP_ON_RIGHT because thei mages on the right 
2680             // seem to be generally nicer than on the left and the generic 
2681             // version only draws them on the right (we don't have a flag to 
2682             // specify the image location anyhow) 
2684             // we don't use LVCFMT_COL_HAS_IMAGES because it doesn't seem to 
2685             // make any difference in my tests -- but maybe we should? 
2686             lvCol
.fmt 
|= LVCFMT_BITMAP_ON_RIGHT 
| LVCFMT_IMAGE
; 
2688             lvCol
.iImage 
= item
.m_image
; 
2690         //else: it doesn't support item images anyhow 
2692 #endif // _WIN32_IE >= 0x0300 
2695 #endif // wxUSE_LISTCTRL