X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5d12911287cc904447883e389f22ea0a8eda0742..abd474ea63667f727940a009cc3e0b23ba9f418f:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 3804bf0e6f..09354910b6 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -25,15 +25,6 @@ #include "wx/listctrl.h" -#if ((!defined(__WXMSW__) && !(defined(__WXMAC__) && wxOSX_USE_CARBON)) || defined(__WXUNIVERSAL__)) - // if we have a native version, its implementation file does all this - IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) - IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) - IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) - - IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl) -#endif - #ifndef WX_PRECOMP #include "wx/scrolwin.h" #include "wx/timer.h" @@ -667,11 +658,20 @@ void wxListLineData::SetAttr(wxListItemAttr *attr) item->SetAttr(attr); } -bool wxListLineData::SetAttributes(wxDC *dc, - const wxListItemAttr *attr, - bool highlighted) +void wxListLineData::ApplyAttributes(wxDC *dc, + const wxRect& rectHL, + bool highlighted, + bool current) { - wxWindow *listctrl = m_owner->GetParent(); + const wxListItemAttr * const attr = GetAttr(); + + wxWindow * const listctrl = m_owner->GetParent(); + + const bool hasFocus = listctrl->HasFocus() +#if defined(__WXMAC__) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON + && IsControlActive( (ControlRef)listctrl->GetHandle() ) +#endif + ; // fg colour @@ -680,20 +680,19 @@ bool wxListLineData::SetAttributes(wxDC *dc, // arithmetics on wxColour, unfortunately) wxColour colText; if ( highlighted ) -#ifdef __WXMAC__ { - if (m_owner->HasFocus() -#if !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON - && IsControlActive( (ControlRef)m_owner->GetHandle() ) -#endif - ) +#ifdef __WXMAC__ + if ( hasFocus ) colText = *wxWHITE; else colText = *wxBLACK; - } #else - colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + if ( hasFocus ) + colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + else + colText = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT); #endif + } else if ( attr && attr->HasTextColour() ) colText = attr->GetTextColour(); else @@ -710,57 +709,26 @@ bool wxListLineData::SetAttributes(wxDC *dc, dc->SetFont(font); - // bg colour - bool hasBgCol = attr && attr->HasBackgroundColour(); - if ( highlighted || hasBgCol ) + // background + if ( highlighted ) { - if ( highlighted ) - dc->SetBrush( *m_owner->GetHighlightBrush() ); - else - dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); - - dc->SetPen( *wxTRANSPARENT_PEN ); - - return true; + // Use the renderer method to ensure that the selected items use the + // native look. + int flags = wxCONTROL_SELECTED; + if ( hasFocus ) + flags |= wxCONTROL_FOCUSED; + if (current) + flags |= wxCONTROL_CURRENT; + wxRendererNative::Get(). + DrawItemSelectionRect( m_owner, *dc, rectHL, flags ); } - - return false; -} - -void wxListLineData::Draw( wxDC *dc ) -{ - wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); - wxCHECK_RET( node, wxT("no subitems at all??") ); - - bool highlighted = IsHighlighted(); - - wxListItemAttr *attr = GetAttr(); - - if ( SetAttributes(dc, attr, highlighted) ) -#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) ) + else if ( attr && attr->HasBackgroundColour() ) { - dc->DrawRectangle( m_gi->m_rectHighlight ); + // Draw the background using the items custom background colour. + dc->SetBrush(attr->GetBackgroundColour()); + dc->SetPen(*wxTRANSPARENT_PEN); + dc->DrawRectangle(rectHL); } -#else - { - if (highlighted) - { - int flags = wxCONTROL_SELECTED; - if (m_owner->HasFocus() -#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON - && IsControlActive( (ControlRef)m_owner->GetHandle() ) -#endif - ) - flags |= wxCONTROL_FOCUSED; - wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, m_gi->m_rectHighlight, flags ); - - } - else - { - dc->DrawRectangle( m_gi->m_rectHighlight ); - } - } -#endif // just for debugging to better see where the items are #if 0 @@ -770,6 +738,14 @@ void wxListLineData::Draw( wxDC *dc ) dc->SetPen(*wxGREEN_PEN); dc->DrawRectangle( m_gi->m_rectIcon ); #endif +} + +void wxListLineData::Draw(wxDC *dc, bool current) +{ + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); + wxCHECK_RET( node, wxT("no subitems at all??") ); + + ApplyAttributes(dc, m_gi->m_rectHighlight, IsHighlighted(), current); wxListItemData *item = node->GetData(); if (item->HasImage()) @@ -799,31 +775,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc, // TODO: later we should support setting different attributes for // different columns - to do it, just add "col" argument to // GetAttr() and move these lines into the loop below - wxListItemAttr *attr = GetAttr(); - if ( SetAttributes(dc, attr, highlighted) ) -#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) ) - { - dc->DrawRectangle( rectHL ); - wxUnusedVar(current); - } -#else - { - if (highlighted) - { - int flags = wxCONTROL_SELECTED; - if (m_owner->HasFocus()) - flags |= wxCONTROL_FOCUSED; - if (current) - flags |= wxCONTROL_CURRENT; - wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, rectHL, flags ); - } - else - { - dc->DrawRectangle( rectHL ); - } - } -#endif + ApplyAttributes(dc, rectHL, highlighted, current); wxCoord x = rect.x + HEADER_OFFSET_X, yMid = rect.y + rect.height/2; @@ -845,7 +798,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc, int xOld = x; x += width; - const int wText = width - 8; + width -= 8; + const int wText = width; wxDCClipper clipper(*dc, xOld, rect.y, wText, rect.height); if ( item->HasImage() ) @@ -861,7 +815,7 @@ void wxListLineData::DrawInReportMode( wxDC *dc, } if ( item->HasText() ) - DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, wText); + DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, width); } } @@ -1092,7 +1046,7 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) // NB: The code below is not really Mac-specific, but since we are close // to 2.8 release and I don't have time to test on other platforms, I -// defined this only for wxMac. If this behavior is desired on +// defined this only for wxMac. If this behaviour is desired on // other platforms, please go ahead and revise or remove the #ifdef. #ifdef __WXMAC__ if ( !m_owner->IsVirtual() && (item.m_mask & wxLIST_MASK_STATE) && @@ -1160,7 +1114,7 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) // draw the text and image clipping them so that they // don't overwrite the column boundary - wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 ); + wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h); // if we have an image, draw it on the right of the label if ( imageList ) @@ -1170,13 +1124,13 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) image, dc, xAligned + wLabel - ix - HEADER_IMAGE_MARGIN_IN_REPORT_MODE, - HEADER_OFFSET_Y + (h - 4 - iy)/2, + HEADER_OFFSET_Y + (h - iy)/2, wxIMAGELIST_DRAW_TRANSPARENT ); } dc.DrawText( item.GetText(), - xAligned + EXTRA_WIDTH, h / 2 - hLabel / 2 ); //HEADER_OFFSET_Y + EXTRA_HEIGHT ); + xAligned + EXTRA_WIDTH, (h - hLabel) / 2 ); x += wCol; } @@ -1448,23 +1402,31 @@ wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner, m_text->PushEventHandler(this); } -void wxListTextCtrlWrapper::EndEdit(bool discardChanges) +void wxListTextCtrlWrapper::EndEdit(EndReason reason) { m_aboutToFinish = true; - if ( discardChanges ) + switch ( reason ) { - m_owner->OnRenameCancelled(m_itemEdited); + case End_Accept: + // Notify the owner about the changes + AcceptChanges(); - Finish( true ); - } - else - { - // Notify the owner about the changes - AcceptChanges(); + // Even if vetoed, close the control (consistent with MSW) + Finish( true ); + break; - // Even if vetoed, close the control (consistent with MSW) - Finish( true ); + case End_Discard: + m_owner->OnRenameCancelled(m_itemEdited); + + Finish( true ); + break; + + case End_Destroy: + // Don't generate any notifications for the control being destroyed + // and don't set focus to it neither. + Finish(false); + break; } } @@ -1503,11 +1465,11 @@ void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event ) switch ( event.m_keyCode ) { case WXK_RETURN: - EndEdit( false ); + EndEdit( End_Accept ); break; case WXK_ESCAPE: - EndEdit( true ); + EndEdit( End_Discard ); break; default: @@ -1641,6 +1603,9 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, wxListMainWindow::~wxListMainWindow() { + if ( m_textctrlWrapper ) + m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Destroy); + DoDeleteAllItems(); WX_CLEAR_LIST(wxListHeaderDataList, m_columns); WX_CLEAR_ARRAY(m_aColWidths); @@ -1650,6 +1615,15 @@ wxListMainWindow::~wxListMainWindow() delete m_renameTimer; } +void wxListMainWindow::SetReportView(bool inReportView) +{ + const size_t count = m_lines.size(); + for ( size_t n = 0; n < count; n++ ) + { + m_lines[n].SetReportView(inReportView); + } +} + void wxListMainWindow::CacheLineData(size_t line) { wxGenericListCtrl *listctrl = GetListCtrl(); @@ -2125,24 +2099,24 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) size_t count = GetItemCount(); for ( size_t i = 0; i < count; i++ ) { - GetLine(i)->Draw( &dc ); + GetLine(i)->Draw( &dc, i == m_current ); } } -#if !defined( __WXMAC__) && !defined(__WXGTK20__) - // Don't draw rect outline under Mac at all. - // Draw it elsewhere under GTK. + // DrawFocusRect() is unusable under Mac, it draws outside of the highlight + // rectangle somehow and so leaves traces when the item is not selected any + // more, see #12229. +#ifndef __WXMAC__ if ( HasCurrent() ) { - if ( m_hasFocus ) - { - wxRect rect( GetLineHighlightRect( m_current ) ); - dc.SetPen( *wxBLACK_PEN ); - dc.SetBrush( *wxTRANSPARENT_BRUSH ); - dc.DrawRectangle( rect ); - } + int flags = 0; + if ( IsHighlighted(m_current) ) + flags |= wxCONTROL_SELECTED; + + wxRendererNative::Get(). + DrawFocusRect(this, dc, GetLineHighlightRect(m_current), flags); } -#endif +#endif // !__WXMAC__ } void wxListMainWindow::HighlightAll( bool on ) @@ -2305,7 +2279,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) // listctrl because the order of events is different (or something like // that), so explicitly end the edit if it is active. if ( event.LeftDown() && m_textctrlWrapper ) - m_textctrlWrapper->EndEdit( false ); + m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Accept); #endif // __WXMAC__ if ( event.LeftDown() ) @@ -2714,6 +2688,16 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event ) if (parent->GetEventHandler()->ProcessEvent( ke )) return; + // send a list event + wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, parent->GetId() ); + le.m_itemIndex = m_current; + if (HasCurrent()) + GetLine(m_current)->GetItem( 0, le.m_item ); + le.m_code = event.GetKeyCode(); + le.SetEventObject( parent ); + if (parent->GetEventHandler()->ProcessEvent( le )) + return; + event.Skip(); } @@ -2733,32 +2717,11 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) { wxWindow *parent = GetParent(); - // send a list_key event up - if ( HasCurrent() ) - { - wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetParent()->GetId() ); - le.m_itemIndex = m_current; - GetLine(m_current)->GetItem( 0, le.m_item ); - le.m_code = event.GetKeyCode(); - le.SetEventObject( parent ); - parent->GetEventHandler()->ProcessEvent( le ); - } - - if ( (event.GetKeyCode() != WXK_UP) && - (event.GetKeyCode() != WXK_DOWN) && - (event.GetKeyCode() != WXK_RIGHT) && - (event.GetKeyCode() != WXK_LEFT) && - (event.GetKeyCode() != WXK_PAGEUP) && - (event.GetKeyCode() != WXK_PAGEDOWN) && - (event.GetKeyCode() != WXK_END) && - (event.GetKeyCode() != WXK_HOME) ) - { - // propagate the char event upwards - wxKeyEvent ke(event); - ke.SetEventObject( parent ); - if (parent->GetEventHandler()->ProcessEvent( ke )) - return; - } + // propagate the char event upwards + wxKeyEvent ke(event); + ke.SetEventObject( parent ); + if (parent->GetEventHandler()->ProcessEvent( ke )) + return; if ( HandleAsNavigationKey(event) ) return; @@ -4050,8 +4013,11 @@ void wxListMainWindow::InsertItem( wxListItem &item ) { ResetVisibleLinesRange(); + const unsigned col = item.GetColumn(); + wxCHECK_RET( col < m_aColWidths.size(), "invalid item column" ); + // calculate the width of the item and adjust the max column width - wxColWidthInfo *pWidthInfo = m_aColWidths.Item(item.GetColumn()); + wxColWidthInfo *pWidthInfo = m_aColWidths.Item(col); int width = GetItemWidthWithImage(&item); item.SetWidth(width); if (width > pWidthInfo->nMaxWidth) @@ -4061,6 +4027,19 @@ void wxListMainWindow::InsertItem( wxListItem &item ) wxListLineData *line = new wxListLineData(this); line->SetItem( item.m_col, item ); + if ( item.m_mask & wxLIST_MASK_IMAGE && item.GetImage() != -1) + { + // Reset the buffered height if it's not big enough for the new image. + if (m_small_image_list) + { + int imageWidth, imageHeight; + m_small_image_list->GetSize(item.GetImage(), + imageWidth, imageHeight); + + if ( imageHeight > m_lineHeight ) + m_lineHeight = 0; + } + } m_lines.Insert( line, id ); @@ -4307,8 +4286,7 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded() ); #if defined( __WXMAC__ ) - wxFont font; - font.CreateSystemFont( wxOSX_SYSTEM_FONT_SMALL ); + static wxFont font( wxOSX_SYSTEM_FONT_SMALL ); m_headerWin->SetFont( font ); #endif @@ -4318,9 +4296,7 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded() { GetSizer()->Detach( m_headerWin ); - delete m_headerWin; - - m_headerWin = NULL; + wxDELETE(m_headerWin); } } @@ -4350,6 +4326,11 @@ bool wxGenericListCtrl::Create(wxWindow *parent, SetTargetWindow( m_mainWin ); + // We use the cursor keys for moving the selection, not scrolling, so call + // this method to ensure wxScrollHelperEvtHandler doesn't catch all + // keyboard events forwarded to us from wxListMainWindow. + DisableKeyboardScrolling(); + wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL ); sizer->Add( m_mainWin, 1, wxGROW ); SetSizer( sizer ); @@ -4444,12 +4425,27 @@ void wxGenericListCtrl::SetSingleStyle( long style, bool add ) void wxGenericListCtrl::SetWindowStyleFlag( long flag ) { + // we add wxHSCROLL and wxVSCROLL in ctor unconditionally and it never + // makes sense to remove them as we'll always add scrollbars anyhow when + // needed + flag |= wxHSCROLL | wxVSCROLL; + + const bool wasInReportView = HasFlag(wxLC_REPORT); + // update the window style first so that the header is created or destroyed // corresponding to the new style wxWindow::SetWindowStyleFlag( flag ); if (m_mainWin) { + const bool inReportView = (flag & wxLC_REPORT) != 0; + if ( inReportView != wasInReportView ) + { + // we need to notify the main window about this change as it must + // update its data structures + m_mainWin->SetReportView(inReportView); + } + // m_mainWin->DeleteEverything(); wxMSW doesn't do that CreateOrDestroyHeaderWindowAsNeeded(); @@ -4544,9 +4540,9 @@ wxGenericListCtrl::SetItemColumnImage( long item, long column, int image ) return true; } -wxString wxGenericListCtrl::GetItemText( long item ) const +wxString wxGenericListCtrl::GetItemText( long item, int col ) const { - return m_mainWin->GetItemText(item); + return m_mainWin->GetItemText(item, col); } void wxGenericListCtrl::SetItemText( long item, const wxString& str ) @@ -4870,7 +4866,9 @@ long wxGenericListCtrl::InsertItem( long index, const wxString &label, int image wxListItem info; info.m_text = label; info.m_image = imageIndex; - info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE; + info.m_mask = wxLIST_MASK_TEXT; + if (imageIndex > -1) + info.m_mask |= wxLIST_MASK_IMAGE; info.m_itemId = index; return InsertItem( info ); } @@ -5072,12 +5070,22 @@ bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const { - m_mainWin->DoClientToScreen(x, y); + // It's not clear whether this can be called before m_mainWin is created + // but it seems better to be on the safe side and check. + if ( m_mainWin ) + m_mainWin->DoClientToScreen(x, y); + else + wxControl::DoClientToScreen(x, y); } void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const { - m_mainWin->DoScreenToClient(x, y); + // At least in wxGTK/Univ build this method can be called before m_mainWin + // is created so avoid crashes in this case. + if ( m_mainWin ) + m_mainWin->DoScreenToClient(x, y); + else + wxControl::DoScreenToClient(x, y); } void wxGenericListCtrl::SetFocus() @@ -5088,12 +5096,63 @@ void wxGenericListCtrl::SetFocus() m_mainWin->SetFocus(); } -wxSize wxGenericListCtrl::DoGetBestSize() const +wxSize wxGenericListCtrl::DoGetBestClientSize() const { - // Something is better than nothing... - // 100x80 is what the MSW version will get from the default - // wxControl::DoGetBestSize - return wxSize(100, 80); + // Something is better than nothing even if this is completely arbitrary. + wxSize sizeBest(100, 80); + + if ( !InReportView() ) + { + // Ensure that our minimal width is at least big enough to show all our + // items. This is important for wxListbook to size itself correctly. + + // Remember the offset of the first item: this corresponds to the + // margins around the item so we will add it to the minimal size below + // to ensure that we have equal margins on all sides. + wxPoint ofs; + + // We can iterate over all items as there shouldn't be too many of them + // in non-report view. If it ever becomes a problem, we could examine + // just the first few items probably, the determination of the best + // size is less important if we will need scrollbars anyhow. + for ( int n = 0; n < GetItemCount(); n++ ) + { + const wxRect itemRect = m_mainWin->GetLineRect(n); + if ( !n ) + { + // Remember the position of the first item as all the rest are + // offset by at least this number of pixels too. + ofs = itemRect.GetPosition(); + } + + sizeBest.IncTo(itemRect.GetSize()); + } + + sizeBest.IncBy(2*ofs); + + + // If we have the scrollbars we need to account for them too. And to + // make sure the scrollbars status is up to date we need to call this + // function to set them. + m_mainWin->RecalculatePositions(true /* no refresh */); + + // Unfortunately we can't use wxWindow::HasScrollbar() here as we need + // to use m_mainWin client/virtual size for determination of whether we + // use scrollbars and not the size of this window itself. Maybe that + // function should be extended to work correctly in the case when our + // scrollbars manage a different window from this one but currently it + // doesn't work. + const wxSize sizeClient = m_mainWin->GetClientSize(); + const wxSize sizeVirt = m_mainWin->GetVirtualSize(); + + if ( sizeVirt.x > sizeClient.x /* HasScrollbar(wxHORIZONTAL) */ ) + sizeBest.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + + if ( sizeVirt.y > sizeClient.y /* HasScrollbar(wxVERTICAL) */ ) + sizeBest.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + } + + return sizeBest; } // ----------------------------------------------------------------------------