1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/listctrl.cpp 
   4 // Author:      Julian Smart 
   5 // Modified by: Agron Selimaj 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/listctrl.h" 
  32     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  36     #include "wx/settings.h" 
  37     #include "wx/stopwatch.h" 
  38     #include "wx/dcclient.h" 
  39     #include "wx/textctrl.h" 
  42 #include "wx/imaglist.h" 
  43 #include "wx/vector.h" 
  45 #include "wx/msw/private.h" 
  46 #include "wx/msw/private/keyboard.h" 
  48 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) 
  56 // Currently gcc and watcom don't define NMLVFINDITEM, and DMC only defines 
  57 // it by its old name NM_FINDTIEM. 
  59 #if defined(__VISUALC__) || defined(__BORLANDC__) || defined(NMLVFINDITEM) 
  60     #define HAVE_NMLVFINDITEM 1 
  61 #elif defined(__DMC__) || defined(NM_FINDITEM) 
  62     #define HAVE_NMLVFINDITEM 1 
  63     #define NMLVFINDITEM NM_FINDITEM 
  66 // ---------------------------------------------------------------------------- 
  68 // ---------------------------------------------------------------------------- 
  70 // convert our state and mask flags to LV_ITEM constants 
  71 static void wxConvertToMSWFlags(long state
, long mask
, LV_ITEM
& lvItem
); 
  73 // convert wxListItem to LV_ITEM 
  74 static void wxConvertToMSWListItem(const wxListCtrl 
*ctrl
, 
  75                                    const wxListItem
& info
, LV_ITEM
& lvItem
); 
  77 // convert LV_ITEM to wxListItem 
  78 static void wxConvertFromMSWListItem(HWND hwndListCtrl
, 
  80                                      /* const */ LV_ITEM
& lvItem
); 
  82 // convert our wxListItem to LV_COLUMN 
  83 static void wxConvertToMSWListCol(HWND hwndList
, 
  85                                   const wxListItem
& item
, 
  91 // replacement for ListView_GetSubItemRect() which provokes warnings like 
  92 // "the address of 'rc' will always evaluate as 'true'" when used with mingw32 
  95 // this function does no error checking on item and subitem parameters, notice 
  96 // that subitem 0 means the whole item so there is no way to retrieve the 
  97 // rectangle of the first subitem using this function, in particular notice 
  98 // that the index is *not* 1-based, in spite of what MSDN says 
 100 wxGetListCtrlSubItemRect(HWND hwnd
, int item
, int subitem
, int flags
, RECT
& rect
) 
 104     return ::SendMessage(hwnd
, LVM_GETSUBITEMRECT
, item
, (LPARAM
)&rect
) != 0; 
 108 wxGetListCtrlItemRect(HWND hwnd
, int item
, int flags
, RECT
& rect
) 
 110     return wxGetListCtrlSubItemRect(hwnd
, item
, 0, flags
, rect
); 
 113 } // anonymous namespace 
 115 // ---------------------------------------------------------------------------- 
 116 // private helper classes 
 117 // ---------------------------------------------------------------------------- 
 119 // We have to handle both fooW and fooA notifications in several cases 
 120 // because of broken comctl32.dll and/or unicows.dll. This class is used to 
 121 // convert LV_ITEMA and LV_ITEMW to LV_ITEM (which is either LV_ITEMA or 
 122 // LV_ITEMW depending on wxUSE_UNICODE setting), so that it can be processed 
 123 // by wxConvertToMSWListItem(). 
 125     #define LV_ITEM_NATIVE  LV_ITEMW 
 126     #define LV_ITEM_OTHER   LV_ITEMA 
 128     #define LV_CONV_TO_WX   cMB2WX 
 129     #define LV_CONV_BUF     wxMB2WXbuf 
 131     #define LV_ITEM_NATIVE  LV_ITEMA 
 132     #define LV_ITEM_OTHER   LV_ITEMW 
 134     #define LV_CONV_TO_WX   cWC2WX 
 135     #define LV_CONV_BUF     wxWC2WXbuf 
 136 #endif // Unicode/ANSI 
 141     // default ctor, use Init() later 
 142     wxLV_ITEM() { m_buf 
= NULL
; m_pItem 
= NULL
; } 
 144     // init without conversion 
 145     void Init(LV_ITEM_NATIVE
& item
) 
 147         wxASSERT_MSG( !m_pItem
, wxT("Init() called twice?") ); 
 152     // init with conversion 
 153     void Init(const LV_ITEM_OTHER
& item
) 
 155         // avoid unnecessary dynamic memory allocation, jjust make m_pItem 
 156         // point to our own m_item 
 158         // memcpy() can't work if the struct sizes are different 
 159         wxCOMPILE_TIME_ASSERT( sizeof(LV_ITEM_OTHER
) == sizeof(LV_ITEM_NATIVE
), 
 160                                CodeCantWorkIfDiffSizes
); 
 162         memcpy(&m_item
, &item
, sizeof(LV_ITEM_NATIVE
)); 
 164         // convert text from ANSI to Unicod if necessary 
 165         if ( (item
.mask 
& LVIF_TEXT
) && item
.pszText 
) 
 167             m_buf 
= new LV_CONV_BUF(wxConvLocal
.LV_CONV_TO_WX(item
.pszText
)); 
 168             m_item
.pszText 
= (wxChar 
*)m_buf
->data(); 
 172     // ctor without conversion 
 173     wxLV_ITEM(LV_ITEM_NATIVE
& item
) : m_buf(NULL
), m_pItem(&item
) { } 
 175     // ctor with conversion 
 176     wxLV_ITEM(LV_ITEM_OTHER
& item
) : m_buf(NULL
) 
 181     ~wxLV_ITEM() { delete m_buf
; } 
 183     // conversion to the real LV_ITEM 
 184     operator LV_ITEM_NATIVE
&() const { return *m_pItem
; } 
 189     LV_ITEM_NATIVE 
*m_pItem
; 
 190     LV_ITEM_NATIVE m_item
; 
 192     wxDECLARE_NO_COPY_CLASS(wxLV_ITEM
); 
 195 /////////////////////////////////////////////////////// 
 197 // The MSW version had problems with SetTextColour() et 
 198 // al as the wxListItemAttr's were stored keyed on the 
 199 // item index. If a item was inserted anywhere but the end 
 200 // of the list the text attributes (colour etc) for 
 201 // the following items were out of sync. 
 204 // Under MSW the only way to associate data with a List 
 205 // item independent of its position in the list is to 
 206 // store a pointer to it in its lParam attribute. However 
 207 // user programs are already using this (via the 
 208 // SetItemData() GetItemData() calls). 
 210 // However what we can do is store a pointer to a 
 211 // structure which contains the attributes we want *and* 
 212 // a lParam -- and this is what wxMSWListItemData does. 
 214 // To conserve memory, a wxMSWListItemData is 
 215 // only allocated for a LV_ITEM if text attributes or 
 216 // user data(lparam) are being set. 
 217 class wxMSWListItemData
 
 220    wxMSWListItemData() : attr(NULL
), lParam(0) {} 
 221    ~wxMSWListItemData() { delete attr
; } 
 223     wxListItemAttr 
*attr
; 
 224     LPARAM lParam
; // real user data 
 226     wxDECLARE_NO_COPY_CLASS(wxMSWListItemData
); 
 229 BEGIN_EVENT_TABLE(wxListCtrl
, wxListCtrlBase
) 
 230     EVT_PAINT(wxListCtrl::OnPaint
) 
 231     EVT_CHAR_HOOK(wxListCtrl::OnCharHook
) 
 234 // ============================================================================ 
 236 // ============================================================================ 
 238 // ---------------------------------------------------------------------------- 
 239 // wxListCtrl construction 
 240 // ---------------------------------------------------------------------------- 
 242 void wxListCtrl::Init() 
 246     m_imageListState 
= NULL
; 
 247     m_ownsImageListNormal 
= 
 248     m_ownsImageListSmall 
= 
 249     m_ownsImageListState 
= false; 
 255     m_hasAnyAttr 
= false; 
 258 bool wxListCtrl::Create(wxWindow 
*parent
, 
 263                         const wxValidator
& validator
, 
 264                         const wxString
& name
) 
 266     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 269     if ( !MSWCreateControl(WC_LISTVIEW
, wxEmptyString
, pos
, size
) ) 
 272     // explicitly say that we want to use Unicode because otherwise we get ANSI 
 273     // versions of _some_ messages (notably LVN_GETDISPINFOA) in MSLU build 
 274     wxSetCCUnicodeFormat(GetHwnd()); 
 276     // We must set the default text colour to the system/theme color, otherwise 
 277     // GetTextColour will always return black 
 278     SetTextColour(GetDefaultAttributes().colFg
); 
 280     if ( InReportView() ) 
 281         MSWSetExListStyles(); 
 286 void wxListCtrl::MSWSetExListStyles() 
 288     // for comctl32.dll v 4.70+ we want to have some non default extended 
 289     // styles because it's prettier (and also because wxGTK does it like this) 
 290     if ( wxApp::GetComCtl32Version() >= 470 ) 
 294             GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE
, 0, 
 295             // LVS_EX_LABELTIP shouldn't be used under Windows CE where it's 
 296             // not defined in the SDK headers 
 297 #ifdef LVS_EX_LABELTIP 
 300             LVS_EX_FULLROWSELECT 
| 
 301             LVS_EX_SUBITEMIMAGES 
| 
 302             // normally this should be governed by a style as it's probably not 
 303             // always appropriate, but we don't have any free styles left and 
 304             // it seems better to enable it by default than disable 
 305             LVS_EX_HEADERDRAGDROP
 
 310 WXDWORD 
wxListCtrl::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 312     WXDWORD wstyle 
= wxListCtrlBase::MSWGetStyle(style
, exstyle
); 
 314     wstyle 
|= LVS_SHAREIMAGELISTS 
| LVS_SHOWSELALWAYS
; 
 319     #define MAP_MODE_STYLE(wx, ms)                                            \ 
 320         if ( style & (wx) ) { wstyle |= (ms); nModes++; } 
 321 #else // !wxDEBUG_LEVEL 
 322     #define MAP_MODE_STYLE(wx, ms)                                            \ 
 323         if ( style & (wx) ) wstyle |= (ms); 
 324 #endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL 
 326     MAP_MODE_STYLE(wxLC_ICON
, LVS_ICON
) 
 327     MAP_MODE_STYLE(wxLC_SMALL_ICON
, LVS_SMALLICON
) 
 328     MAP_MODE_STYLE(wxLC_LIST
, LVS_LIST
) 
 329     MAP_MODE_STYLE(wxLC_REPORT
, LVS_REPORT
) 
 331     wxASSERT_MSG( nModes 
== 1, 
 332                   wxT("wxListCtrl style should have exactly one mode bit set") ); 
 334 #undef MAP_MODE_STYLE 
 336     if ( style 
& wxLC_ALIGN_LEFT 
) 
 337         wstyle 
|= LVS_ALIGNLEFT
; 
 339     if ( style 
& wxLC_ALIGN_TOP 
) 
 340         wstyle 
|= LVS_ALIGNTOP
; 
 342     if ( style 
& wxLC_AUTOARRANGE 
) 
 343         wstyle 
|= LVS_AUTOARRANGE
; 
 345     if ( style 
& wxLC_NO_SORT_HEADER 
) 
 346         wstyle 
|= LVS_NOSORTHEADER
; 
 348     if ( style 
& wxLC_NO_HEADER 
) 
 349         wstyle 
|= LVS_NOCOLUMNHEADER
; 
 351     if ( style 
& wxLC_EDIT_LABELS 
) 
 352         wstyle 
|= LVS_EDITLABELS
; 
 354     if ( style 
& wxLC_SINGLE_SEL 
) 
 355         wstyle 
|= LVS_SINGLESEL
; 
 357     if ( style 
& wxLC_SORT_ASCENDING 
) 
 359         wstyle 
|= LVS_SORTASCENDING
; 
 361         wxASSERT_MSG( !(style 
& wxLC_SORT_DESCENDING
), 
 362                       wxT("can't sort in ascending and descending orders at once") ); 
 364     else if ( style 
& wxLC_SORT_DESCENDING 
) 
 365         wstyle 
|= LVS_SORTDESCENDING
; 
 367 #if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) 
 368     if ( style 
& wxLC_VIRTUAL 
) 
 370         int ver 
= wxApp::GetComCtl32Version(); 
 373             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."), 
 374                         ver 
/ 100, ver 
% 100); 
 377         wstyle 
|= LVS_OWNERDATA
; 
 379 #endif // ancient cygwin 
 384 void wxListCtrl::UpdateStyle() 
 388         // The new window view style 
 389         DWORD dwStyleNew 
= MSWGetStyle(m_windowStyle
, NULL
); 
 391         // some styles are not returned by MSWGetStyle() 
 393             dwStyleNew 
|= WS_VISIBLE
; 
 395         // Get the current window style. 
 396         DWORD dwStyleOld 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
 398         // we don't have wxVSCROLL style, but the list control may have it, 
 399         // don't change it then 
 400         dwStyleNew 
|= dwStyleOld 
& (WS_HSCROLL 
| WS_VSCROLL
); 
 402         // Only set the window style if the view bits have changed. 
 403         if ( dwStyleOld 
!= dwStyleNew 
) 
 405             ::SetWindowLong(GetHwnd(), GWL_STYLE
, dwStyleNew
); 
 407             // if we switched to the report view, set the extended styles for 
 409             if ( !(dwStyleOld 
& LVS_REPORT
) && (dwStyleNew 
& LVS_REPORT
) ) 
 410                 MSWSetExListStyles(); 
 415 void wxListCtrl::FreeAllInternalData() 
 417     const unsigned count 
= m_internalData
.size(); 
 418     for ( unsigned n 
= 0; n 
< count
; n
++ ) 
 419         delete m_internalData
[n
]; 
 421     m_internalData
.clear(); 
 424 void wxListCtrl::DeleteEditControl() 
 428         m_textCtrl
->UnsubclassWin(); 
 429         m_textCtrl
->SetHWND(0); 
 430         wxDELETE(m_textCtrl
); 
 434 wxListCtrl::~wxListCtrl() 
 436     FreeAllInternalData(); 
 440     if (m_ownsImageListNormal
) 
 441         delete m_imageListNormal
; 
 442     if (m_ownsImageListSmall
) 
 443         delete m_imageListSmall
; 
 444     if (m_ownsImageListState
) 
 445         delete m_imageListState
; 
 448 // ---------------------------------------------------------------------------- 
 449 // set/get/change style 
 450 // ---------------------------------------------------------------------------- 
 452 // Add or remove a single window style 
 453 void wxListCtrl::SetSingleStyle(long style
, bool add
) 
 455     long flag 
= GetWindowStyleFlag(); 
 457     // Get rid of conflicting styles 
 460         if ( style 
& wxLC_MASK_TYPE
) 
 461             flag 
= flag 
& ~wxLC_MASK_TYPE
; 
 462         if ( style 
& wxLC_MASK_ALIGN 
) 
 463             flag 
= flag 
& ~wxLC_MASK_ALIGN
; 
 464         if ( style 
& wxLC_MASK_SORT 
) 
 465             flag 
= flag 
& ~wxLC_MASK_SORT
; 
 473     SetWindowStyleFlag(flag
); 
 476 // Set the whole window style 
 477 void wxListCtrl::SetWindowStyleFlag(long flag
) 
 479     if ( flag 
!= m_windowStyle 
) 
 481         wxListCtrlBase::SetWindowStyleFlag(flag
); 
 489 // ---------------------------------------------------------------------------- 
 491 // ---------------------------------------------------------------------------- 
 493 /* static */ wxVisualAttributes
 
 494 wxListCtrl::GetClassDefaultAttributes(wxWindowVariant variant
) 
 496     wxVisualAttributes attrs 
= GetCompositeControlsDefaultAttributes(variant
); 
 498     // common controls have their own default font 
 499     attrs
.font 
= wxGetCCDefaultFont(); 
 504 // Sets the foreground, i.e. text, colour 
 505 bool wxListCtrl::SetForegroundColour(const wxColour
& col
) 
 507     if ( !wxWindow::SetForegroundColour(col
) ) 
 510     ListView_SetTextColor(GetHwnd(), wxColourToRGB(col
)); 
 515 // Sets the background colour 
 516 bool wxListCtrl::SetBackgroundColour(const wxColour
& col
) 
 518     if ( !wxWindow::SetBackgroundColour(col
) ) 
 521     // we set the same colour for both the "empty" background and the items 
 523     COLORREF color 
= wxColourToRGB(col
); 
 524     ListView_SetBkColor(GetHwnd(), color
); 
 525     ListView_SetTextBkColor(GetHwnd(), color
); 
 530 // Gets information about this column 
 531 bool wxListCtrl::GetColumn(int col
, wxListItem
& item
) const 
 536     lvCol
.mask 
= LVCF_WIDTH
; 
 538     if ( item
.m_mask 
& wxLIST_MASK_TEXT 
) 
 540         lvCol
.mask 
|= LVCF_TEXT
; 
 541         lvCol
.pszText 
= new wxChar
[513]; 
 542         lvCol
.cchTextMax 
= 512; 
 545     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
 547         lvCol
.mask 
|= LVCF_FMT
; 
 550     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
 552         lvCol
.mask 
|= LVCF_IMAGE
; 
 555     bool success 
= ListView_GetColumn(GetHwnd(), col
, &lvCol
) != 0; 
 557     //  item.m_subItem = lvCol.iSubItem; 
 558     item
.m_width 
= lvCol
.cx
; 
 560     if ( (item
.m_mask 
& wxLIST_MASK_TEXT
) && lvCol
.pszText 
) 
 562         item
.m_text 
= lvCol
.pszText
; 
 563         delete[] lvCol
.pszText
; 
 566     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
 568         switch (lvCol
.fmt 
& LVCFMT_JUSTIFYMASK
) { 
 570                 item
.m_format 
= wxLIST_FORMAT_LEFT
; 
 573                 item
.m_format 
= wxLIST_FORMAT_RIGHT
; 
 576                 item
.m_format 
= wxLIST_FORMAT_CENTRE
; 
 579                 item
.m_format 
= -1;  // Unknown? 
 584     // the column images were not supported in older versions but how to check 
 585     // for this? we can't use _WIN32_IE because we always define it to a very 
 586     // high value, so see if another symbol which is only defined starting from 
 587     // comctl32.dll 4.70 is available 
 588 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
 589     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
 591         item
.m_image 
= lvCol
.iImage
; 
 593 #endif // LVCOLUMN::iImage exists 
 598 // Sets information about this column 
 599 bool wxListCtrl::SetColumn(int col
, const wxListItem
& item
) 
 602     wxConvertToMSWListCol(GetHwnd(), col
, item
, lvCol
); 
 604     return ListView_SetColumn(GetHwnd(), col
, &lvCol
) != 0; 
 607 // Gets the column width 
 608 int wxListCtrl::GetColumnWidth(int col
) const 
 610     return ListView_GetColumnWidth(GetHwnd(), col
); 
 613 // Sets the column width 
 614 bool wxListCtrl::SetColumnWidth(int col
, int width
) 
 616     if ( m_windowStyle 
& wxLC_LIST 
) 
 619     if ( width 
== wxLIST_AUTOSIZE
) 
 620         width 
= LVSCW_AUTOSIZE
; 
 621     else if ( width 
== wxLIST_AUTOSIZE_USEHEADER
) 
 622         width 
= LVSCW_AUTOSIZE_USEHEADER
; 
 624     return ListView_SetColumnWidth(GetHwnd(), col
, width
) != 0; 
 627 // ---------------------------------------------------------------------------- 
 629 // ---------------------------------------------------------------------------- 
 631 int wxListCtrl::GetColumnIndexFromOrder(int order
) const 
 633     const int numCols 
= GetColumnCount(); 
 634     wxCHECK_MSG( order 
>= 0 && order 
< numCols
, -1, 
 635                 wxT("Column position out of bounds") ); 
 637     wxArrayInt 
indexArray(numCols
); 
 638     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols
, &indexArray
[0]) ) 
 641     return indexArray
[order
]; 
 644 int wxListCtrl::GetColumnOrder(int col
) const 
 646     const int numCols 
= GetColumnCount(); 
 647     wxASSERT_MSG( col 
>= 0 && col 
< numCols
, wxT("Column index out of bounds") ); 
 649     wxArrayInt 
indexArray(numCols
); 
 650     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols
, &indexArray
[0]) ) 
 653     for ( int pos 
= 0; pos 
< numCols
; pos
++ ) 
 655         if ( indexArray
[pos
] == col 
) 
 659     wxFAIL_MSG( wxT("no column with with given order?") ); 
 664 // Gets the column order for all columns 
 665 wxArrayInt 
wxListCtrl::GetColumnsOrder() const 
 667     const int numCols 
= GetColumnCount(); 
 669     wxArrayInt 
orders(numCols
); 
 670     if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols
, &orders
[0]) ) 
 676 // Sets the column order for all columns 
 677 bool wxListCtrl::SetColumnsOrder(const wxArrayInt
& orders
) 
 679     const int numCols 
= GetColumnCount(); 
 681     wxCHECK_MSG( orders
.size() == (size_t)numCols
, false, 
 682                     wxT("wrong number of elements in column orders array") ); 
 684     return ListView_SetColumnOrderArray(GetHwnd(), numCols
, &orders
[0]) != 0; 
 688 // Gets the number of items that can fit vertically in the 
 689 // visible area of the list control (list or report view) 
 690 // or the total number of items in the list control (icon 
 691 // or small icon view) 
 692 int wxListCtrl::GetCountPerPage() const 
 694     return ListView_GetCountPerPage(GetHwnd()); 
 697 // Gets the edit control for editing labels. 
 698 wxTextCtrl
* wxListCtrl::GetEditControl() const 
 700     // first check corresponds to the case when the label editing was started 
 701     // by user and hence m_textCtrl wasn't created by EditLabel() at all, while 
 702     // the second case corresponds to us being called from inside EditLabel() 
 703     // (e.g. from a user wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT handler): in this 
 704     // case EditLabel() did create the control but it didn't have an HWND to 
 705     // initialize it with yet 
 706     if ( !m_textCtrl 
|| !m_textCtrl
->GetHWND() ) 
 708         HWND hwndEdit 
= ListView_GetEditControl(GetHwnd()); 
 711             wxListCtrl 
* const self 
= const_cast<wxListCtrl 
*>(this); 
 714                 self
->m_textCtrl 
= new wxTextCtrl
; 
 715             self
->InitEditControl((WXHWND
)hwndEdit
); 
 722 // Gets information about the item 
 723 bool wxListCtrl::GetItem(wxListItem
& info
) const 
 726     wxZeroMemory(lvItem
); 
 728     lvItem
.iItem 
= info
.m_itemId
; 
 729     lvItem
.iSubItem 
= info
.m_col
; 
 731     if ( info
.m_mask 
& wxLIST_MASK_TEXT 
) 
 733         lvItem
.mask 
|= LVIF_TEXT
; 
 734         lvItem
.pszText 
= new wxChar
[513]; 
 735         lvItem
.cchTextMax 
= 512; 
 739         lvItem
.pszText 
= NULL
; 
 742     if (info
.m_mask 
& wxLIST_MASK_DATA
) 
 743         lvItem
.mask 
|= LVIF_PARAM
; 
 745     if (info
.m_mask 
& wxLIST_MASK_IMAGE
) 
 746         lvItem
.mask 
|= LVIF_IMAGE
; 
 748     if ( info
.m_mask 
& wxLIST_MASK_STATE 
) 
 750         lvItem
.mask 
|= LVIF_STATE
; 
 751         wxConvertToMSWFlags(0, info
.m_stateMask
, lvItem
); 
 754     bool success 
= ListView_GetItem((HWND
)GetHWND(), &lvItem
) != 0; 
 757         wxLogError(_("Couldn't retrieve information about list control item %d."), 
 762         // give NULL as hwnd as we already have everything we need 
 763         wxConvertFromMSWListItem(NULL
, info
, lvItem
); 
 767         delete[] lvItem
.pszText
; 
 772 // Sets information about the item 
 773 bool wxListCtrl::SetItem(wxListItem
& info
) 
 775     const long id 
= info
.GetId(); 
 776     wxCHECK_MSG( id 
>= 0 && id 
< GetItemCount(), false, 
 777                  wxT("invalid item index in SetItem") ); 
 780     wxConvertToMSWListItem(this, info
, item
); 
 782     // we never update the lParam if it contains our pointer 
 783     // to the wxMSWListItemData structure 
 784     item
.mask 
&= ~LVIF_PARAM
; 
 786     // check if setting attributes or lParam 
 787     if ( info
.HasAttributes() || (info
.m_mask 
& wxLIST_MASK_DATA
) ) 
 789         // get internal item data 
 790         wxMSWListItemData 
*data 
= MSWGetItemData(id
); 
 794             // need to allocate the internal data object 
 795             data 
= new wxMSWListItemData
; 
 796             m_internalData
.push_back(data
); 
 797             item
.lParam 
= (LPARAM
) data
; 
 798             item
.mask 
|= LVIF_PARAM
; 
 803         if ( info
.m_mask 
& wxLIST_MASK_DATA 
) 
 804             data
->lParam 
= info
.m_data
; 
 807         if ( info
.HasAttributes() ) 
 809             const wxListItemAttr
& attrNew 
= *info
.GetAttributes(); 
 811             // don't overwrite the already set attributes if we have them 
 813                 data
->attr
->AssignFrom(attrNew
); 
 815                 data
->attr 
= new wxListItemAttr(attrNew
); 
 820     // we could be changing only the attribute in which case we don't need to 
 821     // call ListView_SetItem() at all 
 824         if ( !ListView_SetItem(GetHwnd(), &item
) ) 
 826             wxLogDebug(wxT("ListView_SetItem() failed")); 
 832     // we need to update the item immediately to show the new image 
 833     bool updateNow 
= (info
.m_mask 
& wxLIST_MASK_IMAGE
) != 0; 
 835     // check whether it has any custom attributes 
 836     if ( info
.HasAttributes() ) 
 840         // if the colour has changed, we must redraw the item 
 846         // we need this to make the change visible right now 
 847         RefreshItem(item
.iItem
); 
 853 long wxListCtrl::SetItem(long index
, int col
, const wxString
& label
, int imageId
) 
 857     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 858     info
.m_itemId 
= index
; 
 862         info
.m_image 
= imageId
; 
 863         info
.m_mask 
|= wxLIST_MASK_IMAGE
; 
 865     return SetItem(info
); 
 869 // Gets the item state 
 870 int wxListCtrl::GetItemState(long item
, long stateMask
) const 
 874     info
.m_mask 
= wxLIST_MASK_STATE
; 
 875     info
.m_stateMask 
= stateMask
; 
 876     info
.m_itemId 
= item
; 
 884 // Sets the item state 
 885 bool wxListCtrl::SetItemState(long item
, long state
, long stateMask
) 
 887     // NB: don't use SetItem() here as it doesn't work with the virtual list 
 890     wxZeroMemory(lvItem
); 
 892     wxConvertToMSWFlags(state
, stateMask
, lvItem
); 
 894     const bool changingFocus 
= (stateMask 
& wxLIST_STATE_FOCUSED
) && 
 895                                     (state 
& wxLIST_STATE_FOCUSED
); 
 897     // for the virtual list controls we need to refresh the previously focused 
 898     // item manually when changing focus without changing selection 
 899     // programmatically because otherwise it keeps its focus rectangle until 
 900     // next repaint (yet another comctl32 bug) 
 902     if ( IsVirtual() && changingFocus 
) 
 904         focusOld 
= GetNextItem(-1, wxLIST_NEXT_ALL
, wxLIST_STATE_FOCUSED
); 
 911     if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE
, 
 912                         (WPARAM
)item
, (LPARAM
)&lvItem
) ) 
 914         wxLogLastError(wxT("ListView_SetItemState")); 
 919     if ( focusOld 
!= -1 ) 
 921         // no need to refresh the item if it was previously selected, it would 
 922         // only result in annoying flicker 
 923         if ( !(GetItemState(focusOld
, 
 924                             wxLIST_STATE_SELECTED
) & wxLIST_STATE_SELECTED
) ) 
 926             RefreshItem(focusOld
); 
 930     // we expect the selection anchor, i.e. the item from which multiple 
 931     // selection (such as performed with e.g. Shift-arrows) starts, to be the 
 932     // same as the currently focused item but the native control doesn't update 
 933     // it when we change focus and leaves at the last item it set itself focus 
 934     // to, so do it explicitly 
 935     if ( changingFocus 
&& !HasFlag(wxLC_SINGLE_SEL
) ) 
 937         ListView_SetSelectionMark(GetHwnd(), item
); 
 943 // Sets the item image 
 944 bool wxListCtrl::SetItemImage(long item
, int image
, int WXUNUSED(selImage
)) 
 946     return SetItemColumnImage(item
, 0, image
); 
 949 // Sets the item image 
 950 bool wxListCtrl::SetItemColumnImage(long item
, long column
, int image
) 
 954     info
.m_mask 
= wxLIST_MASK_IMAGE
; 
 955     info
.m_image 
= image
; 
 956     info
.m_itemId 
= item
; 
 959     return SetItem(info
); 
 962 // Gets the item text 
 963 wxString 
wxListCtrl::GetItemText(long item
, int col
) const 
 967     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 968     info
.m_itemId 
= item
; 
 972         return wxEmptyString
; 
 976 // Sets the item text 
 977 void wxListCtrl::SetItemText(long item
, const wxString
& str
) 
 981     info
.m_mask 
= wxLIST_MASK_TEXT
; 
 982     info
.m_itemId 
= item
; 
 988 // Gets the internal item data 
 989 wxMSWListItemData 
*wxListCtrl::MSWGetItemData(long itemId
) const 
 992     it
.mask 
= LVIF_PARAM
; 
 995     if ( !ListView_GetItem(GetHwnd(), &it
) ) 
 998     return (wxMSWListItemData 
*) it
.lParam
; 
1001 // Gets the item data 
1002 wxUIntPtr 
wxListCtrl::GetItemData(long item
) const 
1006     info
.m_mask 
= wxLIST_MASK_DATA
; 
1007     info
.m_itemId 
= item
; 
1014 // Sets the item data 
1015 bool wxListCtrl::SetItemPtrData(long item
, wxUIntPtr data
) 
1019     info
.m_mask 
= wxLIST_MASK_DATA
; 
1020     info
.m_itemId 
= item
; 
1023     return SetItem(info
); 
1026 wxRect 
wxListCtrl::GetViewRect() const 
1030     // ListView_GetViewRect() can only be used in icon and small icon views 
1031     // (this is documented in MSDN and, indeed, it returns bogus results in 
1032     // report view, at least with comctl32.dll v6 under Windows 2003) 
1033     if ( HasFlag(wxLC_ICON 
| wxLC_SMALL_ICON
) ) 
1036         if ( !ListView_GetViewRect(GetHwnd(), &rc
) ) 
1038             wxLogDebug(wxT("ListView_GetViewRect() failed.")); 
1043         wxCopyRECTToRect(rc
, rect
); 
1045     else if ( HasFlag(wxLC_REPORT
) ) 
1047         const long count 
= GetItemCount(); 
1050             GetItemRect(wxMin(GetTopItem() + GetCountPerPage(), count 
- 1), rect
); 
1052             // extend the rectangle to start at the top (we include the column 
1053             // headers, if any, for compatibility with the generic version) 
1054             rect
.height 
+= rect
.y
; 
1060         wxFAIL_MSG( wxT("not implemented in this mode") ); 
1066 // Gets the item rectangle 
1067 bool wxListCtrl::GetItemRect(long item
, wxRect
& rect
, int code
) const 
1069     return GetSubItemRect( item
, wxLIST_GETSUBITEMRECT_WHOLEITEM
, rect
, code
) ; 
1072 bool wxListCtrl::GetSubItemRect(long item
, long subItem
, wxRect
& rect
, int code
) const 
1074     // ListView_GetSubItemRect() doesn't do subItem error checking and returns 
1075     // true even for the out of range values of it (even if the results are 
1076     // completely bogus in this case), so we check item validity ourselves 
1077     wxCHECK_MSG( subItem 
== wxLIST_GETSUBITEMRECT_WHOLEITEM 
|| 
1078                     (subItem 
>= 0 && subItem 
< GetColumnCount()), 
1079                  false, wxT("invalid sub item index") ); 
1081     // use wxCHECK_MSG against "item" too, for coherency with the generic implementation: 
1082     wxCHECK_MSG( item 
>= 0 && item 
< GetItemCount(), false, 
1083                  wxT("invalid item in GetSubItemRect") ); 
1086     if ( code 
== wxLIST_RECT_BOUNDS 
) 
1087         codeWin 
= LVIR_BOUNDS
; 
1088     else if ( code 
== wxLIST_RECT_ICON 
) 
1089         codeWin 
= LVIR_ICON
; 
1090     else if ( code 
== wxLIST_RECT_LABEL 
) 
1091         codeWin 
= LVIR_LABEL
; 
1094         wxFAIL_MSG( wxT("incorrect code in GetItemRect() / GetSubItemRect()") ); 
1095         codeWin 
= LVIR_BOUNDS
; 
1099     if ( !wxGetListCtrlSubItemRect
 
1103             subItem 
== wxLIST_GETSUBITEMRECT_WHOLEITEM 
? 0 : subItem
, 
1111     wxCopyRECTToRect(rectWin
, rect
); 
1113     // there is no way to retrieve the first sub item bounding rectangle using 
1114     // wxGetListCtrlSubItemRect() as 0 means the whole item, so we need to 
1115     // truncate it at first column ourselves 
1116     if ( subItem 
== 0 && code 
== wxLIST_RECT_BOUNDS 
) 
1117         rect
.width 
= GetColumnWidth(0); 
1125 // Gets the item position 
1126 bool wxListCtrl::GetItemPosition(long item
, wxPoint
& pos
) const 
1130     bool success 
= (ListView_GetItemPosition(GetHwnd(), (int) item
, &pt
) != 0); 
1132     pos
.x 
= pt
.x
; pos
.y 
= pt
.y
; 
1136 // Sets the item position. 
1137 bool wxListCtrl::SetItemPosition(long item
, const wxPoint
& pos
) 
1139     return (ListView_SetItemPosition(GetHwnd(), (int) item
, pos
.x
, pos
.y
) != 0); 
1142 // Gets the number of items in the list control 
1143 int wxListCtrl::GetItemCount() const 
1148 wxSize 
wxListCtrl::GetItemSpacing() const 
1150     const int spacing 
= ListView_GetItemSpacing(GetHwnd(), (BOOL
)HasFlag(wxLC_SMALL_ICON
)); 
1152     return wxSize(LOWORD(spacing
), HIWORD(spacing
)); 
1155 #if WXWIN_COMPATIBILITY_2_6 
1157 int wxListCtrl::GetItemSpacing(bool isSmall
) const 
1159     return ListView_GetItemSpacing(GetHwnd(), (BOOL
) isSmall
); 
1162 #endif // WXWIN_COMPATIBILITY_2_6 
1164 void wxListCtrl::SetItemTextColour( long item
, const wxColour 
&col 
) 
1167     info
.m_itemId 
= item
; 
1168     info
.SetTextColour( col 
); 
1172 wxColour 
wxListCtrl::GetItemTextColour( long item 
) const 
1175     wxMSWListItemData 
*data 
= MSWGetItemData(item
); 
1176     if ( data 
&& data
->attr 
) 
1177         col 
= data
->attr
->GetTextColour(); 
1182 void wxListCtrl::SetItemBackgroundColour( long item
, const wxColour 
&col 
) 
1185     info
.m_itemId 
= item
; 
1186     info
.SetBackgroundColour( col 
); 
1190 wxColour 
wxListCtrl::GetItemBackgroundColour( long item 
) const 
1193     wxMSWListItemData 
*data 
= MSWGetItemData(item
); 
1194     if ( data 
&& data
->attr 
) 
1195         col 
= data
->attr
->GetBackgroundColour(); 
1200 void wxListCtrl::SetItemFont( long item
, const wxFont 
&f 
) 
1203     info
.m_itemId 
= item
; 
1208 wxFont 
wxListCtrl::GetItemFont( long item 
) const 
1211     wxMSWListItemData 
*data 
= MSWGetItemData(item
); 
1212     if ( data 
&& data
->attr 
) 
1213         f 
= data
->attr
->GetFont(); 
1218 // Gets the number of selected items in the list control 
1219 int wxListCtrl::GetSelectedItemCount() const 
1221     return ListView_GetSelectedCount(GetHwnd()); 
1224 // Gets the text colour of the listview 
1225 wxColour 
wxListCtrl::GetTextColour() const 
1227     COLORREF ref 
= ListView_GetTextColor(GetHwnd()); 
1228     wxColour 
col(GetRValue(ref
), GetGValue(ref
), GetBValue(ref
)); 
1232 // Sets the text colour of the listview 
1233 void wxListCtrl::SetTextColour(const wxColour
& col
) 
1235     ListView_SetTextColor(GetHwnd(), PALETTERGB(col
.Red(), col
.Green(), col
.Blue())); 
1238 // Gets the index of the topmost visible item when in 
1239 // list or report view 
1240 long wxListCtrl::GetTopItem() const 
1242     return (long) ListView_GetTopIndex(GetHwnd()); 
1245 // Searches for an item, starting from 'item'. 
1246 // 'geometry' is one of 
1247 // wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT. 
1248 // 'state' is a state bit flag, one or more of 
1249 // wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT. 
1250 // item can be -1 to find the first item that matches the 
1252 // Returns the item or -1 if unsuccessful. 
1253 long wxListCtrl::GetNextItem(long item
, int geom
, int state
) const 
1257     if ( geom 
== wxLIST_NEXT_ABOVE 
) 
1258         flags 
|= LVNI_ABOVE
; 
1259     if ( geom 
== wxLIST_NEXT_ALL 
) 
1261     if ( geom 
== wxLIST_NEXT_BELOW 
) 
1262         flags 
|= LVNI_BELOW
; 
1263     if ( geom 
== wxLIST_NEXT_LEFT 
) 
1264         flags 
|= LVNI_TOLEFT
; 
1265     if ( geom 
== wxLIST_NEXT_RIGHT 
) 
1266         flags 
|= LVNI_TORIGHT
; 
1268     if ( state 
& wxLIST_STATE_CUT 
) 
1270     if ( state 
& wxLIST_STATE_DROPHILITED 
) 
1271         flags 
|= LVNI_DROPHILITED
; 
1272     if ( state 
& wxLIST_STATE_FOCUSED 
) 
1273         flags 
|= LVNI_FOCUSED
; 
1274     if ( state 
& wxLIST_STATE_SELECTED 
) 
1275         flags 
|= LVNI_SELECTED
; 
1277     return (long) ListView_GetNextItem(GetHwnd(), item
, flags
); 
1281 wxImageList 
*wxListCtrl::GetImageList(int which
) const 
1283     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1285         return m_imageListNormal
; 
1287     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1289         return m_imageListSmall
; 
1291     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1293         return m_imageListState
; 
1298 void wxListCtrl::SetImageList(wxImageList 
*imageList
, int which
) 
1301     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1303         flags 
= LVSIL_NORMAL
; 
1304         if (m_ownsImageListNormal
) delete m_imageListNormal
; 
1305         m_imageListNormal 
= imageList
; 
1306         m_ownsImageListNormal 
= false; 
1308     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1310         flags 
= LVSIL_SMALL
; 
1311         if (m_ownsImageListSmall
) delete m_imageListSmall
; 
1312         m_imageListSmall 
= imageList
; 
1313         m_ownsImageListSmall 
= false; 
1315     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1317         flags 
= LVSIL_STATE
; 
1318         if (m_ownsImageListState
) delete m_imageListState
; 
1319         m_imageListState 
= imageList
; 
1320         m_ownsImageListState 
= false; 
1322     (void) ListView_SetImageList(GetHwnd(), (HIMAGELIST
) imageList 
? imageList
->GetHIMAGELIST() : 0, flags
); 
1325 void wxListCtrl::AssignImageList(wxImageList 
*imageList
, int which
) 
1327     SetImageList(imageList
, which
); 
1328     if ( which 
== wxIMAGE_LIST_NORMAL 
) 
1329         m_ownsImageListNormal 
= true; 
1330     else if ( which 
== wxIMAGE_LIST_SMALL 
) 
1331         m_ownsImageListSmall 
= true; 
1332     else if ( which 
== wxIMAGE_LIST_STATE 
) 
1333         m_ownsImageListState 
= true; 
1336 // ---------------------------------------------------------------------------- 
1338 // ---------------------------------------------------------------------------- 
1340 wxSize 
wxListCtrl::MSWGetBestViewRect(int x
, int y
) const 
1342     const DWORD rc 
= ListView_ApproximateViewRect(GetHwnd(), x
, y
, -1); 
1344     wxSize 
size(LOWORD(rc
), HIWORD(rc
)); 
1346     // We have to add space for the scrollbars ourselves, they're not taken 
1347     // into account by ListView_ApproximateViewRect(), at least not with 
1348     // commctrl32.dll v6. 
1349     const DWORD mswStyle 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
1351     if ( mswStyle 
& WS_HSCROLL 
) 
1352         size
.y 
+= wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y
); 
1353     if ( mswStyle 
& WS_VSCROLL 
) 
1354         size
.x 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
1359 // ---------------------------------------------------------------------------- 
1361 // ---------------------------------------------------------------------------- 
1363 // Arranges the items 
1364 bool wxListCtrl::Arrange(int flag
) 
1367     if ( flag 
== wxLIST_ALIGN_LEFT 
) 
1368         code 
= LVA_ALIGNLEFT
; 
1369     else if ( flag 
== wxLIST_ALIGN_TOP 
) 
1370         code 
= LVA_ALIGNTOP
; 
1371     else if ( flag 
== wxLIST_ALIGN_DEFAULT 
) 
1373     else if ( flag 
== wxLIST_ALIGN_SNAP_TO_GRID 
) 
1374         code 
= LVA_SNAPTOGRID
; 
1376     return (ListView_Arrange(GetHwnd(), code
) != 0); 
1380 bool wxListCtrl::DeleteItem(long item
) 
1382     if ( !ListView_DeleteItem(GetHwnd(), (int) item
) ) 
1384         wxLogLastError(wxT("ListView_DeleteItem")); 
1389     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
1390                   wxT("m_count should match ListView_GetItemCount")); 
1392     // the virtual list control doesn't refresh itself correctly, help it 
1395         // we need to refresh all the lines below the one which was deleted 
1397         if ( item 
> 0 && GetItemCount() ) 
1399             GetItemRect(item 
- 1, rectItem
); 
1404             rectItem
.height 
= 0; 
1407         wxRect rectWin 
= GetRect(); 
1408         rectWin
.height 
= rectWin
.GetBottom() - rectItem
.GetBottom(); 
1409         rectWin
.y 
= rectItem
.GetBottom(); 
1411         RefreshRect(rectWin
); 
1417 // Deletes all items 
1418 bool wxListCtrl::DeleteAllItems() 
1420     // Calling ListView_DeleteAllItems() will always generate an event but we 
1421     // shouldn't do it if the control is empty 
1422     return !GetItemCount() || ListView_DeleteAllItems(GetHwnd()) != 0; 
1425 // Deletes all items 
1426 bool wxListCtrl::DeleteAllColumns() 
1428     while ( m_colCount 
> 0 ) 
1430         if ( ListView_DeleteColumn(GetHwnd(), 0) == 0 ) 
1432             wxLogLastError(wxT("ListView_DeleteColumn")); 
1440     wxASSERT_MSG( m_colCount 
== 0, wxT("no columns should be left") ); 
1446 bool wxListCtrl::DeleteColumn(int col
) 
1448     bool success 
= (ListView_DeleteColumn(GetHwnd(), col
) != 0); 
1450     if ( success 
&& (m_colCount 
> 0) ) 
1455 // Clears items, and columns if there are any. 
1456 void wxListCtrl::ClearAll() 
1459     if ( m_colCount 
> 0 ) 
1463 void wxListCtrl::InitEditControl(WXHWND hWnd
) 
1465     m_textCtrl
->SetHWND(hWnd
); 
1466     m_textCtrl
->SubclassWin(hWnd
); 
1467     m_textCtrl
->SetParent(this); 
1469     // we must disallow TABbing away from the control while the edit control is 
1470     // shown because this leaves it in some strange state (just try removing 
1471     // this line and then pressing TAB while editing an item in  listctrl 
1473     m_textCtrl
->SetWindowStyle(m_textCtrl
->GetWindowStyle() | wxTE_PROCESS_TAB
); 
1476 wxTextCtrl
* wxListCtrl::EditLabel(long item
, wxClassInfo
* textControlClass
) 
1478     wxCHECK_MSG( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)), NULL
, 
1479                   "control used for label editing must be a wxTextCtrl" ); 
1481     // ListView_EditLabel requires that the list has focus. 
1484     // create m_textCtrl here before calling ListView_EditLabel() because it 
1485     // generates wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT event from inside it and 
1486     // the user handler for it can call GetEditControl() resulting in an on 
1487     // demand creation of a stock wxTextCtrl instead of the control of a 
1488     // (possibly) custom wxClassInfo 
1489     DeleteEditControl(); 
1490     m_textCtrl 
= (wxTextCtrl 
*)textControlClass
->CreateObject(); 
1492     WXHWND hWnd 
= (WXHWND
) ListView_EditLabel(GetHwnd(), item
); 
1495         // failed to start editing 
1496         wxDELETE(m_textCtrl
); 
1501     // if GetEditControl() hasn't been called, we need to initialize the edit 
1502     // control ourselves 
1503     if ( !m_textCtrl
->GetHWND() ) 
1504         InitEditControl(hWnd
); 
1509 // End label editing, optionally cancelling the edit 
1510 bool wxListCtrl::EndEditLabel(bool cancel
) 
1512     // m_textCtrl is not always ready, ie. in EVT_LIST_BEGIN_LABEL_EDIT 
1513     HWND hwnd 
= ListView_GetEditControl(GetHwnd()); 
1517     // Newer versions of Windows have a special ListView_CancelEditLabel() 
1518     // message for cancelling editing but it, rather counter-intuitively, keeps 
1519     // the last text entered in the dialog while cancelling as we do it below 
1520     // restores the original text which is the more expected behaviour. 
1522     // We shouldn't destroy the control ourselves according to MSDN, which 
1523     // proposes WM_CANCELMODE to do this, but it doesn't seem to work so 
1524     // emulate the corresponding user action instead. 
1525     ::SendMessage(hwnd
, WM_KEYDOWN
, cancel 
? VK_ESCAPE 
: VK_RETURN
, 0); 
1530 // Ensures this item is visible 
1531 bool wxListCtrl::EnsureVisible(long item
) 
1533     return ListView_EnsureVisible(GetHwnd(), (int) item
, FALSE
) != FALSE
; 
1536 // Find an item whose label matches this string, starting from the item after 'start' 
1537 // or the beginning if 'start' is -1. 
1538 long wxListCtrl::FindItem(long start
, const wxString
& str
, bool partial
) 
1540     LV_FINDINFO findInfo
; 
1542     findInfo
.flags 
= LVFI_STRING
; 
1544         findInfo
.flags 
|= LVFI_PARTIAL
; 
1545     findInfo
.psz 
= str
.wx_str(); 
1547     // ListView_FindItem() excludes the first item from search and to look 
1548     // through all the items you need to start from -1 which is unnatural and 
1549     // inconsistent with the generic version - so we adjust the index 
1552     return ListView_FindItem(GetHwnd(), start
, &findInfo
); 
1555 // Find an item whose data matches this data, starting from the item after 
1556 // 'start' or the beginning if 'start' is -1. 
1557 long wxListCtrl::FindItem(long start
, wxUIntPtr data
) 
1559     // we can't use ListView_FindItem() directly as we don't store the data 
1560     // pointer itself in the control but rather our own internal data, so first 
1561     // we need to find the right value to search for (and there can be several 
1563     int idx 
= wxNOT_FOUND
; 
1564     const unsigned count 
= m_internalData
.size(); 
1565     for ( unsigned n 
= 0; n 
< count
; n
++ ) 
1567         if ( m_internalData
[n
]->lParam 
== (LPARAM
)data 
) 
1569             LV_FINDINFO findInfo
; 
1570             findInfo
.flags 
= LVFI_PARAM
; 
1571             findInfo
.lParam 
= (LPARAM
)wxPtrToUInt(m_internalData
[n
]); 
1573             int rc 
= ListView_FindItem(GetHwnd(), start
, &findInfo
); 
1576                 if ( idx 
== wxNOT_FOUND 
|| rc 
< idx 
) 
1579                     if ( idx 
== start 
+ 1 ) 
1581                         // we can stop here, we don't risk finding a closer 
1586                 //else: this item is after the previously found one 
1594 // Find an item nearest this position in the specified direction, starting from 
1595 // the item after 'start' or the beginning if 'start' is -1. 
1596 long wxListCtrl::FindItem(long start
, const wxPoint
& pt
, int direction
) 
1598     LV_FINDINFO findInfo
; 
1600     findInfo
.flags 
= LVFI_NEARESTXY
; 
1601     findInfo
.pt
.x 
= pt
.x
; 
1602     findInfo
.pt
.y 
= pt
.y
; 
1603     findInfo
.vkDirection 
= VK_RIGHT
; 
1605     if ( direction 
== wxLIST_FIND_UP 
) 
1606         findInfo
.vkDirection 
= VK_UP
; 
1607     else if ( direction 
== wxLIST_FIND_DOWN 
) 
1608         findInfo
.vkDirection 
= VK_DOWN
; 
1609     else if ( direction 
== wxLIST_FIND_LEFT 
) 
1610         findInfo
.vkDirection 
= VK_LEFT
; 
1611     else if ( direction 
== wxLIST_FIND_RIGHT 
) 
1612         findInfo
.vkDirection 
= VK_RIGHT
; 
1614     return ListView_FindItem(GetHwnd(), start
, &findInfo
); 
1617 // Determines which item (if any) is at the specified point, 
1618 // giving details in 'flags' (see wxLIST_HITTEST_... flags above) 
1620 wxListCtrl::HitTest(const wxPoint
& point
, int& flags
, long *ptrSubItem
) const 
1622     LV_HITTESTINFO hitTestInfo
; 
1623     hitTestInfo
.pt
.x 
= (int) point
.x
; 
1624     hitTestInfo
.pt
.y 
= (int) point
.y
; 
1627 #ifdef LVM_SUBITEMHITTEST 
1628     if ( ptrSubItem 
&& wxApp::GetComCtl32Version() >= 470 ) 
1630         item 
= ListView_SubItemHitTest(GetHwnd(), &hitTestInfo
); 
1631         *ptrSubItem 
= hitTestInfo
.iSubItem
; 
1634 #endif // LVM_SUBITEMHITTEST 
1636         item 
= ListView_HitTest(GetHwnd(), &hitTestInfo
); 
1641     if ( hitTestInfo
.flags 
& LVHT_ABOVE 
) 
1642         flags 
|= wxLIST_HITTEST_ABOVE
; 
1643     if ( hitTestInfo
.flags 
& LVHT_BELOW 
) 
1644         flags 
|= wxLIST_HITTEST_BELOW
; 
1645     if ( hitTestInfo
.flags 
& LVHT_TOLEFT 
) 
1646         flags 
|= wxLIST_HITTEST_TOLEFT
; 
1647     if ( hitTestInfo
.flags 
& LVHT_TORIGHT 
) 
1648         flags 
|= wxLIST_HITTEST_TORIGHT
; 
1650     if ( hitTestInfo
.flags 
& LVHT_NOWHERE 
) 
1651         flags 
|= wxLIST_HITTEST_NOWHERE
; 
1653     // note a bug or at least a very strange feature of comtl32.dll (tested 
1654     // with version 4.0 under Win95 and 6.0 under Win 2003): if you click to 
1655     // the right of the item label, ListView_HitTest() returns a combination of 
1656     // LVHT_ONITEMICON, LVHT_ONITEMLABEL and LVHT_ONITEMSTATEICON -- filter out 
1657     // the bits which don't make sense 
1658     if ( hitTestInfo
.flags 
& LVHT_ONITEMLABEL 
) 
1660         flags 
|= wxLIST_HITTEST_ONITEMLABEL
; 
1662         // do not translate LVHT_ONITEMICON here, as per above 
1666         if ( hitTestInfo
.flags 
& LVHT_ONITEMICON 
) 
1667             flags 
|= wxLIST_HITTEST_ONITEMICON
; 
1668         if ( hitTestInfo
.flags 
& LVHT_ONITEMSTATEICON 
) 
1669             flags 
|= wxLIST_HITTEST_ONITEMSTATEICON
; 
1676 // Inserts an item, returning the index of the new item if successful, 
1678 long wxListCtrl::InsertItem(const wxListItem
& info
) 
1680     wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual controls") ); 
1682     // In 2.8 it was possible to succeed inserting an item without initializing 
1683     // its ID as it defaulted to 0. This was however never supported and in 2.9 
1684     // the ID is -1 by default and inserting it simply fails, but it might be 
1685     // not obvious why does it happen, so check it proactively. 
1686     wxASSERT_MSG( info
.m_itemId 
!= -1, wxS("Item ID must be set.") ); 
1689     wxConvertToMSWListItem(this, info
, item
); 
1690     item
.mask 
&= ~LVIF_PARAM
; 
1692     // check whether we need to allocate our internal data 
1693     bool needInternalData 
= (info
.m_mask 
& wxLIST_MASK_DATA
) || 
1694                                 info
.HasAttributes(); 
1695     if ( needInternalData 
) 
1697         item
.mask 
|= LVIF_PARAM
; 
1699         wxMSWListItemData 
* const data 
= new wxMSWListItemData
; 
1700         m_internalData
.push_back(data
); 
1701         item
.lParam 
= (LPARAM
)data
; 
1703         if ( info
.m_mask 
& wxLIST_MASK_DATA 
) 
1704             data
->lParam 
= info
.m_data
; 
1706         // check whether it has any custom attributes 
1707         if ( info
.HasAttributes() ) 
1709             // take copy of attributes 
1710             data
->attr 
= new wxListItemAttr(*info
.GetAttributes()); 
1712             // and remember that we have some now... 
1713             m_hasAnyAttr 
= true; 
1717     const long rv 
= ListView_InsertItem(GetHwnd(), & item
); 
1719     // failing to insert the item is really unexpected 
1720     wxCHECK_MSG( rv 
!= -1, rv
, "failed to insert an item in wxListCtrl" ); 
1723     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
1724                   wxT("m_count should match ListView_GetItemCount")); 
1729 long wxListCtrl::InsertItem(long index
, const wxString
& label
) 
1732     info
.m_text 
= label
; 
1733     info
.m_mask 
= wxLIST_MASK_TEXT
; 
1734     info
.m_itemId 
= index
; 
1735     return InsertItem(info
); 
1738 // Inserts an image item 
1739 long wxListCtrl::InsertItem(long index
, int imageIndex
) 
1742     info
.m_image 
= imageIndex
; 
1743     info
.m_mask 
= wxLIST_MASK_IMAGE
; 
1744     info
.m_itemId 
= index
; 
1745     return InsertItem(info
); 
1748 // Inserts an image/string item 
1749 long wxListCtrl::InsertItem(long index
, const wxString
& label
, int imageIndex
) 
1752     info
.m_image 
= imageIndex
; 
1753     info
.m_text 
= label
; 
1754     info
.m_mask 
= wxLIST_MASK_TEXT
; 
1755     if (imageIndex 
> -1) 
1756         info
.m_mask 
|= wxLIST_MASK_IMAGE
; 
1757     info
.m_itemId 
= index
; 
1758     return InsertItem(info
); 
1761 // For list view mode (only), inserts a column. 
1762 long wxListCtrl::DoInsertColumn(long col
, const wxListItem
& item
) 
1765     wxConvertToMSWListCol(GetHwnd(), col
, item
, lvCol
); 
1767     // LVSCW_AUTOSIZE_USEHEADER is not supported when inserting new column, 
1768     // we'll deal with it below instead. Plain LVSCW_AUTOSIZE is not supported 
1769     // neither but it doesn't need any special handling as we use fixed value 
1770     // for it here, both because we can't do anything else (there are no items 
1771     // with values in this column to compute the size from yet) and for 
1772     // compatibility as wxLIST_AUTOSIZE == -1 and -1 as InsertColumn() width 
1773     // parameter used to mean "arbitrary fixed width". 
1774     if ( !(lvCol
.mask 
& LVCF_WIDTH
) || lvCol
.cx 
< 0 ) 
1776         // always give some width to the new column: this one is compatible 
1777         // with the generic version 
1778         lvCol
.mask 
|= LVCF_WIDTH
; 
1782     long n 
= ListView_InsertColumn(GetHwnd(), col
, &lvCol
); 
1785         wxLogDebug(wxT("Failed to insert the column '%s' into listview!"), 
1792     // Now adjust the new column size. 
1793     if ( (item
.GetMask() & wxLIST_MASK_WIDTH
) && 
1794             (item
.GetWidth() == wxLIST_AUTOSIZE_USEHEADER
) ) 
1796         SetColumnWidth(n
, wxLIST_AUTOSIZE_USEHEADER
); 
1802 // scroll the control by the given number of pixels (exception: in list view, 
1803 // dx is interpreted as number of columns) 
1804 bool wxListCtrl::ScrollList(int dx
, int dy
) 
1806     if ( !ListView_Scroll(GetHwnd(), dx
, dy
) ) 
1808         wxLogDebug(wxT("ListView_Scroll(%d, %d) failed"), dx
, dy
); 
1818 // fn is a function which takes 3 long arguments: item1, item2, data. 
1819 // item1 is the long data associated with a first item (NOT the index). 
1820 // item2 is the long data associated with a second item (NOT the index). 
1821 // data is the same value as passed to SortItems. 
1822 // The return value is a negative number if the first item should precede the second 
1823 // item, a positive number of the second item should precede the first, 
1824 // or zero if the two items are equivalent. 
1826 // data is arbitrary data to be passed to the sort function. 
1828 // Internal structures for proxying the user compare function 
1829 // so that we can pass it the *real* user data 
1831 // translate lParam data and call user func 
1832 struct wxInternalDataSort
 
1834     wxListCtrlCompare user_fn
; 
1838 int CALLBACK 
wxInternalDataCompareFunc(LPARAM lParam1
, LPARAM lParam2
,  LPARAM lParamSort
) 
1840     wxInternalDataSort 
* const internalData 
= (wxInternalDataSort 
*) lParamSort
; 
1842     wxMSWListItemData 
*data1 
= (wxMSWListItemData 
*) lParam1
; 
1843     wxMSWListItemData 
*data2 
= (wxMSWListItemData 
*) lParam2
; 
1845     wxIntPtr d1 
= (data1 
== NULL 
? 0 : data1
->lParam
); 
1846     wxIntPtr d2 
= (data2 
== NULL 
? 0 : data2
->lParam
); 
1848     return internalData
->user_fn(d1
, d2
, internalData
->data
); 
1852 bool wxListCtrl::SortItems(wxListCtrlCompare fn
, wxIntPtr data
) 
1854     wxInternalDataSort internalData
; 
1855     internalData
.user_fn 
= fn
; 
1856     internalData
.data 
= data
; 
1858     // WPARAM cast is needed for mingw/cygwin 
1859     if ( !ListView_SortItems(GetHwnd(), 
1860                              wxInternalDataCompareFunc
, 
1861                              (WPARAM
) &internalData
) ) 
1863         wxLogDebug(wxT("ListView_SortItems() failed")); 
1873 // ---------------------------------------------------------------------------- 
1874 // message processing 
1875 // ---------------------------------------------------------------------------- 
1877 bool wxListCtrl::MSWShouldPreProcessMessage(WXMSG
* msg
) 
1879     if ( msg
->message 
== WM_KEYDOWN 
) 
1881         // Only eat VK_RETURN if not being used by the application in 
1882         // conjunction with modifiers 
1883         if ( msg
->wParam 
== VK_RETURN 
&& !wxIsAnyModifierDown() ) 
1885             // we need VK_RETURN to generate wxEVT_COMMAND_LIST_ITEM_ACTIVATED 
1889     return wxListCtrlBase::MSWShouldPreProcessMessage(msg
); 
1892 bool wxListCtrl::MSWCommand(WXUINT cmd
, WXWORD id_
) 
1894     const int id 
= (signed short)id_
; 
1895     if (cmd 
== EN_UPDATE
) 
1897         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, id
); 
1898         event
.SetEventObject( this ); 
1899         ProcessCommand(event
); 
1902     else if (cmd 
== EN_KILLFOCUS
) 
1904         wxCommandEvent 
event(wxEVT_KILL_FOCUS
, id
); 
1905         event
.SetEventObject( this ); 
1906         ProcessCommand(event
); 
1913 // utility used by wxListCtrl::MSWOnNotify and by wxDataViewHeaderWindowMSW::MSWOnNotify 
1914 int WXDLLIMPEXP_CORE 
wxMSWGetColumnClicked(NMHDR 
*nmhdr
, POINT 
*ptClick
) 
1916     // find the column clicked: we have to search for it ourselves as the 
1917     // notification message doesn't provide this info 
1919     // where did the click occur? 
1920 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400 
1921     if ( nmhdr
->code 
== GN_CONTEXTMENU 
) 
1923         *ptClick 
= ((NMRGINFO
*)nmhdr
)->ptAction
; 
1926 #endif //__WXWINCE__ 
1928        wxGetCursorPosMSW(ptClick
); 
1931     // we need to use listctrl coordinates for the event point so this is what 
1932     // we return in ptClick, but for comparison with Header_GetItemRect() 
1933     // result below we need to use header window coordinates 
1934     POINT ptClickHeader 
= *ptClick
; 
1935     if ( !::ScreenToClient(nmhdr
->hwndFrom
, &ptClickHeader
) ) 
1937         wxLogLastError(wxT("ScreenToClient(listctrl header)")); 
1940     if ( !::ScreenToClient(::GetParent(nmhdr
->hwndFrom
), ptClick
) ) 
1942         wxLogLastError(wxT("ScreenToClient(listctrl)")); 
1945     const int colCount 
= Header_GetItemCount(nmhdr
->hwndFrom
); 
1946     for ( int col 
= 0; col 
< colCount
; col
++ ) 
1949         if ( Header_GetItemRect(nmhdr
->hwndFrom
, col
, &rect
) ) 
1951             if ( ::PtInRect(&rect
, ptClickHeader
) ) 
1961 bool wxListCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
1964     // prepare the event 
1965     // ----------------- 
1967     wxListEvent 
event(wxEVT_NULL
, m_windowId
); 
1968     event
.SetEventObject(this); 
1970     wxEventType eventType 
= wxEVT_NULL
; 
1972     NMHDR 
*nmhdr 
= (NMHDR 
*)lParam
; 
1974     // if your compiler is as broken as this, you should really change it: this 
1975     // code is needed for normal operation! #ifdef below is only useful for 
1976     // automatic rebuilds which are done with a very old compiler version 
1977 #ifdef HDN_BEGINTRACKA 
1979     // check for messages from the header (in report view) 
1980     HWND hwndHdr 
= ListView_GetHeader(GetHwnd()); 
1982     // is it a message from the header? 
1983     if ( nmhdr
->hwndFrom 
== hwndHdr 
) 
1985         HD_NOTIFY 
*nmHDR 
= (HD_NOTIFY 
*)nmhdr
; 
1987         event
.m_itemIndex 
= -1; 
1989         bool ignore 
= false; 
1990         switch ( nmhdr
->code 
) 
1992             // yet another comctl32.dll bug: under NT/W2K it sends Unicode 
1993             // TRACK messages even to ANSI programs: on my system I get 
1994             // HDN_BEGINTRACKW and HDN_ENDTRACKA! 
1996             // work around is to simply catch both versions and hope that it 
1997             // works (why should this message exist in ANSI and Unicode is 
1998             // beyond me as it doesn't deal with strings at all...) 
2000             // another problem is that HDN_TRACK is not sent at all by header 
2001             // with HDS_FULLDRAG style which is used by default by wxListCtrl 
2002             // under recent Windows versions (starting from at least XP) so we 
2003             // need to use HDN_ITEMCHANGING instead of it 
2004             case HDN_BEGINTRACKA
: 
2005             case HDN_BEGINTRACKW
: 
2006                 eventType 
= wxEVT_COMMAND_LIST_COL_BEGIN_DRAG
; 
2009             case HDN_ITEMCHANGING
: 
2010                 if ( eventType 
== wxEVT_NULL 
) 
2012                     if ( !nmHDR
->pitem 
|| !(nmHDR
->pitem
->mask 
& HDI_WIDTH
) ) 
2014                         // something other than the width is being changed, 
2020                     // also ignore the events sent when the width didn't really 
2021                     // change: this is not just an optimization but also gets 
2022                     // rid of a useless and unexpected DRAGGING event which 
2023                     // would otherwise be sent after the END_DRAG one as we get 
2024                     // an HDN_ITEMCHANGING after HDN_ENDTRACK for some reason 
2025                     if ( nmHDR
->pitem
->cxy 
== GetColumnWidth(nmHDR
->iItem
) ) 
2031                     eventType 
= wxEVT_COMMAND_LIST_COL_DRAGGING
; 
2037                 if ( eventType 
== wxEVT_NULL 
) 
2038                     eventType 
= wxEVT_COMMAND_LIST_COL_END_DRAG
; 
2040                 event
.m_item
.m_width 
= nmHDR
->pitem
->cxy
; 
2041                 event
.m_col 
= nmHDR
->iItem
; 
2044 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400 
2045             case GN_CONTEXTMENU
: 
2046 #endif //__WXWINCE__ 
2051                     eventType 
= wxEVT_COMMAND_LIST_COL_RIGHT_CLICK
; 
2052                     event
.m_col 
= wxMSWGetColumnClicked(nmhdr
, &ptClick
); 
2053                     event
.m_pointDrag
.x 
= ptClick
.x
; 
2054                     event
.m_pointDrag
.y 
= ptClick
.y
; 
2058             case HDN_GETDISPINFOW
: 
2059                 // letting Windows XP handle this message results in mysterious 
2060                 // crashes in comctl32.dll seemingly because of bad message 
2063                 // I have no idea what is the real cause of the bug (which is, 
2064                 // just to make things interesting, impossible to reproduce 
2065                 // reliably) but ignoring all these messages does fix it and 
2066                 // doesn't seem to have any negative consequences 
2074             return wxListCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
); 
2077 #endif // defined(HDN_BEGINTRACKA) 
2078     if ( nmhdr
->hwndFrom 
== GetHwnd() ) 
2080         // almost all messages use NM_LISTVIEW 
2081         NM_LISTVIEW 
*nmLV 
= (NM_LISTVIEW 
*)nmhdr
; 
2083         const int iItem 
= nmLV
->iItem
; 
2086         // If we have a valid item then check if there is a data value 
2087         // associated with it and put it in the event. 
2088         if ( iItem 
>= 0 && iItem 
< GetItemCount() ) 
2090             wxMSWListItemData 
*internaldata 
= 
2091                 MSWGetItemData(iItem
); 
2094                 event
.m_item
.m_data 
= internaldata
->lParam
; 
2097         bool processed 
= true; 
2098         switch ( nmhdr
->code 
) 
2100             case LVN_BEGINRDRAG
: 
2101                 eventType 
= wxEVT_COMMAND_LIST_BEGIN_RDRAG
; 
2105                 if ( eventType 
== wxEVT_NULL 
) 
2107                     eventType 
= wxEVT_COMMAND_LIST_BEGIN_DRAG
; 
2110                 event
.m_itemIndex 
= iItem
; 
2111                 event
.m_pointDrag
.x 
= nmLV
->ptAction
.x
; 
2112                 event
.m_pointDrag
.y 
= nmLV
->ptAction
.y
; 
2115             // NB: we have to handle both *A and *W versions here because some 
2116             //     versions of comctl32.dll send ANSI messages even to the 
2118             case LVN_BEGINLABELEDITA
: 
2119             case LVN_BEGINLABELEDITW
: 
2122                     if ( nmhdr
->code 
== LVN_BEGINLABELEDITA 
) 
2124                         item
.Init(((LV_DISPINFOA 
*)lParam
)->item
); 
2126                     else // LVN_BEGINLABELEDITW 
2128                         item
.Init(((LV_DISPINFOW 
*)lParam
)->item
); 
2131                     eventType 
= wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT
; 
2132                     wxConvertFromMSWListItem(GetHwnd(), event
.m_item
, item
); 
2133                     event
.m_itemIndex 
= event
.m_item
.m_itemId
; 
2137             case LVN_ENDLABELEDITA
: 
2138             case LVN_ENDLABELEDITW
: 
2141                     if ( nmhdr
->code 
== LVN_ENDLABELEDITA 
) 
2143                         item
.Init(((LV_DISPINFOA 
*)lParam
)->item
); 
2145                     else // LVN_ENDLABELEDITW 
2147                         item
.Init(((LV_DISPINFOW 
*)lParam
)->item
); 
2150                     // was editing cancelled? 
2151                     const LV_ITEM
& lvi 
= (LV_ITEM
)item
; 
2152                     if ( !lvi
.pszText 
|| lvi
.iItem 
== -1 ) 
2154                         // EDIT control will be deleted by the list control 
2155                         // itself so prevent us from deleting it as well 
2156                         DeleteEditControl(); 
2158                         event
.SetEditCanceled(true); 
2161                     eventType 
= wxEVT_COMMAND_LIST_END_LABEL_EDIT
; 
2162                     wxConvertFromMSWListItem(NULL
, event
.m_item
, item
); 
2163                     event
.m_itemIndex 
= event
.m_item
.m_itemId
; 
2167             case LVN_COLUMNCLICK
: 
2168                 eventType 
= wxEVT_COMMAND_LIST_COL_CLICK
; 
2169                 event
.m_itemIndex 
= -1; 
2170                 event
.m_col 
= nmLV
->iSubItem
; 
2173             case LVN_DELETEALLITEMS
: 
2174                 eventType 
= wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS
; 
2175                 event
.m_itemIndex 
= -1; 
2178             case LVN_DELETEITEM
: 
2181                     // this should be prevented by the post-processing code 
2182                     // below, but "just in case" 
2186                 eventType 
= wxEVT_COMMAND_LIST_DELETE_ITEM
; 
2187                 event
.m_itemIndex 
= iItem
; 
2191             case LVN_INSERTITEM
: 
2192                 eventType 
= wxEVT_COMMAND_LIST_INSERT_ITEM
; 
2193                 event
.m_itemIndex 
= iItem
; 
2196             case LVN_ITEMCHANGED
: 
2197                 // we translate this catch all message into more interesting 
2198                 // (and more easy to process) wxWidgets events 
2200                 // first of all, we deal with the state change events only and 
2201                 // only for valid items (item == -1 for the virtual list 
2203                 if ( nmLV
->uChanged 
& LVIF_STATE 
&& iItem 
!= -1 ) 
2205                     // temp vars for readability 
2206                     const UINT stOld 
= nmLV
->uOldState
; 
2207                     const UINT stNew 
= nmLV
->uNewState
; 
2209                     event
.m_item
.SetId(iItem
); 
2210                     event
.m_item
.SetMask(wxLIST_MASK_TEXT 
| 
2213                     GetItem(event
.m_item
); 
2215                     // has the focus changed? 
2216                     if ( !(stOld 
& LVIS_FOCUSED
) && (stNew 
& LVIS_FOCUSED
) ) 
2218                         eventType 
= wxEVT_COMMAND_LIST_ITEM_FOCUSED
; 
2219                         event
.m_itemIndex 
= iItem
; 
2222                     if ( (stNew 
& LVIS_SELECTED
) != (stOld 
& LVIS_SELECTED
) ) 
2224                         if ( eventType 
!= wxEVT_NULL 
) 
2226                             // focus and selection have both changed: send the 
2227                             // focus event from here and the selection one 
2229                             event
.SetEventType(eventType
); 
2230                             (void)HandleWindowEvent(event
); 
2232                         else // no focus event to send 
2234                             // then need to set m_itemIndex as it wasn't done 
2236                             event
.m_itemIndex 
= iItem
; 
2239                         eventType 
= stNew 
& LVIS_SELECTED
 
2240                                         ? wxEVT_COMMAND_LIST_ITEM_SELECTED
 
2241                                         : wxEVT_COMMAND_LIST_ITEM_DESELECTED
; 
2245                 if ( eventType 
== wxEVT_NULL 
) 
2247                     // not an interesting event for us 
2255                     LV_KEYDOWN 
*info 
= (LV_KEYDOWN 
*)lParam
; 
2256                     WORD wVKey 
= info
->wVKey
; 
2258                     // get the current selection 
2259                     long lItem 
= GetNextItem(-1, 
2261                                              wxLIST_STATE_SELECTED
); 
2263                     // <Enter> or <Space> activate the selected item if any (but 
2264                     // not with any modifiers as they have a predefined meaning 
2267                          (wVKey 
== VK_RETURN 
|| wVKey 
== VK_SPACE
) && 
2268                          !wxIsAnyModifierDown() ) 
2270                         eventType 
= wxEVT_COMMAND_LIST_ITEM_ACTIVATED
; 
2274                         eventType 
= wxEVT_COMMAND_LIST_KEY_DOWN
; 
2276                         event
.m_code 
= wxMSWKeyboard::VKToWX(wVKey
); 
2278                         if ( event
.m_code 
== WXK_NONE 
) 
2280                             // We can't translate this to a standard key code, 
2281                             // until support for Unicode key codes is added to 
2282                             // wxListEvent we just ignore them. 
2288                     event
.m_item
.m_itemId 
= lItem
; 
2292                         // fill the other fields too 
2293                         event
.m_item
.m_text 
= GetItemText(lItem
); 
2294                         event
.m_item
.m_data 
= GetItemData(lItem
); 
2300                 // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do 
2302                 if ( wxListCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
) ) 
2307                 // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event 
2308                 // if it happened on an item (and not on empty place) 
2315                 eventType 
= wxEVT_COMMAND_LIST_ITEM_ACTIVATED
; 
2316                 event
.m_itemIndex 
= iItem
; 
2317                 event
.m_item
.m_text 
= GetItemText(iItem
); 
2318                 event
.m_item
.m_data 
= GetItemData(iItem
); 
2321 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400 
2322             case GN_CONTEXTMENU
: 
2323 #endif //__WXWINCE__ 
2325                 // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(), 
2326                 // don't do anything else 
2327                 if ( wxListCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
) ) 
2332                 // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event 
2333                 LV_HITTESTINFO lvhti
; 
2334                 wxZeroMemory(lvhti
); 
2336 #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400 
2337                 if ( nmhdr
->code 
== GN_CONTEXTMENU 
) 
2339                     lvhti
.pt 
= ((NMRGINFO
*)nmhdr
)->ptAction
; 
2342 #endif //__WXWINCE__ 
2344                     wxGetCursorPosMSW(&(lvhti
.pt
)); 
2347                 ::ScreenToClient(GetHwnd(), &lvhti
.pt
); 
2348                 if ( ListView_HitTest(GetHwnd(), &lvhti
) != -1 ) 
2350                     if ( lvhti
.flags 
& LVHT_ONITEM 
) 
2352                         eventType 
= wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK
; 
2353                         event
.m_itemIndex 
= lvhti
.iItem
; 
2354                         event
.m_pointDrag
.x 
= lvhti
.pt
.x
; 
2355                         event
.m_pointDrag
.y 
= lvhti
.pt
.y
; 
2360 #ifdef NM_CUSTOMDRAW 
2362                 *result 
= OnCustomDraw(lParam
); 
2364                 return *result 
!= CDRF_DODEFAULT
; 
2365 #endif // _WIN32_IE >= 0x300 
2367             case LVN_ODCACHEHINT
: 
2369                     const NM_CACHEHINT 
*cacheHint 
= (NM_CACHEHINT 
*)lParam
; 
2371                     eventType 
= wxEVT_COMMAND_LIST_CACHE_HINT
; 
2373                     // we get some really stupid cache hints like ones for 
2374                     // items in range 0..0 for an empty control or, after 
2375                     // deleting an item, for items in invalid range -- filter 
2377                     if ( cacheHint
->iFrom 
> cacheHint
->iTo 
) 
2380                     event
.m_oldItemIndex 
= cacheHint
->iFrom
; 
2382                     const long iMax 
= GetItemCount(); 
2383                     event
.m_itemIndex 
= cacheHint
->iTo 
< iMax 
? cacheHint
->iTo
 
2388 #ifdef HAVE_NMLVFINDITEM 
2389             case LVN_ODFINDITEM
: 
2390                 // Find an item in a (necessarily virtual) list control. 
2393                     NMLVFINDITEM
* pFindInfo 
= (NMLVFINDITEM
*)lParam
; 
2395                     // no match by default 
2398                     // we only handle string-based searches here 
2400                     // TODO: what about LVFI_PARTIAL, should we handle this? 
2401                     if ( !(pFindInfo
->lvfi
.flags 
& LVFI_STRING
) ) 
2406                     const wxChar 
* const searchstr 
= pFindInfo
->lvfi
.psz
; 
2407                     const size_t len 
= wxStrlen(searchstr
); 
2409                     // this is the first item we should examine, search from it 
2410                     // wrapping if necessary 
2411                     int startPos 
= pFindInfo
->iStart
; 
2412                     const int maxPos 
= GetItemCount(); 
2414                     // Check that the index is valid to ensure that our loop 
2415                     // below always terminates. 
2416                     if ( startPos 
< 0 || startPos 
>= maxPos 
) 
2418                         // When the last item in the control is selected, 
2419                         // iStart is really set to (invalid) maxPos index so 
2420                         // accept this silently. 
2421                         if ( startPos 
!= maxPos 
) 
2423                             wxLogDebug(wxT("Ignoring invalid search start ") 
2424                                        wxT("position %d in list control with ") 
2425                                        wxT("%d items."), startPos
, maxPos
); 
2431                     // Linear search in a control with a lot of items can take 
2432                     // a long time so we limit the total time of the search to 
2433                     // ensure that the program doesn't appear to hang. 
2436 #endif // wxUSE_STOPWATCH 
2437                     for ( int currentPos 
= startPos
; ; ) 
2439                         // does this item begin with searchstr? 
2440                         if ( wxStrnicmp(searchstr
, 
2441                                             GetItemText(currentPos
), len
) == 0 ) 
2443                             *result 
= currentPos
; 
2447                         // Go to next item with wrapping if necessary. 
2448                         if ( ++currentPos 
== maxPos 
) 
2450                             // Surprisingly, LVFI_WRAP seems to be never set in 
2451                             // the flags so wrap regardless of it. 
2455                         if ( currentPos 
== startPos 
) 
2457                             // We examined all items without finding anything. 
2459                             // Notice that we still return true as we did 
2460                             // perform the search, if we didn't do this the 
2461                             // message would have been considered unhandled and 
2462                             // the control seems to always select the first 
2463                             // item by default in this case. 
2468                         // Check the time elapsed only every thousand 
2469                         // iterations for performance reasons: if we did it 
2470                         // more often calling wxStopWatch::Time() could take 
2471                         // noticeable time on its own. 
2472                         if ( !((currentPos 
- startPos
)%1000
) ) 
2474                             // We use half a second to limit the search time 
2475                             // which is about as long as we can take without 
2476                             // annoying the user. 
2477                             if ( sw
.Time() > 500 ) 
2479                                 // As above, return true to prevent the control 
2480                                 // from selecting the first item by default. 
2484 #endif // wxUSE_STOPWATCH 
2488                     SetItemState(*result
, 
2489                                  wxLIST_STATE_SELECTED 
| wxLIST_STATE_FOCUSED
, 
2490                                  wxLIST_STATE_SELECTED 
| wxLIST_STATE_FOCUSED
); 
2491                     EnsureVisible(*result
); 
2499 #endif // HAVE_NMLVFINDITEM 
2501             case LVN_GETDISPINFO
: 
2504                     LV_DISPINFO 
*info 
= (LV_DISPINFO 
*)lParam
; 
2506                     LV_ITEM
& lvi 
= info
->item
; 
2507                     long item 
= lvi
.iItem
; 
2509                     if ( lvi
.mask 
& LVIF_TEXT 
) 
2511                         wxString text 
= OnGetItemText(item
, lvi
.iSubItem
); 
2512                         wxStrlcpy(lvi
.pszText
, text
.c_str(), lvi
.cchTextMax
); 
2515                     // see comment at the end of wxListCtrl::GetColumn() 
2516 #ifdef NM_CUSTOMDRAW 
2517                     if ( lvi
.mask 
& LVIF_IMAGE 
) 
2519                         lvi
.iImage 
= OnGetItemColumnImage(item
, lvi
.iSubItem
); 
2521 #endif // NM_CUSTOMDRAW 
2523                     // even though we never use LVM_SETCALLBACKMASK, we still 
2524                     // can get messages with LVIF_STATE in lvi.mask under Vista 
2525                     if ( lvi
.mask 
& LVIF_STATE 
) 
2527                         // we don't have anything to return from here... 
2540             return wxListCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
); 
2544         // where did this one come from? 
2548     // process the event 
2549     // ----------------- 
2551     event
.SetEventType(eventType
); 
2553     // fill in the item before passing it to the event handler if we do have a 
2554     // valid item index and haven't filled it yet (e.g. for LVN_ITEMCHANGED) 
2555     // and we're not using a virtual control as in this case the program 
2556     // already has the data anyhow and we don't want to call GetItem() for 
2557     // potentially many items 
2558     if ( event
.m_itemIndex 
!= -1 && !event
.m_item
.GetMask() 
2561         wxListItem
& item 
= event
.m_item
; 
2563         item
.SetId(event
.m_itemIndex
); 
2564         item
.SetMask(wxLIST_MASK_TEXT 
| wxLIST_MASK_IMAGE 
| wxLIST_MASK_DATA
); 
2568     bool processed 
= HandleWindowEvent(event
); 
2572     switch ( nmhdr
->code 
) 
2574         case LVN_DELETEALLITEMS
: 
2575             // always return true to suppress all additional LVN_DELETEITEM 
2576             // notifications - this makes deleting all items from a list ctrl 
2580             // also, we may free all user data now (couldn't do it before as 
2581             // the user should have access to it in OnDeleteAllItems() handler) 
2582             FreeAllInternalData(); 
2584             // the control is empty now, synchronize the cached number of items 
2585             // with the real one 
2589         case LVN_DELETEITEM
: 
2590             // Delete the associated internal data. Notice that this can be 
2591             // done only after the event has been handled as the data could be 
2592             // accessed during the handling of the event. 
2593             if ( wxMSWListItemData 
*data 
= MSWGetItemData(event
.m_itemIndex
) ) 
2595                 const unsigned count 
= m_internalData
.size(); 
2596                 for ( unsigned n 
= 0; n 
< count
; n
++ ) 
2598                     if ( m_internalData
[n
] == data 
) 
2600                         m_internalData
.erase(m_internalData
.begin() + n
); 
2606                 wxASSERT_MSG( !data
, "invalid internal data pointer?" ); 
2610         case LVN_ENDLABELEDITA
: 
2611         case LVN_ENDLABELEDITW
: 
2612             // logic here is inverted compared to all the other messages 
2613             *result 
= event
.IsAllowed(); 
2615             // EDIT control will be deleted by the list control itself so 
2616             // prevent us from deleting it as well 
2617             DeleteEditControl(); 
2623         *result 
= !event
.IsAllowed(); 
2628 // ---------------------------------------------------------------------------- 
2629 // custom draw stuff 
2630 // ---------------------------------------------------------------------------- 
2632 // see comment at the end of wxListCtrl::GetColumn() 
2633 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
2635 static RECT 
GetCustomDrawnItemRect(const NMCUSTOMDRAW
& nmcd
) 
2638     wxGetListCtrlItemRect(nmcd
.hdr
.hwndFrom
, nmcd
.dwItemSpec
, LVIR_BOUNDS
, rc
); 
2641     wxGetListCtrlItemRect(nmcd
.hdr
.hwndFrom
, nmcd
.dwItemSpec
, LVIR_ICON
, rcIcon
); 
2643     // exclude the icon part, neither the selection background nor focus rect 
2645     rc
.left 
= rcIcon
.right
; 
2651 bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD
, HFONT hfont
, int colCount
) 
2653     NMCUSTOMDRAW
& nmcd 
= pLVCD
->nmcd
; 
2656     HWND hwndList 
= nmcd
.hdr
.hwndFrom
; 
2657     const int col 
= pLVCD
->iSubItem
; 
2658     const DWORD item 
= nmcd
.dwItemSpec
; 
2660     // the font must be valid, otherwise we wouldn't be painting the item at all 
2661     SelectInHDC 
selFont(hdc
, hfont
); 
2663     // get the rectangle to paint 
2664     int subitem 
= colCount 
? col 
+ 1 : col
; 
2666     wxGetListCtrlSubItemRect(hwndList
, item
, subitem
, LVIR_BOUNDS
, rc
); 
2669     // get the image and text to draw 
2673     it
.mask 
= LVIF_TEXT 
| LVIF_IMAGE
; 
2677     it
.cchTextMax 
= WXSIZEOF(text
); 
2678     ListView_GetItem(hwndList
, &it
); 
2680     HIMAGELIST himl 
= ListView_GetImageList(hwndList
, LVSIL_SMALL
); 
2681     if ( himl 
&& ImageList_GetImageCount(himl
) ) 
2683         if ( it
.iImage 
!= -1 ) 
2685             ImageList_Draw(himl
, it
.iImage
, hdc
, rc
.left
, rc
.top
, 
2686                            nmcd
.uItemState 
& CDIS_SELECTED 
? ILD_SELECTED
 
2690         // notice that even if this item doesn't have any image, the list 
2691         // control still leaves space for the image in the first column if the 
2692         // image list is not empty (presumably so that items with and without 
2694         if ( it
.iImage 
!= -1 || it
.iSubItem 
== 0 ) 
2697             ImageList_GetIconSize(himl
, &wImage
, &hImage
); 
2699             rc
.left 
+= wImage 
+ 2; 
2703     ::SetBkMode(hdc
, TRANSPARENT
); 
2705     UINT fmt 
= DT_SINGLELINE 
| 
2708 #endif // __WXWINCE__ 
2713     wxZeroMemory(lvCol
); 
2714     lvCol
.mask 
= LVCF_FMT
; 
2715     if ( ListView_GetColumn(hwndList
, col
, &lvCol
) ) 
2717         switch ( lvCol
.fmt 
& LVCFMT_JUSTIFYMASK 
) 
2732     //else: failed to get alignment, assume it's DT_LEFT (default) 
2734     DrawText(hdc
, text
, -1, &rc
, fmt
); 
2739 static void HandleItemPostpaint(NMCUSTOMDRAW nmcd
) 
2741     if ( nmcd
.uItemState 
& CDIS_FOCUS 
) 
2743         RECT rc 
= GetCustomDrawnItemRect(nmcd
); 
2745         // don't use the provided HDC, it's in some strange state by now 
2746         ::DrawFocusRect(WindowHDC(nmcd
.hdr
.hwndFrom
), &rc
); 
2750 // pLVCD->clrText and clrTextBk should contain the colours to use 
2751 static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD
, HFONT hfont
) 
2753     NMCUSTOMDRAW
& nmcd 
= pLVCD
->nmcd
; // just a shortcut 
2755     const HWND hwndList 
= nmcd
.hdr
.hwndFrom
; 
2756     const int item 
= nmcd
.dwItemSpec
; 
2758     // unfortunately we can't trust CDIS_SELECTED, it is often set even when 
2759     // the item is not at all selected for some reason (comctl32 6), but we 
2760     // also can't always trust ListView_GetItem() as it could return the old 
2761     // item status if we're called just after the (de)selection, so remember 
2762     // the last item to gain selection and also check for it here 
2763     for ( int i 
= -1;; ) 
2765         i 
= ListView_GetNextItem(hwndList
, i
, LVNI_SELECTED
); 
2768             nmcd
.uItemState 
&= ~CDIS_SELECTED
; 
2774             nmcd
.uItemState 
|= CDIS_SELECTED
; 
2779     // same thing for CDIS_FOCUS (except simpler as there is only one of them) 
2781     // NB: cast is needed to work around the bug in mingw32 headers which don't 
2782     //     have it inside ListView_GetNextItem() itself (unlike SDK ones) 
2783     if ( ::GetFocus() == hwndList 
&& 
2784             ListView_GetNextItem( 
2785                 hwndList
, static_cast<WPARAM
>(-1), LVNI_FOCUSED
) == item 
) 
2787         nmcd
.uItemState 
|= CDIS_FOCUS
; 
2791         nmcd
.uItemState 
&= ~CDIS_FOCUS
; 
2794     if ( nmcd
.uItemState 
& CDIS_SELECTED 
) 
2796         int syscolFg
, syscolBg
; 
2797         if ( ::GetFocus() == hwndList 
) 
2799             syscolFg 
= COLOR_HIGHLIGHTTEXT
; 
2800             syscolBg 
= COLOR_HIGHLIGHT
; 
2802         else // selected but unfocused 
2804             syscolFg 
= COLOR_WINDOWTEXT
; 
2805             syscolBg 
= COLOR_BTNFACE
; 
2807             // don't grey out the icon in this case neither 
2808             nmcd
.uItemState 
&= ~CDIS_SELECTED
; 
2811         pLVCD
->clrText 
= ::GetSysColor(syscolFg
); 
2812         pLVCD
->clrTextBk 
= ::GetSysColor(syscolBg
); 
2814     //else: not selected, use normal colours from pLVCD 
2817     RECT rc 
= GetCustomDrawnItemRect(nmcd
); 
2819     ::SetTextColor(hdc
, pLVCD
->clrText
); 
2820     ::FillRect(hdc
, &rc
, AutoHBRUSH(pLVCD
->clrTextBk
)); 
2822     // we could use CDRF_NOTIFYSUBITEMDRAW here but it results in weird repaint 
2823     // problems so just draw everything except the focus rect from here instead 
2824     const int colCount 
= Header_GetItemCount(ListView_GetHeader(hwndList
)); 
2825     for ( int col 
= 0; col 
< colCount
; col
++ ) 
2827         pLVCD
->iSubItem 
= col
; 
2828         HandleSubItemPrepaint(pLVCD
, hfont
, colCount
); 
2831     HandleItemPostpaint(nmcd
); 
2834 static WXLPARAM 
HandleItemPrepaint(wxListCtrl 
*listctrl
, 
2835                                    LPNMLVCUSTOMDRAW pLVCD
, 
2836                                    wxListItemAttr 
*attr
) 
2840         // nothing to do for this item 
2841         return CDRF_DODEFAULT
; 
2845     // set the colours to use for text drawing 
2846     pLVCD
->clrText 
= attr
->HasTextColour() 
2847                      ? wxColourToRGB(attr
->GetTextColour()) 
2848                      : wxColourToRGB(listctrl
->GetTextColour()); 
2849     pLVCD
->clrTextBk 
= attr
->HasBackgroundColour() 
2850                        ? wxColourToRGB(attr
->GetBackgroundColour()) 
2851                        : wxColourToRGB(listctrl
->GetBackgroundColour()); 
2853     // select the font if non default one is specified 
2854     if ( attr
->HasFont() ) 
2856         wxFont font 
= attr
->GetFont(); 
2857         if ( font
.GetEncoding() != wxFONTENCODING_SYSTEM 
) 
2859             // the standard control ignores the font encoding/charset, at least 
2860             // with recent comctl32.dll versions (5 and 6, it uses to work with 
2861             // 4.something) so we have to draw the item entirely ourselves in 
2863             HandleItemPaint(pLVCD
, GetHfontOf(font
)); 
2864             return CDRF_SKIPDEFAULT
; 
2867         ::SelectObject(pLVCD
->nmcd
.hdc
, GetHfontOf(font
)); 
2869         return CDRF_NEWFONT
; 
2872     return CDRF_DODEFAULT
; 
2875 WXLPARAM 
wxListCtrl::OnCustomDraw(WXLPARAM lParam
) 
2877     LPNMLVCUSTOMDRAW pLVCD 
= (LPNMLVCUSTOMDRAW
)lParam
; 
2878     NMCUSTOMDRAW
& nmcd 
= pLVCD
->nmcd
; 
2879     switch ( nmcd
.dwDrawStage 
) 
2882             // if we've got any items with non standard attributes, 
2883             // notify us before painting each item 
2885             // for virtual controls, always suppose that we have attributes as 
2886             // there is no way to check for this 
2887             if ( IsVirtual() || m_hasAnyAttr 
) 
2888                 return CDRF_NOTIFYITEMDRAW
; 
2891         case CDDS_ITEMPREPAINT
: 
2892             // get a message for each subitem 
2893             return CDRF_NOTIFYITEMDRAW
; 
2895         case CDDS_SUBITEM 
| CDDS_ITEMPREPAINT
: 
2896             const int item 
= nmcd
.dwItemSpec
; 
2897             const int column 
= pLVCD
->iSubItem
; 
2899             // we get this message with item == 0 for an empty control, we 
2900             // must ignore it as calling OnGetItemAttr() would be wrong 
2901             if ( item 
< 0 || item 
>= GetItemCount() ) 
2904             if ( column 
< 0 || column 
>= GetColumnCount() ) 
2907             return HandleItemPrepaint(this, pLVCD
, DoGetItemColumnAttr(item
, column
)); 
2910     return CDRF_DODEFAULT
; 
2913 #endif // NM_CUSTOMDRAW supported 
2915 // Necessary for drawing hrules and vrules, if specified 
2916 void wxListCtrl::OnPaint(wxPaintEvent
& event
) 
2918     const int itemCount 
= GetItemCount(); 
2919     const bool drawHRules 
= HasFlag(wxLC_HRULES
); 
2920     const bool drawVRules 
= HasFlag(wxLC_VRULES
); 
2922     if (!InReportView() || !(drawHRules 
|| drawVRules
) || !itemCount
) 
2930     wxListCtrlBase::OnPaint(event
); 
2932     // Reset the device origin since it may have been set 
2933     dc
.SetDeviceOrigin(0, 0); 
2935     wxPen 
pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
)); 
2937     dc
.SetBrush(* wxTRANSPARENT_BRUSH
); 
2939     wxSize clientSize 
= GetClientSize(); 
2944         const long top 
= GetTopItem(); 
2945         for ( int i 
= top
; i 
< top 
+ GetCountPerPage() + 1; i
++ ) 
2947             if (GetItemRect(i
, itemRect
)) 
2949                 int cy 
= itemRect
.GetTop(); 
2950                 if (i 
!= 0) // Don't draw the first one 
2952                     dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
2955                 if (i 
== itemCount 
- 1) 
2957                     cy 
= itemRect
.GetBottom(); 
2958                     dc
.DrawLine(0, cy
, clientSize
.x
, cy
); 
2967         wxRect firstItemRect
; 
2968         GetItemRect(0, firstItemRect
); 
2970         if (GetItemRect(itemCount 
- 1, itemRect
)) 
2972             // this is a fix for bug 673394: erase the pixels which we would 
2973             // otherwise leave on the screen 
2974             static const int gap 
= 2; 
2975             dc
.SetPen(*wxTRANSPARENT_PEN
); 
2976             dc
.SetBrush(wxBrush(GetBackgroundColour())); 
2977             dc
.DrawRectangle(0, firstItemRect
.GetY() - gap
, 
2978                              clientSize
.GetWidth(), gap
); 
2981             dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
2983             const int numCols 
= GetColumnCount(); 
2984             wxVector
<int> indexArray(numCols
); 
2985             if ( !ListView_GetColumnOrderArray(GetHwnd(), 
2989                 wxFAIL_MSG( wxT("invalid column index array in OnPaint()") ); 
2993             int x 
= itemRect
.GetX(); 
2994             for (int col 
= 0; col 
< numCols
; col
++) 
2996                 int colWidth 
= GetColumnWidth(indexArray
[col
]); 
2998                 dc
.DrawLine(x
-1, firstItemRect
.GetY() - gap
, 
2999                             x
-1, itemRect
.GetBottom()); 
3005 void wxListCtrl::OnCharHook(wxKeyEvent
& event
) 
3007     if ( GetEditControl() ) 
3009         // We need to ensure that Escape is not stolen from the in-place editor 
3010         // by the containing dialog. 
3012         // Notice that we don't have to care about Enter key here as we return 
3013         // false from MSWShouldPreProcessMessage() for it. 
3014         if ( event
.GetKeyCode() == WXK_ESCAPE 
) 
3016             EndEditLabel(true /* cancel */); 
3018             // Don't call Skip() below. 
3027 wxListCtrl::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
3033             // we should bypass our own WM_PRINT handling as we don't handle 
3034             // PRF_CHILDREN flag, so leave it to the native control itself 
3035             return MSWDefWindowProc(nMsg
, wParam
, lParam
); 
3038         case WM_CONTEXTMENU
: 
3039             // because this message is propagated upwards the child-parent 
3040             // chain, we get it for the right clicks on the header window but 
3041             // this is confusing in wx as right clicking there already 
3042             // generates a separate wxEVT_COMMAND_LIST_COL_RIGHT_CLICK event 
3043             // so just ignore them 
3044             if ( (HWND
)wParam 
== ListView_GetHeader(GetHwnd()) ) 
3049     return wxListCtrlBase::MSWWindowProc(nMsg
, wParam
, lParam
); 
3052 // ---------------------------------------------------------------------------- 
3053 // virtual list controls 
3054 // ---------------------------------------------------------------------------- 
3056 wxString 
wxListCtrl::OnGetItemText(long WXUNUSED(item
), long WXUNUSED(col
)) const 
3058     // this is a pure virtual function, in fact - which is not really pure 
3059     // because the controls which are not virtual don't need to implement it 
3060     wxFAIL_MSG( wxT("wxListCtrl::OnGetItemText not supposed to be called") ); 
3062     return wxEmptyString
; 
3065 int wxListCtrl::OnGetItemImage(long WXUNUSED(item
)) const 
3067     wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL
), 
3069                 wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden.")); 
3073 int wxListCtrl::OnGetItemColumnImage(long item
, long column
) const 
3076         return OnGetItemImage(item
); 
3081 wxListItemAttr 
*wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item
)) const 
3083     wxASSERT_MSG( item 
>= 0 && item 
< GetItemCount(), 
3084                   wxT("invalid item index in OnGetItemAttr()") ); 
3086     // no attributes by default 
3090 wxListItemAttr 
*wxListCtrl::DoGetItemColumnAttr(long item
, long column
) const 
3093         return OnGetItemColumnAttr(item
, column
); 
3095     wxMSWListItemData 
* const data 
= MSWGetItemData(item
); 
3096     return data 
? data
->attr 
: NULL
; 
3099 void wxListCtrl::SetItemCount(long count
) 
3101     wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") ); 
3103     if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT
, (WPARAM
)count
, 
3104                         LVSICF_NOSCROLL 
| LVSICF_NOINVALIDATEALL
) ) 
3106         wxLogLastError(wxT("ListView_SetItemCount")); 
3109     wxASSERT_MSG( m_count 
== ListView_GetItemCount(GetHwnd()), 
3110                   wxT("m_count should match ListView_GetItemCount")); 
3113 void wxListCtrl::RefreshItem(long item
) 
3115     RefreshItems(item
, item
); 
3118 void wxListCtrl::RefreshItems(long itemFrom
, long itemTo
) 
3120     ListView_RedrawItems(GetHwnd(), itemFrom
, itemTo
); 
3123 // ---------------------------------------------------------------------------- 
3124 // wxWin <-> MSW items conversions 
3125 // ---------------------------------------------------------------------------- 
3127 static void wxConvertFromMSWListItem(HWND hwndListCtrl
, 
3131     wxMSWListItemData 
*internaldata 
= 
3132         (wxMSWListItemData 
*) lvItem
.lParam
; 
3135         info
.m_data 
= internaldata
->lParam
; 
3139     info
.m_stateMask 
= 0; 
3140     info
.m_itemId 
= lvItem
.iItem
; 
3142     long oldMask 
= lvItem
.mask
; 
3144     bool needText 
= false; 
3145     if (hwndListCtrl 
!= 0) 
3147         if ( lvItem
.mask 
& LVIF_TEXT 
) 
3154             lvItem
.pszText 
= new wxChar
[513]; 
3155             lvItem
.cchTextMax 
= 512; 
3157         lvItem
.mask 
|= LVIF_TEXT 
| LVIF_IMAGE 
| LVIF_PARAM
; 
3158         ::SendMessage(hwndListCtrl
, LVM_GETITEM
, 0, (LPARAM
)& lvItem
); 
3161     if ( lvItem
.mask 
& LVIF_STATE 
) 
3163         info
.m_mask 
|= wxLIST_MASK_STATE
; 
3165         if ( lvItem
.stateMask 
& LVIS_CUT
) 
3167             info
.m_stateMask 
|= wxLIST_STATE_CUT
; 
3168             if ( lvItem
.state 
& LVIS_CUT 
) 
3169                 info
.m_state 
|= wxLIST_STATE_CUT
; 
3171         if ( lvItem
.stateMask 
& LVIS_DROPHILITED
) 
3173             info
.m_stateMask 
|= wxLIST_STATE_DROPHILITED
; 
3174             if ( lvItem
.state 
& LVIS_DROPHILITED 
) 
3175                 info
.m_state 
|= wxLIST_STATE_DROPHILITED
; 
3177         if ( lvItem
.stateMask 
& LVIS_FOCUSED
) 
3179             info
.m_stateMask 
|= wxLIST_STATE_FOCUSED
; 
3180             if ( lvItem
.state 
& LVIS_FOCUSED 
) 
3181                 info
.m_state 
|= wxLIST_STATE_FOCUSED
; 
3183         if ( lvItem
.stateMask 
& LVIS_SELECTED
) 
3185             info
.m_stateMask 
|= wxLIST_STATE_SELECTED
; 
3186             if ( lvItem
.state 
& LVIS_SELECTED 
) 
3187                 info
.m_state 
|= wxLIST_STATE_SELECTED
; 
3191     if ( lvItem
.mask 
& LVIF_TEXT 
) 
3193         info
.m_mask 
|= wxLIST_MASK_TEXT
; 
3194         info
.m_text 
= lvItem
.pszText
; 
3196     if ( lvItem
.mask 
& LVIF_IMAGE 
) 
3198         info
.m_mask 
|= wxLIST_MASK_IMAGE
; 
3199         info
.m_image 
= lvItem
.iImage
; 
3201     if ( lvItem
.mask 
& LVIF_PARAM 
) 
3202         info
.m_mask 
|= wxLIST_MASK_DATA
; 
3203     if ( lvItem
.mask 
& LVIF_DI_SETITEM 
) 
3204         info
.m_mask 
|= wxLIST_SET_ITEM
; 
3205     info
.m_col 
= lvItem
.iSubItem
; 
3210             delete[] lvItem
.pszText
; 
3212     lvItem
.mask 
= oldMask
; 
3215 static void wxConvertToMSWFlags(long state
, long stateMask
, LV_ITEM
& lvItem
) 
3217     if (stateMask 
& wxLIST_STATE_CUT
) 
3219         lvItem
.stateMask 
|= LVIS_CUT
; 
3220         if (state 
& wxLIST_STATE_CUT
) 
3221             lvItem
.state 
|= LVIS_CUT
; 
3223     if (stateMask 
& wxLIST_STATE_DROPHILITED
) 
3225         lvItem
.stateMask 
|= LVIS_DROPHILITED
; 
3226         if (state 
& wxLIST_STATE_DROPHILITED
) 
3227             lvItem
.state 
|= LVIS_DROPHILITED
; 
3229     if (stateMask 
& wxLIST_STATE_FOCUSED
) 
3231         lvItem
.stateMask 
|= LVIS_FOCUSED
; 
3232         if (state 
& wxLIST_STATE_FOCUSED
) 
3233             lvItem
.state 
|= LVIS_FOCUSED
; 
3235     if (stateMask 
& wxLIST_STATE_SELECTED
) 
3237         lvItem
.stateMask 
|= LVIS_SELECTED
; 
3238         if (state 
& wxLIST_STATE_SELECTED
) 
3239             lvItem
.state 
|= LVIS_SELECTED
; 
3243 static void wxConvertToMSWListItem(const wxListCtrl 
*ctrl
, 
3244                                    const wxListItem
& info
, 
3247     if ( ctrl
->InReportView() ) 
3249         wxASSERT_MSG( 0 <= info
.m_col 
&& info
.m_col 
< ctrl
->GetColumnCount(), 
3250                       "wxListCtrl column index out of bounds" ); 
3252     else // not in report view 
3254         wxASSERT_MSG( info
.m_col 
== 0, "columns only exist in report view" ); 
3257     lvItem
.iItem 
= (int) info
.m_itemId
; 
3259     lvItem
.iImage 
= info
.m_image
; 
3260     lvItem
.stateMask 
= 0; 
3263     lvItem
.iSubItem 
= info
.m_col
; 
3265     if (info
.m_mask 
& wxLIST_MASK_STATE
) 
3267         lvItem
.mask 
|= LVIF_STATE
; 
3269         wxConvertToMSWFlags(info
.m_state
, info
.m_stateMask
, lvItem
); 
3272     if (info
.m_mask 
& wxLIST_MASK_TEXT
) 
3274         lvItem
.mask 
|= LVIF_TEXT
; 
3275         if ( ctrl
->HasFlag(wxLC_USER_TEXT
) ) 
3277             lvItem
.pszText 
= LPSTR_TEXTCALLBACK
; 
3281             // pszText is not const, hence the cast 
3282             lvItem
.pszText 
= (wxChar 
*)info
.m_text
.wx_str(); 
3283             if ( lvItem
.pszText 
) 
3284                 lvItem
.cchTextMax 
= info
.m_text
.length(); 
3286                 lvItem
.cchTextMax 
= 0; 
3289     if (info
.m_mask 
& wxLIST_MASK_IMAGE
) 
3290         lvItem
.mask 
|= LVIF_IMAGE
; 
3293 static void wxConvertToMSWListCol(HWND hwndList
, 
3295                                   const wxListItem
& item
, 
3298     wxZeroMemory(lvCol
); 
3300     if ( item
.m_mask 
& wxLIST_MASK_TEXT 
) 
3302         lvCol
.mask 
|= LVCF_TEXT
; 
3303         lvCol
.pszText 
= (wxChar 
*)item
.m_text
.wx_str(); // cast is safe 
3306     if ( item
.m_mask 
& wxLIST_MASK_FORMAT 
) 
3308         lvCol
.mask 
|= LVCF_FMT
; 
3310         if ( item
.m_format 
== wxLIST_FORMAT_LEFT 
) 
3311             lvCol
.fmt 
= LVCFMT_LEFT
; 
3312         else if ( item
.m_format 
== wxLIST_FORMAT_RIGHT 
) 
3313             lvCol
.fmt 
= LVCFMT_RIGHT
; 
3314         else if ( item
.m_format 
== wxLIST_FORMAT_CENTRE 
) 
3315             lvCol
.fmt 
= LVCFMT_CENTER
; 
3318     if ( item
.m_mask 
& wxLIST_MASK_WIDTH 
) 
3320         lvCol
.mask 
|= LVCF_WIDTH
; 
3321         if ( item
.m_width 
== wxLIST_AUTOSIZE
) 
3322             lvCol
.cx 
= LVSCW_AUTOSIZE
; 
3323         else if ( item
.m_width 
== wxLIST_AUTOSIZE_USEHEADER
) 
3324             lvCol
.cx 
= LVSCW_AUTOSIZE_USEHEADER
; 
3326             lvCol
.cx 
= item
.m_width
; 
3329     // see comment at the end of wxListCtrl::GetColumn() 
3330 #ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300 
3331     if ( item
.m_mask 
& wxLIST_MASK_IMAGE 
) 
3333         if ( wxApp::GetComCtl32Version() >= 470 ) 
3335             lvCol
.mask 
|= LVCF_IMAGE
; 
3337             // we use LVCFMT_BITMAP_ON_RIGHT because the images on the right 
3338             // seem to be generally nicer than on the left and the generic 
3339             // version only draws them on the right (we don't have a flag to 
3340             // specify the image location anyhow) 
3342             // we don't use LVCFMT_COL_HAS_IMAGES because it doesn't seem to 
3343             // make any difference in my tests -- but maybe we should? 
3344             if ( item
.m_image 
!= -1 ) 
3346                 // as we're going to overwrite the format field, get its 
3347                 // current value first -- unless we want to overwrite it anyhow 
3348                 if ( !(lvCol
.mask 
& LVCF_FMT
) ) 
3351                     wxZeroMemory(lvColOld
); 
3352                     lvColOld
.mask 
= LVCF_FMT
; 
3353                     if ( ListView_GetColumn(hwndList
, col
, &lvColOld
) ) 
3355                         lvCol
.fmt 
= lvColOld
.fmt
; 
3358                     lvCol
.mask 
|= LVCF_FMT
; 
3361                 lvCol
.fmt 
|= LVCFMT_BITMAP_ON_RIGHT 
| LVCFMT_IMAGE
; 
3364             lvCol
.iImage 
= item
.m_image
; 
3366         //else: it doesn't support item images anyhow 
3368 #endif // _WIN32_IE >= 0x0300 
3371 #endif // wxUSE_LISTCTRL