]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
supporting promised file urls for transfer, see #14281
[wxWidgets.git] / src / generic / listctrl.cpp
index a59e9dc976cf644e118ab8e0721e3362816fcd4d..de34935ced4fdb7b61eb45d95f2e27fdd4557584 100644 (file)
 
 #include "wx/listctrl.h"
 
-#if ((!defined(__WXMSW__) && !(defined(__WXMAC__) && wxOSX_USE_CARBON)) || defined(__WXUNIVERSAL__))
-    // 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
-
 #ifndef WX_PRECOMP
     #include "wx/scrolwin.h"
     #include "wx/timer.h"
@@ -351,12 +342,23 @@ bool wxListHeaderData::IsHit( int x, int y ) const
 
 void wxListHeaderData::GetItem( wxListItem& item )
 {
-    item.m_mask = m_mask;
-    item.m_text = m_text;
-    item.m_image = m_image;
-    item.m_format = m_format;
-    item.m_width = m_width;
-    item.m_state = m_state;
+    long mask = item.m_mask;
+    if ( !mask )
+    {
+        // by default, get everything for backwards compatibility
+        mask = -1;
+    }
+
+    if ( mask & wxLIST_MASK_STATE )
+        item.m_state = m_state;
+    if ( mask & wxLIST_MASK_TEXT )
+        item.m_text = m_text;
+    if ( mask & wxLIST_MASK_IMAGE )
+        item.m_image = m_image;
+    if ( mask & wxLIST_MASK_WIDTH )
+        item.m_width = m_width;
+    if ( mask & wxLIST_MASK_FORMAT )
+        item.m_format = m_format;
 }
 
 int wxListHeaderData::GetImage() const
@@ -667,11 +669,20 @@ void wxListLineData::SetAttr(wxListItemAttr *attr)
     item->SetAttr(attr);
 }
 
-bool wxListLineData::SetAttributes(wxDC *dc,
-                                   const wxListItemAttr *attr,
-                                   bool highlighted)
+void wxListLineData::ApplyAttributes(wxDC *dc,
+                                     const wxRect& rectHL,
+                                     bool highlighted,
+                                     bool current)
 {
-    wxWindow *listctrl = m_owner->GetParent();
+    const wxListItemAttr * const attr = GetAttr();
+
+    wxWindow * const listctrl = m_owner->GetParent();
+
+    const bool hasFocus = listctrl->HasFocus()
+#if defined(__WXMAC__) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
+                && IsControlActive( (ControlRef)listctrl->GetHandle() )
+#endif
+                ;
 
     // fg colour
 
@@ -680,20 +691,19 @@ bool wxListLineData::SetAttributes(wxDC *dc,
     // arithmetics on wxColour, unfortunately)
     wxColour colText;
     if ( highlighted )
-#ifdef __WXMAC__
     {
-        if (m_owner->HasFocus()
-#if !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
-                && IsControlActive( (ControlRef)m_owner->GetHandle() )
-#endif
-        )
+#ifdef __WXMAC__
+        if ( hasFocus )
             colText = *wxWHITE;
         else
             colText = *wxBLACK;
-    }
 #else
-        colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+        if ( hasFocus )
+            colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
+        else
+            colText = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT);
 #endif
+    }
     else if ( attr && attr->HasTextColour() )
         colText = attr->GetTextColour();
     else
@@ -710,57 +720,26 @@ bool wxListLineData::SetAttributes(wxDC *dc,
 
     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(), wxBRUSHSTYLE_SOLID));
-
-        dc->SetPen( *wxTRANSPARENT_PEN );
-
-        return true;
-    }
-
-    return false;
-}
-
-void wxListLineData::Draw( wxDC *dc )
-{
-    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
-    wxCHECK_RET( node, wxT("no subitems at all??") );
-
-    bool highlighted = IsHighlighted();
-
-    wxListItemAttr *attr = GetAttr();
-
-    if ( SetAttributes(dc, attr, highlighted) )
-#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) )
+    // background
+    if ( highlighted )
     {
-        dc->DrawRectangle( m_gi->m_rectHighlight );
+        // Use the renderer method to ensure that the selected items use the
+        // native look.
+        int flags = wxCONTROL_SELECTED;
+        if ( hasFocus )
+            flags |= wxCONTROL_FOCUSED;
+        if (current)
+           flags |= wxCONTROL_CURRENT;
+        wxRendererNative::Get().
+            DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
     }
-#else
+    else if ( attr && attr->HasBackgroundColour() )
     {
-        if (highlighted)
-        {
-            int flags = wxCONTROL_SELECTED;
-            if (m_owner->HasFocus()
-#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
-                && IsControlActive( (ControlRef)m_owner->GetHandle() )
-#endif
-            )
-                flags |= wxCONTROL_FOCUSED;
-            wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, m_gi->m_rectHighlight, flags );
-
-        }
-        else
-        {
-            dc->DrawRectangle( m_gi->m_rectHighlight );
-        }
+        // Draw the background using the items custom background colour.
+        dc->SetBrush(attr->GetBackgroundColour());
+        dc->SetPen(*wxTRANSPARENT_PEN);
+        dc->DrawRectangle(rectHL);
     }
-#endif
 
     // just for debugging to better see where the items are
 #if 0
@@ -770,6 +749,14 @@ void wxListLineData::Draw( wxDC *dc )
     dc->SetPen(*wxGREEN_PEN);
     dc->DrawRectangle( m_gi->m_rectIcon );
 #endif
+}
+
+void wxListLineData::Draw(wxDC *dc, bool current)
+{
+    wxListItemDataList::compatibility_iterator node = m_items.GetFirst();
+    wxCHECK_RET( node, wxT("no subitems at all??") );
+
+    ApplyAttributes(dc, m_gi->m_rectHighlight, IsHighlighted(), current);
 
     wxListItemData *item = node->GetData();
     if (item->HasImage())
@@ -799,31 +786,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc,
     // 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) )
-#if ( !defined(__WXGTK20__) && !defined(__WXMAC__) )
-    {
-        dc->DrawRectangle( rectHL );
 
-        wxUnusedVar(current);
-    }
-#else
-    {
-        if (highlighted)
-        {
-            int flags = wxCONTROL_SELECTED;
-            if (m_owner->HasFocus())
-                flags |= wxCONTROL_FOCUSED;
-            if (current)
-               flags |= wxCONTROL_CURRENT;
-            wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
-        }
-        else
-        {
-            dc->DrawRectangle( rectHL );
-        }
-    }
-#endif
+    ApplyAttributes(dc, rectHL, highlighted, current);
 
     wxCoord x = rect.x + HEADER_OFFSET_X,
             yMid = rect.y + rect.height/2;
@@ -845,7 +809,8 @@ void wxListLineData::DrawInReportMode( wxDC *dc,
         int xOld = x;
         x += width;
 
-        const int wText = width - 8;
+        width -= 8;
+        const int wText = width;
         wxDCClipper clipper(*dc, xOld, rect.y, wText, rect.height);
 
         if ( item->HasImage() )
@@ -861,7 +826,7 @@ void wxListLineData::DrawInReportMode( wxDC *dc,
         }
 
         if ( item->HasText() )
-            DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, wText);
+            DrawTextFormatted(dc, item->GetText(), col, xOld, yMid, width);
     }
 }
 
@@ -969,7 +934,6 @@ void wxListLineData::ReverseHighlight( void )
 BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
     EVT_PAINT         (wxListHeaderWindow::OnPaint)
     EVT_MOUSE_EVENTS  (wxListHeaderWindow::OnMouse)
-    EVT_SET_FOCUS     (wxListHeaderWindow::OnSetFocus)
 END_EVENT_TABLE()
 
 void wxListHeaderWindow::Init()
@@ -1092,7 +1056,7 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
 
 // NB: The code below is not really Mac-specific, but since we are close
 // to 2.8 release and I don't have time to test on other platforms, I
-// defined this only for wxMac. If this behavior is desired on
+// defined this only for wxMac. If this behaviour is desired on
 // other platforms, please go ahead and revise or remove the #ifdef.
 #ifdef __WXMAC__
         if ( !m_owner->IsVirtual() && (item.m_mask & wxLIST_MASK_STATE) &&
@@ -1376,12 +1340,6 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
     }
 }
 
-void wxListHeaderWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
-{
-    m_owner->SetFocus();
-    m_owner->Update();
-}
-
 bool wxListHeaderWindow::SendListEvent(wxEventType type, const wxPoint& pos)
 {
     wxWindow *parent = GetParent();
@@ -1448,23 +1406,31 @@ wxListTextCtrlWrapper::wxListTextCtrlWrapper(wxListMainWindow *owner,
     m_text->PushEventHandler(this);
 }
 
-void wxListTextCtrlWrapper::EndEdit(bool discardChanges)
+void wxListTextCtrlWrapper::EndEdit(EndReason reason)
 {
     m_aboutToFinish = true;
 
-    if ( discardChanges )
+    switch ( reason )
     {
-        m_owner->OnRenameCancelled(m_itemEdited);
+        case End_Accept:
+            // Notify the owner about the changes
+            AcceptChanges();
 
-        Finish( true );
-    }
-    else
-    {
-        // Notify the owner about the changes
-        AcceptChanges();
+            // Even if vetoed, close the control (consistent with MSW)
+            Finish( true );
+            break;
 
-        // Even if vetoed, close the control (consistent with MSW)
-        Finish( true );
+        case End_Discard:
+            m_owner->OnRenameCancelled(m_itemEdited);
+
+            Finish( true );
+            break;
+
+        case End_Destroy:
+            // Don't generate any notifications for the control being destroyed
+            // and don't set focus to it neither.
+            Finish(false);
+            break;
     }
 }
 
@@ -1499,20 +1465,28 @@ bool wxListTextCtrlWrapper::AcceptChanges()
 }
 
 void wxListTextCtrlWrapper::OnChar( wxKeyEvent &event )
+{
+    if ( !CheckForEndEditKey(event) )
+        event.Skip();
+}
+
+bool wxListTextCtrlWrapper::CheckForEndEditKey(const wxKeyEvent& event)
 {
     switch ( event.m_keyCode )
     {
         case WXK_RETURN:
-            EndEdit( false );
+            EndEdit( End_Accept );
             break;
 
         case WXK_ESCAPE:
-            EndEdit( true );
+            EndEdit( End_Discard );
             break;
 
         default:
-            event.Skip();
+            return false;
     }
+
+    return true;
 }
 
 void wxListTextCtrlWrapper::OnKeyUp( wxKeyEvent &event )
@@ -1556,6 +1530,7 @@ void wxListTextCtrlWrapper::OnKillFocus( wxFocusEvent &event )
 BEGIN_EVENT_TABLE(wxListMainWindow, wxWindow)
   EVT_PAINT          (wxListMainWindow::OnPaint)
   EVT_MOUSE_EVENTS   (wxListMainWindow::OnMouse)
+  EVT_CHAR_HOOK      (wxListMainWindow::OnCharHook)
   EVT_CHAR           (wxListMainWindow::OnChar)
   EVT_KEY_DOWN       (wxListMainWindow::OnKeyDown)
   EVT_KEY_UP         (wxListMainWindow::OnKeyUp)
@@ -1607,10 +1582,9 @@ wxListMainWindow::wxListMainWindow()
 wxListMainWindow::wxListMainWindow( wxWindow *parent,
                                     wxWindowID id,
                                     const wxPoint& pos,
-                                    const wxSize& size,
-                                    long style,
-                                    const wxString &name )
-                : wxWindow( parent, id, pos, size, style, name )
+                                    const wxSize& size )
+                : wxWindow( parent, id, pos, size,
+                            wxWANTS_CHARS | wxBORDER_NONE )
 {
     Init();
 
@@ -1641,6 +1615,9 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent,
 
 wxListMainWindow::~wxListMainWindow()
 {
+    if ( m_textctrlWrapper )
+        m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Destroy);
+
     DoDeleteAllItems();
     WX_CLEAR_LIST(wxListHeaderDataList, m_columns);
     WX_CLEAR_ARRAY(m_aColWidths);
@@ -2058,6 +2035,7 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
                                 GetParent()->GetId());
             evCache.SetEventObject( GetParent() );
             evCache.m_oldItemIndex = visibleFrom;
+            evCache.m_item.m_itemId =
             evCache.m_itemIndex = visibleTo;
             GetParent()->GetEventHandler()->ProcessEvent( evCache );
         }
@@ -2134,24 +2112,24 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
         size_t count = GetItemCount();
         for ( size_t i = 0; i < count; i++ )
         {
-            GetLine(i)->Draw( &dc );
+            GetLine(i)->Draw( &dc, i == m_current );
         }
     }
 
-#if !defined( __WXMAC__) && !defined(__WXGTK20__)
-    // Don't draw rect outline under Mac at all.
-    // Draw it elsewhere under GTK.
+    // DrawFocusRect() is unusable under Mac, it draws outside of the highlight
+    // rectangle somehow and so leaves traces when the item is not selected any
+    // more, see #12229.
+#ifndef __WXMAC__
     if ( HasCurrent() )
     {
-        if ( m_hasFocus )
-        {
-            wxRect rect( GetLineHighlightRect( m_current ) );
-            dc.SetPen( *wxBLACK_PEN );
-            dc.SetBrush( *wxTRANSPARENT_BRUSH );
-            dc.DrawRectangle( rect );
-        }
+        int flags = 0;
+        if ( IsHighlighted(m_current) )
+            flags |= wxCONTROL_SELECTED;
+
+        wxRendererNative::Get().
+            DrawFocusRect(this, dc, GetLineHighlightRect(m_current), flags);
     }
-#endif
+#endif // !__WXMAC__
 }
 
 void wxListMainWindow::HighlightAll( bool on )
@@ -2188,6 +2166,7 @@ void wxListMainWindow::SendNotify( size_t line,
     wxListEvent le( command, GetParent()->GetId() );
     le.SetEventObject( GetParent() );
 
+    le.m_item.m_itemId =
     le.m_itemIndex = line;
 
     // set only for events which have position
@@ -2228,13 +2207,14 @@ wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass
     wxCHECK_MSG( (item >= 0) && ((size_t)item < GetItemCount()), NULL,
                  wxT("wrong index in wxGenericListCtrl::EditLabel()") );
 
-    wxASSERT_MSG( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)),
+    wxASSERT_MSG( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)),
                  wxT("EditLabel() needs a text control") );
 
     size_t itemEdit = (size_t)item;
 
     wxListEvent le( wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT, GetParent()->GetId() );
     le.SetEventObject( GetParent() );
+    le.m_item.m_itemId =
     le.m_itemIndex = item;
     wxListLineData *data = GetLine(itemEdit);
     wxCHECK_MSG( data, NULL, wxT("invalid index in EditLabel()") );
@@ -2277,6 +2257,7 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
 {
     wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
     le.SetEventObject( GetParent() );
+    le.m_item.m_itemId =
     le.m_itemIndex = itemEdit;
 
     wxListLineData *data = GetLine(itemEdit);
@@ -2297,6 +2278,7 @@ void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
     le.SetEditCanceled(true);
 
     le.SetEventObject( GetParent() );
+    le.m_item.m_itemId =
     le.m_itemIndex = itemEdit;
 
     wxListLineData *data = GetLine(itemEdit);
@@ -2314,7 +2296,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
     // listctrl because the order of events is different (or something like
     // that), so explicitly end the edit if it is active.
     if ( event.LeftDown() && m_textctrlWrapper )
-        m_textctrlWrapper->EndEdit( false );
+        m_textctrlWrapper->EndEdit(wxListTextCtrlWrapper::End_Accept);
 #endif // __WXMAC__
 
     if ( event.LeftDown() )
@@ -2381,37 +2363,19 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         }
     }
 
+    // Update drag events counter first as we must do it even if the mouse is
+    // not on any item right now as we must keep count in case we started
+    // dragging from the empty control area but continued to do it over a valid
+    // item -- in this situation we must not start dragging this item.
     if (event.Dragging())
-    {
-        if (m_dragCount == 0)
-        {
-            // 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++;
-
-        if (m_dragCount != 3)
-            return;
-
-        int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG
-                                          : wxEVT_COMMAND_LIST_BEGIN_DRAG;
-
-        wxListEvent le( command, GetParent()->GetId() );
-        le.SetEventObject( GetParent() );
-        le.m_itemIndex = m_lineLastClicked;
-        le.m_pointDrag = m_dragStart;
-        GetParent()->GetEventHandler()->ProcessEvent( le );
-
-        return;
-    }
     else
-    {
         m_dragCount = 0;
-    }
 
+    // The only mouse event that can be generated without any valid item is
+    // wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK as it can be useful to have a global
+    // popup menu for the list control itself which should be shown even when
+    // the user clicks outside of any item.
     if ( !hitResult )
     {
         // outside of any item
@@ -2435,6 +2399,27 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         return;
     }
 
+    if ( event.Dragging() )
+    {
+        if (m_dragCount == 1)
+        {
+            // 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();
+        }
+
+        if (m_dragCount != 3)
+            return;
+
+        int command = event.RightIsDown() ? wxEVT_COMMAND_LIST_BEGIN_RDRAG
+                                          : wxEVT_COMMAND_LIST_BEGIN_DRAG;
+
+        SendNotify( m_lineLastClicked, command, m_dragStart );
+
+        return;
+    }
+
     bool forceClick = false;
     if (event.ButtonDClick())
     {
@@ -2723,6 +2708,17 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
     if (parent->GetEventHandler()->ProcessEvent( ke ))
         return;
 
+    // send a list event
+    wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, parent->GetId() );
+    le.m_item.m_itemId =
+    le.m_itemIndex = m_current;
+    if (HasCurrent())
+        GetLine(m_current)->GetItem( 0, le.m_item );
+    le.m_code = event.GetKeyCode();
+    le.SetEventObject( parent );
+    if (parent->GetEventHandler()->ProcessEvent( le ))
+        return;
+
     event.Skip();
 }
 
@@ -2738,37 +2734,32 @@ void wxListMainWindow::OnKeyUp( wxKeyEvent &event )
     event.Skip();
 }
 
-void wxListMainWindow::OnChar( wxKeyEvent &event )
+void wxListMainWindow::OnCharHook( wxKeyEvent &event )
 {
-    wxWindow *parent = GetParent();
-
-    // send a list_key event up
-    if ( HasCurrent() )
+    if ( m_textctrlWrapper )
     {
-        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 = event.GetKeyCode();
-        le.SetEventObject( parent );
-        parent->GetEventHandler()->ProcessEvent( le );
-    }
-
-    if ( (event.GetKeyCode() != WXK_UP) &&
-         (event.GetKeyCode() != WXK_DOWN) &&
-         (event.GetKeyCode() != WXK_RIGHT) &&
-         (event.GetKeyCode() != WXK_LEFT) &&
-         (event.GetKeyCode() != WXK_PAGEUP) &&
-         (event.GetKeyCode() != WXK_PAGEDOWN) &&
-         (event.GetKeyCode() != WXK_END) &&
-         (event.GetKeyCode() != WXK_HOME) )
-    {
-        // propagate the char event upwards
-        wxKeyEvent ke(event);
-        ke.SetEventObject( parent );
-        if (parent->GetEventHandler()->ProcessEvent( ke ))
+        // When an in-place editor is active we should ensure that it always
+        // gets the key events that are special to it.
+        if ( m_textctrlWrapper->CheckForEndEditKey(event) )
+        {
+            // Skip the call to wxEvent::Skip() below.
             return;
+        }
     }
 
+    event.Skip();
+}
+
+void wxListMainWindow::OnChar( wxKeyEvent &event )
+{
+    wxWindow *parent = GetParent();
+
+    // propagate the char event upwards
+    wxKeyEvent ke(event);
+    ke.SetEventObject( parent );
+    if (parent->GetEventHandler()->ProcessEvent( ke ))
+        return;
+
     if ( HandleAsNavigationKey(event) )
         return;
 
@@ -2978,17 +2969,6 @@ void wxListMainWindow::GetImageSize( int index, int &width, int &height ) const
     }
 }
 
-int wxListMainWindow::GetTextLength( const wxString &s ) const
-{
-    wxClientDC dc( wxConstCast(this, wxListMainWindow) );
-    dc.SetFont( GetFont() );
-
-    wxCoord lw;
-    dc.GetTextExtent( s, &lw, NULL );
-
-    return lw + AUTOSIZE_COL_MARGIN;
-}
-
 void wxListMainWindow::SetImageList( wxImageList *imageList, int which )
 {
     m_dirty = true;
@@ -3031,18 +3011,42 @@ int wxListMainWindow::GetItemSpacing( bool isSmall )
 // columns
 // ----------------------------------------------------------------------------
 
-void wxListMainWindow::SetColumn( int col, wxListItem &item )
+int
+wxListMainWindow::ComputeMinHeaderWidth(const wxListHeaderData* column) const
+{
+    wxClientDC dc(const_cast<wxListMainWindow*>(this));
+
+    int width = dc.GetTextExtent(column->GetText()).x + AUTOSIZE_COL_MARGIN;
+
+    width += 2*EXTRA_WIDTH;
+
+    // check for column header's image availability
+    const int image = column->GetImage();
+    if ( image != -1 )
+    {
+        if ( m_small_image_list )
+        {
+            int ix = 0, iy = 0;
+            m_small_image_list->GetSize(image, ix, iy);
+            width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
+        }
+    }
+
+    return width;
+}
+
+void wxListMainWindow::SetColumn( int col, const wxListItem &item )
 {
     wxListHeaderDataList::compatibility_iterator node = m_columns.Item( col );
 
     wxCHECK_RET( node, wxT("invalid column index in SetColumn") );
 
-    if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
-        item.m_width = GetTextLength( item.m_text );
-
     wxListHeaderData *column = node->GetData();
     column->SetItem( item );
 
+    if ( item.m_width == wxLIST_AUTOSIZE_USEHEADER )
+        column->SetWidth(ComputeMinHeaderWidth(column));
+
     wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
     if ( headerWin )
         headerWin->m_dirty = true;
@@ -3076,29 +3080,13 @@ void wxListMainWindow::SetColumnWidth( int col, int width )
 
     if (width == wxLIST_AUTOSIZE_USEHEADER)
     {
-        width = GetTextLength(column->GetText());
-        width += 2*EXTRA_WIDTH;
-
-        // check for column header's image availability
-        const int image = column->GetImage();
-        if ( image != -1 )
-        {
-            if ( m_small_image_list )
-            {
-                int ix = 0, iy = 0;
-                m_small_image_list->GetSize(image, ix, iy);
-                width += ix + HEADER_IMAGE_MARGIN_IN_REPORT_MODE;
-            }
-        }
+        width = ComputeMinHeaderWidth(column);
     }
     else if ( width == wxLIST_AUTOSIZE )
     {
-        if ( IsVirtual() )
-        {
-            // TODO: determine the max width somehow...
-            width = WIDTH_COL_DEFAULT;
-        }
-        else // !virtual
+        width = ComputeMinHeaderWidth(column);
+
+        if ( !IsVirtual() )
         {
             wxClientDC dc(this);
             dc.SetFont( GetFont() );
@@ -3128,8 +3116,9 @@ void wxListMainWindow::SetColumnWidth( int col, int width )
                 m_aColWidths.Item(col)->nMaxWidth = max;
             }
 
-            max = m_aColWidths.Item(col)->nMaxWidth;
-            width = max + AUTOSIZE_COL_MARGIN;
+            max = m_aColWidths.Item(col)->nMaxWidth + AUTOSIZE_COL_MARGIN;
+            if ( width < max )
+                width = max;
         }
     }
 
@@ -3202,10 +3191,14 @@ void wxListMainWindow::SetItem( wxListItem &item )
         }
     }
 
-    // update the item on screen
-    wxRect rectItem;
-    GetItemRect(id, rectItem);
-    RefreshRect(rectItem);
+    // update the item on screen unless we're going to update everything soon
+    // anyhow
+    if ( !m_dirty )
+    {
+        wxRect rectItem;
+        GetItemRect(id, rectItem);
+        RefreshRect(rectItem);
+    }
 }
 
 void wxListMainWindow::SetItemStateAll(long state, long stateMask)
@@ -3650,6 +3643,9 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh)
 
                 int x = EXTRA_BORDER_X;
                 int y = EXTRA_BORDER_Y;
+
+                // Note that "row" here is vertical, i.e. what is called
+                // "column" in many other places in wxWidgets.
                 int maxWidthInThisRow = 0;
 
                 m_linesPerPage = 0;
@@ -3671,8 +3667,24 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh)
                     if (currentlyVisibleLines > m_linesPerPage)
                         m_linesPerPage = currentlyVisibleLines;
 
-                    if ( y + sizeLine.y >= clientHeight )
+                    // Have we reached the end of the row either because no
+                    // more items would fit or because there are simply no more
+                    // items?
+                    if ( y + sizeLine.y >= clientHeight
+                            || i == count - 1)
                     {
+                        // Adjust all items in this row to have the same
+                        // width to ensure that they all align horizontally in
+                        // icon view.
+                        if ( HasFlag(wxLC_ICON) || HasFlag(wxLC_SMALL_ICON) )
+                        {
+                            size_t firstRowLine = i - currentlyVisibleLines + 1;
+                            for (size_t j = firstRowLine; j <= i; j++)
+                            {
+                                GetLine(j)->m_gi->ExtendWidth(maxWidthInThisRow);
+                            }
+                        }
+
                         currentlyVisibleLines = 0;
                         y = EXTRA_BORDER_Y;
                         maxWidthInThisRow += MARGIN_BETWEEN_ROWS;
@@ -3681,10 +3693,6 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh)
                         maxWidthInThisRow = 0;
                     }
 
-                    // We have reached the last item.
-                    if ( i == count - 1 )
-                        entireWidth += maxWidthInThisRow;
-
                     if ( (tries == 0) &&
                             (entireWidth + SCROLL_UNIT_X > clientWidth) )
                     {
@@ -3857,6 +3865,21 @@ void wxListMainWindow::DeleteColumn( int col )
         for ( size_t i = 0; i < m_lines.GetCount(); i++ )
         {
             wxListLineData * const line = GetLine(i);
+
+            // In the following atypical but possible scenario it can be
+            // legal to call DeleteColumn() but the items may not have any
+            // values for it:
+            //  1. In report view, insert a second column.
+            //  2. Still in report view, add an item with 2 values.
+            //  3. Switch to an icon (or list) view.
+            //  4. Add an item -- necessarily with 1 value only.
+            //  5. Switch back to report view.
+            //  6. Call DeleteColumn().
+            // So we need to check for this as otherwise we would simply crash
+            // if this happens.
+            if ( line->m_items.GetCount() <= static_cast<unsigned>(col) )
+                continue;
+
             wxListItemDataList::compatibility_iterator n = line->m_items.Item( col );
             delete n->GetData();
             line->m_items.Erase(n);
@@ -4059,8 +4082,11 @@ void wxListMainWindow::InsertItem( wxListItem &item )
     {
         ResetVisibleLinesRange();
 
+        const unsigned col = item.GetColumn();
+        wxCHECK_RET( col < m_aColWidths.size(), "invalid item column" );
+
         // calculate the width of the item and adjust the max column width
-        wxColWidthInfo *pWidthInfo = m_aColWidths.Item(item.GetColumn());
+        wxColWidthInfo *pWidthInfo = m_aColWidths.Item(col);
         int width = GetItemWidthWithImage(&item);
         item.SetWidth(width);
         if (width > pWidthInfo->nMaxWidth)
@@ -4070,6 +4096,19 @@ void wxListMainWindow::InsertItem( wxListItem &item )
     wxListLineData *line = new wxListLineData(this);
 
     line->SetItem( item.m_col, item );
+    if ( item.m_mask & wxLIST_MASK_IMAGE )
+    {
+        // Reset the buffered height if it's not big enough for the new image.
+        int image = item.GetImage();
+        if ( m_small_image_list && image != -1 && InReportView() )
+        {
+            int imageWidth, imageHeight;
+            m_small_image_list->GetSize(image, imageWidth, imageHeight);
+
+            if ( imageHeight > m_lineHeight )
+                m_lineHeight = 0;
+        }
+    }
 
     m_lines.Insert( line, id );
 
@@ -4086,15 +4125,15 @@ void wxListMainWindow::InsertItem( wxListItem &item )
     RefreshLines(id, GetItemCount() - 1);
 }
 
-void wxListMainWindow::InsertColumn( long col, wxListItem &item )
+void wxListMainWindow::InsertColumn( long col, const wxListItem &item )
 {
     m_dirty = true;
     if ( InReportView() )
     {
+        wxListHeaderData *column = new wxListHeaderData( item );
         if (item.m_width == wxLIST_AUTOSIZE_USEHEADER)
-            item.m_width = GetTextLength( item.m_text );
+            column->SetWidth(ComputeMinHeaderWidth(column));
 
-        wxListHeaderData *column = new wxListHeaderData( item );
         wxColWidthInfo *colWidthInfo = new wxColWidthInfo();
 
         bool insert = (col >= 0) && ((size_t)col < m_columns.GetCount());
@@ -4264,7 +4303,7 @@ void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
 
 IMPLEMENT_DYNAMIC_CLASS(wxGenericListCtrl, wxControl)
 
-BEGIN_EVENT_TABLE(wxGenericListCtrl,wxControl)
+BEGIN_EVENT_TABLE(wxGenericListCtrl,wxListCtrlBase)
   EVT_SIZE(wxGenericListCtrl::OnSize)
   EVT_SCROLLWIN(wxGenericListCtrl::OnScroll)
 END_EVENT_TABLE()
@@ -4326,9 +4365,7 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded()
     {
         GetSizer()->Detach( m_headerWin );
 
-        delete m_headerWin;
-
-        m_headerWin = NULL;
+        wxDELETE(m_headerWin);
     }
 }
 
@@ -4346,18 +4383,20 @@ bool wxGenericListCtrl::Create(wxWindow *parent,
     wxASSERT_MSG( (style & wxLC_MASK_TYPE),
                   wxT("wxListCtrl style should have exactly one mode bit set") );
 
-    if ( !wxControl::Create( parent, id, pos, size, style|wxVSCROLL|wxHSCROLL, validator, name ) )
+    if ( !wxListCtrlBase::Create( parent, id, pos, size,
+                                  style | wxVSCROLL | wxHSCROLL,
+                                  validator, name ) )
         return false;
 
-#ifdef __WXGTK__
-    style &= ~wxBORDER_MASK;
-    style |= wxBORDER_THEME;
-#endif
-
-    m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0, 0), size, style );
+    m_mainWin = new wxListMainWindow(this, wxID_ANY, wxPoint(0, 0), size);
 
     SetTargetWindow( m_mainWin );
 
+    // We use the cursor keys for moving the selection, not scrolling, so call
+    // this method to ensure wxScrollHelperEvtHandler doesn't catch all
+    // keyboard events forwarded to us from wxListMainWindow.
+    DisableKeyboardScrolling();
+
     wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
     sizer->Add( m_mainWin, 1, wxGROW );
     SetSizer( sizer );
@@ -4379,7 +4418,7 @@ WXLRESULT wxGenericListCtrl::MSWWindowProc(WXUINT nMsg,
                                        WXWPARAM wParam,
                                        WXLPARAM lParam)
 {
-    WXLRESULT rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
+    WXLRESULT rc = wxListCtrlBase::MSWWindowProc(nMsg, wParam, lParam);
 
     // we need to process arrows ourselves for scrolling
     if ( nMsg == WM_GETDLGCODE )
@@ -4452,6 +4491,11 @@ void wxGenericListCtrl::SetSingleStyle( long style, bool add )
 
 void wxGenericListCtrl::SetWindowStyleFlag( long flag )
 {
+    // we add wxHSCROLL and wxVSCROLL in ctor unconditionally and it never
+    // makes sense to remove them as we'll always add scrollbars anyhow when
+    // needed
+    flag |= wxHSCROLL | wxVSCROLL;
+
     const bool wasInReportView = HasFlag(wxLC_REPORT);
 
     // update the window style first so that the header is created or destroyed
@@ -4482,7 +4526,7 @@ bool wxGenericListCtrl::GetColumn(int col, wxListItem &item) const
     return true;
 }
 
-bool wxGenericListCtrl::SetColumn( int col, wxListItem& item )
+bool wxGenericListCtrl::SetColumn( int col, const wxListItem& item )
 {
     m_mainWin->SetColumn( col, item );
     return true;
@@ -4817,6 +4861,10 @@ bool wxGenericListCtrl::DeleteColumn( int col )
     // if we don't have the header any longer, we need to relayout the window
     // if ( !GetColumnCount() )
 
+
+    // Ensure that the non-existent columns are really removed from display.
+    Refresh();
+
     return true;
 }
 
@@ -4888,12 +4936,14 @@ long wxGenericListCtrl::InsertItem( long index, const wxString &label, int image
     wxListItem info;
     info.m_text = label;
     info.m_image = imageIndex;
-    info.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE;
+    info.m_mask = wxLIST_MASK_TEXT;
+    if (imageIndex > -1)
+        info.m_mask |= wxLIST_MASK_IMAGE;
     info.m_itemId = index;
     return InsertItem( info );
 }
 
-long wxGenericListCtrl::InsertColumn( long col, wxListItem &item )
+long wxGenericListCtrl::DoInsertColumn( long col, const wxListItem &item )
 {
     wxCHECK_MSG( InReportView(), -1, wxT("can't add column in non report mode") );
 
@@ -4907,23 +4957,6 @@ long wxGenericListCtrl::InsertColumn( long col, wxListItem &item )
     return 0;
 }
 
-long wxGenericListCtrl::InsertColumn( long col, const wxString &heading,
-                               int format, int width )
-{
-    wxListItem item;
-    item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
-    item.m_text = heading;
-    if (width >= -2)
-    {
-        item.m_mask |= wxLIST_MASK_WIDTH;
-        item.m_width = width;
-    }
-
-    item.m_format = format;
-
-    return InsertColumn( col, item );
-}
-
 bool wxGenericListCtrl::ScrollList( int dx, int dy )
 {
     return m_mainWin->ScrollList(dx, dy);
@@ -5090,28 +5123,81 @@ bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
 
 void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const
 {
-    m_mainWin->DoClientToScreen(x, y);
+    // It's not clear whether this can be called before m_mainWin is created
+    // but it seems better to be on the safe side and check.
+    if ( m_mainWin )
+        m_mainWin->DoClientToScreen(x, y);
+    else
+        wxListCtrlBase::DoClientToScreen(x, y);
 }
 
 void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const
 {
-    m_mainWin->DoScreenToClient(x, y);
+    // At least in wxGTK/Univ build this method can be called before m_mainWin
+    // is created so avoid crashes in this case.
+    if ( m_mainWin )
+        m_mainWin->DoScreenToClient(x, y);
+    else
+        wxListCtrlBase::DoScreenToClient(x, y);
 }
 
-void wxGenericListCtrl::SetFocus()
+wxSize wxGenericListCtrl::DoGetBestClientSize() const
 {
-    // The test in window.cpp fails as we are a composite
-    // window, so it checks against "this", but not m_mainWin.
-    if ( DoFindFocus() != this )
-        m_mainWin->SetFocus();
-}
+    // The base class version can compute the best size in report view only.
+    wxSize sizeBest = wxListCtrlBase::DoGetBestClientSize();
 
-wxSize wxGenericListCtrl::DoGetBestSize() const
-{
-    // Something is better than nothing...
-    // 100x80 is what the MSW version will get from the default
-    // wxControl::DoGetBestSize
-    return wxSize(100, 80);
+    if ( !InReportView() )
+    {
+        // Ensure that our minimal width is at least big enough to show all our
+        // items. This is important for wxListbook to size itself correctly.
+
+        // Remember the offset of the first item: this corresponds to the
+        // margins around the item so we will add it to the minimal size below
+        // to ensure that we have equal margins on all sides.
+        wxPoint ofs;
+
+        // We can iterate over all items as there shouldn't be too many of them
+        // in non-report view. If it ever becomes a problem, we could examine
+        // just the first few items probably, the determination of the best
+        // size is less important if we will need scrollbars anyhow.
+        for ( int n = 0; n < GetItemCount(); n++ )
+        {
+            const wxRect itemRect = m_mainWin->GetLineRect(n);
+            if ( !n )
+            {
+                // Remember the position of the first item as all the rest are
+                // offset by at least this number of pixels too.
+                ofs = itemRect.GetPosition();
+            }
+
+            sizeBest.IncTo(itemRect.GetSize());
+        }
+
+        sizeBest.IncBy(2*ofs);
+
+
+        // If we have the scrollbars we need to account for them too. And to
+        // make sure the scrollbars status is up to date we need to call this
+        // function to set them.
+        m_mainWin->RecalculatePositions(true /* no refresh */);
+
+        // Unfortunately we can't use wxWindow::HasScrollbar() here as we need
+        // to use m_mainWin client/virtual size for determination of whether we
+        // use scrollbars and not the size of this window itself. Maybe that
+        // function should be extended to work correctly in the case when our
+        // scrollbars manage a different window from this one but currently it
+        // doesn't work.
+        const wxSize sizeClient = m_mainWin->GetClientSize();
+        const wxSize sizeVirt = m_mainWin->GetVirtualSize();
+
+        if ( sizeVirt.x > sizeClient.x /* HasScrollbar(wxHORIZONTAL) */ )
+            sizeBest.y += wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y);
+
+        if ( sizeVirt.y > sizeClient.y /* HasScrollbar(wxVERTICAL) */ )
+            sizeBest.x += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
+    }
+
+    return sizeBest;
 }
 
 // ----------------------------------------------------------------------------