// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "listctrl.h"
- #pragma implementation "listctrlbase.h"
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
// include <commctrl.h> "properly"
#include "wx/msw/wrapcctl.h"
+// Currently gcc and watcom don't define NMLVFINDITEM, and DMC only defines
+// it by its old name NM_FINDTIEM.
+//
+#if defined(__VISUALC__) || defined(__BORLANDC__) || defined(NMLVFINDITEM)
+ #define HAVE_NMLVFINDITEM 1
+#elif defined(__DMC__) || defined(NM_FINDITEM)
+ #define HAVE_NMLVFINDITEM 1
+ #define NMLVFINDITEM NM_FINDITEM
+#endif
+
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
}
// init with conversion
- void Init(LV_ITEM_OTHER& item)
+ void Init(const LV_ITEM_OTHER& 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);
+ CodeCantWorkIfDiffSizes);
memcpy(&m_item, &item, sizeof(LV_ITEM_NATIVE));
//
// Solution:
// Under MSW the only way to associate data with a List
-// item independant of its position in the list is to
+// item independent of its position in the list is to
// store a pointer to it in its lParam attribute. However
// user programs are already using this (via the
// SetItemData() GetItemData() calls).
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_END_LABEL_EDIT)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ITEM)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS)
+#if WXWIN_COMPATIBILITY_2_4
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_GET_INFO)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_SET_INFO)
+#endif
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_SELECTED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_ITEM_DESELECTED)
DEFINE_EVENT_TYPE(wxEVT_COMMAND_LIST_KEY_DOWN)
if ( !CreateControl(parent, id, pos, size, style, validator, name) )
return false;
- if ( !MSWCreateControl(WC_LISTVIEW, _T(""), pos, size) )
+ if ( !MSWCreateControl(WC_LISTVIEW, wxEmptyString, pos, size) )
return false;
// 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());
- // 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 )
+ // for comctl32.dll v 4.70+ we want to have some non default extended
+ // styles because it's prettier (and also because wxGTK does it like this)
+ if ( InReportView() && wxApp::GetComCtl32Version() >= 470 )
{
::SendMessage(GetHwnd(), LVM_SETEXTENDEDLISTVIEWSTYLE,
- 0, LVS_EX_FULLROWSELECT);
+ 0, LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT);
}
return true;
#if !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
if ( style & wxLC_VIRTUAL )
{
- int ver = wxTheApp->GetComCtl32Version();
+ int ver = wxApp::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."),
m_textCtrl = NULL;
}
- if (m_ownsImageListNormal) delete m_imageListNormal;
- if (m_ownsImageListSmall) delete m_imageListSmall;
- if (m_ownsImageListState) delete m_imageListState;
+ if (m_ownsImageListNormal)
+ delete m_imageListNormal;
+ if (m_ownsImageListSmall)
+ delete m_imageListSmall;
+ if (m_ownsImageListState)
+ delete m_imageListState;
}
// ----------------------------------------------------------------------------
}
// Sets information about this column
-bool wxListCtrl::SetColumn(int col, wxListItem& item)
+bool wxListCtrl::SetColumn(int col, const wxListItem& item)
{
LV_COLUMN lvCol;
wxConvertToMSWListCol(col, item, lvCol);
// Sets the column width
bool wxListCtrl::SetColumnWidth(int col, int width)
{
- int col2 = col;
if ( m_windowStyle & wxLC_LIST )
- col2 = -1;
+ col = 0;
- int width2 = width;
- if ( width2 == wxLIST_AUTOSIZE)
- width2 = LVSCW_AUTOSIZE;
- else if ( width2 == wxLIST_AUTOSIZE_USEHEADER)
- width2 = LVSCW_AUTOSIZE_USEHEADER;
+ if ( width == wxLIST_AUTOSIZE)
+ width = LVSCW_AUTOSIZE;
+ else if ( width == wxLIST_AUTOSIZE_USEHEADER)
+ width = LVSCW_AUTOSIZE_USEHEADER;
- return ListView_SetColumnWidth(GetHwnd(), col2, width2) != 0;
+ return ListView_SetColumnWidth(GetHwnd(), col, width) != 0;
}
// Gets the number of items that can fit vertically in the
if ( info.m_mask & wxLIST_MASK_STATE )
{
lvItem.mask |= LVIF_STATE;
- // the other bits are hardly interesting anyhow
- lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
+ wxConvertToMSWFlags(0, info.m_stateMask, lvItem);
}
bool success = ListView_GetItem((HWND)GetHWND(), &lvItem) != 0;
}
// Gets the item data
-long wxListCtrl::GetItemData(long item) const
+wxUIntPtr wxListCtrl::GetItemData(long item) const
{
wxListItem info;
return col;
}
+void wxListCtrl::SetItemFont( long item, const wxFont &f )
+{
+ wxListItem info;
+ info.m_itemId = item;
+ info.SetFont( f );
+ SetItem( info );
+}
+
+wxFont wxListCtrl::GetItemFont( long item ) const
+{
+ wxFont f;
+ wxListItemInternalData *data = wxGetInternalData(this, item);
+ if ( data && data->attr )
+ f = data->attr->GetFont();
+
+ return f;
+}
+
// Gets the number of selected items in the list control
int wxListCtrl::GetSelectedItemCount() const
{
// Gets the text colour of the listview
wxColour wxListCtrl::GetTextColour() const
{
- COLORREF ref = ListView_GetTextColor(GetHwnd());
- wxColour col(GetRValue(ref), GetGValue(ref), GetBValue(ref));
- return col;
+ // Use GetDefaultAttributes instead of ListView_GetTextColor because
+ // the latter seems to return black all the time (instead of the theme color)
+ return GetDefaultAttributes().colFg;
}
// Sets the text colour of the listview
return false;
}
- m_count -= 1;
+ m_count--;
wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
wxT("m_count should match ListView_GetItemCount"));
// Deletes all items
bool wxListCtrl::DeleteAllItems()
{
- FreeAllInternalData();
return ListView_DeleteAllItems(GetHwnd()) != 0;
}
// NOTE : Lindsay Mathieson - 14-July-2002
// No longer use ListView_FindItem as the data attribute is now stored
// in a wxListItemInternalData structure refernced by the actual lParam
-long wxListCtrl::FindItem(long start, long data)
+long wxListCtrl::FindItem(long start, wxUIntPtr data)
{
long idx = start + 1;
long count = GetItemCount();
ListView_HitTest(GetHwnd(), & hitTestInfo);
flags = 0;
+
if ( hitTestInfo.flags & LVHT_ABOVE )
flags |= wxLIST_HITTEST_ABOVE;
if ( hitTestInfo.flags & LVHT_BELOW )
flags |= wxLIST_HITTEST_BELOW;
- if ( hitTestInfo.flags & LVHT_NOWHERE )
- flags |= wxLIST_HITTEST_NOWHERE;
- if ( hitTestInfo.flags & LVHT_ONITEMICON )
- flags |= wxLIST_HITTEST_ONITEMICON;
- if ( hitTestInfo.flags & LVHT_ONITEMLABEL )
- flags |= wxLIST_HITTEST_ONITEMLABEL;
- if ( hitTestInfo.flags & LVHT_ONITEMSTATEICON )
- flags |= wxLIST_HITTEST_ONITEMSTATEICON;
if ( hitTestInfo.flags & LVHT_TOLEFT )
flags |= wxLIST_HITTEST_TOLEFT;
if ( hitTestInfo.flags & LVHT_TORIGHT )
flags |= wxLIST_HITTEST_TORIGHT;
+ if ( hitTestInfo.flags & LVHT_NOWHERE )
+ flags |= wxLIST_HITTEST_NOWHERE;
+
+ // note a bug or at least a very strange feature of comtl32.dll (tested
+ // with version 4.0 under Win95 and 6.0 under Win 2003): if you click to
+ // the right of the item label, ListView_HitTest() returns a combination of
+ // LVHT_ONITEMICON, LVHT_ONITEMLABEL and LVHT_ONITEMSTATEICON -- filter out
+ // the bits which don't make sense
+ if ( hitTestInfo.flags & LVHT_ONITEMLABEL )
+ {
+ flags |= wxLIST_HITTEST_ONITEMLABEL;
+
+ // do not translate LVHT_ONITEMICON here, as per above
+ }
+ else
+ {
+ if ( hitTestInfo.flags & LVHT_ONITEMICON )
+ flags |= wxLIST_HITTEST_ONITEMICON;
+ if ( hitTestInfo.flags & LVHT_ONITEMSTATEICON )
+ flags |= wxLIST_HITTEST_ONITEMSTATEICON;
+ }
+
return (long) hitTestInfo.iItem;
}
// Inserts an item, returning the index of the new item if successful,
// -1 otherwise.
-long wxListCtrl::InsertItem(wxListItem& info)
+long wxListCtrl::InsertItem(const wxListItem& info)
{
wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") );
}
// For list view mode (only), inserts a column.
-long wxListCtrl::InsertColumn(long col, wxListItem& item)
+long wxListCtrl::InsertColumn(long col, const wxListItem& item)
{
LV_COLUMN lvCol;
wxConvertToMSWListCol(col, item, lvCol);
return internalData->user_fn(d1, d2, internalData->data);
-};
+}
bool wxListCtrl::SortItems(wxListCtrlCompare fn, long data)
{
break;
case HDN_GETDISPINFOW:
- {
- LPNMHDDISPINFOW info = (LPNMHDDISPINFOW) lParam;
- // This is a fix for a strange bug under XP.
- // Normally, info->iItem is a valid index, but
- // sometimes this is a silly (large) number
- // and when we return false via wxControl::MSWOnNotify
- // to indicate that it hasn't yet been processed,
- // there's a GPF in Windows.
- // By returning true here, we avoid further processing
- // of this strange message.
- if ( (unsigned)info->iItem >= (unsigned)GetColumnCount() )
- return true;
- }
- // fall through
+ // letting Windows XP handle this message results in mysterious
+ // crashes in comctl32.dll seemingly because of bad message
+ // parameters
+ //
+ // I have no idea what is the real cause of the bug (which is,
+ // just to make things interesting, is impossible to reproduce
+ // reliably) but ignoring all these messages does fix it and
+ // doesn't seem to have any negative consequences
+ return true;
default:
return wxControl::MSWOnNotify(idCtrl, lParam, result);
// 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)))
+ ( (nmLV->hdr.code == LVN_ITEMCHANGED) ||
+ (nmLV->hdr.code == LVN_ITEMCHANGING)) )
{
return true;
}
event.m_item.m_data = internaldata->lParam;
}
-
+ bool processed = true;
switch ( nmhdr->code )
{
case LVN_BEGINRDRAG:
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"
+ 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;
wxDeleteInternalData(this, iItem);
break;
+#if WXWIN_COMPATIBILITY_2_4
case LVN_SETDISPINFO:
{
eventType = wxEVT_COMMAND_LIST_SET_INFO;
wxConvertFromMSWListItem(GetHwnd(), event.m_item, info->item);
}
break;
+#endif
case LVN_INSERTITEM:
eventType = wxEVT_COMMAND_LIST_INSERT_ITEM;
}
break;
+#ifdef HAVE_NMLVFINDITEM
+ case LVN_ODFINDITEM:
+ // this message is only used with the virtual list control but
+ // even there we don't want to always use it: in a control with
+ // sufficiently big number of items (defined as > 1000 here),
+ // accidentally pressing a key could result in hanging an
+ // application waiting while it performs linear search
+ if ( IsVirtual() && GetItemCount() <= 1000 )
+ {
+ NMLVFINDITEM* pFindInfo = (NMLVFINDITEM*)lParam;
+
+ // no match by default
+ *result = -1;
+
+ // we only handle string-based searches here
+ //
+ // TODO: what about LVFI_PARTIAL, should we handle this?
+ if ( !(pFindInfo->lvfi.flags & LVFI_STRING) )
+ {
+ return false;
+ }
+
+ const wxChar * const searchstr = pFindInfo->lvfi.psz;
+ const size_t len = wxStrlen(searchstr);
+
+ // this is the first item we should examine, search from it
+ // wrapping if necessary
+ const int startPos = pFindInfo->iStart;
+ const int maxPos = GetItemCount();
+ wxCHECK_MSG( startPos <= maxPos, false,
+ _T("bad starting position in LVN_ODFINDITEM") );
+
+ int currentPos = startPos;
+ do
+ {
+ // wrap to the beginning if necessary
+ if ( currentPos == maxPos )
+ {
+ // somewhat surprizingly, LVFI_WRAP isn't set in
+ // flags but we still should wrap
+ currentPos = 0;
+ }
+
+ // does this item begin with searchstr?
+ if ( wxStrnicmp(searchstr,
+ GetItemText(currentPos), len) == 0 )
+ {
+ *result = currentPos;
+ break;
+ }
+ }
+ while ( ++currentPos != startPos );
+
+ if ( *result == -1 )
+ {
+ // not found
+ return false;
+ }
+
+ SetItemState(*result,
+ wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED,
+ wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
+ EnsureVisible(*result);
+ return true;
+ }
+ else
+ {
+ processed = false;
+ }
+ break;
+#endif // HAVE_NMLVFINDITEM
+
case LVN_GETDISPINFO:
if ( IsVirtual() )
{
// fall through
default:
- return wxControl::MSWOnNotify(idCtrl, lParam, result);
+ processed = false;
}
+
+ if ( !processed )
+ return wxControl::MSWOnNotify(idCtrl, lParam, result);
}
else
{
// notifications - this makes deleting all items from a list ctrl
// much faster
*result = TRUE;
+
+ // also, we may free all user data now (couldn't do it before as
+ // the user should have access to it in OnDeleteAllItems() handler)
+ FreeAllInternalData();
+
+ // the control is empty now, synchronize the cached number of items
+ // with the real one
+ m_count = 0;
return true;
case LVN_ENDLABELEDITA:
case LVN_ENDLABELEDITW:
- // logic here is inversed compared to all the other messages
+ // logic here is inverted compared to all the other messages
*result = event.IsAllowed();
// don't keep a stale wxTextCtrl around
}
}
+WXLRESULT
+wxListCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+#ifdef WM_PRINT
+ if ( nMsg == WM_PRINT )
+ {
+ // we should bypass our own WM_PRINT handling as we don't handle
+ // PRF_CHILDREN flag, so leave it to the native control itself
+ return MSWDefWindowProc(nMsg, wParam, lParam);
+ }
+#endif // WM_PRINT
+
+ return wxControl::MSWWindowProc(nMsg, wParam, lParam);
+}
+
// ----------------------------------------------------------------------------
// virtual list controls
// ----------------------------------------------------------------------------
int wxListCtrl::OnGetItemImage(long WXUNUSED(item)) const
{
- // same as above
- wxFAIL_MSG( _T("wxListCtrl::OnGetItemImage not supposed to be called") );
-
+ wxCHECK_MSG(!GetImageList(wxIMAGE_LIST_SMALL),
+ -1,
+ wxT("List control has an image list, OnGetItemImage should be overridden."));
return -1;
}
return NULL;
return (wxListItemInternalData *) it.lParam;
-};
+}
static
wxListItemInternalData *wxGetInternalData(const wxListCtrl *ctl, long itemId)
{
return wxGetInternalData(GetHwndOf(ctl), itemId);
-};
+}
static wxListItemAttr *wxGetInternalDataAttr(wxListCtrl *ctl, long itemId)
{
wxListItemInternalData *data = wxGetInternalData(ctl, itemId);
return data ? data->attr : NULL;
-};
+}
static void wxDeleteInternalData(wxListCtrl* ctl, long itemId)
{
//
// 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;
+ if ( item.m_image != -1 )
+ lvCol.fmt |= LVCFMT_BITMAP_ON_RIGHT | LVCFMT_IMAGE;
lvCol.iImage = item.m_image;
}