]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/vlbox.cpp
Fix for probably rare but potential refcount leak.
[wxWidgets.git] / src / generic / vlbox.cpp
index e15de81c6a40a8b7284bc25b12ee8987ff711f92..0a08835aa4c5b795bada18469d7ac2aade1d8d52 100644 (file)
@@ -48,13 +48,16 @@ END_EVENT_TABLE()
 // implementation
 // ============================================================================
 
+IMPLEMENT_ABSTRACT_CLASS(wxVListBox, wxVScrolledWindow)
+
 // ----------------------------------------------------------------------------
 // wxVListBox creation
 // ----------------------------------------------------------------------------
 
 void wxVListBox::Init()
 {
-    m_current = wxNOT_FOUND;
+    m_current =
+    m_anchor = wxNOT_FOUND;
     m_selStore = NULL;
 }
 
@@ -65,14 +68,18 @@ bool wxVListBox::Create(wxWindow *parent,
                         long style,
                         const wxString& name)
 {
+    style |= wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE;
     if ( !wxVScrolledWindow::Create(parent, id, pos, size, style, name) )
         return false;
 
-    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX));
-
     if ( style & wxLB_MULTIPLE )
         m_selStore = new wxSelectionStore;
 
+    // make sure the native widget has the right colour since we do
+    // transparent drawing by default
+    SetBackgroundColour(GetBackgroundColour());
+    m_colBgSel = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
+
     return true;
 }
 
@@ -188,6 +195,10 @@ bool wxVListBox::DoSelectAll(bool select)
 
 bool wxVListBox::DoSetCurrent(int current)
 {
+    wxASSERT_MSG( current == wxNOT_FOUND ||
+                    (current >= 0 && (size_t)current < GetItemCount()),
+                  _T("wxVListBox::DoSetCurrent(): invalid item index") );
+
     if ( current == m_current )
     {
         // nothing to do
@@ -239,8 +250,15 @@ void wxVListBox::SendSelectedEvent()
 
 void wxVListBox::SetSelection(int selection)
 {
-    wxASSERT_MSG( !HasMultipleSelection(),
-                  _T("SetSelection() is invalid with multiselection listbox") );
+    wxCHECK_RET( selection == wxNOT_FOUND ||
+                  (selection >= 0 && (size_t)selection < GetItemCount()),
+                  _T("wxVListBox::SetSelection(): invalid item index") );
+
+    if ( HasMultipleSelection() )
+    {
+        Select(selection);
+        m_anchor = selection;
+    }
 
     DoSetCurrent(selection);
 }
@@ -273,7 +291,7 @@ int wxVListBox::GetNextSelected(unsigned long& cookie) const
 }
 
 // ----------------------------------------------------------------------------
-// wxVListBox painting
+// wxVListBox appearance parameters
 // ----------------------------------------------------------------------------
 
 void wxVListBox::SetMargins(const wxPoint& pt)
@@ -286,6 +304,15 @@ void wxVListBox::SetMargins(const wxPoint& pt)
     }
 }
 
+void wxVListBox::SetSelectionBackground(const wxColour& col)
+{
+    m_colBgSel = col;
+}
+
+// ----------------------------------------------------------------------------
+// wxVListBox painting
+// ----------------------------------------------------------------------------
+
 wxCoord wxVListBox::OnGetLineHeight(size_t line) const
 {
     return OnMeasureItem(line) + 2*m_ptMargins.y;
@@ -297,7 +324,30 @@ void wxVListBox::OnDrawSeparator(wxDC& WXUNUSED(dc),
 {
 }
 
-void wxVListBox::OnPaint(wxPaintEvent& event)
+void wxVListBox::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
+{
+    // we need to render selected and current items differently
+    const bool isSelected = IsSelected(n),
+               isCurrent = IsCurrent(n);
+    if ( isSelected || isCurrent )
+    {
+        if ( isSelected )
+        {
+            dc.SetBrush(wxBrush(m_colBgSel, wxSOLID));
+        }
+        else // !selected
+        {
+            dc.SetBrush(*wxTRANSPARENT_BRUSH);
+        }
+
+        dc.SetPen(*(isCurrent ? wxBLACK_PEN : wxTRANSPARENT_PEN));
+
+        dc.DrawRectangle(rect);
+    }
+    //else: do nothing for the normal items
+}
+
+void wxVListBox::OnPaint(wxPaintEvent& WXUNUSED(event))
 {
     wxPaintDC dc(this);
 
@@ -322,28 +372,9 @@ void wxVListBox::OnPaint(wxPaintEvent& event)
             // don't allow drawing outside of the lines rectangle
             wxDCClipper clip(dc, rectLine);
 
-            // we need to render selected and current items differently
-            const bool isSelected = IsSelected(line);
-            if ( isSelected || IsCurrent(line) )
-            {
-                if ( isSelected )
-                {
-                    wxBrush brush(wxSystemSettings::
-                                    GetColour(wxSYS_COLOUR_HIGHLIGHT),
-                                    wxSOLID);
-                    dc.SetBrush(brush);
-                }
-                else // !selected
-                {
-                    dc.SetBrush(*wxTRANSPARENT_BRUSH);
-                }
-
-                dc.SetPen(*(IsCurrent(line) ? wxBLACK_PEN : wxTRANSPARENT_PEN));
-
-                dc.DrawRectangle(rectLine);
-            }
-
             wxRect rect = rectLine;
+            OnDrawBackground(dc, rect, line);
+
             OnDrawSeparator(dc, rect, line);
 
             rect.Deflate(m_ptMargins.x, m_ptMargins.y);
@@ -368,7 +399,7 @@ void wxVListBox::OnPaint(wxPaintEvent& event)
 // wxVListBox keyboard/mouse handling
 // ============================================================================
 
-void wxVListBox::DoHandleItemClick(int item, bool shiftDown, bool ctrlDown)
+void wxVListBox::DoHandleItemClick(int item, int flags)
 {
     // has anything worth telling the client code about happened?
     bool notify = false;
@@ -381,32 +412,44 @@ void wxVListBox::DoHandleItemClick(int item, bool shiftDown, bool ctrlDown)
         // NB: the keyboard interface we implement here corresponds to
         //     wxLB_EXTENDED rather than wxLB_MULTIPLE but this one makes more
         //     sense IMHO
-        if ( shiftDown )
+        if ( flags & ItemClick_Shift )
         {
             if ( m_current != wxNOT_FOUND )
             {
+                if ( m_anchor == wxNOT_FOUND )
+                    m_anchor = m_current;
+
                 select = false;
 
-                // only the range from old m_current to new m_current must be
-                // selected
+                // only the range from the selection anchor to new m_current
+                // must be selected
                 if ( DeselectAll() )
                     notify = true;
 
-                if ( SelectRange(m_current, item) )
+                if ( SelectRange(m_anchor, item) )
                     notify = true;
             }
             //else: treat it as ordinary click/keypress
         }
-        else if ( ctrlDown )
+        else // Shift not pressed
         {
-            select = false;
+            m_anchor = item;
 
-            Toggle(item);
+            if ( flags & ItemClick_Ctrl )
+            {
+                select = false;
 
-            // the status of the item has definitely changed
-            notify = true;
+                if ( !(flags & ItemClick_Kbd) )
+                {
+                    Toggle(item);
+
+                    // the status of the item has definitely changed
+                    notify = true;
+                }
+                //else: Ctrl-arrow pressed, don't change selection
+            }
+            //else: behave as in single selection case
         }
-        //else: behave as in single selection case
 
         if ( select )
         {
@@ -443,7 +486,10 @@ void wxVListBox::DoHandleItemClick(int item, bool shiftDown, bool ctrlDown)
 
 void wxVListBox::OnKeyDown(wxKeyEvent& event)
 {
-    int current = 0; // just to silent the stupid compiler warnings
+    // flags for DoHandleItemClick()
+    int flags = ItemClick_Kbd;
+
+    int current;
     switch ( event.GetKeyCode() )
     {
         case WXK_HOME:
@@ -486,12 +532,40 @@ void wxVListBox::OnKeyDown(wxKeyEvent& event)
             current = GetFirstVisibleLine();
             break;
 
+        case WXK_SPACE:
+            // hack: pressing space should work like a mouse click rather than
+            // like a keyboard arrow press, so trick DoHandleItemClick() in
+            // thinking we were clicked
+            flags &= ~ItemClick_Kbd;
+            current = m_current;
+            break;
+
+#ifdef __WXMSW__
+        case WXK_TAB:
+            // Since we are using wxWANTS_CHARS we need to send navigation
+            // events for the tabs on MSW
+            {
+                wxNavigationKeyEvent ne;
+                ne.SetDirection(!event.ShiftDown());
+                ne.SetCurrentFocus(this);
+                ne.SetEventObject(this);
+                GetParent()->GetEventHandler()->ProcessEvent(ne);
+            }
+            // fall through to default
+#endif
         default:
             event.Skip();
+            current = 0; // just to silent the stupid compiler warnings
+            wxUnusedVar(current);
             return;
     }
 
-    DoHandleItemClick(current, event.ShiftDown(), event.ControlDown());
+    if ( event.ShiftDown() )
+       flags |= ItemClick_Shift;
+    if ( event.ControlDown() )
+        flags |= ItemClick_Ctrl;
+
+    DoHandleItemClick(current, flags);
 }
 
 // ----------------------------------------------------------------------------
@@ -502,13 +576,23 @@ void wxVListBox::OnLeftDown(wxMouseEvent& event)
 {
     int item = HitTest(event.GetPosition());
 
-    DoHandleItemClick(item, event.ShiftDown(),
+    if ( item != wxNOT_FOUND )
+    {
+        int flags = 0;
+        if ( event.ShiftDown() )
+           flags |= ItemClick_Shift;
+
+        // under Mac Apple-click is used in the same way as Ctrl-click
+        // elsewhere
 #ifdef __WXMAC__
-                            event.MetaDown()
+        if ( event.MetaDown() )
 #else
-                            event.ControlDown()
+        if ( event.ControlDown() )
 #endif
-                     );
+            flags |= ItemClick_Ctrl;
+
+        DoHandleItemClick(item, flags);
+    }
 }
 
 void wxVListBox::OnLeftDClick(wxMouseEvent& event)
@@ -524,3 +608,16 @@ void wxVListBox::OnLeftDClick(wxMouseEvent& event)
     }
 }
 
+
+// ----------------------------------------------------------------------------
+// use the same default attributes as wxListBox
+// ----------------------------------------------------------------------------
+
+#include "wx/listbox.h"
+
+//static
+wxVisualAttributes
+wxVListBox::GetClassDefaultAttributes(wxWindowVariant variant)
+{
+    return wxListBox::GetClassDefaultAttributes(variant);
+}