]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
revert togglable tools to previous state if the change was vetoed by OnLeftClick()
[wxWidgets.git] / src / generic / listctrl.cpp
index c561337dae48f30ce5cd34b0e08008a7946917e3..2e9852802b718ad4b663883436352aa55758df0b 100644 (file)
 #endif // HAVE_NATIVE_LISTCTRL/!HAVE_NATIVE_LISTCTRL
 
 #include "wx/selstore.h"
-
 #include "wx/renderer.h"
+#include "wx/math.h"
 
 #ifdef __WXMAC__
     #include "wx/mac/private.h"
 #endif
 
+
+
 // NOTE: If using the wxListBox visual attributes works everywhere then this can
 // be removed, as well as the #else case below.
 #define _USE_VISATTR 0
@@ -91,8 +93,10 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_END_LABEL_EDIT)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ITEM)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS)
+#if WXWIN_COMPATIBILITY_2_4
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_GET_INFO)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_SET_INFO)
+#endif
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_SELECTED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN)
@@ -161,7 +165,7 @@ public:
 
     void SetItem( const wxListItem &info );
     void SetImage( int image ) { m_image = image; }
-    void SetData( long data ) { m_data = data; }
+    void SetData( wxUIntPtr data ) { m_data = data; }
     void SetPosition( int x, int y );
     void SetSize( int width, int height );
 
@@ -200,7 +204,7 @@ public:
     int m_image;
 
     // user data associated with the item
-    long m_data;
+    wxUIntPtr m_data;
 
     // the item coordinates are not used in report mode, instead this pointer
     // is NULL and the owner window is used to retrieve the item position and
@@ -490,6 +494,7 @@ private:
     wxString            m_startValue;
     size_t              m_itemEdited;
     bool                m_finished;
+    bool                m_aboutToFinish;
 
     DECLARE_EVENT_TABLE()
 };
@@ -515,6 +520,8 @@ public:
 
     virtual ~wxListMainWindow();
 
+    wxWindow *GetMainWindowOfCompositeControl() { return GetParent(); }
+
     bool HasFlag(int flag) const { return m_parent->HasFlag(flag); }
 
     // return true if this is a virtual list control
@@ -599,8 +606,6 @@ public:
     void Freeze();
     void Thaw();
 
-    void SetFocus();
-
     void OnRenameTimer();
     bool OnRenameAccept(size_t itemEdit, const wxString& value);
     void OnRenameCancelled(size_t itemEdit);
@@ -640,6 +645,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;
@@ -676,7 +682,8 @@ public:
     void DeleteEverything();
     void EnsureVisible( long index );
     long FindItem( long start, const wxString& str, bool partial = false );
-    long FindItem( long start, long data);
+    long FindItem( long start, wxUIntPtr data);
+    long FindItem( const wxPoint& pt );
     long HitTest( int x, int y, int &flags );
     void InsertItem( wxListItem &item );
     void InsertColumn( long col, wxListItem &item );
@@ -764,7 +771,8 @@ public:
 
     // for double click logic
     size_t m_lineLastClicked,
-           m_lineBeforeLastClicked;
+           m_lineBeforeLastClicked,
+           m_lineSelectSingleOnUp;
 
 protected:
     // the total count of items in a virtual list control
@@ -1668,11 +1676,13 @@ wxListHeaderWindow::wxListHeaderWindow( wxWindow *win,
     wxVisualAttributes attr = wxPanel::GetClassDefaultAttributes();
     SetOwnForegroundColour( attr.colFg );
     SetOwnBackgroundColour( attr.colBg );
-    SetOwnFont( attr.font );
+    if (!m_hasFont)
+        SetOwnFont( attr.font );
 #else
     SetOwnForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
     SetOwnBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
-    SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
+    if (!m_hasFont)
+        SetOwnFont( wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT ));
 #endif
 }
 
@@ -1731,15 +1741,20 @@ 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
-                                                          : wxCONTROL_DISABLED
+                                                          : (int)wxCONTROL_DISABLED
                                 );
 
         // see if we have enough space for the column label
@@ -2018,6 +2033,7 @@ wxListTextCtrl::wxListTextCtrl(wxListMainWindow *owner, size_t itemEdit)
 {
     m_owner = owner;
     m_finished = false;
+    m_aboutToFinish = false;
 
     wxRect rectLabel = owner->GetLineLabelRect(itemEdit);
 
@@ -2037,7 +2053,7 @@ void wxListTextCtrl::Finish()
 
         m_finished = true;
 
-        m_owner->SetFocus();
+        m_owner->SetFocusIgnoringChildren();
     }
 }
 
@@ -2068,12 +2084,12 @@ void wxListTextCtrl::OnChar( wxKeyEvent &event )
     switch ( event.m_keyCode )
     {
         case WXK_RETURN:
-            if ( !AcceptChanges() )
-            {
-                // vetoed by the user code
-                break;
-            }
-            //else: fall through
+            m_aboutToFinish = true;
+            // Notify the owner about the changes
+            AcceptChanges();
+            // Even if vetoed, close the control (consistent with MSW)
+            Finish();
+            break;
 
         case WXK_ESCAPE:
             Finish();
@@ -2103,7 +2119,7 @@ void wxListTextCtrl::OnKeyUp( wxKeyEvent &event )
         sx = parentSize.x - myPos.x;
     if (mySize.x > sx)
         sx = mySize.x;
-    SetSize(sx, wxDefaultSize.y);
+    SetSize(sx, wxDefaultCoord);
 
     event.Skip();
 }
@@ -2112,13 +2128,16 @@ void wxListTextCtrl::OnKillFocus( wxFocusEvent &event )
 {
     if ( !m_finished )
     {
-        // We must finish regardless of success, otherwise we'll get focus problems
+        // We must finish regardless of success, otherwise we'll get
+        // focus problems:
         Finish();
 
         if ( !AcceptChanges() )
             m_owner->OnRenameCancelled( m_itemEdited );
     }
 
+    // We must let the native text control handle focus, too, otherwise
+    // it could have problems with the cursor (e.g., in wxGTK).
     event.Skip();
 }
 
@@ -2164,6 +2183,7 @@ void wxListMainWindow::Init()
 
     m_current =
     m_lineLastClicked =
+    m_lineSelectSingleOnUp =
     m_lineBeforeLastClicked = (size_t)-1;
 
     m_freezeCount = 0;
@@ -2214,7 +2234,8 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent,
     wxVisualAttributes attr = wxGenericListCtrl::GetClassDefaultAttributes();
     SetOwnForegroundColour( attr.colFg );
     SetOwnBackgroundColour( attr.colBg );
-    SetOwnFont( attr.font );
+    if (!m_hasFont)
+        SetOwnFont( attr.font );
 }
 
 wxListMainWindow::~wxListMainWindow()
@@ -2861,6 +2882,13 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
     if ( GetParent()->GetEventHandler()->ProcessEvent( event) )
         return;
 
+    if (event.GetEventType() == wxEVT_MOUSEWHEEL)
+    {
+        // let the base handle mouse wheel events.
+        event.Skip();
+        return;
+    }
+
     if ( !HasCurrent() || IsEmpty() )
         return;
 
@@ -2919,7 +2947,7 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
 
         wxListEvent le( command, GetParent()->GetId() );
         le.SetEventObject( GetParent() );
-        le.m_itemIndex = current;
+        le.m_itemIndex = m_lineLastClicked;
         le.m_pointDrag = m_dragStart;
         GetParent()->GetEventHandler()->ProcessEvent( le );
 
@@ -2950,26 +2978,56 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         }
         else
         {
-            // the first click was on another item, so don't interpret this as
+            // The first click was on another item, so don't interpret this as
             // a double click, but as a simple click instead
             forceClick = true;
         }
     }
 
-    if (event.LeftUp() && m_lastOnSame)
+    if (event.LeftUp())
     {
-        if ((current == m_current) &&
-            (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
-            HasFlag(wxLC_EDIT_LABELS)  )
+        if(m_lineSelectSingleOnUp != (size_t) -1)
+        {
+            // select single line
+            HighlightAll( false );
+            ReverseHighlight(m_lineSelectSingleOnUp);
+        }
+        else if (m_lastOnSame)
         {
-            m_renameTimer->Start( 100, true );
+            if ((current == m_current) &&
+                (hitResult == wxLIST_HITTEST_ONITEMLABEL) &&
+                HasFlag(wxLC_EDIT_LABELS)  )
+            {
+                m_renameTimer->Start( 100, true );
+            }
         }
         m_lastOnSame = false;
+        m_lineSelectSingleOnUp = (size_t) -1;
+    }
+    else
+    {
+        // This is neccessary , 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.
+        m_lineSelectSingleOnUp = (size_t) -1;
     }
-    else if (event.RightDown())
+    if (event.RightDown())
     {
+        m_lineBeforeLastClicked = m_lineLastClicked;
+        m_lineLastClicked = current;
+        // If the item is already selected, do not update the selection.
+        // Multi-selections should not be cleared if a selected item is clicked.
+        if (!IsHighlighted(current))
+        {
+            HighlightAll(false);
+            ChangeCurrent(current);
+            ReverseHighlight(m_current);
+        }
         SendNotify( current, wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK,
                     event.GetPosition() );
+        // Allow generation of context menu event
+        event.Skip();
     }
     else if (event.MiddleDown())
     {
@@ -2981,19 +3039,22 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
         m_lineLastClicked = current;
 
         size_t oldCurrent = m_current;
-        bool cmdModifierDown;
-#ifdef __WXMAC__
-        cmdModifierDown = event.MetaDown();
-#else
-        cmdModifierDown = event.ControlDown();
-#endif
+        bool cmdModifierDown = event.CmdDown();
         if ( IsSingleSel() || !(cmdModifierDown || event.ShiftDown()) )
         {
-            HighlightAll( false );
+            if( IsSingleSel() || !IsHighlighted(current) )
+            {
+                HighlightAll( false );
 
-            ChangeCurrent(current);
+                ChangeCurrent(current);
 
-            ReverseHighlight(m_current);
+                ReverseHighlight(m_current);
+            }
+            else // multi sel & current is highlighted & no mod keys
+            {
+                m_lineSelectSingleOnUp = current;
+                ChangeCurrent(current); // change focus
+            }
         }
         else // multi sel & either ctrl or shift is down
         {
@@ -3288,26 +3349,6 @@ void wxListMainWindow::OnChar( wxKeyEvent &event )
 // focus handling
 // ----------------------------------------------------------------------------
 
-void wxListMainWindow::SetFocus()
-{
-    // VS: wxListMainWindow derives from wxPanel (via wxScrolledWindow) and wxPanel
-    //     overrides SetFocus in such way that it does never change focus from
-    //     panel's child to the panel itself. Unfortunately, we must be able to change
-    //     focus to the panel from wxListTextCtrl because the text control should
-    //     disappear when the user clicks outside it.
-
-    wxWindow *oldFocus = FindFocus();
-
-    if ( oldFocus && oldFocus->GetParent() == this )
-    {
-        wxWindow::SetFocus();
-    }
-    else
-    {
-        wxScrolledWindow::SetFocus();
-    }
-}
-
 void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
 {
     if ( GetParent() )
@@ -3601,8 +3642,58 @@ 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 )
 {
+    if ( litem == -1 )
+    {
+        SetItemStateAll(state, stateMask);
+        return;
+    }
+
      wxCHECK_RET( litem >= 0 && (size_t)litem < GetItemCount(),
                   _T("invalid list ctrl item index in SetItem") );
 
@@ -3857,7 +3948,7 @@ void wxListMainWindow::RecalculatePositions(bool noRefresh)
         iconSpacing = 0;
 
     // Note that we do not call GetClientSize() here but
-    // GetSize() and substract the border size for sunken
+    // GetSize() and subtract the border size for sunken
     // borders manually. This is technically incorrect,
     // but we need to know the client area's size WITHOUT
     // scrollbars here. Since we don't know if there are
@@ -4264,7 +4355,7 @@ long wxListMainWindow::FindItem(long start, const wxString& str, bool WXUNUSED(p
     return wxNOT_FOUND;
 }
 
-long wxListMainWindow::FindItem(long start, long data)
+long wxListMainWindow::FindItem(long start, wxUIntPtr data)
 {
     long pos = start;
     if (pos < 0)
@@ -4283,6 +4374,22 @@ long wxListMainWindow::FindItem(long start, long data)
     return wxNOT_FOUND;
 }
 
+long wxListMainWindow::FindItem( const wxPoint& pt )
+{
+    size_t topItem;
+    GetVisibleLinesRange(&topItem, NULL);
+
+    wxPoint p;
+    GetItemPosition( GetItemCount()-1, p );
+    if( p.y == 0 )
+        return topItem;
+    long id = (long) floor( pt.y*double(GetItemCount()-topItem-1)/p.y+topItem );
+    if( id >= 0 && id < (long)GetItemCount() )
+        return id;
+
+    return wxNOT_FOUND;
+}
+
 long wxListMainWindow::HitTest( int x, int y, int &flags )
 {
     CalcUnscrolledPosition( x, y, &x, &y );
@@ -4322,9 +4429,11 @@ void wxListMainWindow::InsertItem( wxListItem &item )
 {
     wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual control") );
 
-    size_t count = GetItemCount();
-    wxCHECK_RET( item.m_itemId >= 0 && (size_t)item.m_itemId <= count,
-                 _T("invalid item index") );
+    int count = GetItemCount();
+    wxCHECK_RET( item.m_itemId >= 0, _T("invalid item index") );
+
+    if (item.m_itemId > count)
+        item.m_itemId = count;
 
     size_t id = item.m_itemId;
 
@@ -4370,7 +4479,7 @@ void wxListMainWindow::InsertItem( wxListItem &item )
 
     wxListLineData *line = new wxListLineData(this);
 
-    line->SetItem( 0, item );
+    line->SetItem( item.m_col, item );
 
     m_lines.Insert( line, id );
 
@@ -4441,9 +4550,9 @@ int LINKAGEMODE list_ctrl_compare_func_1( wxListLineData **arg1, wxListLineData
     wxListLineData *line2 = *arg2;
     wxListItem item;
     line1->GetItem( 0, item );
-    long data1 = item.m_data;
+    wxUIntPtr data1 = item.m_data;
     line2->GetItem( 0, item );
-    long data2 = item.m_data;
+    wxUIntPtr data2 = item.m_data;
     return list_ctrl_compare_func_2( data1, data2, list_ctrl_compare_data );
 }
 
@@ -4570,11 +4679,15 @@ void wxGenericListCtrl::CalculateAndSetHeaderHeight()
 {
     if ( m_headerWin )
     {
+#ifdef __WXMAC__
+        SInt32 h ;
+        GetThemeMetric( kThemeMetricListHeaderHeight, &h );
+#else
         // we use 'g' to get the descent, too
         int w, h, d;
         m_headerWin->GetTextExtent(wxT("Hg"), &w, &h, &d);
         h += d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT;
-
+#endif
         // only update if changed
         if ( h != m_headerHeight )
         {
@@ -4593,7 +4706,7 @@ void wxGenericListCtrl::CreateHeaderWindow()
     m_headerWin = new wxListHeaderWindow
                       (
                         this, wxID_ANY, m_mainWin,
-                        wxPoint(0, 0),
+                        wxPoint(0,0),
                         wxSize(GetClientSize().x, m_headerHeight),
                         wxTAB_TRAVERSAL
                       );
@@ -4633,15 +4746,27 @@ bool wxGenericListCtrl::Create(wxWindow *parent,
 
     m_mainWin = new wxListMainWindow( this, wxID_ANY, wxPoint(0,0), size, style );
 
-#if defined( __WXMAC__ ) && __WXMAC_CARBON__
-    wxFont font ;
-    font.MacCreateThemeFont( kThemeViewsFont ) ;
-    SetFont( font ) ;
+#ifdef  __WXMAC_CARBON__
+    // Human Interface Guidelines ask us for a special font in this case
+    if ( GetWindowVariant() == wxWINDOW_VARIANT_NORMAL )
+    {
+        wxFont font ;
+        font.MacCreateThemeFont( kThemeViewsFont ) ;
+        SetFont( font ) ;
+    }
 #endif
     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?
@@ -4791,7 +4916,8 @@ bool wxGenericListCtrl::SetItemState( long item, long state, long stateMask )
     return true;
 }
 
-bool wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
+bool
+wxGenericListCtrl::SetItemImage( long item, int image, int WXUNUSED(selImage) )
 {
     wxListItem info;
     info.m_image = image;
@@ -4811,7 +4937,7 @@ void wxGenericListCtrl::SetItemText( long item, const wxString& str )
     m_mainWin->SetItemText(item, str);
 }
 
-long wxGenericListCtrl::GetItemData( long item ) const
+wxUIntPtr wxGenericListCtrl::GetItemData( long item ) const
 {
     wxListItem info;
     info.m_itemId = item;
@@ -5051,15 +5177,15 @@ long wxGenericListCtrl::FindItem( long start, const wxString& str,  bool partial
     return m_mainWin->FindItem( start, str, partial );
 }
 
-long wxGenericListCtrl::FindItem( long start, long data )
+long wxGenericListCtrl::FindItem( long start, wxUIntPtr data )
 {
     return m_mainWin->FindItem( start, data );
 }
 
-long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& WXUNUSED(pt),
+long wxGenericListCtrl::FindItem( long WXUNUSED(start), const wxPoint& pt,
                            int WXUNUSED(direction))
 {
-    return 0;
+    return m_mainWin->FindItem( pt );
 }
 
 long wxGenericListCtrl::HitTest( const wxPoint &point, int &flags )
@@ -5322,10 +5448,18 @@ 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 ( FindFocus() != this )
+    if ( DoFindFocus() != this )
         m_mainWin->SetFocus();
 }
 
+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);
+}
+
 // ----------------------------------------------------------------------------
 // virtual list control support
 // ----------------------------------------------------------------------------
@@ -5341,9 +5475,9 @@ wxString wxGenericListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col
 
 int wxGenericListCtrl::OnGetItemImage(long WXUNUSED(item)) const
 {
-    // same as above
-    wxFAIL_MSG( _T("wxGenericListCtrl::OnGetItemImage not supposed to be called") );
-
+    wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
+                -1,
+                wxT("List control has an image list, OnGetItemImage should be overridden."));
     return -1;
 }