X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/e9d9f136ee346ff8a29f204ddf75ef8471475722..b838cfc9151aea38402ad2b1ba5d2f97cf94e973:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index e625f22269..3ca9aa51ac 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -9,38 +9,10 @@ ///////////////////////////////////////////////////////////////////////////// /* - FIXME for virtual list controls + TODO - +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: - - 1. less dumb line caching, we should cache at least all those visible - in the control itself and probably twice as many (we might also need to - cache the first one always for geometry calculations?) - - +2. storing selections: we can't use an array to store the selected indices - like right now as selecting all in a control with 1000000 items is not - doable like this - instead, store selections as collection of individual - items and ranges - - => wxSelectionStore - - 3. we need to implement searching/sorting somehow - - 4. the idea of storing the line index in the line itself is really stupid, - we shouldn't need it - but for this we have to get rid of all calles to - wxListLineData::GetFoo() and replace them with something like - if ( IsVirtual() - ... we have it ourselves ... - else - line->GetFoo(); + 1. we need to implement searching/sorting for virtual controls somehow + 2. when changing selection the lines are refreshed twice */ // ============================================================================ @@ -96,6 +68,7 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT) // ---------------------------------------------------------------------------- // constants @@ -126,6 +99,9 @@ static const int AUTOSIZE_COL_MARGIN = 10; static const int WIDTH_COL_DEFAULT = 80; static const int WIDTH_COL_MIN = 10; +// the space between the image and the text in the report mode +static const int IMAGE_MARGIN_IN_REPORT_MODE = 5; + // ============================================================================ // private classes // ============================================================================ @@ -170,7 +146,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; @@ -208,7 +191,7 @@ class WXDLLEXPORT wxListItemData { public: wxListItemData(wxListMainWindow *owner); - ~wxListItemData() { delete m_attr; delete m_rect; } + ~wxListItemData(); void SetItem( const wxListItem &info ); void SetImage( int image ) { m_image = image; } @@ -243,7 +226,8 @@ public: void GetItem( wxListItem &info ) const; - wxListItemAttr *GetAttributes() const { return m_attr; } + void SetAttr(wxListItemAttr *attr) { m_attr = attr; } + wxListItemAttr *GetAttr() const { return m_attr; } public: // the item image or -1 @@ -348,7 +332,7 @@ public: wxListMainWindow *m_owner; public: - wxListLineData( wxListMainWindow *owner, size_t line ); + wxListLineData(wxListMainWindow *owner); ~wxListLineData() { delete m_gi; } @@ -367,7 +351,12 @@ public: // remember the position this line appears at void SetPosition( int x, int y, int window_width, int spacing ); - long IsHit( int x, int y ); + // wxListCtrl API + + void SetImage( int image ) { SetImage(0, image); } + int GetImage() const { return GetImage(0); } + bool HasImage() const { return GetImage() != -1; } + bool HasText() const { return !GetText(0).empty(); } void SetItem( int index, const wxListItem &info ); void GetItem( int index, wxListItem &info ); @@ -375,33 +364,14 @@ public: wxString GetText(int index) const; void SetText( int index, const wxString s ); - void SetImage( int index, int image ); - int GetImage( int index ) const; - - // get the bound rect of this line - wxRect GetRect() const; - - // get the bound rect of the label - wxRect GetLabelRect() const; - - // get the bound rect of the items icon (only may be called if we do have - // an icon!) - wxRect GetIconRect() const; - - // get the rect to be highlighted when the item has focus - wxRect GetHighlightRect() const; - - // get the size of the total line rect - wxSize GetSize() const { return GetRect().GetSize(); } + wxListItemAttr *GetAttr() const; + void SetAttr(wxListItemAttr *attr); // return true if the highlighting really changed bool Highlight( bool on ); void ReverseHighlight(); - // draw the line on the given DC - void Draw( wxDC *dc, int y = 0, int height = 0, bool highlighted = FALSE ); - bool IsHighlighted() const { wxASSERT_MSG( !IsVirtual(), _T("unexpected call to IsHighlighted") ); @@ -409,8 +379,14 @@ public: return m_highlighted; } - // only for wxListMainWindow::CacheLineData() - void SetLineIndex(size_t line) { m_lineIndex = line; } + // draw the line on the given DC in icon/list mode + void Draw( wxDC *dc ); + + // the same in report mode + void DrawInReportMode( wxDC *dc, + const wxRect& rect, + const wxRect& rectHL, + bool highlighted ); private: // set the line to contain num items (only can be > 1 in report mode) @@ -419,17 +395,18 @@ 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); - // the index of this line (only used in report mode) - size_t m_lineIndex; + // these are only used by GetImage/SetImage above, we don't support images + // with subitems at the public API level yet + void SetImage( int index, int image ); + int GetImage( int index ) const; }; - WX_DECLARE_EXPORTED_OBJARRAY(wxListLineData, wxListLineDataArray); #include "wx/arrimpl.cpp" WX_DEFINE_OBJARRAY(wxListLineDataArray); @@ -554,6 +531,9 @@ public: // return true if this is a virtual list control bool IsVirtual() const { return HasFlag(wxLC_VIRTUAL); } + // return true if the control is in report mode + bool InReportView() const { return HasFlag(wxLC_REPORT); } + // return true if we are in single selection mode, false if multi sel bool IsSingleSel() const { return HasFlag(wxLC_SINGLE_SEL); } @@ -578,19 +558,56 @@ public: void ReverseHighlight( size_t line ) { HighlightLine(line, !IsHighlighted(line)); RefreshLine(line); } + // return true if the line is highlighted + bool IsHighlighted(size_t line) const; + // refresh one or several lines at once void RefreshLine( size_t line ); void RefreshLines( size_t lineFrom, size_t lineTo ); - // return true if the line is highlighted - bool IsHighlighted(size_t line) const; + // refresh all selected items + void RefreshSelected(); + + // refresh all lines below the given one: the difference with + // RefreshLines() is that the index here might not be a valid one (happens + // when the last line is deleted) + void RefreshAfter( size_t lineFrom ); + + // the methods which are forwarded to wxListLineData itself in list/icon + // modes but are here because the lines don't store their positions in the + // report mode + + // get the bound rect for the entire line + wxRect GetLineRect(size_t line) const; + + // get the bound rect of the label + wxRect GetLineLabelRect(size_t line) const; + + // get the bound rect of the items icon (only may be called if we do have + // an icon!) + wxRect GetLineIconRect(size_t line) const; + + // get the rect to be highlighted when the item has focus + wxRect GetLineHighlightRect(size_t line) const; + + // get the size of the total line rect + wxSize GetLineSize(size_t line) const + { return GetLineRect(line).GetSize(); } + + // 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 ); @@ -604,8 +621,8 @@ public: void OnPaint( wxPaintEvent &event ); void DrawImage( int index, wxDC *dc, int x, int y ); - void GetImageSize( int index, int &width, int &height ); - int GetTextLength( const wxString &s ); + void GetImageSize( int index, int &width, int &height ) const; + int GetTextLength( const wxString &s ) const; void SetImageList( wxImageList *imageList, int which ); void SetItemSpacing( int spacing, bool isSmall = FALSE ); @@ -620,7 +637,7 @@ public: // returns the sum of the heights of all columns int GetHeaderWidth() const; - int GetCountPerPage() { return m_linesPerPage; } + int GetCountPerPage() const; void SetItem( wxListItem &item ); void GetItem( wxListItem &item ); @@ -631,7 +648,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(); @@ -686,6 +703,12 @@ public: // get the y position of the given line (only for report view) wxCoord GetLineY(size_t line) const; + // get the brush to use for the item highlighting + wxBrush *GetHighlightBrush() const + { + return m_hasFocus ? m_highlightBrush : m_highlightUnfocusedBrush; + } + //protected: // the array of all line objects for a non virtual list control wxListLineDataArray m_lines; @@ -710,7 +733,6 @@ public: // call bool m_dirty; - wxBrush *m_highlightBrush; wxColour *m_highlightColour; int m_xScroll, m_yScroll; @@ -788,6 +810,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 @@ -806,6 +831,10 @@ private: size_t m_lineFrom, m_lineTo; + // the brushes to use for item highlighting when we do/don't have focus + wxBrush *m_highlightBrush, + *m_highlightUnfocusedBrush; + DECLARE_DYNAMIC_CLASS(wxListMainWindow); DECLARE_EVENT_TABLE() }; @@ -856,12 +885,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 ) { @@ -888,6 +924,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 { @@ -911,6 +950,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); } } @@ -918,12 +968,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) @@ -935,6 +1004,8 @@ void wxSelectionStore::OnItemDelete(size_t item) { // this item itself was in m_itemsSel, remove it from there m_itemsSel.RemoveAt(i); + + count--; } // and adjust the index of all which follow it @@ -951,6 +1022,18 @@ void wxSelectionStore::OnItemDelete(size_t item) // wxListItemData //----------------------------------------------------------------------------- +wxListItemData::~wxListItemData() +{ + // in the virtual list control the attributes are managed by the main + // program, so don't delete them + if ( !m_owner->IsVirtual() ) + { + delete m_attr; + } + + delete m_rect; +} + void wxListItemData::Init() { m_image = -1; @@ -965,7 +1048,7 @@ wxListItemData::wxListItemData(wxListMainWindow *owner) m_owner = owner; - if ( owner->HasFlag(wxLC_REPORT) ) + if ( owner->InReportView() ) { m_rect = NULL; } @@ -1184,13 +1267,11 @@ inline bool wxListLineData::IsVirtual() const return m_owner->IsVirtual(); } -wxListLineData::wxListLineData( wxListMainWindow *owner, size_t line ) +wxListLineData::wxListLineData( wxListMainWindow *owner ) { m_owner = owner; m_items.DeleteContents( TRUE ); - SetLineIndex(line); - if ( InReportView() ) { m_gi = NULL; @@ -1205,59 +1286,6 @@ wxListLineData::wxListLineData( wxListMainWindow *owner, size_t line ) InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 ); } -wxRect wxListLineData::GetRect() const -{ - if ( !InReportView() ) - return m_gi->m_rectAll; - - wxRect rect; - rect.x = HEADER_OFFSET_X; - rect.y = m_owner->GetLineY(m_lineIndex); - rect.width = m_owner->GetHeaderWidth(); - rect.height = m_owner->GetLineHeight(); - - return rect; -} - -wxRect wxListLineData::GetLabelRect() const -{ - if ( !InReportView() ) - return m_gi->m_rectLabel; - - wxRect rect; - rect.x = HEADER_OFFSET_X; - rect.y = m_owner->GetLineY(m_lineIndex); - rect.width = m_owner->GetColumnWidth(0); - rect.height = m_owner->GetLineHeight(); - - return rect; -} - -wxRect wxListLineData::GetIconRect() const -{ - if ( !InReportView() ) - return m_gi->m_rectIcon; - - wxRect rect; - - wxListItemDataList::Node *node = m_items.GetFirst(); - wxCHECK_MSG( node, rect, _T("no subitems at all??") ); - - wxListItemData *item = node->GetData(); - wxASSERT_MSG( item->HasImage(), _T("GetIconRect() called but no image") ); - - rect.x = HEADER_OFFSET_X; - rect.y = m_owner->GetLineY(m_lineIndex); - m_owner->GetImageSize(item->GetImage(), rect.width, rect.height); - - return rect; -} - -wxRect wxListLineData::GetHighlightRect() const -{ - return InReportView() ? GetRect() : m_gi->m_rectHighlight; -} - void wxListLineData::CalculateSize( wxDC *dc, int spacing ) { wxListItemDataList::Node *node = m_items.GetFirst(); @@ -1435,25 +1463,6 @@ void wxListLineData::SetPosition( int x, int y, } } -long wxListLineData::IsHit( int x, int y ) -{ - wxListItemDataList::Node *node = m_items.GetFirst(); - wxCHECK_MSG( node, 0, _T("no subitems at all??") ); - - wxListItemData *item = node->GetData(); - if ( item->HasImage() && GetIconRect().Inside(x, y) ) - return wxLIST_HITTEST_ONITEMICON; - - if ( item->HasText() ) - { - wxRect rect = InReportView() ? GetRect() : GetLabelRect(); - if ( rect.Inside(x, y) ) - return wxLIST_HITTEST_ONITEMLABEL; - } - - return 0; -} - void wxListLineData::InitItems( int num ) { for (int i = 0; i < num; i++) @@ -1521,145 +1530,168 @@ int wxListLineData::GetImage( int index ) const return item->GetImage(); } -void wxListLineData::SetAttributes(wxDC *dc, - const wxListItemAttr *attr, - const wxColour& colText, - const wxFont& font, - bool highlight) +wxListItemAttr *wxListLineData::GetAttr() const { - // don't use foregroud 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() ) - { - dc->SetTextForeground(attr->GetTextColour()); - } - else - { - dc->SetTextForeground(colText); - } + wxListItemDataList::Node *node = m_items.GetFirst(); + wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") ); - if ( attr && attr->HasFont() ) - { - dc->SetFont(attr->GetFont()); - } - else - { - dc->SetFont(font); - } + wxListItemData *item = node->GetData(); + return item->GetAttr(); } -void wxListLineData::Draw( wxDC *dc, int y, int height, bool highlighted ) +void wxListLineData::SetAttr(wxListItemAttr *attr) { - wxRect rect = GetRect(); - m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + wxListItemDataList::Node *node = m_items.GetFirst(); + wxCHECK_RET( node, _T("invalid column index in SetAttr()") ); - if ( !m_owner->IsExposed( rect ) ) - return; + wxListItemData *item = node->GetData(); + item->SetAttr(attr); +} +bool wxListLineData::SetAttributes(wxDC *dc, + const wxListItemAttr *attr, + bool highlighted) +{ wxWindow *listctrl = m_owner->GetParent(); - // use our own flag if we maintain it - if ( !m_owner->IsVirtual() ) - highlighted = m_highlighted; + // fg colour - // default foreground 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) wxColour colText; if ( highlighted ) { - colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ); + colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { - colText = listctrl->GetForegroundColour(); + if ( attr && attr->HasTextColour() ) + { + colText = attr->GetTextColour(); + } + else + { + colText = listctrl->GetForegroundColour(); + } } - // default font - wxFont font = listctrl->GetFont(); + dc->SetTextForeground(colText); + + // font + wxFont font; + if ( attr && attr->HasFont() ) + { + font = attr->GetFont(); + } + else + { + font = listctrl->GetFont(); + } - // VZ: currently we set the colours/fonts only once, but like this (i.e. - // using SetAttributes() inside the loop), it will be trivial to - // customize the subitems (in report mode) too. - wxListItemData *item = m_items.GetFirst()->GetData(); - wxListItemAttr *attr = item->GetAttributes(); - SetAttributes(dc, attr, colText, font, highlighted); + dc->SetFont(font); + // bg colour bool hasBgCol = attr && attr->HasBackgroundColour(); if ( highlighted || hasBgCol ) { if ( highlighted ) { - dc->SetBrush( *m_owner->m_highlightBrush ); + dc->SetBrush( *m_owner->GetHighlightBrush() ); } else { - if ( hasBgCol ) - dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); - else - dc->SetBrush( * wxWHITE_BRUSH ); + dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); } - dc->SetPen( * wxTRANSPARENT_PEN ); - dc->DrawRectangle( GetHighlightRect() ); + dc->SetPen( *wxTRANSPARENT_PEN ); + + return TRUE; } + return FALSE; +} + +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 ( GetMode() == wxLC_REPORT) + if ( SetAttributes(dc, attr, highlighted) ) { - size_t col = 0; - int x = HEADER_OFFSET_X; + dc->DrawRectangle( m_gi->m_rectHighlight ); + } - y += (LINE_SPACING + EXTRA_HEIGHT) / 2; + wxListItemData *item = node->GetData(); + if (item->HasImage()) + { + wxRect rectIcon = m_gi->m_rectIcon; + m_owner->DrawImage( item->GetImage(), dc, + rectIcon.x, rectIcon.y ); + } - while ( node ) - { - wxListItemData *item = node->GetData(); + if (item->HasText()) + { + wxRect rectLabel = m_gi->m_rectLabel; - int xOld = x; + wxDCClipper clipper(*dc, rectLabel); + dc->DrawText( item->GetText(), rectLabel.x, rectLabel.y ); + } +} - if ( item->HasImage() ) - { - int ix, iy; - m_owner->DrawImage( item->GetImage(), dc, x, y ); - m_owner->GetImageSize( item->GetImage(), ix, iy ); - x += ix + 5; // FIXME: what is "5"? - } +void wxListLineData::DrawInReportMode( wxDC *dc, + const wxRect& rect, + const wxRect& rectHL, + bool highlighted ) +{ + // 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) ) + { + dc->DrawRectangle( rectHL ); + } - int width = m_owner->GetColumnWidth(col++); + wxListItemDataList::Node *node = m_items.GetFirst(); + wxCHECK_RET( node, _T("no subitems at all??") ); - dc->SetClippingRegion(x, y, width, height); + size_t col = 0; + wxCoord x = rect.x + HEADER_OFFSET_X, + y = rect.y + (LINE_SPACING + EXTRA_HEIGHT) / 2; - if ( item->HasText() ) - { - dc->DrawText( item->GetText(), x, y + 1 ); - } + while ( node ) + { + wxListItemData *item = node->GetData(); - dc->DestroyClippingRegion(); + int width = m_owner->GetColumnWidth(col++); + int xOld = x; + x += width; + + if ( item->HasImage() ) + { + int ix, iy; + m_owner->DrawImage( item->GetImage(), dc, xOld, y ); + m_owner->GetImageSize( item->GetImage(), ix, iy ); - x = xOld + width; + ix += IMAGE_MARGIN_IN_REPORT_MODE; - node = node->GetNext(); + xOld += ix; + width -= ix; } - } - else // !report - { - if (node) - { - wxListItemData *item = node->GetData(); - if (item->HasImage()) - { - wxRect rectIcon = GetIconRect(); - m_owner->DrawImage( item->GetImage(), dc, - rectIcon.x, rectIcon.y ); - } - if (item->HasText()) - { - wxRect rectLabel = GetLabelRect(); - dc->DrawText( item->GetText(), rectLabel.x, rectLabel.y ); - } + wxDCClipper clipper(*dc, xOld, y, width, rect.height); + + if ( item->HasText() ) + { + dc->DrawText( item->GetText(), xOld, y ); } + + node = node->GetNext(); } } @@ -1832,9 +1864,11 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dc.SetPen( *wxWHITE_PEN ); DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 ); - dc.SetClippingRegion( x, HEADER_OFFSET_Y, cw-5, h-4 ); - dc.DrawText( item.GetText(), x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT ); - dc.DestroyClippingRegion(); + wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw-5, h-4 ); + + dc.DrawText( item.GetText(), + x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT ); + x += wCol; if (xEnd > w+5) @@ -2150,7 +2184,8 @@ wxListMainWindow::wxListMainWindow() { Init(); - m_highlightBrush = (wxBrush *) NULL; + m_highlightBrush = + m_highlightUnfocusedBrush = (wxBrush *) NULL; m_xScroll = m_yScroll = 0; @@ -2167,7 +2202,24 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, { Init(); - m_highlightBrush = new wxBrush( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT), wxSOLID ); + m_highlightBrush = new wxBrush + ( + wxSystemSettings::GetSystemColour + ( + wxSYS_COLOUR_HIGHLIGHT + ), + wxSOLID + ); + + m_highlightUnfocusedBrush = new wxBrush + ( + wxSystemSettings::GetSystemColour + ( + wxSYS_COLOUR_BTNSHADOW + ), + wxSOLID + ); + wxSize sz = size; sz.y = 25; @@ -2179,9 +2231,10 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, wxListMainWindow::~wxListMainWindow() { - DeleteEverything(); + DoDeleteAllItems(); delete m_highlightBrush; + delete m_highlightUnfocusedBrush; delete m_renameTimer; } @@ -2198,8 +2251,8 @@ void wxListMainWindow::CacheLineData(size_t line) ld->SetText(col, listctrl->OnGetItemText(line, col)); } - ld->SetImage(0, listctrl->OnGetItemImage(line)); - ld->SetLineIndex(line); + ld->SetImage(listctrl->OnGetItemImage(line)); + ld->SetAttr(listctrl->OnGetItemAttr(line)); } wxListLineData *wxListMainWindow::GetDummyLine() const @@ -2213,15 +2266,17 @@ wxListLineData *wxListMainWindow::GetDummyLine() const wxASSERT_MSG( IsVirtual(), _T("logic error") ); wxListMainWindow *self = wxConstCast(this, wxListMainWindow); - wxListLineData *line = new wxListLineData( self, 0 ); + wxListLineData *line = new wxListLineData(self); self->m_lines.Add(line); } - m_lines[0].SetLineIndex(0); - return &m_lines[0]; } +// ---------------------------------------------------------------------------- +// line geometry (report mode only) +// ---------------------------------------------------------------------------- + wxCoord wxListMainWindow::GetLineHeight() const { wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("only works in report mode") ); @@ -2254,6 +2309,79 @@ wxCoord wxListMainWindow::GetLineY(size_t line) const return LINE_SPACING + line*GetLineHeight(); } +wxRect wxListMainWindow::GetLineRect(size_t line) const +{ + if ( !InReportView() ) + return GetLine(line)->m_gi->m_rectAll; + + wxRect rect; + rect.x = HEADER_OFFSET_X; + rect.y = GetLineY(line); + rect.width = GetHeaderWidth(); + rect.height = GetLineHeight(); + + return rect; +} + +wxRect wxListMainWindow::GetLineLabelRect(size_t line) const +{ + if ( !InReportView() ) + return GetLine(line)->m_gi->m_rectLabel; + + wxRect rect; + rect.x = HEADER_OFFSET_X; + rect.y = GetLineY(line); + rect.width = GetColumnWidth(0); + rect.height = GetLineHeight(); + + return rect; +} + +wxRect wxListMainWindow::GetLineIconRect(size_t line) const +{ + if ( !InReportView() ) + return GetLine(line)->m_gi->m_rectIcon; + + wxListLineData *ld = GetLine(line); + wxASSERT_MSG( ld->HasImage(), _T("should have an image") ); + + wxRect rect; + rect.x = HEADER_OFFSET_X; + rect.y = GetLineY(line); + GetImageSize(ld->GetImage(), rect.width, rect.height); + + return rect; +} + +wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const +{ + return InReportView() ? GetLineRect(line) + : GetLine(line)->m_gi->m_rectHighlight; +} + +long wxListMainWindow::HitTestLine(size_t line, int x, int y) const +{ + wxListLineData *ld = GetLine(line); + + if ( ld->HasImage() && GetLineIconRect(line).Inside(x, y) ) + return wxLIST_HITTEST_ONITEMICON; + + if ( ld->HasText() ) + { + wxRect rect = InReportView() ? GetLineRect(line) + : GetLineLabelRect(line); + + if ( rect.Inside(x, y) ) + return wxLIST_HITTEST_ONITEMLABEL; + } + + return 0; +} + +// ---------------------------------------------------------------------------- +// highlight (selection) handling +// ---------------------------------------------------------------------------- + bool wxListMainWindow::IsHighlighted(size_t line) const { if ( IsVirtual() ) @@ -2269,25 +2397,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); } } @@ -2302,7 +2442,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) else // !virtual { wxListLineData *ld = GetLine(line); - wxCHECK_MSG( ld, FALSE, _T("invalid index in IsHighlighted") ); + wxCHECK_MSG( ld, FALSE, _T("invalid index in HighlightLine") ); changed = ld->Highlight(highlight); } @@ -2310,7 +2450,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) if ( changed ) { SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED - : wxEVT_COMMAND_LIST_ITEM_DESELECTED ); + : wxEVT_COMMAND_LIST_ITEM_DESELECTED ); } return changed; @@ -2318,7 +2458,16 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) void wxListMainWindow::RefreshLine( size_t line ) { - wxRect rect = GetLine(line)->GetRect(); + if ( HasFlag(wxLC_REPORT) ) + { + size_t visibleFrom, visibleTo; + GetVisibleLinesRange(&visibleFrom, &visibleTo); + + if ( line < visibleFrom || line > visibleTo ) + return; + } + + wxRect rect = GetLineRect(line); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); RefreshRect( rect ); @@ -2329,6 +2478,8 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) // we suppose that they are ordered by caller wxASSERT_MSG( lineFrom <= lineTo, _T("indices in disorder") ); + wxASSERT_MSG( lineTo < GetItemCount(), _T("invalid line range") ); + if ( HasFlag(wxLC_REPORT) ) { size_t visibleFrom, visibleTo; @@ -2343,7 +2494,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 ); @@ -2358,6 +2509,66 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) } } +void wxListMainWindow::RefreshAfter( size_t lineFrom ) +{ + if ( HasFlag(wxLC_REPORT) ) + { + size_t visibleFrom; + GetVisibleLinesRange(&visibleFrom, NULL); + + if ( lineFrom < visibleFrom ) + lineFrom = visibleFrom; + + wxRect rect; + rect.x = 0; + rect.y = GetLineY(lineFrom); + + wxSize size = GetClientSize(); + rect.width = size.x; + // refresh till the bottom of the window + rect.height = size.y - rect.y; + + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + RefreshRect( rect ); + } + else // !report + { + // TODO: how to do it more efficiently? + m_dirty = TRUE; + } +} + +void wxListMainWindow::RefreshSelected() +{ + if ( IsEmpty() ) + return; + + size_t from, to; + if ( InReportView() ) + { + GetVisibleLinesRange(&from, &to); + } + else // !virtual + { + from = 0; + to = GetItemCount() - 1; + } + + if ( HasCurrent() && m_current > from && m_current <= to ) + { + RefreshLine(m_current); + } + + for ( size_t line = from; line <= to; line++ ) + { + // NB: the test works as expected even if m_current == -1 + if ( line != m_current && IsHighlighted(line) ) + { + RefreshLine(line); + } + } +} + void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { // Note: a wxPaintDC must be constructed even if no drawing is @@ -2370,6 +2581,12 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) return; } + if ( m_dirty ) + { + // delay the repainting until we calculate all the items positions + return; + } + PrepareDC( dc ); int dev_x, dev_y; @@ -2385,12 +2602,37 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) size_t visibleFrom, visibleTo; GetVisibleLinesRange(&visibleFrom, &visibleTo); + + wxRect rectLine; + wxCoord xOrig, yOrig; + CalcUnscrolledPosition(0, 0, &xOrig, &yOrig); + + // tell the caller cache to cache the data + 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++ ) { - GetLine(line)->Draw( &dc, - GetLineY(line), - lineHeight, - IsHighlighted(line) ); + rectLine = GetLineRect(line); + + if ( !IsExposed(rectLine.x - xOrig, rectLine.y - yOrig, + rectLine.width, rectLine.height) ) + { + // don't redraw unaffected lines to avoid flicker + continue; + } + + GetLine(line)->DrawInReportMode( &dc, + rectLine, + GetLineHighlightRect(line), + IsHighlighted(line) ); } if ( HasFlag(wxLC_HRULES) ) @@ -2447,28 +2689,21 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) } } - if ( HasCurrent() && m_hasFocus ) + if ( HasCurrent() ) { - wxRect rect; - - if ( IsVirtual() ) - { - // just offset the rect of the first line to position it correctly - wxListLineData *line = GetDummyLine(); - rect = line->GetHighlightRect(); - rect.y = GetLineY(m_current); - } - else + // don't draw rect outline under Max if we already have the background + // color but under other platforms only draw it if we do: it is a bit + // silly to draw "focus rect" if we don't have focus! +#ifdef __WXMAC__ + if ( !m_hasFocus ) +#else // !__WXMAC__ + if ( m_hasFocus ) +#endif // __WXMAC__/!__WXMAC__ { - rect = GetLine(m_current)->GetHighlightRect(); + dc.SetPen( *wxBLACK_PEN ); + dc.SetBrush( *wxTRANSPARENT_BRUSH ); + dc.DrawRectangle( GetLineHighlightRect(m_current) ); } -#ifdef __WXMAC__ - // no rect outline, we already have the background color -#else - dc.SetPen( *wxBLACK_PEN ); - dc.SetBrush( *wxTRANSPARENT_BRUSH ); - dc.DrawRectangle( rect ); -#endif } dc.EndDrawing(); @@ -2505,7 +2740,16 @@ void wxListMainWindow::SendNotify( size_t line, if ( point != wxDefaultPosition ) le.m_pointDrag = point; - GetLine(line)->GetItem( 0, le.m_item ); + // don't try to get the line info for virtual list controls: the main + // program has it anyhow and if we did it would result in accessing all + // the lines, even those which are not visible now and this is precisely + // what we're trying to avoid + if ( !IsVirtual() && (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 ); } @@ -2546,7 +2790,7 @@ void wxListMainWindow::EditLabel( long item ) PrepareDC( dc ); wxString s = data->GetText(0); - wxRect rectLabel = data->GetLabelRect(); + wxRect rectLabel = GetLineLabelRect(m_currentEdit); rectLabel.x = dc.LogicalToDeviceX( rectLabel.x ); rectLabel.y = dc.LogicalToDeviceY( rectLabel.y ); @@ -2614,7 +2858,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) int y = event.GetY(); CalcUnscrolledPosition( x, y, &x, &y ); - /* Did we actually hit an item ? */ + // where did we hit it (if we did)? long hitResult = 0; size_t count = GetItemCount(), @@ -2622,10 +2866,9 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if ( HasFlag(wxLC_REPORT) ) { - wxCoord lineHeight = GetLineHeight(); - - current = y / lineHeight; - hitResult = GetDummyLine()->IsHit( x, y % lineHeight ); + current = y / GetLineHeight(); + if ( current < count ) + hitResult = HitTestLine(current, x, y); } else // !report { @@ -2633,10 +2876,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) // enumerating all items is still not a way to do it!! for ( current = 0; current < count; current++ ) { - wxListLineData *line = (wxListLineData *) NULL; - line = GetLine(current); - hitResult = line->IsHit( x, y ); - if (hitResult) + hitResult = HitTestLine(current, x, y); + if ( hitResult ) break; } } @@ -2767,24 +3008,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; - - if ( IsVirtual() ) - { - // just offset the rect of the first line to position it correctly - wxListLineData *line = GetDummyLine(); - rect = line->GetRect(); - rect.y = GetLineY(m_current); - } - else - { - rect = GetLine(m_current)->GetRect(); - } + wxRect rect = GetLineRect(item); int client_w, client_h; GetClientSize( &client_w, &client_h ); @@ -3021,11 +3250,16 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) le.m_itemIndex = m_current; GetLine(m_current)->GetItem( 0, le.m_item ); GetParent()->GetEventHandler()->ProcessEvent( le ); + + if ( IsHighlighted(m_current) ) + { + // don't unselect the item in single selection mode + break; + } + //else: select it in ReverseHighlight() below if unselected } - else - { - ReverseHighlight(m_current); - } + + ReverseHighlight(m_current); break; case WXK_RETURN: @@ -3057,12 +3291,11 @@ void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) { m_hasFocus = TRUE; - if ( HasCurrent() ) - RefreshLine( m_current ); - if (!GetParent()) return; + RefreshSelected(); + #ifdef __WXGTK__ g_focusWindow = GetParent(); #endif @@ -3076,8 +3309,7 @@ void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) { m_hasFocus = FALSE; - if ( HasCurrent() ) - RefreshLine( m_current ); + RefreshSelected(); } void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y ) @@ -3100,7 +3332,7 @@ void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y ) } } -void wxListMainWindow::GetImageSize( int index, int &width, int &height ) +void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const { if ( HasFlag(wxLC_ICON) && m_normal_image_list ) { @@ -3125,9 +3357,9 @@ void wxListMainWindow::GetImageSize( int index, int &width, int &height ) } } -int wxListMainWindow::GetTextLength( const wxString &s ) +int wxListMainWindow::GetTextLength( const wxString &s ) const { - wxClientDC dc( this ); + wxClientDC dc( wxConstCast(this, wxListMainWindow) ); dc.SetFont( GetFont() ); wxCoord lw; @@ -3322,19 +3554,21 @@ void wxListMainWindow::SetItem( wxListItem &item ) wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(), _T("invalid item index in SetItem") ); - if ( IsVirtual() ) + if ( !IsVirtual() ) + { + wxListLineData *line = GetLine((size_t)id); + line->SetItem( item.m_col, item ); + } + + if ( InReportView() ) { // just refresh the line to show the new value of the text/image RefreshLine((size_t)id); } - else // !virtual + else // !report { + // refresh everything (resulting in horrible flicker - FIXME!) m_dirty = TRUE; - - wxListLineData *line = GetLine((size_t)id); - if ( HasFlag(wxLC_REPORT) ) - item.m_width = GetColumnWidth( item.m_col ); - line->SetItem( item.m_col, item ); } } @@ -3344,8 +3578,9 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) _T("invalid list ctrl item index in SetItem") ); size_t oldCurrent = m_current; - size_t item = (size_t)litem; // sdafe because of the check above + size_t item = (size_t)litem; // safe because of the check above + // do we need to change the focus? if ( stateMask & wxLIST_STATE_FOCUSED ) { if ( state & wxLIST_STATE_FOCUSED ) @@ -3357,9 +3592,13 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) m_current = item; OnFocusLine( m_current ); - if ( IsSingleSel() && (oldCurrent != (size_t)-1) ) + if ( oldCurrent != (size_t)-1 ) { - HighlightLine(oldCurrent, FALSE); + if ( IsSingleSel() ) + { + HighlightLine(oldCurrent, FALSE); + } + RefreshLine(oldCurrent); } @@ -3373,10 +3612,13 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) { OnUnfocusLine( m_current ); m_current = (size_t)-1; + + RefreshLine( oldCurrent ); } } } + // do we need to change the selection state? if ( stateMask & wxLIST_STATE_SELECTED ) { bool on = (state & wxLIST_STATE_SELECTED) != 0; @@ -3460,7 +3702,10 @@ void wxListMainWindow::SetItemCount(long count) m_selStore.SetItemCount(count); m_countVirt = count; - Refresh(); + ResetVisibleLinesRange(); + + // scrollbars must be reset + m_dirty = TRUE; } int wxListMainWindow::GetSelectedItemCount() @@ -3497,15 +3742,7 @@ void wxListMainWindow::GetItemRect( long index, wxRect &rect ) wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(), _T("invalid index in GetItemRect") ); - if ( HasFlag(wxLC_REPORT) ) - { - rect = GetDummyLine()->GetRect(); - rect.y = GetLineY((size_t)index); - } - else - { - rect = GetLine((size_t)index)->GetRect(); - } + rect = GetLineRect((size_t)index); CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y); } @@ -3525,11 +3762,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() ); @@ -3566,18 +3800,6 @@ void wxListMainWindow::RecalculatePositions() GetScrollPos(wxHORIZONTAL), GetScrollPos(wxVERTICAL), TRUE ); - - // FIXME: wxGTK::wxScrolledWindow doesn't have SetTargetRect() -#if !defined(__WXGTK__) || defined(__WXUNIVERSAL__) - // we must have an integer number of lines on screen and so we fit - // the real control size to the line height - wxRect rect; - rect.x = 0; - rect.y = LINE_SPACING; - rect.width = clientWidth; - rect.height = ((clientHeight - LINE_SPACING) / lineHeight)*lineHeight; - SetTargetRect(rect); -#endif } else // !report { @@ -3606,7 +3828,7 @@ void wxListMainWindow::RecalculatePositions() line->CalculateSize( &dc, iconSpacing ); line->SetPosition( x, y, clientWidth, iconSpacing ); - wxSize sizeLine = line->GetSize(); + wxSize sizeLine = GetLineSize(i); if ( maxWidth < sizeLine.x ) maxWidth = sizeLine.x; @@ -3642,10 +3864,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() @@ -3654,7 +3879,7 @@ void wxListMainWindow::RefreshAll() Refresh(); wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; - if ( headerWin ) + if ( headerWin && headerWin->m_dirty ) { headerWin->m_dirty = FALSE; headerWin->Refresh(); @@ -3726,26 +3951,26 @@ 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 - 2 ) + // 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 = count - 2; + m_current--; } } - SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); + if ( InReportView() ) + { + ResetVisibleLinesRange(); + } if ( IsVirtual() ) { - if ( m_lineTo == --m_countVirt ) - { - m_lineTo--; - } + m_countVirt--; m_selStore.OnItemDelete(index); } @@ -3753,6 +3978,13 @@ void wxListMainWindow::DeleteItem( long lindex ) { m_lines.RemoveAt( index ); } + + // we need to refresh the (vert) scrollbar as the number of items changed + m_dirty = TRUE; + + SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); + + RefreshAfter(index); } void wxListMainWindow::DeleteColumn( int col ) @@ -3765,7 +3997,7 @@ void wxListMainWindow::DeleteColumn( int col ) m_columns.DeleteNode( node ); } -void wxListMainWindow::DeleteAllItems() +void wxListMainWindow::DoDeleteAllItems() { if ( IsEmpty() ) { @@ -3773,8 +4005,6 @@ void wxListMainWindow::DeleteAllItems() return; } - m_dirty = TRUE; - ResetCurrent(); // to make the deletion of all items faster, we don't send the @@ -3790,12 +4020,22 @@ void wxListMainWindow::DeleteAllItems() { m_countVirt = 0; + m_selStore.Clear(); + } + + if ( InReportView() ) + { ResetVisibleLinesRange(); } m_lines.Clear(); +} - m_selStore.Clear(); +void wxListMainWindow::DeleteAllItems() +{ + DoDeleteAllItems(); + + RecalculatePositions(); } void wxListMainWindow::DeleteEverything() @@ -3815,14 +4055,13 @@ 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 ) + { + RecalculatePositions(TRUE /* no refresh */); + } - size_t oldCurrent = m_current; - m_current = (size_t)index; - MoveToFocus(); - m_current = oldCurrent; + MoveToItem((size_t)index); } long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(partial) ) @@ -3866,15 +4105,23 @@ long wxListMainWindow::HitTest( int x, int y, int &flags ) { CalcUnscrolledPosition( x, y, &x, &y ); - size_t count = GetItemCount(); - for (size_t i = 0; i < count; i++) + if ( HasFlag(wxLC_REPORT) ) { - wxListLineData *line = GetLine(i); - long ret = line->IsHit( x, y ); - if (ret) + size_t current = y / GetLineHeight(); + flags = HitTestLine(current, x, y); + if ( flags ) + return current; + } + else // !report + { + // TODO: optimize it too! this is less simple than for report view but + // enumerating all items is still not a way to do it!! + size_t count = GetItemCount(); + for ( size_t current = 0; current < count; current++ ) { - flags = (int)ret; - return i; + flags = HitTestLine(current, x, y); + if ( flags ) + return current; } } @@ -3911,11 +4158,14 @@ void wxListMainWindow::InsertItem( wxListItem &item ) wxFAIL_MSG( _T("unknown mode") ); } - wxListLineData *line = new wxListLineData( this, id ); + wxListLineData *line = new wxListLineData(this); line->SetItem( 0, item ); m_lines.Insert( line, id ); + + m_dirty = TRUE; + RefreshLines(id, GetItemCount() - 1); } void wxListMainWindow::InsertColumn( long col, wxListItem &item ) @@ -3994,23 +4244,49 @@ void wxListMainWindow::OnScroll(wxScrollWinEvent& event) } } +int wxListMainWindow::GetCountPerPage() const +{ + if ( !m_linesPerPage ) + { + wxConstCast(this, wxListMainWindow)-> + m_linesPerPage = GetClientSize().y / GetLineHeight(); + } + + return m_linesPerPage; +} + void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to) { wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("this is for report mode only") ); if ( m_lineFrom == (size_t)-1 ) { - m_lineFrom = GetScrollPos(wxVERTICAL); - size_t count = GetItemCount(); + if ( count ) + { + m_lineFrom = GetScrollPos(wxVERTICAL); - wxASSERT_MSG( m_lineFrom < count, _T("invalid scroll position?") ); + // this may happen if SetScrollbars() hadn't been called yet + if ( m_lineFrom >= count ) + m_lineFrom = count - 1; - m_lineTo = m_lineFrom + m_linesPerPage - 1; - if ( m_lineTo >= count ) - m_lineTo = count - 1; + // we redraw one extra line but this is needed to make the redrawing + // logic work when there is a fractional number of lines on screen + m_lineTo = m_lineFrom + m_linesPerPage; + if ( m_lineTo >= count ) + m_lineTo = count - 1; + } + else // empty control + { + m_lineFrom = 0; + m_lineTo = (size_t)-1; + } } + wxASSERT_MSG( IsEmpty() || + (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()), + _T("GetVisibleLinesRange() returns incorrect result") ); + if ( from ) *from = m_lineFrom; if ( to ) @@ -4115,6 +4391,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) @@ -4239,36 +4516,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); } } @@ -4659,10 +4935,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 ); @@ -4671,8 +4954,6 @@ void wxListCtrl::OnSize(wxSizeEvent& event) { m_mainWin->SetSize( 0, 0, cw, ch ); } - - m_mainWin->RecalculatePositions(); } void wxListCtrl::OnIdle( wxIdleEvent & event ) @@ -4810,6 +5091,15 @@ int wxListCtrl::OnGetItemImage(long item) const return -1; } +wxListItemAttr *wxListCtrl::OnGetItemAttr(long item) const +{ + wxASSERT_MSG( item >= 0 && item < GetItemCount(), + _T("invalid item index in OnGetItemAttr()") ); + + // no attributes by default + return NULL; +} + void wxListCtrl::SetItemCount(long count) { wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); @@ -4817,4 +5107,14 @@ void wxListCtrl::SetItemCount(long count) m_mainWin->SetItemCount(count); } +void wxListCtrl::RefreshItem(long item) +{ + m_mainWin->RefreshLine(item); +} + +void wxListCtrl::RefreshItems(long itemFrom, long itemTo) +{ + m_mainWin->RefreshLines(itemFrom, itemTo); +} + #endif // wxUSE_LISTCTRL