X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2b5f62a0b2db198609b45dec622a018dae37008e..33cc6200a8c797c72536ca319148c0f712cf658a:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 51f600fa5b..1d3d6605ec 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -23,7 +23,7 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "listctrl.h" #pragma implementation "listctrlbase.h" #endif @@ -68,11 +68,21 @@ IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl) #endif // HAVE_NATIVE_LISTCTRL/!HAVE_NATIVE_LISTCTRL -#if defined(__WXGTK__) - #include - #include "wx/gtk/win_gtk.h" +#include "wx/selstore.h" +#include "wx/renderer.h" +#include "wx/math.h" + +#ifdef __WXMAC__ + #include "wx/mac/private.h" #endif + + +// NOTE: If using the wxListBox visual attributes works everywhere then this can +// be removed, as well as the #else case below. +#define _USE_VISATTR 0 + + // ---------------------------------------------------------------------------- // events // ---------------------------------------------------------------------------- @@ -83,8 +93,10 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_END_LABEL_EDIT) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ITEM) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS) +#if WXWIN_COMPATIBILITY_2_4 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_GET_INFO) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_SET_INFO) +#endif DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_SELECTED) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN) @@ -104,24 +116,29 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT) // constants // ---------------------------------------------------------------------------- -// the height of the header window (FIXME: should depend on its font!) -static const int HEADER_HEIGHT = 23; +// // the height of the header window (FIXME: should depend on its font!) +// static const int HEADER_HEIGHT = 23; -// the scrollbar units static const int SCROLL_UNIT_X = 15; -static const int SCROLL_UNIT_Y = 15; // the spacing between the lines (in report mode) static const int LINE_SPACING = 0; // extra margins around the text label -static const int EXTRA_WIDTH = 3; +static const int EXTRA_WIDTH = 4; static const int EXTRA_HEIGHT = 4; +// margin between the window and the items +static const int EXTRA_BORDER_X = 2; +static const int EXTRA_BORDER_Y = 2; + // offset for the header window static const int HEADER_OFFSET_X = 1; static const int HEADER_OFFSET_Y = 1; +// margin between rows of icons in [small] icon view +static const int MARGIN_BETWEEN_ROWS = 6; + // when autosizing the columns, add some slack static const int AUTOSIZE_COL_MARGIN = 10; @@ -136,83 +153,25 @@ 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); +//----------------------------------------------------------------------------- +// wxColWidthInfo (internal) +//----------------------------------------------------------------------------- -// 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 +struct wxColWidthInfo { -public: - wxSelectionStore() : m_itemsSel(wxSizeTCmpFn) { Init(); } + int nMaxWidth; + bool bNeedsUpdate; // only set to true when an item whose + // width == nMaxWidth is removed - // 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 + wxColWidthInfo(int w = 0, bool needsUpdate = false) { - return m_defaultState ? m_count - m_itemsSel.GetCount() - : m_itemsSel.GetCount(); + nMaxWidth = w; + bNeedsUpdate = needsUpdate; } - -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) }; +WX_DEFINE_ARRAY_PTR(wxColWidthInfo *, ColWidthArray); + //----------------------------------------------------------------------------- // wxListItemData (internal) //----------------------------------------------------------------------------- @@ -225,7 +184,7 @@ public: void SetItem( const wxListItem &info ); void SetImage( int image ) { m_image = image; } - void SetData( long data ) { m_data = data; } + void SetData( wxUIntPtr data ) { m_data = data; } void SetPosition( int x, int y ); void SetSize( int width, int height ); @@ -264,7 +223,7 @@ public: int m_image; // user data associated with the item - long m_data; + wxUIntPtr m_data; // the item coordinates are not used in report mode, instead this pointer // is NULL and the owner window is used to retrieve the item position and @@ -333,7 +292,7 @@ WX_DECLARE_LIST(wxListItemData, wxListItemDataList); #include "wx/listimpl.cpp" WX_DEFINE_LIST(wxListItemDataList); -class WXDLLEXPORT wxListLineData +class wxListLineData { public: // the list of subitems: only may have more than one item in report mode @@ -353,6 +312,18 @@ public: // the part to be highlighted wxRect m_rectHighlight; + + // extend all our rects to be centered inside the one of given width + void ExtendWidth(wxCoord w) + { + wxASSERT_MSG( m_rectAll.width <= w, + _T("width can only be increased") ); + + m_rectAll.width = w; + m_rectLabel.x = m_rectAll.x + (w - m_rectLabel.width)/2; + m_rectIcon.x = m_rectAll.x + (w - m_rectIcon.width)/2; + m_rectHighlight.x = m_rectAll.x + (w - m_rectHighlight.width)/2; + } } *m_gi; // is this item selected? [NB: not used in virtual mode] @@ -364,7 +335,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; @@ -379,7 +354,7 @@ public: void CalculateSize( wxDC *dc, int spacing ); // remember the position this line appears at - void SetPosition( int x, int y, int window_width, int spacing ); + void SetPosition( int x, int y, int spacing ); // wxListCtrl API @@ -480,7 +455,6 @@ public: virtual ~wxListHeaderWindow(); - void DoDrawRect( wxDC *dc, int x, int y, int w, int h ); void DrawCurrent(); void AdjustDC(wxDC& dc); @@ -495,7 +469,9 @@ private: // common part of all ctors void Init(); - void SendListEvent(wxEventType type, wxPoint pos); + // generate and process the list event of the given type, return true if + // it wasn't vetoed, i.e. if we should proceed + bool SendListEvent(wxEventType type, wxPoint pos); DECLARE_DYNAMIC_CLASS(wxListHeaderWindow) DECLARE_EVENT_TABLE() @@ -537,6 +513,7 @@ private: wxString m_startValue; size_t m_itemEdited; bool m_finished; + bool m_aboutToFinish; DECLARE_EVENT_TABLE() }; @@ -549,7 +526,7 @@ WX_DECLARE_LIST(wxListHeaderData, wxListHeaderDataList); #include "wx/listimpl.cpp" WX_DEFINE_LIST(wxListHeaderDataList); -class WXDLLEXPORT wxListMainWindow : public wxScrolledWindow +class wxListMainWindow : public wxScrolledWindow { public: wxListMainWindow(); @@ -562,6 +539,8 @@ public: virtual ~wxListMainWindow(); + wxWindow *GetMainWindowOfCompositeControl() { return GetParent(); } + bool HasFlag(int flag) const { return m_parent->HasFlag(flag); } // return true if this is a virtual list control @@ -575,20 +554,20 @@ public: // do we have a header window? bool HasHeader() const - { return HasFlag(wxLC_REPORT) && !HasFlag(wxLC_NO_HEADER); } + { return InReportView() && !HasFlag(wxLC_NO_HEADER); } void HighlightAll( bool on ); // all these functions only do something if the line is currently visible - // change the line "selected" state, return TRUE if it really changed - bool HighlightLine( size_t line, bool highlight = TRUE); + // change the line "selected" state, return true if it really changed + bool HighlightLine( size_t line, bool highlight = true); // as HighlightLine() but do it for the range of lines: this is incredibly // more efficient for virtual list controls! // // NB: unlike HighlightLine() this one does refresh the lines on screen - void HighlightLines( size_t lineFrom, size_t lineTo, bool on = TRUE ); + void HighlightLines( size_t lineFrom, size_t lineTo, bool on = true ); // toggle the line state and refresh it void ReverseHighlight( size_t line ) @@ -646,10 +625,9 @@ public: void Freeze(); void Thaw(); - void SetFocus(); - void OnRenameTimer(); bool OnRenameAccept(size_t itemEdit, const wxString& value); + void OnRenameCancelled(size_t itemEdit); void OnMouse( wxMouseEvent &event ); @@ -669,8 +647,8 @@ public: int GetTextLength( const wxString &s ) const; void SetImageList( wxImageListType *imageList, int which ); - void SetItemSpacing( int spacing, bool isSmall = FALSE ); - int GetItemSpacing( bool isSmall = FALSE ); + void SetItemSpacing( int spacing, bool isSmall = false ); + int GetItemSpacing( bool isSmall = false ); void SetColumn( int col, wxListItem &item ); void SetColumnWidth( int col, int width ); @@ -686,8 +664,10 @@ public: void SetItem( wxListItem &item ); void GetItem( wxListItem &item ) const; void SetItemState( long item, long state, long stateMask ); + void SetItemStateAll( long state, long stateMask ); int GetItemState( long item, long stateMask ) const; void GetItemRect( long index, wxRect &rect ) const; + wxRect GetViewRect() const; bool GetItemPosition( long item, wxPoint& pos ) const; int GetSelectedItemCount() const; @@ -709,7 +689,7 @@ public: } // set the scrollbars and update the positions of the items - void RecalculatePositions(bool noRefresh = FALSE); + void RecalculatePositions(bool noRefresh = false); // refresh the window and the header void RefreshAll(); @@ -720,11 +700,13 @@ public: void DeleteColumn( int col ); void DeleteEverything(); void EnsureVisible( long index ); - long FindItem( long start, const wxString& str, bool partial = FALSE ); - long FindItem( long start, long data); + long FindItem( long start, const wxString& str, bool partial = false ); + long FindItem( long start, wxUIntPtr data); + long FindItem( const wxPoint& pt ); long HitTest( int x, int y, int &flags ); void InsertItem( wxListItem &item ); void InsertColumn( long col, wxListItem &item ); + int GetItemWidthWithImage(wxListItem * item); void SortItems( wxListCtrlCompare fn, long data ); size_t GetItemCount() const; @@ -745,11 +727,11 @@ public: virtual bool SetFont(const wxFont& font) { if ( !wxScrolledWindow::SetFont(font) ) - return FALSE; + return false; m_lineHeight = 0; - return TRUE; + return true; } // these are for wxListLineData usage only @@ -795,8 +777,6 @@ public: bool m_dirty; wxColour *m_highlightColour; - int m_xScroll, - m_yScroll; wxImageListType *m_small_image_list; wxImageListType *m_normal_image_list; int m_small_spacing; @@ -808,10 +788,12 @@ public: bool m_isCreated; int m_dragCount; wxPoint m_dragStart; + ColWidthArray m_aColWidths; // for double click logic size_t m_lineLastClicked, - m_lineBeforeLastClicked; + m_lineBeforeLastClicked, + m_lineSelectSingleOnUp; protected: // the total count of items in a virtual list control @@ -824,9 +806,6 @@ protected: // common part of all ctors void Init(); - // intiialize m_[xy]Scroll - void InitScrolling(); - // get the line data for the given index wxListLineData *GetLine(size_t n) const { @@ -901,183 +880,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 //----------------------------------------------------------------------------- @@ -1164,7 +966,7 @@ void wxListItemData::SetSize( int width, int height ) bool wxListItemData::IsHit( int x, int y ) const { - wxCHECK_MSG( m_rect, FALSE, _T("can't be called in this mode") ); + wxCHECK_MSG( m_rect, false, _T("can't be called in this mode") ); return wxRect(GetX(), GetY(), GetWidth(), GetHeight()).Inside(x, y); } @@ -1339,7 +1141,6 @@ inline bool wxListLineData::IsVirtual() const wxListLineData::wxListLineData( wxListMainWindow *owner ) { m_owner = owner; - m_items.DeleteContents( TRUE ); if ( InReportView() ) { @@ -1350,108 +1151,101 @@ wxListLineData::wxListLineData( wxListMainWindow *owner ) m_gi = new GeometryInfo; } - m_highlighted = FALSE; + m_highlighted = false; InitItems( GetMode() == wxLC_REPORT ? m_owner->GetColumnCount() : 1 ); } 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(); + wxString s; + wxCoord lw, lh; + switch ( GetMode() ) { case wxLC_ICON: case wxLC_SMALL_ICON: - { - m_gi->m_rectAll.width = spacing; - - wxString s = item->GetText(); - - wxCoord lw, lh; - if ( s.empty() ) - { - lh = - m_gi->m_rectLabel.width = - m_gi->m_rectLabel.height = 0; - } - else // has label - { - dc->GetTextExtent( s, &lw, &lh ); - if (lh < SCROLL_UNIT_Y) - lh = SCROLL_UNIT_Y; - lw += EXTRA_WIDTH; - lh += EXTRA_HEIGHT; - - m_gi->m_rectAll.height = spacing + lh; - if (lw > spacing) - m_gi->m_rectAll.width = lw; - - m_gi->m_rectLabel.width = lw; - m_gi->m_rectLabel.height = lh; - } + m_gi->m_rectAll.width = spacing; - if (item->HasImage()) - { - int w, h; - m_owner->GetImageSize( item->GetImage(), w, h ); - m_gi->m_rectIcon.width = w + 8; - m_gi->m_rectIcon.height = h + 8; - - if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width ) - m_gi->m_rectAll.width = m_gi->m_rectIcon.width; - if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 ) - m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4; - } + s = item->GetText(); - if ( item->HasText() ) - { - m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width; - m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height; - } - else // no text, highlight the icon - { - m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width; - m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height; - } + if ( s.empty() ) + { + lh = + m_gi->m_rectLabel.width = + m_gi->m_rectLabel.height = 0; } - break; - - case wxLC_LIST: + else // has label { - wxString s = item->GetTextForMeasuring(); - - wxCoord lw,lh; dc->GetTextExtent( s, &lw, &lh ); - if (lh < SCROLL_UNIT_Y) - lh = SCROLL_UNIT_Y; lw += EXTRA_WIDTH; lh += EXTRA_HEIGHT; + m_gi->m_rectAll.height = spacing + lh; + if (lw > spacing) + m_gi->m_rectAll.width = lw; + m_gi->m_rectLabel.width = lw; m_gi->m_rectLabel.height = lh; + } + + if (item->HasImage()) + { + int w, h; + m_owner->GetImageSize( item->GetImage(), w, h ); + m_gi->m_rectIcon.width = w + 8; + m_gi->m_rectIcon.height = h + 8; + + if ( m_gi->m_rectIcon.width > m_gi->m_rectAll.width ) + m_gi->m_rectAll.width = m_gi->m_rectIcon.width; + if ( m_gi->m_rectIcon.height + lh > m_gi->m_rectAll.height - 4 ) + m_gi->m_rectAll.height = m_gi->m_rectIcon.height + lh + 4; + } + + if ( item->HasText() ) + { + m_gi->m_rectHighlight.width = m_gi->m_rectLabel.width; + m_gi->m_rectHighlight.height = m_gi->m_rectLabel.height; + } + else // no text, highlight the icon + { + m_gi->m_rectHighlight.width = m_gi->m_rectIcon.width; + m_gi->m_rectHighlight.height = m_gi->m_rectIcon.height; + } + break; - m_gi->m_rectAll.width = lw; - m_gi->m_rectAll.height = lh; + case wxLC_LIST: + s = item->GetTextForMeasuring(); - if (item->HasImage()) - { - int w, h; - m_owner->GetImageSize( item->GetImage(), w, h ); - m_gi->m_rectIcon.width = w; - m_gi->m_rectIcon.height = h; - - m_gi->m_rectAll.width += 4 + w; - if (h > m_gi->m_rectAll.height) - m_gi->m_rectAll.height = h; - } + dc->GetTextExtent( s, &lw, &lh ); + lw += EXTRA_WIDTH; + lh += EXTRA_HEIGHT; + + m_gi->m_rectLabel.width = lw; + m_gi->m_rectLabel.height = lh; - m_gi->m_rectHighlight.width = m_gi->m_rectAll.width; - m_gi->m_rectHighlight.height = m_gi->m_rectAll.height; + m_gi->m_rectAll.width = lw; + m_gi->m_rectAll.height = lh; + + if (item->HasImage()) + { + int w, h; + m_owner->GetImageSize( item->GetImage(), w, h ); + m_gi->m_rectIcon.width = w; + m_gi->m_rectIcon.height = h; + + m_gi->m_rectAll.width += 4 + w; + if (h > m_gi->m_rectAll.height) + m_gi->m_rectAll.height = h; } + + m_gi->m_rectHighlight.width = m_gi->m_rectAll.width; + m_gi->m_rectHighlight.height = m_gi->m_rectAll.height; break; case wxLC_REPORT: @@ -1463,11 +1257,9 @@ void wxListLineData::CalculateSize( wxDC *dc, int spacing ) } } -void wxListLineData::SetPosition( int x, int y, - int window_width, - int spacing ) +void wxListLineData::SetPosition( int x, int y, 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(); @@ -1540,7 +1332,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(); @@ -1549,7 +1341,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(); @@ -1561,7 +1353,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(); @@ -1573,7 +1365,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(); @@ -1583,7 +1375,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(); @@ -1592,7 +1384,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(); @@ -1601,7 +1393,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(); @@ -1610,7 +1402,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(); @@ -1675,15 +1467,15 @@ bool wxListLineData::SetAttributes(wxDC *dc, dc->SetPen( *wxTRANSPARENT_PEN ); - return TRUE; + return true; } - return FALSE; + return false; } 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(); @@ -1695,20 +1487,31 @@ void wxListLineData::Draw( wxDC *dc ) dc->DrawRectangle( m_gi->m_rectHighlight ); } + // just for debugging to better see where the items are +#if 0 + dc->SetPen(*wxRED_PEN); + dc->SetBrush(*wxTRANSPARENT_BRUSH); + dc->DrawRectangle( m_gi->m_rectAll ); + dc->SetPen(*wxGREEN_PEN); + dc->DrawRectangle( m_gi->m_rectIcon ); +#endif // 0 + wxListItemData *item = node->GetData(); if (item->HasImage()) { - wxRect rectIcon = m_gi->m_rectIcon; - m_owner->DrawImage( item->GetImage(), dc, - rectIcon.x, rectIcon.y ); + // centre the image inside our rectangle, this looks nicer when items + // ae aligned in a row + const wxRect& rectIcon = m_gi->m_rectIcon; + + m_owner->DrawImage(item->GetImage(), dc, rectIcon.x, rectIcon.y); } if (item->HasText()) { - wxRect rectLabel = m_gi->m_rectLabel; + const wxRect& rectLabel = m_gi->m_rectLabel; wxDCClipper clipper(*dc, rectLabel); - dc->DrawText( item->GetText(), rectLabel.x, rectLabel.y ); + dc->DrawText(item->GetText(), rectLabel.x, rectLabel.y); } } @@ -1730,7 +1533,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++ ) { @@ -1774,17 +1577,30 @@ void wxListLineData::DrawTextFormatted(wxDC *dc, // determine if the string can fit inside the current width dc->GetTextExtent(text, &w, &h); - - // if it can, draw it if (w <= width) { + // it can, draw it using the items alignment m_owner->GetColumn(col, item); - if (item.m_format == wxLIST_FORMAT_LEFT) - dc->DrawText(text, x, y); - else if (item.m_format == wxLIST_FORMAT_RIGHT) - dc->DrawText(text, x + width - w, y); - else if (item.m_format == wxLIST_FORMAT_CENTER) - dc->DrawText(text, x + ((width - w) / 2), y); + switch ( item.GetAlign() ) + { + default: + wxFAIL_MSG( _T("unknown list item format") ); + // fall through + + case wxLIST_FORMAT_LEFT: + // nothing to do + break; + + case wxLIST_FORMAT_RIGHT: + x += width - w; + break; + + case wxLIST_FORMAT_CENTER: + x += (width - w) / 2; + break; + } + + dc->DrawText(text, x, y); } else // otherwise, truncate and add an ellipsis if possible { @@ -1793,13 +1609,17 @@ void wxListLineData::DrawTextFormatted(wxDC *dc, dc->GetTextExtent(ellipsis, &base_w, &h); // continue until we have enough space or only one character left - drawntext = text.Left(text.Length() - 1); - while (drawntext.Length() > 1) + wxCoord w_c, h_c; + size_t len = text.Length(); + drawntext = text.Left(len); + while (len > 1) { - dc->GetTextExtent(drawntext, &w, &h); + dc->GetTextExtent(drawntext.Last(), &w_c, &h_c); + drawntext.RemoveLast(); + len--; + w -= w_c; if (w + base_w <= width) break; - drawntext = drawntext.Left(drawntext.Length() - 1); } // if still not enough space, remove ellipsis characters @@ -1817,14 +1637,14 @@ void wxListLineData::DrawTextFormatted(wxDC *dc, bool wxListLineData::Highlight( bool on ) { - wxCHECK_MSG( !m_owner->IsVirtual(), FALSE, _T("unexpected call to Highlight") ); + wxCHECK_MSG( !IsVirtual(), false, _T("unexpected call to Highlight") ); if ( on == m_highlighted ) - return FALSE; + return false; m_highlighted = on; - return TRUE; + return true; } void wxListLineData::ReverseHighlight( void ) @@ -1847,8 +1667,8 @@ END_EVENT_TABLE() void wxListHeaderWindow::Init() { m_currentCursor = (wxCursor *) NULL; - m_isDragging = FALSE; - m_dirty = FALSE; + m_isDragging = false; + m_dirty = false; } wxListHeaderWindow::wxListHeaderWindow() @@ -1873,7 +1693,18 @@ wxListHeaderWindow::wxListHeaderWindow( wxWindow *win, m_owner = owner; m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE ); - SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); +#if _USE_VISATTR + wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes(); + SetOwnForegroundColour( attr.colFg ); + SetOwnBackgroundColour( attr.colBg ); + if (!m_hasFont) + SetOwnFont( attr.font ); +#else + SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); + SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + if (!m_hasFont) + SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT )); +#endif } wxListHeaderWindow::~wxListHeaderWindow() @@ -1886,66 +1717,6 @@ wxListHeaderWindow::~wxListHeaderWindow() #include "wx/univ/theme.h" #endif -void wxListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h ) -{ -#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__) - GtkStateType state = m_parent->IsEnabled() ? GTK_STATE_NORMAL - : GTK_STATE_INSENSITIVE; - - x = dc->XLOG2DEV( x ); - - gtk_paint_box (m_wxwindow->style, GTK_PIZZA(m_wxwindow)->bin_window, - state, GTK_SHADOW_OUT, - (GdkRectangle*) NULL, m_wxwindow, - (char *)"button", // const_cast - x-1, y-1, w+2, h+2); -#elif defined(__WXUNIVERSAL__) - wxTheme *theme = wxTheme::Get(); - wxRenderer *renderer = theme->GetRenderer(); - renderer->DrawBorder( *dc, wxBORDER_RAISED, wxRect(x,y,w,h), 0 ); -#elif defined(__WXMAC__) - const int m_corner = 1; - - dc->SetBrush( *wxTRANSPARENT_BRUSH ); - - dc->SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNSHADOW ) , 1 , wxSOLID ) ); - dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer) - dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer) - - wxPen pen( wxColour( 0x88 , 0x88 , 0x88 ), 1, wxSOLID ); - - dc->SetPen( pen ); - dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner) - dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner) - - dc->SetPen( *wxWHITE_PEN ); - dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer) - dc->DrawRectangle( x, y, 1, h ); // left (outer) - dc->DrawLine( x, y+h-1, x+1, y+h-1 ); - dc->DrawLine( x+w-1, y, x+w-1, y+1 ); -#else // !GTK, !Mac - const int m_corner = 1; - - dc->SetBrush( *wxTRANSPARENT_BRUSH ); - - dc->SetPen( *wxBLACK_PEN ); - dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer) - dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer) - - wxPen pen( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID ); - - dc->SetPen( pen ); - dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner) - dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner) - - dc->SetPen( *wxWHITE_PEN ); - dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer) - dc->DrawRectangle( x, y, 1, h ); // left (outer) - dc->DrawLine( x, y+h-1, x+1, y+h-1 ); - dc->DrawLine( x+w-1, y, x+w-1, y+1 ); -#endif -} - // shift the DC origin to match the position of the main window horz // scrollbar: this allows us to always use logical coords void wxListHeaderWindow::AdjustDC(wxDC& dc) @@ -1962,11 +1733,7 @@ void wxListHeaderWindow::AdjustDC(wxDC& dc) void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { -#if defined(__WXGTK__) - wxClientDC dc( this ); -#else wxPaintDC dc( this ); -#endif PrepareDC( dc ); AdjustDC( dc ); @@ -1982,11 +1749,7 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dc.SetBackgroundMode(wxTRANSPARENT); - // do *not* use the listctrl colour for headers - one day we will have a - // function to set it separately - //dc.SetTextForeground( *wxBLACK ); - dc.SetTextForeground(wxSystemSettings:: - GetSystemColour( wxSYS_COLOUR_WINDOWTEXT )); + dc.SetTextForeground(GetForegroundColour()); int x = HEADER_OFFSET_X; @@ -1999,34 +1762,85 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) // the width of the rect to draw: make it smaller to fit entirely // inside the column rect +#ifdef __WXMAC__ + int cw = wCol ; + int ch = h ; +#else int cw = wCol - 2; - - dc.SetPen( *wxWHITE_PEN ); - - DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 ); - - // if we have an image, draw it on the right of the label - int image = item.m_image; + int ch = h-2 ; +#endif + wxRendererNative::Get().DrawHeaderButton + ( + this, + dc, + wxRect(x, HEADER_OFFSET_Y, cw, ch), + m_parent->IsEnabled() ? 0 + : (int)wxCONTROL_DISABLED + ); + + // see if we have enough space for the column label + + // for this we need the width of the text + wxCoord wLabel; + wxCoord hLabel; + dc.GetTextExtent(item.GetText(), &wLabel, &hLabel); + wLabel += 2*EXTRA_WIDTH; + + // and the width of the icon, if any + static const int MARGIN_BETWEEN_TEXT_AND_ICON = 2; + int ix = 0, // init them just to suppress the compiler warnings + iy = 0; + const int image = item.m_image; + wxImageListType *imageList; if ( image != -1 ) { - wxImageListType *imageList = m_owner->m_small_image_list; + imageList = m_owner->m_small_image_list; if ( imageList ) { - int ix, iy; imageList->GetSize(image, ix, iy); + wLabel += ix + MARGIN_BETWEEN_TEXT_AND_ICON; + } + } + else + { + imageList = NULL; + } - imageList->Draw - ( - image, - dc, - x + cw - ix - 1, - HEADER_OFFSET_Y + (h - 4 - iy)/2, - wxIMAGELIST_DRAW_TRANSPARENT - ); + // ignore alignment if there is not enough space anyhow + int xAligned; + switch ( wLabel < cw ? item.GetAlign() : wxLIST_FORMAT_LEFT ) + { + default: + wxFAIL_MSG( _T("unknown list item format") ); + // fall through - cw -= ix + 2; - } - //else: ignore the column image + case wxLIST_FORMAT_LEFT: + xAligned = x; + break; + + case wxLIST_FORMAT_RIGHT: + xAligned = x + cw - wLabel; + break; + + case wxLIST_FORMAT_CENTER: + xAligned = x + (cw - wLabel) / 2; + break; + } + + + // if we have an image, draw it on the right of the label + if ( imageList ) + { + imageList->Draw + ( + image, + dc, + xAligned + wLabel - ix - MARGIN_BETWEEN_TEXT_AND_ICON, + HEADER_OFFSET_Y + (h - 4 - iy)/2, + wxIMAGELIST_DRAW_TRANSPARENT + ); + + cw -= ix + MARGIN_BETWEEN_TEXT_AND_ICON; } // draw the text clipping it so that it doesn't overwrite the column @@ -2034,7 +1848,7 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 ); dc.DrawText( item.GetText(), - x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT ); + xAligned + EXTRA_WIDTH, h / 2 - hLabel / 2 ); //HEADER_OFFSET_Y + EXTRA_HEIGHT ); x += wCol; } @@ -2077,8 +1891,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) if (m_isDragging) { - SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, - event.GetPosition()); + SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition()); // we don't draw the line beyond our window, but we allow dragging it // there @@ -2094,11 +1907,10 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) if (event.ButtonUp()) { ReleaseMouse(); - m_isDragging = FALSE; - m_dirty = TRUE; + m_isDragging = false; + m_dirty = true; m_owner->SetColumnWidth( m_column, m_currentX - m_minX ); - SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, - event.GetPosition()); + SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition()); } else { @@ -2115,7 +1927,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) else // not dragging { m_minX = 0; - bool hit_border = FALSE; + bool hit_border = false; // end of the current column int xpos = 0; @@ -2131,7 +1943,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) if ( (abs(x-xpos) < 3) && (y < 22) ) { // near the column border - hit_border = TRUE; + hit_border = true; break; } @@ -2151,12 +1963,15 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) { if (hit_border && event.LeftDown()) { - m_isDragging = TRUE; - m_currentX = x; - DrawCurrent(); - CaptureMouse(); - SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, - event.GetPosition()); + if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, + event.GetPosition()) ) + { + m_isDragging = true; + m_currentX = x; + CaptureMouse(); + DrawCurrent(); + } + //else: column resizing was vetoed by the user code } else // click on a column { @@ -2189,9 +2004,10 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) { m_owner->SetFocus(); + m_owner->Update(); } -void wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos) +bool wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos) { wxWindow *parent = GetParent(); wxListEvent le( type, parent->GetId() ); @@ -2205,7 +2021,7 @@ void wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos) le.m_pointDrag.y -= GetSize().y; le.m_col = m_column; - parent->GetEventHandler()->ProcessEvent( le ); + return !parent->GetEventHandler()->ProcessEvent( le ) || le.IsAllowed(); } //----------------------------------------------------------------------------- @@ -2237,7 +2053,8 @@ wxListTextCtrl::wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit) m_itemEdited(itemEdit) { m_owner = owner; - m_finished = FALSE; + m_finished = false; + m_aboutToFinish = false; wxRect rectLabel = owner->GetLineLabelRect(itemEdit); @@ -2255,9 +2072,9 @@ void wxListTextCtrl::Finish() { wxPendingDelete.Append(this); - m_finished = TRUE; + m_finished = true; - m_owner->SetFocus(); + m_owner->SetFocusIgnoringChildren(); } } @@ -2268,19 +2085,19 @@ bool wxListTextCtrl::AcceptChanges() if ( value == m_startValue ) { // nothing changed, always accept - return TRUE; + return true; } if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) { // vetoed by the user - return FALSE; + return false; } // accepted, do rename the item m_owner->SetItemText(m_itemEdited, value); - return TRUE; + return true; } void wxListTextCtrl::OnChar( wxKeyEvent &event ) @@ -2288,15 +2105,16 @@ void wxListTextCtrl::OnChar( wxKeyEvent &event ) switch ( event.m_keyCode ) { case WXK_RETURN: - if ( !AcceptChanges() ) - { - // vetoed by the user code - break; - } - //else: fall through + m_aboutToFinish = true; + // Notify the owner about the changes + AcceptChanges(); + // Even if vetoed, close the control (consistent with MSW) + Finish(); + break; case WXK_ESCAPE: Finish(); + m_owner->OnRenameCancelled( m_itemEdited ); break; default: @@ -2322,7 +2140,7 @@ void wxListTextCtrl::OnKeyUp( wxKeyEvent &event ) sx = parentSize.x - myPos.x; if (mySize.x > sx) sx = mySize.x; - SetSize(sx, -1); + SetSize(sx, wxDefaultCoord); event.Skip(); } @@ -2331,11 +2149,16 @@ 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 ); } + // We must let the native text control handle focus, too, otherwise + // it could have problems with the cursor (e.g., in wxGTK). event.Skip(); } @@ -2357,8 +2180,7 @@ END_EVENT_TABLE() void wxListMainWindow::Init() { - m_columns.DeleteContents( TRUE ); - m_dirty = TRUE; + m_dirty = true; m_countVirt = 0; m_lineFrom = m_lineTo = (size_t)-1; @@ -2373,43 +2195,27 @@ void wxListMainWindow::Init() m_small_spacing = 30; m_normal_spacing = 40; - m_hasFocus = FALSE; + m_hasFocus = false; m_dragCount = 0; - m_isCreated = FALSE; + m_isCreated = false; - m_lastOnSame = FALSE; + m_lastOnSame = false; m_renameTimer = new wxListRenameTimer( this ); m_current = m_lineLastClicked = + m_lineSelectSingleOnUp = m_lineBeforeLastClicked = (size_t)-1; m_freezeCount = 0; } -void wxListMainWindow::InitScrolling() -{ - if ( HasFlag(wxLC_REPORT) ) - { - m_xScroll = SCROLL_UNIT_X; - m_yScroll = SCROLL_UNIT_Y; - } - else - { - m_xScroll = SCROLL_UNIT_Y; - m_yScroll = 0; - } -} - wxListMainWindow::wxListMainWindow() { Init(); m_highlightBrush = m_highlightUnfocusedBrush = (wxBrush *) NULL; - - m_xScroll = - m_yScroll = 0; } wxListMainWindow::wxListMainWindow( wxWindow *parent, @@ -2444,15 +2250,20 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, wxSize sz = size; sz.y = 25; - InitScrolling(); - SetScrollbars( m_xScroll, m_yScroll, 0, 0, 0, 0 ); + SetScrollbars( 0, 0, 0, 0, 0, 0 ); - SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ) ); + wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes(); + SetOwnForegroundColour( attr.colFg ); + SetOwnBackgroundColour( attr.colBg ); + if (!m_hasFont) + SetOwnFont( attr.font ); } wxListMainWindow::~wxListMainWindow() { DoDeleteAllItems(); + WX_CLEAR_LIST(wxListHeaderDataList, m_columns); + WX_CLEAR_ARRAY(m_aColWidths); delete m_highlightBrush; delete m_highlightUnfocusedBrush; @@ -2512,8 +2323,6 @@ wxListLineData *wxListMainWindow::GetDummyLine() const wxCoord wxListMainWindow::GetLineHeight() const { - wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("only works in report mode") ); - // we cache the line height as calling GetTextExtent() is slow if ( !m_lineHeight ) { @@ -2525,10 +2334,15 @@ wxCoord wxListMainWindow::GetLineHeight() const wxCoord y; dc.GetTextExtent(_T("H"), NULL, &y); - if ( y < SCROLL_UNIT_Y ) - y = SCROLL_UNIT_Y; - y += EXTRA_HEIGHT; + if ( m_small_image_list && m_small_image_list->GetImageCount() ) + { + int iw = 0; + int ih = 0; + m_small_image_list->GetSize(0, iw, ih); + y = wxMax(y, ih); + } + y += EXTRA_HEIGHT; self->m_lineHeight = y + LINE_SPACING; } @@ -2537,7 +2351,7 @@ wxCoord wxListMainWindow::GetLineHeight() const wxCoord wxListMainWindow::GetLineY(size_t line) const { - wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("only works in report mode") ); + wxASSERT_MSG( InReportView(), _T("only works in report mode") ); return LINE_SPACING + line*GetLineHeight(); } @@ -2629,7 +2443,7 @@ bool wxListMainWindow::IsHighlighted(size_t line) const else // !virtual { wxListLineData *ld = GetLine(line); - wxCHECK_MSG( ld, FALSE, _T("invalid index in IsHighlighted") ); + wxCHECK_MSG( ld, false, _T("invalid index in IsHighlighted") ); return ld->IsHighlighted(); } @@ -2680,7 +2494,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) else // !virtual { wxListLineData *ld = GetLine(line); - wxCHECK_MSG( ld, FALSE, _T("invalid index in HighlightLine") ); + wxCHECK_MSG( ld, false, _T("invalid index in HighlightLine") ); changed = ld->Highlight(highlight); } @@ -2696,7 +2510,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) void wxListMainWindow::RefreshLine( size_t line ) { - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { size_t visibleFrom, visibleTo; GetVisibleLinesRange(&visibleFrom, &visibleTo); @@ -2718,7 +2532,7 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) wxASSERT_MSG( lineTo < GetItemCount(), _T("invalid line range") ); - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { size_t visibleFrom, visibleTo; GetVisibleLinesRange(&visibleFrom, &visibleTo); @@ -2749,7 +2563,7 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) void wxListMainWindow::RefreshAfter( size_t lineFrom ) { - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { size_t visibleFrom, visibleTo; GetVisibleLinesRange(&visibleFrom, &visibleTo); @@ -2774,7 +2588,7 @@ void wxListMainWindow::RefreshAfter( size_t lineFrom ) else // !report { // TODO: how to do it more efficiently? - m_dirty = TRUE; + m_dirty = true; } } @@ -2851,7 +2665,7 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) dc.SetFont( GetFont() ); - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { int lineHeight = GetLineHeight(); @@ -2919,7 +2733,6 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPen pen(GetRuleColour(), 1, wxSOLID); - int col = 0; wxRect firstItemRect; wxRect lastItemRect; GetItemRect(visibleFrom, firstItemRect); @@ -2927,7 +2740,7 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) int x = firstItemRect.GetX(); dc.SetPen(pen); dc.SetBrush(* wxTRANSPARENT_BRUSH); - for (col = 0; col < GetColumnCount(); col++) + for (int col = 0; col < GetColumnCount(); col++) { int colWidth = GetColumnWidth(col); x += colWidth; @@ -2945,22 +2758,18 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) } } +#ifndef __WXMAC__ + // Don't draw rect outline under Mac at all. if ( HasCurrent() ) { - // 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__ { dc.SetPen( *wxBLACK_PEN ); dc.SetBrush( *wxTRANSPARENT_BRUSH ); dc.DrawRectangle( GetLineHighlightRect(m_current) ); } } +#endif dc.EndDrawing(); } @@ -2974,7 +2783,7 @@ void wxListMainWindow::HighlightAll( bool on ) // we just have one item to turn off if ( HasCurrent() && IsHighlighted(m_current) ) { - HighlightLine(m_current, FALSE); + HighlightLine(m_current, false); RefreshLine(m_current); } } @@ -3063,7 +2872,7 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) le.m_itemIndex = itemEdit; wxListLineData *data = GetLine(itemEdit); - wxCHECK_MSG( data, FALSE, _T("invalid index in OnRenameAccept()") ); + wxCHECK_MSG( data, false, _T("invalid index in OnRenameAccept()") ); data->GetItem( 0, le.m_item ); le.m_item.m_text = value; @@ -3071,14 +2880,39 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) le.IsAllowed(); } +void wxListMainWindow::OnRenameCancelled(size_t itemEdit) +{ + // let owner know that the edit was cancelled + wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); + + le.SetEditCanceled(true); + + 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 ); +} + void wxListMainWindow::OnMouse( wxMouseEvent &event ) { event.SetEventObject( GetParent() ); if ( GetParent()->GetEventHandler()->ProcessEvent( event) ) return; - if ( !HasCurrent() || IsEmpty() ) - return; + if (event.GetEventType() == wxEVT_MOUSEWHEEL) + { + // let the base handle mouse wheel events. + event.Skip(); + return; + } + + if ( !HasCurrent() || IsEmpty() ) + return; if (m_dirty) return; @@ -3097,7 +2931,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) size_t count = GetItemCount(), current; - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { current = y / GetLineHeight(); if ( current < count ) @@ -3135,6 +2969,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) wxListEvent le( command, GetParent()->GetId() ); le.SetEventObject( GetParent() ); + le.m_itemIndex = m_lineLastClicked; le.m_pointDrag = m_dragStart; GetParent()->GetEventHandler()->ProcessEvent( le ); @@ -3151,11 +2986,11 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) return; } - bool forceClick = FALSE; + bool forceClick = false; if (event.ButtonDClick()) { m_renameTimer->Stop(); - m_lastOnSame = FALSE; + m_lastOnSame = false; if ( current == m_lineLastClicked ) { @@ -3165,26 +3000,56 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) } else { - // the first click was on another item, so don't interpret this as + // The first click was on another item, so don't interpret this as // a double click, but as a simple click instead - forceClick = TRUE; + forceClick = true; } } - if (event.LeftUp() && m_lastOnSame) + if (event.LeftUp()) { - if ((current == m_current) && - (hitResult == wxLIST_HITTEST_ONITEMLABEL) && - HasFlag(wxLC_EDIT_LABELS) ) + if(m_lineSelectSingleOnUp != (size_t) -1) + { + // select single line + HighlightAll( false ); + ReverseHighlight(m_lineSelectSingleOnUp); + } + else if (m_lastOnSame) { - m_renameTimer->Start( 100, TRUE ); + if ((current == m_current) && + (hitResult == wxLIST_HITTEST_ONITEMLABEL) && + HasFlag(wxLC_EDIT_LABELS) ) + { + m_renameTimer->Start( 100, true ); + } } - m_lastOnSame = FALSE; + m_lastOnSame = false; + m_lineSelectSingleOnUp = (size_t) -1; } - else if (event.RightDown()) + else { + // This is neccessary , because after a DnD operation in + // from and to ourself, the up event is swallowed by the + // DnD code. So on next non-up event (which means here and + // now) m_lineSelectSingleOnUp should be reset. + m_lineSelectSingleOnUp = (size_t) -1; + } + if (event.RightDown()) + { + m_lineBeforeLastClicked = m_lineLastClicked; + m_lineLastClicked = current; + // If the item is already selected, do not update the selection. + // Multi-selections should not be cleared if a selected item is clicked. + if (!IsHighlighted(current)) + { + HighlightAll(false); + ChangeCurrent(current); + ReverseHighlight(m_current); + } SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() ); + // Allow generation of context menu event + event.Skip(); } else if (event.MiddleDown()) { @@ -3196,18 +3061,26 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_lineLastClicked = current; size_t oldCurrent = m_current; - - if ( IsSingleSel() || !(event.ControlDown() || event.ShiftDown()) ) + bool cmdModifierDown = event.CmdDown(); + if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) ) { - HighlightAll( FALSE ); + if( IsSingleSel() || !IsHighlighted(current) ) + { + HighlightAll( false ); - ChangeCurrent(current); + ChangeCurrent(current); - ReverseHighlight(m_current); + ReverseHighlight(m_current); + } + else // multi sel & current is highlighted & no mod keys + { + m_lineSelectSingleOnUp = current; + ChangeCurrent(current); // change focus + } } else // multi sel & either ctrl or shift is down { - if (event.ControlDown()) + if (cmdModifierDown) { ChangeCurrent(current); @@ -3255,26 +3128,28 @@ void wxListMainWindow::MoveToItem(size_t item) int client_w, client_h; GetClientSize( &client_w, &client_h ); - int view_x = m_xScroll*GetScrollPos( wxHORIZONTAL ); - int view_y = m_yScroll*GetScrollPos( wxVERTICAL ); + const int hLine = GetLineHeight(); + + int view_x = SCROLL_UNIT_X*GetScrollPos( wxHORIZONTAL ); + int view_y = hLine*GetScrollPos( wxVERTICAL ); - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { // the next we need the range of lines shown it might be different, so // recalculate it ResetVisibleLinesRange(); if (rect.y < view_y ) - Scroll( -1, rect.y/m_yScroll ); + Scroll( -1, rect.y/hLine ); if (rect.y+rect.height+5 > view_y+client_h) - Scroll( -1, (rect.y+rect.height-client_h+SCROLL_UNIT_Y)/m_yScroll ); + Scroll( -1, (rect.y+rect.height-client_h+hLine)/hLine ); } else // !report { if (rect.x-view_x < 5) - Scroll( (rect.x-5)/m_xScroll, -1 ); + Scroll( (rect.x-5)/SCROLL_UNIT_X, -1 ); if (rect.x+rect.width-5 > view_x+client_w) - Scroll( (rect.x+rect.width-client_w+SCROLL_UNIT_X)/m_xScroll, -1 ); + Scroll( (rect.x+rect.width-client_w+SCROLL_UNIT_X)/SCROLL_UNIT_X, -1 ); } } @@ -3295,6 +3170,9 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) { ChangeCurrent(newCurrent); + // refresh the old focus to remove it + RefreshLine( oldCurrent ); + // select all the items between the old and the new one if ( oldCurrent > newCurrent ) { @@ -3308,7 +3186,7 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) { // all previously selected items are unselected unless ctrl is held if ( !event.ControlDown() ) - HighlightAll(FALSE); + HighlightAll(false); ChangeCurrent(newCurrent); @@ -3317,7 +3195,7 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) if ( !event.ControlDown() ) { - HighlightLine( m_current, TRUE ); + HighlightLine( m_current, true ); } } @@ -3355,7 +3233,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, GetParent()->GetId() ); le.m_itemIndex = m_current; GetLine(m_current)->GetItem( 0, le.m_item ); - le.m_code = (int)event.KeyCode(); + le.m_code = event.GetKeyCode(); le.SetEventObject( parent ); parent->GetEventHandler()->ProcessEvent( le ); } @@ -3372,7 +3250,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) ke.SetEventObject( parent ); if (parent->GetEventHandler()->ProcessEvent( ke )) return; - if (event.KeyCode() == WXK_TAB) + if (event.GetKeyCode() == WXK_TAB) { wxNavigationKeyEvent nevent; nevent.SetWindowChange( event.ControlDown() ); @@ -3390,7 +3268,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) return; } - switch (event.KeyCode()) + switch (event.GetKeyCode()) { case WXK_UP: if ( m_current > 0 ) @@ -3414,15 +3292,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) case WXK_PRIOR: { - int steps = 0; - if ( HasFlag(wxLC_REPORT) ) - { - steps = m_linesPerPage - 1; - } - else - { - steps = m_current % m_linesPerPage; - } + int steps = InReportView() ? m_linesPerPage - 1 : m_current % m_linesPerPage; int index = m_current - steps; if (index < 0) @@ -3434,15 +3304,9 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) case WXK_NEXT: { - int steps = 0; - if ( HasFlag(wxLC_REPORT) ) - { - steps = m_linesPerPage - 1; - } - else - { - steps = m_linesPerPage - (m_current % m_linesPerPage) - 1; - } + int steps = InReportView() + ? m_linesPerPage - 1 + : m_linesPerPage - (m_current % m_linesPerPage) - 1; size_t index = m_current + steps; size_t count = GetItemCount(); @@ -3454,7 +3318,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) break; case WXK_LEFT: - if ( !HasFlag(wxLC_REPORT) ) + if ( !InReportView() ) { int index = m_current - m_linesPerPage; if (index < 0) @@ -3465,7 +3329,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) break; case WXK_RIGHT: - if ( !HasFlag(wxLC_REPORT) ) + if ( !InReportView() ) { size_t index = m_current + m_linesPerPage; @@ -3507,51 +3371,38 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) // focus handling // ---------------------------------------------------------------------------- -void wxListMainWindow::SetFocus() +void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) { - // VS: wxListMainWindow derives from wxPanel (via wxScrolledWindow) and wxPanel - // overrides SetFocus in such way that it does never change focus from - // panel's child to the panel itself. Unfortunately, we must be able to change - // focus to the panel from wxListTextCtrl because the text control should - // disappear when the user clicks outside it. - - wxWindow *oldFocus = FindFocus(); - - if ( oldFocus && oldFocus->GetParent() == this ) + if ( GetParent() ) { - wxWindow::SetFocus(); - } - else - { - wxScrolledWindow::SetFocus(); + wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() ); + event.SetEventObject( GetParent() ); + if ( GetParent()->GetEventHandler()->ProcessEvent( event) ) + return; } -} -void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) -{ // wxGTK sends us EVT_SET_FOCUS events even if we had never got // EVT_KILL_FOCUS before which means that we finish by redrawing the items // which are already drawn correctly resulting in horrible flicker - avoid // it if ( !m_hasFocus ) { - m_hasFocus = TRUE; + m_hasFocus = true; RefreshSelected(); } - - if ( !GetParent() ) - return; - - wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() ); - event.SetEventObject( GetParent() ); - GetParent()->GetEventHandler()->ProcessEvent( event ); } void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) { - m_hasFocus = FALSE; - + if ( GetParent() ) + { + wxFocusEvent event( wxEVT_KILL_FOCUS, GetParent()->GetId() ); + event.SetEventObject( GetParent() ); + if ( GetParent()->GetEventHandler()->ProcessEvent( event) ) + return; + } + m_hasFocus = false; RefreshSelected(); } @@ -3569,7 +3420,7 @@ void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y ) { m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT ); } - else if ( HasFlag(wxLC_REPORT) && (m_small_image_list)) + else if ( InReportView() && (m_small_image_list)) { m_small_image_list->Draw( index, *dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT ); } @@ -3589,7 +3440,7 @@ void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const { m_small_image_list->GetSize( index, width, height ); } - else if ( HasFlag(wxLC_REPORT) && m_small_image_list ) + else if ( InReportView() && m_small_image_list ) { m_small_image_list->GetSize( index, width, height ); } @@ -3613,7 +3464,7 @@ int wxListMainWindow::GetTextLength( const wxString &s ) const void wxListMainWindow::SetImageList( wxImageListType *imageList, int which ) { - m_dirty = TRUE; + m_dirty = true; // calc the spacing from the icon size int width = 0, @@ -3633,12 +3484,13 @@ void wxListMainWindow::SetImageList( wxImageListType *imageList, int which ) { m_small_image_list = imageList; m_small_spacing = width + 14; + m_lineHeight = 0; // ensure that the line height will be recalc'd } } void wxListMainWindow::SetItemSpacing( int spacing, bool isSmall ) { - m_dirty = TRUE; + m_dirty = true; if (isSmall) { m_small_spacing = spacing; @@ -3660,7 +3512,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") ); @@ -3672,9 +3524,9 @@ void wxListMainWindow::SetColumn( int col, wxListItem &item ) wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; if ( headerWin ) - headerWin->m_dirty = TRUE; + headerWin->m_dirty = true; - m_dirty = TRUE; + m_dirty = true; // invalidate it as it has to be recalculated m_headerWidth = 0; @@ -3685,15 +3537,15 @@ void wxListMainWindow::SetColumnWidth( int col, int width ) wxCHECK_RET( col >= 0 && col < GetColumnCount(), _T("invalid column index") ); - wxCHECK_RET( HasFlag(wxLC_REPORT), + wxCHECK_RET( InReportView(), _T("SetColumnWidth() can only be called in report mode.") ); - m_dirty = TRUE; + m_dirty = true; wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; if ( headerWin ) - headerWin->m_dirty = TRUE; + 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(); @@ -3718,34 +3570,30 @@ void wxListMainWindow::SetColumnWidth( int col, int width ) int max = AUTOSIZE_COL_MARGIN; - for ( size_t i = 0; i < count; i++ ) + // if the cached column width isn't valid then recalculate it + if (m_aColWidths.Item(col)->bNeedsUpdate) { - wxListLineData *line = GetLine(i); - wxListItemDataList::Node *n = line->m_items.Item( col ); + for (size_t i = 0; i < count; i++) + { + wxListLineData *line = GetLine(i); + wxListItemDataList::compatibility_iterator n = line->m_items.Item( col ); - wxCHECK_RET( n, _T("no subitem?") ); + wxCHECK_RET( n, _T("no subitem?") ); - wxListItemData *item = n->GetData(); - int current = 0; + wxListItemData *itemData = n->GetData(); + wxListItem item; - if (item->HasImage()) - { - int ix, iy; - GetImageSize( item->GetImage(), ix, iy ); - current += ix + 5; + itemData->GetItem(item); + int itemWidth = GetItemWidthWithImage(&item); + if (itemWidth > max) + max = itemWidth; } - if (item->HasText()) - { - wxCoord w; - dc.GetTextExtent( item->GetText(), &w, NULL ); - current += w; - } - - if (current > max) - max = current; + m_aColWidths.Item(col)->bNeedsUpdate = false; + m_aColWidths.Item(col)->nMaxWidth = max; } + max = m_aColWidths.Item(col)->nMaxWidth; width = max + AUTOSIZE_COL_MARGIN; } } @@ -3774,7 +3622,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(); @@ -3783,7 +3631,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(); @@ -3804,22 +3652,75 @@ void wxListMainWindow::SetItem( wxListItem &item ) { wxListLineData *line = GetLine((size_t)id); line->SetItem( item.m_col, item ); + + if (InReportView()) + { + // update the Max Width Cache if needed + int width = GetItemWidthWithImage(&item); + + if (width > m_aColWidths.Item(item.m_col)->nMaxWidth) + m_aColWidths.Item(item.m_col)->nMaxWidth = width; + } } - if ( InReportView() ) + // update the item on screen + wxRect rectItem; + GetItemRect(id, rectItem); + RefreshRect(rectItem); +} + +void wxListMainWindow::SetItemStateAll(long state, long stateMask) +{ + if ( IsEmpty() ) + return; + + // first deal with selection + if ( stateMask & wxLIST_STATE_SELECTED ) { - // just refresh the line to show the new value of the text/image - RefreshLine((size_t)id); + // set/clear select state + if ( IsVirtual() ) + { + // optimized version for virtual listctrl. + m_selStore.SelectRange(0, GetItemCount() - 1, state == wxLIST_STATE_SELECTED); + Refresh(); + } + else if ( state & wxLIST_STATE_SELECTED ) + { + const long count = GetItemCount(); + for( long i = 0; i < count; i++ ) + { + SetItemState( i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); + } + + } + else + { + // clear for non virtual (somewhat optimized by using GetNextItem()) + long i = -1; + while ( (i = GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1 ) + { + SetItemState( i, 0, wxLIST_STATE_SELECTED ); + } + } } - else // !report + + if ( HasCurrent() && (state == 0) && (stateMask & wxLIST_STATE_FOCUSED) ) { - // refresh everything (resulting in horrible flicker - FIXME!) - m_dirty = TRUE; + // unfocus all: only one item can be focussed, so clearing focus for + // all items is simply clearing focus of the focussed item. + SetItemState(m_current, state, stateMask); } + //(setting focus to all items makes no sense, so it is not handled here.) } void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) { + if ( litem == -1 ) + { + SetItemStateAll(state, stateMask); + return; + } + wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(), _T("invalid list ctrl item index in SetItem") ); @@ -3840,7 +3741,7 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) { if ( IsSingleSel() ) { - HighlightLine(oldCurrent, FALSE); + HighlightLine(oldCurrent, false); } RefreshLine(oldCurrent); @@ -3861,7 +3762,7 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) // we must unselect the old current item as well or we // might end up with more than one selected item in a // single selection control - HighlightLine(oldCurrent, FALSE); + HighlightLine(oldCurrent, false); } RefreshLine( oldCurrent ); @@ -3886,7 +3787,7 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) if ( oldCurrent != (size_t)-1 ) { - HighlightLine( oldCurrent, FALSE ); + HighlightLine( oldCurrent, false ); RefreshLine( oldCurrent ); } } @@ -3954,7 +3855,7 @@ void wxListMainWindow::SetItemCount(long count) ResetVisibleLinesRange(); // scrollbars must be reset - m_dirty = TRUE; + m_dirty = true; } int wxListMainWindow::GetSelectedItemCount() const @@ -3962,7 +3863,7 @@ int wxListMainWindow::GetSelectedItemCount() const // deal with the quick case first if ( IsSingleSel() ) { - return HasCurrent() ? IsHighlighted(m_current) : FALSE; + return HasCurrent() ? IsHighlighted(m_current) : false; } // virtual controls remmebers all its selections itself @@ -3986,11 +3887,58 @@ int wxListMainWindow::GetSelectedItemCount() const // item position/size // ---------------------------------------------------------------------------- +wxRect wxListMainWindow::GetViewRect() const +{ + wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST), + _T("wxListCtrl::GetViewRect() only works in icon mode") ); + + // we need to find the longest/tallest label + wxCoord xMax = 0, + yMax = 0; + const int count = GetItemCount(); + if ( count ) + { + for ( int i = 0; i < count; i++ ) + { + wxRect r; + GetItemRect(i, r); + + wxCoord x = r.GetRight(), + y = r.GetBottom(); + + if ( x > xMax ) + xMax = x; + if ( y > yMax ) + yMax = y; + } + } + + // some fudge needed to make it look prettier + xMax += 2*EXTRA_BORDER_X; + yMax += 2*EXTRA_BORDER_Y; + + // account for the scrollbars if necessary + const wxSize sizeAll = GetClientSize(); + if ( xMax > sizeAll.x ) + yMax += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y); + if ( yMax > sizeAll.y ) + xMax += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + + return wxRect(0, 0, xMax, yMax); +} + void wxListMainWindow::GetItemRect( long index, wxRect &rect ) const { wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(), _T("invalid index in GetItemRect") ); + // ensure that we're laid out, otherwise we could return nonsense + if ( m_dirty ) + { + wxConstCast(this, wxListMainWindow)-> + RecalculatePositions(true /* no refresh */); + } + rect = GetLineRect((size_t)index); CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y); @@ -4004,7 +3952,7 @@ bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) const pos.x = rect.x; pos.y = rect.y; - return TRUE; + return true; } // ---------------------------------------------------------------------------- @@ -4016,6 +3964,8 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh) wxClientDC dc( this ); dc.SetFont( GetFont() ); + const size_t count = GetItemCount(); + int iconSpacing; if ( HasFlag(wxLC_ICON) ) iconSpacing = m_normal_spacing; @@ -4025,7 +3975,7 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh) iconSpacing = 0; // Note that we do not call GetClientSize() here but - // GetSize() and substract the border size for sunken + // GetSize() and subtract the border size for sunken // borders manually. This is technically incorrect, // but we need to know the client area's size WITHOUT // scrollbars here. Since we don't know if there are @@ -4044,106 +3994,163 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh) clientHeight; GetSize( &clientWidth, &clientHeight ); - if ( HasFlag(wxLC_REPORT) ) - { - // all lines have the same height - int lineHeight = GetLineHeight(); - - // scroll one line per step - m_yScroll = lineHeight; + const int lineHeight = GetLineHeight(); - size_t lineCount = GetItemCount(); - int entireHeight = lineCount*lineHeight + LINE_SPACING; + if ( InReportView() ) + { + // all lines have the same height and we scroll one line per step + int entireHeight = count*lineHeight + LINE_SPACING; m_linesPerPage = clientHeight / lineHeight; ResetVisibleLinesRange(); - SetScrollbars( m_xScroll, m_yScroll, - GetHeaderWidth() / m_xScroll, - (entireHeight + m_yScroll - 1)/m_yScroll, + SetScrollbars( SCROLL_UNIT_X, lineHeight, + GetHeaderWidth() / SCROLL_UNIT_X, + (entireHeight + lineHeight - 1) / lineHeight, GetScrollPos(wxHORIZONTAL), GetScrollPos(wxVERTICAL), - TRUE ); + true ); } else // !report { - // at first we try without any scrollbar. if the items don't - // fit into the window, we recalculate after subtracting an - // approximated 15 pt for the horizontal scrollbar - - int entireWidth = 0; - - for (int tries = 0; tries < 2; tries++) + // we have 3 different layout strategies: either layout all items + // horizontally/vertically (wxLC_ALIGN_XXX styles explicitly given) or + // to arrange them in top to bottom, left to right (don't ask me why + // not the other way round...) order + if ( HasFlag(wxLC_ALIGN_LEFT | wxLC_ALIGN_TOP) ) { - // We start with 4 for the border around all items - entireWidth = 4; - - if (tries == 1) - { - // Now we have decided that the items do not fit into the - // client area. Unfortunately, wxWindows sometimes thinks - // that it does fit and therefore NO horizontal scrollbar - // is inserted. This looks ugly, so we fudge here and make - // the calculated width bigger than was actually has been - // calculated. This ensures that wxScrolledWindows puts - // a scrollbar at the bottom of its client area. - entireWidth += SCROLL_UNIT_X; - } + int x = EXTRA_BORDER_X; + int y = EXTRA_BORDER_Y; - // Start at 2,2 so the text does not touch the border - int x = 2; - int y = 2; - int maxWidth = 0; - m_linesPerPage = 0; - int currentlyVisibleLines = 0; + wxCoord widthMax = 0; - size_t count = GetItemCount(); - for (size_t i = 0; i < count; i++) + size_t i; + for ( i = 0; i < count; i++ ) { - currentlyVisibleLines++; wxListLineData *line = GetLine(i); line->CalculateSize( &dc, iconSpacing ); - line->SetPosition( x, y, clientWidth, iconSpacing ); // Why clientWidth? (FIXME) + line->SetPosition( x, y, iconSpacing ); wxSize sizeLine = GetLineSize(i); - if ( maxWidth < sizeLine.x ) - maxWidth = sizeLine.x; + if ( HasFlag(wxLC_ALIGN_TOP) ) + { + if ( sizeLine.x > widthMax ) + widthMax = sizeLine.x; - y += sizeLine.y; - if (currentlyVisibleLines > m_linesPerPage) - m_linesPerPage = currentlyVisibleLines; + y += sizeLine.y; + } + else // wxLC_ALIGN_LEFT + { + x += sizeLine.x + MARGIN_BETWEEN_ROWS; + } + } - // Assume that the size of the next one is the same... (FIXME) - if ( y + sizeLine.y >= clientHeight ) + if ( HasFlag(wxLC_ALIGN_TOP) ) + { + // traverse the items again and tweak their sizes so that they are + // all the same in a row + for ( i = 0; i < count; i++ ) { - currentlyVisibleLines = 0; - y = 2; - x += maxWidth+6; - entireWidth += maxWidth+6; - maxWidth = 0; + wxListLineData *line = GetLine(i); + line->m_gi->ExtendWidth(widthMax); } + } - // We have reached the last item. - if ( i == count - 1 ) - entireWidth += maxWidth; - if ( (tries == 0) && (entireWidth+SCROLL_UNIT_X > clientWidth) ) + SetScrollbars + ( + SCROLL_UNIT_X, + lineHeight, + (x + SCROLL_UNIT_X) / SCROLL_UNIT_X, + (y + lineHeight) / lineHeight, + GetScrollPos( wxHORIZONTAL ), + GetScrollPos( wxVERTICAL ), + true + ); + } + else // "flowed" arrangement, the most complicated case + { + // at first we try without any scrollbars, if the items don't fit into + // the window, we recalculate after subtracting the space taken by the + // scrollbar + + int entireWidth = 0; + + for (int tries = 0; tries < 2; tries++) + { + entireWidth = 2*EXTRA_BORDER_X; + + if (tries == 1) { - clientHeight -= 15; // We guess the scrollbar height. (FIXME) - m_linesPerPage = 0; - currentlyVisibleLines = 0; - break; + // Now we have decided that the items do not fit into the + // client area, so we need a scrollbar + entireWidth += SCROLL_UNIT_X; } - if ( i == count - 1 ) - tries = 1; // Everything fits, no second try required. + int x = EXTRA_BORDER_X; + int y = EXTRA_BORDER_Y; + int maxWidthInThisRow = 0; + + m_linesPerPage = 0; + int currentlyVisibleLines = 0; + + for (size_t i = 0; i < count; i++) + { + currentlyVisibleLines++; + wxListLineData *line = GetLine(i); + line->CalculateSize( &dc, iconSpacing ); + line->SetPosition( x, y, iconSpacing ); + + wxSize sizeLine = GetLineSize(i); + + if ( maxWidthInThisRow < sizeLine.x ) + maxWidthInThisRow = sizeLine.x; + + y += sizeLine.y; + if (currentlyVisibleLines > m_linesPerPage) + m_linesPerPage = currentlyVisibleLines; + + if ( y + sizeLine.y >= clientHeight ) + { + currentlyVisibleLines = 0; + y = EXTRA_BORDER_Y; + maxWidthInThisRow += MARGIN_BETWEEN_ROWS; + x += maxWidthInThisRow; + entireWidth += maxWidthInThisRow; + maxWidthInThisRow = 0; + } + + // We have reached the last item. + if ( i == count - 1 ) + entireWidth += maxWidthInThisRow; + + if ( (tries == 0) && + (entireWidth + SCROLL_UNIT_X > clientWidth) ) + { + clientHeight -= wxSystemSettings:: + GetMetric(wxSYS_HSCROLL_Y); + m_linesPerPage = 0; + break; + } + + if ( i == count - 1 ) + tries = 1; // Everything fits, no second try required. + } } - } - int scroll_pos = GetScrollPos( wxHORIZONTAL ); - SetScrollbars( m_xScroll, m_yScroll, (entireWidth+SCROLL_UNIT_X) / m_xScroll, 0, scroll_pos, 0, TRUE ); + SetScrollbars + ( + SCROLL_UNIT_X, + lineHeight, + (entireWidth + SCROLL_UNIT_X) / SCROLL_UNIT_X, + 0, + GetScrollPos( wxHORIZONTAL ), + 0, + true + ); + } } if ( !noRefresh ) @@ -4157,13 +4164,13 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh) void wxListMainWindow::RefreshAll() { - m_dirty = FALSE; + m_dirty = false; Refresh(); wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; if ( headerWin && headerWin->m_dirty ) { - headerWin->m_dirty = FALSE; + headerWin->m_dirty = false; headerWin->Refresh(); } } @@ -4242,6 +4249,26 @@ void wxListMainWindow::DeleteItem( long lindex ) if ( InReportView() ) { + // mark the Column Max Width cache as dirty if the items in the line + // we're deleting contain the Max Column Width + wxListLineData * const line = GetLine(index); + wxListItemDataList::compatibility_iterator n; + wxListItemData *itemData; + wxListItem item; + int itemWidth; + + for (size_t i = 0; i < m_columns.GetCount(); i++) + { + n = line->m_items.Item( i ); + itemData = n->GetData(); + itemData->GetItem(item); + + itemWidth = GetItemWidthWithImage(&item); + + if (itemWidth >= m_aColWidths.Item(i)->nMaxWidth) + m_aColWidths.Item(i)->bNeedsUpdate = true; + } + ResetVisibleLinesRange(); } @@ -4257,7 +4284,7 @@ void wxListMainWindow::DeleteItem( long lindex ) } // we need to refresh the (vert) scrollbar as the number of items changed - m_dirty = TRUE; + m_dirty = true; SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); @@ -4266,12 +4293,31 @@ 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 ); + m_dirty = true; + 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); + } + } + + if ( InReportView() ) // we only cache max widths when in Report View + { + delete m_aColWidths.Item(col); + m_aColWidths.RemoveAt(col); + } // invalidate it as it has to be recalculated m_headerWidth = 0; @@ -4306,6 +4352,10 @@ void wxListMainWindow::DoDeleteAllItems() if ( InReportView() ) { ResetVisibleLinesRange(); + for (size_t i = 0; i < m_aColWidths.GetCount(); i++) + { + m_aColWidths.Item(i)->bNeedsUpdate = true; + } } m_lines.Clear(); @@ -4320,7 +4370,8 @@ void wxListMainWindow::DeleteAllItems() void wxListMainWindow::DeleteEverything() { - m_columns.Clear(); + WX_CLEAR_LIST(wxListHeaderDataList, m_columns); + WX_CLEAR_ARRAY(m_aColWidths); DeleteAllItems(); } @@ -4338,7 +4389,7 @@ void wxListMainWindow::EnsureVisible( long index ) // been added and its position is not known yet if ( m_dirty ) { - RecalculatePositions(TRUE /* no refresh */); + RecalculatePositions(true /* no refresh */); } MoveToItem((size_t)index); @@ -4362,7 +4413,7 @@ long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(p return wxNOT_FOUND; } -long wxListMainWindow::FindItem(long start, long data) +long wxListMainWindow::FindItem(long start, wxUIntPtr data) { long pos = start; if (pos < 0) @@ -4381,13 +4432,29 @@ long wxListMainWindow::FindItem(long start, long data) return wxNOT_FOUND; } +long wxListMainWindow::FindItem( const wxPoint& pt ) +{ + size_t topItem; + GetVisibleLinesRange(&topItem, NULL); + + wxPoint p; + GetItemPosition( GetItemCount()-1, p ); + if( p.y == 0 ) + return topItem; + long id = (long) floor( pt.y*double(GetItemCount()-topItem-1)/p.y+topItem ); + if( id >= 0 && id < (long)GetItemCount() ) + return id; + + return wxNOT_FOUND; +} + long wxListMainWindow::HitTest( int x, int y, int &flags ) { CalcUnscrolledPosition( x, y, &x, &y ); size_t count = GetItemCount(); - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { size_t current = y / GetLineHeight(); if ( current < count ) @@ -4420,57 +4487,86 @@ void wxListMainWindow::InsertItem( wxListItem &item ) { wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual control") ); - size_t count = GetItemCount(); - wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId <= count, - _T("invalid item index") ); + int count = GetItemCount(); + wxCHECK_RET( item.m_itemId >= 0, _T("invalid item index") ); + + if (item.m_itemId > count) + item.m_itemId = count; size_t id = item.m_itemId; - m_dirty = TRUE; + m_dirty = true; - int mode = 0; - if ( HasFlag(wxLC_REPORT) ) + if ( InReportView() ) { - mode = wxLC_REPORT; ResetVisibleLinesRange(); - } - else if ( HasFlag(wxLC_LIST) ) - mode = wxLC_LIST; - else if ( HasFlag(wxLC_ICON) ) - mode = wxLC_ICON; - else if ( HasFlag(wxLC_SMALL_ICON) ) - mode = wxLC_ICON; // no typo - else - { - wxFAIL_MSG( _T("unknown mode") ); + + // calculate the width of the item and adjust the max column width + wxColWidthInfo *pWidthInfo = m_aColWidths.Item(item.GetColumn()); + int width = GetItemWidthWithImage(&item); + item.SetWidth(width); + if (width > pWidthInfo->nMaxWidth) + pWidthInfo->nMaxWidth = width; } wxListLineData *line = new wxListLineData(this); - line->SetItem( 0, item ); + line->SetItem( item.m_col, item ); m_lines.Insert( line, id ); - m_dirty = TRUE; + m_dirty = true; + + // If an item is selected at or below the point of insertion, we need to + // increment the member variables because the current row's index has gone + // up by one + if ( HasCurrent() && m_current >= id ) + { + m_current++; + } + + SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM); + RefreshLines(id, GetItemCount() - 1); } void wxListMainWindow::InsertColumn( long col, wxListItem &item ) { - m_dirty = TRUE; - if ( HasFlag(wxLC_REPORT) ) + m_dirty = true; + if ( InReportView() ) { 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())) + wxColWidthInfo *colWidthInfo = new wxColWidthInfo(); + + 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 ); + m_aColWidths.Insert( colWidthInfo, col ); } else { m_columns.Append( column ); + m_aColWidths.Add( colWidthInfo ); + } + + 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 @@ -4478,6 +4574,30 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item ) } } +int wxListMainWindow::GetItemWidthWithImage(wxListItem * item) +{ + int width = 0; + wxClientDC dc(this); + + dc.SetFont( GetFont() ); + + if (item->GetImage() != -1) + { + int ix, iy; + GetImageSize( item->GetImage(), ix, iy ); + width += ix + 5; + } + + if (!item->GetText().empty()) + { + wxCoord w; + dc.GetTextExtent( item->GetText(), &w, NULL ); + width += w; + } + + return width; +} + // ---------------------------------------------------------------------------- // sorting // ---------------------------------------------------------------------------- @@ -4491,9 +4611,9 @@ int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData wxListLineData *line2 = *arg2; wxListItem item; line1->GetItem( 0, item ); - long data1 = item.m_data; + wxUIntPtr data1 = item.m_data; line2->GetItem( 0, item ); - long data2 = item.m_data; + wxUIntPtr data2 = item.m_data; return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data ); } @@ -4502,7 +4622,7 @@ void wxListMainWindow::SortItems( wxListCtrlCompare fn, long data ) list_ctrl_compare_func_2 = fn; list_ctrl_compare_data = data; m_lines.Sort( list_ctrl_compare_func_1 ); - m_dirty = TRUE; + m_dirty = true; } // ---------------------------------------------------------------------------- @@ -4545,7 +4665,7 @@ int wxListMainWindow::GetCountPerPage() const void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to) { - wxASSERT_MSG( HasFlag(wxLC_REPORT), _T("this is for report mode only") ); + wxASSERT_MSG( InReportView(), _T("this is for report mode only") ); if ( m_lineFrom == (size_t)-1 ) { @@ -4589,7 +4709,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl) BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl) EVT_SIZE(wxGenericListCtrl::OnSize) - EVT_IDLE(wxGenericListCtrl::OnIdle) END_EVENT_TABLE() wxGenericListCtrl::wxGenericListCtrl() @@ -4600,10 +4719,11 @@ wxGenericListCtrl::wxGenericListCtrl() m_ownsImageListNormal = m_ownsImageListSmall = - m_ownsImageListState = FALSE; + m_ownsImageListState = false; m_mainWin = (wxListMainWindow*) NULL; m_headerWin = (wxListHeaderWindow*) NULL; + m_headerHeight = 0; } wxGenericListCtrl::~wxGenericListCtrl() @@ -4616,15 +4736,42 @@ wxGenericListCtrl::~wxGenericListCtrl() delete m_imageListState; } +void wxGenericListCtrl::CalculateAndSetHeaderHeight() +{ + if ( m_headerWin ) + { +#ifdef __WXMAC__ + SInt32 h ; + GetThemeMetric( kThemeMetricListHeaderHeight, &h ); +#else + // we use 'g' to get the descent, too + int w, h, d; + m_headerWin->GetTextExtent(wxT("Hg"), &w, &h, &d); + h += d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT; +#endif + // only update if changed + if ( h != m_headerHeight ) + { + m_headerHeight = h; + + m_headerWin->SetSize(m_headerWin->GetSize().x, m_headerHeight); + + if ( HasHeader() ) + ResizeReportView(true); + } + } +} + void wxGenericListCtrl::CreateHeaderWindow() { m_headerWin = new wxListHeaderWindow ( - this, -1, m_mainWin, - wxPoint(0, 0), - wxSize(GetClientSize().x, HEADER_HEIGHT), + this, wxID_ANY, m_mainWin, + wxPoint(0,0), + wxSize(GetClientSize().x, m_headerHeight), wxTAB_TRAVERSAL ); + CalculateAndSetHeaderHeight(); } bool wxGenericListCtrl::Create(wxWindow *parent, @@ -4640,36 +4787,57 @@ bool wxGenericListCtrl::Create(wxWindow *parent, m_imageListState = (wxImageListType *) NULL; m_ownsImageListNormal = m_ownsImageListSmall = - m_ownsImageListState = FALSE; + m_ownsImageListState = false; m_mainWin = (wxListMainWindow*) NULL; m_headerWin = (wxListHeaderWindow*) NULL; + m_headerHeight = 0; + if ( !(style & wxLC_MASK_TYPE) ) { style = style | wxLC_LIST; } if ( !wxControl::Create( parent, id, pos, size, style, validator, name ) ) - return FALSE; + 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 ); + m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0,0), size, style ); - if ( HasFlag(wxLC_REPORT) ) +#ifdef __WXMAC_CARBON__ + // Human Interface Guidelines ask us for a special font in this case + if ( GetWindowVariant() == wxWINDOW_VARIANT_NORMAL ) + { + wxFont font ; + font.MacCreateThemeFont( kThemeViewsFont ) ; + SetFont( font ) ; + } +#endif + if ( InReportView() ) { CreateHeaderWindow(); - +#ifdef __WXMAC_CARBON__ + if (m_headerWin) + { + wxFont font ; + font.MacCreateThemeFont( kThemeSmallSystemFont ) ; + m_headerWin->SetFont( font ); + CalculateAndSetHeaderHeight(); + } +#endif if ( HasFlag(wxLC_NO_HEADER) ) { // VZ: why do we create it at all then? - m_headerWin->Show( FALSE ); + m_headerWin->Show( false ); } } - return TRUE; + SetBestSize(size); + + return true; } void wxGenericListCtrl::SetSingleStyle( long style, bool add ) @@ -4708,8 +4876,8 @@ void wxGenericListCtrl::SetWindowStyleFlag( long flag ) m_mainWin->DeleteEverything(); // has the header visibility changed? - bool hasHeader = HasFlag(wxLC_REPORT) && !HasFlag(wxLC_NO_HEADER), - willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER); + bool hasHeader = HasHeader(); + bool willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER); if ( hasHeader != willHaveHeader ) { @@ -4719,7 +4887,7 @@ void wxGenericListCtrl::SetWindowStyleFlag( long flag ) if ( m_headerWin ) { // don't delete, just hide, as we can reuse it later - m_headerWin->Show(FALSE); + m_headerWin->Show(false); } //else: nothing to do } @@ -4731,7 +4899,7 @@ void wxGenericListCtrl::SetWindowStyleFlag( long flag ) } else // already have it, just show { - m_headerWin->Show( TRUE ); + m_headerWin->Show( true ); } } @@ -4745,13 +4913,13 @@ void wxGenericListCtrl::SetWindowStyleFlag( long flag ) bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const { m_mainWin->GetColumn( col, item ); - return TRUE; + return true; } bool wxGenericListCtrl::SetColumn( int col, wxListItem& item ) { m_mainWin->SetColumn( col, item ); - return TRUE; + return true; } int wxGenericListCtrl::GetColumnWidth( int col ) const @@ -4762,7 +4930,7 @@ int wxGenericListCtrl::GetColumnWidth( int col ) const bool wxGenericListCtrl::SetColumnWidth( int col, int width ) { m_mainWin->SetColumnWidth( col, width ); - return TRUE; + return true; } int wxGenericListCtrl::GetCountPerPage() const @@ -4773,13 +4941,13 @@ int wxGenericListCtrl::GetCountPerPage() const bool wxGenericListCtrl::GetItem( wxListItem &info ) const { m_mainWin->GetItem( info ); - return TRUE; + return true; } bool wxGenericListCtrl::SetItem( wxListItem &info ) { m_mainWin->SetItem( info ); - return TRUE; + return true; } long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int imageId ) @@ -4795,7 +4963,7 @@ long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int info.m_mask |= wxLIST_MASK_IMAGE; }; m_mainWin->SetItem(info); - return TRUE; + return true; } int wxGenericListCtrl::GetItemState( long item, long stateMask ) const @@ -4806,17 +4974,18 @@ int wxGenericListCtrl::GetItemState( long item, long stateMask ) const bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask ) { m_mainWin->SetItemState( item, state, stateMask ); - return TRUE; + return true; } -bool wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) ) +bool +wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) ) { wxListItem info; info.m_image = image; info.m_mask = wxLIST_MASK_IMAGE; info.m_itemId = item; m_mainWin->SetItem( info ); - return TRUE; + return true; } wxString wxGenericListCtrl::GetItemText( long item ) const @@ -4829,7 +4998,7 @@ void wxGenericListCtrl::SetItemText( long item, const wxString& str ) m_mainWin->SetItemText(item, str); } -long wxGenericListCtrl::GetItemData( long item ) const +wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const { wxListItem info; info.m_itemId = item; @@ -4844,19 +5013,26 @@ bool wxGenericListCtrl::SetItemData( long item, long data ) info.m_itemId = item; info.m_data = data; m_mainWin->SetItem( info ); - return TRUE; + return true; +} + +wxRect wxGenericListCtrl::GetViewRect() const +{ + return m_mainWin->GetViewRect(); } bool wxGenericListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const { m_mainWin->GetItemRect( item, rect ); - return TRUE; + if ( m_mainWin->HasHeader() ) + rect.y += m_headerHeight + 1; + return true; } bool wxGenericListCtrl::GetItemPosition( long item, wxPoint& pos ) const { m_mainWin->GetItemPosition( item, pos ); - return TRUE; + return true; } bool wxGenericListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) ) @@ -4879,6 +5055,13 @@ void wxGenericListCtrl::SetItemSpacing( int spacing, bool isSmall ) m_mainWin->SetItemSpacing( spacing, isSmall ); } +wxSize wxGenericListCtrl::GetItemSpacing() const +{ + const int spacing = m_mainWin->GetItemSpacing(HasFlag(wxLC_SMALL_ICON)); + + return wxSize(spacing, spacing); +} + int wxGenericListCtrl::GetItemSpacing( bool isSmall ) const { return m_mainWin->GetItemSpacing( isSmall ); @@ -4966,19 +5149,19 @@ void wxGenericListCtrl::SetImageList( wxImageListType *imageList, int which ) { if (m_ownsImageListNormal) delete m_imageListNormal; m_imageListNormal = imageList; - m_ownsImageListNormal = FALSE; + m_ownsImageListNormal = false; } else if ( which == wxIMAGE_LIST_SMALL ) { if (m_ownsImageListSmall) delete m_imageListSmall; m_imageListSmall = imageList; - m_ownsImageListSmall = FALSE; + m_ownsImageListSmall = false; } else if ( which == wxIMAGE_LIST_STATE ) { if (m_ownsImageListState) delete m_imageListState; m_imageListState = imageList; - m_ownsImageListState = FALSE; + m_ownsImageListState = false; } m_mainWin->SetImageList( imageList, which ); @@ -4988,11 +5171,11 @@ void wxGenericListCtrl::AssignImageList(wxImageListType *imageList, int which) { SetImageList(imageList, which); if ( which == wxIMAGE_LIST_NORMAL ) - m_ownsImageListNormal = TRUE; + m_ownsImageListNormal = true; else if ( which == wxIMAGE_LIST_SMALL ) - m_ownsImageListSmall = TRUE; + m_ownsImageListSmall = true; else if ( which == wxIMAGE_LIST_STATE ) - m_ownsImageListState = TRUE; + m_ownsImageListState = true; } bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) ) @@ -5003,13 +5186,13 @@ bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) ) bool wxGenericListCtrl::DeleteItem( long item ) { m_mainWin->DeleteItem( item ); - return TRUE; + return true; } bool wxGenericListCtrl::DeleteAllItems() { m_mainWin->DeleteAllItems(); - return TRUE; + return true; } bool wxGenericListCtrl::DeleteAllColumns() @@ -5018,7 +5201,7 @@ bool wxGenericListCtrl::DeleteAllColumns() for ( size_t n = 0; n < count; n++ ) DeleteColumn(0); - return TRUE; + return true; } void wxGenericListCtrl::ClearAll() @@ -5033,10 +5216,10 @@ bool wxGenericListCtrl::DeleteColumn( int col ) // if we don't have the header any longer, we need to relayout the window if ( !GetColumnCount() ) { - ResizeReportView(FALSE /* no header */); + ResizeReportView(false /* no header */); } - return TRUE; + return true; } void wxGenericListCtrl::Edit( long item ) @@ -5047,7 +5230,7 @@ void wxGenericListCtrl::Edit( long item ) bool wxGenericListCtrl::EnsureVisible( long item ) { m_mainWin->EnsureVisible( item ); - return TRUE; + return true; } long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial ) @@ -5055,15 +5238,15 @@ long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial return m_mainWin->FindItem( start, str, partial ); } -long wxGenericListCtrl::FindItem( long start, long data ) +long wxGenericListCtrl::FindItem( long start, wxUIntPtr data ) { return m_mainWin->FindItem( start, data ); } -long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt), +long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& pt, int WXUNUSED(direction)) { - return 0; + return m_mainWin->FindItem( pt ); } long wxGenericListCtrl::HitTest( const wxPoint &point, int &flags ) @@ -5113,9 +5296,9 @@ 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 */); + ResizeReportView(true /* have header */); } m_headerWin->Refresh(); @@ -5157,7 +5340,7 @@ bool wxGenericListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) ) bool wxGenericListCtrl::SortItems( wxListCtrlCompare fn, long data ) { m_mainWin->SortItems( fn, data ); - return TRUE; + return true; } // ---------------------------------------------------------------------------- @@ -5181,8 +5364,8 @@ void wxGenericListCtrl::ResizeReportView(bool showHeader) if ( showHeader ) { - m_headerWin->SetSize( 0, 0, cw, HEADER_HEIGHT ); - m_mainWin->SetSize( 0, HEADER_HEIGHT + 1, cw, ch - HEADER_HEIGHT - 1 ); + m_headerWin->SetSize( 0, 0, cw, m_headerHeight ); + m_mainWin->SetSize( 0, m_headerHeight + 1, cw, ch - m_headerHeight - 1 ); } else // no header window { @@ -5190,9 +5373,9 @@ 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 ) @@ -5210,21 +5393,21 @@ bool wxGenericListCtrl::SetBackgroundColour( const wxColour &colour ) if (m_mainWin) { m_mainWin->SetBackgroundColour( colour ); - m_mainWin->m_dirty = TRUE; + m_mainWin->m_dirty = true; } - return TRUE; + return true; } bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour ) { if ( !wxWindow::SetForegroundColour( colour ) ) - return FALSE; + return false; if (m_mainWin) { m_mainWin->SetForegroundColour( colour ); - m_mainWin->m_dirty = TRUE; + m_mainWin->m_dirty = true; } if (m_headerWin) @@ -5232,26 +5415,52 @@ bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour ) m_headerWin->SetForegroundColour( colour ); } - return TRUE; + return true; } bool wxGenericListCtrl::SetFont( const wxFont &font ) { if ( !wxWindow::SetFont( font ) ) - return FALSE; + return false; if (m_mainWin) { m_mainWin->SetFont( font ); - m_mainWin->m_dirty = TRUE; + m_mainWin->m_dirty = true; } if (m_headerWin) { m_headerWin->SetFont( font ); + CalculateAndSetHeaderHeight(); } - return TRUE; + Refresh(); + + return true; +} + + + +#if _USE_VISATTR +#include "wx/listbox.h" +#endif + +//static +wxVisualAttributes +wxGenericListCtrl::GetClassDefaultAttributes(wxWindowVariant variant) +{ +#if _USE_VISATTR + // Use the same color scheme as wxListBox + return wxListBox::GetClassDefaultAttributes(variant); +#else + wxUnusedVar(variant); + wxVisualAttributes attr; + attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); + attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX); + attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + return attr; +#endif } // ---------------------------------------------------------------------------- @@ -5274,7 +5483,7 @@ wxDropTarget *wxGenericListCtrl::GetDropTarget() const bool wxGenericListCtrl::SetCursor( const wxCursor &cursor ) { - return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : FALSE; + return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : false; } wxColour wxGenericListCtrl::GetBackgroundColour() const @@ -5292,7 +5501,7 @@ bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) #if wxUSE_MENUS return m_mainWin->PopupMenu( menu, x, y ); #else - return FALSE; + return false; #endif // wxUSE_MENUS } @@ -5300,10 +5509,18 @@ void wxGenericListCtrl::SetFocus() { /* The test in window.cpp fails as we are a composite window, so it checks against "this", but not m_mainWin. */ - if ( FindFocus() != this ) + if ( DoFindFocus() != this ) m_mainWin->SetFocus(); } +wxSize wxGenericListCtrl::DoGetBestSize() const +{ + // Something is better than nothing... + // 100x80 is what the MSW version will get from the default + // wxControl::DoGetBestSize + return wxSize(100,80); +} + // ---------------------------------------------------------------------------- // virtual list control support // ---------------------------------------------------------------------------- @@ -5319,13 +5536,14 @@ wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const { - // same as above - wxFAIL_MSG( _T("wxGenericListCtrl::OnGetItemImage not supposed to be called") ); - + wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL), + -1, + wxT("List control has an image list, OnGetItemImage should be overridden.")); return -1; } -wxListItemAttr *wxGenericListCtrl::OnGetItemAttr(long item) const +wxListItemAttr * +wxGenericListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const { wxASSERT_MSG( item >= 0 && item < GetItemCount(), _T("invalid item index in OnGetItemAttr()") ); @@ -5351,6 +5569,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();