// 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
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// 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>
+#if defined(__WXWINCE__)
+ #include <ole2.h>
+ #include <shellapi.h>
+ #if _WIN32_WCE < 400
+ #include <aygshell.h>
+ #endif
#endif
-#include "wx/msw/missing.h"
+// include <commctrl.h> "properly"
+#include "wx/msw/wrapcctl.h"
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
// We have to handle both fooW and fooA notifications in several cases
-// because of broken commctl.dll and/or unicows.dll. This class is used to
+// because of broken comctl32.dll and/or unicows.dll. This class is used to
// convert LV_ITEMA and LV_ITEMW to LV_ITEM (which is either LV_ITEMA or
// LV_ITEMW depending on wxUSE_UNICODE setting), so that it can be processed
// by wxConvertToMSWListItem().
+#if wxUSE_UNICODE
+ #define LV_ITEM_NATIVE LV_ITEMW
+ #define LV_ITEM_OTHER LV_ITEMA
+
+ #define LV_CONV_TO_WX cMB2WX
+ #define LV_CONV_BUF wxMB2WXbuf
+#else // ANSI
+ #define LV_ITEM_NATIVE LV_ITEMA
+ #define LV_ITEM_OTHER LV_ITEMW
+
+ #define LV_CONV_TO_WX cWC2WX
+ #define LV_CONV_BUF wxWC2WXbuf
+#endif // Unicode/ANSI
+
class wxLV_ITEM
{
public:
- ~wxLV_ITEM() { delete m_buf; }
- operator LV_ITEM&() const { return *m_item; }
+ // default ctor, use Init() later
+ wxLV_ITEM() { m_buf = NULL; m_pItem = NULL; }
-#if wxUSE_UNICODE
- wxLV_ITEM(LV_ITEMW &item) : m_buf(NULL), m_item(&item) {}
- wxLV_ITEM(LV_ITEMA &item)
+ // init without conversion
+ void Init(LV_ITEM_NATIVE& item)
{
- m_item = new LV_ITEM((LV_ITEM&)item);
- if ( (item.mask & LVIF_TEXT) && item.pszText )
- {
- m_buf = new wxMB2WXbuf(wxConvLocal.cMB2WX(item.pszText));
- m_item->pszText = (wxChar*)m_buf->data();
- }
- else
- m_buf = NULL;
+ wxASSERT_MSG( !m_pItem, _T("Init() called twice?") );
+
+ m_pItem = &item;
}
-private:
- wxMB2WXbuf *m_buf;
-#else // !wxUSE_UNICODE
- wxLV_ITEM(LV_ITEMW &item)
+ // init with conversion
+ void Init(LV_ITEM_OTHER& item)
{
- m_item = new LV_ITEM((LV_ITEM&)item);
+ // avoid unnecessary dynamic memory allocation, jjust make m_pItem
+ // point to our own m_item
+
+ // memcpy() can't work if the struct sizes are different
+ wxCOMPILE_TIME_ASSERT( sizeof(LV_ITEM_OTHER) == sizeof(LV_ITEM_NATIVE),
+ CodeCantWorkIfDiffSizes);
- // the code below doesn't compile without wxUSE_WCHAR_T and as I don't
- // know if it's useful to have it at all (do we ever get Unicode
- // notifications in ANSI mode? I don't think so...) I'm not going to
- // write alternative implementation right now
- //
- // but if it is indeed used, we should simply directly use
- // ::WideCharToMultiByte() here
-#if wxUSE_WCHAR_T
+ memcpy(&m_item, &item, sizeof(LV_ITEM_NATIVE));
+
+ // convert text from ANSI to Unicod if necessary
if ( (item.mask & LVIF_TEXT) && item.pszText )
{
-#ifdef __WXWINE__
- // FIXME
- m_buf = new wxWC2WXbuf(wxConvLocal.cWC2WX((const __wchar_t* ) item.pszText));
-#else
- m_buf = new wxWC2WXbuf(wxConvLocal.cWC2WX(item.pszText));
-#endif
- m_item->pszText = (wxChar*)m_buf->data();
+ m_buf = new LV_CONV_BUF(wxConvLocal.LV_CONV_TO_WX(item.pszText));
+ m_item.pszText = (wxChar *)m_buf->data();
}
- else
-#endif // wxUSE_WCHAR_T
- m_buf = NULL;
}
- wxLV_ITEM(LV_ITEMA &item) : m_buf(NULL), m_item(&item) {}
+
+ // ctor without conversion
+ wxLV_ITEM(LV_ITEM_NATIVE& item) : m_buf(NULL), m_pItem(&item) { }
+
+ // ctor with conversion
+ wxLV_ITEM(LV_ITEM_OTHER& item) : m_buf(NULL)
+ {
+ Init(item);
+ }
+
+ ~wxLV_ITEM() { delete m_buf; }
+
+ // conversion to the real LV_ITEM
+ operator LV_ITEM_NATIVE&() const { return *m_pItem; }
+
private:
- wxWC2WXbuf *m_buf;
-#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+ LV_CONV_BUF *m_buf;
- LV_ITEM *m_item;
+ LV_ITEM_NATIVE *m_pItem;
+ LV_ITEM_NATIVE m_item;
DECLARE_NO_COPY_CLASS(wxLV_ITEM)
};
// Get the internal data structure
static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId);
-static wxListItemInternalData *wxGetInternalData(wxListCtrl *ctl, long itemId);
+static wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
static wxListItemAttr *wxGetInternalDataAttr(wxListCtrl *ctl, long itemId);
static void wxDeleteInternalData(wxListCtrl* ctl, long itemId);
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_FOCUSED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_CACHE_HINT)
+#if wxUSE_EXTENDED_RTTI
+WX_DEFINE_FLAGS( wxListCtrlStyle )
+
+wxBEGIN_FLAGS( wxListCtrlStyle )
+ // new style border flags, we put them first to
+ // use them for streaming out
+ wxFLAGS_MEMBER(wxBORDER_SIMPLE)
+ wxFLAGS_MEMBER(wxBORDER_SUNKEN)
+ wxFLAGS_MEMBER(wxBORDER_DOUBLE)
+ wxFLAGS_MEMBER(wxBORDER_RAISED)
+ wxFLAGS_MEMBER(wxBORDER_STATIC)
+ wxFLAGS_MEMBER(wxBORDER_NONE)
+
+ // old style border flags
+ wxFLAGS_MEMBER(wxSIMPLE_BORDER)
+ wxFLAGS_MEMBER(wxSUNKEN_BORDER)
+ wxFLAGS_MEMBER(wxDOUBLE_BORDER)
+ wxFLAGS_MEMBER(wxRAISED_BORDER)
+ wxFLAGS_MEMBER(wxSTATIC_BORDER)
+ wxFLAGS_MEMBER(wxBORDER)
+
+ // standard window styles
+ wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
+ wxFLAGS_MEMBER(wxCLIP_CHILDREN)
+ wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
+ wxFLAGS_MEMBER(wxWANTS_CHARS)
+ wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
+ wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
+ wxFLAGS_MEMBER(wxVSCROLL)
+ wxFLAGS_MEMBER(wxHSCROLL)
+
+ wxFLAGS_MEMBER(wxLC_LIST)
+ wxFLAGS_MEMBER(wxLC_REPORT)
+ wxFLAGS_MEMBER(wxLC_ICON)
+ wxFLAGS_MEMBER(wxLC_SMALL_ICON)
+ wxFLAGS_MEMBER(wxLC_ALIGN_TOP)
+ wxFLAGS_MEMBER(wxLC_ALIGN_LEFT)
+ wxFLAGS_MEMBER(wxLC_AUTOARRANGE)
+ wxFLAGS_MEMBER(wxLC_USER_TEXT)
+ wxFLAGS_MEMBER(wxLC_EDIT_LABELS)
+ wxFLAGS_MEMBER(wxLC_NO_HEADER)
+ wxFLAGS_MEMBER(wxLC_SINGLE_SEL)
+ wxFLAGS_MEMBER(wxLC_SORT_ASCENDING)
+ wxFLAGS_MEMBER(wxLC_SORT_DESCENDING)
+ wxFLAGS_MEMBER(wxLC_VIRTUAL)
+
+wxEND_FLAGS( wxListCtrlStyle )
+
+IMPLEMENT_DYNAMIC_CLASS_XTI(wxListCtrl, wxControl,"wx/listctrl.h")
+
+wxBEGIN_PROPERTIES_TABLE(wxListCtrl)
+ wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
+
+ wxPROPERTY_FLAGS( WindowStyle , wxListCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
+wxEND_PROPERTIES_TABLE()
+
+wxBEGIN_HANDLERS_TABLE(wxListCtrl)
+wxEND_HANDLERS_TABLE()
+
+wxCONSTRUCTOR_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)
m_imageListSmall = NULL;
m_imageListState = NULL;
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;
const wxValidator& validator,
const wxString& name)
{
-#if wxUSE_VALIDATORS
- SetValidator(validator);
-#endif // wxUSE_VALIDATORS
+ if ( !CreateControl(parent, id, pos, size, style, validator, name) )
+ return FALSE;
- SetName(name);
+ if ( !MSWCreateControl(WC_LISTVIEW, _T(""), pos, size) )
+ return FALSE;
- int x = pos.x;
- int y = pos.y;
- int width = size.x;
- int height = size.y;
+ // explicitly say that we want to use Unicode because otherwise we get ANSI
+ // versions of _some_ messages (notably LVN_GETDISPINFOA) in MSLU build
+ wxSetCCUnicodeFormat(GetHwnd());
- m_windowStyle = style;
+ // for comctl32.dll v 4.70+ we want to have this attribute because it's
+ // prettier (and also because wxGTK does it like this)
+ if ( InReportView() && wxTheApp->GetComCtl32Version() >= 470 )
+ {
+ ::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE,
+ 0, LVS_EX_FULLROWSELECT);
+ }
- SetParent(parent);
+ return TRUE;
+}
- if (width <= 0)
- width = 100;
- if (height <= 0)
- height = 30;
- if (x < 0)
- x = 0;
- if (y < 0)
- y = 0;
+WXDWORD wxListCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
+{
+ WXDWORD wstyle = wxControl::MSWGetStyle(style, exstyle);
- m_windowId = (id == -1) ? NewControlId() : id;
+ wstyle |= LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
- DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
- LVS_SHAREIMAGELISTS | LVS_SHOWSELALWAYS;
+#ifdef __WXDEBUG__
+ size_t nModes = 0;
- if ( m_windowStyle & wxCLIP_SIBLINGS )
- wstyle |= WS_CLIPSIBLINGS;
+ #define MAP_MODE_STYLE(wx, ms) \
+ if ( style & (wx) ) { wstyle |= (ms); nModes++; }
+#else // !__WXDEBUG__
+ #define MAP_MODE_STYLE(wx, ms) \
+ if ( style & (wx) ) wstyle |= (ms);
+#endif // __WXDEBUG__
- if ( wxStyleHasBorder(m_windowStyle) )
- wstyle |= WS_BORDER;
- m_baseStyle = wstyle;
+ MAP_MODE_STYLE(wxLC_ICON, LVS_ICON)
+ MAP_MODE_STYLE(wxLC_SMALL_ICON, LVS_SMALLICON)
+ MAP_MODE_STYLE(wxLC_LIST, LVS_LIST)
+ MAP_MODE_STYLE(wxLC_REPORT, LVS_REPORT)
- if ( !DoCreateControl(x, y, width, height) )
- return FALSE;
+ wxASSERT_MSG( nModes == 1,
+ _T("wxListCtrl style should have exactly one mode bit set") );
- if (parent)
- parent->AddChild(this);
+#undef MAP_MODE_STYLE
- return TRUE;
-}
+ if ( style & wxLC_ALIGN_LEFT )
+ wstyle |= LVS_ALIGNLEFT;
-bool wxListCtrl::DoCreateControl(int x, int y, int w, int h)
-{
- DWORD wstyle = m_baseStyle;
+ if ( style & wxLC_ALIGN_TOP )
+ wstyle |= LVS_ALIGNTOP;
- bool want3D;
- WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
+ if ( style & wxLC_AUTOARRANGE )
+ wstyle |= LVS_AUTOARRANGE;
+
+ if ( style & wxLC_NO_SORT_HEADER )
+ wstyle |= LVS_NOSORTHEADER;
- // Even with extended styles, need to combine with WS_BORDER
- // for them to look right.
- if ( want3D )
- wstyle |= WS_BORDER;
+ if ( style & wxLC_NO_HEADER )
+ wstyle |= LVS_NOCOLUMNHEADER;
- long oldStyle = 0; // Dummy
- wstyle |= ConvertToMSWStyle(oldStyle, m_windowStyle);
+ if ( style & wxLC_EDIT_LABELS )
+ wstyle |= LVS_EDITLABELS;
- // Create the ListView control.
- m_hWnd = (WXHWND)CreateWindowEx(exStyle,
- WC_LISTVIEW,
- wxT(""),
- wstyle,
- x, y, w, h,
- GetWinHwnd(GetParent()),
- (HMENU)m_windowId,
- wxGetInstance(),
- NULL);
+ if ( style & wxLC_SINGLE_SEL )
+ wstyle |= LVS_SINGLESEL;
- if ( !m_hWnd )
+ if ( style & wxLC_SORT_ASCENDING )
{
- wxLogError(_("Can't create list control window, check that comctl32.dll is installed."));
+ wstyle |= LVS_SORTASCENDING;
- return FALSE;
+ wxASSERT_MSG( !(style & wxLC_SORT_DESCENDING),
+ _T("can't sort in ascending and descending orders at once") );
}
+ else if ( style & wxLC_SORT_DESCENDING )
+ wstyle |= LVS_SORTDESCENDING;
- // for comctl32.dll v 4.70+ we want to have this attribute because it's
- // prettier (and also because wxGTK does it like this)
- if ( (wstyle & LVS_REPORT) && wxTheApp->GetComCtl32Version() >= 470 )
+#if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+ if ( style & wxLC_VIRTUAL )
{
- ::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE,
- 0, LVS_EX_FULLROWSELECT);
- }
-
- SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
- SetForegroundColour(GetParent()->GetForegroundColour());
+ int ver = wxTheApp->GetComCtl32Version();
+ if ( ver < 470 )
+ {
+ wxLogWarning(_("Please install a newer version of comctl32.dll\n(at least version 4.70 is required but you have %d.%02d)\nor this program won't operate correctly."),
+ ver / 100, ver % 100);
+ }
- SubclassWin(m_hWnd);
+ wstyle |= LVS_OWNERDATA;
+ }
+#endif // ancient cygwin
- return TRUE;
+ return wstyle;
}
void wxListCtrl::UpdateStyle()
{
- if ( GetHWND() )
+ if ( GetHwnd() )
{
// The new window view style
- long dummy;
- DWORD dwStyleNew = ConvertToMSWStyle(dummy, m_windowStyle);
- dwStyleNew |= m_baseStyle;
+ DWORD dwStyleNew = MSWGetStyle(m_windowStyle, NULL);
// Get the current window style.
DWORD dwStyleOld = ::GetWindowLong(GetHwnd(), GWL_STYLE);
int n = GetItemCount();
int i = 0;
+ m_ignoreChangeMessages = TRUE;
for (i = 0; i < n; i++)
wxDeleteInternalData(this, i);
+ m_ignoreChangeMessages = FALSE;
m_AnyInternalData = FALSE;
}
if ( m_textCtrl )
{
- m_textCtrl->SetHWND(0);
m_textCtrl->UnsubclassWin();
+ m_textCtrl->SetHWND(0);
delete m_textCtrl;
m_textCtrl = NULL;
}
UpdateStyle();
}
-// Can be just a single style, or a bitlist
-long wxListCtrl::ConvertToMSWStyle(long& oldStyle, long style) const
-{
- long wstyle = 0;
- if ( style & wxLC_ICON )
- {
- if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
- oldStyle -= LVS_SMALLICON;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
- oldStyle -= LVS_REPORT;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
- oldStyle -= LVS_LIST;
- wstyle |= LVS_ICON;
- }
-
- if ( style & wxLC_SMALL_ICON )
- {
- if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
- oldStyle -= LVS_ICON;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
- oldStyle -= LVS_REPORT;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
- oldStyle -= LVS_LIST;
- wstyle |= LVS_SMALLICON;
- }
-
- if ( style & wxLC_LIST )
- {
- if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
- oldStyle -= LVS_ICON;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_REPORT )
- oldStyle -= LVS_REPORT;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
- oldStyle -= LVS_SMALLICON;
- wstyle |= LVS_LIST;
- }
-
- if ( style & wxLC_REPORT )
- {
- if ( (oldStyle & LVS_TYPEMASK) == LVS_ICON )
- oldStyle -= LVS_ICON;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_LIST )
- oldStyle -= LVS_LIST;
- if ( (oldStyle & LVS_TYPEMASK) == LVS_SMALLICON )
- oldStyle -= LVS_SMALLICON;
-
- wstyle |= LVS_REPORT;
- }
-
- if ( style & wxLC_ALIGN_LEFT )
- {
- if ( oldStyle & LVS_ALIGNTOP )
- oldStyle -= LVS_ALIGNTOP;
- wstyle |= LVS_ALIGNLEFT;
- }
-
- if ( style & wxLC_ALIGN_TOP )
- {
- if ( oldStyle & LVS_ALIGNLEFT )
- oldStyle -= LVS_ALIGNLEFT;
- wstyle |= LVS_ALIGNTOP;
- }
-
- if ( style & wxLC_AUTOARRANGE )
- wstyle |= LVS_AUTOARRANGE;
-
- if ( style & wxLC_NO_SORT_HEADER )
- wstyle |= LVS_NOSORTHEADER;
-
- if ( style & wxLC_NO_HEADER )
- wstyle |= LVS_NOCOLUMNHEADER;
-
- if ( style & wxLC_EDIT_LABELS )
- wstyle |= LVS_EDITLABELS;
-
- if ( style & wxLC_SINGLE_SEL )
- wstyle |= LVS_SINGLESEL;
-
- if ( style & wxLC_SORT_ASCENDING )
- {
- if ( oldStyle & LVS_SORTDESCENDING )
- oldStyle -= LVS_SORTDESCENDING;
- wstyle |= LVS_SORTASCENDING;
- }
-
- if ( style & wxLC_SORT_DESCENDING )
- {
- if ( oldStyle & LVS_SORTASCENDING )
- oldStyle -= LVS_SORTASCENDING;
- wstyle |= LVS_SORTDESCENDING;
- }
+// ----------------------------------------------------------------------------
+// accessors
+// ----------------------------------------------------------------------------
-#if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
- if ( style & wxLC_VIRTUAL )
- {
- int ver = wxTheApp->GetComCtl32Version();
- if ( ver < 470 )
- {
- wxLogWarning(_("Please install a newer version of comctl32.dll\n(at least version 4.70 is required but you have %d.%02d)\nor this program won't operate correctly."),
- ver / 100, ver % 100);
- }
+/* static */ wxVisualAttributes
+wxListCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
+{
+ wxVisualAttributes attrs = GetCompositeControlsDefaultAttributes(variant);
- wstyle |= LVS_OWNERDATA;
- }
-#endif
+ // common controls have their own default font
+ attrs.font = wxGetCCDefaultFont();
- return wstyle;
+ return attrs;
}
-// ----------------------------------------------------------------------------
-// accessors
-// ----------------------------------------------------------------------------
-
// Sets the foreground, i.e. text, colour
bool wxListCtrl::SetForegroundColour(const wxColour& col)
{
}
}
-#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;
}
return SetItem(info);
}
+wxRect wxListCtrl::GetViewRect() const
+{
+ wxASSERT_MSG( !HasFlag(wxLC_REPORT | wxLC_LIST),
+ _T("wxListCtrl::GetViewRect() only works in icon mode") );
+
+ RECT rc;
+ if ( !ListView_GetViewRect(GetHwnd(), &rc) )
+ {
+ wxLogDebug(_T("ListView_GetViewRect() failed."));
+
+ wxZeroMemory(rc);
+ }
+
+ wxRect rect;
+ wxCopyRECTToRect(rc, rect);
+
+ return rect;
+}
+
// Gets the item rectangle
bool wxListCtrl::GetItemRect(long item, wxRect& rect, int code) const
{
// Gets the number of items in the list control
int wxListCtrl::GetItemCount() const
{
- return ListView_GetItemCount(GetHwnd());
+ return m_count;
+}
+
+wxSize wxListCtrl::GetItemSpacing() const
+{
+ const int spacing = ListView_GetItemSpacing(GetHwnd(), (BOOL)HasFlag(wxLC_SMALL_ICON));
+
+ return wxSize(LOWORD(spacing), HIWORD(spacing));
}
-// Retrieves the spacing between icons in pixels.
-// If small is TRUE, gets the spacing for the small icon
-// view, otherwise the large icon view.
int wxListCtrl::GetItemSpacing(bool isSmall) const
{
return ListView_GetItemSpacing(GetHwnd(), (BOOL) isSmall);
wxColour wxListCtrl::GetItemTextColour( long item ) const
{
- wxListItem info;
- info.m_itemId = item;
- GetItem( info );
- return info.GetTextColour();
+ wxColour col;
+ wxListItemInternalData *data = wxGetInternalData(this, item);
+ if ( data && data->attr )
+ col = data->attr->GetTextColour();
+
+ return col;
}
void wxListCtrl::SetItemBackgroundColour( long item, const wxColour &col )
wxColour wxListCtrl::GetItemBackgroundColour( long item ) const
{
- wxListItem info;
- info.m_itemId = item;
- GetItem( info );
- return info.GetBackgroundColour();
+ wxColour col;
+ wxListItemInternalData *data = wxGetInternalData(this, item);
+ if ( data && data->attr )
+ col = data->attr->GetBackgroundColour();
+
+ return col;
}
// Gets the number of selected items in the list control
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() )
{
// Deletes all items
bool wxListCtrl::DeleteAllItems()
{
+ FreeAllInternalData();
return ListView_DeleteAllItems(GetHwnd()) != 0;
}
// [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;
}
if (info.m_mask & wxLIST_MASK_DATA)
data->lParam = info.m_data;
- // check whether it has any custom attributes
- if ( info.HasAttributes() )
- {
+ // check whether it has any custom attributes
+ if ( info.HasAttributes() )
+ {
// take copy of attributes
data->attr = new wxListItemAttr(*info.GetAttributes());
- }
+
+ // and remember that we have some now...
+ m_hasAnyAttr = TRUE;
+ }
};
+ long rv = ListView_InsertItem(GetHwnd(), & item);
+
+ m_count++;
+ 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)
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,
bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
{
+
// prepare the event
// -----------------
event.m_col = nmHDR->iItem;
break;
+#if defined(__WXWINCE__) && _WIN32_WCE < 400
+ case GN_CONTEXTMENU:
+#endif //__WXWINCE__
case NM_RCLICK:
{
eventType = wxEVT_COMMAND_LIST_COL_RIGHT_CLICK;
// where did the click occur?
POINT ptClick;
+#if defined(__WXWINCE__) && _WIN32_WCE < 400
+ if(nmhdr->code == GN_CONTEXTMENU) {
+ ptClick = ((NMRGINFO*)nmhdr)->ptAction;
+ } else
+#endif //__WXWINCE__
if ( !::GetCursorPos(&ptClick) )
{
wxLogLastError(_T("GetCursorPos"));
// there's a GPF in Windows.
// By returning TRUE here, we avoid further processing
// of this strange message.
- if (info->iItem > GetColumnCount())
+ if ( (unsigned)info->iItem >= (unsigned)GetColumnCount() )
return TRUE;
}
// fall through
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 ( iItem != -1 )
- {
- wxListItemInternalData *internaldata =
- (wxListItemInternalData *) nmLV->lParam;
+ return TRUE;
+ }
- if ( internaldata )
- event.m_item.m_data = internaldata->lParam;
- }
- default:
- // fall through
- ;
+ // 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);
+
+ if ( internaldata )
+ event.m_item.m_data = internaldata->lParam;
}
+
switch ( nmhdr->code )
{
case LVN_BEGINRDRAG:
break;
// NB: we have to handle both *A and *W versions here because some
- // versions of comctl32.dll send ANSI message to an Unicode app
+ // versions of comctl32.dll send ANSI messages even to the
+ // Unicode windows
case LVN_BEGINLABELEDITA:
- {
- eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
- wxLV_ITEM item(((LV_DISPINFOA *)lParam)->item);
- wxConvertFromMSWListItem(GetHwnd(), event.m_item, item);
- event.m_itemIndex = event.m_item.m_itemId;
- }
- break;
case LVN_BEGINLABELEDITW:
{
+ wxLV_ITEM item;
+ if ( nmhdr->code == LVN_BEGINLABELEDITA )
+ {
+ item.Init(((LV_DISPINFOA *)lParam)->item);
+ }
+ else // LVN_BEGINLABELEDITW
+ {
+ item.Init(((LV_DISPINFOW *)lParam)->item);
+ }
+
eventType = wxEVT_COMMAND_LIST_BEGIN_LABEL_EDIT;
- wxLV_ITEM item(((LV_DISPINFOW *)lParam)->item);
wxConvertFromMSWListItem(GetHwnd(), event.m_item, item);
event.m_itemIndex = event.m_item.m_itemId;
}
break;
case LVN_ENDLABELEDITA:
- {
- eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
- wxLV_ITEM item(((LV_DISPINFOA *)lParam)->item);
- wxConvertFromMSWListItem(NULL, event.m_item, item);
- if ( ((LV_ITEM)item).pszText == NULL ||
- ((LV_ITEM)item).iItem == -1 )
- return FALSE;
-
- event.m_itemIndex = event.m_item.m_itemId;
- }
- break;
case LVN_ENDLABELEDITW:
{
+ wxLV_ITEM item;
+ if ( nmhdr->code == LVN_ENDLABELEDITA )
+ {
+ item.Init(((LV_DISPINFOA *)lParam)->item);
+ }
+ else // LVN_ENDLABELEDITW
+ {
+ item.Init(((LV_DISPINFOW *)lParam)->item);
+ }
+
+ // was editing cancelled?
+ const LV_ITEM& lvi = (LV_ITEM)item;
+ if ( !lvi.pszText || lvi.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;
+ }
+
+ event.SetEditCanceled(true);
+ }
+
eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
- wxLV_ITEM item(((LV_DISPINFOW *)lParam)->item);
wxConvertFromMSWListItem(NULL, event.m_item, item);
- if ( ((LV_ITEM)item).pszText == NULL ||
- ((LV_ITEM)item).iItem == -1 )
- return FALSE;
-
event.m_itemIndex = event.m_item.m_itemId;
}
break;
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
event.m_item.m_data = GetItemData(iItem);
break;
+#if defined(__WXWINCE__) && _WIN32_WCE < 400
+ case GN_CONTEXTMENU:
+#endif //__WXWINCE__
case NM_RCLICK:
// if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(),
// don't do anything else
LV_HITTESTINFO lvhti;
wxZeroMemory(lvhti);
+#if defined(__WXWINCE__) && _WIN32_WCE < 400
+ if(nmhdr->code == GN_CONTEXTMENU) {
+ lvhti.pt = ((NMRGINFO*)nmhdr)->ptAction;
+ } else
+#endif //__WXWINCE__
::GetCursorPos(&(lvhti.pt));
::ScreenToClient(GetHwnd(),&(lvhti.pt));
if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
}
break;
-#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 \
- && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+#ifdef NM_CUSTOMDRAW
case NM_CUSTOMDRAW:
*result = OnCustomDraw(lParam);
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;
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
event.SetEventType(eventType);
- if ( !GetEventHandler()->ProcessEvent(event) )
- return FALSE;
+ bool processed = GetEventHandler()->ProcessEvent(event);
// post processing
// ---------------
-
switch ( nmhdr->code )
{
case LVN_DELETEALLITEMS:
// notifications - this makes deleting all items from a list ctrl
// much faster
*result = TRUE;
-
return TRUE;
case LVN_ENDLABELEDITA:
{
// 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;
}
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)
{
// Reset the device origin since it may have been set
dc.SetDeviceOrigin(0, 0);
- bool drawHRules = ((GetWindowStyle() & wxLC_HRULES) != 0);
- bool drawVRules = ((GetWindowStyle() & wxLC_VRULES) != 0);
+ bool drawHRules = HasFlag(wxLC_HRULES);
+ bool drawVRules = HasFlag(wxLC_VRULES);
- if (!drawHRules && !drawVRules)
- return;
- if ((GetWindowStyle() & wxLC_REPORT) == 0)
+ if (!InReportView() || !drawHRules && !drawVRules)
return;
wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
{
// 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;
}
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;
}
{
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 | LVSICF_NOINVALIDATEALL) )
{
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)
RefreshRect(rect);
}
+// ----------------------------------------------------------------------------
+// internal data stuff
+// ----------------------------------------------------------------------------
+
static wxListItemInternalData *wxGetInternalData(HWND hwnd, long itemId)
{
LV_ITEM it;
it.mask = LVIF_PARAM;
it.iItem = itemId;
- bool success = ListView_GetItem(hwnd, &it) != 0;
- if (success)
- return (wxListItemInternalData *) it.lParam;
- else
+ if ( !ListView_GetItem(hwnd, &it) )
return NULL;
+
+ return (wxListItemInternalData *) it.lParam;
};
-static wxListItemInternalData *wxGetInternalData(wxListCtrl *ctl, long itemId)
+static
+wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId)
{
- return wxGetInternalData((HWND) ctl->GetHWND(), itemId);
+ return wxGetInternalData(GetHwndOf(ctl), itemId);
};
static wxListItemAttr *wxGetInternalDataAttr(wxListCtrl *ctl, long itemId)
{
wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
- if (data)
- return data->attr;
- else
- return NULL;
+
+ return data ? data->attr : NULL;
};
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;
}
}
+// ----------------------------------------------------------------------------
+// wxWin <-> MSW items conversions
+// ----------------------------------------------------------------------------
+
static void wxConvertFromMSWListItem(HWND hwndListCtrl,
wxListItem& info,
LV_ITEM& lvItem)
if (info.m_mask & wxLIST_MASK_TEXT)
{
lvItem.mask |= LVIF_TEXT;
- if ( ctrl->GetWindowStyleFlag() & wxLC_USER_TEXT )
+ if ( ctrl->HasFlag(wxLC_USER_TEXT) )
{
lvItem.pszText = LPSTR_TEXTCALLBACK;
}
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
+