]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
added wxIsSameDouble() which wraps double comparison in a pragmas disabling icc warni...
[wxWidgets.git] / src / generic / listctrl.cpp
index 25d1f61b83d15858c47370b288e6129aaa47b074..cd1e3dd934acc86e44cde49aa0cc49ed9a538910 100644 (file)
 // headers
 // ----------------------------------------------------------------------------
 
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-    #pragma implementation "listctrl.h"
-    #pragma implementation "listctrlbase.h"
-#endif
-
 // For compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
@@ -153,6 +148,25 @@ static const int IMAGE_MARGIN_IN_REPORT_MODE = 5;
 // private classes
 // ============================================================================
 
+//-----------------------------------------------------------------------------
+//  wxColWidthInfo (internal)
+//-----------------------------------------------------------------------------
+
+struct wxColWidthInfo
+{
+    int     nMaxWidth;
+    bool    bNeedsUpdate;   //  only set to true when an item whose
+                            //  width == nMaxWidth is removed
+
+    wxColWidthInfo(int w = 0, bool needsUpdate = false)
+    {
+        nMaxWidth = w;
+        bNeedsUpdate = needsUpdate;
+    }
+};
+
+WX_DEFINE_ARRAY_PTR(wxColWidthInfo *, ColWidthArray);
+
 //-----------------------------------------------------------------------------
 //  wxListItemData (internal)
 //-----------------------------------------------------------------------------
@@ -294,7 +308,7 @@ public:
         // the part to be highlighted
         wxRect m_rectHighlight;
 
-        // extend all our rects to be centered inside thene of given width
+        // extend all our rects to be centered inside the one of given width
         void ExtendWidth(wxCoord w)
         {
             wxASSERT_MSG( m_rectAll.width <= w,
@@ -348,7 +362,7 @@ public:
     void GetItem( int index, wxListItem &info );
 
     wxString GetText(int index) const;
-    void SetText( int index, const wxString s );
+    void SetText( int index, const wxString& s );
 
     wxListItemAttr *GetAttr() const;
     void SetAttr(wxListItemAttr *attr);
@@ -452,7 +466,7 @@ private:
 
     // generate and process the list event of the given type, return true if
     // it wasn't vetoed, i.e. if we should proceed
-    bool SendListEvent(wxEventType type, wxPoint pos);
+    bool SendListEvent(wxEventType type, const wxPoint& pos);
 
     DECLARE_DYNAMIC_CLASS(wxListHeaderWindow)
     DECLARE_EVENT_TABLE()
@@ -494,6 +508,7 @@ private:
     wxString            m_startValue;
     size_t              m_itemEdited;
     bool                m_finished;
+    bool                m_aboutToFinish;
 
     DECLARE_EVENT_TABLE()
 };
@@ -644,6 +659,7 @@ public:
     void SetItem( wxListItem &item );
     void GetItem( wxListItem &item ) const;
     void SetItemState( long item, long state, long stateMask );
+    void SetItemStateAll( long state, long stateMask );
     int GetItemState( long item, long stateMask ) const;
     void GetItemRect( long index, wxRect &rect ) const;
     wxRect GetViewRect() const;
@@ -653,6 +669,7 @@ public:
     wxString GetItemText(long item) const
     {
         wxListItem info;
+        info.m_mask = wxLIST_MASK_TEXT;
         info.m_itemId = item;
         GetItem( info );
         return info.m_text;
@@ -685,6 +702,7 @@ public:
     long HitTest( int x, int y, int &flags );
     void InsertItem( wxListItem &item );
     void InsertColumn( long col, wxListItem &item );
+    int GetItemWidthWithImage(wxListItem * item);
     void SortItems( wxListCtrlCompare fn, long data );
 
     size_t GetItemCount() const;
@@ -699,7 +717,7 @@ public:
     // send out a wxListEvent
     void SendNotify( size_t line,
                      wxEventType command,
-                     wxPoint point = wxDefaultPosition );
+                     const wxPoint& point = wxDefaultPosition );
 
     // override base class virtual to reset m_lineHeight when the font changes
     virtual bool SetFont(const wxFont& font)
@@ -766,6 +784,7 @@ public:
     bool                 m_isCreated;
     int                  m_dragCount;
     wxPoint              m_dragStart;
+    ColWidthArray        m_aColWidths;
 
     // for double click logic
     size_t m_lineLastClicked,
@@ -814,11 +833,7 @@ protected:
     // get the colour to be used for drawing the rules
     wxColour GetRuleColour() const
     {
-#ifdef __WXMAC__
-        return *wxWHITE;
-#else
         return wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT);
-#endif
     }
 
 private:
@@ -978,9 +993,19 @@ int wxListItemData::GetHeight() const
 
 void wxListItemData::GetItem( wxListItem &info ) const
 {
-    info.m_text = m_text;
-    info.m_image = m_image;
-    info.m_data = m_data;
+    long mask = info.m_mask;
+    if ( !mask )
+    {
+        // by default, get everything for backwards compatibility
+        mask = -1;
+    }
+
+    if ( mask & wxLIST_MASK_TEXT )
+        info.m_text = m_text;
+    if ( mask & wxLIST_MASK_IMAGE )
+        info.m_image = m_image;
+    if ( mask & wxLIST_MASK_DATA )
+        info.m_data = m_data;
 
     if ( m_attr )
     {
@@ -1340,7 +1365,7 @@ wxString wxListLineData::GetText(int index) const
     return s;
 }
 
-void wxListLineData::SetText( int index, const wxString s )
+void wxListLineData::SetText( int index, const wxString& s )
 {
     wxListItemDataList::compatibility_iterator node = m_items.Item( index );
     if (node)
@@ -1739,13 +1764,18 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
 
         // the width of the rect to draw: make it smaller to fit entirely
         // inside the column rect
+#ifdef __WXMAC__
+        int cw = wCol ;
+        int ch = h ;
+#else
         int cw = wCol - 2;
-
+        int ch = h-2 ;
+#endif
         wxRendererNative::Get().DrawHeaderButton
                                 (
                                     this,
                                     dc,
-                                    wxRect(x, HEADER_OFFSET_Y, cw, h - 2),
+                                    wxRect(x, HEADER_OFFSET_Y, cw, ch),
                                     m_parent->IsEnabled() ? 0
                                                           : (int)wxCONTROL_DISABLED
                                 );
@@ -1904,7 +1934,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
         // end of the current column
         int xpos = 0;
 
-        // find the column where this event occured
+        // find the column where this event occurred
         int col,
             countCol = m_owner->GetColumnCount();
         for (col = 0; col < countCol; col++)
@@ -1979,7 +2009,7 @@ void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
     m_owner->Update();
 }
 
-bool wxListHeaderWindow::SendListEvent(wxEventType type, wxPoint pos)
+bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
 {
     wxWindow *parent = GetParent();
     wxListEvent le( type, parent->GetId() );
@@ -2026,6 +2056,7 @@ wxListTextCtrl::wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit)
 {
     m_owner = owner;
     m_finished = false;
+    m_aboutToFinish = false;
 
     wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
 
@@ -2076,12 +2107,11 @@ void wxListTextCtrl::OnChar( wxKeyEvent &event )
     switch ( event.m_keyCode )
     {
         case WXK_RETURN:
+            m_aboutToFinish = true;
             // Notify the owner about the changes
             AcceptChanges();
-
             // Even if vetoed, close the control (consistent with MSW)
             Finish();
-
             break;
 
         case WXK_ESCAPE:
@@ -2119,7 +2149,7 @@ void wxListTextCtrl::OnKeyUp( wxKeyEvent &event )
 
 void wxListTextCtrl::OnKillFocus( wxFocusEvent &event )
 {
-    if ( !m_finished )
+    if ( !m_finished && !m_aboutToFinish  )
     {
         // We must finish regardless of success, otherwise we'll get
         // focus problems:
@@ -2130,7 +2160,7 @@ void wxListTextCtrl::OnKillFocus( wxFocusEvent &event )
     }
 
     // We must let the native text control handle focus, too, otherwise
-    // it could have problems with the cursor (e.g., in wxGTK):
+    // it could have problems with the cursor (e.g., in wxGTK).
     event.Skip();
 }
 
@@ -2219,9 +2249,6 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent,
                                        wxSOLID
                                     );
 
-    wxSize sz = size;
-    sz.y = 25;
-
     SetScrollbars( 0, 0, 0, 0, 0, 0 );
 
     wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
@@ -2235,6 +2262,7 @@ wxListMainWindow::~wxListMainWindow()
 {
     DoDeleteAllItems();
     WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
+    WX_CLEAR_ARRAY(m_aColWidths);
 
     delete m_highlightBrush;
     delete m_highlightUnfocusedBrush;
@@ -2766,7 +2794,7 @@ void wxListMainWindow::HighlightAll( bool on )
 
 void wxListMainWindow::SendNotify( size_t line,
                                    wxEventType command,
-                                   wxPoint point )
+                                   const wxPoint& point )
 {
     wxListEvent le( command, GetParent()->GetId() );
     le.SetEventObject( GetParent() );
@@ -2985,11 +3013,11 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
             HighlightAll( false );
             ReverseHighlight(m_lineSelectSingleOnUp);
         }
-        else if (m_lastOnSame)
+        if (m_lastOnSame)
         {
             if ((current == m_current) &&
                 (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
-                HasFlag(wxLC_EDIT_LABELS)  )
+                HasFlag(wxLC_EDIT_LABELS) )
             {
                 m_renameTimer->Start( 100, true );
             }
@@ -2999,7 +3027,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
     }
     else
     {
-        // This is neccessary , because after a DnD operation in
+        // This is necessary, because after a DnD operation in
         // from and to ourself, the up event is swallowed by the
         // DnD code. So on next non-up event (which means here and
         // now) m_lineSelectSingleOnUp should be reset.
@@ -3032,8 +3060,10 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         m_lineLastClicked = current;
 
         size_t oldCurrent = m_current;
+        bool oldWasSelected = IsHighlighted(m_current);
+
         bool cmdModifierDown = event.CmdDown();
-        if ( !(cmdModifierDown || event.ShiftDown()) )
+        if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
         {
             if( IsSingleSel() || !IsHighlighted(current) )
             {
@@ -3085,7 +3115,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         }
 
         // forceClick is only set if the previous click was on another item
-        m_lastOnSame = !forceClick && (m_current == oldCurrent);
+        m_lastOnSame = !forceClick && (m_current == oldCurrent) && oldWasSelected;
     }
 }
 
@@ -3541,34 +3571,30 @@ void wxListMainWindow::SetColumnWidth( int col, int width )
 
             int max = AUTOSIZE_COL_MARGIN;
 
-            for ( size_t i = 0; i < count; i++ )
+            //  if the cached column width isn't valid then recalculate it
+            if (m_aColWidths.Item(col)->bNeedsUpdate)
             {
-                wxListLineData *line = GetLine(i);
-                wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
-
-                wxCHECK_RET( n, _T("no subitem?") );
+                for (size_t i = 0; i < count; i++)
+                {
+                    wxListLineData *line = GetLine(i);
+                    wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
 
-                wxListItemData *item = n->GetData();
-                int current = 0;
+                    wxCHECK_RET( n, _T("no subitem?") );
 
-                if (item->HasImage())
-                {
-                    int ix, iy;
-                    GetImageSize( item->GetImage(), ix, iy );
-                    current += ix + 5;
-                }
+                    wxListItemData *itemData = n->GetData();
+                    wxListItem      item;
 
-                if (item->HasText())
-                {
-                    wxCoord w;
-                    dc.GetTextExtent( item->GetText(), &w, NULL );
-                    current += w;
+                    itemData->GetItem(item);
+                    int itemWidth = GetItemWidthWithImage(&item);
+                    if (itemWidth > max)
+                        max = itemWidth;
                 }
 
-                if (current > max)
-                    max = current;
+                m_aColWidths.Item(col)->bNeedsUpdate = false;
+                m_aColWidths.Item(col)->nMaxWidth = max;
             }
 
+            max = m_aColWidths.Item(col)->nMaxWidth;
             width = max + AUTOSIZE_COL_MARGIN;
         }
     }
@@ -3627,6 +3653,19 @@ void wxListMainWindow::SetItem( wxListItem &item )
     {
         wxListLineData *line = GetLine((size_t)id);
         line->SetItem( item.m_col, item );
+
+        // Set item state if user wants
+        if ( item.m_mask & wxLIST_MASK_STATE )
+            SetItemState( item.m_itemId, item.m_state, item.m_state );
+
+        if (InReportView())
+        {
+            //  update the Max Width Cache if needed
+            int width = GetItemWidthWithImage(&item);
+
+            if (width > m_aColWidths.Item(item.m_col)->nMaxWidth)
+                m_aColWidths.Item(item.m_col)->nMaxWidth = width;
+        }
     }
 
     // update the item on screen
@@ -3635,10 +3674,60 @@ void wxListMainWindow::SetItem( wxListItem &item )
     RefreshRect(rectItem);
 }
 
+void wxListMainWindow::SetItemStateAll(long state, long stateMask)
+{
+    if ( IsEmpty() )
+        return;
+
+    // first deal with selection
+    if ( stateMask & wxLIST_STATE_SELECTED )
+    {
+        // set/clear select state
+        if ( IsVirtual() )
+        {
+            // optimized version for virtual listctrl.
+            m_selStore.SelectRange(0, GetItemCount() - 1, state == wxLIST_STATE_SELECTED);
+            Refresh();
+        }
+        else if ( state & wxLIST_STATE_SELECTED )
+        {
+            const long count = GetItemCount();
+            for( long i = 0; i <  count; i++ )
+            {
+                SetItemState( i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
+            }
+
+        }
+        else
+        {
+            // clear for non virtual (somewhat optimized by using GetNextItem())
+            long i = -1;
+            while ( (i = GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED)) != -1 )
+            {
+                SetItemState( i, 0, wxLIST_STATE_SELECTED );
+            }
+        }
+    }
+
+    if ( HasCurrent() && (state == 0) && (stateMask & wxLIST_STATE_FOCUSED) )
+    {
+        // unfocus all: only one item can be focussed, so clearing focus for
+        // all items is simply clearing focus of the focussed item.
+        SetItemState(m_current, state, stateMask);
+    }
+    //(setting focus to all items makes no sense, so it is not handled here.)
+}
+
 void wxListMainWindow::SetItemState( long litem, long state, long stateMask )
 {
-     wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
-                  _T("invalid list ctrl item index in SetItem") );
+    if ( litem == -1 )
+    {
+        SetItemStateAll(state, stateMask);
+        return;
+    }
+
+    wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
+                 _T("invalid list ctrl item index in SetItem") );
 
     size_t oldCurrent = m_current;
     size_t item = (size_t)litem;    // safe because of the check above
@@ -3752,6 +3841,11 @@ void wxListMainWindow::GetItem( wxListItem &item ) const
 
     wxListLineData *line = GetLine((size_t)item.m_itemId);
     line->GetItem( item.m_col, item );
+
+    // Get item state if user wants it
+    if ( item.m_mask & wxLIST_MASK_STATE )
+        item.m_state = GetItemState( item.m_itemId, wxLIST_STATE_SELECTED |
+                                                 wxLIST_STATE_FOCUSED );
 }
 
 // ----------------------------------------------------------------------------
@@ -4165,6 +4259,26 @@ void wxListMainWindow::DeleteItem( long lindex )
 
     if ( InReportView() )
     {
+    //  mark the Column Max Width cache as dirty if the items in the line
+    //  we're deleting contain the Max Column Width
+        wxListLineData * const line = GetLine(index);
+        wxListItemDataList::compatibility_iterator n;
+        wxListItemData *itemData;
+        wxListItem      item;
+        int             itemWidth;
+
+        for (size_t i = 0; i < m_columns.GetCount(); i++)
+        {
+            n = line->m_items.Item( i );
+            itemData = n->GetData();
+            itemData->GetItem(item);
+
+            itemWidth = GetItemWidthWithImage(&item);
+
+            if (itemWidth >= m_aColWidths.Item(i)->nMaxWidth)
+                m_aColWidths.Item(i)->bNeedsUpdate = true;
+        }
+
         ResetVisibleLinesRange();
     }
 
@@ -4209,6 +4323,12 @@ void wxListMainWindow::DeleteColumn( int col )
         }
     }
 
+    if ( InReportView() )   //  we only cache max widths when in Report View
+    {
+        delete m_aColWidths.Item(col);
+        m_aColWidths.RemoveAt(col);
+    }
+
     // invalidate it as it has to be recalculated
     m_headerWidth = 0;
 }
@@ -4242,6 +4362,10 @@ void wxListMainWindow::DoDeleteAllItems()
     if ( InReportView() )
     {
         ResetVisibleLinesRange();
+        for (size_t i = 0; i < m_aColWidths.GetCount(); i++)
+        {
+            m_aColWidths.Item(i)->bNeedsUpdate = true;
+        }
     }
 
     m_lines.Clear();
@@ -4257,6 +4381,7 @@ void wxListMainWindow::DeleteAllItems()
 void wxListMainWindow::DeleteEverything()
 {
     WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
+    WX_CLEAR_ARRAY(m_aColWidths);
 
     DeleteAllItems();
 }
@@ -4382,42 +4507,16 @@ void wxListMainWindow::InsertItem( wxListItem &item )
 
     m_dirty = true;
 
-    #if 0
-    // this is unused variable
-    int mode = 0;
-    #endif
     if ( InReportView() )
     {
-        #if 0
-        // this is unused variable
-        mode = wxLC_REPORT;
-        #endif
         ResetVisibleLinesRange();
-    }
-    else if ( HasFlag(wxLC_LIST) )
-        #if 0
-        // this is unused variable
-        mode = wxLC_LIST;
-        #else
-        {}
-        #endif
-    else if ( HasFlag(wxLC_ICON) )
-        #if 0
-        // this is unused variable
-        mode = wxLC_ICON;
-        #else
-        {}
-        #endif
-    else if ( HasFlag(wxLC_SMALL_ICON) )
-        #if 0
-        // this is unused variable
-        mode = wxLC_ICON;  // no typo
-        #else
-        {}
-        #endif
-    else
-    {
-        wxFAIL_MSG( _T("unknown mode") );
+
+        // calculate the width of the item and adjust the max column width
+        wxColWidthInfo *pWidthInfo = m_aColWidths.Item(item.GetColumn());
+        int width = GetItemWidthWithImage(&item);
+        item.SetWidth(width);
+        if (width > pWidthInfo->nMaxWidth)
+            pWidthInfo->nMaxWidth = width;
     }
 
     wxListLineData *line = new wxListLineData(this);
@@ -4450,15 +4549,20 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item )
             item.m_width = GetTextLength( item.m_text );
 
         wxListHeaderData *column = new wxListHeaderData( item );
+        wxColWidthInfo *colWidthInfo = new wxColWidthInfo();
+
         bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
         if ( insert )
         {
-            wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
+            wxListHeaderDataList::compatibility_iterator
+                node = m_columns.Item( col );
             m_columns.Insert( node, column );
+            m_aColWidths.Insert( colWidthInfo, col );
         }
         else
         {
             m_columns.Append( column );
+            m_aColWidths.Add( colWidthInfo );
         }
 
         if ( !IsVirtual() )
@@ -4480,6 +4584,30 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item )
     }
 }
 
+int wxListMainWindow::GetItemWidthWithImage(wxListItem * item)
+{
+    int width = 0;
+    wxClientDC dc(this);
+
+    dc.SetFont( GetFont() );
+
+    if (item->GetImage() != -1)
+    {
+        int ix, iy;
+        GetImageSize( item->GetImage(), ix, iy );
+        width += ix + 5;
+    }
+
+    if (!item->GetText().empty())
+    {
+        wxCoord w;
+        dc.GetTextExtent( item->GetText(), &w, NULL );
+        width += w;
+    }
+
+    return width;
+}
+
 // ----------------------------------------------------------------------------
 // sorting
 // ----------------------------------------------------------------------------
@@ -4518,7 +4646,7 @@ void wxListMainWindow::OnScroll(wxScrollWinEvent& event)
     ResetVisibleLinesRange();
 
     // FIXME
-#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__)
+#if ( defined(__WXGTK__) || defined(__WXMAC__) ) && !defined(__WXUNIVERSAL__)
     wxScrolledWindow::OnScroll(event);
 #else
     HandleOnScroll( event );
@@ -4624,7 +4752,7 @@ void wxGenericListCtrl::CalculateAndSetHeaderHeight()
     {
 #ifdef __WXMAC__
         SInt32 h ;
-        GetThemeMetric( kThemeMetricListHeaderHeight, &h );    
+        GetThemeMetric( kThemeMetricListHeaderHeight, &h );
 #else
         // we use 'g' to get the descent, too
         int w, h, d;
@@ -4701,7 +4829,15 @@ bool wxGenericListCtrl::Create(wxWindow *parent,
     if ( InReportView() )
     {
         CreateHeaderWindow();
-
+#ifdef  __WXMAC_CARBON__
+        if (m_headerWin)
+        {
+            wxFont font ;
+            font.MacCreateThemeFont( kThemeSmallSystemFont ) ;
+            m_headerWin->SetFont( font );
+            CalculateAndSetHeaderHeight();
+        }
+#endif
         if ( HasFlag(wxLC_NO_HEADER) )
         {
             // VZ: why do we create it at all then?
@@ -4875,6 +5011,7 @@ void wxGenericListCtrl::SetItemText( long item, const wxString& str )
 wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const
 {
     wxListItem info;
+    info.m_mask = wxLIST_MASK_DATA;
     info.m_itemId = item;
     m_mainWin->GetItem( info );
     return info.m_data;
@@ -4973,6 +5110,22 @@ wxColour wxGenericListCtrl::GetItemBackgroundColour( long item ) const
     return info.GetBackgroundColour();
 }
 
+void wxGenericListCtrl::SetItemFont( long item, const wxFont &f )
+{
+    wxListItem info;
+    info.m_itemId = item;
+    info.SetFont( f );
+    m_mainWin->SetItem( info );
+}
+
+wxFont wxGenericListCtrl::GetItemFont( long item ) const
+{
+    wxListItem info;
+    info.m_itemId = item;
+    m_mainWin->GetItem( info );
+    return info.GetFont();
+}
+
 int wxGenericListCtrl::GetSelectedItemCount() const
 {
     return m_mainWin->GetSelectedItemCount();