]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
Interface fixes and tweaks for Phoenix
[wxWidgets.git] / src / generic / listctrl.cpp
index c9915b198ef3957415be737c57152cc64d92c0a6..481654ecd854bf43afab5d1edeaa2dd9f01a063c 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"
@@ -667,11 +658,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 +680,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,45 +709,25 @@ 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) )
+    // background
+    if ( highlighted )
     {
-        int flags = 0;
-        if (highlighted)
-            flags |= wxCONTROL_SELECTED;
-        if (m_owner->HasFocus()
-#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON
-            && IsControlActive( (ControlRef)m_owner->GetHandle() )
-#endif
-        )
+        // 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, m_gi->m_rectHighlight, flags );
+            DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
+    }
+    else if ( attr && attr->HasBackgroundColour() )
+    {
+        // Draw the background using the items custom background colour.
+        dc->SetBrush(attr->GetBackgroundColour());
+        dc->SetPen(*wxTRANSPARENT_PEN);
+        dc->DrawRectangle(rectHL);
     }
 
     // just for debugging to better see where the items are
@@ -759,6 +738,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())
@@ -788,18 +775,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) )
-    {
-        int flags = 0;
-        if (highlighted)
-            flags |= wxCONTROL_SELECTED;
-        if (m_owner->HasFocus())
-            flags |= wxCONTROL_FOCUSED;
-        if (current)
-           flags |= wxCONTROL_CURRENT;
-        wxRendererNative::Get().DrawItemSelectionRect( m_owner, *dc, rectHL, flags );
-    }
+
+    ApplyAttributes(dc, rectHL, highlighted, current);
 
     wxCoord x = rect.x + HEADER_OFFSET_X,
             yMid = rect.y + rect.height/2;
@@ -821,7 +798,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() )
@@ -837,7 +815,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);
     }
 }
 
@@ -1068,7 +1046,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) &&
@@ -1424,23 +1402,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;
+
+        case End_Discard:
+            m_owner->OnRenameCancelled(m_itemEdited);
 
-        // Even if vetoed, close the control (consistent with MSW)
-        Finish( true );
+            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;
     }
 }
 
@@ -1475,20 +1461,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 )
@@ -1532,6 +1526,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)
@@ -1617,6 +1612,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);
@@ -2110,7 +2108,7 @@ 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 );
         }
     }
 
@@ -2290,7 +2288,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() )
@@ -2699,6 +2697,16 @@ 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_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();
 }
 
@@ -2714,21 +2722,26 @@ 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 );
+        // 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 );
@@ -3168,10 +3181,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)
@@ -3616,6 +3633,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;
@@ -3637,8 +3657,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;
@@ -3647,10 +3683,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) )
                     {
@@ -3823,6 +3855,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);
@@ -4039,6 +4086,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 );
 
@@ -4794,6 +4854,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;
 }
 
@@ -4865,7 +4929,9 @@ 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 );
 }
@@ -5067,12 +5133,22 @@ 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
+        wxControl::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
+        wxControl::DoScreenToClient(x, y);
 }
 
 void wxGenericListCtrl::SetFocus()