X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/5cd891743efcff4d6603c68b98e72fb033792a88..578dc95029e761a59911b7c1bc09f59384d64ddf:/src/generic/listctrl.cpp diff --git a/src/generic/listctrl.cpp b/src/generic/listctrl.cpp index 725faf3c0f..d1e2359f23 100644 --- a/src/generic/listctrl.cpp +++ b/src/generic/listctrl.cpp @@ -9,38 +9,10 @@ ///////////////////////////////////////////////////////////////////////////// /* - FIXME for virtual list controls + TODO - +1. clicking on the item with a mouse is awfully slow, what is going on? - note that selecting with keyboard seems to be much faster - => fixed HighlightAll() - iterating over 1000000 items *is* slow - - 2. background colour is wrong? - */ - -/* - TODO for better virtual list control support: - - 1. less dumb line caching, we should cache at least all those visible - in the control itself and probably twice as many (we might also need to - cache the first one always for geometry calculations?) - - +2. storing selections: we can't use an array to store the selected indices - like right now as selecting all in a control with 1000000 items is not - doable like this - instead, store selections as collection of individual - items and ranges - - => wxSelectionStore - - 3. we need to implement searching/sorting somehow - - 4. the idea of storing the line index in the line itself is really stupid, - we shouldn't need it - but for this we have to get rid of all calles to - wxListLineData::GetFoo() and replace them with something like - if ( IsVirtual() - ... we have it ourselves ... - else - line->GetFoo(); + 1. we need to implement searching/sorting for virtual controls somehow + ?2. when changing selection the lines are refreshed twice */ // ============================================================================ @@ -56,10 +28,6 @@ #pragma implementation "listctrlbase.h" #endif -#if 0 -#include "listctrl.old.cpp" -#else - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -69,15 +37,40 @@ #if wxUSE_LISTCTRL -#include "wx/dcscreen.h" -#include "wx/app.h" -#include "wx/listctrl.h" -#include "wx/imaglist.h" -#include "wx/dynarray.h" +#ifndef WX_PRECOMP + #include "wx/app.h" -#ifdef __WXGTK__ -#include -#include "wx/gtk/win_gtk.h" + #include "wx/dynarray.h" + + #include "wx/dcscreen.h" + + #include "wx/textctrl.h" +#endif + +// under Win32 we always use the native version and also may use the generic +// one, however some things should be done only if we use only the generic +// version +#if defined(__WIN32__) && !defined(__WXUNIVERSAL__) + #define HAVE_NATIVE_LISTCTRL +#endif + +// if we have the native control, wx/listctrl.h declares it and not this one +#ifdef HAVE_NATIVE_LISTCTRL + #include "wx/generic/listctrl.h" +#else // !HAVE_NATIVE_LISTCTRL + #include "wx/listctrl.h" + + // if we have a native version, its implementation file does all this + IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) + IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl) + IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) + + IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxGenericListCtrl) +#endif // HAVE_NATIVE_LISTCTRL/!HAVE_NATIVE_LISTCTRL + +#if defined(__WXGTK__) + #include + #include "wx/gtk/win_gtk.h" #endif // ---------------------------------------------------------------------------- @@ -97,9 +90,15 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_INSERT_ITEM) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_CLICK) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_RIGHT_CLICK) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_DRAGGING) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_END_DRAG) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK) DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_FOCUSED) +DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT) // ---------------------------------------------------------------------------- // constants @@ -130,6 +129,9 @@ static const int AUTOSIZE_COL_MARGIN = 10; static const int WIDTH_COL_DEFAULT = 80; static const int WIDTH_COL_MIN = 10; +// the space between the image and the text in the report mode +static const int IMAGE_MARGIN_IN_REPORT_MODE = 5; + // ============================================================================ // private classes // ============================================================================ @@ -140,7 +142,7 @@ static const int WIDTH_COL_MIN = 10; int CMPFUNC_CONV wxSizeTCmpFn(size_t n1, size_t n2) { return n1 - n2; } -WX_DEFINE_SORTED_EXPORTED_ARRAY(size_t, wxIndexArray); +WX_DEFINE_SORTED_EXPORTED_ARRAY_LONG(size_t, wxIndexArray); // this class is used to store the selected items in the virtual list control // (but it is not tied to list control and so can be used with other controls @@ -160,7 +162,7 @@ public: void SetItemCount(size_t count) { m_count = count; } // special case of SetItemCount(0) - void Clear() { m_itemsSel.Clear(); m_count = 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") ); } @@ -174,7 +176,14 @@ public: bool SelectItem(size_t item, bool select = TRUE); // select the range of items - void SelectRange(size_t itemFrom, size_t itemTo, bool select = TRUE); + // + // return true and fill the itemsChanged array with the indices of items + // which have changed state if "few" of them did, otherwise return false + // (meaning that too many items changed state to bother counting them + // individually) + bool SelectRange(size_t itemFrom, size_t itemTo, + bool select = TRUE, + wxArrayInt *itemsChanged = NULL); // return true if the given item is selected bool IsSelected(size_t item) const; @@ -212,7 +221,7 @@ class WXDLLEXPORT wxListItemData { public: wxListItemData(wxListMainWindow *owner); - ~wxListItemData() { delete m_attr; delete m_rect; } + ~wxListItemData(); void SetItem( const wxListItem &info ); void SetImage( int image ) { m_image = image; } @@ -247,7 +256,8 @@ public: void GetItem( wxListItem &info ) const; - wxListItemAttr *GetAttributes() const { return m_attr; } + void SetAttr(wxListItemAttr *attr) { m_attr = attr; } + wxListItemAttr *GetAttr() const { return m_attr; } public: // the item image or -1 @@ -280,16 +290,6 @@ protected: class WXDLLEXPORT wxListHeaderData : public wxObject { -protected: - long m_mask; - int m_image; - wxString m_text; - int m_format; - int m_width; - int m_xpos, - m_ypos; - int m_height; - public: wxListHeaderData(); wxListHeaderData( const wxListItem &info ); @@ -311,8 +311,18 @@ public: int GetWidth() const; int GetFormat() const; +protected: + long m_mask; + int m_image; + wxString m_text; + int m_format; + int m_width; + int m_xpos, + m_ypos; + int m_height; + private: - DECLARE_DYNAMIC_CLASS(wxListHeaderData); + void Init(); }; //----------------------------------------------------------------------------- @@ -384,6 +394,9 @@ public: wxString GetText(int index) const; void SetText( int index, const wxString s ); + wxListItemAttr *GetAttr() const; + void SetAttr(wxListItemAttr *attr); + // return true if the highlighting really changed bool Highlight( bool on ); @@ -412,12 +425,16 @@ private: // get the mode (i.e. style) of the list control inline int GetMode() const; - void SetAttributes(wxDC *dc, + // prepare the DC for drawing with these item's attributes, return true if + // we need to draw the items background to highlight it, false otherwise + bool SetAttributes(wxDC *dc, const wxListItemAttr *attr, - const wxColour& colText, - const wxFont& font, bool highlight); + // draw the text on the DC with the correct justification; also add an + // ellipsis if the text is too large to fit in the current width + void DrawTextFormatted(wxDC *dc, const wxString &text, int col, int x, int y, int width); + // these are only used by GetImage/SetImage above, we don't support images // with subitems at the public API level yet void SetImage( int index, int image ); @@ -440,7 +457,7 @@ protected: wxCursor *m_resizeCursor; bool m_isDragging; - // column being resized + // column being resized or -1 int m_column; // divider line position in logical (unscrolled) coords @@ -452,7 +469,6 @@ protected: public: wxListHeaderWindow(); - virtual ~wxListHeaderWindow(); wxListHeaderWindow( wxWindow *win, wxWindowID id, @@ -460,7 +476,9 @@ public: const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = 0, - const wxString &name = "wxlistctrlcolumntitles" ); + const wxString &name = wxT("wxlistctrlcolumntitles") ); + + virtual ~wxListHeaderWindow(); void DoDrawRect( wxDC *dc, int x, int y, int w, int h ); void DrawCurrent(); @@ -474,6 +492,11 @@ public: bool m_dirty; private: + // common part of all ctors + void Init(); + + void SendListEvent(wxEventType type, wxPoint pos); + DECLARE_DYNAMIC_CLASS(wxListHeaderWindow) DECLARE_EVENT_TABLE() }; @@ -498,27 +521,23 @@ public: class WXDLLEXPORT wxListTextCtrl: public wxTextCtrl { -private: - bool *m_accept; - wxString *m_res; - wxListMainWindow *m_owner; - wxString m_startValue; - public: - wxListTextCtrl() {} - wxListTextCtrl( wxWindow *parent, const wxWindowID id, - bool *accept, wxString *res, wxListMainWindow *owner, - const wxString &value = "", - const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, - int style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString &name = "listctrltextctrl" ); + wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit); + +protected: void OnChar( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); + bool AcceptChanges(); + void Finish(); + private: - DECLARE_DYNAMIC_CLASS(wxListTextCtrl); + wxListMainWindow *m_owner; + wxString m_startValue; + size_t m_itemEdited; + bool m_finished; + DECLARE_EVENT_TABLE() }; @@ -575,12 +594,20 @@ public: void ReverseHighlight( size_t line ) { HighlightLine(line, !IsHighlighted(line)); RefreshLine(line); } + // return true if the line is highlighted + bool IsHighlighted(size_t line) const; + // refresh one or several lines at once void RefreshLine( size_t line ); void RefreshLines( size_t lineFrom, size_t lineTo ); - // return true if the line is highlighted - bool IsHighlighted(size_t line) const; + // refresh all selected items + void RefreshSelected(); + + // refresh all lines below the given one: the difference with + // RefreshLines() is that the index here might not be a valid one (happens + // when the last line is deleted) + void RefreshAfter( size_t lineFrom ); // the methods which are forwarded to wxListLineData itself in list/icon // modes but are here because the lines don't store their positions in the @@ -606,12 +633,25 @@ public: // return the hit code for the corresponding position (in this line) long HitTestLine(size_t line, int x, int y) const; + // bring the selected item into view, scrolling to it if necessary + void MoveToItem(size_t item); + + // bring the current item into view + void MoveToFocus() { MoveToItem(m_current); } + + // start editing the label of the given item void EditLabel( long item ); + + // suspend/resume redrawing the control + void Freeze(); + void Thaw(); + + void SetFocus(); + void OnRenameTimer(); - void OnRenameAccept(); + bool OnRenameAccept(size_t itemEdit, const wxString& value); void OnMouse( wxMouseEvent &event ); - void MoveToFocus(); // called to switch the selection from the current item to newCurrent, void OnArrowChar( size_t newCurrent, const wxKeyEvent& event ); @@ -628,7 +668,7 @@ public: void GetImageSize( int index, int &width, int &height ) const; int GetTextLength( const wxString &s ) const; - void SetImageList( wxImageList *imageList, int which ); + void SetImageList( wxImageListType *imageList, int which ); void SetItemSpacing( int spacing, bool isSmall = FALSE ); int GetItemSpacing( bool isSmall = FALSE ); @@ -644,20 +684,37 @@ public: int GetCountPerPage() const; void SetItem( wxListItem &item ); - void GetItem( wxListItem &item ); + void GetItem( wxListItem &item ) const; void SetItemState( long item, long state, long stateMask ); - int GetItemState( long item, long stateMask ); - void GetItemRect( long index, wxRect &rect ); - bool GetItemPosition( long item, wxPoint& pos ); - int GetSelectedItemCount(); + int GetItemState( long item, long stateMask ) const; + void GetItemRect( long index, wxRect &rect ) const; + bool GetItemPosition( long item, wxPoint& pos ) const; + int GetSelectedItemCount() const; + + wxString GetItemText(long item) const + { + wxListItem info; + info.m_itemId = item; + GetItem( info ); + return info.m_text; + } + + void SetItemText(long item, const wxString& value) + { + wxListItem info; + info.m_mask = wxLIST_MASK_TEXT; + info.m_itemId = item; + info.m_text = value; + SetItem( info ); + } // set the scrollbars and update the positions of the items - void RecalculatePositions(); + void RecalculatePositions(bool noRefresh = FALSE); // refresh the window and the header void RefreshAll(); - long GetNextItem( long item, int geometry, int state ); + long GetNextItem( long item, int geometry, int state ) const; void DeleteItem( long index ); void DeleteAllItems(); void DeleteColumn( int col ); @@ -674,7 +731,9 @@ public: bool IsEmpty() const { return GetItemCount() == 0; } void SetItemCount(long count); - void ResetCurrent() { m_current = (size_t)-1; } + // change the current (== focused) item, send a notification event + void ChangeCurrent(size_t current); + void ResetCurrent() { ChangeCurrent((size_t)-1); } bool HasCurrent() const { return m_current != (size_t)-1; } // send out a wxListEvent @@ -696,9 +755,9 @@ public: // these are for wxListLineData usage only // get the backpointer to the list ctrl - wxListCtrl *GetListCtrl() const + wxGenericListCtrl *GetListCtrl() const { - return wxStaticCast(GetParent(), wxListCtrl); + return wxStaticCast(GetParent(), wxGenericListCtrl); } // get the height of all lines (assuming they all do have the same height) @@ -707,8 +766,15 @@ public: // get the y position of the given line (only for report view) wxCoord GetLineY(size_t line) const; + // get the brush to use for the item highlighting + wxBrush *GetHighlightBrush() const + { + return m_hasFocus ? m_highlightBrush : m_highlightUnfocusedBrush; + } + //protected: - // the array of all line objects for a non virtual list control + // the array of all line objects for a non virtual list control (for the + // virtual list control we only ever use m_lines[0]) wxListLineDataArray m_lines; // the list of column objects @@ -717,9 +783,6 @@ public: // currently focused item or -1 size_t m_current; - // the item currently being edited or -1 - size_t m_currentEdit; - // the number of lines per page int m_linesPerPage; @@ -731,20 +794,17 @@ public: // call bool m_dirty; - wxBrush *m_highlightBrush; wxColour *m_highlightColour; int m_xScroll, m_yScroll; - wxImageList *m_small_image_list; - wxImageList *m_normal_image_list; + wxImageListType *m_small_image_list; + wxImageListType *m_normal_image_list; int m_small_spacing; int m_normal_spacing; bool m_hasFocus; bool m_lastOnSame; wxTimer *m_renameTimer; - bool m_renameAccept; - wxString m_renameRes; bool m_isCreated; int m_dragCount; wxPoint m_dragStart; @@ -801,7 +861,7 @@ protected: #ifdef __WXMAC__ return *wxWHITE; #else - return wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT); + return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); #endif } @@ -809,11 +869,8 @@ private: // initialize the current item if needed void UpdateCurrent(); - // called when an item is [un]focuded, i.e. becomes [not] current - // - // currently unused - void OnFocusLine( size_t line ); - void OnUnfocusLine( size_t line ); + // delete all items but don't refresh: called from dtor + void DoDeleteAllItems(); // the height of one line using the current font wxCoord m_lineHeight; @@ -827,8 +884,17 @@ private: size_t m_lineFrom, m_lineTo; - DECLARE_DYNAMIC_CLASS(wxListMainWindow); + // the brushes to use for item highlighting when we do/don't have focus + wxBrush *m_highlightBrush, + *m_highlightUnfocusedBrush; + + // if this is > 0, the control is frozen and doesn't redraw itself + size_t m_freezeCount; + + DECLARE_DYNAMIC_CLASS(wxListMainWindow) DECLARE_EVENT_TABLE() + + friend class wxGenericListCtrl; }; // ============================================================================ @@ -877,12 +943,19 @@ bool wxSelectionStore::SelectItem(size_t item, bool select) return FALSE; } -void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) +bool wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, + bool select, + wxArrayInt *itemsChanged) { + // 100 is hardcoded but it shouldn't matter much: the important thing is + // that we don't refresh everything when really few (e.g. 1 or 2) items + // change state + static const size_t MANY_ITEMS = 100; + wxASSERT_MSG( itemFrom <= itemTo, _T("should be in order") ); // are we going to have more [un]selected items than the other ones? - if ( itemTo - itemFrom > m_count / 2 ) + if ( itemTo - itemFrom > m_count/2 ) { if ( select != m_defaultState ) { @@ -909,6 +982,9 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) if ( selOld.Index(item) == wxNOT_FOUND ) m_itemsSel.Add(item); } + + // many items (> half) changed state + itemsChanged = NULL; } else // select == m_defaultState { @@ -932,6 +1008,19 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) // delete all of them (from end to avoid changing indices) for ( int i = end; i >= (int)start; i-- ) { + if ( itemsChanged ) + { + if ( itemsChanged->GetCount() > MANY_ITEMS ) + { + // stop counting (see comment below) + itemsChanged = NULL; + } + else + { + itemsChanged->Add(m_itemsSel[i]); + } + } + m_itemsSel.RemoveAt(i); } } @@ -939,12 +1028,31 @@ void wxSelectionStore::SelectRange(size_t itemFrom, size_t itemTo, bool select) } else // "few" items change state { + if ( itemsChanged ) + { + itemsChanged->Empty(); + } + // just add the items to the selection for ( size_t item = itemFrom; item <= itemTo; item++ ) { - SelectItem(item, select); + if ( SelectItem(item, select) && itemsChanged ) + { + itemsChanged->Add(item); + + if ( itemsChanged->GetCount() > MANY_ITEMS ) + { + // stop counting them, we'll just eat gobs of memory + // for nothing at all - faster to refresh everything in + // this case + itemsChanged = NULL; + } + } } } + + // we set it to NULL if there are many items changing state + return itemsChanged != NULL; } void wxSelectionStore::OnItemDelete(size_t item) @@ -956,6 +1064,8 @@ void wxSelectionStore::OnItemDelete(size_t item) { // this item itself was in m_itemsSel, remove it from there m_itemsSel.RemoveAt(i); + + count--; } // and adjust the index of all which follow it @@ -972,6 +1082,18 @@ void wxSelectionStore::OnItemDelete(size_t item) // wxListItemData //----------------------------------------------------------------------------- +wxListItemData::~wxListItemData() +{ + // in the virtual list control the attributes are managed by the main + // program, so don't delete them + if ( !m_owner->IsVirtual() ) + { + delete m_attr; + } + + delete m_rect; +} + void wxListItemData::Init() { m_image = -1; @@ -986,7 +1108,7 @@ wxListItemData::wxListItemData(wxListMainWindow *owner) m_owner = owner; - if ( owner->HasFlag(wxLC_REPORT) ) + if ( owner->InReportView() ) { m_rect = NULL; } @@ -1096,12 +1218,10 @@ void wxListItemData::GetItem( wxListItem &info ) const // wxListHeaderData //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListHeaderData,wxObject); - -wxListHeaderData::wxListHeaderData() +void wxListHeaderData::Init() { m_mask = 0; - m_image = 0; + m_image = -1; m_format = 0; m_width = 0; m_xpos = 0; @@ -1109,22 +1229,33 @@ wxListHeaderData::wxListHeaderData() m_height = 0; } +wxListHeaderData::wxListHeaderData() +{ + Init(); +} + wxListHeaderData::wxListHeaderData( const wxListItem &item ) { + Init(); + SetItem( item ); - m_xpos = 0; - m_ypos = 0; - m_height = 0; } void wxListHeaderData::SetItem( const wxListItem &item ) { m_mask = item.m_mask; - m_text = item.m_text; - m_image = item.m_image; - m_format = item.m_format; - SetWidth(item.m_width); + if ( m_mask & wxLIST_MASK_TEXT ) + m_text = item.m_text; + + if ( m_mask & wxLIST_MASK_IMAGE ) + m_image = item.m_image; + + if ( m_mask & wxLIST_MASK_FORMAT ) + m_format = item.m_format; + + if ( m_mask & wxLIST_MASK_WIDTH ) + SetWidth(item.m_width); } void wxListHeaderData::SetPosition( int x, int y ) @@ -1143,7 +1274,7 @@ void wxListHeaderData::SetWidth( int w ) m_width = w; if (m_width < 0) m_width = WIDTH_COL_DEFAULT; - if (m_width < WIDTH_COL_MIN) + else if (m_width < WIDTH_COL_MIN) m_width = WIDTH_COL_MIN; } @@ -1154,7 +1285,7 @@ void wxListHeaderData::SetFormat( int format ) bool wxListHeaderData::HasImage() const { - return (m_image != 0); + return m_image != -1; } bool wxListHeaderData::IsHit( int x, int y ) const @@ -1162,7 +1293,7 @@ bool wxListHeaderData::IsHit( int x, int y ) const return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height)); } -void wxListHeaderData::GetItem( wxListItem &item ) +void wxListHeaderData::GetItem( wxListItem& item ) { item.m_mask = m_mask; item.m_text = m_text; @@ -1350,8 +1481,8 @@ void wxListLineData::SetPosition( int x, int y, if ( item->HasImage() ) { - m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 - + (spacing - m_gi->m_rectIcon.width)/2; + m_gi->m_rectIcon.x = m_gi->m_rectAll.x + 4 + + (m_gi->m_rectAll.width - m_gi->m_rectIcon.width) / 2; m_gi->m_rectIcon.y = m_gi->m_rectAll.y + 4; } @@ -1468,32 +1599,86 @@ int wxListLineData::GetImage( int index ) const return item->GetImage(); } -void wxListLineData::SetAttributes(wxDC *dc, +wxListItemAttr *wxListLineData::GetAttr() const +{ + wxListItemDataList::Node *node = m_items.GetFirst(); + wxCHECK_MSG( node, NULL, _T("invalid column index in GetAttr()") ); + + wxListItemData *item = node->GetData(); + return item->GetAttr(); +} + +void wxListLineData::SetAttr(wxListItemAttr *attr) +{ + wxListItemDataList::Node *node = m_items.GetFirst(); + wxCHECK_RET( node, _T("invalid column index in SetAttr()") ); + + wxListItemData *item = node->GetData(); + item->SetAttr(attr); +} + +bool wxListLineData::SetAttributes(wxDC *dc, const wxListItemAttr *attr, - const wxColour& colText, - const wxFont& font, - bool highlight) + bool highlighted) { - // don't use foregroud colour for drawing highlighted items - this might + wxWindow *listctrl = m_owner->GetParent(); + + // fg colour + + // don't use foreground colour for drawing highlighted items - this might // make them completely invisible (and there is no way to do bit // arithmetics on wxColour, unfortunately) - if ( !highlight && attr && attr->HasTextColour() ) + wxColour colText; + if ( highlighted ) { - dc->SetTextForeground(attr->GetTextColour()); + colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { - dc->SetTextForeground(colText); + if ( attr && attr->HasTextColour() ) + { + colText = attr->GetTextColour(); + } + else + { + colText = listctrl->GetForegroundColour(); + } } + dc->SetTextForeground(colText); + + // font + wxFont font; if ( attr && attr->HasFont() ) { - dc->SetFont(attr->GetFont()); + font = attr->GetFont(); } else { - dc->SetFont(font); + font = listctrl->GetFont(); } + + dc->SetFont(font); + + // bg colour + bool hasBgCol = attr && attr->HasBackgroundColour(); + if ( highlighted || hasBgCol ) + { + if ( highlighted ) + { + dc->SetBrush( *m_owner->GetHighlightBrush() ); + } + else + { + dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); + } + + dc->SetPen( *wxTRANSPARENT_PEN ); + + return TRUE; + } + + return FALSE; } void wxListLineData::Draw( wxDC *dc ) @@ -1501,6 +1686,15 @@ void wxListLineData::Draw( wxDC *dc ) wxListItemDataList::Node *node = m_items.GetFirst(); wxCHECK_RET( node, _T("no subitems at all??") ); + bool highlighted = IsHighlighted(); + + wxListItemAttr *attr = GetAttr(); + + if ( SetAttributes(dc, attr, highlighted) ) + { + dc->DrawRectangle( m_gi->m_rectHighlight ); + } + wxListItemData *item = node->GetData(); if (item->HasImage()) { @@ -1512,102 +1706,125 @@ void wxListLineData::Draw( wxDC *dc ) if (item->HasText()) { wxRect rectLabel = m_gi->m_rectLabel; + + wxDCClipper clipper(*dc, rectLabel); dc->DrawText( item->GetText(), rectLabel.x, rectLabel.y ); } } void wxListLineData::DrawInReportMode( wxDC *dc, - const wxRect& r, + const wxRect& rect, const wxRect& rectHL, bool highlighted ) { - wxRect rect = r; - //m_owner->CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); - - if ( !m_owner->IsExposed( rect ) ) - return; - - // use our own flag if we maintain it - if ( !IsVirtual() ) - highlighted = m_highlighted; - - // default foreground colour - wxWindow *listctrl = m_owner->GetParent(); - wxColour colText; - if ( highlighted ) + // TODO: later we should support setting different attributes for + // different columns - to do it, just add "col" argument to + // GetAttr() and move these lines into the loop below + wxListItemAttr *attr = GetAttr(); + if ( SetAttributes(dc, attr, highlighted) ) { - colText = wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ); - } - else - { - colText = listctrl->GetForegroundColour(); - } - - // default font - wxFont font = listctrl->GetFont(); - - // VZ: currently we set the colours/fonts only once, but like this (i.e. - // using SetAttributes() inside the loop), it will be trivial to - // customize the subitems (in report mode) too. - wxListItemData *item = m_items.GetFirst()->GetData(); - wxListItemAttr *attr = item->GetAttributes(); - SetAttributes(dc, attr, colText, font, highlighted); - - bool hasBgCol = attr && attr->HasBackgroundColour(); - if ( highlighted || hasBgCol ) - { - if ( highlighted ) - { - dc->SetBrush( *m_owner->m_highlightBrush ); - } - else - { - if ( hasBgCol ) - dc->SetBrush(wxBrush(attr->GetBackgroundColour(), wxSOLID)); - else - dc->SetBrush( * wxWHITE_BRUSH ); - } - - dc->SetPen( * wxTRANSPARENT_PEN ); dc->DrawRectangle( rectHL ); } - wxListItemDataList::Node *node = m_items.GetFirst(); - wxCHECK_RET( node, _T("no subitems at all??") ); + wxCoord x = rect.x + HEADER_OFFSET_X, + y = rect.y + (LINE_SPACING + EXTRA_HEIGHT) / 2; size_t col = 0; - int x = rect.x + HEADER_OFFSET_X; - - rect.y += (LINE_SPACING + EXTRA_HEIGHT) / 2; - - while ( node ) + for ( wxListItemDataList::Node *node = m_items.GetFirst(); + node; + node = node->GetNext(), col++ ) { wxListItemData *item = node->GetData(); + int width = m_owner->GetColumnWidth(col); int xOld = x; + x += width; if ( item->HasImage() ) { int ix, iy; - m_owner->DrawImage( item->GetImage(), dc, x, rect.y ); + m_owner->DrawImage( item->GetImage(), dc, xOld, y ); m_owner->GetImageSize( item->GetImage(), ix, iy ); - x += ix + 5; // FIXME: what is "5"? - } - int width = m_owner->GetColumnWidth(col++); + ix += IMAGE_MARGIN_IN_REPORT_MODE; + + xOld += ix; + width -= ix; + } - dc->SetClippingRegion(x, rect.y, width, rect.height); + wxDCClipper clipper(*dc, xOld, y, width - 8, rect.height); if ( item->HasText() ) { - dc->DrawText( item->GetText(), x, rect.y ); + DrawTextFormatted(dc, item->GetText(), col, xOld, y, width - 8); + } + } +} + +void wxListLineData::DrawTextFormatted(wxDC *dc, + const wxString &text, + int col, + int x, + int y, + int width) +{ + wxString drawntext, ellipsis; + wxCoord w, h, base_w; + wxListItem item; + + // determine if the string can fit inside the current width + dc->GetTextExtent(text, &w, &h); + if (w <= width) + { + // it can, draw it using the items alignment + m_owner->GetColumn(col, item); + 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->DestroyClippingRegion(); + dc->DrawText(text, x, y); + } + else // otherwise, truncate and add an ellipsis if possible + { + // determine the base width + ellipsis = wxString(wxT("...")); + 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) + { + dc->GetTextExtent(drawntext, &w, &h); + if (w + base_w <= width) + break; + drawntext = drawntext.Left(drawntext.Length() - 1); + } - x = xOld + width; + // if still not enough space, remove ellipsis characters + while (ellipsis.Length() > 0 && w + base_w > width) + { + ellipsis = ellipsis.Left(ellipsis.Length() - 1); + dc->GetTextExtent(ellipsis, &base_w, &h); + } - node = node->GetNext(); + // now draw the text + dc->DrawText(drawntext, x, y); + dc->DrawText(ellipsis, x + w, y); } } @@ -1632,7 +1849,7 @@ void wxListLineData::ReverseHighlight( void ) // wxListHeaderWindow //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListHeaderWindow,wxWindow); +IMPLEMENT_DYNAMIC_CLASS(wxListHeaderWindow,wxWindow) BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow) EVT_PAINT (wxListHeaderWindow::OnPaint) @@ -1640,37 +1857,51 @@ BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow) EVT_SET_FOCUS (wxListHeaderWindow::OnSetFocus) END_EVENT_TABLE() -wxListHeaderWindow::wxListHeaderWindow( void ) +void wxListHeaderWindow::Init() { - m_owner = (wxListMainWindow *) NULL; m_currentCursor = (wxCursor *) NULL; - m_resizeCursor = (wxCursor *) NULL; m_isDragging = FALSE; + m_dirty = FALSE; +} + +wxListHeaderWindow::wxListHeaderWindow() +{ + Init(); + + m_owner = (wxListMainWindow *) NULL; + m_resizeCursor = (wxCursor *) NULL; } -wxListHeaderWindow::wxListHeaderWindow( wxWindow *win, wxWindowID id, wxListMainWindow *owner, - const wxPoint &pos, const wxSize &size, - long style, const wxString &name ) : - wxWindow( win, id, pos, size, style, name ) +wxListHeaderWindow::wxListHeaderWindow( wxWindow *win, + wxWindowID id, + wxListMainWindow *owner, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString &name ) + : wxWindow( win, id, pos, size, style, name ) { + Init(); + m_owner = owner; -// m_currentCursor = wxSTANDARD_CURSOR; - m_currentCursor = (wxCursor *) NULL; m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE ); - m_isDragging = FALSE; - m_dirty = FALSE; - SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE ) ); + SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); } -wxListHeaderWindow::~wxListHeaderWindow( void ) +wxListHeaderWindow::~wxListHeaderWindow() { delete m_resizeCursor; } +#ifdef __WXUNIVERSAL__ +#include "wx/univ/renderer.h" +#include "wx/univ/theme.h" +#endif + void wxListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h ) { -#ifdef __WXGTK__ +#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__) GtkStateType state = m_parent->IsEnabled() ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE; @@ -1678,14 +1909,19 @@ void wxListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h ) gtk_paint_box (m_wxwindow->style, GTK_PIZZA(m_wxwindow)->bin_window, state, GTK_SHADOW_OUT, - (GdkRectangle*) NULL, m_wxwindow, "button", + (GdkRectangle*) NULL, m_wxwindow, + (char *)"button", // const_cast x-1, y-1, w+2, h+2); -#elif defined( __WXMAC__ ) +#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::GetSystemColour( wxSYS_COLOUR_BTNSHADOW ) , 1 , wxSOLID ) ); + 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) @@ -1709,7 +1945,7 @@ void wxListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h ) 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::GetSystemColour( wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID ); + 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) @@ -1739,7 +1975,7 @@ void wxListHeaderWindow::AdjustDC(wxDC& dc) void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { -#ifdef __WXGTK__ +#if defined(__WXGTK__) wxClientDC dc( this ); #else wxPaintDC dc( this ); @@ -1769,25 +2005,93 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) int numColumns = m_owner->GetColumnCount(); wxListItem item; - for (int i = 0; i < numColumns; i++) + for ( int i = 0; i < numColumns && x < w; i++ ) { m_owner->GetColumn( i, item ); int wCol = item.m_width; - int cw = wCol - 2; // the width of the rect to draw - int xEnd = x + wCol; + // the width of the rect to draw: make it smaller to fit entirely + // inside the column rect + int cw = wCol - 2; dc.SetPen( *wxWHITE_PEN ); DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 ); - dc.SetClippingRegion( x, HEADER_OFFSET_Y, cw-5, h-4 ); - dc.DrawText( item.GetText(), x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT ); - dc.DestroyClippingRegion(); - x += wCol; - if (xEnd > w+5) - break; + // see if we have enough space for the column label + + // for this we need the width of the text + wxCoord wLabel; + dc.GetTextExtent(item.GetText(), &wLabel, NULL); + 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 ) + { + imageList = m_owner->m_small_image_list; + if ( imageList ) + { + imageList->GetSize(image, ix, iy); + wLabel += ix + MARGIN_BETWEEN_TEXT_AND_ICON; + } + } + else + { + imageList = NULL; + } + + // 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 + + 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 + // boundary + wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 ); + + dc.DrawText( item.GetText(), + xAligned + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT ); + + x += wCol; } + dc.EndDrawing(); } @@ -1795,9 +2099,9 @@ void wxListHeaderWindow::DrawCurrent() { int x1 = m_currentX; int y1 = 0; - ClientToScreen( &x1, &y1 ); + m_owner->ClientToScreen( &x1, &y1 ); - int x2 = m_currentX-1; + int x2 = m_currentX; int y2 = 0; m_owner->GetClientSize( NULL, &y2 ); m_owner->ClientToScreen( &x2, &y2 ); @@ -1826,6 +2130,9 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) if (m_isDragging) { + SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, + event.GetPosition()); + // we don't draw the line beyond our window, but we allow dragging it // there int w = 0; @@ -1843,6 +2150,8 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) m_isDragging = FALSE; m_dirty = TRUE; m_owner->SetColumnWidth( m_column, m_currentX - m_minX ); + SendListEvent(wxEVT_COMMAND_LIST_COL_END_DRAG, + event.GetPosition()); } else { @@ -1865,8 +2174,9 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) int xpos = 0; // find the column where this event occured - int countCol = m_owner->GetColumnCount(); - for (int col = 0; col < countCol; col++) + int col, + countCol = m_owner->GetColumnCount(); + for (col = 0; col < countCol; col++) { xpos += m_owner->GetColumnWidth( col ); m_column = col; @@ -1887,22 +2197,26 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event ) m_minX = xpos; } - if (event.LeftDown()) + if ( col == countCol ) + m_column = -1; + + if (event.LeftDown() || event.RightUp()) { - if (hit_border) + if (hit_border && event.LeftDown()) { m_isDragging = TRUE; m_currentX = x; DrawCurrent(); CaptureMouse(); + SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, + event.GetPosition()); } - else + else // click on a column { - wxWindow *parent = GetParent(); - wxListEvent le( wxEVT_COMMAND_LIST_COL_CLICK, parent->GetId() ); - le.SetEventObject( parent ); - le.m_col = m_column; - parent->GetEventHandler()->ProcessEvent( le ); + SendListEvent( event.LeftDown() + ? wxEVT_COMMAND_LIST_COL_CLICK + : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK, + event.GetPosition()); } } else if (event.Moving()) @@ -1930,6 +2244,23 @@ void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) m_owner->SetFocus(); } +void wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos) +{ + wxWindow *parent = GetParent(); + wxListEvent le( type, parent->GetId() ); + le.SetEventObject( parent ); + le.m_pointDrag = pos; + + // the position should be relative to the parent window, not + // this one for compatibility with MSW and common sense: the + // user code doesn't know anything at all about this header + // window, so why should it get positions relative to it? + le.m_pointDrag.y -= GetSize().y; + + le.m_col = m_column; + parent->GetEventHandler()->ProcessEvent( le ); +} + //----------------------------------------------------------------------------- // wxListRenameTimer (internal) //----------------------------------------------------------------------------- @@ -1948,72 +2279,98 @@ void wxListRenameTimer::Notify() // wxListTextCtrl (internal) //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListTextCtrl,wxTextCtrl); - BEGIN_EVENT_TABLE(wxListTextCtrl,wxTextCtrl) EVT_CHAR (wxListTextCtrl::OnChar) EVT_KEY_UP (wxListTextCtrl::OnKeyUp) EVT_KILL_FOCUS (wxListTextCtrl::OnKillFocus) END_EVENT_TABLE() -wxListTextCtrl::wxListTextCtrl( wxWindow *parent, - const wxWindowID id, - bool *accept, - wxString *res, - wxListMainWindow *owner, - const wxString &value, - const wxPoint &pos, - const wxSize &size, - int style, - const wxValidator& validator, - const wxString &name ) - : wxTextCtrl( parent, id, value, pos, size, style, validator, name ) -{ - m_res = res; - m_accept = accept; +wxListTextCtrl::wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit) + : m_startValue(owner->GetItemText(itemEdit)), + m_itemEdited(itemEdit) +{ m_owner = owner; - (*m_accept) = FALSE; - (*m_res) = ""; - m_startValue = value; + m_finished = FALSE; + + wxRect rectLabel = owner->GetLineLabelRect(itemEdit); + + m_owner->CalcScrolledPosition(rectLabel.x, rectLabel.y, + &rectLabel.x, &rectLabel.y); + + (void)Create(owner, wxID_ANY, m_startValue, + wxPoint(rectLabel.x-4,rectLabel.y-4), + wxSize(rectLabel.width+11,rectLabel.height+8)); } -void wxListTextCtrl::OnChar( wxKeyEvent &event ) +void wxListTextCtrl::Finish() { - if (event.m_keyCode == WXK_RETURN) + if ( !m_finished ) { - (*m_accept) = TRUE; - (*m_res) = GetValue(); + wxPendingDelete.Append(this); - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + m_finished = TRUE; - if ((*m_accept) && ((*m_res) != m_startValue)) - m_owner->OnRenameAccept(); + m_owner->SetFocus(); + } +} - return; +bool wxListTextCtrl::AcceptChanges() +{ + const wxString value = GetValue(); + + if ( value == m_startValue ) + { + // nothing changed, always accept + return TRUE; } - if (event.m_keyCode == WXK_ESCAPE) + + if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) { - (*m_accept) = FALSE; - (*m_res) = ""; + // vetoed by the user + return FALSE; + } - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + // accepted, do rename the item + m_owner->SetItemText(m_itemEdited, value); - return; - } + return TRUE; +} - event.Skip(); +void wxListTextCtrl::OnChar( wxKeyEvent &event ) +{ + switch ( event.m_keyCode ) + { + case WXK_RETURN: + if ( !AcceptChanges() ) + { + // vetoed by the user code + break; + } + //else: fall through + + case WXK_ESCAPE: + Finish(); + break; + + default: + event.Skip(); + } } void wxListTextCtrl::OnKeyUp( wxKeyEvent &event ) { + if (m_finished) + { + event.Skip(); + return; + } + // auto-grow the textctrl: wxSize parentSize = m_owner->GetSize(); wxPoint myPos = GetPosition(); wxSize mySize = GetSize(); int sx, sy; - GetTextExtent(GetValue() + _T("MM"), &sx, &sy); // FIXME: MM?? + GetTextExtent(GetValue() + _T("MM"), &sx, &sy); if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x; if (mySize.x > sx) @@ -2023,20 +2380,23 @@ void wxListTextCtrl::OnKeyUp( wxKeyEvent &event ) event.Skip(); } -void wxListTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) +void wxListTextCtrl::OnKillFocus( wxFocusEvent &event ) { - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + if ( !m_finished ) + { + (void)AcceptChanges(); + + Finish(); + } - if ((*m_accept) && ((*m_res) != m_startValue)) - m_owner->OnRenameAccept(); + event.Skip(); } //----------------------------------------------------------------------------- // wxListMainWindow //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListMainWindow,wxScrolledWindow); +IMPLEMENT_DYNAMIC_CLASS(wxListMainWindow,wxScrolledWindow) BEGIN_EVENT_TABLE(wxListMainWindow,wxScrolledWindow) EVT_PAINT (wxListMainWindow::OnPaint) @@ -2060,8 +2420,8 @@ void wxListMainWindow::Init() m_headerWidth = m_lineHeight = 0; - m_small_image_list = (wxImageList *) NULL; - m_normal_image_list = (wxImageList *) NULL; + m_small_image_list = (wxImageListType *) NULL; + m_normal_image_list = (wxImageListType *) NULL; m_small_spacing = 30; m_normal_spacing = 40; @@ -2072,12 +2432,12 @@ void wxListMainWindow::Init() m_lastOnSame = FALSE; m_renameTimer = new wxListRenameTimer( this ); - m_renameAccept = FALSE; m_current = - m_currentEdit = m_lineLastClicked = m_lineBeforeLastClicked = (size_t)-1; + + m_freezeCount = 0; } void wxListMainWindow::InitScrolling() @@ -2098,7 +2458,8 @@ wxListMainWindow::wxListMainWindow() { Init(); - m_highlightBrush = (wxBrush *) NULL; + m_highlightBrush = + m_highlightUnfocusedBrush = (wxBrush *) NULL; m_xScroll = m_yScroll = 0; @@ -2115,28 +2476,46 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent, { Init(); - m_highlightBrush = new wxBrush( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT), wxSOLID ); + m_highlightBrush = new wxBrush + ( + wxSystemSettings::GetColour + ( + wxSYS_COLOUR_HIGHLIGHT + ), + wxSOLID + ); + + m_highlightUnfocusedBrush = new wxBrush + ( + wxSystemSettings::GetColour + ( + wxSYS_COLOUR_BTNSHADOW + ), + wxSOLID + ); + wxSize sz = size; sz.y = 25; InitScrolling(); SetScrollbars( m_xScroll, m_yScroll, 0, 0, 0, 0 ); - SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) ); + SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ) ); } wxListMainWindow::~wxListMainWindow() { - DeleteEverything(); + DoDeleteAllItems(); delete m_highlightBrush; + delete m_highlightUnfocusedBrush; delete m_renameTimer; } void wxListMainWindow::CacheLineData(size_t line) { - wxListCtrl *listctrl = GetListCtrl(); + wxGenericListCtrl *listctrl = GetListCtrl(); wxListLineData *ld = GetDummyLine(); @@ -2147,21 +2526,34 @@ void wxListMainWindow::CacheLineData(size_t line) } ld->SetImage(listctrl->OnGetItemImage(line)); + ld->SetAttr(listctrl->OnGetItemAttr(line)); } wxListLineData *wxListMainWindow::GetDummyLine() const { wxASSERT_MSG( !IsEmpty(), _T("invalid line index") ); - if ( m_lines.IsEmpty() ) + wxASSERT_MSG( IsVirtual(), _T("GetDummyLine() shouldn't be called") ); + + wxListMainWindow *self = wxConstCast(this, wxListMainWindow); + + // we need to recreate the dummy line if the number of columns in the + // control changed as it would have the incorrect number of fields + // otherwise + if ( !m_lines.IsEmpty() && + m_lines[0].m_items.GetCount() != (size_t)GetColumnCount() ) { - // normal controls are supposed to have something in m_lines - // already if it's not empty - wxASSERT_MSG( IsVirtual(), _T("logic error") ); + self->m_lines.Clear(); + } - wxListMainWindow *self = wxConstCast(this, wxListMainWindow); + if ( m_lines.IsEmpty() ) + { wxListLineData *line = new wxListLineData(self); self->m_lines.Add(line); + + // don't waste extra memory -- there never going to be anything + // else/more in this array + self->m_lines.Shrink(); } return &m_lines[0]; @@ -2188,8 +2580,16 @@ wxCoord wxListMainWindow::GetLineHeight() const 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; } @@ -2255,12 +2655,17 @@ wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const long wxListMainWindow::HitTestLine(size_t line, int x, int y) const { + wxASSERT_MSG( line < GetItemCount(), _T("invalid line in HitTestLine") ); + wxListLineData *ld = GetLine(line); if ( ld->HasImage() && GetLineIconRect(line).Inside(x, y) ) return wxLIST_HITTEST_ONITEMICON; - if ( ld->HasText() ) + // VS: Testing for "ld->HasText() || InReportView()" instead of + // "ld->HasText()" is needed to make empty lines in report view + // possible + if ( ld->HasText() || InReportView() ) { wxRect rect = InReportView() ? GetLineRect(line) : GetLineLabelRect(line); @@ -2291,25 +2696,37 @@ bool wxListMainWindow::IsHighlighted(size_t line) const } } -void wxListMainWindow::HighlightLines( size_t lineFrom, size_t lineTo, bool highlight ) +void wxListMainWindow::HighlightLines( size_t lineFrom, + size_t lineTo, + bool highlight ) { if ( IsVirtual() ) { - m_selStore.SelectRange(lineFrom, lineTo, highlight); - RefreshLines(lineFrom, lineTo); + wxArrayInt linesChanged; + if ( !m_selStore.SelectRange(lineFrom, lineTo, highlight, + &linesChanged) ) + { + // meny items changed state, refresh everything + RefreshLines(lineFrom, lineTo); + } + else // only a few items changed state, refresh only them + { + size_t count = linesChanged.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + RefreshLine(linesChanged[n]); + } + } } - else + else // iterate over all items in non report view { - // do it the dumb way - bool needsRefresh = FALSE; for ( size_t line = lineFrom; line <= lineTo; line++ ) { if ( HighlightLine(line, highlight) ) - needsRefresh = TRUE; + { + RefreshLine(line); + } } - - if ( needsRefresh ) - RefreshLines(lineFrom, lineTo); } } @@ -2324,7 +2741,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) else // !virtual { wxListLineData *ld = GetLine(line); - wxCHECK_MSG( ld, FALSE, _T("invalid index in IsHighlighted") ); + wxCHECK_MSG( ld, FALSE, _T("invalid index in HighlightLine") ); changed = ld->Highlight(highlight); } @@ -2340,6 +2757,15 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight ) void wxListMainWindow::RefreshLine( size_t line ) { + if ( HasFlag(wxLC_REPORT) ) + { + size_t visibleFrom, visibleTo; + GetVisibleLinesRange(&visibleFrom, &visibleTo); + + if ( line < visibleFrom || line > visibleTo ) + return; + } + wxRect rect = GetLineRect(line); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); @@ -2351,6 +2777,8 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) // we suppose that they are ordered by caller wxASSERT_MSG( lineFrom <= lineTo, _T("indices in disorder") ); + wxASSERT_MSG( lineTo < GetItemCount(), _T("invalid line range") ); + if ( HasFlag(wxLC_REPORT) ) { size_t visibleFrom, visibleTo; @@ -2365,7 +2793,7 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) rect.x = 0; rect.y = GetLineY(lineFrom); rect.width = GetClientSize().x; - rect.height = GetLineY(lineTo) - rect.y; + rect.height = GetLineY(lineTo) - rect.y + GetLineHeight(); CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); RefreshRect( rect ); @@ -2380,15 +2808,98 @@ void wxListMainWindow::RefreshLines( size_t lineFrom, size_t lineTo ) } } +void wxListMainWindow::RefreshAfter( size_t lineFrom ) +{ + if ( HasFlag(wxLC_REPORT) ) + { + size_t visibleFrom, visibleTo; + GetVisibleLinesRange(&visibleFrom, &visibleTo); + + if ( lineFrom < visibleFrom ) + lineFrom = visibleFrom; + else if ( lineFrom > visibleTo ) + return; + + wxRect rect; + rect.x = 0; + rect.y = GetLineY(lineFrom); + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + + wxSize size = GetClientSize(); + rect.width = size.x; + // refresh till the bottom of the window + rect.height = size.y - rect.y; + + RefreshRect( rect ); + } + else // !report + { + // TODO: how to do it more efficiently? + m_dirty = TRUE; + } +} + +void wxListMainWindow::RefreshSelected() +{ + if ( IsEmpty() ) + return; + + size_t from, to; + if ( InReportView() ) + { + GetVisibleLinesRange(&from, &to); + } + else // !virtual + { + from = 0; + to = GetItemCount() - 1; + } + + if ( HasCurrent() && m_current >= from && m_current <= to ) + { + RefreshLine(m_current); + } + + for ( size_t line = from; line <= to; line++ ) + { + // NB: the test works as expected even if m_current == -1 + if ( line != m_current && IsHighlighted(line) ) + { + RefreshLine(line); + } + } +} + +void wxListMainWindow::Freeze() +{ + m_freezeCount++; +} + +void wxListMainWindow::Thaw() +{ + wxCHECK_RET( m_freezeCount > 0, _T("thawing unfrozen list control?") ); + + if ( !--m_freezeCount ) + { + Refresh(); + } +} + void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { // Note: a wxPaintDC must be constructed even if no drawing is // done (a Windows requirement). wxPaintDC dc( this ); - if ( IsEmpty() ) + if ( IsEmpty() || m_freezeCount ) + { + // nothing to draw or not the moment to draw it + return; + } + + if ( m_dirty ) { - // empty control. nothing to draw + // delay the repainting until we calculate all the items positions return; } @@ -2407,10 +2918,35 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) size_t visibleFrom, visibleTo; GetVisibleLinesRange(&visibleFrom, &visibleTo); + + wxRect rectLine; + wxCoord xOrig, yOrig; + CalcUnscrolledPosition(0, 0, &xOrig, &yOrig); + + // tell the caller cache to cache the data + if ( IsVirtual() ) + { + wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT, + GetParent()->GetId()); + evCache.SetEventObject( GetParent() ); + evCache.m_oldItemIndex = visibleFrom; + evCache.m_itemIndex = visibleTo; + GetParent()->GetEventHandler()->ProcessEvent( evCache ); + } + for ( size_t line = visibleFrom; line <= visibleTo; line++ ) { + rectLine = GetLineRect(line); + + if ( !IsExposed(rectLine.x - xOrig, rectLine.y - yOrig, + rectLine.width, rectLine.height) ) + { + // don't redraw unaffected lines to avoid flicker + continue; + } + GetLine(line)->DrawInReportMode( &dc, - GetLineRect(line), + rectLine, GetLineHighlightRect(line), IsHighlighted(line) ); } @@ -2420,7 +2956,8 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) wxPen pen(GetRuleColour(), 1, wxSOLID); wxSize clientSize = GetClientSize(); - for ( size_t i = visibleFrom; i <= visibleTo; i++ ) + // Don't draw the first one + for ( size_t i = visibleFrom+1; i <= visibleTo; i++ ) { dc.SetPen(pen); dc.SetBrush( *wxTRANSPARENT_BRUSH ); @@ -2429,12 +2966,12 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) } // Draw last horizontal rule - if ( visibleTo > visibleFrom ) + if ( visibleTo == GetItemCount() - 1 ) { dc.SetPen(pen); dc.SetBrush( *wxTRANSPARENT_BRUSH ); - dc.DrawLine(0 - dev_x, m_lineTo*lineHeight, - clientSize.x - dev_x , m_lineTo*lineHeight ); + dc.DrawLine(0 - dev_x, (m_lineTo+1)*lineHeight, + clientSize.x - dev_x , (m_lineTo+1)*lineHeight ); } } @@ -2446,8 +2983,8 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) int col = 0; wxRect firstItemRect; wxRect lastItemRect; - GetItemRect(0, firstItemRect); - GetItemRect(GetItemCount() - 1, lastItemRect); + GetItemRect(visibleFrom, firstItemRect); + GetItemRect(visibleTo, lastItemRect); int x = firstItemRect.GetX(); dc.SetPen(pen); dc.SetBrush(* wxTRANSPARENT_BRUSH); @@ -2455,8 +2992,8 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) { int colWidth = GetColumnWidth(col); x += colWidth; - dc.DrawLine(x - dev_x, firstItemRect.GetY() - 1 - dev_y, - x - dev_x, lastItemRect.GetBottom() + 1 - dev_y); + dc.DrawLine(x - dev_x - 2, firstItemRect.GetY() - 1 - dev_y, + x - dev_x - 2, lastItemRect.GetBottom() + 1 - dev_y); } } } @@ -2469,15 +3006,21 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) } } - if ( HasCurrent() && m_hasFocus ) + 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__ - // no rect outline, we already have the background color -#else - dc.SetPen( *wxBLACK_PEN ); - dc.SetBrush( *wxTRANSPARENT_BRUSH ); - dc.DrawRectangle( GetLineHighlightRect(m_current) ); -#endif + if ( !m_hasFocus ) +#else // !__WXMAC__ + if ( m_hasFocus ) +#endif // __WXMAC__/!__WXMAC__ + { + dc.SetPen( *wxBLACK_PEN ); + dc.SetBrush( *wxTRANSPARENT_BRUSH ); + dc.DrawRectangle( GetLineHighlightRect(m_current) ); + } } dc.EndDrawing(); @@ -2514,62 +3057,56 @@ void wxListMainWindow::SendNotify( size_t line, if ( point != wxDefaultPosition ) le.m_pointDrag = point; - GetLine(line)->GetItem( 0, le.m_item ); + // don't try to get the line info for virtual list controls: the main + // program has it anyhow and if we did it would result in accessing all + // the lines, even those which are not visible now and this is precisely + // what we're trying to avoid + if ( !IsVirtual() && (command != wxEVT_COMMAND_LIST_DELETE_ITEM) ) + { + if ( line != (size_t)-1 ) + { + GetLine(line)->GetItem( 0, le.m_item ); + } + //else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event + } + //else: there may be no more such item + GetParent()->GetEventHandler()->ProcessEvent( le ); } -void wxListMainWindow::OnFocusLine( size_t WXUNUSED(line) ) +void wxListMainWindow::ChangeCurrent(size_t current) { -// SendNotify( line, wxEVT_COMMAND_LIST_ITEM_FOCUSSED ); -} + m_current = current; -void wxListMainWindow::OnUnfocusLine( size_t WXUNUSED(line) ) -{ -// SendNotify( line, wxEVT_COMMAND_LIST_ITEM_UNFOCUSSED ); + SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED); } void wxListMainWindow::EditLabel( long item ) { wxCHECK_RET( (item >= 0) && ((size_t)item < GetItemCount()), - wxT("wrong index in wxListCtrl::EditLabel()") ); + wxT("wrong index in wxGenericListCtrl::EditLabel()") ); - m_currentEdit = (size_t)item; + size_t itemEdit = (size_t)item; wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() ); le.SetEventObject( GetParent() ); le.m_itemIndex = item; - wxListLineData *data = GetLine(m_currentEdit); + wxListLineData *data = GetLine(itemEdit); wxCHECK_RET( data, _T("invalid index in EditLabel()") ); data->GetItem( 0, le.m_item ); - GetParent()->GetEventHandler()->ProcessEvent( le ); - - if (!le.IsAllowed()) + if ( GetParent()->GetEventHandler()->ProcessEvent( le ) && !le.IsAllowed() ) + { + // vetoed by user code return; + } // We have to call this here because the label in question might just have // been added and no screen update taken place. - if (m_dirty) + if ( m_dirty ) wxSafeYield(); - wxClientDC dc(this); - PrepareDC( dc ); + wxListTextCtrl *text = new wxListTextCtrl(this, itemEdit); - wxString s = data->GetText(0); - wxRect rectLabel = GetLineLabelRect(m_currentEdit); - - rectLabel.x = dc.LogicalToDeviceX( rectLabel.x ); - rectLabel.y = dc.LogicalToDeviceY( rectLabel.y ); - - wxListTextCtrl *text = new wxListTextCtrl - ( - this, -1, - &m_renameAccept, - &m_renameRes, - this, - s, - wxPoint(rectLabel.x-4,rectLabel.y-4), - wxSize(rectLabel.width+11,rectLabel.height+8) - ); text->SetFocus(); } @@ -2580,27 +3117,19 @@ void wxListMainWindow::OnRenameTimer() EditLabel( m_current ); } -void wxListMainWindow::OnRenameAccept() +bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value) { wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() ); le.SetEventObject( GetParent() ); - le.m_itemIndex = m_currentEdit; - - wxListLineData *data = GetLine(m_currentEdit); - wxCHECK_RET( data, _T("invalid index in OnRenameAccept()") ); + le.m_itemIndex = itemEdit; - data->GetItem( 0, le.m_item ); - le.m_item.m_text = m_renameRes; - GetParent()->GetEventHandler()->ProcessEvent( le ); + wxListLineData *data = GetLine(itemEdit); + wxCHECK_MSG( data, FALSE, _T("invalid index in OnRenameAccept()") ); - if (!le.IsAllowed()) return; - - wxListItem info; - info.m_mask = wxLIST_MASK_TEXT; - info.m_itemId = le.m_itemIndex; - info.m_text = m_renameRes; - info.SetTextColour(le.m_item.GetTextColour()); - SetItem( info ); + data->GetItem( 0, le.m_item ); + le.m_item.m_text = value; + return !GetParent()->GetEventHandler()->ProcessEvent( le ) || + le.IsAllowed(); } void wxListMainWindow::OnMouse( wxMouseEvent &event ) @@ -2623,7 +3152,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) int y = event.GetY(); CalcUnscrolledPosition( x, y, &x, &y ); - /* Did we actually hit an item ? */ + // where did we hit it (if we did)? long hitResult = 0; size_t count = GetItemCount(), @@ -2632,22 +3161,30 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if ( HasFlag(wxLC_REPORT) ) { current = y / GetLineHeight(); - hitResult = HitTestLine(current, x, y); + if ( current < count ) + hitResult = HitTestLine(current, x, y); } else // !report { // TODO: optimize it too! this is less simple than for report view but // enumerating all items is still not a way to do it!! - for ( current = 0; current < count && !hitResult; current++ ) + for ( current = 0; current < count; current++ ) { hitResult = HitTestLine(current, x, y); + if ( hitResult ) + break; } } if (event.Dragging()) { if (m_dragCount == 0) - m_dragStart = wxPoint(x,y); + { + // we have to report the raw, physical coords as we want to be + // able to call HitTest(event.m_pointDrag) from the user code to + // get the item being dragged + m_dragStart = event.GetPosition(); + } m_dragCount++; @@ -2681,7 +3218,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) m_renameTimer->Stop(); m_lastOnSame = FALSE; - if ( current == m_lineBeforeLastClicked ) + if ( current == m_lineLastClicked ) { SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); @@ -2724,7 +3261,8 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if ( IsSingleSel() || !(event.ControlDown() || event.ShiftDown()) ) { HighlightAll( FALSE ); - m_current = current; + + ChangeCurrent(current); ReverseHighlight(m_current); } @@ -2732,13 +3270,13 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) { if (event.ControlDown()) { - m_current = current; + ChangeCurrent(current); ReverseHighlight(m_current); } else if (event.ShiftDown()) { - m_current = current; + ChangeCurrent(current); size_t lineFrom = oldCurrent, lineTo = current; @@ -2761,8 +3299,6 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) if (m_current != oldCurrent) { RefreshLine( oldCurrent ); - OnUnfocusLine( oldCurrent ); - OnFocusLine( m_current ); } // forceClick is only set if the previous click was on another item @@ -2770,12 +3306,12 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event ) } } -void wxListMainWindow::MoveToFocus() +void wxListMainWindow::MoveToItem(size_t item) { - if ( !HasCurrent() ) + if ( item == (size_t)-1 ) return; - wxRect rect = GetLineRect(m_current); + wxRect rect = GetLineRect(item); int client_w, client_h; GetClientSize( &client_w, &client_h ); @@ -2818,7 +3354,7 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) // items anyhow if ( event.ShiftDown() && !IsSingleSel() ) { - m_current = newCurrent; + ChangeCurrent(newCurrent); // select all the items between the old and the new one if ( oldCurrent > newCurrent ) @@ -2835,9 +3371,9 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) if ( !event.ControlDown() ) HighlightAll(FALSE); - m_current = newCurrent; + ChangeCurrent(newCurrent); - HighlightLine( oldCurrent, FALSE ); + // refresh the old focus to remove it RefreshLine( oldCurrent ); if ( !event.ControlDown() ) @@ -2846,8 +3382,6 @@ void wxListMainWindow::OnArrowChar(size_t newCurrent, const wxKeyEvent& event) } } - OnUnfocusLine( oldCurrent ); - OnFocusLine( m_current ); RefreshLine( m_current ); MoveToFocus(); @@ -2882,7 +3416,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 ); } @@ -2899,14 +3433,15 @@ 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() ); nevent.SetDirection( !event.ShiftDown() ); nevent.SetEventObject( GetParent()->GetParent() ); nevent.SetCurrentFocus( m_parent ); - if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent )) return; + if (GetParent()->GetParent()->GetEventHandler()->ProcessEvent( nevent )) + return; } /* no item -> nothing to do */ @@ -2916,7 +3451,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) return; } - switch (event.KeyCode()) + switch (event.GetKeyCode()) { case WXK_UP: if ( m_current > 0 ) @@ -3006,29 +3541,22 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) case WXK_SPACE: if ( IsSingleSel() ) { - wxListEvent le( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, - GetParent()->GetId() ); - le.SetEventObject( GetParent() ); - le.m_itemIndex = m_current; - GetLine(m_current)->GetItem( 0, le.m_item ); - GetParent()->GetEventHandler()->ProcessEvent( le ); - } - else - { - ReverseHighlight(m_current); + SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); + + if ( IsHighlighted(m_current) ) + { + // don't unselect the item in single selection mode + break; + } + //else: select it in ReverseHighlight() below if unselected } + + ReverseHighlight(m_current); break; case WXK_RETURN: case WXK_EXECUTE: - { - wxListEvent le( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, - GetParent()->GetId() ); - le.SetEventObject( GetParent() ); - le.m_itemIndex = m_current; - GetLine(m_current)->GetItem( 0, le.m_item ); - GetParent()->GetEventHandler()->ProcessEvent( le ); - } + SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED ); break; default: @@ -3040,24 +3568,42 @@ void wxListMainWindow::OnChar( wxKeyEvent &event ) // focus handling // ---------------------------------------------------------------------------- -#ifdef __WXGTK__ -extern wxWindow *g_focusWindow; -#endif +void wxListMainWindow::SetFocus() +{ + // 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 ) + { + wxWindow::SetFocus(); + } + else + { + wxScrolledWindow::SetFocus(); + } +} void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) ) { - m_hasFocus = TRUE; + // 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; - if ( HasCurrent() ) - RefreshLine( m_current ); + RefreshSelected(); + } - if (!GetParent()) + if ( !GetParent() ) return; -#ifdef __WXGTK__ - g_focusWindow = GetParent(); -#endif - wxFocusEvent event( wxEVT_SET_FOCUS, GetParent()->GetId() ); event.SetEventObject( GetParent() ); GetParent()->GetEventHandler()->ProcessEvent( event ); @@ -3067,8 +3613,7 @@ void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) { m_hasFocus = FALSE; - if ( HasCurrent() ) - RefreshLine( m_current ); + RefreshSelected(); } void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y ) @@ -3127,7 +3672,7 @@ int wxListMainWindow::GetTextLength( const wxString &s ) const return lw + AUTOSIZE_COL_MARGIN; } -void wxListMainWindow::SetImageList( wxImageList *imageList, int which ) +void wxListMainWindow::SetImageList( wxImageListType *imageList, int which ) { m_dirty = TRUE; @@ -3149,6 +3694,7 @@ void wxListMainWindow::SetImageList( wxImageList *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 } } @@ -3205,6 +3751,9 @@ void wxListMainWindow::SetColumnWidth( int col, int width ) _T("SetColumnWidth() can only be called in report mode.") ); m_dirty = TRUE; + wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; + if ( headerWin ) + headerWin->m_dirty = TRUE; wxListHeaderDataList::Node *node = m_columns.Item( col ); wxCHECK_RET( node, _T("no column?") ); @@ -3313,20 +3862,16 @@ void wxListMainWindow::SetItem( wxListItem &item ) wxCHECK_RET( id >= 0 && (size_t)id < GetItemCount(), _T("invalid item index in SetItem") ); - if ( IsVirtual() ) - { - // just refresh the line to show the new value of the text/image - RefreshLine((size_t)id); - } - else // !virtual + if ( !IsVirtual() ) { - m_dirty = TRUE; - wxListLineData *line = GetLine((size_t)id); - if ( HasFlag(wxLC_REPORT) ) - item.m_width = GetColumnWidth( item.m_col ); line->SetItem( item.m_col, item ); } + + // update the item on screen + wxRect rectItem; + GetItemRect(id, rectItem); + RefreshRect(rectItem); } void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) @@ -3335,8 +3880,9 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) _T("invalid list ctrl item index in SetItem") ); size_t oldCurrent = m_current; - size_t item = (size_t)litem; // sdafe because of the check above + size_t item = (size_t)litem; // safe because of the check above + // do we need to change the focus? if ( stateMask & wxLIST_STATE_FOCUSED ) { if ( state & wxLIST_STATE_FOCUSED ) @@ -3344,13 +3890,15 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) // don't do anything if this item is already focused if ( item != m_current ) { - OnUnfocusLine( m_current ); - m_current = item; - OnFocusLine( m_current ); + ChangeCurrent(item); - if ( IsSingleSel() && (oldCurrent != (size_t)-1) ) + if ( oldCurrent != (size_t)-1 ) { - HighlightLine(oldCurrent, FALSE); + if ( IsSingleSel() ) + { + HighlightLine(oldCurrent, FALSE); + } + RefreshLine(oldCurrent); } @@ -3362,12 +3910,22 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) // don't do anything if this item is not focused if ( item == m_current ) { - OnUnfocusLine( m_current ); - m_current = (size_t)-1; + ResetCurrent(); + + if ( IsSingleSel() ) + { + // 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); + } + + RefreshLine( oldCurrent ); } } } + // do we need to change the selection state? if ( stateMask & wxLIST_STATE_SELECTED ) { bool on = (state & wxLIST_STATE_SELECTED) != 0; @@ -3380,9 +3938,7 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) // single sel mode if ( m_current != item ) { - OnUnfocusLine( m_current ); - m_current = item; - OnFocusLine( m_current ); + ChangeCurrent(item); if ( oldCurrent != (size_t)-1 ) { @@ -3406,7 +3962,7 @@ void wxListMainWindow::SetItemState( long litem, long state, long stateMask ) } } -int wxListMainWindow::GetItemState( long item, long stateMask ) +int wxListMainWindow::GetItemState( long item, long stateMask ) const { wxCHECK_MSG( item >= 0 && (size_t)item < GetItemCount(), 0, _T("invalid list ctrl item index in GetItemState()") ); @@ -3428,7 +3984,7 @@ int wxListMainWindow::GetItemState( long item, long stateMask ) return ret; } -void wxListMainWindow::GetItem( wxListItem &item ) +void wxListMainWindow::GetItem( wxListItem &item ) const { wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId < GetItemCount(), _T("invalid item index in GetItem") ); @@ -3451,10 +4007,13 @@ void wxListMainWindow::SetItemCount(long count) m_selStore.SetItemCount(count); m_countVirt = count; - Refresh(); + ResetVisibleLinesRange(); + + // scrollbars must be reset + m_dirty = TRUE; } -int wxListMainWindow::GetSelectedItemCount() +int wxListMainWindow::GetSelectedItemCount() const { // deal with the quick case first if ( IsSingleSel() ) @@ -3483,7 +4042,7 @@ int wxListMainWindow::GetSelectedItemCount() // item position/size // ---------------------------------------------------------------------------- -void wxListMainWindow::GetItemRect( long index, wxRect &rect ) +void wxListMainWindow::GetItemRect( long index, wxRect &rect ) const { wxCHECK_RET( index >= 0 && (size_t)index < GetItemCount(), _T("invalid index in GetItemRect") ); @@ -3493,7 +4052,7 @@ void wxListMainWindow::GetItemRect( long index, wxRect &rect ) CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y); } -bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) +bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) const { wxRect rect; GetItemRect(item, rect); @@ -3508,11 +4067,8 @@ bool wxListMainWindow::GetItemPosition(long item, wxPoint& pos) // geometry calculation // ---------------------------------------------------------------------------- -void wxListMainWindow::RecalculatePositions() +void wxListMainWindow::RecalculatePositions(bool noRefresh) { - if ( IsEmpty() ) - return; - wxClientDC dc( this ); dc.SetFont( GetFont() ); @@ -3524,9 +4080,25 @@ void wxListMainWindow::RecalculatePositions() else iconSpacing = 0; + // Note that we do not call GetClientSize() here but + // GetSize() and substract 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 + // any scrollbars, we use GetSize() instead. Another + // solution would be to call SetScrollbars() here to + // remove the scrollbars and call GetClientSize() then, + // but this might result in flicker and - worse - will + // reset the scrollbars to 0 which is not good at all + // if you resize a dialog/window, but don't want to + // reset the window scrolling. RR. + // Furthermore, we actually do NOT subtract the border + // width as 2 pixels is just the extra space which we + // need around the actual content in the window. Other- + // wise the text would e.g. touch the upper border. RR. int clientWidth, clientHeight; - GetClientSize( &clientWidth, &clientHeight ); + GetSize( &clientWidth, &clientHeight ); if ( HasFlag(wxLC_REPORT) ) { @@ -3544,23 +4116,11 @@ void wxListMainWindow::RecalculatePositions() ResetVisibleLinesRange(); SetScrollbars( m_xScroll, m_yScroll, - (GetHeaderWidth() + m_xScroll - 1)/m_xScroll, + GetHeaderWidth() / m_xScroll, (entireHeight + m_yScroll - 1)/m_yScroll, GetScrollPos(wxHORIZONTAL), GetScrollPos(wxVERTICAL), TRUE ); - - // FIXME: wxGTK::wxScrolledWindow doesn't have SetTargetRect() -#if !defined(__WXGTK__) || defined(__WXUNIVERSAL__) - // we must have an integer number of lines on screen and so we fit - // the real control size to the line height - wxRect rect; - rect.x = 0; - rect.y = LINE_SPACING; - rect.width = clientWidth; - rect.height = ((clientHeight - LINE_SPACING) / lineHeight)*lineHeight; - SetTargetRect(rect); -#endif } else // !report { @@ -3568,13 +4128,26 @@ void wxListMainWindow::RecalculatePositions() // fit into the window, we recalculate after subtracting an // approximated 15 pt for the horizontal scrollbar - clientHeight -= 4; // sunken frame - int entireWidth = 0; for (int tries = 0; tries < 2; tries++) { - entireWidth = 0; + // 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; + } + + // Start at 2,2 so the text does not touch the border int x = 2; int y = 2; int maxWidth = 0; @@ -3587,7 +4160,7 @@ void wxListMainWindow::RecalculatePositions() currentlyVisibleLines++; wxListLineData *line = GetLine(i); line->CalculateSize( &dc, iconSpacing ); - line->SetPosition( x, y, clientWidth, iconSpacing ); + line->SetPosition( x, y, clientWidth, iconSpacing ); // Why clientWidth? (FIXME) wxSize sizeLine = GetLineSize(i); @@ -3598,8 +4171,8 @@ void wxListMainWindow::RecalculatePositions() if (currentlyVisibleLines > m_linesPerPage) m_linesPerPage = currentlyVisibleLines; - // assume that the size of the next one is the same... (FIXME) - if ( y + sizeLine.y - 6 >= clientHeight ) + // Assume that the size of the next one is the same... (FIXME) + if ( y + sizeLine.y >= clientHeight ) { currentlyVisibleLines = 0; y = 2; @@ -3607,17 +4180,21 @@ void wxListMainWindow::RecalculatePositions() entireWidth += maxWidth+6; maxWidth = 0; } + + // We have reached the last item. if ( i == count - 1 ) entireWidth += maxWidth; - if ((tries == 0) && (entireWidth > clientWidth)) + + if ( (tries == 0) && (entireWidth+SCROLL_UNIT_X > clientWidth) ) { - clientHeight -= 15; // scrollbar height + clientHeight -= 15; // We guess the scrollbar height. (FIXME) m_linesPerPage = 0; currentlyVisibleLines = 0; break; } + if ( i == count - 1 ) - tries = 1; // everything fits, no second try required + tries = 1; // Everything fits, no second try required. } } @@ -3625,10 +4202,13 @@ void wxListMainWindow::RecalculatePositions() SetScrollbars( m_xScroll, m_yScroll, (entireWidth+SCROLL_UNIT_X) / m_xScroll, 0, scroll_pos, 0, TRUE ); } - // FIXME: why should we call it from here? - UpdateCurrent(); + if ( !noRefresh ) + { + // FIXME: why should we call it from here? + UpdateCurrent(); - RefreshAll(); + RefreshAll(); + } } void wxListMainWindow::RefreshAll() @@ -3637,7 +4217,7 @@ void wxListMainWindow::RefreshAll() Refresh(); wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin; - if ( headerWin ) + if ( headerWin && headerWin->m_dirty ) { headerWin->m_dirty = FALSE; headerWin->Refresh(); @@ -3648,18 +4228,13 @@ void wxListMainWindow::UpdateCurrent() { if ( !HasCurrent() && !IsEmpty() ) { - m_current = 0; - } - - if ( m_current != (size_t)-1 ) - { - OnFocusLine( m_current ); + ChangeCurrent(0); } } long wxListMainWindow::GetNextItem( long item, int WXUNUSED(geometry), - int state ) + int state ) const { long ret = item, max = GetItemCount(); @@ -3709,26 +4284,26 @@ void wxListMainWindow::DeleteItem( long lindex ) size_t index = (size_t)lindex; - m_dirty = TRUE; - - // select the next item when the selected one is deleted - if ( m_current == index ) + // we don't need to adjust the index for the previous items + if ( HasCurrent() && m_current >= index ) { - // the last valid index after deleting the item will be count-2 - if ( m_current == count - 1 ) + // if the current item is being deleted, we want the next one to + // become selected - unless there is no next one - so don't adjust + // m_current in this case + if ( m_current != index || m_current == count - 1 ) { m_current--; } } - SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); + if ( InReportView() ) + { + ResetVisibleLinesRange(); + } if ( IsVirtual() ) { - if ( m_lineTo == --m_countVirt ) - { - m_lineTo--; - } + m_countVirt--; m_selStore.OnItemDelete(index); } @@ -3737,7 +4312,12 @@ void wxListMainWindow::DeleteItem( long lindex ) m_lines.RemoveAt( index ); } - RefreshLines(index, GetItemCount() - 1); + // we need to refresh the (vert) scrollbar as the number of items changed + m_dirty = TRUE; + + SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM ); + + RefreshAfter(index); } void wxListMainWindow::DeleteColumn( int col ) @@ -3748,9 +4328,12 @@ void wxListMainWindow::DeleteColumn( int col ) m_dirty = TRUE; m_columns.DeleteNode( node ); + + // invalidate it as it has to be recalculated + m_headerWidth = 0; } -void wxListMainWindow::DeleteAllItems() +void wxListMainWindow::DoDeleteAllItems() { if ( IsEmpty() ) { @@ -3758,8 +4341,6 @@ void wxListMainWindow::DeleteAllItems() return; } - m_dirty = TRUE; - ResetCurrent(); // to make the deletion of all items faster, we don't send the @@ -3775,19 +4356,29 @@ void wxListMainWindow::DeleteAllItems() { m_countVirt = 0; + m_selStore.Clear(); + } + + if ( InReportView() ) + { ResetVisibleLinesRange(); } m_lines.Clear(); +} - m_selStore.Clear(); +void wxListMainWindow::DeleteAllItems() +{ + DoDeleteAllItems(); + + RecalculatePositions(); } void wxListMainWindow::DeleteEverything() { - DeleteAllItems(); - m_columns.Clear(); + + DeleteAllItems(); } // ---------------------------------------------------------------------------- @@ -3800,14 +4391,13 @@ void wxListMainWindow::EnsureVisible( long index ) _T("invalid index in EnsureVisible") ); // We have to call this here because the label in question might just have - // been added and no screen update taken place. - if (m_dirty) - wxSafeYield(); + // been added and its position is not known yet + if ( m_dirty ) + { + RecalculatePositions(TRUE /* no refresh */); + } - size_t oldCurrent = m_current; - m_current = (size_t)index; - MoveToFocus(); - m_current = oldCurrent; + MoveToItem((size_t)index); } long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(partial) ) @@ -3851,18 +4441,22 @@ long wxListMainWindow::HitTest( int x, int y, int &flags ) { CalcUnscrolledPosition( x, y, &x, &y ); + size_t count = GetItemCount(); + if ( HasFlag(wxLC_REPORT) ) { size_t current = y / GetLineHeight(); - flags = HitTestLine(current, x, y); - if ( flags ) - return current; + if ( current < count ) + { + flags = HitTestLine(current, x, y); + if ( flags ) + return current; + } } else // !report { // TODO: optimize it too! this is less simple than for report view but // enumerating all items is still not a way to do it!! - size_t count = GetItemCount(); for ( size_t current = 0; current < count; current++ ) { flags = HitTestLine(current, x, y); @@ -3892,7 +4486,10 @@ void wxListMainWindow::InsertItem( wxListItem &item ) int mode = 0; if ( HasFlag(wxLC_REPORT) ) + { mode = wxLC_REPORT; + ResetVisibleLinesRange(); + } else if ( HasFlag(wxLC_LIST) ) mode = wxLC_LIST; else if ( HasFlag(wxLC_ICON) ) @@ -3910,6 +4507,7 @@ void wxListMainWindow::InsertItem( wxListItem &item ) m_lines.Insert( line, id ); + m_dirty = TRUE; RefreshLines(id, GetItemCount() - 1); } @@ -3930,6 +4528,9 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item ) { m_columns.Append( column ); } + + // invalidate it as it has to be recalculated + m_headerWidth = 0; } } @@ -3979,13 +4580,11 @@ void wxListMainWindow::OnScroll(wxScrollWinEvent& event) if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() ) { - wxListCtrl* lc = GetListCtrl(); + wxGenericListCtrl* lc = GetListCtrl(); wxCHECK_RET( lc, _T("no listctrl window?") ); - lc->m_headerWin->Refresh() ; -#ifdef __WXMAC__ - lc->m_headerWin->MacUpdateImmediately() ; -#endif + lc->m_headerWin->Refresh(); + lc->m_headerWin->Update(); } } @@ -4006,17 +4605,32 @@ void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to) if ( m_lineFrom == (size_t)-1 ) { - m_lineFrom = GetScrollPos(wxVERTICAL); - size_t count = GetItemCount(); + if ( count ) + { + m_lineFrom = GetScrollPos(wxVERTICAL); - wxASSERT_MSG( m_lineFrom < count, _T("invalid scroll position?") ); + // this may happen if SetScrollbars() hadn't been called yet + if ( m_lineFrom >= count ) + m_lineFrom = count - 1; - m_lineTo = m_lineFrom + m_linesPerPage - 1; - if ( m_lineTo >= count ) - m_lineTo = count - 1; + // we redraw one extra line but this is needed to make the redrawing + // logic work when there is a fractional number of lines on screen + m_lineTo = m_lineFrom + m_linesPerPage; + if ( m_lineTo >= count ) + m_lineTo = count - 1; + } + else // empty control + { + m_lineFrom = 0; + m_lineTo = (size_t)-1; + } } + wxASSERT_MSG( IsEmpty() || + (m_lineFrom <= m_lineTo && m_lineTo < GetItemCount()), + _T("GetVisibleLinesRange() returns incorrect result") ); + if ( from ) *from = m_lineFrom; if ( to ) @@ -4024,114 +4638,21 @@ void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to) } // ------------------------------------------------------------------------------------- -// wxListItem -// ------------------------------------------------------------------------------------- - -IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject) - -wxListItem::wxListItem() -{ - m_mask = 0; - m_itemId = 0; - m_col = 0; - m_state = 0; - m_stateMask = 0; - m_image = 0; - m_data = 0; - m_format = wxLIST_FORMAT_CENTRE; - m_width = 0; - - m_attr = NULL; -} - -void wxListItem::Clear() -{ - m_mask = 0; - m_itemId = 0; - m_col = 0; - m_state = 0; - m_stateMask = 0; - m_image = 0; - m_data = 0; - m_format = wxLIST_FORMAT_CENTRE; - m_width = 0; - m_text = _T(""); - - ClearAttributes(); -} - -void wxListItem::ClearAttributes() -{ - if (m_attr) - { - delete m_attr; - m_attr = NULL; - } -} - -// ------------------------------------------------------------------------------------- -// wxListEvent -// ------------------------------------------------------------------------------------- - -IMPLEMENT_DYNAMIC_CLASS(wxListEvent, wxNotifyEvent) - -wxListEvent::wxListEvent( wxEventType commandType, int id ) - : wxNotifyEvent( commandType, id ) -{ - m_code = 0; - m_itemIndex = 0; - m_oldItemIndex = 0; - m_col = 0; - m_cancelled = FALSE; - m_pointDrag.x = 0; - m_pointDrag.y = 0; -} - -void wxListEvent::CopyObject(wxObject& object_dest) const -{ - wxListEvent *obj = (wxListEvent *)&object_dest; - - wxNotifyEvent::CopyObject(object_dest); - - obj->m_code = m_code; - obj->m_itemIndex = m_itemIndex; - obj->m_oldItemIndex = m_oldItemIndex; - obj->m_col = m_col; - obj->m_cancelled = m_cancelled; - obj->m_pointDrag = m_pointDrag; - obj->m_item.m_mask = m_item.m_mask; - obj->m_item.m_itemId = m_item.m_itemId; - obj->m_item.m_col = m_item.m_col; - obj->m_item.m_state = m_item.m_state; - obj->m_item.m_stateMask = m_item.m_stateMask; - obj->m_item.m_text = m_item.m_text; - obj->m_item.m_image = m_item.m_image; - obj->m_item.m_data = m_item.m_data; - obj->m_item.m_format = m_item.m_format; - obj->m_item.m_width = m_item.m_width; - - if ( m_item.HasAttributes() ) - { - obj->m_item.SetTextColour(m_item.GetTextColour()); - } -} - -// ------------------------------------------------------------------------------------- -// wxListCtrl +// wxGenericListCtrl // ------------------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl) -BEGIN_EVENT_TABLE(wxListCtrl,wxControl) - EVT_SIZE(wxListCtrl::OnSize) - EVT_IDLE(wxListCtrl::OnIdle) +BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl) + EVT_SIZE(wxGenericListCtrl::OnSize) + EVT_IDLE(wxGenericListCtrl::OnIdle) END_EVENT_TABLE() -wxListCtrl::wxListCtrl() +wxGenericListCtrl::wxGenericListCtrl() { - m_imageListNormal = (wxImageList *) NULL; - m_imageListSmall = (wxImageList *) NULL; - m_imageListState = (wxImageList *) NULL; + m_imageListNormal = (wxImageListType *) NULL; + m_imageListSmall = (wxImageListType *) NULL; + m_imageListState = (wxImageListType *) NULL; m_ownsImageListNormal = m_ownsImageListSmall = @@ -4141,11 +4662,8 @@ wxListCtrl::wxListCtrl() m_headerWin = (wxListHeaderWindow*) NULL; } -wxListCtrl::~wxListCtrl() +wxGenericListCtrl::~wxGenericListCtrl() { - if ( m_mainWin ) - m_mainWin->ResetCurrent(); - if (m_ownsImageListNormal) delete m_imageListNormal; if (m_ownsImageListSmall) @@ -4154,7 +4672,7 @@ wxListCtrl::~wxListCtrl() delete m_imageListState; } -void wxListCtrl::CreateHeaderWindow() +void wxGenericListCtrl::CreateHeaderWindow() { m_headerWin = new wxListHeaderWindow ( @@ -4165,7 +4683,7 @@ void wxListCtrl::CreateHeaderWindow() ); } -bool wxListCtrl::Create(wxWindow *parent, +bool wxGenericListCtrl::Create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, @@ -4175,7 +4693,7 @@ bool wxListCtrl::Create(wxWindow *parent, { m_imageListNormal = m_imageListSmall = - m_imageListState = (wxImageList *) NULL; + m_imageListState = (wxImageListType *) NULL; m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = FALSE; @@ -4210,7 +4728,7 @@ bool wxListCtrl::Create(wxWindow *parent, return TRUE; } -void wxListCtrl::SetSingleStyle( long style, bool add ) +void wxGenericListCtrl::SetSingleStyle( long style, bool add ) { wxASSERT_MSG( !(style & wxLC_VIRTUAL), _T("wxLC_VIRTUAL can't be [un]set") ); @@ -4239,89 +4757,88 @@ void wxListCtrl::SetSingleStyle( long style, bool add ) SetWindowStyleFlag( flag ); } -void wxListCtrl::SetWindowStyleFlag( long flag ) +void wxGenericListCtrl::SetWindowStyleFlag( long flag ) { if (m_mainWin) { m_mainWin->DeleteEverything(); - int width = 0; - int height = 0; - GetClientSize( &width, &height ); + // has the header visibility changed? + bool hasHeader = HasFlag(wxLC_REPORT) && !HasFlag(wxLC_NO_HEADER), + willHaveHeader = (flag & wxLC_REPORT) && !(flag & wxLC_NO_HEADER); - if (flag & wxLC_REPORT) + if ( hasHeader != willHaveHeader ) { - if (!HasFlag(wxLC_REPORT)) + // toggle it + if ( hasHeader ) + { + if ( m_headerWin ) + { + // don't delete, just hide, as we can reuse it later + m_headerWin->Show(FALSE); + } + //else: nothing to do + } + else // must show header { if (!m_headerWin) { CreateHeaderWindow(); - - if (HasFlag(wxLC_NO_HEADER)) - m_headerWin->Show( FALSE ); } - else + else // already have it, just show { - if (flag & wxLC_NO_HEADER) - m_headerWin->Show( FALSE ); - else - m_headerWin->Show( TRUE ); + m_headerWin->Show( TRUE ); } } - } - else // !report - { - if ( m_mainWin->HasHeader() ) - { - m_headerWin->Show( FALSE ); - } + + ResizeReportView(willHaveHeader); } } wxWindow::SetWindowStyleFlag( flag ); } -bool wxListCtrl::GetColumn(int col, wxListItem &item) const +bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const { m_mainWin->GetColumn( col, item ); return TRUE; } -bool wxListCtrl::SetColumn( int col, wxListItem& item ) +bool wxGenericListCtrl::SetColumn( int col, wxListItem& item ) { m_mainWin->SetColumn( col, item ); return TRUE; } -int wxListCtrl::GetColumnWidth( int col ) const +int wxGenericListCtrl::GetColumnWidth( int col ) const { return m_mainWin->GetColumnWidth( col ); } -bool wxListCtrl::SetColumnWidth( int col, int width ) +bool wxGenericListCtrl::SetColumnWidth( int col, int width ) { m_mainWin->SetColumnWidth( col, width ); return TRUE; } -int wxListCtrl::GetCountPerPage() const +int wxGenericListCtrl::GetCountPerPage() const { return m_mainWin->GetCountPerPage(); // different from Windows ? } -bool wxListCtrl::GetItem( wxListItem &info ) const +bool wxGenericListCtrl::GetItem( wxListItem &info ) const { m_mainWin->GetItem( info ); return TRUE; } -bool wxListCtrl::SetItem( wxListItem &info ) +bool wxGenericListCtrl::SetItem( wxListItem &info ) { m_mainWin->SetItem( info ); return TRUE; } -long wxListCtrl::SetItem( long index, int col, const wxString& label, int imageId ) +long wxGenericListCtrl::SetItem( long index, int col, const wxString& label, int imageId ) { wxListItem info; info.m_text = label; @@ -4337,18 +4854,18 @@ long wxListCtrl::SetItem( long index, int col, const wxString& label, int imageI return TRUE; } -int wxListCtrl::GetItemState( long item, long stateMask ) const +int wxGenericListCtrl::GetItemState( long item, long stateMask ) const { return m_mainWin->GetItemState( item, stateMask ); } -bool wxListCtrl::SetItemState( long item, long state, long stateMask ) +bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask ) { m_mainWin->SetItemState( item, state, stateMask ); return TRUE; } -bool wxListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) ) +bool wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) ) { wxListItem info; info.m_image = image; @@ -4358,24 +4875,17 @@ bool wxListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) ) return TRUE; } -wxString wxListCtrl::GetItemText( long item ) const +wxString wxGenericListCtrl::GetItemText( long item ) const { - wxListItem info; - info.m_itemId = item; - m_mainWin->GetItem( info ); - return info.m_text; + return m_mainWin->GetItemText(item); } -void wxListCtrl::SetItemText( long item, const wxString &str ) +void wxGenericListCtrl::SetItemText( long item, const wxString& str ) { - wxListItem info; - info.m_mask = wxLIST_MASK_TEXT; - info.m_itemId = item; - info.m_text = str; - m_mainWin->SetItem( info ); + m_mainWin->SetItemText(item, str); } -long wxListCtrl::GetItemData( long item ) const +long wxGenericListCtrl::GetItemData( long item ) const { wxListItem info; info.m_itemId = item; @@ -4383,7 +4893,7 @@ long wxListCtrl::GetItemData( long item ) const return info.m_data; } -bool wxListCtrl::SetItemData( long item, long data ) +bool wxGenericListCtrl::SetItemData( long item, long data ) { wxListItem info; info.m_mask = wxLIST_MASK_DATA; @@ -4393,69 +4903,103 @@ bool wxListCtrl::SetItemData( long item, long data ) return TRUE; } -bool wxListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const +bool wxGenericListCtrl::GetItemRect( long item, wxRect &rect, int WXUNUSED(code) ) const { m_mainWin->GetItemRect( item, rect ); return TRUE; } -bool wxListCtrl::GetItemPosition( long item, wxPoint& pos ) const +bool wxGenericListCtrl::GetItemPosition( long item, wxPoint& pos ) const { m_mainWin->GetItemPosition( item, pos ); return TRUE; } -bool wxListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) ) +bool wxGenericListCtrl::SetItemPosition( long WXUNUSED(item), const wxPoint& WXUNUSED(pos) ) { return 0; } -int wxListCtrl::GetItemCount() const +int wxGenericListCtrl::GetItemCount() const { return m_mainWin->GetItemCount(); } -int wxListCtrl::GetColumnCount() const +int wxGenericListCtrl::GetColumnCount() const { return m_mainWin->GetColumnCount(); } -void wxListCtrl::SetItemSpacing( int spacing, bool isSmall ) +void wxGenericListCtrl::SetItemSpacing( int spacing, bool isSmall ) { m_mainWin->SetItemSpacing( spacing, isSmall ); } -int wxListCtrl::GetItemSpacing( bool isSmall ) const +int wxGenericListCtrl::GetItemSpacing( bool isSmall ) const { return m_mainWin->GetItemSpacing( isSmall ); } -int wxListCtrl::GetSelectedItemCount() const +void wxGenericListCtrl::SetItemTextColour( long item, const wxColour &col ) +{ + wxListItem info; + info.m_itemId = item; + info.SetTextColour( col ); + m_mainWin->SetItem( info ); +} + +wxColour wxGenericListCtrl::GetItemTextColour( long item ) const +{ + wxListItem info; + info.m_itemId = item; + m_mainWin->GetItem( info ); + return info.GetTextColour(); +} + +void wxGenericListCtrl::SetItemBackgroundColour( long item, const wxColour &col ) +{ + wxListItem info; + info.m_itemId = item; + info.SetBackgroundColour( col ); + m_mainWin->SetItem( info ); +} + +wxColour wxGenericListCtrl::GetItemBackgroundColour( long item ) const +{ + wxListItem info; + info.m_itemId = item; + m_mainWin->GetItem( info ); + return info.GetBackgroundColour(); +} + +int wxGenericListCtrl::GetSelectedItemCount() const { return m_mainWin->GetSelectedItemCount(); } -wxColour wxListCtrl::GetTextColour() const +wxColour wxGenericListCtrl::GetTextColour() const { return GetForegroundColour(); } -void wxListCtrl::SetTextColour(const wxColour& col) +void wxGenericListCtrl::SetTextColour(const wxColour& col) { SetForegroundColour(col); } -long wxListCtrl::GetTopItem() const +long wxGenericListCtrl::GetTopItem() const { - return 0; + size_t top; + m_mainWin->GetVisibleLinesRange(&top, NULL); + return (long)top; } -long wxListCtrl::GetNextItem( long item, int geom, int state ) const +long wxGenericListCtrl::GetNextItem( long item, int geom, int state ) const { return m_mainWin->GetNextItem( item, geom, state ); } -wxImageList *wxListCtrl::GetImageList(int which) const +wxImageListType *wxGenericListCtrl::GetImageList(int which) const { if (which == wxIMAGE_LIST_NORMAL) { @@ -4469,10 +5013,10 @@ wxImageList *wxListCtrl::GetImageList(int which) const { return m_imageListState; } - return (wxImageList *) NULL; + return (wxImageListType *) NULL; } -void wxListCtrl::SetImageList( wxImageList *imageList, int which ) +void wxGenericListCtrl::SetImageList( wxImageListType *imageList, int which ) { if ( which == wxIMAGE_LIST_NORMAL ) { @@ -4496,7 +5040,7 @@ void wxListCtrl::SetImageList( wxImageList *imageList, int which ) m_mainWin->SetImageList( imageList, which ); } -void wxListCtrl::AssignImageList(wxImageList *imageList, int which) +void wxGenericListCtrl::AssignImageList(wxImageListType *imageList, int which) { SetImageList(imageList, which); if ( which == wxIMAGE_LIST_NORMAL ) @@ -4507,82 +5051,89 @@ void wxListCtrl::AssignImageList(wxImageList *imageList, int which) m_ownsImageListState = TRUE; } -bool wxListCtrl::Arrange( int WXUNUSED(flag) ) +bool wxGenericListCtrl::Arrange( int WXUNUSED(flag) ) { return 0; } -bool wxListCtrl::DeleteItem( long item ) +bool wxGenericListCtrl::DeleteItem( long item ) { m_mainWin->DeleteItem( item ); return TRUE; } -bool wxListCtrl::DeleteAllItems() +bool wxGenericListCtrl::DeleteAllItems() { m_mainWin->DeleteAllItems(); return TRUE; } -bool wxListCtrl::DeleteAllColumns() +bool wxGenericListCtrl::DeleteAllColumns() { size_t count = m_mainWin->m_columns.GetCount(); for ( size_t n = 0; n < count; n++ ) - DeleteColumn(n); + DeleteColumn(0); return TRUE; } -void wxListCtrl::ClearAll() +void wxGenericListCtrl::ClearAll() { m_mainWin->DeleteEverything(); } -bool wxListCtrl::DeleteColumn( int col ) +bool wxGenericListCtrl::DeleteColumn( int col ) { m_mainWin->DeleteColumn( col ); + + // if we don't have the header any longer, we need to relayout the window + if ( !GetColumnCount() ) + { + ResizeReportView(FALSE /* no header */); + } + return TRUE; } -void wxListCtrl::Edit( long item ) +void wxGenericListCtrl::Edit( long item ) { m_mainWin->EditLabel( item ); } -bool wxListCtrl::EnsureVisible( long item ) +bool wxGenericListCtrl::EnsureVisible( long item ) { m_mainWin->EnsureVisible( item ); return TRUE; } -long wxListCtrl::FindItem( long start, const wxString& str, bool partial ) +long wxGenericListCtrl::FindItem( long start, const wxString& str, bool partial ) { return m_mainWin->FindItem( start, str, partial ); } -long wxListCtrl::FindItem( long start, long data ) +long wxGenericListCtrl::FindItem( long start, long data ) { return m_mainWin->FindItem( start, data ); } -long wxListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt), +long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt), int WXUNUSED(direction)) { return 0; } -long wxListCtrl::HitTest( const wxPoint &point, int &flags ) +long wxGenericListCtrl::HitTest( const wxPoint &point, int &flags ) { return m_mainWin->HitTest( (int)point.x, (int)point.y, flags ); } -long wxListCtrl::InsertItem( wxListItem& info ) +long wxGenericListCtrl::InsertItem( wxListItem& info ) { m_mainWin->InsertItem( info ); return info.m_itemId; } -long wxListCtrl::InsertItem( long index, const wxString &label ) +long wxGenericListCtrl::InsertItem( long index, const wxString &label ) { wxListItem info; info.m_text = label; @@ -4591,7 +5142,7 @@ long wxListCtrl::InsertItem( long index, const wxString &label ) return InsertItem( info ); } -long wxListCtrl::InsertItem( long index, int imageIndex ) +long wxGenericListCtrl::InsertItem( long index, int imageIndex ) { wxListItem info; info.m_mask = wxLIST_MASK_IMAGE; @@ -4600,7 +5151,7 @@ long wxListCtrl::InsertItem( long index, int imageIndex ) return InsertItem( info ); } -long wxListCtrl::InsertItem( long index, const wxString &label, int imageIndex ) +long wxGenericListCtrl::InsertItem( long index, const wxString &label, int imageIndex ) { wxListItem info; info.m_text = label; @@ -4610,16 +5161,25 @@ long wxListCtrl::InsertItem( long index, const wxString &label, int imageIndex ) return InsertItem( info ); } -long wxListCtrl::InsertColumn( long col, wxListItem &item ) +long wxGenericListCtrl::InsertColumn( long col, wxListItem &item ) { - wxASSERT( m_headerWin ); + wxCHECK_MSG( m_headerWin, -1, _T("can't add column in non report mode") ); + m_mainWin->InsertColumn( col, item ); + + // if we hadn't had header before and have it now we need to relayout the + // window + if ( GetColumnCount() == 1 ) + { + ResizeReportView(TRUE /* have header */); + } + m_headerWin->Refresh(); return 0; } -long wxListCtrl::InsertColumn( long col, const wxString &heading, +long wxGenericListCtrl::InsertColumn( long col, const wxString &heading, int format, int width ) { wxListItem item; @@ -4635,7 +5195,7 @@ long wxListCtrl::InsertColumn( long col, const wxString &heading, return InsertColumn( col, item ); } -bool wxListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) ) +bool wxGenericListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) ) { return 0; } @@ -4650,7 +5210,7 @@ bool wxListCtrl::ScrollList( int WXUNUSED(dx), int WXUNUSED(dy) ) // or zero if the two items are equivalent. // data is arbitrary data to be passed to the sort function. -bool wxListCtrl::SortItems( wxListCtrlCompare fn, long data ) +bool wxGenericListCtrl::SortItems( wxListCtrlCompare fn, long data ) { m_mainWin->SortItems( fn, data ); return TRUE; @@ -4660,15 +5220,22 @@ bool wxListCtrl::SortItems( wxListCtrlCompare fn, long data ) // event handlers // ---------------------------------------------------------------------------- -void wxListCtrl::OnSize(wxSizeEvent& event) +void wxGenericListCtrl::OnSize(wxSizeEvent& WXUNUSED(event)) { if ( !m_mainWin ) return; + ResizeReportView(m_mainWin->HasHeader()); + + m_mainWin->RecalculatePositions(); +} + +void wxGenericListCtrl::ResizeReportView(bool showHeader) +{ int cw, ch; GetClientSize( &cw, &ch ); - if ( m_mainWin->HasHeader() ) + if ( showHeader ) { m_headerWin->SetSize( 0, 0, cw, HEADER_HEIGHT ); m_mainWin->SetSize( 0, HEADER_HEIGHT + 1, cw, ch - HEADER_HEIGHT - 1 ); @@ -4677,11 +5244,9 @@ void wxListCtrl::OnSize(wxSizeEvent& event) { m_mainWin->SetSize( 0, 0, cw, ch ); } - - m_mainWin->RecalculatePositions(); } -void wxListCtrl::OnIdle( wxIdleEvent & event ) +void wxGenericListCtrl::OnIdle( wxIdleEvent & event ) { event.Skip(); @@ -4696,7 +5261,7 @@ void wxListCtrl::OnIdle( wxIdleEvent & event ) // font/colours // ---------------------------------------------------------------------------- -bool wxListCtrl::SetBackgroundColour( const wxColour &colour ) +bool wxGenericListCtrl::SetBackgroundColour( const wxColour &colour ) { if (m_mainWin) { @@ -4707,7 +5272,7 @@ bool wxListCtrl::SetBackgroundColour( const wxColour &colour ) return TRUE; } -bool wxListCtrl::SetForegroundColour( const wxColour &colour ) +bool wxGenericListCtrl::SetForegroundColour( const wxColour &colour ) { if ( !wxWindow::SetForegroundColour( colour ) ) return FALSE; @@ -4726,7 +5291,7 @@ bool wxListCtrl::SetForegroundColour( const wxColour &colour ) return TRUE; } -bool wxListCtrl::SetFont( const wxFont &font ) +bool wxGenericListCtrl::SetFont( const wxFont &font ) { if ( !wxWindow::SetFont( font ) ) return FALSE; @@ -4751,34 +5316,34 @@ bool wxListCtrl::SetFont( const wxFont &font ) #if wxUSE_DRAG_AND_DROP -void wxListCtrl::SetDropTarget( wxDropTarget *dropTarget ) +void wxGenericListCtrl::SetDropTarget( wxDropTarget *dropTarget ) { m_mainWin->SetDropTarget( dropTarget ); } -wxDropTarget *wxListCtrl::GetDropTarget() const +wxDropTarget *wxGenericListCtrl::GetDropTarget() const { return m_mainWin->GetDropTarget(); } #endif // wxUSE_DRAG_AND_DROP -bool wxListCtrl::SetCursor( const wxCursor &cursor ) +bool wxGenericListCtrl::SetCursor( const wxCursor &cursor ) { return m_mainWin ? m_mainWin->wxWindow::SetCursor(cursor) : FALSE; } -wxColour wxListCtrl::GetBackgroundColour() const +wxColour wxGenericListCtrl::GetBackgroundColour() const { return m_mainWin ? m_mainWin->GetBackgroundColour() : wxColour(); } -wxColour wxListCtrl::GetForegroundColour() const +wxColour wxGenericListCtrl::GetForegroundColour() const { return m_mainWin ? m_mainWin->GetForegroundColour() : wxColour(); } -bool wxListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) +bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) { #if wxUSE_MENUS return m_mainWin->PopupMenu( menu, x, y ); @@ -4787,7 +5352,7 @@ bool wxListCtrl::DoPopupMenu( wxMenu *menu, int x, int y ) #endif // wxUSE_MENUS } -void wxListCtrl::SetFocus() +void wxGenericListCtrl::SetFocus() { /* The test in window.cpp fails as we are a composite window, so it checks against "this", but not m_mainWin. */ @@ -4799,30 +5364,58 @@ void wxListCtrl::SetFocus() // virtual list control support // ---------------------------------------------------------------------------- -wxString wxListCtrl::OnGetItemText(long item, long col) const +wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) const { // this is a pure virtual function, in fact - which is not really pure // because the controls which are not virtual don't need to implement it - wxFAIL_MSG( _T("not supposed to be called") ); + wxFAIL_MSG( _T("wxGenericListCtrl::OnGetItemText not supposed to be called") ); return wxEmptyString; } -int wxListCtrl::OnGetItemImage(long item) const +int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const { // same as above - wxFAIL_MSG( _T("not supposed to be called") ); + wxFAIL_MSG( _T("wxGenericListCtrl::OnGetItemImage not supposed to be called") ); return -1; } -void wxListCtrl::SetItemCount(long count) +wxListItemAttr *wxGenericListCtrl::OnGetItemAttr(long item) const +{ + wxASSERT_MSG( item >= 0 && item < GetItemCount(), + _T("invalid item index in OnGetItemAttr()") ); + + // no attributes by default + return NULL; +} + +void wxGenericListCtrl::SetItemCount(long count) { wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") ); m_mainWin->SetItemCount(count); } +void wxGenericListCtrl::RefreshItem(long item) +{ + m_mainWin->RefreshLine(item); +} + +void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo) +{ + m_mainWin->RefreshLines(itemFrom, itemTo); +} + +void wxGenericListCtrl::Freeze() +{ + m_mainWin->Freeze(); +} + +void wxGenericListCtrl::Thaw() +{ + m_mainWin->Thaw(); +} + #endif // wxUSE_LISTCTRL -#endif