1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/msw/listctrl.cpp
3// Purpose:     wxListCtrl
4// Author:      Julian Smart
5// Modified by: Agron Selimaj
6// Created:     04/01/98
7// RCS-ID:      $Id: listctrl.cpp 57021 2008-11-29 13:43:32Z VZ $
8// Copyright:   (c) Julian Smart
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24    #pragma hdrstop
25#endif
26
27#if wxUSE_LISTCTRL
28
29#include "wx/listctrl.h"
30
31#ifndef WX_PRECOMP
32    #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
33    #include "wx/app.h"
34    #include "wx/intl.h"
35    #include "wx/log.h"
36    #include "wx/settings.h"
37    #include "wx/dcclient.h"
38    #include "wx/textctrl.h"
39#endif
40
41#include "wx/imaglist.h"
42
43#include "wx/msw/private.h"
44
45#if defined(__WXWINCE__) && !defined(__HANDHELDPC__)
46  #include <ole2.h>
47  #include <shellapi.h>
48  #if _WIN32_WCE < 400
49    #include <aygshell.h>
50  #endif
51#endif
52
53// Currently gcc and watcom don't define NMLVFINDITEM, and DMC only defines
54// it by its old name NM_FINDTIEM.
55//
56#if defined(__VISUALC__) || defined(__BORLANDC__) || defined(NMLVFINDITEM)
57    #define HAVE_NMLVFINDITEM 1
58#elif defined(__DMC__) || defined(NM_FINDITEM)
59    #define HAVE_NMLVFINDITEM 1
60    #define NMLVFINDITEM NM_FINDITEM
61#endif
62
63// ----------------------------------------------------------------------------
64// private functions
65// ----------------------------------------------------------------------------
66
67// convert our state and mask flags to LV_ITEM constants
68static void wxConvertToMSWFlags(long state, long mask, LV_ITEM& lvItem);
69
70// convert wxListItem to LV_ITEM
71static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
72                                   const wxListItem& info, LV_ITEM& lvItem);
73
74// convert LV_ITEM to wxListItem
75static void wxConvertFromMSWListItem(HWND hwndListCtrl,
76                                     wxListItem& info,
77                                     /* const */ LV_ITEM& lvItem);
78
79// convert our wxListItem to LV_COLUMN
80static void wxConvertToMSWListCol(HWND hwndList,
81                                  int col,
82                                  const wxListItem& item,
83                                  LV_COLUMN& lvCol);
84
85// ----------------------------------------------------------------------------
86// private helper classes
87// ----------------------------------------------------------------------------
88
89// We have to handle both fooW and fooA notifications in several cases
90// because of broken comctl32.dll and/or unicows.dll. This class is used to
91// convert LV_ITEMA and LV_ITEMW to LV_ITEM (which is either LV_ITEMA or
92// LV_ITEMW depending on wxUSE_UNICODE setting), so that it can be processed
93// by wxConvertToMSWListItem().
94#if wxUSE_UNICODE
95    #define LV_ITEM_NATIVE  LV_ITEMW
96    #define LV_ITEM_OTHER   LV_ITEMA
97
98    #define LV_CONV_TO_WX   cMB2WX
99    #define LV_CONV_BUF     wxMB2WXbuf
100#else // ANSI
101    #define LV_ITEM_NATIVE  LV_ITEMA
102    #define LV_ITEM_OTHER   LV_ITEMW
103
104    #define LV_CONV_TO_WX   cWC2WX
105    #define LV_CONV_BUF     wxWC2WXbuf
106#endif // Unicode/ANSI
107
108class wxLV_ITEM
109{
110public:
111    // default ctor, use Init() later
112    wxLV_ITEM() { m_buf = NULL; m_pItem = NULL; }
113
114    // init without conversion
115    void Init(LV_ITEM_NATIVE& item)
116    {
117        wxASSERT_MSG( !m_pItem, _T("Init() called twice?") );
118
119        m_pItem = &item;
120    }
121
122    // init with conversion
123    void Init(const LV_ITEM_OTHER& item)
124    {
125        // avoid unnecessary dynamic memory allocation, jjust make m_pItem
126        // point to our own m_item
127
128        // memcpy() can't work if the struct sizes are different
129        wxCOMPILE_TIME_ASSERT( sizeof(LV_ITEM_OTHER) == sizeof(LV_ITEM_NATIVE),
130                               CodeCantWorkIfDiffSizes);
131
132        memcpy(&m_item, &item, sizeof(LV_ITEM_NATIVE));
133
134        // convert text from ANSI to Unicod if necessary
135        if ( (item.mask & LVIF_TEXT) && item.pszText )
136        {
137            m_buf = new LV_CONV_BUF(wxConvLocal.LV_CONV_TO_WX(item.pszText));
138            m_item.pszText = (wxChar *)m_buf->data();
139        }
140    }
141
142    // ctor without conversion
143    wxLV_ITEM(LV_ITEM_NATIVE& item) : m_buf(NULL), m_pItem(&item) { }
144
145    // ctor with conversion
146    wxLV_ITEM(LV_ITEM_OTHER& item) : m_buf(NULL)
147    {
148        Init(item);
149    }
150
151    ~wxLV_ITEM() { delete m_buf; }
152
153    // conversion to the real LV_ITEM
154    operator LV_ITEM_NATIVE&() const { return *m_pItem; }
155
156private:
157    LV_CONV_BUF *m_buf;
158
159    LV_ITEM_NATIVE *m_pItem;
160    LV_ITEM_NATIVE m_item;
161
162    DECLARE_NO_COPY_CLASS(wxLV_ITEM)
163};
164
165///////////////////////////////////////////////////////
166// Problem:
167// The MSW version had problems with SetTextColour() et
168// al as the wxListItemAttr's were stored keyed on the
169// item index. If a item was inserted anywhere but the end
170// of the list the the text attributes (colour etc) for
171// the following items were out of sync.
172//
173// Solution:
174// Under MSW the only way to associate data with a List
175// item independent of its position in the list is to
176// store a pointer to it in its lParam attribute. However
177// user programs are already using this (via the
178// SetItemData() GetItemData() calls).
179//
180// However what we can do is store a pointer to a
181// structure which contains the attributes we want *and*
182// a lParam for the users data, e.g.
183//
184// class wxListItemInternalData
185// {
186// public:
187//   wxListItemAttr *attr;
188//   long lParam; // user data
189// };
190//
191// To conserve memory, a wxListItemInternalData is
192// only allocated for a LV_ITEM if text attributes or
193// user data(lparam) are being set.
194
195
196// class wxListItemInternalData
197class wxListItemInternalData
198{
199public:
200   wxListItemAttr *attr;
201   LPARAM lParam; // user data
202
203   wxListItemInternalData() : attr(NULL), lParam(0) {}
204   ~wxListItemInternalData()
205   {
206       if (attr)
207           delete attr;
208   }
209
210    DECLARE_NO_COPY_CLASS(wxListItemInternalData)
211};
212
213// Get the internal data structure
214static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId);
215static wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
216static wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId);
217static void wxDeleteInternalData(wxListCtrl* ctl, long itemId);
218
219
220#if wxUSE_EXTENDED_RTTI
221WX_DEFINE_FLAGS( wxListCtrlStyle )
222
223wxBEGIN_FLAGS( wxListCtrlStyle )
224    // new style border flags, we put them first to
225    // use them for streaming out
226    wxFLAGS_MEMBER(wxBORDER_SIMPLE)
227    wxFLAGS_MEMBER(wxBORDER_SUNKEN)
228    wxFLAGS_MEMBER(wxBORDER_DOUBLE)
229    wxFLAGS_MEMBER(wxBORDER_RAISED)
230    wxFLAGS_MEMBER(wxBORDER_STATIC)
231    wxFLAGS_MEMBER(wxBORDER_NONE)
232
233    // old style border flags
234    wxFLAGS_MEMBER(wxSIMPLE_BORDER)
235    wxFLAGS_MEMBER(wxSUNKEN_BORDER)
236    wxFLAGS_MEMBER(wxDOUBLE_BORDER)
237    wxFLAGS_MEMBER(wxRAISED_BORDER)
238    wxFLAGS_MEMBER(wxSTATIC_BORDER)
239    wxFLAGS_MEMBER(wxBORDER)
240
241    // standard window styles
242    wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
243    wxFLAGS_MEMBER(wxCLIP_CHILDREN)
244    wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
245    wxFLAGS_MEMBER(wxWANTS_CHARS)
246    wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
247    wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
248    wxFLAGS_MEMBER(wxVSCROLL)
249    wxFLAGS_MEMBER(wxHSCROLL)
250
251    wxFLAGS_MEMBER(wxLC_LIST)
252    wxFLAGS_MEMBER(wxLC_REPORT)
253    wxFLAGS_MEMBER(wxLC_ICON)
254    wxFLAGS_MEMBER(wxLC_SMALL_ICON)
255    wxFLAGS_MEMBER(wxLC_ALIGN_TOP)
256    wxFLAGS_MEMBER(wxLC_ALIGN_LEFT)
257    wxFLAGS_MEMBER(wxLC_AUTOARRANGE)
258    wxFLAGS_MEMBER(wxLC_USER_TEXT)
259    wxFLAGS_MEMBER(wxLC_EDIT_LABELS)
260    wxFLAGS_MEMBER(wxLC_NO_HEADER)
261    wxFLAGS_MEMBER(wxLC_SINGLE_SEL)
262    wxFLAGS_MEMBER(wxLC_SORT_ASCENDING)
263    wxFLAGS_MEMBER(wxLC_SORT_DESCENDING)
264    wxFLAGS_MEMBER(wxLC_VIRTUAL)
265
266wxEND_FLAGS( wxListCtrlStyle )
267
268IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h")
269
270wxBEGIN_PROPERTIES_TABLE(wxListCtrl)
271    wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
272
273    wxPROPERTY_FLAGS( WindowStyle , wxListCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
274wxEND_PROPERTIES_TABLE()
275
276wxBEGIN_HANDLERS_TABLE(wxListCtrl)
277wxEND_HANDLERS_TABLE()
278
279wxCONSTRUCTOR_5( wxListCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
280
281/*
282 TODO : Expose more information of a list's layout etc. via appropriate objects (a la NotebookPageInfo)
283*/
284#else
285IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
286#endif
287
288IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
289IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
290
291IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
292
293BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
294    EVT_PAINT(wxListCtrl::OnPaint)
295END_EVENT_TABLE()
296
297// ============================================================================
298// implementation
299// ============================================================================
300
301// ----------------------------------------------------------------------------
302// wxListCtrl construction
303// ----------------------------------------------------------------------------
304
305void wxListCtrl::Init()
306{
307    m_imageListNormal = NULL;
308    m_imageListSmall = NULL;
309    m_imageListState = NULL;
310    m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = false;
311    m_colCount = 0;
312    m_count = 0;
313    m_ignoreChangeMessages = false;
314    m_textCtrl = NULL;
315    m_AnyInternalData = false;
316    m_hasAnyAttr = false;
317}
318
319bool wxListCtrl::Create(wxWindow *parent,
320                        wxWindowID id,
321                        const wxPoint& pos,
322                        const wxSize& size,
323                        long style,
324                        const wxValidator& validator,
325                        const wxString& name)
326{
327    if ( !CreateControl(parent, id, pos, size, style, validator, name) )
328        return false;
329
330    if ( !MSWCreateControl(WC_LISTVIEW, wxEmptyString, pos, size) )
331        return false;
332
333    // explicitly say that we want to use Unicode because otherwise we get ANSI
334    // versions of _some_ messages (notably LVN_GETDISPINFOA) in MSLU build
335    wxSetCCUnicodeFormat(GetHwnd());
336
337    // We must set the default text colour to the system/theme color, otherwise
338    // GetTextColour will always return black
339    SetTextColour(GetDefaultAttributes().colFg);
340
341    if ( InReportView() )
342        MSWSetExListStyles();
343
344    return true;
345}
346
347void wxListCtrl::MSWSetExListStyles()
348{
349    // for comctl32.dll v 4.70+ we want to have some non default extended
350    // styles because it's prettier (and also because wxGTK does it like this)
351    if ( wxApp::GetComCtl32Version() >= 470 )
352    {
353        ::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE,
354                      0, LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES);
355    }
356}
357
358WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
359{
360    WXDWORD wstyle = wxControl::MSWGetStyle(style, exstyle);
361
362    wstyle |= LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
363
364#ifdef __WXDEBUG__
365    size_t nModes = 0;
366
367    #define MAP_MODE_STYLE(wx, ms)                                            \
368        if ( style & (wx) ) { wstyle |= (ms); nModes++; }
369#else // !__WXDEBUG__
370    #define MAP_MODE_STYLE(wx, ms)                                            \
371        if ( style & (wx) ) wstyle |= (ms);
372#endif // __WXDEBUG__
373
374    MAP_MODE_STYLE(wxLC_ICON, LVS_ICON)
375    MAP_MODE_STYLE(wxLC_SMALL_ICON, LVS_SMALLICON)
376    MAP_MODE_STYLE(wxLC_LIST, LVS_LIST)
377    MAP_MODE_STYLE(wxLC_REPORT, LVS_REPORT)
378
379    wxASSERT_MSG( nModes == 1,
380                  _T("wxListCtrl style should have exactly one mode bit set") );
381
382#undef MAP_MODE_STYLE
383
384    if ( style & wxLC_ALIGN_LEFT )
385        wstyle |= LVS_ALIGNLEFT;
386
387    if ( style & wxLC_ALIGN_TOP )
388        wstyle |= LVS_ALIGNTOP;
389
390    if ( style & wxLC_AUTOARRANGE )
391        wstyle |= LVS_AUTOARRANGE;
392
393    if ( style & wxLC_NO_SORT_HEADER )
394        wstyle |= LVS_NOSORTHEADER;
395
396    if ( style & wxLC_NO_HEADER )
397        wstyle |= LVS_NOCOLUMNHEADER;
398
399    if ( style & wxLC_EDIT_LABELS )
400        wstyle |= LVS_EDITLABELS;
401
402    if ( style & wxLC_SINGLE_SEL )
403        wstyle |= LVS_SINGLESEL;
404
405    if ( style & wxLC_SORT_ASCENDING )
406    {
407        wstyle |= LVS_SORTASCENDING;
408
409        wxASSERT_MSG( !(style & wxLC_SORT_DESCENDING),
410                      _T("can't sort in ascending and descending orders at once") );
411    }
412    else if ( style & wxLC_SORT_DESCENDING )
413        wstyle |= LVS_SORTDESCENDING;
414
415#if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
416    if ( style & wxLC_VIRTUAL )
417    {
418        int ver = wxApp::GetComCtl32Version();
419        if ( ver < 470 )
420        {
421            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."),
422                        ver / 100, ver % 100);
423        }
424
425        wstyle |= LVS_OWNERDATA;
426    }
427#endif // ancient cygwin
428
429    return wstyle;
430}
431
432void wxListCtrl::UpdateStyle()
433{
434    if ( GetHwnd() )
435    {
436        // The new window view style
437        DWORD dwStyleNew = MSWGetStyle(m_windowStyle, NULL);
438
439        // some styles are not returned by MSWGetStyle()
440        if ( IsShown() )
441            dwStyleNew |= WS_VISIBLE;
442
443        // Get the current window style.
444        DWORD dwStyleOld = ::GetWindowLong(GetHwnd(), GWL_STYLE);
445
446        // we don't have wxVSCROLL style, but the list control may have it,
447        // don't change it then
448        dwStyleNew |= dwStyleOld & (WS_HSCROLL | WS_VSCROLL);
449
450        // Only set the window style if the view bits have changed.
451        if ( dwStyleOld != dwStyleNew )
452        {
453            ::SetWindowLong(GetHwnd(), GWL_STYLE, dwStyleNew);
454
455            // if we switched to the report view, set the extended styles for
456            // it too
457            if ( !(dwStyleOld & LVS_REPORT) && (dwStyleNew & LVS_REPORT) )
458                MSWSetExListStyles();
459        }
460    }
461}
462
463void wxListCtrl::FreeAllInternalData()
464{
465    if (m_AnyInternalData)
466    {
467        int n = GetItemCount();
468
469        m_ignoreChangeMessages = true;
470        for (int i = 0; i < n; i++)
471            wxDeleteInternalData(this, i);
472        m_ignoreChangeMessages = false;
473
474        m_AnyInternalData = false;
475    }
476}
477
478void wxListCtrl::DeleteEditControl()
479{
480    if ( m_textCtrl )
481    {
482        m_textCtrl->UnsubclassWin();
483        m_textCtrl->SetHWND(0);
484        delete m_textCtrl;
485        m_textCtrl = NULL;
486    }
487}
488
489wxListCtrl::~wxListCtrl()
490{
491    FreeAllInternalData();
492
493    DeleteEditControl();
494
495    if (m_ownsImageListNormal)
496        delete m_imageListNormal;
497    if (m_ownsImageListSmall)
498        delete m_imageListSmall;
499    if (m_ownsImageListState)
500        delete m_imageListState;
501}
502
503// ----------------------------------------------------------------------------
504// set/get/change style
505// ----------------------------------------------------------------------------
506
507// Add or remove a single window style
508void wxListCtrl::SetSingleStyle(long style, bool add)
509{
510    long flag = GetWindowStyleFlag();
511
512    // Get rid of conflicting styles
513    if ( add )
514    {
515        if ( style & wxLC_MASK_TYPE)
516            flag = flag & ~wxLC_MASK_TYPE;
517        if ( style & wxLC_MASK_ALIGN )
518            flag = flag & ~wxLC_MASK_ALIGN;
519        if ( style & wxLC_MASK_SORT )
520            flag = flag & ~wxLC_MASK_SORT;
521    }
522
523    if ( add )
524        flag |= style;
525    else
526        flag &= ~style;
527
528    SetWindowStyleFlag(flag);
529}
530
531// Set the whole window style
532void wxListCtrl::SetWindowStyleFlag(long flag)
533{
534    if ( flag != m_windowStyle )
535    {
536        wxControl::SetWindowStyleFlag(flag);
537
538        UpdateStyle();
539
540        Refresh();
541    }
542}
543
544// ----------------------------------------------------------------------------
545// accessors
546// ----------------------------------------------------------------------------
547
548/* static */ wxVisualAttributes
549wxListCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
550{
551    wxVisualAttributes attrs = GetCompositeControlsDefaultAttributes(variant);
552
553    // common controls have their own default font
554    attrs.font = wxGetCCDefaultFont();
555
556    return attrs;
557}
558
559// Sets the foreground, i.e. text, colour
560bool wxListCtrl::SetForegroundColour(const wxColour& col)
561{
562    if ( !wxWindow::SetForegroundColour(col) )
563        return false;
564
565    ListView_SetTextColor(GetHwnd(), wxColourToRGB(col));
566
567    return true;
568}
569
570// Sets the background colour
571bool wxListCtrl::SetBackgroundColour(const wxColour& col)
572{
573    if ( !wxWindow::SetBackgroundColour(col) )
574        return false;
575
576    // we set the same colour for both the "empty" background and the items
577    // background
578    COLORREF color = wxColourToRGB(col);
579    ListView_SetBkColor(GetHwnd(), color);
580    ListView_SetTextBkColor(GetHwnd(), color);
581
582    return true;
583}
584
585// Gets information about this column
586bool wxListCtrl::GetColumn(int col, wxListItem& item) const
587{
588    LV_COLUMN lvCol;
589    wxZeroMemory(lvCol);
590
591    lvCol.mask = LVCF_WIDTH;
592
593    if ( item.m_mask & wxLIST_MASK_TEXT )
594    {
595        lvCol.mask |= LVCF_TEXT;
596        lvCol.pszText = new wxChar[513];
597        lvCol.cchTextMax = 512;
598    }
599
600    if ( item.m_mask & wxLIST_MASK_FORMAT )
601    {
602        lvCol.mask |= LVCF_FMT;
603    }
604
605    if ( item.m_mask & wxLIST_MASK_IMAGE )
606    {
607        lvCol.mask |= LVCF_IMAGE;
608    }
609
610    bool success = ListView_GetColumn(GetHwnd(), col, &lvCol) != 0;
611
612    //  item.m_subItem = lvCol.iSubItem;
613    item.m_width = lvCol.cx;
614
615    if ( (item.m_mask & wxLIST_MASK_TEXT) && lvCol.pszText )
616    {
617        item.m_text = lvCol.pszText;
618        delete[] lvCol.pszText;
619    }
620
621    if ( item.m_mask & wxLIST_MASK_FORMAT )
622    {
623        switch (lvCol.fmt & LVCFMT_JUSTIFYMASK) {
624            case LVCFMT_LEFT:
625                item.m_format = wxLIST_FORMAT_LEFT;
626                break;
627            case LVCFMT_RIGHT:
628                item.m_format = wxLIST_FORMAT_RIGHT;
629                break;
630            case LVCFMT_CENTER:
631                item.m_format = wxLIST_FORMAT_CENTRE;
632                break;
633            default:
634                item.m_format = -1;  // Unknown?
635                break;
636        }
637    }
638
639    // the column images were not supported in older versions but how to check
640    // for this? we can't use _WIN32_IE because we always define it to a very
641    // high value, so see if another symbol which is only defined starting from
642    // comctl32.dll 4.70 is available
643#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
644    if ( item.m_mask & wxLIST_MASK_IMAGE )
645    {
646        item.m_image = lvCol.iImage;
647    }
648#endif // LVCOLUMN::iImage exists
649
650    return success;
651}
652
653// Sets information about this column
654bool wxListCtrl::SetColumn(int col, const wxListItem& item)
655{
656    LV_COLUMN lvCol;
657    wxConvertToMSWListCol(GetHwnd(), col, item, lvCol);
658
659    return ListView_SetColumn(GetHwnd(), col, &lvCol) != 0;
660}
661
662// Gets the column width
663int wxListCtrl::GetColumnWidth(int col) const
664{
665    return ListView_GetColumnWidth(GetHwnd(), col);
666}
667
668// Sets the column width
669bool wxListCtrl::SetColumnWidth(int col, int width)
670{
671    if ( m_windowStyle & wxLC_LIST )
672        col = 0;
673
674    if ( width == wxLIST_AUTOSIZE)
675        width = LVSCW_AUTOSIZE;
676    else if ( width == wxLIST_AUTOSIZE_USEHEADER)
677        width = LVSCW_AUTOSIZE_USEHEADER;
678
679    return ListView_SetColumnWidth(GetHwnd(), col, width) != 0;
680}
681
682// Gets the number of items that can fit vertically in the
683// visible area of the list control (list or report view)
684// or the total number of items in the list control (icon
685// or small icon view)
686int wxListCtrl::GetCountPerPage() const
687{
688    return ListView_GetCountPerPage(GetHwnd());
689}
690
691// Gets the edit control for editing labels.
692wxTextCtrl* wxListCtrl::GetEditControl() const
693{
694    // first check corresponds to the case when the label editing was started
695    // by user and hence m_textCtrl wasn't created by EditLabel() at all, while
696    // the second case corresponds to us being called from inside EditLabel()
697    // (e.g. from a user wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT handler): in this
698    // case EditLabel() did create the control but it didn't have an HWND to
699    // initialize it with yet
700    if ( !m_textCtrl || !m_textCtrl->GetHWND() )
701    {
702        HWND hwndEdit = ListView_GetEditControl(GetHwnd());
703        if ( hwndEdit )
704        {
705            wxListCtrl * const self = wx_const_cast(wxListCtrl *, this);
706
707            if ( !m_textCtrl )
708                self->m_textCtrl = new wxTextCtrl;
709            self->InitEditControl((WXHWND)hwndEdit);
710        }
711    }
712
713    return m_textCtrl;
714}
715
716// Gets information about the item
717bool wxListCtrl::GetItem(wxListItem& info) const
718{
719    LV_ITEM lvItem;
720    wxZeroMemory(lvItem);
721
722    lvItem.iItem = info.m_itemId;
723    lvItem.iSubItem = info.m_col;
724
725    if ( info.m_mask & wxLIST_MASK_TEXT )
726    {
727        lvItem.mask |= LVIF_TEXT;
728        lvItem.pszText = new wxChar[513];
729        lvItem.cchTextMax = 512;
730    }
731    else
732    {
733        lvItem.pszText = NULL;
734    }
735
736    if (info.m_mask & wxLIST_MASK_DATA)
737        lvItem.mask |= LVIF_PARAM;
738
739    if (info.m_mask & wxLIST_MASK_IMAGE)
740        lvItem.mask |= LVIF_IMAGE;
741
742    if ( info.m_mask & wxLIST_MASK_STATE )
743    {
744        lvItem.mask |= LVIF_STATE;
745        wxConvertToMSWFlags(0, info.m_stateMask, lvItem);
746    }
747
748    bool success = ListView_GetItem((HWND)GetHWND(), &lvItem) != 0;
749    if ( !success )
750    {
751        wxLogError(_("Couldn't retrieve information about list control item %d."),
752                lvItem.iItem);
753    }
754    else
755    {
756        // give NULL as hwnd as we already have everything we need
757        wxConvertFromMSWListItem(NULL, info, lvItem);
758    }
759
760    if (lvItem.pszText)
761        delete[] lvItem.pszText;
762
763    return success;
764}
765
766// Sets information about the item
767bool wxListCtrl::SetItem(wxListItem& info)
768{
769    const long id = info.GetId();
770    wxCHECK_MSG( id >= 0 && id < GetItemCount(), false,
771                 _T("invalid item index in SetItem") );
772
773    LV_ITEM item;
774    wxConvertToMSWListItem(this, info, item);
775
776    // we never update the lParam if it contains our pointer
777    // to the wxListItemInternalData structure
778    item.mask &= ~LVIF_PARAM;
779
780    // check if setting attributes or lParam
781    if (info.HasAttributes() || (info.m_mask  & wxLIST_MASK_DATA))
782    {
783        // get internal item data
784        // perhaps a cache here ?
785        wxListItemInternalData *data = wxGetInternalData(this, id);
786
787        if (! data)
788        {
789            // need to set it
790            m_AnyInternalData = true;
791            data = new wxListItemInternalData();
792            item.lParam = (LPARAM) data;
793            item.mask |= LVIF_PARAM;
794        };
795
796
797        // user data
798        if (info.m_mask  & wxLIST_MASK_DATA)
799            data->lParam = info.m_data;
800
801        // attributes
802        if ( info.HasAttributes() )
803        {
804            const wxListItemAttr& attrNew = *info.GetAttributes();
805
806            // don't overwrite the already set attributes if we have them
807            if ( data->attr )
808                data->attr->AssignFrom(attrNew);
809            else
810                data->attr = new wxListItemAttr(attrNew);
811        };
812    };
813
814
815    // we could be changing only the attribute in which case we don't need to
816    // call ListView_SetItem() at all
817    if ( item.mask )
818    {
819        item.cchTextMax = 0;
820        if ( !ListView_SetItem(GetHwnd(), &item) )
821        {
822            wxLogDebug(_T("ListView_SetItem() failed"));
823
824            return false;
825        }
826    }
827
828    // we need to update the item immediately to show the new image
829    bool updateNow = (info.m_mask & wxLIST_MASK_IMAGE) != 0;
830
831    // check whether it has any custom attributes
832    if ( info.HasAttributes() )
833    {
834        m_hasAnyAttr = true;
835
836        // if the colour has changed, we must redraw the item
837        updateNow = true;
838    }
839
840    if ( updateNow )
841    {
842        // we need this to make the change visible right now
843        RefreshItem(item.iItem);
844    }
845
846    return true;
847}
848
849long wxListCtrl::SetItem(long index, int col, const wxString& label, int imageId)
850{
851    wxListItem info;
852    info.m_text = label;
853    info.m_mask = wxLIST_MASK_TEXT;
854    info.m_itemId = index;
855    info.m_col = col;
856    if ( imageId > -1 )
857    {
858        info.m_image = imageId;
859        info.m_mask |= wxLIST_MASK_IMAGE;
860    }
861    return SetItem(info);
862}
863
864
865// Gets the item state
866int wxListCtrl::GetItemState(long item, long stateMask) const
867{
868    wxListItem info;
869
870    info.m_mask = wxLIST_MASK_STATE;
871    info.m_stateMask = stateMask;
872    info.m_itemId = item;
873
874    if (!GetItem(info))
875        return 0;
876
877    return info.m_state;
878}
879
880// Sets the item state
881bool wxListCtrl::SetItemState(long item, long state, long stateMask)
882{
883    // NB: don't use SetItem() here as it doesn't work with the virtual list
884    //     controls
885    LV_ITEM lvItem;
886    wxZeroMemory(lvItem);
887
888    wxConvertToMSWFlags(state, stateMask, lvItem);
889
890    // for the virtual list controls we need to refresh the previously focused
891    // item manually when changing focus without changing selection
892    // programmatically because otherwise it keeps its focus rectangle until
893    // next repaint (yet another comctl32 bug)
894    long focusOld;
895    if ( IsVirtual() &&
896         (stateMask & wxLIST_STATE_FOCUSED) &&
897         (state & wxLIST_STATE_FOCUSED) )
898    {
899        focusOld = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
900    }
901    else
902    {
903        focusOld = -1;
904    }
905
906    if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE,
907                        (WPARAM)item, (LPARAM)&lvItem) )
908    {
909        wxLogLastError(_T("ListView_SetItemState"));
910
911        return false;
912    }
913
914    if ( focusOld != -1 )
915    {
916        // no need to refresh the item if it was previously selected, it would
917        // only result in annoying flicker
918        if ( !(GetItemState(focusOld,
919                            wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED) )
920        {
921            RefreshItem(focusOld);
922        }
923    }
924
925    return true;
926}
927
928// Sets the item image
929bool wxListCtrl::SetItemImage(long item, int image, int WXUNUSED(selImage))
930{
931    return SetItemColumnImage(item, 0, image);
932}
933
934// Sets the item image
935bool wxListCtrl::SetItemColumnImage(long item, long column, int image)
936{
937    wxListItem info;
938
939    info.m_mask = wxLIST_MASK_IMAGE;
940    info.m_image = image;
941    info.m_itemId = item;
942    info.m_col = column;
943
944    return SetItem(info);
945}
946
947// Gets the item text
948wxString wxListCtrl::GetItemText(long item) const
949{
950    wxListItem info;
951
952    info.m_mask = wxLIST_MASK_TEXT;
953    info.m_itemId = item;
954
955    if (!GetItem(info))
956        return wxEmptyString;
957    return info.m_text;
958}
959
960// Sets the item text
961void wxListCtrl::SetItemText(long item, const wxString& str)
962{
963    wxListItem info;
964
965    info.m_mask = wxLIST_MASK_TEXT;
966    info.m_itemId = item;
967    info.m_text = str;
968
969    SetItem(info);
970}
971
972// Gets the item data
973wxUIntPtr wxListCtrl::GetItemData(long item) const
974{
975    wxListItem info;
976
977    info.m_mask = wxLIST_MASK_DATA;
978    info.m_itemId = item;
979
980    if (!GetItem(info))
981        return 0;
982    return info.m_data;
983}
984
985// Sets the item data
986bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data)
987{
988    wxListItem info;
989
990    info.m_mask = wxLIST_MASK_DATA;
991    info.m_itemId = item;
992    info.m_data = data;
993
994    return SetItem(info);
995}
996
997bool wxListCtrl::SetItemData(long item, long data)
998{
999    return SetItemPtrData(item, data);
1000}
1001
1002wxRect wxListCtrl::GetViewRect() const
1003{
1004    wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST),
1005                    _T("wxListCtrl::GetViewRect() only works in icon mode") );
1006
1007    RECT rc;
1008    if ( !ListView_GetViewRect(GetHwnd(), &rc) )
1009    {
1010        wxLogDebug(_T("ListView_GetViewRect() failed."));
1011
1012        wxZeroMemory(rc);
1013    }
1014
1015    wxRect rect;
1016    wxCopyRECTToRect(rc, rect);
1017
1018    return rect;
1019}
1020
1021// Gets the item rectangle
1022bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const
1023{
1024    return GetSubItemRect( item, wxLIST_GETSUBITEMRECT_WHOLEITEM, rect, code) ;
1025}
1026
1027/*!
1028 * Retrieve coordinates and size of a specified subitem of a listview control.
1029 * This function only works if the listview control is in the report mode.
1030 *
1031 * @param item : Item number
1032 * @param subItem : Subitem or column number, use -1 for the whole row including
1033 *                  all columns or subitems
1034 * @param rect : A pointer to an allocated wxRect object
1035 * @param code : Specify the part of the subitem coordinates you need. Choices are
1036 *               wxLIST_RECT_BOUNDS, wxLIST_RECT_ICON, wxLIST_RECT_LABEL
1037 *
1038 * @return bool  : True if successful.
1039 */
1040bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) const
1041{
1042    RECT rectWin;
1043
1044    int codeWin;
1045    if ( code == wxLIST_RECT_BOUNDS )
1046        codeWin = LVIR_BOUNDS;
1047    else if ( code == wxLIST_RECT_ICON )
1048        codeWin = LVIR_ICON;
1049    else if ( code == wxLIST_RECT_LABEL )
1050        codeWin = LVIR_LABEL;
1051    else
1052    {
1053        wxFAIL_MSG( _T("incorrect code in GetItemRect() / GetSubItemRect()") );
1054        codeWin = LVIR_BOUNDS;
1055    }
1056
1057    bool success;
1058    if( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM)
1059    {
1060      success = ListView_GetItemRect(GetHwnd(), (int) item, &rectWin, codeWin) != 0;
1061    }
1062    else if( subItem >= 0)
1063    {
1064      success = ListView_GetSubItemRect( GetHwnd(), (int) item, (int) subItem, codeWin, &rectWin) != 0;
1065    }
1066    else
1067    {
1068      wxFAIL_MSG( _T("incorrect subItem number in GetSubItemRect()") );
1069      return false;
1070    }
1071
1072    rect.x = rectWin.left;
1073    rect.y = rectWin.top;
1074    rect.width = rectWin.right - rectWin.left;
1075    rect.height = rectWin.bottom - rectWin.top;
1076
1077    return success;
1078}
1079
1080
1081
1082
1083// Gets the item position
1084bool wxListCtrl::GetItemPosition(long item, wxPoint& pos) const
1085{
1086    POINT pt;
1087
1088    bool success = (ListView_GetItemPosition(GetHwnd(), (int) item, &pt) != 0);
1089
1090    pos.x = pt.x; pos.y = pt.y;
1091    return success;
1092}
1093
1094// Sets the item position.
1095bool wxListCtrl::SetItemPosition(long item, const wxPoint& pos)
1096{
1097    return (ListView_SetItemPosition(GetHwnd(), (int) item, pos.x, pos.y) != 0);
1098}
1099
1100// Gets the number of items in the list control
1101int wxListCtrl::GetItemCount() const
1102{
1103    return m_count;
1104}
1105
1106wxSize wxListCtrl::GetItemSpacing() const
1107{
1108    const int spacing = ListView_GetItemSpacing(GetHwnd(), (BOOL)HasFlag(wxLC_SMALL_ICON));
1109
1110    return wxSize(LOWORD(spacing), HIWORD(spacing));
1111}
1112
1113#if WXWIN_COMPATIBILITY_2_6
1114
1115int wxListCtrl::GetItemSpacing(bool isSmall) const
1116{
1117    return ListView_GetItemSpacing(GetHwnd(), (BOOL) isSmall);
1118}
1119
1120#endif // WXWIN_COMPATIBILITY_2_6
1121
1122void wxListCtrl::SetItemTextColour( long item, const wxColour &col )
1123{
1124    wxListItem info;
1125    info.m_itemId = item;
1126    info.SetTextColour( col );
1127    SetItem( info );
1128}
1129
1130wxColour wxListCtrl::GetItemTextColour( long item ) const
1131{
1132    wxColour col;
1133    wxListItemInternalData *data = wxGetInternalData(this, item);
1134    if ( data && data->attr )
1135        col = data->attr->GetTextColour();
1136
1137    return col;
1138}
1139
1140void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
1141{
1142    wxListItem info;
1143    info.m_itemId = item;
1144    info.SetBackgroundColour( col );
1145    SetItem( info );
1146}
1147
1148wxColour wxListCtrl::GetItemBackgroundColour( long item ) const
1149{
1150    wxColour col;
1151    wxListItemInternalData *data = wxGetInternalData(this, item);
1152    if ( data && data->attr )
1153        col = data->attr->GetBackgroundColour();
1154
1155    return col;
1156}
1157
1158void wxListCtrl::SetItemFont( long item, const wxFont &f )
1159{
1160    wxListItem info;
1161    info.m_itemId = item;
1162    info.SetFont( f );
1163    SetItem( info );
1164}
1165
1166wxFont wxListCtrl::GetItemFont( long item ) const
1167{
1168    wxFont f;
1169    wxListItemInternalData *data = wxGetInternalData(this, item);
1170    if ( data && data->attr )
1171        f = data->attr->GetFont();
1172
1173    return f;
1174}
1175
1176// Gets the number of selected items in the list control
1177int wxListCtrl::GetSelectedItemCount() const
1178{
1179    return ListView_GetSelectedCount(GetHwnd());
1180}
1181
1182// Gets the text colour of the listview
1183wxColour wxListCtrl::GetTextColour() const
1184{
1185    COLORREF ref = ListView_GetTextColor(GetHwnd());
1186    wxColour col(GetRValue(ref), GetGValue(ref), GetBValue(ref));
1187    return col;
1188}
1189
1190// Sets the text colour of the listview
1191void wxListCtrl::SetTextColour(const wxColour& col)
1192{
1193    ListView_SetTextColor(GetHwnd(), PALETTERGB(col.Red(), col.Green(), col.Blue()));
1194}
1195
1196// Gets the index of the topmost visible item when in
1197// list or report view
1198long wxListCtrl::GetTopItem() const
1199{
1200    return (long) ListView_GetTopIndex(GetHwnd());
1201}
1202
1203// Searches for an item, starting from 'item'.
1204// 'geometry' is one of
1205// wxLIST_NEXT_ABOVE/ALL/BELOW/LEFT/RIGHT.
1206// 'state' is a state bit flag, one or more of
1207// wxLIST_STATE_DROPHILITED/FOCUSED/SELECTED/CUT.
1208// item can be -1 to find the first item that matches the
1209// specified flags.
1210// Returns the item or -1 if unsuccessful.
1211long wxListCtrl::GetNextItem(long item, int geom, int state) const
1212{
1213    long flags = 0;
1214
1215    if ( geom == wxLIST_NEXT_ABOVE )
1216        flags |= LVNI_ABOVE;
1217    if ( geom == wxLIST_NEXT_ALL )
1218        flags |= LVNI_ALL;
1219    if ( geom == wxLIST_NEXT_BELOW )
1220        flags |= LVNI_BELOW;
1221    if ( geom == wxLIST_NEXT_LEFT )
1222        flags |= LVNI_TOLEFT;
1223    if ( geom == wxLIST_NEXT_RIGHT )
1224        flags |= LVNI_TORIGHT;
1225
1226    if ( state & wxLIST_STATE_CUT )
1227        flags |= LVNI_CUT;
1228    if ( state & wxLIST_STATE_DROPHILITED )
1229        flags |= LVNI_DROPHILITED;
1230    if ( state & wxLIST_STATE_FOCUSED )
1231        flags |= LVNI_FOCUSED;
1232    if ( state & wxLIST_STATE_SELECTED )
1233        flags |= LVNI_SELECTED;
1234
1235    return (long) ListView_GetNextItem(GetHwnd(), item, flags);
1236}
1237
1238
1239wxImageList *wxListCtrl::GetImageList(int which) const
1240{
1241    if ( which == wxIMAGE_LIST_NORMAL )
1242    {
1243        return m_imageListNormal;
1244    }
1245    else if ( which == wxIMAGE_LIST_SMALL )
1246    {
1247        return m_imageListSmall;
1248    }
1249    else if ( which == wxIMAGE_LIST_STATE )
1250    {
1251        return m_imageListState;
1252    }
1253    return NULL;
1254}
1255
1256void wxListCtrl::SetImageList(wxImageList *imageList, int which)
1257{
1258    int flags = 0;
1259    if ( which == wxIMAGE_LIST_NORMAL )
1260    {
1261        flags = LVSIL_NORMAL;
1262        if (m_ownsImageListNormal) delete m_imageListNormal;
1263        m_imageListNormal = imageList;
1264        m_ownsImageListNormal = false;
1265    }
1266    else if ( which == wxIMAGE_LIST_SMALL )
1267    {
1268        flags = LVSIL_SMALL;
1269        if (m_ownsImageListSmall) delete m_imageListSmall;
1270        m_imageListSmall = imageList;
1271        m_ownsImageListSmall = false;
1272    }
1273    else if ( which == wxIMAGE_LIST_STATE )
1274    {
1275        flags = LVSIL_STATE;
1276        if (m_ownsImageListState) delete m_imageListState;
1277        m_imageListState = imageList;
1278        m_ownsImageListState = false;
1279    }
1280    (void) ListView_SetImageList(GetHwnd(), (HIMAGELIST) imageList ? imageList->GetHIMAGELIST() : 0, flags);
1281}
1282
1283void wxListCtrl::AssignImageList(wxImageList *imageList, int which)
1284{
1285    SetImageList(imageList, which);
1286    if ( which == wxIMAGE_LIST_NORMAL )
1287        m_ownsImageListNormal = true;
1288    else if ( which == wxIMAGE_LIST_SMALL )
1289        m_ownsImageListSmall = true;
1290    else if ( which == wxIMAGE_LIST_STATE )
1291        m_ownsImageListState = true;
1292}
1293
1294// ----------------------------------------------------------------------------
1295// Operations
1296// ----------------------------------------------------------------------------
1297
1298// Arranges the items
1299bool wxListCtrl::Arrange(int flag)
1300{
1301    UINT code = 0;
1302    if ( flag == wxLIST_ALIGN_LEFT )
1303        code = LVA_ALIGNLEFT;
1304    else if ( flag == wxLIST_ALIGN_TOP )
1305        code = LVA_ALIGNTOP;
1306    else if ( flag == wxLIST_ALIGN_DEFAULT )
1307        code = LVA_DEFAULT;
1308    else if ( flag == wxLIST_ALIGN_SNAP_TO_GRID )
1309        code = LVA_SNAPTOGRID;
1310
1311    return (ListView_Arrange(GetHwnd(), code) != 0);
1312}
1313
1314// Deletes an item
1315bool wxListCtrl::DeleteItem(long item)
1316{
1317    if ( !ListView_DeleteItem(GetHwnd(), (int) item) )
1318    {
1319        wxLogLastError(_T("ListView_DeleteItem"));
1320        return false;
1321    }
1322
1323    m_count--;
1324    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
1325                  wxT("m_count should match ListView_GetItemCount"));
1326
1327    // the virtual list control doesn't refresh itself correctly, help it
1328    if ( IsVirtual() )
1329    {
1330        // we need to refresh all the lines below the one which was deleted
1331        wxRect rectItem;
1332        if ( item > 0 && GetItemCount() )
1333        {
1334            GetItemRect(item - 1, rectItem);
1335        }
1336        else
1337        {
1338            rectItem.y =
1339            rectItem.height = 0;
1340        }
1341
1342        wxRect rectWin = GetRect();
1343        rectWin.height = rectWin.GetBottom() - rectItem.GetBottom();
1344        rectWin.y = rectItem.GetBottom();
1345
1346        RefreshRect(rectWin);
1347    }
1348
1349    return true;
1350}
1351
1352// Deletes all items
1353bool wxListCtrl::DeleteAllItems()
1354{
1355    return ListView_DeleteAllItems(GetHwnd()) != 0;
1356}
1357
1358// Deletes all items
1359bool wxListCtrl::DeleteAllColumns()
1360{
1361    while ( m_colCount > 0 )
1362    {
1363        if ( ListView_DeleteColumn(GetHwnd(), 0) == 0 )
1364        {
1365            wxLogLastError(wxT("ListView_DeleteColumn"));
1366
1367            return false;
1368        }
1369
1370        m_colCount--;
1371    }
1372
1373    wxASSERT_MSG( m_colCount == 0, wxT("no columns should be left") );
1374
1375    return true;
1376}
1377
1378// Deletes a column
1379bool wxListCtrl::DeleteColumn(int col)
1380{
1381    bool success = (ListView_DeleteColumn(GetHwnd(), col) != 0);
1382
1383    if ( success && (m_colCount > 0) )
1384        m_colCount --;
1385    return success;
1386}
1387
1388// Clears items, and columns if there are any.
1389void wxListCtrl::ClearAll()
1390{
1391    DeleteAllItems();
1392    if ( m_colCount > 0 )
1393        DeleteAllColumns();
1394}
1395
1396void wxListCtrl::InitEditControl(WXHWND hWnd)
1397{
1398    m_textCtrl->SetHWND(hWnd);
1399    m_textCtrl->SubclassWin(hWnd);
1400    m_textCtrl->SetParent(this);
1401
1402    // we must disallow TABbing away from the control while the edit contol is
1403    // shown because this leaves it in some strange state (just try removing
1404    // this line and then pressing TAB while editing an item in  listctrl
1405    // inside a panel)
1406    m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() | wxTE_PROCESS_TAB);
1407}
1408
1409wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
1410{
1411    wxCHECK_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)), NULL,
1412                  wxT("control used for label editing must be a wxTextCtrl") );
1413
1414    // ListView_EditLabel requires that the list has focus.
1415    SetFocus();
1416
1417    // create m_textCtrl here before calling ListView_EditLabel() because it
1418    // generates wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT event from inside it and
1419    // the user handler for it can call GetEditControl() resulting in an on
1420    // demand creation of a stock wxTextCtrl instead of the control of a
1421    // (possibly) custom wxClassInfo
1422    DeleteEditControl();
1423    m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
1424
1425    WXHWND hWnd = (WXHWND) ListView_EditLabel(GetHwnd(), item);
1426    if ( !hWnd )
1427    {
1428        // failed to start editing
1429        delete m_textCtrl;
1430        m_textCtrl = NULL;
1431
1432        return NULL;
1433    }
1434
1435    // if GetEditControl() hasn't been called, we need to initialize the edit
1436    // control ourselves
1437    if ( !m_textCtrl->GetHWND() )
1438        InitEditControl(hWnd);
1439
1440    return m_textCtrl;
1441}
1442
1443// End label editing, optionally cancelling the edit
1444bool wxListCtrl::EndEditLabel(bool cancel)
1445{
1446    // m_textCtrl is not always ready, ie. in EVT_LIST_BEGIN_LABEL_EDIT
1447    HWND hwnd = ListView_GetEditControl(GetHwnd());
1448    if ( !hwnd )
1449        return false;
1450
1451    if ( cancel )
1452        ::SetWindowText(hwnd, wxEmptyString); // dubious but better than nothing
1453
1454    // we shouldn't destroy the control ourselves according to MSDN, which
1455    // proposes WM_CANCELMODE to do this, but it doesn't seem to work
1456    //
1457    // posting WM_CLOSE to it does seem to work without any side effects
1458    ::PostMessage(hwnd, WM_CLOSE, 0, 0);
1459
1460    return true;
1461}
1462
1463// Ensures this item is visible
1464bool wxListCtrl::EnsureVisible(long item)
1465{
1466    return ListView_EnsureVisible(GetHwnd(), (int) item, FALSE) != FALSE;
1467}
1468
1469// Find an item whose label matches this string, starting from the item after 'start'
1470// or the beginning if 'start' is -1.
1471long wxListCtrl::FindItem(long start, const wxString& str, bool partial)
1472{
1473    LV_FINDINFO findInfo;
1474
1475    findInfo.flags = LVFI_STRING;
1476    if ( partial )
1477        findInfo.flags |= LVFI_PARTIAL;
1478    findInfo.psz = str;
1479
1480    // ListView_FindItem() excludes the first item from search and to look
1481    // through all the items you need to start from -1 which is unnatural and
1482    // inconsistent with the generic version - so we adjust the index
1483    if (start != -1)
1484        start --;
1485    return ListView_FindItem(GetHwnd(), (int) start, &findInfo);
1486}
1487
1488// Find an item whose data matches this data, starting from the item after 'start'
1489// or the beginning if 'start' is -1.
1490// NOTE : Lindsay Mathieson - 14-July-2002
1491//        No longer use ListView_FindItem as the data attribute is now stored
1492//        in a wxListItemInternalData structure refernced by the actual lParam
1493long wxListCtrl::FindItem(long start, wxUIntPtr data)
1494{
1495    long  idx = start + 1;
1496    long count = GetItemCount();
1497
1498    while (idx < count)
1499    {
1500        if (GetItemData(idx) == data)
1501            return idx;
1502        idx++;
1503    };
1504
1505    return -1;
1506}
1507
1508// Find an item nearest this position in the specified direction, starting from
1509// the item after 'start' or the beginning if 'start' is -1.
1510long wxListCtrl::FindItem(long start, const wxPoint& pt, int direction)
1511{
1512    LV_FINDINFO findInfo;
1513
1514    findInfo.flags = LVFI_NEARESTXY;
1515    findInfo.pt.x = pt.x;
1516    findInfo.pt.y = pt.y;
1517    findInfo.vkDirection = VK_RIGHT;
1518
1519    if ( direction == wxLIST_FIND_UP )
1520        findInfo.vkDirection = VK_UP;
1521    else if ( direction == wxLIST_FIND_DOWN )
1522        findInfo.vkDirection = VK_DOWN;
1523    else if ( direction == wxLIST_FIND_LEFT )
1524        findInfo.vkDirection = VK_LEFT;
1525    else if ( direction == wxLIST_FIND_RIGHT )
1526        findInfo.vkDirection = VK_RIGHT;
1527
1528    return ListView_FindItem(GetHwnd(), (int) start, & findInfo);
1529}
1530
1531// Determines which item (if any) is at the specified point,
1532// giving details in 'flags' (see wxLIST_HITTEST_... flags above)
1533long
1534wxListCtrl::HitTest(const wxPoint& point, int& flags, long *ptrSubItem) const
1535{
1536    LV_HITTESTINFO hitTestInfo;
1537    hitTestInfo.pt.x = (int) point.x;
1538    hitTestInfo.pt.y = (int) point.y;
1539
1540    long item;
1541#ifdef LVM_SUBITEMHITTEST
1542    if ( ptrSubItem && wxApp::GetComCtl32Version() >= 470 )
1543    {
1544        item = ListView_SubItemHitTest(GetHwnd(), &hitTestInfo);
1545        *ptrSubItem = hitTestInfo.iSubItem;
1546    }
1547    else
1548#endif // LVM_SUBITEMHITTEST
1549    {
1550        item = ListView_HitTest(GetHwnd(), &hitTestInfo);
1551    }
1552
1553    flags = 0;
1554
1555    if ( hitTestInfo.flags & LVHT_ABOVE )
1556        flags |= wxLIST_HITTEST_ABOVE;
1557    if ( hitTestInfo.flags & LVHT_BELOW )
1558        flags |= wxLIST_HITTEST_BELOW;
1559    if ( hitTestInfo.flags & LVHT_TOLEFT )
1560        flags |= wxLIST_HITTEST_TOLEFT;
1561    if ( hitTestInfo.flags & LVHT_TORIGHT )
1562        flags |= wxLIST_HITTEST_TORIGHT;
1563
1564    if ( hitTestInfo.flags & LVHT_NOWHERE )
1565        flags |= wxLIST_HITTEST_NOWHERE;
1566
1567    // note a bug or at least a very strange feature of comtl32.dll (tested
1568    // with version 4.0 under Win95 and 6.0 under Win 2003): if you click to
1569    // the right of the item label, ListView_HitTest() returns a combination of
1570    // LVHT_ONITEMICON, LVHT_ONITEMLABEL and LVHT_ONITEMSTATEICON -- filter out
1571    // the bits which don't make sense
1572    if ( hitTestInfo.flags & LVHT_ONITEMLABEL )
1573    {
1574        flags |= wxLIST_HITTEST_ONITEMLABEL;
1575
1576        // do not translate LVHT_ONITEMICON here, as per above
1577    }
1578    else
1579    {
1580        if ( hitTestInfo.flags & LVHT_ONITEMICON )
1581            flags |= wxLIST_HITTEST_ONITEMICON;
1582        if ( hitTestInfo.flags & LVHT_ONITEMSTATEICON )
1583            flags |= wxLIST_HITTEST_ONITEMSTATEICON;
1584    }
1585
1586    return item;
1587}
1588
1589
1590// Inserts an item, returning the index of the new item if successful,
1591// -1 otherwise.
1592long wxListCtrl::InsertItem(const wxListItem& info)
1593{
1594    wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") );
1595
1596    LV_ITEM item;
1597    wxConvertToMSWListItem(this, info, item);
1598    item.mask &= ~LVIF_PARAM;
1599
1600    // check wether we need to allocate our internal data
1601    bool needInternalData = ((info.m_mask & wxLIST_MASK_DATA) || info.HasAttributes());
1602    if (needInternalData)
1603    {
1604        m_AnyInternalData = true;
1605        item.mask |= LVIF_PARAM;
1606
1607        // internal stucture that manages data
1608        wxListItemInternalData *data = new wxListItemInternalData();
1609        item.lParam = (LPARAM) data;
1610
1611        if (info.m_mask & wxLIST_MASK_DATA)
1612            data->lParam = info.m_data;
1613
1614        // check whether it has any custom attributes
1615        if ( info.HasAttributes() )
1616        {
1617            // take copy of attributes
1618            data->attr = new wxListItemAttr(*info.GetAttributes());
1619
1620            // and remember that we have some now...
1621            m_hasAnyAttr = true;
1622        }
1623    };
1624
1625    long rv = ListView_InsertItem(GetHwnd(), & item);
1626
1627    m_count++;
1628    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
1629                  wxT("m_count should match ListView_GetItemCount"));
1630
1631    return rv;
1632}
1633
1634long wxListCtrl::InsertItem(long index, const wxString& label)
1635{
1636    wxListItem info;
1637    info.m_text = label;
1638    info.m_mask = wxLIST_MASK_TEXT;
1639    info.m_itemId = index;
1640    return InsertItem(info);
1641}
1642
1643// Inserts an image item
1644long wxListCtrl::InsertItem(long index, int imageIndex)
1645{
1646    wxListItem info;
1647    info.m_image = imageIndex;
1648    info.m_mask = wxLIST_MASK_IMAGE;
1649    info.m_itemId = index;
1650    return InsertItem(info);
1651}
1652
1653// Inserts an image/string item
1654long wxListCtrl::InsertItem(long index, const wxString& label, int imageIndex)
1655{
1656    wxListItem info;
1657    info.m_image = imageIndex;
1658    info.m_text = label;
1659    info.m_mask = wxLIST_MASK_IMAGE | wxLIST_MASK_TEXT;
1660    info.m_itemId = index;
1661    return InsertItem(info);
1662}
1663
1664// For list view mode (only), inserts a column.
1665long wxListCtrl::InsertColumn(long col, const wxListItem& item)
1666{
1667    LV_COLUMN lvCol;
1668    wxConvertToMSWListCol(GetHwnd(), col, item, lvCol);
1669
1670    if ( !(lvCol.mask & LVCF_WIDTH) )
1671    {
1672        // always give some width to the new column: this one is compatible
1673        // with the generic version
1674        lvCol.mask |= LVCF_WIDTH;
1675        lvCol.cx = 80;
1676    }
1677
1678    long n = ListView_InsertColumn(GetHwnd(), col, &lvCol);
1679    if ( n != -1 )
1680    {
1681        m_colCount++;
1682    }
1683    else // failed to insert?
1684    {
1685        wxLogDebug(wxT("Failed to insert the column '%s' into listview!"),
1686                   lvCol.pszText);
1687    }
1688
1689    return n;
1690}
1691
1692long wxListCtrl::InsertColumn(long col,
1693                              const wxString& heading,
1694                              int format,
1695                              int width)
1696{
1697    wxListItem item;
1698    item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
1699    item.m_text = heading;
1700    if ( width > -1 )
1701    {
1702        item.m_mask |= wxLIST_MASK_WIDTH;
1703        item.m_width = width;
1704    }
1705    item.m_format = format;
1706
1707    return InsertColumn(col, item);
1708}
1709
1710// scroll the control by the given number of pixels (exception: in list view,
1711// dx is interpreted as number of columns)
1712bool wxListCtrl::ScrollList(int dx, int dy)
1713{
1714    if ( !ListView_Scroll(GetHwnd(), dx, dy) )
1715    {
1716        wxLogDebug(_T("ListView_Scroll(%d, %d) failed"), dx, dy);
1717
1718        return false;
1719    }
1720
1721    return true;
1722}
1723
1724// Sort items.
1725
1726// fn is a function which takes 3 long arguments: item1, item2, data.
1727// item1 is the long data associated with a first item (NOT the index).
1728// item2 is the long data associated with a second item (NOT the index).
1729// data is the same value as passed to SortItems.
1730// The return value is a negative number if the first item should precede the second
1731// item, a positive number of the second item should precede the first,
1732// or zero if the two items are equivalent.
1733
1734// data is arbitrary data to be passed to the sort function.
1735
1736// Internal structures for proxying the user compare function
1737// so that we can pass it the *real* user data
1738
1739// translate lParam data and call user func
1740struct wxInternalDataSort
1741{
1742    wxListCtrlCompare user_fn;
1743    long data;
1744};
1745
1746int CALLBACK wxInternalDataCompareFunc(LPARAM lParam1, LPARAM lParam2,  LPARAM lParamSort)
1747{
1748    struct wxInternalDataSort *internalData = (struct wxInternalDataSort *) lParamSort;
1749
1750    wxListItemInternalData *data1 = (wxListItemInternalData *) lParam1;
1751    wxListItemInternalData *data2 = (wxListItemInternalData *) lParam2;
1752
1753    long d1 = (data1 == NULL ? 0 : data1->lParam);
1754    long d2 = (data2 == NULL ? 0 : data2->lParam);
1755
1756    return internalData->user_fn(d1, d2, internalData->data);
1757
1758}
1759
1760bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
1761{
1762    struct wxInternalDataSort internalData;
1763    internalData.user_fn = fn;
1764    internalData.data = data;
1765
1766    // WPARAM cast is needed for mingw/cygwin
1767    if ( !ListView_SortItems(GetHwnd(),
1768                             wxInternalDataCompareFunc,
1769                             (WPARAM) &internalData) )
1770    {
1771        wxLogDebug(_T("ListView_SortItems() failed"));
1772
1773        return false;
1774    }
1775
1776    return true;
1777}
1778
1779
1780
1781// ----------------------------------------------------------------------------
1782// message processing
1783// ----------------------------------------------------------------------------
1784
1785bool wxListCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
1786{
1787    if ( msg->message == WM_KEYDOWN )
1788    {
1789        if ( msg->wParam == VK_RETURN )
1790        {
1791            // We need VK_RETURN to generate wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
1792            // but only if none of the modifiers is down.  We'll let normal
1793            // accelerators handle those.
1794            if ( !wxIsCtrlDown() && !wxIsShiftDown() &&
1795                 !((HIWORD(msg->lParam) & KF_ALTDOWN) == KF_ALTDOWN))
1796            return false;
1797        }
1798    }
1799
1800    return wxControl::MSWShouldPreProcessMessage(msg);
1801}
1802
1803bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id)
1804{
1805    if (cmd == EN_UPDATE)
1806    {
1807        wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
1808        event.SetEventObject( this );
1809        ProcessCommand(event);
1810        return true;
1811    }
1812    else if (cmd == EN_KILLFOCUS)
1813    {
1814        wxCommandEvent event(wxEVT_KILL_FOCUS, id);
1815        event.SetEventObject( this );
1816        ProcessCommand(event);
1817        return true;
1818    }
1819    else
1820        return false;
1821}
1822
1823bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1824{
1825
1826    // prepare the event
1827    // -----------------
1828
1829    wxListEvent event(wxEVT_NULL, m_windowId);
1830    event.SetEventObject(this);
1831
1832    wxEventType eventType = wxEVT_NULL;
1833
1834    NMHDR *nmhdr = (NMHDR *)lParam;
1835
1836    // if your compiler is as broken as this, you should really change it: this
1837    // code is needed for normal operation! #ifdef below is only useful for
1838    // automatic rebuilds which are done with a very old compiler version
1839#ifdef HDN_BEGINTRACKA
1840
1841    // check for messages from the header (in report view)
1842    HWND hwndHdr = ListView_GetHeader(GetHwnd());
1843
1844    // is it a message from the header?
1845    if ( nmhdr->hwndFrom == hwndHdr )
1846    {
1847        HD_NOTIFY *nmHDR = (HD_NOTIFY *)nmhdr;
1848
1849        event.m_itemIndex = -1;
1850
1851        switch ( nmhdr->code )
1852        {
1853            // yet another comctl32.dll bug: under NT/W2K it sends Unicode
1854            // TRACK messages even to ANSI programs: on my system I get
1855            // HDN_BEGINTRACKW and HDN_ENDTRACKA and no HDN_TRACK at all!
1856            //
1857            // work around is to simply catch both versions and hope that it
1858            // works (why should this message exist in ANSI and Unicode is
1859            // beyond me as it doesn't deal with strings at all...)
1860            //
1861            // note that fr HDN_TRACK another possibility could be to use
1862            // HDN_ITEMCHANGING but it is sent even after HDN_ENDTRACK and when
1863            // something other than the item width changes so we'd have to
1864            // filter out the unwanted events then
1865            case HDN_BEGINTRACKA:
1866            case HDN_BEGINTRACKW:
1867                eventType = wxEVT_COMMAND_LIST_COL_BEGIN_DRAG;
1868                // fall through
1869
1870            case HDN_TRACKA:
1871            case HDN_TRACKW:
1872                if ( eventType == wxEVT_NULL )
1873                    eventType = wxEVT_COMMAND_LIST_COL_DRAGGING;
1874                // fall through
1875
1876            case HDN_ENDTRACKA:
1877            case HDN_ENDTRACKW:
1878                if ( eventType == wxEVT_NULL )
1879                    eventType = wxEVT_COMMAND_LIST_COL_END_DRAG;
1880
1881                event.m_item.m_width = nmHDR->pitem->cxy;
1882                event.m_col = nmHDR->iItem;
1883                break;
1884
1885#if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
1886            case GN_CONTEXTMENU:
1887#endif //__WXWINCE__
1888            case NM_RCLICK:
1889                {
1890                    eventType = wxEVT_COMMAND_LIST_COL_RIGHT_CLICK;
1891                    event.m_col = -1;
1892
1893                    // find the column clicked: we have to search for it
1894                    // ourselves as the notification message doesn't provide
1895                    // this info
1896
1897                    // where did the click occur?
1898                    POINT ptClick;
1899#if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
1900                    if ( nmhdr->code == GN_CONTEXTMENU )
1901                    {
1902                        ptClick = ((NMRGINFO*)nmhdr)->ptAction;
1903                    }
1904                    else
1905#endif //__WXWINCE__
1906                    if ( !::GetCursorPos(&ptClick) )
1907                    {
1908                        wxLogLastError(_T("GetCursorPos"));
1909                    }
1910
1911                    // we need to use listctrl coordinates for the event point
1912                    // but for comparison with Header_GetItemRect() result
1913                    // below we need to use header window coordinates
1914                    POINT ptClickList = ptClick;
1915                    if ( ::ScreenToClient(GetHwnd(), &ptClickList) )
1916                    {
1917                        event.m_pointDrag.x = ptClickList.x;
1918                        event.m_pointDrag.y = ptClickList.y;
1919                    }
1920                    else
1921                    {
1922                        wxLogLastError(_T("ScreenToClient(listctrl)"));
1923                    }
1924
1925                    if ( !::ScreenToClient(hwndHdr, &ptClick) )
1926                    {
1927                        wxLogLastError(_T("ScreenToClient(listctrl header)"));
1928                    }
1929
1930                    const int colCount = Header_GetItemCount(hwndHdr);
1931                    for ( int col = 0; col < colCount; col++ )
1932                    {
1933                        RECT rect;
1934                        if ( Header_GetItemRect(hwndHdr, col, &rect) )
1935                        {
1936                            if ( ::PtInRect(&rect, ptClick) )
1937                            {
1938                                event.m_col = col;
1939                                break;
1940                            }
1941                        }
1942                    }
1943                }
1944                break;
1945
1946            case HDN_GETDISPINFOW:
1947                // letting Windows XP handle this message results in mysterious
1948                // crashes in comctl32.dll seemingly because of bad message
1949                // parameters
1950                //
1951                // I have no idea what is the real cause of the bug (which is,
1952                // just to make things interesting, is impossible to reproduce
1953                // reliably) but ignoring all these messages does fix it and
1954                // doesn't seem to have any negative consequences
1955                return true;
1956
1957            default:
1958                return wxControl::MSWOnNotify(idCtrl, lParam, result);
1959        }
1960    }
1961    else
1962#endif // defined(HDN_BEGINTRACKA)
1963    if ( nmhdr->hwndFrom == GetHwnd() )
1964    {
1965        // almost all messages use NM_LISTVIEW
1966        NM_LISTVIEW *nmLV = (NM_LISTVIEW *)nmhdr;
1967
1968        const int iItem = nmLV->iItem;
1969
1970
1971        // FreeAllInternalData will cause LVN_ITEMCHANG* messages, which can be
1972        // ignored for efficiency.  It is done here because the internal data is in the
1973        // process of being deleted so we don't want to try and access it below.
1974        if ( m_ignoreChangeMessages &&
1975             ( (nmLV->hdr.code == LVN_ITEMCHANGED) ||
1976               (nmLV->hdr.code == LVN_ITEMCHANGING)) )
1977        {
1978            return true;
1979        }
1980
1981
1982        // If we have a valid item then check if there is a data value
1983        // associated with it and put it in the event.
1984        if ( iItem >= 0 && iItem < GetItemCount() )
1985        {
1986            wxListItemInternalData *internaldata =
1987                wxGetInternalData(GetHwnd(), iItem);
1988
1989            if ( internaldata )
1990                event.m_item.m_data = internaldata->lParam;
1991        }
1992
1993        bool processed = true;
1994        switch ( nmhdr->code )
1995        {
1996            case LVN_BEGINRDRAG:
1997                eventType = wxEVT_COMMAND_LIST_BEGIN_RDRAG;
1998                // fall through
1999
2000            case LVN_BEGINDRAG:
2001                if ( eventType == wxEVT_NULL )
2002                {
2003                    eventType = wxEVT_COMMAND_LIST_BEGIN_DRAG;
2004                }
2005
2006                event.m_itemIndex = iItem;
2007                event.m_pointDrag.x = nmLV->ptAction.x;
2008                event.m_pointDrag.y = nmLV->ptAction.y;
2009                break;
2010
2011            // NB: we have to handle both *A and *W versions here because some
2012            //     versions of comctl32.dll send ANSI messages even to the
2013            //     Unicode windows
2014            case LVN_BEGINLABELEDITA:
2015            case LVN_BEGINLABELEDITW:
2016                {
2017                    wxLV_ITEM item;
2018                    if ( nmhdr->code == LVN_BEGINLABELEDITA )
2019                    {
2020                        item.Init(((LV_DISPINFOA *)lParam)->item);
2021                    }
2022                    else // LVN_BEGINLABELEDITW
2023                    {
2024                        item.Init(((LV_DISPINFOW *)lParam)->item);
2025                    }
2026
2027                    eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
2028                    wxConvertFromMSWListItem(GetHwnd(), event.m_item, item);
2029                    event.m_itemIndex = event.m_item.m_itemId;
2030                }
2031                break;
2032
2033            case LVN_ENDLABELEDITA:
2034            case LVN_ENDLABELEDITW:
2035                {
2036                    wxLV_ITEM item;
2037                    if ( nmhdr->code == LVN_ENDLABELEDITA )
2038                    {
2039                        item.Init(((LV_DISPINFOA *)lParam)->item);
2040                    }
2041                    else // LVN_ENDLABELEDITW
2042                    {
2043                        item.Init(((LV_DISPINFOW *)lParam)->item);
2044                    }
2045
2046                    // was editing cancelled?
2047                    const LV_ITEM& lvi = (LV_ITEM)item;
2048                    if ( !lvi.pszText || lvi.iItem == -1 )
2049                    {
2050                        // don't keep a stale wxTextCtrl around
2051                        if ( m_textCtrl )
2052                        {
2053                            // EDIT control will be deleted by the list control itself so
2054                            // prevent us from deleting it as well
2055                            m_textCtrl->UnsubclassWin();
2056                            m_textCtrl->SetHWND(0);
2057                            delete m_textCtrl;
2058                            m_textCtrl = NULL;
2059                        }
2060
2061                        event.SetEditCanceled(true);
2062                    }
2063
2064                    eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
2065                    wxConvertFromMSWListItem(NULL, event.m_item, item);
2066                    event.m_itemIndex = event.m_item.m_itemId;
2067                }
2068                break;
2069
2070            case LVN_COLUMNCLICK:
2071                eventType = wxEVT_COMMAND_LIST_COL_CLICK;
2072                event.m_itemIndex = -1;
2073                event.m_col = nmLV->iSubItem;
2074                break;
2075
2076            case LVN_DELETEALLITEMS:
2077                eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
2078                event.m_itemIndex = -1;
2079                break;
2080
2081            case LVN_DELETEITEM:
2082                if ( m_count == 0 )
2083                {
2084                    // this should be prevented by the post-processing code
2085                    // below, but "just in case"
2086                    return false;
2087                }
2088
2089                eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
2090                event.m_itemIndex = iItem;
2091                // delete the assoicated internal data
2092                wxDeleteInternalData(this, iItem);
2093                break;
2094
2095#if WXWIN_COMPATIBILITY_2_4
2096            case LVN_SETDISPINFO:
2097                {
2098                    eventType = wxEVT_COMMAND_LIST_SET_INFO;
2099                    LV_DISPINFO *info = (LV_DISPINFO *)lParam;
2100                    wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
2101                }
2102                break;
2103#endif
2104
2105            case LVN_INSERTITEM:
2106                eventType = wxEVT_COMMAND_LIST_INSERT_ITEM;
2107                event.m_itemIndex = iItem;
2108                break;
2109
2110            case LVN_ITEMCHANGED:
2111                // we translate this catch all message into more interesting
2112                // (and more easy to process) wxWidgets events
2113
2114                // first of all, we deal with the state change events only and
2115                // only for valid items (item == -1 for the virtual list
2116                // control)
2117                if ( nmLV->uChanged & LVIF_STATE && iItem != -1 )
2118                {
2119                    // temp vars for readability
2120                    const UINT stOld = nmLV->uOldState;
2121                    const UINT stNew = nmLV->uNewState;
2122
2123                    event.m_item.SetId(iItem);
2124                    event.m_item.SetMask(wxLIST_MASK_TEXT |
2125                                         wxLIST_MASK_IMAGE |
2126                                         wxLIST_MASK_DATA);
2127                    GetItem(event.m_item);
2128
2129                    // has the focus changed?
2130                    if ( !(stOld & LVIS_FOCUSED) && (stNew & LVIS_FOCUSED) )
2131                    {
2132                        eventType = wxEVT_COMMAND_LIST_ITEM_FOCUSED;
2133                        event.m_itemIndex = iItem;
2134                    }
2135
2136                    if ( (stNew & LVIS_SELECTED) != (stOld & LVIS_SELECTED) )
2137                    {
2138                        if ( eventType != wxEVT_NULL )
2139                        {
2140                            // focus and selection have both changed: send the
2141                            // focus event from here and the selection one
2142                            // below
2143                            event.SetEventType(eventType);
2144                            (void)GetEventHandler()->ProcessEvent(event);
2145                        }
2146                        else // no focus event to send
2147                        {
2148                            // then need to set m_itemIndex as it wasn't done
2149                            // above
2150                            event.m_itemIndex = iItem;
2151                        }
2152
2153                        eventType = stNew & LVIS_SELECTED
2154                                        ? wxEVT_COMMAND_LIST_ITEM_SELECTED
2155                                        : wxEVT_COMMAND_LIST_ITEM_DESELECTED;
2156                    }
2157                }
2158
2159                if ( eventType == wxEVT_NULL )
2160                {
2161                    // not an interesting event for us
2162                    return false;
2163                }
2164
2165                break;
2166
2167            case LVN_KEYDOWN:
2168                {
2169                    LV_KEYDOWN *info = (LV_KEYDOWN *)lParam;
2170                    WORD wVKey = info->wVKey;
2171
2172                    // get the current selection
2173                    long lItem = GetNextItem(-1,
2174                                             wxLIST_NEXT_ALL,
2175                                             wxLIST_STATE_SELECTED);
2176
2177                    // <Enter> or <Space> activate the selected item if any (but
2178                    // not with modified keys pressed as they have a predefined
2179                    // meaning for the list view then)
2180                    if ( lItem != -1 &&
2181                         (wVKey == VK_RETURN || wVKey == VK_SPACE) &&
2182                         !(wxIsShiftDown() || wxIsCtrlDown() ||
2183                             (GetKeyState(VK_MENU) < 0)) )
2184                    {
2185                        eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
2186                    }
2187                    else
2188                    {
2189                        eventType = wxEVT_COMMAND_LIST_KEY_DOWN;
2190
2191                        // wxCharCodeMSWToWX() returns 0 if the key is an ASCII
2192                        // value which should be used as is
2193                        int code = wxCharCodeMSWToWX(wVKey);
2194                        event.m_code = code ? code : wVKey;
2195                    }
2196
2197                    event.m_itemIndex =
2198                    event.m_item.m_itemId = lItem;
2199
2200                    if ( lItem != -1 )
2201                    {
2202                        // fill the other fields too
2203                        event.m_item.m_text = GetItemText(lItem);
2204                        event.m_item.m_data = GetItemData(lItem);
2205                    }
2206                }
2207                break;
2208
2209            case NM_DBLCLK:
2210                // if the user processes it in wxEVT_COMMAND_LEFT_CLICK(), don't do
2211                // anything else
2212                if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
2213                {
2214                    return true;
2215                }
2216
2217                // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event
2218                // if it happened on an item (and not on empty place)
2219                if ( iItem == -1 )
2220                {
2221                    // not on item
2222                    return false;
2223                }
2224
2225                eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
2226                event.m_itemIndex = iItem;
2227                event.m_item.m_text = GetItemText(iItem);
2228                event.m_item.m_data = GetItemData(iItem);
2229                break;
2230
2231#if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
2232            case GN_CONTEXTMENU:
2233#endif //__WXWINCE__
2234            case NM_RCLICK:
2235                // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(),
2236                // don't do anything else
2237                if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
2238                {
2239                    return true;
2240                }
2241
2242                // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event
2243                LV_HITTESTINFO lvhti;
2244                wxZeroMemory(lvhti);
2245
2246#if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
2247              if(nmhdr->code == GN_CONTEXTMENU) {
2248                  lvhti.pt = ((NMRGINFO*)nmhdr)->ptAction;
2249              } else
2250#endif //__WXWINCE__
2251                ::GetCursorPos(&(lvhti.pt));
2252                ::ScreenToClient(GetHwnd(),&(lvhti.pt));
2253                if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
2254                {
2255                    if ( lvhti.flags & LVHT_ONITEM )
2256                    {
2257                        eventType = wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK;
2258                        event.m_itemIndex = lvhti.iItem;
2259                        event.m_pointDrag.x = lvhti.pt.x;
2260                        event.m_pointDrag.y = lvhti.pt.y;
2261                    }
2262                }
2263                break;
2264
2265#ifdef NM_CUSTOMDRAW
2266            case NM_CUSTOMDRAW:
2267                *result = OnCustomDraw(lParam);
2268
2269                return *result != CDRF_DODEFAULT;
2270#endif // _WIN32_IE >= 0x300
2271
2272            case LVN_ODCACHEHINT:
2273                {
2274                    const NM_CACHEHINT *cacheHint = (NM_CACHEHINT *)lParam;
2275
2276                    eventType = wxEVT_COMMAND_LIST_CACHE_HINT;
2277
2278                    // we get some really stupid cache hints like ones for
2279                    // items in range 0..0 for an empty control or, after
2280                    // deleting an item, for items in invalid range -- filter
2281                    // this garbage out
2282                    if ( cacheHint->iFrom > cacheHint->iTo )
2283                        return false;
2284
2285                    event.m_oldItemIndex = cacheHint->iFrom;
2286
2287                    const long iMax = GetItemCount();
2288                    event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo
2289                                                              : iMax - 1;
2290                }
2291                break;
2292
2293#ifdef HAVE_NMLVFINDITEM
2294            case LVN_ODFINDITEM:
2295                // this message is only used with the virtual list control but
2296                // even there we don't want to always use it: in a control with
2297                // sufficiently big number of items (defined as > 1000 here),
2298                // accidentally pressing a key could result in hanging an
2299                // application waiting while it performs linear search
2300                if ( IsVirtual() && GetItemCount() <= 1000 )
2301                {
2302                    NMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)lParam;
2303
2304                    // no match by default
2305                    *result = -1;
2306
2307                    // we only handle string-based searches here
2308                    //
2309                    // TODO: what about LVFI_PARTIAL, should we handle this?
2310                    if ( !(pFindInfo->lvfi.flags & LVFI_STRING) )
2311                    {
2312                        return false;
2313                    }
2314
2315                    const wxChar * const searchstr = pFindInfo->lvfi.psz;
2316                    const size_t len = wxStrlen(searchstr);
2317
2318                    // this is the first item we should examine, search from it
2319                    // wrapping if necessary
2320                    const int startPos = pFindInfo->iStart;
2321                    const int maxPos = GetItemCount();
2322                    wxCHECK_MSG( startPos <= maxPos, false,
2323                                 _T("bad starting position in LVN_ODFINDITEM") );
2324
2325                    int currentPos = startPos;
2326                    do
2327                    {
2328                        // wrap to the beginning if necessary
2329                        if ( currentPos == maxPos )
2330                        {
2331                            // somewhat surprizingly, LVFI_WRAP isn't set in
2332                            // flags but we still should wrap
2333                            currentPos = 0;
2334                        }
2335
2336                        // does this item begin with searchstr?
2337                        if ( wxStrnicmp(searchstr,
2338                                            GetItemText(currentPos), len) == 0 )
2339                        {
2340                            *result = currentPos;
2341                            break;
2342                        }
2343                    }
2344                    while ( ++currentPos != startPos );
2345
2346                    if ( *result == -1 )
2347                    {
2348                        // not found
2349                        return false;
2350                    }
2351
2352                    SetItemState(*result,
2353                                 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
2354                                 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
2355                    EnsureVisible(*result);
2356                    return true;
2357                }
2358                else
2359                {
2360                    processed = false;
2361                }
2362                break;
2363#endif // HAVE_NMLVFINDITEM
2364
2365            case LVN_GETDISPINFO:
2366                if ( IsVirtual() )
2367                {
2368                    LV_DISPINFO *info = (LV_DISPINFO *)lParam;
2369
2370                    LV_ITEM& lvi = info->item;
2371                    long item = lvi.iItem;
2372
2373                    if ( lvi.mask & LVIF_TEXT )
2374                    {
2375                        wxString text = OnGetItemText(item, lvi.iSubItem);
2376                        wxStrncpy(lvi.pszText, text, lvi.cchTextMax - 1);
2377                        lvi.pszText[lvi.cchTextMax - 1] = _T('\0');
2378                    }
2379
2380                    // see comment at the end of wxListCtrl::GetColumn()
2381#ifdef NM_CUSTOMDRAW
2382                    if ( lvi.mask & LVIF_IMAGE )
2383                    {
2384                        lvi.iImage = OnGetItemColumnImage(item, lvi.iSubItem);
2385                    }
2386#endif // NM_CUSTOMDRAW
2387
2388                    // even though we never use LVM_SETCALLBACKMASK, we still
2389                    // can get messages with LVIF_STATE in lvi.mask under Vista
2390                    if ( lvi.mask & LVIF_STATE )
2391                    {
2392                        // we don't have anything to return from here...
2393                        lvi.stateMask = 0;
2394                    }
2395
2396                    return true;
2397                }
2398                // fall through
2399
2400            default:
2401                processed = false;
2402        }
2403
2404        if ( !processed )
2405            return wxControl::MSWOnNotify(idCtrl, lParam, result);
2406    }
2407    else
2408    {
2409        // where did this one come from?
2410        return false;
2411    }
2412
2413    // process the event
2414    // -----------------
2415
2416    event.SetEventType(eventType);
2417
2418    bool processed = GetEventHandler()->ProcessEvent(event);
2419
2420    // post processing
2421    // ---------------
2422    switch ( nmhdr->code )
2423    {
2424        case LVN_DELETEALLITEMS:
2425            // always return true to suppress all additional LVN_DELETEITEM
2426            // notifications - this makes deleting all items from a list ctrl
2427            // much faster
2428            *result = TRUE;
2429
2430            // also, we may free all user data now (couldn't do it before as
2431            // the user should have access to it in OnDeleteAllItems() handler)
2432            FreeAllInternalData();
2433
2434            // the control is empty now, synchronize the cached number of items
2435            // with the real one
2436            m_count = 0;
2437            return true;
2438
2439        case LVN_ENDLABELEDITA:
2440        case LVN_ENDLABELEDITW:
2441            // logic here is inverted compared to all the other messages
2442            *result = event.IsAllowed();
2443
2444            // don't keep a stale wxTextCtrl around
2445            if ( m_textCtrl )
2446            {
2447                // EDIT control will be deleted by the list control itself so
2448                // prevent us from deleting it as well
2449                m_textCtrl->UnsubclassWin();
2450                m_textCtrl->SetHWND(0);
2451                delete m_textCtrl;
2452                m_textCtrl = NULL;
2453            }
2454
2455            return true;
2456    }
2457
2458    if ( processed )
2459        *result = !event.IsAllowed();
2460
2461    return processed;
2462}
2463
2464// ----------------------------------------------------------------------------
2465// custom draw stuff
2466// ----------------------------------------------------------------------------
2467
2468// see comment at the end of wxListCtrl::GetColumn()
2469#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
2470
2471#if defined(__VISUALC__) && __VISUALC__ >= 1400 && __VISUALC__ < 1500
2472// Turn off optimizations for this function to avoid an ICE in the MSVC8
2473// 64-bit compiler, observed with the compiler included with the PDSK-2003.
2474// If there is a better way to test for this please do.
2475#pragma optimize( "", off )
2476#endif
2477static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
2478{
2479    RECT rc;
2480    ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rc, LVIR_BOUNDS);
2481
2482    RECT rcIcon;
2483    ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rcIcon, LVIR_ICON);
2484
2485    // exclude the icon part, neither the selection background nor focus rect
2486    // should cover it
2487    rc.left = rcIcon.right;
2488
2489    return rc;
2490}
2491
2492#if defined(__VISUALC__) && __VISUALC__ >= 1400 && __VISUALC__ < 1500
2493// Reset optimizations to their former setting
2494#pragma optimize( "", on )
2495#endif
2496
2497
2498static
2499bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
2500{
2501    NMCUSTOMDRAW& nmcd = pLVCD->nmcd;
2502
2503    HDC hdc = nmcd.hdc;
2504    HWND hwndList = nmcd.hdr.hwndFrom;
2505    const int col = pLVCD->iSubItem;
2506    const DWORD item = nmcd.dwItemSpec;
2507
2508    // the font must be valid, otherwise we wouldn't be painting the item at all
2509    SelectInHDC selFont(hdc, hfont);
2510
2511    // get the rectangle to paint
2512    RECT rc;
2513    ListView_GetSubItemRect(hwndList, item, col, LVIR_BOUNDS, &rc);
2514    if ( !col && colCount > 1 )
2515    {
2516        // broken ListView_GetSubItemRect() returns the entire item rect for
2517        // 0th subitem while we really need just the part for this column
2518        RECT rc2;
2519        ListView_GetSubItemRect(hwndList, item, 1, LVIR_BOUNDS, &rc2);
2520
2521        rc.right = rc2.left;
2522        rc.left += 4;
2523    }
2524    else // not first subitem
2525    {
2526        rc.left += 6;
2527    }
2528
2529    // get the image and text to draw
2530    wxChar text[512];
2531    LV_ITEM it;
2532    wxZeroMemory(it);
2533    it.mask = LVIF_TEXT | LVIF_IMAGE;
2534    it.iItem = item;
2535    it.iSubItem = col;
2536    it.pszText = text;
2537    it.cchTextMax = WXSIZEOF(text);
2538    ListView_GetItem(hwndList, &it);
2539
2540    HIMAGELIST himl = ListView_GetImageList(hwndList, LVSIL_SMALL);
2541    if ( himl && ImageList_GetImageCount(himl) )
2542    {
2543        if ( it.iImage != -1 )
2544        {
2545            ImageList_Draw(himl, it.iImage, hdc, rc.left, rc.top,
2546                           nmcd.uItemState & CDIS_SELECTED ? ILD_SELECTED
2547                                                           : ILD_TRANSPARENT);
2548        }
2549
2550        // notice that even if this item doesn't have any image, the list
2551        // control still leaves space for the image in the first column if the
2552        // image list is not empty (presumably so that items with and without
2553        // images align?)
2554        if ( it.iImage != -1 || it.iSubItem == 0 )
2555        {
2556            int wImage, hImage;
2557            ImageList_GetIconSize(himl, &wImage, &hImage);
2558
2559            rc.left += wImage + 2;
2560        }
2561    }
2562
2563    ::SetBkMode(hdc, TRANSPARENT);
2564
2565    UINT fmt = DT_SINGLELINE |
2566#ifndef __WXWINCE__
2567               DT_WORD_ELLIPSIS |
2568#endif // __WXWINCE__
2569               DT_NOPREFIX |
2570               DT_VCENTER;
2571
2572    LV_COLUMN lvCol;
2573    wxZeroMemory(lvCol);
2574    lvCol.mask = LVCF_FMT;
2575    if ( ListView_GetColumn(hwndList, col, &lvCol) )
2576    {
2577        switch ( lvCol.fmt & LVCFMT_JUSTIFYMASK )
2578        {
2579            case LVCFMT_LEFT:
2580                fmt |= DT_LEFT;
2581                break;
2582
2583            case LVCFMT_CENTER:
2584                fmt |= DT_CENTER;
2585                break;
2586
2587            case LVCFMT_RIGHT:
2588                fmt |= DT_RIGHT;
2589                break;
2590        }
2591    }
2592    //else: failed to get alignment, assume it's DT_LEFT (default)
2593
2594    DrawText(hdc, text, -1, &rc, fmt);
2595
2596    return true;
2597}
2598
2599static void HandleItemPostpaint(NMCUSTOMDRAW nmcd)
2600{
2601    if ( nmcd.uItemState & CDIS_FOCUS )
2602    {
2603        RECT rc = GetCustomDrawnItemRect(nmcd);
2604
2605        // don't use the provided HDC, it's in some strange state by now
2606        ::DrawFocusRect(WindowHDC(nmcd.hdr.hwndFrom), &rc);
2607    }
2608}
2609
2610// pLVCD->clrText and clrTextBk should contain the colours to use
2611static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
2612{
2613    NMCUSTOMDRAW& nmcd = pLVCD->nmcd; // just a shortcut
2614
2615    const HWND hwndList = nmcd.hdr.hwndFrom;
2616    const int item = nmcd.dwItemSpec;
2617
2618    // unfortunately we can't trust CDIS_SELECTED, it is often set even when
2619    // the item is not at all selected for some reason (comctl32 6), but we
2620    // also can't always trust ListView_GetItem() as it could return the old
2621    // item status if we're called just after the (de)selection, so remember
2622    // the last item to gain selection and also check for it here
2623    for ( int i = -1;; )
2624    {
2625        i = ListView_GetNextItem(hwndList, i, LVNI_SELECTED);
2626        if ( i == -1 )
2627        {
2628            nmcd.uItemState &= ~CDIS_SELECTED;
2629            break;
2630        }
2631
2632        if ( i == item )
2633        {
2634            nmcd.uItemState |= CDIS_SELECTED;
2635            break;
2636        }
2637    }
2638
2639    // same thing for CDIS_FOCUS (except simpler as there is only one of them)
2640    if ( ::GetFocus() == hwndList &&
2641            ListView_GetNextItem(hwndList, (WPARAM)-1, LVNI_FOCUSED) == item )
2642    {
2643        nmcd.uItemState |= CDIS_FOCUS;
2644    }
2645    else
2646    {
2647        nmcd.uItemState &= ~CDIS_FOCUS;
2648    }
2649
2650    if ( nmcd.uItemState & CDIS_SELECTED )
2651    {
2652        int syscolFg, syscolBg;
2653        if ( ::GetFocus() == hwndList )
2654        {
2655            syscolFg = COLOR_HIGHLIGHTTEXT;
2656            syscolBg = COLOR_HIGHLIGHT;
2657        }
2658        else // selected but unfocused
2659        {
2660            syscolFg = COLOR_WINDOWTEXT;
2661            syscolBg = COLOR_BTNFACE;
2662
2663            // don't grey out the icon in this case neither
2664            nmcd.uItemState &= ~CDIS_SELECTED;
2665        }
2666
2667        pLVCD->clrText = ::GetSysColor(syscolFg);
2668        pLVCD->clrTextBk = ::GetSysColor(syscolBg);
2669    }
2670    //else: not selected, use normal colours from pLVCD
2671
2672    HDC hdc = nmcd.hdc;
2673    RECT rc = GetCustomDrawnItemRect(nmcd);
2674
2675    ::SetTextColor(hdc, pLVCD->clrText);
2676    ::FillRect(hdc, &rc, AutoHBRUSH(pLVCD->clrTextBk));
2677
2678    // we could use CDRF_NOTIFYSUBITEMDRAW here but it results in weird repaint
2679    // problems so just draw everything except the focus rect from here instead
2680    const int colCount = Header_GetItemCount(ListView_GetHeader(hwndList));
2681    for ( int col = 0; col < colCount; col++ )
2682    {
2683        pLVCD->iSubItem = col;
2684        HandleSubItemPrepaint(pLVCD, hfont, colCount);
2685    }
2686
2687    HandleItemPostpaint(nmcd);
2688}
2689
2690static WXLPARAM HandleItemPrepaint(wxListCtrl *listctrl,
2691                                   LPNMLVCUSTOMDRAW pLVCD,
2692                                   wxListItemAttr *attr)
2693{
2694    if ( !attr )
2695    {
2696        // nothing to do for this item
2697        return CDRF_DODEFAULT;
2698    }
2699
2700
2701    // set the colours to use for text drawing
2702    pLVCD->clrText = attr->HasTextColour()
2703                     ? wxColourToRGB(attr->GetTextColour())
2704                     : wxColourToRGB(listctrl->GetTextColour());
2705    pLVCD->clrTextBk = attr->HasBackgroundColour()
2706                       ? wxColourToRGB(attr->GetBackgroundColour())
2707                       : wxColourToRGB(listctrl->GetBackgroundColour());
2708
2709    // select the font if non default one is specified
2710    if ( attr->HasFont() )
2711    {
2712        wxFont font = attr->GetFont();
2713        if ( font.GetEncoding() != wxFONTENCODING_SYSTEM )
2714        {
2715            // the standard control ignores the font encoding/charset, at least
2716            // with recent comctl32.dll versions (5 and 6, it uses to work with
2717            // 4.something) so we have to draw the item entirely ourselves in
2718            // this case
2719            HandleItemPaint(pLVCD, GetHfontOf(font));
2720            return CDRF_SKIPDEFAULT;
2721        }
2722
2723        ::SelectObject(pLVCD->nmcd.hdc, GetHfontOf(font));
2724
2725        return CDRF_NEWFONT;
2726    }
2727
2728    return CDRF_DODEFAULT;
2729}
2730
2731WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
2732{
2733    LPNMLVCUSTOMDRAW pLVCD = (LPNMLVCUSTOMDRAW)lParam;
2734    NMCUSTOMDRAW& nmcd = pLVCD->nmcd;
2735    switch ( nmcd.dwDrawStage )
2736    {
2737        case CDDS_PREPAINT:
2738            // if we've got any items with non standard attributes,
2739            // notify us before painting each item
2740            //
2741            // for virtual controls, always suppose that we have attributes as
2742            // there is no way to check for this
2743            if ( IsVirtual() || m_hasAnyAttr )
2744                return CDRF_NOTIFYITEMDRAW;
2745            break;
2746
2747        case CDDS_ITEMPREPAINT:
2748            const int item = nmcd.dwItemSpec;
2749
2750            // we get this message with item == 0 for an empty control, we
2751            // must ignore it as calling OnGetItemAttr() would be wrong
2752            if ( item < 0 || item >= GetItemCount() )
2753                break;
2754
2755            return HandleItemPrepaint(this, pLVCD, DoGetItemAttr(item));
2756    }
2757
2758    return CDRF_DODEFAULT;
2759}
2760
2761#endif // NM_CUSTOMDRAW supported
2762
2763// Necessary for drawing hrules and vrules, if specified
2764void wxListCtrl::OnPaint(wxPaintEvent& event)
2765{
2766    bool drawHRules = HasFlag(wxLC_HRULES);
2767    bool drawVRules = HasFlag(wxLC_VRULES);
2768
2769    if (!InReportView() || !drawHRules && !drawVRules)
2770    {
2771        event.Skip();
2772        return;
2773    }
2774
2775    wxPaintDC dc(this);
2776
2777    wxControl::OnPaint(event);
2778
2779    // Reset the device origin since it may have been set
2780    dc.SetDeviceOrigin(0, 0);
2781
2782    wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
2783    dc.SetPen(pen);
2784    dc.SetBrush(* wxTRANSPARENT_BRUSH);
2785
2786    wxSize clientSize = GetClientSize();
2787    wxRect itemRect;
2788
2789    int itemCount = GetItemCount();
2790    int i;
2791    if (drawHRules)
2792    {
2793        long top = GetTopItem();
2794        for (i = top; i < top + GetCountPerPage() + 1; i++)
2795        {
2796            if (GetItemRect(i, itemRect))
2797            {
2798                int cy = itemRect.GetTop();
2799                if (i != 0) // Don't draw the first one
2800                {
2801                    dc.DrawLine(0, cy, clientSize.x, cy);
2802                }
2803                // Draw last line
2804                if (i == itemCount - 1)
2805                {
2806                    cy = itemRect.GetBottom();
2807                    dc.DrawLine(0, cy, clientSize.x, cy);
2808                }
2809            }
2810        }
2811    }
2812    i = itemCount - 1;
2813    if (drawVRules && (i > -1))
2814    {
2815        wxRect firstItemRect;
2816        GetItemRect(0, firstItemRect);
2817
2818        if (GetItemRect(i, itemRect))
2819        {
2820            // this is a fix for bug 673394: erase the pixels which we would
2821            // otherwise leave on the screen
2822            static const int gap = 2;
2823            dc.SetPen(*wxTRANSPARENT_PEN);
2824            dc.SetBrush(wxBrush(GetBackgroundColour()));
2825            dc.DrawRectangle(0, firstItemRect.GetY() - gap,
2826                             clientSize.GetWidth(), gap);
2827
2828            dc.SetPen(pen);
2829            dc.SetBrush(*wxTRANSPARENT_BRUSH);
2830            int x = itemRect.GetX();
2831            for (int col = 0; col < GetColumnCount(); col++)
2832            {
2833                int colWidth = GetColumnWidth(col);
2834                x += colWidth ;
2835                dc.DrawLine(x-1, firstItemRect.GetY() - gap,
2836                            x-1, itemRect.GetBottom());
2837            }
2838        }
2839    }
2840}
2841
2842WXLRESULT
2843wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2844{
2845    switch ( nMsg )
2846    {
2847#ifdef WM_PRINT
2848        case WM_PRINT:
2849            // we should bypass our own WM_PRINT handling as we don't handle
2850            // PRF_CHILDREN flag, so leave it to the native control itself
2851            return MSWDefWindowProc(nMsg, wParam, lParam);
2852#endif // WM_PRINT
2853
2854        case WM_CONTEXTMENU:
2855            // because this message is propagated upwards the child-parent
2856            // chain, we get it for the right clicks on the header window but
2857            // this is confusing in wx as right clicking there already
2858            // generates a separate wxEVT_COMMAND_LIST_COL_RIGHT_CLICK event
2859            // so just ignore them
2860            if ( (HWND)wParam == ListView_GetHeader(GetHwnd()) )
2861                return 0;
2862            //else: break
2863    }
2864
2865    return wxControl::MSWWindowProc(nMsg, wParam, lParam);
2866}
2867
2868// ----------------------------------------------------------------------------
2869// virtual list controls
2870// ----------------------------------------------------------------------------
2871
2872wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const
2873{
2874    // this is a pure virtual function, in fact - which is not really pure
2875    // because the controls which are not virtual don't need to implement it
2876    wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") );
2877
2878    return wxEmptyString;
2879}
2880
2881int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const
2882{
2883    wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
2884                -1,
2885                wxT("List control has an image list, OnGetItemImage or OnGetItemColumnImage should be overridden."));
2886    return -1;
2887}
2888
2889int wxListCtrl::OnGetItemColumnImage(long item, long column) const
2890{
2891    if (!column)
2892        return OnGetItemImage(item);
2893
2894    return -1;
2895}
2896
2897wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
2898{
2899    wxASSERT_MSG( item >= 0 && item < GetItemCount(),
2900                  _T("invalid item index in OnGetItemAttr()") );
2901
2902    // no attributes by default
2903    return NULL;
2904}
2905
2906wxListItemAttr *wxListCtrl::DoGetItemAttr(long item) const
2907{
2908    return IsVirtual() ? OnGetItemAttr(item)
2909                       : wxGetInternalDataAttr(this, item);
2910}
2911
2912void wxListCtrl::SetItemCount(long count)
2913{
2914    wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
2915
2916    if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count,
2917                        LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL) )
2918    {
2919        wxLogLastError(_T("ListView_SetItemCount"));
2920    }
2921    m_count = count;
2922    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
2923                  wxT("m_count should match ListView_GetItemCount"));
2924}
2925
2926void wxListCtrl::RefreshItem(long item)
2927{
2928    // strangely enough, ListView_Update() results in much more flicker here
2929    // than a dumb Refresh() -- why?
2930#if 0
2931    if ( !ListView_Update(GetHwnd(), item) )
2932    {
2933        wxLogLastError(_T("ListView_Update"));
2934    }
2935#else // 1
2936    wxRect rect;
2937    GetItemRect(item, rect);
2938    RefreshRect(rect);
2939#endif // 0/1
2940}
2941
2942void wxListCtrl::RefreshItems(long itemFrom, long itemTo)
2943{
2944    wxRect rect1, rect2;
2945    GetItemRect(itemFrom, rect1);
2946    GetItemRect(itemTo, rect2);
2947
2948    wxRect rect = rect1;
2949    rect.height = rect2.GetBottom() - rect1.GetTop();
2950
2951    RefreshRect(rect);
2952}
2953
2954// ----------------------------------------------------------------------------
2955// internal data stuff
2956// ----------------------------------------------------------------------------
2957
2958static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId)
2959{
2960    LV_ITEM it;
2961    it.mask = LVIF_PARAM;
2962    it.iItem = itemId;
2963
2964    if ( !ListView_GetItem(hwnd, &it) )
2965        return NULL;
2966
2967    return (wxListItemInternalData *) it.lParam;
2968}
2969
2970static
2971wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId)
2972{
2973    return wxGetInternalData(GetHwndOf(ctl), itemId);
2974}
2975
2976static
2977wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId)
2978{
2979    wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
2980
2981    return data ? data->attr : NULL;
2982}
2983
2984static void wxDeleteInternalData(wxListCtrl* ctl, long itemId)
2985{
2986    wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
2987    if (data)
2988    {
2989        LV_ITEM item;
2990        memset(&item, 0, sizeof(item));
2991        item.iItem = itemId;
2992        item.mask = LVIF_PARAM;
2993        item.lParam = (LPARAM) 0;
2994        ListView_SetItem((HWND)ctl->GetHWND(), &item);
2995        delete data;
2996    }
2997}
2998
2999// ----------------------------------------------------------------------------
3000// wxWin <-> MSW items conversions
3001// ----------------------------------------------------------------------------
3002
3003static void wxConvertFromMSWListItem(HWND hwndListCtrl,
3004                                     wxListItem& info,
3005                                     LV_ITEM& lvItem)
3006{
3007    wxListItemInternalData *internaldata =
3008        (wxListItemInternalData *) lvItem.lParam;
3009
3010    if (internaldata)
3011        info.m_data = internaldata->lParam;
3012
3013    info.m_mask = 0;
3014    info.m_state = 0;
3015    info.m_stateMask = 0;
3016    info.m_itemId = lvItem.iItem;
3017
3018    long oldMask = lvItem.mask;
3019
3020    bool needText = false;
3021    if (hwndListCtrl != 0)
3022    {
3023        if ( lvItem.mask & LVIF_TEXT )
3024            needText = false;
3025        else
3026            needText = true;
3027
3028        if ( needText )
3029        {
3030            lvItem.pszText = new wxChar[513];
3031            lvItem.cchTextMax = 512;
3032        }
3033        lvItem.mask |= LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3034        ::SendMessage(hwndListCtrl, LVM_GETITEM, 0, (LPARAM)& lvItem);
3035    }
3036
3037    if ( lvItem.mask & LVIF_STATE )
3038    {
3039        info.m_mask |= wxLIST_MASK_STATE;
3040
3041        if ( lvItem.stateMask & LVIS_CUT)
3042        {
3043            info.m_stateMask |= wxLIST_STATE_CUT;
3044            if ( lvItem.state & LVIS_CUT )
3045                info.m_state |= wxLIST_STATE_CUT;
3046        }
3047        if ( lvItem.stateMask & LVIS_DROPHILITED)
3048        {
3049            info.m_stateMask |= wxLIST_STATE_DROPHILITED;
3050            if ( lvItem.state & LVIS_DROPHILITED )
3051                info.m_state |= wxLIST_STATE_DROPHILITED;
3052        }
3053        if ( lvItem.stateMask & LVIS_FOCUSED)
3054        {
3055            info.m_stateMask |= wxLIST_STATE_FOCUSED;
3056            if ( lvItem.state & LVIS_FOCUSED )
3057                info.m_state |= wxLIST_STATE_FOCUSED;
3058        }
3059        if ( lvItem.stateMask & LVIS_SELECTED)
3060        {
3061            info.m_stateMask |= wxLIST_STATE_SELECTED;
3062            if ( lvItem.state & LVIS_SELECTED )
3063                info.m_state |= wxLIST_STATE_SELECTED;
3064        }
3065    }
3066
3067    if ( lvItem.mask & LVIF_TEXT )
3068    {
3069        info.m_mask |= wxLIST_MASK_TEXT;
3070        info.m_text = lvItem.pszText;
3071    }
3072    if ( lvItem.mask & LVIF_IMAGE )
3073    {
3074        info.m_mask |= wxLIST_MASK_IMAGE;
3075        info.m_image = lvItem.iImage;
3076    }
3077    if ( lvItem.mask & LVIF_PARAM )
3078        info.m_mask |= wxLIST_MASK_DATA;
3079    if ( lvItem.mask & LVIF_DI_SETITEM )
3080        info.m_mask |= wxLIST_SET_ITEM;
3081    info.m_col = lvItem.iSubItem;
3082
3083    if (needText)
3084    {
3085        if (lvItem.pszText)
3086            delete[] lvItem.pszText;
3087    }
3088    lvItem.mask = oldMask;
3089}
3090
3091static void wxConvertToMSWFlags(long state, long stateMask, LV_ITEM& lvItem)
3092{
3093    if (stateMask & wxLIST_STATE_CUT)
3094    {
3095        lvItem.stateMask |= LVIS_CUT;
3096        if (state & wxLIST_STATE_CUT)
3097            lvItem.state |= LVIS_CUT;
3098    }
3099    if (stateMask & wxLIST_STATE_DROPHILITED)
3100    {
3101        lvItem.stateMask |= LVIS_DROPHILITED;
3102        if (state & wxLIST_STATE_DROPHILITED)
3103            lvItem.state |= LVIS_DROPHILITED;
3104    }
3105    if (stateMask & wxLIST_STATE_FOCUSED)
3106    {
3107        lvItem.stateMask |= LVIS_FOCUSED;
3108        if (state & wxLIST_STATE_FOCUSED)
3109            lvItem.state |= LVIS_FOCUSED;
3110    }
3111    if (stateMask & wxLIST_STATE_SELECTED)
3112    {
3113        lvItem.stateMask |= LVIS_SELECTED;
3114        if (state & wxLIST_STATE_SELECTED)
3115            lvItem.state |= LVIS_SELECTED;
3116    }
3117}
3118
3119static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
3120                                   const wxListItem& info,
3121                                   LV_ITEM& lvItem)
3122{
3123    lvItem.iItem = (int) info.m_itemId;
3124
3125    lvItem.iImage = info.m_image;
3126    lvItem.stateMask = 0;
3127    lvItem.state = 0;
3128    lvItem.mask = 0;
3129    lvItem.iSubItem = info.m_col;
3130
3131    if (info.m_mask & wxLIST_MASK_STATE)
3132    {
3133        lvItem.mask |= LVIF_STATE;
3134
3135        wxConvertToMSWFlags(info.m_state, info.m_stateMask, lvItem);
3136    }
3137
3138    if (info.m_mask & wxLIST_MASK_TEXT)
3139    {
3140        lvItem.mask |= LVIF_TEXT;
3141        if ( ctrl->HasFlag(wxLC_USER_TEXT) )
3142        {
3143            lvItem.pszText = LPSTR_TEXTCALLBACK;
3144        }
3145        else
3146        {
3147            // pszText is not const, hence the cast
3148            lvItem.pszText = (wxChar *)info.m_text.c_str();
3149            if ( lvItem.pszText )
3150                lvItem.cchTextMax = info.m_text.length();
3151            else
3152                lvItem.cchTextMax = 0;
3153        }
3154    }
3155    if (info.m_mask & wxLIST_MASK_IMAGE)
3156        lvItem.mask |= LVIF_IMAGE;
3157}
3158
3159static void wxConvertToMSWListCol(HWND hwndList,
3160                                  int col,
3161                                  const wxListItem& item,
3162                                  LV_COLUMN& lvCol)
3163{
3164    wxZeroMemory(lvCol);
3165
3166    if ( item.m_mask & wxLIST_MASK_TEXT )
3167    {
3168        lvCol.mask |= LVCF_TEXT;
3169        lvCol.pszText = (wxChar *)item.m_text.c_str(); // cast is safe
3170    }
3171
3172    if ( item.m_mask & wxLIST_MASK_FORMAT )
3173    {
3174        lvCol.mask |= LVCF_FMT;
3175
3176        if ( item.m_format == wxLIST_FORMAT_LEFT )
3177            lvCol.fmt = LVCFMT_LEFT;
3178        else if ( item.m_format == wxLIST_FORMAT_RIGHT )
3179            lvCol.fmt = LVCFMT_RIGHT;
3180        else if ( item.m_format == wxLIST_FORMAT_CENTRE )
3181            lvCol.fmt = LVCFMT_CENTER;
3182    }
3183
3184    if ( item.m_mask & wxLIST_MASK_WIDTH )
3185    {
3186        lvCol.mask |= LVCF_WIDTH;
3187        if ( item.m_width == wxLIST_AUTOSIZE)
3188            lvCol.cx = LVSCW_AUTOSIZE;
3189        else if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER)
3190            lvCol.cx = LVSCW_AUTOSIZE_USEHEADER;
3191        else
3192            lvCol.cx = item.m_width;
3193    }
3194
3195    // see comment at the end of wxListCtrl::GetColumn()
3196#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
3197    if ( item.m_mask & wxLIST_MASK_IMAGE )
3198    {
3199        if ( wxApp::GetComCtl32Version() >= 470 )
3200        {
3201            lvCol.mask |= LVCF_IMAGE;
3202
3203            // we use LVCFMT_BITMAP_ON_RIGHT because the images on the right
3204            // seem to be generally nicer than on the left and the generic
3205            // version only draws them on the right (we don't have a flag to
3206            // specify the image location anyhow)
3207            //
3208            // we don't use LVCFMT_COL_HAS_IMAGES because it doesn't seem to
3209            // make any difference in my tests -- but maybe we should?
3210            if ( item.m_image != -1 )
3211            {
3212                // as we're going to overwrite the format field, get its
3213                // current value first -- unless we want to overwrite it anyhow
3214                if ( !(lvCol.mask & LVCF_FMT) )
3215                {
3216                    LV_COLUMN lvColOld;
3217                    wxZeroMemory(lvColOld);
3218                    lvColOld.mask = LVCF_FMT;
3219                    if ( ListView_GetColumn(hwndList, col, &lvColOld) )
3220                    {
3221                        lvCol.fmt = lvColOld.fmt;
3222                    }
3223
3224                    lvCol.mask |= LVCF_FMT;
3225                }
3226
3227                lvCol.fmt |= LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE;
3228            }
3229
3230            lvCol.iImage = item.m_image;
3231        }
3232        //else: it doesn't support item images anyhow
3233    }
3234#endif // _WIN32_IE >= 0x0300
3235}
3236
3237#endif // wxUSE_LISTCTRL
3238