]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/listctrl.cpp
xti additions / changes, trying to reduce dependencies
[wxWidgets.git] / src / msw / listctrl.cpp
index 26633b3cc91e988e0c8e079c31ec6a7373c9362e..bffa880aa60a29a0c964e9adcbdb077cfd1c400b 100644 (file)
@@ -5,8 +5,8 @@
 // Modified by:
 // Created:     04/01/98
 // RCS-ID:      $Id$
-// Copyright:   (c) Julian Smart and Markus Holzem
-// Licence:     wxWindows license
+// Copyright:   (c) Julian Smart
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // ============================================================================
@@ -17,7 +17,7 @@
 // headers
 // ----------------------------------------------------------------------------
 
-#ifdef __GNUG__
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
     #pragma implementation "listctrl.h"
     #pragma implementation "listctrlbase.h"
 #endif
 
 #include "wx/msw/private.h"
 
-#if ((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__))
-    #include "wx/msw/gnuwin32/extra.h"
-#else
-    #include <commctrl.h>
-#endif
-
-#include "wx/msw/missing.h"
+// include <commctrl.h> "properly"
+#include "wx/msw/wrapcctl.h"
 
 // ----------------------------------------------------------------------------
 // private functions
@@ -218,7 +213,24 @@ DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_ACTIVATED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_FOCUSED)
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT)
 
+#if wxUSE_EXTENDED_RTTI
+IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h")
+
+WX_BEGIN_PROPERTIES_TABLE(wxListCtrl)
+WX_END_PROPERTIES_TABLE()
+
+WX_BEGIN_HANDLERS_TABLE(wxListCtrl)
+WX_END_HANDLERS_TABLE()
+
+WX_CONSTRUCTOR_5( wxListCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) 
+
+/*
+ TODO : Expose more information of a list's layout etc. via appropriate objects (à la NotebookPageInfo)
+*/
+#else
 IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
+#endif
+
 IMPLEMENT_DYNAMIC_CLASS(wxListView, wxListCtrl)
 IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
 
@@ -244,6 +256,8 @@ void wxListCtrl::Init()
     m_ownsImageListNormal = m_ownsImageListSmall = m_ownsImageListState = FALSE;
     m_baseStyle = 0;
     m_colCount = 0;
+    m_count = 0;
+    m_ignoreChangeMessages = FALSE;
     m_textCtrl = NULL;
     m_AnyInternalData = FALSE;
     m_hasAnyAttr = FALSE;
@@ -286,11 +300,6 @@ bool wxListCtrl::Create(wxWindow *parent,
     DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
                    LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
 
-    if ( m_windowStyle & wxCLIP_SIBLINGS )
-        wstyle |= WS_CLIPSIBLINGS;
-
-    if ( wxStyleHasBorder(m_windowStyle) )
-        wstyle |= WS_BORDER;
     m_baseStyle = wstyle;
 
     if ( !DoCreateControl(x, y, width, height) )
@@ -306,21 +315,17 @@ bool wxListCtrl::DoCreateControl(int x, int y, int w, int h)
 {
     DWORD wstyle = m_baseStyle;
 
-    bool want3D;
-    WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
-
-    // Even with extended styles, need to combine with WS_BORDER
-    // for them to look right.
-    if ( want3D )
-        wstyle |= WS_BORDER;
+    WXDWORD exStyle = 0;
+    WXDWORD standardStyle = MSWGetStyle(GetWindowStyle(), & exStyle) ;
 
     long oldStyle = 0; // Dummy
     wstyle |= ConvertToMSWStyle(oldStyle, m_windowStyle);
+    wstyle |= standardStyle;
 
     // Create the ListView control.
     m_hWnd = (WXHWND)CreateWindowEx(exStyle,
                                     WC_LISTVIEW,
-                                    wxT(""),
+                                    wxEmptyString,
                                     wstyle,
                                     x, y, w, h,
                                     GetWinHwnd(GetParent()),
@@ -378,8 +383,10 @@ void wxListCtrl::FreeAllInternalData()
         int n = GetItemCount();
         int i = 0;
 
+        m_ignoreChangeMessages = TRUE;
         for (i = 0; i < n; i++)
             wxDeleteInternalData(this, i);
+        m_ignoreChangeMessages = FALSE;
 
         m_AnyInternalData = FALSE;
     }
@@ -391,8 +398,8 @@ wxListCtrl::~wxListCtrl()
 
     if ( m_textCtrl )
     {
-        m_textCtrl->SetHWND(0);
         m_textCtrl->UnsubclassWin();
+        m_textCtrl->SetHWND(0);
         delete m_textCtrl;
         m_textCtrl = NULL;
     }
@@ -641,12 +648,16 @@ bool wxListCtrl::GetColumn(int col, wxListItem& item) const
         }
     }
 
-#if _WIN32_IE >= 0x0300
+    // the column images were not supported in older versions but how to check
+    // for this? we can't use _WIN32_IE because we always define it to a very
+    // high value, so see if another symbol which is only defined starting from
+    // comctl32.dll 4.70 is available
+#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
     if ( item.m_mask & wxLIST_MASK_IMAGE )
     {
         item.m_image = lvCol.iImage;
     }
-#endif
+#endif // LVCOLUMN::iImage exists
 
     return success;
 }
@@ -1014,7 +1025,7 @@ bool wxListCtrl::SetItemPosition(long item, const wxPoint& pos)
 // Gets the number of items in the list control
 int wxListCtrl::GetItemCount() const
 {
-    return ListView_GetItemCount(GetHwnd());
+    return m_count;
 }
 
 // Retrieves the spacing between icons in pixels.
@@ -1204,6 +1215,10 @@ bool wxListCtrl::DeleteItem(long item)
         return FALSE;
     }
 
+    m_count -= 1;
+    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
+                  wxT("m_count should match ListView_GetItemCount"));
+
     // the virtual list control doesn't refresh itself correctly, help it
     if ( IsVirtual() )
     {
@@ -1232,6 +1247,7 @@ bool wxListCtrl::DeleteItem(long item)
 // Deletes all items
 bool wxListCtrl::DeleteAllItems()
 {
+    FreeAllInternalData();
     return ListView_DeleteAllItems(GetHwnd()) != 0;
 }
 
@@ -1290,8 +1306,8 @@ wxTextCtrl* wxListCtrl::EditLabel(long item, wxClassInfo* textControlClass)
     // [re]create the text control wrapping the HWND we got
     if ( m_textCtrl )
     {
-        m_textCtrl->SetHWND(0);
         m_textCtrl->UnsubclassWin();
+        m_textCtrl->SetHWND(0);
         delete m_textCtrl;
     }
 
@@ -1448,8 +1464,12 @@ long wxListCtrl::InsertItem(wxListItem& info)
     }
     };
 
+    long rv = ListView_InsertItem(GetHwnd(), & item);
+    m_count += 1;
+    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
+                  wxT("m_count should match ListView_GetItemCount"));
 
-    return (long) ListView_InsertItem(GetHwnd(), & item);
+    return rv;
 }
 
 long wxListCtrl::InsertItem(long index, const wxString& label)
@@ -1496,34 +1516,18 @@ long wxListCtrl::InsertColumn(long col, wxListItem& item)
         lvCol.cx = 80;
     }
 
-    // when we insert a column which can contain an image, we must specify this
-    // flag right now as doing it later in SetColumn() has no effect
-    //
-    // we use LVCFMT_BITMAP_ON_RIGHT by default because without it there is no
-    // way to dynamically set/clear the bitmap as the column without a bitmap
-    // on the left looks ugly (there is a hole)
-    //
-    // unfortunately with my version of comctl32.dll (5.80), the left column
-    // image is always on the left and it seems that it's a "feature" - I
-    // didn't find any way to work around it in any case
-    if ( lvCol.mask & LVCF_IMAGE )
-    {
-        lvCol.mask |= LVCF_FMT;
-        lvCol.fmt |= LVCFMT_BITMAP_ON_RIGHT;
-    }
-
-    bool success = ListView_InsertColumn(GetHwnd(), col, &lvCol) != -1;
-    if ( success )
+    long n = ListView_InsertColumn(GetHwnd(), col, &lvCol);
+    if ( n != -1 )
     {
         m_colCount++;
     }
-    else
+    else // failed to insert?
     {
         wxLogDebug(wxT("Failed to insert the column '%s' into listview!"),
                    lvCol.pszText);
     }
 
-    return success;
+    return n;
 }
 
 long wxListCtrl::InsertColumn(long col,
@@ -1641,6 +1645,7 @@ bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id)
 
 bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 {
+
     // prepare the event
     // -----------------
 
@@ -1770,53 +1775,29 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
         const int iItem = nmLV->iItem;
 
-        // set the data event field for all messages for which the system gives
-        // us a valid NM_LISTVIEW::lParam
-        switch ( nmLV->hdr.code )
+
+        // FreeAllInternalData will cause LVN_ITEMCHANG* messages, which can be
+        // ignored for efficiency.  It is done here because the internal data is in the
+        // process of being deleted so we don't want to try and access it below.
+        if ( m_ignoreChangeMessages &&
+             ( (nmLV->hdr.code == LVN_ITEMCHANGED) || (nmLV->hdr.code == LVN_ITEMCHANGING)))
         {
-            case LVN_BEGINDRAG:
-            case LVN_BEGINRDRAG:
-            case LVN_COLUMNCLICK:
-            case LVN_ITEMCHANGED:
-            case LVN_ITEMCHANGING:
-                if ( item != -1 )
-                {
-                    if ( iItem >= GetItemCount() )
-                    {
-                        // there is apparently a bug in comctl32.dll version
-                        // 5.50.4704.1100 (note that the MS DLL database
-                        // doesn't say what this version is, it's not the one
-                        // shipped with W2K although the bug was reported under
-                        // that system) and it sends us LVN_ITEMCHANGING
-                        // notifications with the item out of range -- and as
-                        // we access the items client data, we crash below
-                        //
-                        // see
-                        //
-                        //  http://lists.wxwindows.org/cgi-bin/ezmlm-cgi?8:mss:29852:knlihdmadhaljafjajei
-                        //
-                        // and the thread continuation for more details
-                        // (although note that the bug may be present in other
-                        // versions of comctl32.dll as well as it has been
-                        // reported quite a few times)
-                        //
-                        // to fix this, simply ignore these "bad" events (as
-                        // above with HDN_GETDISPINFOW)
-                        return TRUE;
-                    }
+            return TRUE;
+        }
 
-                    wxListItemInternalData *internaldata =
-                        (wxListItemInternalData *) nmLV->lParam;
 
-                    if ( internaldata )
-                        event.m_item.m_data = internaldata->lParam;
-                }
+        // If we have a valid item then check if there is a data value
+        // associated with it and put it in the event.
+        if ( iItem >= 0 && iItem < GetItemCount() )
+        {
+            wxListItemInternalData *internaldata =
+                wxGetInternalData(GetHwnd(), iItem);
 
-            default:
-                // fall through
-                ;
+            if ( internaldata )
+                event.m_item.m_data = internaldata->lParam;
         }
 
+
         switch ( nmhdr->code )
         {
             case LVN_BEGINRDRAG:
@@ -1860,7 +1841,19 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     wxConvertFromMSWListItem(NULL, event.m_item, item);
                     if ( ((LV_ITEM)item).pszText == NULL ||
                          ((LV_ITEM)item).iItem == -1 )
+                    {
+                        // don't keep a stale wxTextCtrl around
+                        if ( m_textCtrl )
+                        {
+                            // EDIT control will be deleted by the list control itself so
+                            // prevent us from deleting it as well
+                            m_textCtrl->UnsubclassWin();
+                            m_textCtrl->SetHWND(0);
+                            delete m_textCtrl;
+                            m_textCtrl = NULL;
+                        }
                         return FALSE;
+                    }
 
                     event.m_itemIndex = event.m_item.m_itemId;
                 }
@@ -1872,7 +1865,19 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                     wxConvertFromMSWListItem(NULL, event.m_item, item);
                     if ( ((LV_ITEM)item).pszText == NULL ||
                          ((LV_ITEM)item).iItem == -1 )
+                    {
+                        // don't keep a stale wxTextCtrl around
+                        if ( m_textCtrl )
+                        {
+                            // EDIT control will be deleted by the list control itself so
+                            // prevent us from deleting it as well
+                            m_textCtrl->UnsubclassWin();
+                            m_textCtrl->SetHWND(0);
+                            delete m_textCtrl;
+                            m_textCtrl = NULL;
+                        }
                         return FALSE;
+                    }
 
                     event.m_itemIndex = event.m_item.m_itemId;
                 }
@@ -1885,11 +1890,17 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 break;
 
             case LVN_DELETEALLITEMS:
+                m_count = 0;
                 eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
                 event.m_itemIndex = -1;
                 break;
 
             case LVN_DELETEITEM:
+                if (m_count == 0)
+                    // this should be prevented by the post-processing code below,
+                    // but "just in case"
+                    return FALSE;
+
                 eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
                 event.m_itemIndex = iItem;
                 // delete the assoicated internal data
@@ -2055,8 +2066,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                 }
                 break;
 
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
-        && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+#ifdef NM_CUSTOMDRAW
             case NM_CUSTOMDRAW:
                 *result = OnCustomDraw(lParam);
 
@@ -2069,21 +2079,18 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
                     eventType = wxEVT_COMMAND_LIST_CACHE_HINT;
 
-                    // we get some really stupid cache hints like ones for items in
-                    // range 0..0 for an empty control or, after deleting an item,
-                    // for items in invalid range - filter this garbage out
-                    if ( cacheHint->iFrom < cacheHint->iTo )
-                    {
-                        event.m_oldItemIndex = cacheHint->iFrom;
-
-                        long iMax = GetItemCount();
-                        event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo
-                                                                  : iMax - 1;
-                    }
-                    else
-                    {
+                    // we get some really stupid cache hints like ones for
+                    // items in range 0..0 for an empty control or, after
+                    // deleting an item, for items in invalid range -- filter
+                    // this garbage out
+                    if ( cacheHint->iFrom > cacheHint->iTo )
                         return FALSE;
-                    }
+
+                    event.m_oldItemIndex = cacheHint->iFrom;
+
+                    const long iMax = GetItemCount();
+                    event.m_itemIndex = cacheHint->iTo < iMax ? cacheHint->iTo
+                                                              : iMax - 1;
                 }
                 break;
 
@@ -2101,13 +2108,13 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
                         wxStrncpy(lvi.pszText, text, lvi.cchTextMax);
                     }
 
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
-        && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 1 ) )
+                    // see comment at the end of wxListCtrl::GetColumn()
+#ifdef NM_CUSTOMDRAW
                     if ( lvi.mask & LVIF_IMAGE )
                     {
                         lvi.iImage = OnGetItemImage(item);
                     }
-#endif
+#endif // NM_CUSTOMDRAW
 
                     // a little dose of healthy paranoia: as we never use
                     // LVM_SETCALLBACKMASK we're not supposed to get these ones
@@ -2133,12 +2140,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
 
     event.SetEventType(eventType);
 
-    if ( !GetEventHandler()->ProcessEvent(event) )
-        return FALSE;
+    bool processed = GetEventHandler()->ProcessEvent(event);
 
     // post processing
     // ---------------
-
     switch ( nmhdr->code )
     {
         case LVN_DELETEALLITEMS:
@@ -2146,7 +2151,6 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             // notifications - this makes deleting all items from a list ctrl
             // much faster
             *result = TRUE;
-
             return TRUE;
 
         case LVN_ENDLABELEDITA:
@@ -2159,8 +2163,8 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             {
                 // EDIT control will be deleted by the list control itself so
                 // prevent us from deleting it as well
-                m_textCtrl->SetHWND(0);
                 m_textCtrl->UnsubclassWin();
+                m_textCtrl->SetHWND(0);
                 delete m_textCtrl;
                 m_textCtrl = NULL;
             }
@@ -2168,12 +2172,14 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
             return TRUE;
     }
 
-    *result = !event.IsAllowed();
+    if ( processed )
+        *result = !event.IsAllowed();
 
-    return TRUE;
+    return processed;
 }
 
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300
+// see comment at the end of wxListCtrl::GetColumn()
+#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
 
 WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
 {
@@ -2340,7 +2346,7 @@ wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) cons
 {
     // this is a pure virtual function, in fact - which is not really pure
     // because the controls which are not virtual don't need to implement it
-    wxFAIL_MSG( _T("not supposed to be called") );
+    wxFAIL_MSG( _T("wxListCtrl::OnGetItemText not supposed to be called") );
 
     return wxEmptyString;
 }
@@ -2348,7 +2354,7 @@ wxString wxListCtrl::OnGetItemText(long WXUNUSED(item), long WXUNUSED(col)) cons
 int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const
 {
     // same as above
-    wxFAIL_MSG( _T("not supposed to be called") );
+    wxFAIL_MSG( _T("wxListCtrl::OnGetItemImage not supposed to be called") );
 
     return -1;
 }
@@ -2366,10 +2372,13 @@ void wxListCtrl::SetItemCount(long count)
 {
     wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
 
-    if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count, 0) )
+    if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count, LVSICF_NOSCROLL) )
     {
         wxLogLastError(_T("ListView_SetItemCount"));
     }
+    m_count = count;
+    wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
+                  wxT("m_count should match ListView_GetItemCount"));
 }
 
 void wxListCtrl::RefreshItem(long item)
@@ -2432,13 +2441,13 @@ static void wxDeleteInternalData(wxListCtrl* ctl, long itemId)
     wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
     if (data)
     {
-        delete data;
         LV_ITEM item;
         memset(&item, 0, sizeof(item));
         item.iItem = itemId;
         item.mask = LVIF_PARAM;
         item.lParam = (LPARAM) 0;
         ListView_SetItem((HWND)ctl->GetHWND(), &item);
+        delete data;
     }
 }
 
@@ -2632,18 +2641,29 @@ static void wxConvertToMSWListCol(int WXUNUSED(col), const wxListItem& item,
             lvCol.cx = item.m_width;
     }
 
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
-        && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 1 ) )
+    // see comment at the end of wxListCtrl::GetColumn()
+#ifdef NM_CUSTOMDRAW // _WIN32_IE >= 0x0300
     if ( item.m_mask & wxLIST_MASK_IMAGE )
     {
         if ( wxTheApp->GetComCtl32Version() >= 470 )
         {
-            lvCol.mask |= LVCF_IMAGE;
+            lvCol.mask |= LVCF_IMAGE | LVCF_FMT;
+
+            // we use LVCFMT_BITMAP_ON_RIGHT because thei mages on the right
+            // seem to be generally nicer than on the left and the generic
+            // version only draws them on the right (we don't have a flag to
+            // specify the image location anyhow)
+            //
+            // we don't use LVCFMT_COL_HAS_IMAGES because it doesn't seem to
+            // make any difference in my tests -- but maybe we should?
+            lvCol.fmt |= LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE;
+
             lvCol.iImage = item.m_image;
         }
         //else: it doesn't support item images anyhow
     }
-#endif
+#endif // _WIN32_IE >= 0x0300
 }
 
 #endif // wxUSE_LISTCTRL
+