X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5175dbbd9f20e7fd39e0820ea510c1c6ea349584..570aaadf1ff3f8c3f8ecfea44dbeae64532c82c4:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 02a2580a92..d2f8c97ac6 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -73,6 +73,8 @@ #include "wx/gtk/win_gtk.h" #endif +#include "wx/selstore.h" + // ---------------------------------------------------------------------------- // events // ---------------------------------------------------------------------------- @@ -136,83 +138,6 @@ static const int IMAGE_MARGIN_IN_REPORT_MODE = 5; // private classes // ============================================================================ -// ---------------------------------------------------------------------------- -// wxSelectionStore -// ---------------------------------------------------------------------------- - -int CMPFUNC_CONV wxSizeTCmpFn(size_t n1, size_t n2) { return n1 - n2; } - -WX_DEFINE_SORTED_EXPORTED_ARRAY_LONG(size_t, wxIndexArray); - -// this class is used to store the selected items in the virtual list control -// (but it is not tied to list control and so can be used with other controls -// such as wxListBox in wxUniv) -// -// the idea is to make it really smart later (i.e. store the selections as an -// array of ranes + individual items) but, as I don't have time to do it now -// (this would require writing code to merge/break ranges and much more) keep -// it simple but define a clean interface to it which allows it to be made -// smarter later -class WXDLLEXPORT wxSelectionStore -{ -public: - wxSelectionStore() : m_itemsSel(wxSizeTCmpFn) { Init(); } - - // set the total number of items we handle - void SetItemCount(size_t count) { m_count = count; } - - // special case of SetItemCount(0) - void Clear() { m_itemsSel.Clear(); m_count = 0; m_defaultState = FALSE; } - - // must be called when a new item is inserted/added - void OnItemAdd(size_t item) { wxFAIL_MSG( _T("TODO") ); } - - // must be called when an item is deleted - void OnItemDelete(size_t item); - - // select one item, use SelectRange() insted if possible! - // - // returns true if the items selection really changed - bool SelectItem(size_t item, bool select = TRUE); - - // select the range of items - // - // 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 the total number of selected items - size_t GetSelectedCount() const - { - return m_defaultState ? m_count - m_itemsSel.GetCount() - : m_itemsSel.GetCount(); - } - -private: - // (re)init - void Init() { m_defaultState = FALSE; } - - // the total number of items we handle - size_t m_count; - - // the default state: normally, FALSE (i.e. off) but maybe set to TRUE if - // there are more selected items than non selected ones - this allows to - // handle selection of all items efficiently - bool m_defaultState; - - // the array of items whose selection state is different from default - wxIndexArray m_itemsSel; - - DECLARE_NO_COPY_CLASS(wxSelectionStore) -}; - //----------------------------------------------------------------------------- // wxListItemData (internal) //----------------------------------------------------------------------------- @@ -364,7 +289,11 @@ public: public: wxListLineData(wxListMainWindow *owner); - ~wxListLineData() { delete m_gi; } + ~wxListLineData() + { + WX_CLEAR_LIST(wxListItemDataList, m_items); + delete m_gi; + } // are we in report mode? inline bool InReportView() const; @@ -652,6 +581,7 @@ public: void OnRenameTimer(); bool OnRenameAccept(size_t itemEdit, const wxString& value); + void OnRenameCancelled(size_t itemEdit); void OnMouse( wxMouseEvent &event ); @@ -903,183 +833,6 @@ private: // implementation // ============================================================================ -// ---------------------------------------------------------------------------- -// wxSelectionStore -// ---------------------------------------------------------------------------- - -bool wxSelectionStore::IsSelected(size_t item) const -{ - bool isSel = m_itemsSel.Index(item) != wxNOT_FOUND; - - // if the default state is to be selected, being in m_itemsSel means that - // the item is not selected, so we have to inverse the logic - return m_defaultState ? !isSel : isSel; -} - -bool wxSelectionStore::SelectItem(size_t item, bool select) -{ - // search for the item ourselves as like this we get the index where to - // insert it later if needed, so we do only one search in the array instead - // of two (adding item to a sorted array requires a search) - size_t index = m_itemsSel.IndexForInsert(item); - bool isSel = index < m_itemsSel.GetCount() && m_itemsSel[index] == item; - - if ( select != m_defaultState ) - { - if ( !isSel ) - { - m_itemsSel.AddAt(item, index); - - return TRUE; - } - } - else // reset to default state - { - if ( isSel ) - { - m_itemsSel.RemoveAt(index); - return TRUE; - } - } - - return FALSE; -} - -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 ( select != m_defaultState ) - { - // the default state now becomes the same as 'select' - m_defaultState = select; - - // so all the old selections (which had state select) shouldn't be - // selected any more, but all the other ones should - wxIndexArray selOld = m_itemsSel; - m_itemsSel.Empty(); - - // TODO: it should be possible to optimize the searches a bit - // knowing the possible range - - size_t item; - for ( item = 0; item < itemFrom; item++ ) - { - if ( selOld.Index(item) == wxNOT_FOUND ) - m_itemsSel.Add(item); - } - - for ( item = itemTo + 1; item < m_count; item++ ) - { - if ( selOld.Index(item) == wxNOT_FOUND ) - m_itemsSel.Add(item); - } - - // many items (> half) changed state - itemsChanged = NULL; - } - else // select == m_defaultState - { - // get the inclusive range of items between itemFrom and itemTo - size_t count = m_itemsSel.GetCount(), - start = m_itemsSel.IndexForInsert(itemFrom), - end = m_itemsSel.IndexForInsert(itemTo); - - if ( start == count || m_itemsSel[start] < itemFrom ) - { - start++; - } - - if ( end == count || m_itemsSel[end] > itemTo ) - { - end--; - } - - if ( start <= end ) - { - // 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; - } - else - { - 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++ ) - { - 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) -{ - size_t count = m_itemsSel.GetCount(), - i = m_itemsSel.IndexForInsert(item); - - if ( i < count && m_itemsSel[i] == 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 - while ( i < count ) - { - // all following elements must be greater than the one we deleted - wxASSERT_MSG( m_itemsSel[i] > item, _T("logic error") ); - - m_itemsSel[i++]--; - } -} - //----------------------------------------------------------------------------- // wxListItemData //----------------------------------------------------------------------------- @@ -1341,7 +1094,6 @@ inline bool wxListLineData::IsVirtual() const wxListLineData::wxListLineData( wxListMainWindow *owner ) { m_owner = owner; - m_items.DeleteContents( TRUE ); if ( InReportView() ) { @@ -1359,7 +1111,7 @@ wxListLineData::wxListLineData( wxListMainWindow *owner ) void wxListLineData::CalculateSize( wxDC *dc, int spacing ) { - wxListItemDataList::Node *node = m_items.GetFirst(); + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); wxCHECK_RET( node, _T("no subitems at all??") ); wxListItemData *item = node->GetData(); @@ -1469,7 +1221,7 @@ void wxListLineData::SetPosition( int x, int y, int window_width, int spacing ) { - wxListItemDataList::Node *node = m_items.GetFirst(); + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); wxCHECK_RET( node, _T("no subitems at all??") ); wxListItemData *item = node->GetData(); @@ -1542,7 +1294,7 @@ void wxListLineData::InitItems( int num ) void wxListLineData::SetItem( int index, const wxListItem &info ) { - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); wxCHECK_RET( node, _T("invalid column index in SetItem") ); wxListItemData *item = node->GetData(); @@ -1551,7 +1303,7 @@ void wxListLineData::SetItem( int index, const wxListItem &info ) void wxListLineData::GetItem( int index, wxListItem &info ) { - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); if (node) { wxListItemData *item = node->GetData(); @@ -1563,7 +1315,7 @@ wxString wxListLineData::GetText(int index) const { wxString s; - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); if (node) { wxListItemData *item = node->GetData(); @@ -1575,7 +1327,7 @@ wxString wxListLineData::GetText(int index) const void wxListLineData::SetText( int index, const wxString s ) { - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); if (node) { wxListItemData *item = node->GetData(); @@ -1585,7 +1337,7 @@ void wxListLineData::SetText( int index, const wxString s ) void wxListLineData::SetImage( int index, int image ) { - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); wxCHECK_RET( node, _T("invalid column index in SetImage()") ); wxListItemData *item = node->GetData(); @@ -1594,7 +1346,7 @@ void wxListLineData::SetImage( int index, int image ) int wxListLineData::GetImage( int index ) const { - wxListItemDataList::Node *node = m_items.Item( index ); + wxListItemDataList::compatibility_iterator node = m_items.Item( index ); wxCHECK_MSG( node, -1, _T("invalid column index in GetImage()") ); wxListItemData *item = node->GetData(); @@ -1603,7 +1355,7 @@ int wxListLineData::GetImage( int index ) const wxListItemAttr *wxListLineData::GetAttr() const { - wxListItemDataList::Node *node = m_items.GetFirst(); + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") ); wxListItemData *item = node->GetData(); @@ -1612,7 +1364,7 @@ wxListItemAttr *wxListLineData::GetAttr() const void wxListLineData::SetAttr(wxListItemAttr *attr) { - wxListItemDataList::Node *node = m_items.GetFirst(); + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); wxCHECK_RET( node, _T("invalid column index in SetAttr()") ); wxListItemData *item = node->GetData(); @@ -1685,7 +1437,7 @@ bool wxListLineData::SetAttributes(wxDC *dc, void wxListLineData::Draw( wxDC *dc ) { - wxListItemDataList::Node *node = m_items.GetFirst(); + wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); wxCHECK_RET( node, _T("no subitems at all??") ); bool highlighted = IsHighlighted(); @@ -1732,7 +1484,7 @@ void wxListLineData::DrawInReportMode( wxDC *dc, y = rect.y + (LINE_SPACING + EXTRA_HEIGHT) / 2; size_t col = 0; - for ( wxListItemDataList::Node *node = m_items.GetFirst(); + for ( wxListItemDataList::compatibility_iterator node = m_items.GetFirst(); node; node = node->GetNext(), col++ ) { @@ -2357,6 +2109,7 @@ void wxListTextCtrl::OnChar( wxKeyEvent &event ) case WXK_ESCAPE: Finish(); + m_owner->OnRenameCancelled( m_itemEdited ); break; default: @@ -2391,11 +2144,13 @@ void wxListTextCtrl::OnKillFocus( wxFocusEvent &event ) { if ( !m_finished ) { - (void)AcceptChanges(); - + // We must finish regardless of success, otherwise we'll get focus problems Finish(); + + if ( !AcceptChanges() ) + m_owner->OnRenameCancelled( m_itemEdited ); } - + event.Skip(); } @@ -2417,7 +2172,6 @@ END_EVENT_TABLE() void wxListMainWindow::Init() { - m_columns.DeleteContents( TRUE ); m_dirty = TRUE; m_countVirt = 0; m_lineFrom = @@ -2513,6 +2267,7 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, wxListMainWindow::~wxListMainWindow() { DoDeleteAllItems(); + WX_CLEAR_LIST(wxListHeaderDataList, m_columns); delete m_highlightBrush; delete m_highlightUnfocusedBrush; @@ -3139,6 +2894,37 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) le.IsAllowed(); } +#ifdef __VMS__ // Ignore unreacheable code +# pragma message disable initnotreach +#endif + +void wxListMainWindow::OnRenameCancelled(size_t itemEdit) +{ + // wxMSW seems not to notify the program about + // cancelled label edits. + return; + + // let owner know that the edit was cancelled + wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); + + // These only exist for wxTreeCtrl, which should probably be changed + // le.m_editCancelled = TRUE; + // le.m_label = wxEmptyString; + + le.SetEventObject( GetParent() ); + le.m_itemIndex = itemEdit; + + wxListLineData *data = GetLine(itemEdit); + wxCHECK_RET( data, _T("invalid index in OnRenameCancelled()") ); + + data->GetItem( 0, le.m_item ); + + GetEventHandler()->ProcessEvent( le ); +} +#ifdef __VMS__ +# pragma message enable initnotreach +#endif + void wxListMainWindow::OnMouse( wxMouseEvent &event ) { event.SetEventObject( GetParent() ); @@ -3203,6 +2989,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) wxListEvent le( command, GetParent()->GetId() ); le.SetEventObject( GetParent() ); + le.m_itemIndex = current; le.m_pointDrag = m_dragStart; GetParent()->GetEventHandler()->ProcessEvent( le ); @@ -3729,7 +3516,7 @@ int wxListMainWindow::GetItemSpacing( bool isSmall ) void wxListMainWindow::SetColumn( int col, wxListItem &item ) { - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); wxCHECK_RET( node, _T("invalid column index in SetColumn") ); @@ -3762,7 +3549,7 @@ void wxListMainWindow::SetColumnWidth( int col, int width ) if ( headerWin ) headerWin->m_dirty = TRUE; - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); wxCHECK_RET( node, _T("no column?") ); wxListHeaderData *column = node->GetData(); @@ -3790,7 +3577,7 @@ void wxListMainWindow::SetColumnWidth( int col, int width ) for ( size_t i = 0; i < count; i++ ) { wxListLineData *line = GetLine(i); - wxListItemDataList::Node *n = line->m_items.Item( col ); + wxListItemDataList::compatibility_iterator n = line->m_items.Item( col ); wxCHECK_RET( n, _T("no subitem?") ); @@ -3843,7 +3630,7 @@ int wxListMainWindow::GetHeaderWidth() const void wxListMainWindow::GetColumn( int col, wxListItem &item ) const { - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); wxCHECK_RET( node, _T("invalid column index in GetColumn") ); wxListHeaderData *column = node->GetData(); @@ -3852,7 +3639,7 @@ void wxListMainWindow::GetColumn( int col, wxListItem &item ) const int wxListMainWindow::GetColumnWidth( int col ) const { - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); wxCHECK_MSG( node, 0, _T("invalid column index") ); wxListHeaderData *column = node->GetData(); @@ -4329,12 +4116,25 @@ void wxListMainWindow::DeleteItem( long lindex ) void wxListMainWindow::DeleteColumn( int col ) { - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); wxCHECK_RET( node, wxT("invalid column index in DeleteColumn()") ); m_dirty = TRUE; - m_columns.DeleteNode( node ); + delete node->GetData(); + m_columns.Erase( node ); + + if ( !IsVirtual() ) + { + // update all the items + for ( size_t i = 0; i < m_lines.GetCount(); i++ ) + { + wxListLineData * const line = GetLine(i); + wxListItemDataList::compatibility_iterator n = line->m_items.Item( col ); + delete n->GetData(); + line->m_items.Erase(n); + } + } // invalidate it as it has to be recalculated m_headerWidth = 0; @@ -4383,7 +4183,7 @@ void wxListMainWindow::DeleteAllItems() void wxListMainWindow::DeleteEverything() { - m_columns.Clear(); + WX_CLEAR_LIST(wxListHeaderDataList, m_columns); DeleteAllItems(); } @@ -4515,6 +4315,9 @@ void wxListMainWindow::InsertItem( wxListItem &item ) m_lines.Insert( line, id ); m_dirty = TRUE; + + SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM); + RefreshLines(id, GetItemCount() - 1); } @@ -4525,10 +4328,12 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item ) { if (item.m_width == wxLIST_AUTOSIZE_USEHEADER) item.m_width = GetTextLength( item.m_text ); + wxListHeaderData *column = new wxListHeaderData( item ); - if ((col >= 0) && (col < (int)m_columns.GetCount())) + bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount()); + if ( insert ) { - wxListHeaderDataList::Node *node = m_columns.Item( col ); + wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col ); m_columns.Insert( node, column ); } else @@ -4536,6 +4341,20 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item ) m_columns.Append( column ); } + if ( !IsVirtual() ) + { + // update all the items + for ( size_t i = 0; i < m_lines.GetCount(); i++ ) + { + wxListLineData * const line = GetLine(i); + wxListItemData * const data = new wxListItemData(this); + if ( insert ) + line->m_items.Insert(col, data); + else + line->m_items.Append(data); + } + } + // invalidate it as it has to be recalculated m_headerWidth = 0; } @@ -4652,7 +4471,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl) BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl) EVT_SIZE(wxGenericListCtrl::OnSize) - EVT_IDLE(wxGenericListCtrl::OnIdle) END_EVENT_TABLE() wxGenericListCtrl::wxGenericListCtrl() @@ -4717,7 +4535,7 @@ bool wxGenericListCtrl::Create(wxWindow *parent, return FALSE; // don't create the inner window with the border - style &= ~wxSUNKEN_BORDER; + style &= ~wxBORDER_MASK; m_mainWin = new wxListMainWindow( this, -1, wxPoint(0,0), size, style ); @@ -4913,6 +4731,8 @@ bool wxGenericListCtrl::SetItemData( long item, long data ) bool wxGenericListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const { m_mainWin->GetItemRect( item, rect ); + if ( m_mainWin->HasHeader() ) + rect.y += HEADER_HEIGHT + 1; return TRUE; } @@ -5176,7 +4996,7 @@ long wxGenericListCtrl::InsertColumn( long col, wxListItem &item ) // if we hadn't had header before and have it now we need to relayout the // window - if ( GetColumnCount() == 1 ) + if ( GetColumnCount() == 1 && m_mainWin->HasHeader() ) { ResizeReportView(TRUE /* have header */); } @@ -5253,10 +5073,10 @@ void wxGenericListCtrl::ResizeReportView(bool showHeader) } } -void wxGenericListCtrl::OnIdle( wxIdleEvent & event ) +void wxGenericListCtrl::OnInternalIdle() { - event.Skip(); - + wxWindow::OnInternalIdle(); + // do it only if needed if ( !m_mainWin->m_dirty ) return; @@ -5414,6 +5234,57 @@ void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo) m_mainWin->RefreshLines(itemFrom, itemTo); } +/* + * Generic wxListCtrl is more or less a container for two other + * windows which drawings are done upon. These are namely + * 'm_headerWin' and 'm_mainWin'. + * Here we override 'virtual wxWindow::Refresh()' to mimic the + * behaviour wxListCtrl has under wxMSW. + */ +void wxGenericListCtrl::Refresh(bool eraseBackground, const wxRect *rect) +{ + if (!rect) + { + // The easy case, no rectangle specified. + if (m_headerWin) + m_headerWin->Refresh(eraseBackground); + + if (m_mainWin) + m_mainWin->Refresh(eraseBackground); + } + else + { + // Refresh the header window + if (m_headerWin) + { + wxRect rectHeader = m_headerWin->GetRect(); + rectHeader.Intersect(*rect); + if (rectHeader.GetWidth() && rectHeader.GetHeight()) + { + int x, y; + m_headerWin->GetPosition(&x, &y); + rectHeader.Offset(-x, -y); + m_headerWin->Refresh(eraseBackground, &rectHeader); + } + + } + + // Refresh the main window + if (m_mainWin) + { + wxRect rectMain = m_mainWin->GetRect(); + rectMain.Intersect(*rect); + if (rectMain.GetWidth() && rectMain.GetHeight()) + { + int x, y; + m_mainWin->GetPosition(&x, &y); + rectMain.Offset(-x, -y); + m_mainWin->Refresh(eraseBackground, &rectMain); + } + } + } +} + void wxGenericListCtrl::Freeze() { m_mainWin->Freeze();