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