/[pcsx2_0.9.7]/branch/r3113_0.9.7_beta/3rdparty/wxWidgets/src/msw/listctrl.cpp
ViewVC logotype

Contents of /branch/r3113_0.9.7_beta/3rdparty/wxWidgets/src/msw/listctrl.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations) (download)
Tue Sep 7 03:29:01 2010 UTC (9 years, 11 months ago) by william
File size: 98260 byte(s)
branching from upstream revision (http://pcsx2.googlecode.com/svn/trunk
): r3113 to
https://svn.netsolutions.dnsalias.com/websvn/ps2/pcsx2/pcsx2_0.9.7/branch/r3113_0.9.7_beta
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
68 static void wxConvertToMSWFlags(long state, long mask, LV_ITEM& lvItem);
69
70 // convert wxListItem to LV_ITEM
71 static void wxConvertToMSWListItem(const wxListCtrl *ctrl,
72 const wxListItem& info, LV_ITEM& lvItem);
73
74 // convert LV_ITEM to wxListItem
75 static void wxConvertFromMSWListItem(HWND hwndListCtrl,
76 wxListItem& info,
77 /* const */ LV_ITEM& lvItem);
78
79 // convert our wxListItem to LV_COLUMN
80 static 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
108 class wxLV_ITEM
109 {
110 public:
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
156 private:
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
197 class wxListItemInternalData
198 {
199 public:
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
214 static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId);
215 static wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
216 static wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId);
217 static void wxDeleteInternalData(wxListCtrl* ctl, long itemId);
218
219
220 #if wxUSE_EXTENDED_RTTI
221 WX_DEFINE_FLAGS( wxListCtrlStyle )
222
223 wxBEGIN_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
266 wxEND_FLAGS( wxListCtrlStyle )
267
268 IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h")
269
270 wxBEGIN_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
274 wxEND_PROPERTIES_TABLE()
275
276 wxBEGIN_HANDLERS_TABLE(wxListCtrl)
277 wxEND_HANDLERS_TABLE()
278
279 wxCONSTRUCTOR_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
285 IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
286 #endif
287
288 IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
289 IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
290
291 IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent)
292
293 BEGIN_EVENT_TABLE(wxListCtrl, wxControl)
294 EVT_PAINT(wxListCtrl::OnPaint)
295 END_EVENT_TABLE()
296
297 // ============================================================================
298 // implementation
299 // ============================================================================
300
301 // ----------------------------------------------------------------------------
302 // wxListCtrl construction
303 // ----------------------------------------------------------------------------
304
305 void 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
319 bool 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
347 void 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
358 WXDWORD 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
432 void 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
463 void 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
478 void 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
489 wxListCtrl::~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
508 void 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
532 void 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
549 wxListCtrl::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
560 bool 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
571 bool 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
586 bool 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
654 bool 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
663 int wxListCtrl::GetColumnWidth(int col) const
664 {
665 return ListView_GetColumnWidth(GetHwnd(), col);
666 }
667
668 // Sets the column width
669 bool 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)
686 int wxListCtrl::GetCountPerPage() const
687 {
688 return ListView_GetCountPerPage(GetHwnd());
689 }
690
691 // Gets the edit control for editing labels.
692 wxTextCtrl* 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
717 bool 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
767 bool 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
849 long 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
866 int 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
881 bool 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
929 bool wxListCtrl::SetItemImage(long item, int image, int WXUNUSED(selImage))
930 {
931 return SetItemColumnImage(item, 0, image);
932 }
933
934 // Sets the item image
935 bool 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
948 wxString 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
961 void 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
973 wxUIntPtr 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
986 bool 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
997 bool wxListCtrl::SetItemData(long item, long data)
998 {
999 return SetItemPtrData(item, data);
1000 }
1001
1002 wxRect 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
1022 bool 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 */
1040 bool 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
1084 bool 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.
1095 bool 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
1101 int wxListCtrl::GetItemCount() const
1102 {
1103 return m_count;
1104 }
1105
1106 wxSize 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
1115 int wxListCtrl::GetItemSpacing(bool isSmall) const
1116 {
1117 return ListView_GetItemSpacing(GetHwnd(), (BOOL) isSmall);
1118 }
1119
1120 #endif // WXWIN_COMPATIBILITY_2_6
1121
1122 void 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
1130 wxColour 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
1140 void 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
1148 wxColour 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
1158 void 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
1166 wxFont 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
1177 int wxListCtrl::GetSelectedItemCount() const
1178 {
1179 return ListView_GetSelectedCount(GetHwnd());
1180 }
1181
1182 // Gets the text colour of the listview
1183 wxColour 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
1191 void 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
1198 long 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.
1211 long 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
1239 wxImageList *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
1256 void 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
1283 void 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
1299 bool 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
1315 bool 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
1353 bool wxListCtrl::DeleteAllItems()
1354 {
1355 return ListView_DeleteAllItems(GetHwnd()) != 0;
1356 }
1357
1358 // Deletes all items
1359 bool 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
1379 bool 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.
1389 void wxListCtrl::ClearAll()
1390 {
1391 DeleteAllItems();
1392 if ( m_colCount > 0 )
1393 DeleteAllColumns();
1394 }
1395
1396 void 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
1409 wxTextCtrl* 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
1444 bool 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
1464 bool 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.
1471 long 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
1493 long 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.
1510 long 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)
1533 long
1534 wxListCtrl::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.
1592 long 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
1634 long 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
1644 long 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
1654 long 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.
1665 long 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
1692 long 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)
1712 bool 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
1740 struct wxInternalDataSort
1741 {
1742 wxListCtrlCompare user_fn;
1743 long data;
1744 };
1745
1746 int 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
1760 bool 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
1785 bool 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
1803 bool 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
1823 bool 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
2477 static 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
2498 static
2499 bool 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
2599 static 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
2611 static 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
2690 static 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
2731 WXLPARAM 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
2764 void 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
2842 WXLRESULT
2843 wxListCtrl::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
2872 wxString 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
2881 int 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
2889 int wxListCtrl::OnGetItemColumnImage(long item, long column) const
2890 {
2891 if (!column)
2892 return OnGetItemImage(item);
2893
2894 return -1;
2895 }
2896
2897 wxListItemAttr *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
2906 wxListItemAttr *wxListCtrl::DoGetItemAttr(long item) const
2907 {
2908 return IsVirtual() ? OnGetItemAttr(item)
2909 : wxGetInternalDataAttr(this, item);
2910 }
2911
2912 void 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
2926 void 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
2942 void 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
2958 static 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
2970 static
2971 wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId)
2972 {
2973 return wxGetInternalData(GetHwndOf(ctl), itemId);
2974 }
2975
2976 static
2977 wxListItemAttr *wxGetInternalDataAttr(const wxListCtrl *ctl, long itemId)
2978 {
2979 wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
2980
2981 return data ? data->attr : NULL;
2982 }
2983
2984 static 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
3003 static 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
3091 static 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
3119 static 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
3159 static 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

  ViewVC Help
Powered by ViewVC 1.1.22