X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/ff3d11a0ab976879ff87c826f660786fee172ed9..9f39393d9efe16244c8d213076582dfd996cb129:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 8ea0eada4e..93b43f1529 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -8,16 +8,6 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -/* - FIXME for virtual list controls - - +1. clicking on the item with a mouse is awfully slow, what is going on? - note that selecting with keyboard seems to be much faster - => fixed HighlightAll() - iterating over 1000000 items *is* slow - - 2. background colour is wrong? - */ - /* TODO for better virtual list control support: @@ -175,7 +165,14 @@ public: bool SelectItem(size_t item, bool select = TRUE); // select the range of items - void SelectRange(size_t itemFrom, size_t itemTo, bool select = TRUE); + // + // return true and fill the itemsChanged array with the indices of items + // which have changed state if "few" of them did, otherwise return false + // (meaning that too many items changed state to bother counting them + // individually) + bool SelectRange(size_t itemFrom, size_t itemTo, + bool select = TRUE, + wxArrayInt *itemsChanged = NULL); // return true if the given item is selected bool IsSelected(size_t item) const; @@ -417,10 +414,10 @@ private: // get the mode (i.e. style) of the list control inline int GetMode() const; - void SetAttributes(wxDC *dc, + // prepare the DC for drawing with these item's attributes, return true if + // we need to draw the items background to highlight it, false otherwise + bool SetAttributes(wxDC *dc, const wxListItemAttr *attr, - const wxColour& colText, - const wxFont& font, bool highlight); // these are only used by GetImage/SetImage above, we don't support images @@ -616,12 +613,17 @@ public: // return the hit code for the corresponding position (in this line) long HitTestLine(size_t line, int x, int y) const; + // bring the selected item into view, scrolling to it if necessary + void MoveToItem(size_t item); + + // bring the current item into view + void MoveToFocus() { MoveToItem(m_current); } + void EditLabel( long item ); void OnRenameTimer(); void OnRenameAccept(); void OnMouse( wxMouseEvent &event ); - void MoveToFocus(); // called to switch the selection from the current item to newCurrent, void OnArrowChar( size_t newCurrent, const wxKeyEvent& event ); @@ -662,7 +664,7 @@ public: int GetSelectedItemCount(); // set the scrollbars and update the positions of the items - void RecalculatePositions(); + void RecalculatePositions(bool noRefresh = FALSE); // refresh the window and the header void RefreshAll(); @@ -819,6 +821,9 @@ private: // initialize the current item if needed void UpdateCurrent(); + // delete all items but don't refresh: called from dtor + void DoDeleteAllItems(); + // called when an item is [un]focuded, i.e. becomes [not] current // // currently unused @@ -887,12 +892,19 @@ bool wxSelectionStore::SelectItem(size_t item, bool select) return FALSE; } -void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) +bool wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, + bool select, + wxArrayInt *itemsChanged) { + // 100 is hardcoded but it shouldn't matter much: the important thing is + // that we don't refresh everything when really few (e.g. 1 or 2) items + // change state + static const size_t MANY_ITEMS = 100; + wxASSERT_MSG( itemFrom <= itemTo, _T("should be in order") ); // are we going to have more [un]selected items than the other ones? - if ( itemTo - itemFrom > m_count / 2 ) + if ( itemTo - itemFrom > m_count/2 ) { if ( select != m_defaultState ) { @@ -919,6 +931,9 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) if ( selOld.Index(item) == wxNOT_FOUND ) m_itemsSel.Add(item); } + + // many items (> half) changed state + itemsChanged = NULL; } else // select == m_defaultState { @@ -942,6 +957,17 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) // delete all of them (from end to avoid changing indices) for ( int i = end; i >= (int)start; i-- ) { + if ( itemsChanged ) + { + if ( itemsChanged->GetCount() > MANY_ITEMS ) + { + // stop counting (see comment below) + itemsChanged = NULL; + } + + itemsChanged->Add(m_itemsSel[i]); + } + m_itemsSel.RemoveAt(i); } } @@ -949,12 +975,31 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) } else // "few" items change state { + if ( itemsChanged ) + { + itemsChanged->Empty(); + } + // just add the items to the selection for ( size_t item = itemFrom; item <= itemTo; item++ ) { - SelectItem(item, select); + if ( SelectItem(item, select) && itemsChanged ) + { + itemsChanged->Add(item); + + if ( itemsChanged->GetCount() > MANY_ITEMS ) + { + // stop counting them, we'll just eat gobs of memory + // for nothing at all - faster to refresh everything in + // this case + itemsChanged = NULL; + } + } } } + + // we set it to NULL if there are many items changing state + return itemsChanged != NULL; } void wxSelectionStore::OnItemDelete(size_t item) @@ -1510,32 +1555,68 @@ void wxListLineData::SetAttr(wxListItemAttr *attr) item->SetAttr(attr); } -void wxListLineData::SetAttributes(wxDC *dc, +bool wxListLineData::SetAttributes(wxDC *dc, const wxListItemAttr *attr, - const wxColour& colText, - const wxFont& font, - bool highlight) + bool highlighted) { - // don't use foregroud colour for drawing highlighted items - this might + wxWindow *listctrl = m_owner->GetParent(); + + // fg colour + + // don't use foreground colour for drawing highlighted items - this might // make them completely invisible (and there is no way to do bit // arithmetics on wxColour, unfortunately) - if ( !highlight && attr && attr->HasTextColour() ) + wxColour colText; + if ( highlighted ) { - dc->SetTextForeground(attr->GetTextColour()); + colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { - dc->SetTextForeground(colText); + if ( attr && attr->HasTextColour() ) + { + colText = attr->GetTextColour(); + } + else + { + colText = listctrl->GetForegroundColour(); + } } + dc->SetTextForeground(colText); + + // font + wxFont font; if ( attr && attr->HasFont() ) { - dc->SetFont(attr->GetFont()); + font = attr->GetFont(); } else { - dc->SetFont(font); + font = listctrl->GetFont(); } + + dc->SetFont(font); + + // bg colour + bool hasBgCol = attr && attr->HasBackgroundColour(); + if ( highlighted || hasBgCol ) + { + if ( highlighted ) + { + dc->SetBrush( *m_owner->m_highlightBrush ); + } + else + { + dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); + } + + dc->SetPen( *wxTRANSPARENT_PEN ); + + return TRUE; + } + + return FALSE; } void wxListLineData::Draw( wxDC *dc ) @@ -1543,6 +1624,15 @@ void wxListLineData::Draw( wxDC *dc ) wxListItemDataList::Node *node = m_items.GetFirst(); wxCHECK_RET( node, _T("no subitems at all??") ); + bool highlighted = IsHighlighted(); + + wxListItemAttr *attr = GetAttr(); + + if ( SetAttributes(dc, attr, highlighted) ) + { + dc->DrawRectangle( m_gi->m_rectHighlight ); + } + wxListItemData *item = node->GetData(); if (item->HasImage()) { @@ -1567,43 +1657,12 @@ void wxListLineData::DrawInReportMode( wxDC *dc, if ( !IsVirtual() ) highlighted = m_highlighted; - // default foreground colour - wxWindow *listctrl = m_owner->GetParent(); - wxColour colText; - if ( highlighted ) - { - colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ); - } - else - { - colText = listctrl->GetForegroundColour(); - } - - // default font - wxFont font = listctrl->GetFont(); - // TODO: later we should support setting different attributes for // different columns - to do it, just add "col" argument to - // GetAttr() and move this code into the loop below + // GetAttr() and move these lines into the loop below wxListItemAttr *attr = GetAttr(); - SetAttributes(dc, attr, colText, font, highlighted); - - bool hasBgCol = attr && attr->HasBackgroundColour(); - if ( highlighted || hasBgCol ) + if ( SetAttributes(dc, attr, highlighted) ) { - if ( highlighted ) - { - dc->SetBrush( *m_owner->m_highlightBrush ); - } - else - { - if ( hasBgCol ) - dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); - else - dc->SetBrush( * wxWHITE_BRUSH ); - } - - dc->SetPen( * wxTRANSPARENT_PEN ); dc->DrawRectangle( rectHL ); } @@ -2159,7 +2218,7 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, wxListMainWindow::~wxListMainWindow() { - DeleteEverything(); + DoDeleteAllItems(); delete m_highlightBrush; @@ -2324,25 +2383,37 @@ bool wxListMainWindow::IsHighlighted(size_t line) const } } -void wxListMainWindow::HighlightLines( size_t lineFrom, size_t lineTo, bool highlight ) +void wxListMainWindow::HighlightLines( size_t lineFrom, + size_t lineTo, + bool highlight ) { if ( IsVirtual() ) { - m_selStore.SelectRange(lineFrom, lineTo, highlight); - RefreshLines(lineFrom, lineTo); + wxArrayInt linesChanged; + if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight, + &linesChanged) ) + { + // meny items changed state, refresh everything + RefreshLines(lineFrom, lineTo); + } + else // only a few items changed state, refresh only them + { + size_t count = linesChanged.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + RefreshLine(linesChanged[n]); + } + } } - else + else // iterate over all items in non report view { - // do it the dumb way - bool needsRefresh = FALSE; for ( size_t line = lineFrom; line <= lineTo; line++ ) { if ( HighlightLine(line, highlight) ) - needsRefresh = TRUE; + { + RefreshLine(line); + } } - - if ( needsRefresh ) - RefreshLines(lineFrom, lineTo); } } @@ -2400,7 +2471,7 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) rect.x = 0; rect.y = GetLineY(lineFrom); rect.width = GetClientSize().x; - rect.height = GetLineY(lineTo) - rect.y; + rect.height = GetLineY(lineTo) - rect.y + GetLineHeight(); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); RefreshRect( rect ); @@ -2483,11 +2554,15 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) CalcUnscrolledPosition(0, 0, &xOrig, &yOrig); // tell the caller cache to cache the data - wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT, GetParent()->GetId()); - evCache.SetEventObject( GetParent() ); - evCache.m_oldItemIndex = visibleFrom; - evCache.m_itemIndex = visibleTo; - GetParent()->GetEventHandler()->ProcessEvent( evCache ); + if ( IsVirtual() ) + { + wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT, + GetParent()->GetId()); + evCache.SetEventObject( GetParent() ); + evCache.m_oldItemIndex = visibleFrom; + evCache.m_itemIndex = visibleTo; + GetParent()->GetEventHandler()->ProcessEvent( evCache ); + } for ( size_t line = visibleFrom; line <= visibleTo; line++ ) { @@ -2605,7 +2680,12 @@ void wxListMainWindow::SendNotify( size_t line, if ( point != wxDefaultPosition ) le.m_pointDrag = point; - GetLine(line)->GetItem( 0, le.m_item ); + if ( command != wxEVT_COMMAND_LIST_DELETE_ITEM ) + { + GetLine(line)->GetItem( 0, le.m_item ); + } + //else: there may be no more such item + GetParent()->GetEventHandler()->ProcessEvent( le ); } @@ -2864,12 +2944,12 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) } } -void wxListMainWindow::MoveToFocus() +void wxListMainWindow::MoveToItem(size_t item) { - if ( !HasCurrent() ) + if ( item == (size_t)-1 ) return; - wxRect rect = GetLineRect(m_current); + wxRect rect = GetLineRect(item); int client_w, client_h; GetClientSize( &client_w, &client_h ); @@ -3549,7 +3629,8 @@ void wxListMainWindow::SetItemCount(long count) ResetVisibleLinesRange(); - Refresh(); + // scrollbars must be reset + m_dirty = TRUE; } int wxListMainWindow::GetSelectedItemCount() @@ -3606,11 +3687,8 @@ bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) // geometry calculation // ---------------------------------------------------------------------------- -void wxListMainWindow::RecalculatePositions() +void wxListMainWindow::RecalculatePositions(bool noRefresh) { - if ( IsEmpty() ) - return; - wxClientDC dc( this ); dc.SetFont( GetFont() ); @@ -3711,10 +3789,13 @@ void wxListMainWindow::RecalculatePositions() SetScrollbars( m_xScroll, m_yScroll, (entireWidth+SCROLL_UNIT_X) / m_xScroll, 0, scroll_pos, 0, TRUE ); } - // FIXME: why should we call it from here? - UpdateCurrent(); + if ( !noRefresh ) + { + // FIXME: why should we call it from here? + UpdateCurrent(); - RefreshAll(); + RefreshAll(); + } } void wxListMainWindow::RefreshAll() @@ -3795,20 +3876,18 @@ void wxListMainWindow::DeleteItem( long lindex ) size_t index = (size_t)lindex; - m_dirty = TRUE; - - // select the next item when the selected one is deleted - if ( m_current == index ) + // we don't need to adjust the index for the previous items + if ( HasCurrent() && m_current >= index ) { - // the last valid index after deleting the item will be count-2 - if ( m_current == count - 1 ) + // if the current item is being deleted, we want the next one to + // become selected - unless there is no next one - so don't adjust + // m_current in this case + if ( m_current != index || m_current == count - 1 ) { m_current--; } } - SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); - if ( InReportView() ) { ResetVisibleLinesRange(); @@ -3826,6 +3905,9 @@ void wxListMainWindow::DeleteItem( long lindex ) } m_dirty = TRUE; + + SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); + RefreshAfter(index); } @@ -3839,7 +3921,7 @@ void wxListMainWindow::DeleteColumn( int col ) m_columns.DeleteNode( node ); } -void wxListMainWindow::DeleteAllItems() +void wxListMainWindow::DoDeleteAllItems() { if ( IsEmpty() ) { @@ -3871,11 +3953,13 @@ void wxListMainWindow::DeleteAllItems() } m_lines.Clear(); +} - // NB: don't just set m_dirty to TRUE here as RecalculatePositions() - // doesn't do anything if the control is empty and so we won't be - // refreshed - Refresh(); +void wxListMainWindow::DeleteAllItems() +{ + DoDeleteAllItems(); + + RecalculatePositions(); } void wxListMainWindow::DeleteEverything() @@ -3895,14 +3979,15 @@ void wxListMainWindow::EnsureVisible( long index ) _T("invalid index in EnsureVisible") ); // We have to call this here because the label in question might just have - // been added and no screen update taken place. - if (m_dirty) - wxSafeYield(); + // been added and its position is not known yet + if ( m_dirty ) + { + m_dirty = FALSE; - size_t oldCurrent = m_current; - m_current = (size_t)index; - MoveToFocus(); - m_current = oldCurrent; + RecalculatePositions(TRUE /* no refresh */); + } + + MoveToItem((size_t)index); } long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(partial) ) @@ -4232,6 +4317,7 @@ void wxListEvent::CopyObject(wxObject& object_dest) const // ------------------------------------------------------------------------------------- IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) BEGIN_EVENT_TABLE(wxListCtrl,wxControl) EVT_SIZE(wxListCtrl::OnSize) @@ -4356,36 +4442,35 @@ void wxListCtrl::SetWindowStyleFlag( long flag ) { m_mainWin->DeleteEverything(); - int width = 0; - int height = 0; - GetClientSize( &width, &height ); + // has the header visibility changed? + bool hasHeader = HasFlag(wxLC_REPORT) && !HasFlag(wxLC_NO_HEADER), + willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER); - if (flag & wxLC_REPORT) + if ( hasHeader != willHaveHeader ) { - if (!HasFlag(wxLC_REPORT)) + // toggle it + if ( hasHeader ) + { + if ( m_headerWin ) + { + // don't delete, just hide, as we can reuse it later + m_headerWin->Show(FALSE); + } + //else: nothing to do + } + else // must show header { if (!m_headerWin) { CreateHeaderWindow(); - - if (HasFlag(wxLC_NO_HEADER)) - m_headerWin->Show( FALSE ); } - else + else // already have it, just show { - if (flag & wxLC_NO_HEADER) - m_headerWin->Show( FALSE ); - else - m_headerWin->Show( TRUE ); + m_headerWin->Show( TRUE ); } } - } - else // !report - { - if ( m_mainWin->HasHeader() ) - { - m_headerWin->Show( FALSE ); - } + + ResizeReportView(willHaveHeader); } } @@ -4776,10 +4861,17 @@ void wxListCtrl::OnSize(wxSizeEvent& event) if ( !m_mainWin ) return; + ResizeReportView(m_mainWin->HasHeader()); + + m_mainWin->RecalculatePositions(); +} + +void wxListCtrl::ResizeReportView(bool showHeader) +{ int cw, ch; GetClientSize( &cw, &ch ); - if ( m_mainWin->HasHeader() ) + if ( showHeader ) { m_headerWin->SetSize( 0, 0, cw, HEADER_HEIGHT ); m_mainWin->SetSize( 0, HEADER_HEIGHT + 1, cw, ch - HEADER_HEIGHT - 1 ); @@ -4788,8 +4880,6 @@ void wxListCtrl::OnSize(wxSizeEvent& event) { m_mainWin->SetSize( 0, 0, cw, ch ); } - - m_mainWin->RecalculatePositions(); } void wxListCtrl::OnIdle( wxIdleEvent & event )