X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b720b86bfb26d2ae18f3ecccf3262f2538217192..763163a80ae0d5a0684633e72f928302578c1214:/src/msw/listctrl.cpp?ds=sidebyside diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index a32c136696..9e23d1db3f 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -39,6 +39,7 @@ #endif #include "wx/imaglist.h" +#include "wx/vector.h" #include "wx/msw/private.h" @@ -82,6 +83,33 @@ static void wxConvertToMSWListCol(HWND hwndList, const wxListItem& item, LV_COLUMN& lvCol); +namespace +{ + +// replacement for ListView_GetSubItemRect() which provokes warnings like +// "the address of 'rc' will always evaluate as 'true'" when used with mingw32 +// 4.3+ +// +// this function does no error checking on item and subitem parameters, notice +// that subitem 0 means the whole item so there is no way to retrieve the +// rectangle of the first subitem using this function, in particular notice +// that the index is *not* 1-based, in spite of what MSDN says +inline bool +wxGetListCtrlSubItemRect(HWND hwnd, int item, int subitem, int flags, RECT& rect) +{ + rect.top = subitem; + rect.left = flags; + return ::SendMessage(hwnd, LVM_GETSUBITEMRECT, item, (LPARAM)&rect) != 0; +} + +inline bool +wxGetListCtrlItemRect(HWND hwnd, int item, int flags, RECT& rect) +{ + return wxGetListCtrlSubItemRect(hwnd, item, 0, flags, rect); +} + +} // anonymous namespace + // ---------------------------------------------------------------------------- // private helper classes // ---------------------------------------------------------------------------- @@ -353,7 +381,11 @@ void wxListCtrl::MSWSetExListStyles() ::SendMessage ( GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE, 0, + // LVS_EX_LABELTIP shouldn't be used under Windows CE where it's + // not defined in the SDK headers +#ifdef LVS_EX_LABELTIP LVS_EX_LABELTIP | +#endif LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | // normally this should be governed by a style as it's probably not @@ -484,10 +516,8 @@ void wxListCtrl::FreeAllInternalData() } } -wxListCtrl::~wxListCtrl() +void wxListCtrl::DeleteEditControl() { - FreeAllInternalData(); - if ( m_textCtrl ) { m_textCtrl->UnsubclassWin(); @@ -495,6 +525,13 @@ wxListCtrl::~wxListCtrl() delete m_textCtrl; m_textCtrl = NULL; } +} + +wxListCtrl::~wxListCtrl() +{ + FreeAllInternalData(); + + DeleteEditControl(); if (m_ownsImageListNormal) delete m_imageListNormal; @@ -537,7 +574,7 @@ void wxListCtrl::SetWindowStyleFlag(long flag) { if ( flag != m_windowStyle ) { - m_windowStyle = flag; + wxControl::SetWindowStyleFlag(flag); UpdateStyle(); @@ -687,33 +724,32 @@ bool wxListCtrl::SetColumnWidth(int col, int width) // columns order // ---------------------------------------------------------------------------- -int wxListCtrl::GetColumnOrder(int col) const +int wxListCtrl::GetColumnIndexFromOrder(int order) const { const int numCols = GetColumnCount(); - wxCHECK_MSG( col >= 0 && col < numCols, -1, _T("Col index out of bounds") ); + wxCHECK_MSG( order >= 0 && order < numCols, -1, + _T("Column position out of bounds") ); wxArrayInt indexArray(numCols); - if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) ) return -1; - return indexArray[col]; + return indexArray[order]; } -int wxListCtrl::GetColumnIndexFromOrder(int order) const +int wxListCtrl::GetColumnOrder(int col) const { const int numCols = GetColumnCount(); - wxASSERT_MSG( order >= 0 && order < numCols, _T("Col order out of bounds") ); + wxASSERT_MSG( col >= 0 && col < numCols, _T("Column index out of bounds") ); wxArrayInt indexArray(numCols); - if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) ) return -1; - for ( int col = 0; col < numCols; col++ ) + for ( int pos = 0; pos < numCols; pos++ ) { - if ( indexArray[col] == order ) - return col; + if ( indexArray[pos] == col ) + return pos; } wxFAIL_MSG( _T("no column with with given order?") ); @@ -757,6 +793,25 @@ int wxListCtrl::GetCountPerPage() const // Gets the edit control for editing labels. wxTextCtrl* wxListCtrl::GetEditControl() const { + // first check corresponds to the case when the label editing was started + // by user and hence m_textCtrl wasn't created by EditLabel() at all, while + // the second case corresponds to us being called from inside EditLabel() + // (e.g. from a user wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT handler): in this + // case EditLabel() did create the control but it didn't have an HWND to + // initialize it with yet + if ( !m_textCtrl || !m_textCtrl->GetHWND() ) + { + HWND hwndEdit = ListView_GetEditControl(GetHwnd()); + if ( hwndEdit ) + { + wxListCtrl * const self = const_cast(this); + + if ( !m_textCtrl ) + self->m_textCtrl = new wxTextCtrl; + self->InitEditControl((WXHWND)hwndEdit); + } + } + return m_textCtrl; } @@ -1054,19 +1109,40 @@ bool wxListCtrl::SetItemPtrData(long item, wxUIntPtr data) wxRect wxListCtrl::GetViewRect() const { - wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST), - _T("wxListCtrl::GetViewRect() only works in icon mode") ); + wxRect rect; - RECT rc; - if ( !ListView_GetViewRect(GetHwnd(), &rc) ) + // ListView_GetViewRect() can only be used in icon and small icon views + // (this is documented in MSDN and, indeed, it returns bogus results in + // report view, at least with comctl32.dll v6 under Windows 2003) + if ( HasFlag(wxLC_ICON | wxLC_SMALL_ICON) ) { - wxLogDebug(_T("ListView_GetViewRect() failed.")); + RECT rc; + if ( !ListView_GetViewRect(GetHwnd(), &rc) ) + { + wxLogDebug(_T("ListView_GetViewRect() failed.")); + + wxZeroMemory(rc); + } - wxZeroMemory(rc); + wxCopyRECTToRect(rc, rect); } + else if ( HasFlag(wxLC_REPORT) ) + { + const long count = GetItemCount(); + if ( count ) + { + GetItemRect(wxMin(GetTopItem() + GetCountPerPage(), count - 1), rect); - wxRect rect; - wxCopyRECTToRect(rc, rect); + // extend the rectangle to start at the top (we include the column + // headers, if any, for compatibility with the generic version) + rect.height += rect.y; + rect.y = 0; + } + } + else + { + wxFAIL_MSG( _T("not implemented in this mode") ); + } return rect; } @@ -1077,22 +1153,14 @@ bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const return GetSubItemRect( item, wxLIST_GETSUBITEMRECT_WHOLEITEM, rect, code) ; } -/*! - * Retrieve coordinates and size of a specified subitem of a listview control. - * This function only works if the listview control is in the report mode. - * - * @param item : Item number - * @param subItem : Subitem or column number, use -1 for the whole row including - * all columns or subitems - * @param rect : A pointer to an allocated wxRect object - * @param code : Specify the part of the subitem coordinates you need. Choices are - * wxLIST_RECT_BOUNDS, wxLIST_RECT_ICON, wxLIST_RECT_LABEL - * - * @return bool : True if successful. - */ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) const { - RECT rectWin; + // ListView_GetSubItemRect() doesn't do subItem error checking and returns + // true even for the out of range values of it (even if the results are + // completely bogus in this case), so we check item validity ourselves + wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM || + (subItem >= 0 && subItem < GetColumnCount()), + false, _T("invalid sub item index") ); int codeWin; if ( code == wxLIST_RECT_BOUNDS ) @@ -1107,27 +1175,28 @@ bool wxListCtrl::GetSubItemRect(long item, long subItem, wxRect& rect, int code) codeWin = LVIR_BOUNDS; } - bool success; - if( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM) - { - success = ListView_GetItemRect(GetHwnd(), (int) item, &rectWin, codeWin) != 0; - } - else if( subItem >= 0) - { - success = ListView_GetSubItemRect( GetHwnd(), (int) item, (int) subItem, codeWin, &rectWin) != 0; - } - else + RECT rectWin; + if ( !wxGetListCtrlSubItemRect + ( + GetHwnd(), + item, + subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ? 0 : subItem, + codeWin, + rectWin + ) ) { - wxFAIL_MSG( _T("incorrect subItem number in GetSubItemRect()") ); - return false; + return false; } - rect.x = rectWin.left; - rect.y = rectWin.top; - rect.width = rectWin.right - rectWin.left; - rect.height = rectWin.bottom - rectWin.top; + wxCopyRECTToRect(rectWin, rect); - return success; + // there is no way to retrieve the first sub item bounding rectangle using + // wxGetListCtrlSubItemRect() as 0 means the whole item, so we need to + // truncate it at first column ourselves + if ( subItem == 0 ) + rect.width = GetColumnWidth(0); + + return true; } @@ -1446,38 +1515,49 @@ void wxListCtrl::ClearAll() DeleteAllColumns(); } +void wxListCtrl::InitEditControl(WXHWND hWnd) +{ + m_textCtrl->SetHWND(hWnd); + m_textCtrl->SubclassWin(hWnd); + m_textCtrl->SetParent(this); + + // we must disallow TABbing away from the control while the edit contol is + // shown because this leaves it in some strange state (just try removing + // this line and then pressing TAB while editing an item in listctrl + // inside a panel) + m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() | wxTE_PROCESS_TAB); +} + wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass) { - wxASSERT( (textControlClass->IsKindOf(CLASSINFO(wxTextCtrl))) ); + wxCHECK_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)), NULL, + "control used for label editing must be a wxTextCtrl" ); // ListView_EditLabel requires that the list has focus. SetFocus(); + // create m_textCtrl here before calling ListView_EditLabel() because it + // generates wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT event from inside it and + // the user handler for it can call GetEditControl() resulting in an on + // demand creation of a stock wxTextCtrl instead of the control of a + // (possibly) custom wxClassInfo + DeleteEditControl(); + m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); + WXHWND hWnd = (WXHWND) ListView_EditLabel(GetHwnd(), item); if ( !hWnd ) { // failed to start editing - return NULL; - } - - // [re]create the text control wrapping the HWND we got - if ( m_textCtrl ) - { - m_textCtrl->UnsubclassWin(); - m_textCtrl->SetHWND(0); delete m_textCtrl; - } + m_textCtrl = NULL; - m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); - m_textCtrl->SetHWND(hWnd); - m_textCtrl->SubclassWin(hWnd); - m_textCtrl->SetParent(this); + return NULL; + } - // we must disallow TABbing away from the control while the edit contol is - // shown because this leaves it in some strange state (just try removing - // this line and then pressing TAB while editing an item in listctrl - // inside a panel) - m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle() | wxTE_PROCESS_TAB); + // if GetEditControl() hasn't been called, we need to initialize the edit + // control ourselves + if ( !m_textCtrl->GetHWND() ) + InitEditControl(hWnd); return m_textCtrl; } @@ -1863,15 +1943,12 @@ bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id_) // utility used by wxListCtrl::MSWOnNotify and by wxDataViewHeaderWindowMSW::MSWOnNotify int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick) { - wxASSERT(nmhdr && ptClick); - - // find the column clicked: we have to search for it - // ourselves as the notification message doesn't provide - // this info + // find the column clicked: we have to search for it ourselves as the + // notification message doesn't provide this info // where did the click occur? #if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400 - if (nmhdr->code == GN_CONTEXTMENU) + if ( nmhdr->code == GN_CONTEXTMENU ) { *ptClick = ((NMRGINFO*)nmhdr)->ptAction; } @@ -1882,19 +1959,27 @@ int WXDLLIMPEXP_CORE wxMSWGetColumnClicked(NMHDR *nmhdr, POINT *ptClick) wxLogLastError(_T("GetCursorPos")); } - if ( !::ScreenToClient(nmhdr->hwndFrom, ptClick) ) + // we need to use listctrl coordinates for the event point so this is what + // we return in ptClick, but for comparison with Header_GetItemRect() + // result below we need to use header window coordinates + POINT ptClickHeader = *ptClick; + if ( !::ScreenToClient(nmhdr->hwndFrom, &ptClickHeader) ) { - wxLogLastError(_T("ScreenToClient(header)")); + wxLogLastError(_T("ScreenToClient(listctrl header)")); } - int colCount = Header_GetItemCount(nmhdr->hwndFrom); + if ( !::ScreenToClient(::GetParent(nmhdr->hwndFrom), ptClick) ) + { + wxLogLastError(_T("ScreenToClient(listctrl)")); + } - RECT rect; + const int colCount = Header_GetItemCount(nmhdr->hwndFrom); for ( int col = 0; col < colCount; col++ ) { + RECT rect; if ( Header_GetItemRect(nmhdr->hwndFrom, col, &rect) ) { - if ( ::PtInRect(&rect, *ptClick) ) + if ( ::PtInRect(&rect, ptClickHeader) ) { return col; } @@ -2084,16 +2169,9 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) const LV_ITEM& lvi = (LV_ITEM)item; if ( !lvi.pszText || lvi.iItem == -1 ) { - // don't keep a stale wxTextCtrl around - if ( m_textCtrl ) - { - // EDIT control will be deleted by the list control itself so - // prevent us from deleting it as well - m_textCtrl->UnsubclassWin(); - m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; - } + // EDIT control will be deleted by the list control + // itself so prevent us from deleting it as well + DeleteEditControl(); event.SetEditCanceled(true); } @@ -2202,11 +2280,11 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxLIST_STATE_SELECTED); // or activate the selected item if any (but - // not with Shift and/or Ctrl as then they have a predefined - // meaning for the list view) + // not with any modifiers as they have a predefined meaning + // then) if ( lItem != -1 && (wVKey == VK_RETURN || wVKey == VK_SPACE) && - !(wxIsShiftDown() || wxIsCtrlDown()) ) + !wxIsAnyModifierDown() ) { eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED; } @@ -2399,7 +2477,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( lvi.mask & LVIF_TEXT ) { wxString text = OnGetItemText(item, lvi.iSubItem); - wxStrncpy(lvi.pszText, text, lvi.cchTextMax); + wxStrlcpy(lvi.pszText, text.c_str(), lvi.cchTextMax); } // see comment at the end of wxListCtrl::GetColumn() @@ -2466,16 +2544,9 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // logic here is inverted compared to all the other messages *result = event.IsAllowed(); - // don't keep a stale wxTextCtrl around - if ( m_textCtrl ) - { - // EDIT control will be deleted by the list control itself so - // prevent us from deleting it as well - m_textCtrl->UnsubclassWin(); - m_textCtrl->SetHWND(0); - delete m_textCtrl; - m_textCtrl = NULL; - } + // EDIT control will be deleted by the list control itself so + // prevent us from deleting it as well + DeleteEditControl(); return true; } @@ -2496,10 +2567,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd) { RECT rc; - ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rc, LVIR_BOUNDS); + wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_BOUNDS, rc); RECT rcIcon; - ListView_GetItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, &rcIcon, LVIR_ICON); + wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_ICON, rcIcon); // exclude the icon part, neither the selection background nor focus rect // should cover it @@ -2522,22 +2593,10 @@ bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount) SelectInHDC selFont(hdc, hfont); // get the rectangle to paint + int subitem = colCount ? col + 1 : col; RECT rc; - ListView_GetSubItemRect(hwndList, item, col, LVIR_BOUNDS, &rc); - if ( !col && colCount > 1 ) - { - // broken ListView_GetSubItemRect() returns the entire item rect for - // 0th subitem while we really need just the part for this column - RECT rc2; - ListView_GetSubItemRect(hwndList, item, 1, LVIR_BOUNDS, &rc2); - - rc.right = rc2.left; - rc.left += 4; - } - else // not first subitem - { - rc.left += 6; - } + wxGetListCtrlSubItemRect(hwndList, item, subitem, LVIR_BOUNDS, rc); + rc.left += 6; // get the image and text to draw wxChar text[512]; @@ -2651,7 +2710,7 @@ static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont) // same thing for CDIS_FOCUS (except simpler as there is only one of them) if ( ::GetFocus() == hwndList && - ListView_GetNextItem(hwndList, (WPARAM)-1, LVNI_FOCUSED) == item ) + ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED) == item ) { nmcd.uItemState |= CDIS_FOCUS; } @@ -2776,10 +2835,10 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam) // Necessary for drawing hrules and vrules, if specified void wxListCtrl::OnPaint(wxPaintEvent& event) { - bool drawHRules = HasFlag(wxLC_HRULES); - bool drawVRules = HasFlag(wxLC_VRULES); + const bool drawHRules = HasFlag(wxLC_HRULES); + const bool drawVRules = HasFlag(wxLC_VRULES); - if (!InReportView() || !drawHRules && !drawVRules) + if (!InReportView() || !(drawHRules || drawVRules)) { event.Skip(); return; @@ -2792,7 +2851,7 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) // Reset the device origin since it may have been set dc.SetDeviceOrigin(0, 0); - wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID); + wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT)); dc.SetPen(pen); dc.SetBrush(* wxTRANSPARENT_BRUSH); @@ -2841,11 +2900,14 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.SetPen(pen); dc.SetBrush(*wxTRANSPARENT_BRUSH); - int numCols = GetColumnCount(); - int* indexArray = new int[numCols]; - if ( !ListView_GetColumnOrderArray( GetHwnd(), numCols, indexArray) ) + const int numCols = GetColumnCount(); + wxVector indexArray(numCols); + if ( !ListView_GetColumnOrderArray(GetHwnd(), + numCols, + &indexArray[0]) ) { wxFAIL_MSG( _T("invalid column index array in OnPaint()") ); + return; } int x = itemRect.GetX(); @@ -2856,8 +2918,6 @@ void wxListCtrl::OnPaint(wxPaintEvent& event) dc.DrawLine(x-1, firstItemRect.GetY() - gap, x-1, itemRect.GetBottom()); } - - delete indexArray; } } }