]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/listctrl.cpp
applied (slightly modified) wxGLApp patch for MSW
[wxWidgets.git] / src / generic / listctrl.cpp
index 2744044c86fbd12e0dc895a9547b174a2158dd48..38ff1e693c40674e2d09770bf763ca54803abf1e 100644 (file)
@@ -12,7 +12,7 @@
    TODO
 
    1. we need to implement searching/sorting for virtual controls somehow
-   2. when changing selection the lines are refreshed twice
+  ?2. when changing selection the lines are refreshed twice
  */
 
 // ============================================================================
@@ -65,6 +65,10 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_INSERT_ITEM)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_CLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_RIGHT_CLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_BEGIN_DRAG)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_DRAGGING)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_COL_END_DRAG)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
@@ -99,6 +103,9 @@ static const int AUTOSIZE_COL_MARGIN = 10;
 static const int WIDTH_COL_DEFAULT = 80;
 static const int WIDTH_COL_MIN = 10;
 
+// the space between the image and the text in the report mode
+static const int IMAGE_MARGIN_IN_REPORT_MODE = 5;
+
 // ============================================================================
 // private classes
 // ============================================================================
@@ -257,16 +264,6 @@ protected:
 
 class WXDLLEXPORT wxListHeaderData : public wxObject
 {
-protected:
-    long      m_mask;
-    int       m_image;
-    wxString  m_text;
-    int       m_format;
-    int       m_width;
-    int       m_xpos,
-              m_ypos;
-    int       m_height;
-
 public:
     wxListHeaderData();
     wxListHeaderData( const wxListItem &info );
@@ -288,8 +285,18 @@ public:
     int GetWidth() const;
     int GetFormat() const;
 
+protected:
+    long      m_mask;
+    int       m_image;
+    wxString  m_text;
+    int       m_format;
+    int       m_width;
+    int       m_xpos,
+              m_ypos;
+    int       m_height;
+
 private:
-    DECLARE_DYNAMIC_CLASS(wxListHeaderData);
+    void Init();
 };
 
 //-----------------------------------------------------------------------------
@@ -432,7 +439,6 @@ protected:
 
 public:
     wxListHeaderWindow();
-    virtual ~wxListHeaderWindow();
 
     wxListHeaderWindow( wxWindow *win,
                         wxWindowID id,
@@ -442,6 +448,8 @@ public:
                         long style = 0,
                         const wxString &name = "wxlistctrlcolumntitles" );
 
+    virtual ~wxListHeaderWindow();
+
     void DoDrawRect( wxDC *dc, int x, int y, int w, int h );
     void DrawCurrent();
     void AdjustDC(wxDC& dc);
@@ -454,6 +462,9 @@ public:
     bool m_dirty;
 
 private:
+    // common part of all ctors
+    void Init();
+
     DECLARE_DYNAMIC_CLASS(wxListHeaderWindow)
     DECLARE_EVENT_TABLE()
 };
@@ -562,6 +573,9 @@ public:
     void RefreshLine( size_t line );
     void RefreshLines( size_t lineFrom, size_t lineTo );
 
+    // refresh all selected items
+    void RefreshSelected();
+
     // refresh all lines below the given one: the difference with
     // RefreshLines() is that the index here might not be a valid one (happens
     // when the last line is deleted)
@@ -697,6 +711,12 @@ public:
     // get the y position of the given line (only for report view)
     wxCoord GetLineY(size_t line) const;
 
+    // get the brush to use for the item highlighting
+    wxBrush *GetHighlightBrush() const
+    {
+        return m_hasFocus ? m_highlightBrush : m_highlightUnfocusedBrush;
+    }
+
 //protected:
     // the array of all line objects for a non virtual list control
     wxListLineDataArray  m_lines;
@@ -721,7 +741,6 @@ public:
     // call
     bool                 m_dirty;
 
-    wxBrush             *m_highlightBrush;
     wxColour            *m_highlightColour;
     int                  m_xScroll,
                          m_yScroll;
@@ -820,6 +839,10 @@ private:
     size_t m_lineFrom,
            m_lineTo;
 
+    // the brushes to use for item highlighting when we do/don't have focus
+    wxBrush *m_highlightBrush,
+            *m_highlightUnfocusedBrush;
+
     DECLARE_DYNAMIC_CLASS(wxListMainWindow);
     DECLARE_EVENT_TABLE()
 };
@@ -1143,12 +1166,10 @@ void wxListItemData::GetItem( wxListItem &info ) const
 //  wxListHeaderData
 //-----------------------------------------------------------------------------
 
-IMPLEMENT_DYNAMIC_CLASS(wxListHeaderData,wxObject);
-
-wxListHeaderData::wxListHeaderData()
+void wxListHeaderData::Init()
 {
     m_mask = 0;
-    m_image = 0;
+    m_image = -1;
     m_format = 0;
     m_width = 0;
     m_xpos = 0;
@@ -1156,22 +1177,33 @@ wxListHeaderData::wxListHeaderData()
     m_height = 0;
 }
 
+wxListHeaderData::wxListHeaderData()
+{
+    Init();
+}
+
 wxListHeaderData::wxListHeaderData( const wxListItem &item )
 {
+    Init();
+
     SetItem( item );
-    m_xpos = 0;
-    m_ypos = 0;
-    m_height = 0;
 }
 
 void wxListHeaderData::SetItem( const wxListItem &item )
 {
     m_mask = item.m_mask;
-    m_text = item.m_text;
-    m_image = item.m_image;
-    m_format = item.m_format;
 
-    SetWidth(item.m_width);
+    if ( m_mask & wxLIST_MASK_TEXT )
+        m_text = item.m_text;
+
+    if ( m_mask & wxLIST_MASK_IMAGE )
+        m_image = item.m_image;
+
+    if ( m_mask & wxLIST_MASK_FORMAT )
+        m_format = item.m_format;
+
+    if ( m_mask & wxLIST_MASK_WIDTH )
+        SetWidth(item.m_width);
 }
 
 void wxListHeaderData::SetPosition( int x, int y )
@@ -1190,7 +1222,7 @@ void wxListHeaderData::SetWidth( int w )
     m_width = w;
     if (m_width < 0)
         m_width = WIDTH_COL_DEFAULT;
-    if (m_width < WIDTH_COL_MIN)
+    else if (m_width < WIDTH_COL_MIN)
         m_width = WIDTH_COL_MIN;
 }
 
@@ -1201,7 +1233,7 @@ void wxListHeaderData::SetFormat( int format )
 
 bool wxListHeaderData::HasImage() const
 {
-    return (m_image != 0);
+    return m_image != -1;
 }
 
 bool wxListHeaderData::IsHit( int x, int y ) const
@@ -1209,7 +1241,7 @@ bool wxListHeaderData::IsHit( int x, int y ) const
     return ((x >= m_xpos) && (x <= m_xpos+m_width) && (y >= m_ypos) && (y <= m_ypos+m_height));
 }
 
-void wxListHeaderData::GetItem( wxListItem &item )
+void wxListHeaderData::GetItem( wxListItemitem )
 {
     item.m_mask = m_mask;
     item.m_text = m_text;
@@ -1582,7 +1614,7 @@ bool wxListLineData::SetAttributes(wxDC *dc,
     {
         if ( highlighted )
         {
-            dc->SetBrush( *m_owner->m_highlightBrush );
+            dc->SetBrush( *m_owner->GetHighlightBrush() );
         }
         else
         {
@@ -1622,6 +1654,8 @@ void wxListLineData::Draw( wxDC *dc )
     if (item->HasText())
     {
         wxRect rectLabel = m_gi->m_rectLabel;
+
+        wxDCClipper clipper(*dc, rectLabel);
         dc->DrawText( item->GetText(), rectLabel.x, rectLabel.y );
     }
 }
@@ -1651,27 +1685,29 @@ void wxListLineData::DrawInReportMode( wxDC *dc,
     {
         wxListItemData *item = node->GetData();
 
+        int width = m_owner->GetColumnWidth(col++);
         int xOld = x;
+        x += width;
 
         if ( item->HasImage() )
         {
             int ix, iy;
-            m_owner->DrawImage( item->GetImage(), dc, x, y );
+            m_owner->DrawImage( item->GetImage(), dc, xOld, y );
             m_owner->GetImageSize( item->GetImage(), ix, iy );
-            x += ix + 5; // FIXME: what is "5"?
-        }
 
-        int width = m_owner->GetColumnWidth(col++);
+            ix += IMAGE_MARGIN_IN_REPORT_MODE;
 
-        wxDCClipper clipper(*dc, x, y, width, rect.height);
+            xOld += ix;
+            width -= ix;
+        }
+
+        wxDCClipper clipper(*dc, xOld, y, width, rect.height);
 
         if ( item->HasText() )
         {
-            dc->DrawText( item->GetText(), x, y );
+            dc->DrawText( item->GetText(), xOld, y );
         }
 
-        x = xOld + width;
-
         node = node->GetNext();
     }
 }
@@ -1705,30 +1741,39 @@ BEGIN_EVENT_TABLE(wxListHeaderWindow,wxWindow)
     EVT_SET_FOCUS     (wxListHeaderWindow::OnSetFocus)
 END_EVENT_TABLE()
 
-wxListHeaderWindow::wxListHeaderWindow( void )
+void wxListHeaderWindow::Init()
 {
-    m_owner = (wxListMainWindow *) NULL;
     m_currentCursor = (wxCursor *) NULL;
-    m_resizeCursor = (wxCursor *) NULL;
     m_isDragging = FALSE;
+    m_dirty = FALSE;
 }
 
-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 )
+wxListHeaderWindow::wxListHeaderWindow()
 {
+    Init();
+
+    m_owner = (wxListMainWindow *) NULL;
+    m_resizeCursor = (wxCursor *) 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 )
+{
+    Init();
+
     m_owner = owner;
-//  m_currentCursor = wxSTANDARD_CURSOR;
-    m_currentCursor = (wxCursor *) NULL;
     m_resizeCursor = new wxCursor( wxCURSOR_SIZEWE );
-    m_isDragging = FALSE;
-    m_dirty = FALSE;
 
     SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_BTNFACE ) );
 }
 
-wxListHeaderWindow::~wxListHeaderWindow( void )
+wxListHeaderWindow::~wxListHeaderWindow()
 {
     delete m_resizeCursor;
 }
@@ -1838,20 +1883,50 @@ void wxListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
     {
         m_owner->GetColumn( i, item );
         int wCol = item.m_width;
-        int cw = wCol - 2; // the width of the rect to draw
 
-        int xEnd = x + wCol;
+        // the width of the rect to draw: make it smaller to fit entirely
+        // inside the column rect
+        int cw = wCol - 2;
 
         dc.SetPen( *wxWHITE_PEN );
 
         DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 );
-        dc.SetClippingRegion( x, HEADER_OFFSET_Y, cw-5, h-4 );
-        dc.DrawText( item.GetText(), x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT );
-        dc.DestroyClippingRegion();
-        x += wCol;
 
-        if (xEnd > w+5)
+        // if we have an image, draw it on the right of the label
+        int image = item.m_image;
+        if ( image != -1 )
+        {
+            wxImageList *imageList = m_owner->m_small_image_list;
+            if ( imageList )
+            {
+                int ix, iy;
+                imageList->GetSize(image, ix, iy);
+
+                imageList->Draw
+                           (
+                            image,
+                            dc,
+                            x + cw - ix - 1,
+                            HEADER_OFFSET_Y + (h - 4 - iy)/2,
+                            wxIMAGELIST_DRAW_TRANSPARENT
+                           );
+
+                cw -= ix + 2;
+            }
+            //else: ignore the column image
+        }
+
+        // draw the text clipping it so that it doesn't overwrite the column
+        // boundary
+        wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 );
+
+        dc.DrawText( item.GetText(),
+                     x + EXTRA_WIDTH, HEADER_OFFSET_Y + EXTRA_HEIGHT );
+
+        if ( x > w - wCol + 5 )
             break;
+
+        x += wCol;
     }
     dc.EndDrawing();
 }
@@ -1952,20 +2027,31 @@ void wxListHeaderWindow::OnMouse( wxMouseEvent &event )
             m_minX = xpos;
         }
 
-        if (event.LeftDown())
+        if (event.LeftDown() || event.RightUp())
         {
-            if (hit_border)
+            if (hit_border && event.LeftDown())
             {
                 m_isDragging = TRUE;
                 m_currentX = x;
                 DrawCurrent();
                 CaptureMouse();
             }
-            else
+            else // click on a column
             {
                 wxWindow *parent = GetParent();
-                wxListEvent le( wxEVT_COMMAND_LIST_COL_CLICK, parent->GetId() );
+                wxListEvent le( event.LeftDown()
+                                    ? wxEVT_COMMAND_LIST_COL_CLICK
+                                    : wxEVT_COMMAND_LIST_COL_RIGHT_CLICK,
+                                parent->GetId() );
                 le.SetEventObject( parent );
+                le.m_pointDrag = event.GetPosition();
+
+                // the position should be relative to the parent window, not
+                // this one for compatibility with MSW and common sense: the
+                // user code doesn't know anything at all about this header
+                // window, so why should it get positions relative to it?
+                le.m_pointDrag.y -= GetSize().y;
+
                 le.m_col = m_column;
                 parent->GetEventHandler()->ProcessEvent( le );
             }
@@ -2163,7 +2249,8 @@ wxListMainWindow::wxListMainWindow()
 {
     Init();
 
-    m_highlightBrush = (wxBrush *) NULL;
+    m_highlightBrush =
+    m_highlightUnfocusedBrush = (wxBrush *) NULL;
 
     m_xScroll =
     m_yScroll = 0;
@@ -2180,7 +2267,24 @@ wxListMainWindow::wxListMainWindow( wxWindow *parent,
 {
     Init();
 
-    m_highlightBrush = new wxBrush( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT), wxSOLID );
+    m_highlightBrush = new wxBrush
+                           (
+                            wxSystemSettings::GetSystemColour
+                            (
+                                wxSYS_COLOUR_HIGHLIGHT
+                            ),
+                            wxSOLID
+                           );
+
+    m_highlightUnfocusedBrush = new wxBrush
+                                    (
+                                       wxSystemSettings::GetSystemColour
+                                       (
+                                           wxSYS_COLOUR_BTNSHADOW
+                                       ),
+                                       wxSOLID
+                                    );
+
     wxSize sz = size;
     sz.y = 25;
 
@@ -2195,6 +2299,7 @@ wxListMainWindow::~wxListMainWindow()
     DoDeleteAllItems();
 
     delete m_highlightBrush;
+    delete m_highlightUnfocusedBrush;
 
     delete m_renameTimer;
 }
@@ -2321,6 +2426,8 @@ wxRect wxListMainWindow::GetLineHighlightRect(size_t line) const
 
 long wxListMainWindow::HitTestLine(size_t line, int x, int y) const
 {
+    wxASSERT_MSG( line < GetItemCount(), _T("invalid line in HitTestLine") );
+
     wxListLineData *ld = GetLine(line);
 
     if ( ld->HasImage() && GetLineIconRect(line).Inside(x, y) )
@@ -2402,7 +2509,7 @@ bool wxListMainWindow::HighlightLine( size_t line, bool highlight )
     else // !virtual
     {
         wxListLineData *ld = GetLine(line);
-        wxCHECK_MSG( ld, FALSE, _T("invalid index in IsHighlighted") );
+        wxCHECK_MSG( ld, FALSE, _T("invalid index in HighlightLine") );
 
         changed = ld->Highlight(highlight);
     }
@@ -2498,6 +2605,37 @@ void wxListMainWindow::RefreshAfter( size_t lineFrom )
     }
 }
 
+void wxListMainWindow::RefreshSelected()
+{
+    if ( IsEmpty() )
+        return;
+
+    size_t from, to;
+    if ( InReportView() )
+    {
+        GetVisibleLinesRange(&from, &to);
+    }
+    else // !virtual
+    {
+        from = 0;
+        to = GetItemCount() - 1;
+    }
+
+    if ( HasCurrent() && m_current >= from && m_current <= to )
+    {
+        RefreshLine(m_current);
+    }
+
+    for ( size_t line = from; line <= to; line++ )
+    {
+        // NB: the test works as expected even if m_current == -1
+        if ( line != m_current && IsHighlighted(line) )
+        {
+            RefreshLine(line);
+        }
+    }
+}
+
 void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
 {
     // Note: a wxPaintDC must be constructed even if no drawing is
@@ -2561,7 +2699,7 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
             GetLine(line)->DrawInReportMode( &dc,
                                              rectLine,
                                              GetLineHighlightRect(line),
-                                             m_hasFocus && IsHighlighted(line) );
+                                             IsHighlighted(line) );
         }
 
         if ( HasFlag(wxLC_HRULES) )
@@ -2621,10 +2759,13 @@ void wxListMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
     if ( HasCurrent() )
     {
         // don't draw rect outline under Max if we already have the background
-        // color
+        // color but under other platforms only draw it if we do: it is a bit
+        // silly to draw "focus rect" if we don't have focus!
 #ifdef __WXMAC__
         if ( !m_hasFocus )
-#endif // !__WXMAC__
+#else // !__WXMAC__
+        if ( m_hasFocus )
+#endif // __WXMAC__/!__WXMAC__
         {
             dc.SetPen( *wxBLACK_PEN );
             dc.SetBrush( *wxTRANSPARENT_BRUSH );
@@ -2811,7 +2952,12 @@ void wxListMainWindow::OnMouse( wxMouseEvent &event )
     if (event.Dragging())
     {
         if (m_dragCount == 0)
-            m_dragStart = wxPoint(x,y);
+        {
+            // 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++;
 
@@ -3217,12 +3363,11 @@ void wxListMainWindow::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
 {
     m_hasFocus = TRUE;
 
-    if ( HasCurrent() )
-        RefreshLine( m_current );
-
     if (!GetParent())
         return;
 
+    RefreshSelected();
+
 #ifdef __WXGTK__
     g_focusWindow = GetParent();
 #endif
@@ -3236,8 +3381,7 @@ void wxListMainWindow::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
 {
     m_hasFocus = FALSE;
 
-    if ( HasCurrent() )
-        RefreshLine( m_current );
+    RefreshSelected();
 }
 
 void wxListMainWindow::DrawImage( int index, wxDC *dc, int x, int y )
@@ -3374,6 +3518,9 @@ void wxListMainWindow::SetColumnWidth( int col, int width )
                  _T("SetColumnWidth() can only be called in report mode.") );
 
     m_dirty = TRUE;
+    wxListHeaderWindow *headerWin = GetListCtrl()->m_headerWin;
+    if ( headerWin )
+        headerWin->m_dirty = TRUE;
 
     wxListHeaderDataList::Node *node = m_columns.Item( col );
     wxCHECK_RET( node, _T("no column?") );
@@ -3986,8 +4133,6 @@ void wxListMainWindow::EnsureVisible( long index )
     // been added and its position is not known yet
     if ( m_dirty )
     {
-        m_dirty = FALSE;
-
         RecalculatePositions(TRUE /* no refresh */);
     }
 
@@ -4035,18 +4180,22 @@ long wxListMainWindow::HitTest( int x, int y, int &flags )
 {
     CalcUnscrolledPosition( x, y, &x, &y );
 
+    size_t count = GetItemCount();
+
     if ( HasFlag(wxLC_REPORT) )
     {
         size_t current = y / GetLineHeight();
-        flags = HitTestLine(current, x, y);
-        if ( flags )
-            return current;
+        if ( current < count )
+        {
+            flags = HitTestLine(current, x, y);
+            if ( flags )
+                return current;
+        }
     }
     else // !report
     {
         // TODO: optimize it too! this is less simple than for report view but
         //       enumerating all items is still not a way to do it!!
-        size_t count = GetItemCount();
         for ( size_t current = 0; current < count; current++ )
         {
             flags = HitTestLine(current, x, y);
@@ -4231,17 +4380,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
 
 wxListItem::wxListItem()
 {
-    m_mask = 0;
-    m_itemId = 0;
-    m_col = 0;
-    m_state = 0;
-    m_stateMask = 0;
-    m_image = 0;
-    m_data = 0;
-    m_format = wxLIST_FORMAT_CENTRE;
-    m_width = 0;
-
     m_attr = NULL;
+
+    Clear();
 }
 
 void wxListItem::Clear()
@@ -4251,11 +4392,11 @@ void wxListItem::Clear()
     m_col = 0;
     m_state = 0;
     m_stateMask = 0;
-    m_image = 0;
+    m_image = -1;
     m_data = 0;
     m_format = wxLIST_FORMAT_CENTRE;
     m_width = 0;
-    m_text = _T("");
+    m_text.clear();
 
     ClearAttributes();
 }