]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
Add test for absence of events from wxSpinCtrlDouble ctor.
[wxWidgets.git] / src / generic / listctrl.cpp
index 6f02e43f3a317a9e828e1b265346a8a81864fdb7..58a5c733eba0fa3b0ca80ee2fe89e8fbdfcc942b 100644 (file)
@@ -3,7 +3,6 @@
 // Purpose:     generic implementation of wxListCtrl
 // Author:      Robert Roebling
 //              Vadim Zeitlin (virtual list control support)
-// Id:          $Id$
 // Copyright:   (c) 1998 Robert Roebling
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -342,12 +341,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
@@ -923,7 +933,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()
@@ -942,15 +951,17 @@ wxListHeaderWindow::wxListHeaderWindow()
     m_resizeCursor = 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 )
+bool wxListHeaderWindow::Create( wxWindow *win,
+                                 wxWindowID id,
+                                 wxListMainWindow *owner,
+                                 const wxPoint& pos,
+                                 const wxSize& size,
+                                 long style,
+                                 const wxString &name )
 {
+    if ( !wxWindow::Create(win, id, pos, size, style, name) )
+        return false;
+
     Init();
 
     m_owner = owner;
@@ -968,6 +979,8 @@ wxListHeaderWindow::wxListHeaderWindow( wxWindow *win,
     if (!m_hasFont)
         SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
 #endif
+
+    return true;
 }
 
 wxListHeaderWindow::~wxListHeaderWindow()
@@ -1204,7 +1217,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
 
     if (m_isDragging)
     {
-        SendListEvent(wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition());
+        SendListEvent(wxEVT_LIST_COL_DRAGGING, event.GetPosition());
 
         // we don't draw the line beyond our window, but we allow dragging it
         // there
@@ -1223,7 +1236,7 @@ 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());
+            SendListEvent(wxEVT_LIST_COL_END_DRAG, event.GetPosition());
         }
         else
         {
@@ -1276,7 +1289,7 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
         {
             if (hit_border && event.LeftDown())
             {
-                if ( SendListEvent(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG,
+                if ( SendListEvent(wxEVT_LIST_COL_BEGIN_DRAG,
                                    event.GetPosition()) )
                 {
                     m_isDragging = true;
@@ -1305,8 +1318,8 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
                 }
 
                 SendListEvent( event.LeftDown()
-                                    ? wxEVT_COMMAND_LIST_COL_CLICK
-                                    : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
+                                    ? wxEVT_LIST_COL_CLICK
+                                    : wxEVT_LIST_COL_RIGHT_CLICK,
                                 event.GetPosition());
             }
         }
@@ -1330,12 +1343,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();
@@ -1367,6 +1374,15 @@ void wxListRenameTimer::Notify()
     m_owner->OnRenameTimer();
 }
 
+//-----------------------------------------------------------------------------
+// wxListFindTimer (internal)
+//-----------------------------------------------------------------------------
+
+void wxListFindTimer::Notify()
+{
+    m_owner->OnFindTimer();
+}
+
 //-----------------------------------------------------------------------------
 // wxListTextCtrlWrapper (internal)
 //-----------------------------------------------------------------------------
@@ -1559,6 +1575,8 @@ void wxListMainWindow::Init()
 
     m_lastOnSame = false;
     m_renameTimer = new wxListRenameTimer( this );
+    m_findTimer = NULL;
+    m_findBell = 0;  // default is to not ring bell at all
     m_textctrlWrapper = NULL;
 
     m_current =
@@ -1578,10 +1596,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();
 
@@ -1622,6 +1639,7 @@ wxListMainWindow::~wxListMainWindow()
     delete m_highlightBrush;
     delete m_highlightUnfocusedBrush;
     delete m_renameTimer;
+    delete m_findTimer;
 }
 
 void wxListMainWindow::SetReportView(bool inReportView)
@@ -1873,8 +1891,8 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
 
     if ( changed )
     {
-        SendNotify( line, highlight ? wxEVT_COMMAND_LIST_ITEM_SELECTED
-                                    : wxEVT_COMMAND_LIST_ITEM_DESELECTED );
+        SendNotify( line, highlight ? wxEVT_LIST_ITEM_SELECTED
+                                    : wxEVT_LIST_ITEM_DESELECTED );
     }
 
     return changed;
@@ -2028,10 +2046,11 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
         // tell the caller cache to cache the data
         if ( IsVirtual() )
         {
-            wxListEvent evCache(wxEVT_COMMAND_LIST_CACHE_HINT,
+            wxListEvent evCache(wxEVT_LIST_CACHE_HINT,
                                 GetParent()->GetId());
             evCache.SetEventObject( GetParent() );
             evCache.m_oldItemIndex = visibleFrom;
+            evCache.m_item.m_itemId =
             evCache.m_itemIndex = visibleTo;
             GetParent()->GetEventHandler()->ProcessEvent( evCache );
         }
@@ -2162,6 +2181,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
@@ -2178,7 +2198,7 @@ void wxListMainWindow::SendNotify( size_t line,
         {
             GetLine(line)->GetItem( 0, le.m_item );
         }
-        //else: this happens for wxEVT_COMMAND_LIST_ITEM_FOCUSED event
+        //else: this happens for wxEVT_LIST_ITEM_FOCUSED event
     }
     //else: there may be no more such item
 
@@ -2194,7 +2214,7 @@ void wxListMainWindow::ChangeCurrent(size_t current)
     if ( m_renameTimer->IsRunning() )
         m_renameTimer->Stop();
 
-    SendNotify(current, wxEVT_COMMAND_LIST_ITEM_FOCUSED);
+    SendNotify(current, wxEVT_LIST_ITEM_FOCUSED);
 }
 
 wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass)
@@ -2202,13 +2222,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() );
+    wxListEvent le( wxEVT_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()") );
@@ -2220,19 +2241,10 @@ wxTextCtrl *wxListMainWindow::EditLabel(long item, wxClassInfo* textControlClass
         return NULL;
     }
 
-    // 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 )
     {
-        // TODO: use wxTheApp->SafeYieldFor(NULL, wxEVT_CATEGORY_UI) instead
-        //       so that no pending events may change the item count (see below)
-        //       IMPORTANT: needs to be tested!
-        wxSafeYield();
-
-        // Pending events dispatched by wxSafeYield might have changed the item
-        // count
-        if ( (size_t)item >= GetItemCount() )
-            return NULL;
+        // Ensure the display is updated before we start editing.
+        Update();
     }
 
     wxTextCtrl * const text = (wxTextCtrl *)textControlClass->CreateObject();
@@ -2249,8 +2261,9 @@ void wxListMainWindow::OnRenameTimer()
 
 bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
 {
-    wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
+    wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() );
     le.SetEventObject( GetParent() );
+    le.m_item.m_itemId =
     le.m_itemIndex = itemEdit;
 
     wxListLineData *data = GetLine(itemEdit);
@@ -2266,11 +2279,12 @@ bool wxListMainWindow::OnRenameAccept(size_t itemEdit, const wxString& value)
 void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
 {
     // let owner know that the edit was cancelled
-    wxListEvent le( wxEVT_COMMAND_LIST_END_LABEL_EDIT, GetParent()->GetId() );
+    wxListEvent le( wxEVT_LIST_END_LABEL_EDIT, GetParent()->GetId() );
 
     le.SetEditCanceled(true);
 
     le.SetEventObject( GetParent() );
+    le.m_item.m_itemId =
     le.m_itemIndex = itemEdit;
 
     wxListLineData *data = GetLine(itemEdit);
@@ -2280,6 +2294,18 @@ void wxListMainWindow::OnRenameCancelled(size_t itemEdit)
     GetEventHandler()->ProcessEvent( le );
 }
 
+void wxListMainWindow::OnFindTimer()
+{
+    m_findPrefix.clear();
+    if ( m_findBell )
+        m_findBell = 1;
+}
+
+void wxListMainWindow::EnableBellOnNoMatch( bool on )
+{
+    m_findBell = on;
+}
+
 void wxListMainWindow::OnMouse( wxMouseEvent &event )
 {
 #ifdef __WXMAC__
@@ -2292,10 +2318,16 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
 #endif // __WXMAC__
 
     if ( event.LeftDown() )
-        SetFocus();
+    {
+        // Ensure we skip the event to let the system set focus to this window.
+        event.Skip();
+    }
 
-    event.SetEventObject( GetParent() );
-    if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
+    // Pretend that the event happened in wxListCtrl itself.
+    wxMouseEvent me(event);
+    me.SetEventObject( GetParent() );
+    me.SetId(GetParent()->GetId());
+    if ( GetParent()->GetEventHandler()->ProcessEvent( me ))
         return;
 
     if (event.GetEventType() == wxEVT_MOUSEWHEEL)
@@ -2309,7 +2341,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
     {
         if (event.RightDown())
         {
-            SendNotify( (size_t)-1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+            SendNotify( (size_t)-1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
 
             wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU,
                                       GetParent()->GetId(),
@@ -2355,43 +2387,25 @@ 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_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
         if (event.RightDown())
         {
-            SendNotify( (size_t) -1, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+            SendNotify( (size_t) -1, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
 
             wxContextMenuEvent evtCtx(
                 wxEVT_CONTEXT_MENU,
@@ -2409,6 +2423,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_LIST_BEGIN_RDRAG
+                                          : wxEVT_LIST_BEGIN_DRAG;
+
+        SendNotify( m_lineLastClicked, command, m_dragStart );
+
+        return;
+    }
+
     bool forceClick = false;
     if (event.ButtonDClick())
     {
@@ -2419,7 +2454,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
 
         if ( current == m_lineLastClicked )
         {
-            SendNotify( current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+            SendNotify( current, wxEVT_LIST_ITEM_ACTIVATED );
 
             return;
         }
@@ -2453,9 +2488,10 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
                     m_renameTimer->Start(dclick > 0 ? dclick : 250, true);
                 }
             }
+
+            m_lastOnSame = false;
         }
 
-        m_lastOnSame = false;
         m_lineSelectSingleOnUp = (size_t)-1;
     }
     else
@@ -2480,14 +2516,14 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
             ReverseHighlight(m_current);
         }
 
-        SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
+        SendNotify( current, wxEVT_LIST_ITEM_RIGHT_CLICK, event.GetPosition() );
 
         // Allow generation of context menu event
         event.Skip();
     }
     else if (event.MiddleDown())
     {
-        SendNotify( current, wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK );
+        SendNotify( current, wxEVT_LIST_ITEM_MIDDLE_CLICK );
     }
     else if ( event.LeftDown() || forceClick )
     {
@@ -2547,8 +2583,16 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         if (m_current != oldCurrent)
             RefreshLine( oldCurrent );
 
-        // forceClick is only set if the previous click was on another item
-        m_lastOnSame = !forceClick && (m_current == oldCurrent) && oldWasSelected;
+        // Set the flag telling us whether the next click on this item should
+        // start editing its label. This should happen if we clicked on the
+        // current item and it was already selected, i.e. if this click was not
+        // done to select it.
+        //
+        // It should not happen if this was a double click (forceClick is true)
+        // nor if we hadn't had the focus before as then this click was used to
+        // give focus to the control.
+        m_lastOnSame = (m_current == oldCurrent) && oldWasSelected &&
+                            !forceClick && HasFocus();
     }
 }
 
@@ -2694,11 +2738,13 @@ void wxListMainWindow::OnKeyDown( wxKeyEvent &event )
     // propagate the key event upwards
     wxKeyEvent ke(event);
     ke.SetEventObject( parent );
+    ke.SetId(GetParent()->GetId());
     if (parent->GetEventHandler()->ProcessEvent( ke ))
         return;
 
     // send a list event
-    wxListEvent le( wxEVT_COMMAND_LIST_KEY_DOWN, parent->GetId() );
+    wxListEvent le( wxEVT_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 );
@@ -2716,6 +2762,8 @@ void wxListMainWindow::OnKeyUp( wxKeyEvent &event )
 
     // propagate the key event upwards
     wxKeyEvent ke(event);
+    ke.SetEventObject( parent );
+    ke.SetId(GetParent()->GetId());
     if (parent->GetEventHandler()->ProcessEvent( ke ))
         return;
 
@@ -2745,6 +2793,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
     // propagate the char event upwards
     wxKeyEvent ke(event);
     ke.SetEventObject( parent );
+    ke.SetId(GetParent()->GetId());
     if (parent->GetEventHandler()->ProcessEvent( ke ))
         return;
 
@@ -2770,7 +2819,8 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
             event.m_keyCode = WXK_RIGHT;
     }
 
-    switch ( event.GetKeyCode() )
+    int keyCode = event.GetKeyCode();
+    switch ( keyCode )
     {
         case WXK_UP:
             if ( m_current > 0 )
@@ -2853,7 +2903,7 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
                 }
                 else // normal space press
                 {
-                    SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+                    SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED );
                 }
             }
             else // multiple selection
@@ -2864,11 +2914,83 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
 
         case WXK_RETURN:
         case WXK_EXECUTE:
-            SendNotify( m_current, wxEVT_COMMAND_LIST_ITEM_ACTIVATED );
+            SendNotify( m_current, wxEVT_LIST_ITEM_ACTIVATED );
             break;
 
         default:
-            event.Skip();
+            if ( !event.HasModifiers() &&
+                 ((keyCode >= '0' && keyCode <= '9') ||
+                  (keyCode >= 'a' && keyCode <= 'z') ||
+                  (keyCode >= 'A' && keyCode <= 'Z') ||
+                  (keyCode == '_') ||
+                  (keyCode == '+') ||
+                  (keyCode == '*') ||
+                  (keyCode == '-')))
+            {
+                // find the next item starting with the given prefix
+                wxChar ch = (wxChar)keyCode;
+                size_t item;
+
+                // if the same character is typed multiple times then go to the
+                // next entry starting with that character instead of searching
+                // for an item starting with multiple copies of this character,
+                // this is more useful and is how it works under Windows.
+                if ( m_findPrefix.length() == 1 && m_findPrefix[0] == ch )
+                {
+                    item = PrefixFindItem(m_current, ch);
+                }
+                else
+                {
+                    const wxString newPrefix(m_findPrefix + ch);
+                    item = PrefixFindItem(m_current, newPrefix);
+                    if ( item != (size_t)-1 )
+                        m_findPrefix = newPrefix;
+                }
+
+                // also start the timer to reset the current prefix if the user
+                // doesn't press any more alnum keys soon -- we wouldn't want
+                // to use this prefix for a new item search
+                if ( !m_findTimer )
+                {
+                    m_findTimer = new wxListFindTimer( this );
+                }
+
+                // Notice that we should start the timer even if we didn't find
+                // anything to make sure we reset the search state later.
+                m_findTimer->Start(wxListFindTimer::DELAY, wxTIMER_ONE_SHOT);
+
+                // restart timer even when there's no match so bell get's reset
+                if ( item != (size_t)-1 )
+                {
+                    // Select the found item and go to it.
+                    HighlightAll(false);
+                    SetItemState(item,
+                                 wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED,
+                                 wxLIST_STATE_FOCUSED | wxLIST_STATE_SELECTED);
+
+                    // Reset the bell flag if it had been temporarily disabled
+                    // before.
+                    if ( m_findBell )
+                        m_findBell = 1;
+                }
+                else // No such item
+                {
+                    // Signal it with a bell if enabled.
+                    if ( m_findBell == 1 )
+                    {
+                        ::wxBell();
+
+                        // Disable it for the next unsuccessful match, we only
+                        // beep once, this is usually enough and continuing to
+                        // do it would be annoying.
+                        m_findBell = -1;
+                    }
+                }
+            }
+            else
+            {
+                event.Skip();
+            }
     }
 }
 
@@ -2957,17 +3079,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;
@@ -3010,18 +3121,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;
@@ -3055,29 +3190,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() );
@@ -3107,8 +3226,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;
         }
     }
 
@@ -3181,10 +3301,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)
@@ -3817,7 +3941,7 @@ void wxListMainWindow::DeleteItem( long lindex )
         ResetVisibleLinesRange();
     }
 
-    SendNotify( index, wxEVT_COMMAND_LIST_DELETE_ITEM, wxDefaultPosition );
+    SendNotify( index, wxEVT_LIST_DELETE_ITEM, wxDefaultPosition );
 
     if ( IsVirtual() )
     {
@@ -3895,7 +4019,7 @@ void wxListMainWindow::DoDeleteAllItems()
     // for all of them: this is compatible with wxMSW and documented in
     // DeleteAllItems() description
 
-    wxListEvent event( wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
+    wxListEvent event( wxEVT_LIST_DELETE_ALL_ITEMS, GetParent()->GetId() );
     event.SetEventObject( GetParent() );
     GetParent()->GetEventHandler()->ProcessEvent( event );
 
@@ -4106,20 +4230,22 @@ void wxListMainWindow::InsertItem( wxListItem &item )
     if ( HasCurrent() && m_current >= id )
         m_current++;
 
-    SendNotify(id, wxEVT_COMMAND_LIST_INSERT_ITEM);
+    SendNotify(id, wxEVT_LIST_INSERT_ITEM);
 
     RefreshLines(id, GetItemCount() - 1);
 }
 
-void wxListMainWindow::InsertColumn( long col, wxListItem &item )
+long wxListMainWindow::InsertColumn( long col, const wxListItem &item )
 {
+    long idx = -1;
+
     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());
@@ -4129,9 +4255,11 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item )
                 node = m_columns.Item( col );
             m_columns.Insert( node, column );
             m_aColWidths.Insert( colWidthInfo, col );
+            idx = col;
         }
         else
         {
+            idx = m_aColWidths.GetCount();
             m_columns.Append( column );
             m_aColWidths.Add( colWidthInfo );
         }
@@ -4153,6 +4281,7 @@ void wxListMainWindow::InsertColumn( long col, wxListItem &item )
         // invalidate it as it has to be recalculated
         m_headerWidth = 0;
     }
+    return idx;
 }
 
 int wxListMainWindow::GetItemWidthWithImage(wxListItem * item)
@@ -4283,13 +4412,68 @@ void wxListMainWindow::GetVisibleLinesRange(size_t *from, size_t *to)
         *to = m_lineTo;
 }
 
+size_t
+wxListMainWindow::PrefixFindItem(size_t idParent,
+                                 const wxString& prefixOrig) const
+{
+    // if no items then just return
+    if ( idParent == (size_t)-1 )
+        return idParent;
+
+    // match is case insensitive as this is more convenient to the user: having
+    // to press Shift-letter to go to the item starting with a capital letter
+    // would be too bothersome
+    wxString prefix = prefixOrig.Lower();
+
+    // determine the starting point: we shouldn't take the current item (this
+    // allows to switch between two items starting with the same letter just by
+    // pressing it) but we shouldn't jump to the next one if the user is
+    // continuing to type as otherwise he might easily skip the item he wanted
+    size_t itemid = idParent;
+    if ( prefix.length() == 1 )
+    {
+        itemid += 1;
+    }
+
+    // look for the item starting with the given prefix after it
+    while ( ( itemid < (size_t)GetItemCount() ) &&
+            !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
+    {
+        itemid += 1;
+    }
+
+    // if we haven't found anything...
+    if ( !( itemid < (size_t)GetItemCount() ) )
+    {
+        // ... wrap to the beginning
+        itemid = 0;
+
+        // and try all the items (stop when we get to the one we started from)
+        while ( ( itemid < (size_t)GetItemCount() ) && itemid != idParent &&
+                    !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) )
+        {
+            itemid += 1;
+        }
+        // If we haven't found the item, id will be (size_t)-1, as per
+        // documentation
+        if ( !( itemid < (size_t)GetItemCount() ) ||
+             ( ( itemid == idParent ) &&
+               !GetLine(itemid)->GetText(0).Lower().StartsWith(prefix) ) )
+        {
+            itemid = (size_t)-1;
+        }
+    }
+
+    return itemid;
+}
+
 // -------------------------------------------------------------------------------------
 // wxGenericListCtrl
 // -------------------------------------------------------------------------------------
 
 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()
@@ -4328,7 +4512,13 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded()
 
     if (needs_header)
     {
-        m_headerWin = new wxListHeaderWindow
+        // Notice that we must initialize m_headerWin first, and create the
+        // real window only later, so that the test in the beginning of the
+        // function blocks repeated creation of the header as it could happen
+        // before via wxNavigationEnabled::AddChild() -> ToggleWindowStyle() ->
+        // SetWindowStyleFlag().
+        m_headerWin = new wxListHeaderWindow();
+        m_headerWin->Create
                       (
                         this, wxID_ANY, m_mainWin,
                         wxPoint(0,0),
@@ -4339,7 +4529,7 @@ void wxGenericListCtrl::CreateOrDestroyHeaderWindowAsNeeded()
                         ),
                         wxTAB_TRAVERSAL
                       );
-
+        
 #if defined( __WXMAC__ )
         static wxFont font( wxOSX_SYSTEM_FONT_SMALL );
         m_headerWin->SetFont( font );
@@ -4369,15 +4559,12 @@ 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 );
 
@@ -4407,7 +4594,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 )
@@ -4434,13 +4621,14 @@ void wxGenericListCtrl::OnScroll(wxScrollWinEvent& event)
     // the window the next time
     m_mainWin->ResetVisibleLinesRange();
 
-    HandleOnScroll( event );
-
     if ( event.GetOrientation() == wxHORIZONTAL && HasHeader() )
     {
         m_headerWin->Refresh();
         m_headerWin->Update();
     }
+
+    // Let the window be scrolled as usual by the default handler.
+    event.Skip();
 }
 
 void wxGenericListCtrl::SetSingleStyle( long style, bool add )
@@ -4515,7 +4703,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;
@@ -4932,35 +5120,18 @@ long wxGenericListCtrl::InsertItem( long index, const wxString &label, int image
     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") );
 
-    m_mainWin->InsertColumn( col, item );
+    long idx = m_mainWin->InsertColumn( col, item );
 
     // NOTE: if wxLC_NO_HEADER was given, then we are in report view mode but
     //       still have m_headerWin==NULL
     if (m_headerWin)
         m_headerWin->Refresh();
 
-    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 );
+    return idx;
 }
 
 bool wxGenericListCtrl::ScrollList( int dx, int dy )
@@ -5127,38 +5298,10 @@ bool wxGenericListCtrl::DoPopupMenu( wxMenu *menu, int x, int y )
 #endif
 }
 
-void wxGenericListCtrl::DoClientToScreen( int *x, int *y ) const
-{
-    // 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
-        wxControl::DoClientToScreen(x, y);
-}
-
-void wxGenericListCtrl::DoScreenToClient( int *x, int *y ) const
-{
-    // 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
-        wxControl::DoScreenToClient(x, y);
-}
-
-void wxGenericListCtrl::SetFocus()
-{
-    // The test in window.cpp fails as we are a composite
-    // window, so it checks against "this", but not m_mainWin.
-    if ( DoFindFocus() != this )
-        m_mainWin->SetFocus();
-}
-
 wxSize wxGenericListCtrl::DoGetBestClientSize() const
 {
-    // Something is better than nothing even if this is completely arbitrary.
-    wxSize sizeBest(100, 80);
+    // The base class version can compute the best size in report view only.
+    wxSize sizeBest = wxListCtrlBase::DoGetBestClientSize();
 
     if ( !InReportView() )
     {
@@ -5243,16 +5386,6 @@ int wxGenericListCtrl::OnGetItemColumnImage(long item, long column) const
    return -1;
 }
 
-wxListItemAttr *
-wxGenericListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
-{
-    wxASSERT_MSG( item >= 0 && item < GetItemCount(),
-                  wxT("invalid item index in OnGetItemAttr()") );
-
-    // no attributes by default
-    return NULL;
-}
-
 void wxGenericListCtrl::SetItemCount(long count)
 {
     wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
@@ -5270,6 +5403,11 @@ void wxGenericListCtrl::RefreshItems(long itemFrom, long itemTo)
     m_mainWin->RefreshLines(itemFrom, itemTo);
 }
 
+void wxGenericListCtrl::EnableBellOnNoMatch( bool on )
+{
+    m_mainWin->EnableBellOnNoMatch(on);
+}
+
 // Generic wxListCtrl is more or less a container for two other
 // windows which drawings are done upon. These are namely
 // 'm_headerWin' and 'm_mainWin'.