/////////////////////////////////////////////////////////////////////////////
/*
- 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();
-
- => done
-
- 5. attributes support: we need OnGetItemAttr() as well!
+ 1. we need to implement searching/sorting for virtual controls somehow
+ 2. when changing selection the lines are refreshed twice
*/
// ============================================================================
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;
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 )
{
if ( selOld.Index(item) == wxNOT_FOUND )
m_itemsSel.Add(item);
}
+
+ // many items (> half) changed state
+ itemsChanged = NULL;
}
else // select == m_defaultState
{
// 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);
}
}
}
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)
const wxRect& rectHL,
bool highlighted )
{
- // use our own flag if we maintain it
- if ( !IsVirtual() )
- highlighted = m_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
}
}
-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);
}
}
void wxListMainWindow::RefreshLine( size_t line )
{
+ 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 );
GetLine(line)->DrawInReportMode( &dc,
rectLine,
GetLineHighlightRect(line),
- IsHighlighted(line) );
+ m_hasFocus && IsHighlighted(line) );
}
if ( HasFlag(wxLC_HRULES) )
}
}
- if ( HasCurrent() && m_hasFocus )
+ if ( HasCurrent() )
{
+ // don't draw rect outline under Max if we already have the background
+ // color
#ifdef __WXMAC__
- // no rect outline, we already have the background color
-#else
- dc.SetPen( *wxBLACK_PEN );
- dc.SetBrush( *wxTRANSPARENT_BRUSH );
- dc.DrawRectangle( GetLineHighlightRect(m_current) );
-#endif
+ if ( !m_hasFocus )
+#endif // !__WXMAC__
+ {
+ dc.SetPen( *wxBLACK_PEN );
+ dc.SetBrush( *wxTRANSPARENT_BRUSH );
+ dc.DrawRectangle( GetLineHighlightRect(m_current) );
+ }
}
dc.EndDrawing();
if ( point != wxDefaultPosition )
le.m_pointDrag = point;
- if ( command != wxEVT_COMMAND_LIST_DELETE_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 );
}
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:
_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 )
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);
}
{
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;
Refresh();
wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
- if ( headerWin )
+ if ( headerWin && headerWin->m_dirty )
{
headerWin->m_dirty = FALSE;
headerWin->Refresh();
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 );
// -------------------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
+IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
BEGIN_EVENT_TABLE(wxListCtrl,wxControl)
EVT_SIZE(wxListCtrl::OnSize)