#include "wx/vector.h"
#include "wx/msw/private.h"
+#include "wx/msw/private/keyboard.h"
#if defined(__WXWINCE__) && !defined(__HANDHELDPC__)
#include <ole2.h>
// init without conversion
void Init(LV_ITEM_NATIVE& item)
{
- wxASSERT_MSG( !m_pItem, _T("Init() called twice?") );
+ wxASSERT_MSG( !m_pItem, wxT("Init() called twice?") );
m_pItem = &item;
}
wxDECLARE_NO_COPY_CLASS(wxMSWListItemData);
};
-// Get the internal data structure
-static wxMSWListItemData *wxGetInternalData(HWND hwnd, long itemId);
-static wxMSWListItemData *wxGetInternalData(const wxListCtrl *ctl, long itemId);
-
-
#if wxUSE_EXTENDED_RTTI
WX_DEFINE_FLAGS( wxListCtrlStyle )
MAP_MODE_STYLE(wxLC_REPORT, LVS_REPORT)
wxASSERT_MSG( nModes == 1,
- _T("wxListCtrl style should have exactly one mode bit set") );
+ wxT("wxListCtrl style should have exactly one mode bit set") );
#undef MAP_MODE_STYLE
wstyle |= LVS_SORTASCENDING;
wxASSERT_MSG( !(style & wxLC_SORT_DESCENDING),
- _T("can't sort in ascending and descending orders at once") );
+ wxT("can't sort in ascending and descending orders at once") );
}
else if ( style & wxLC_SORT_DESCENDING )
wstyle |= LVS_SORTDESCENDING;
{
m_textCtrl->UnsubclassWin();
m_textCtrl->SetHWND(0);
- delete m_textCtrl;
- m_textCtrl = NULL;
+ wxDELETE(m_textCtrl);
}
}
{
const int numCols = GetColumnCount();
wxCHECK_MSG( order >= 0 && order < numCols, -1,
- _T("Column position out of bounds") );
+ wxT("Column position out of bounds") );
wxArrayInt indexArray(numCols);
if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
int wxListCtrl::GetColumnOrder(int col) const
{
const int numCols = GetColumnCount();
- wxASSERT_MSG( col >= 0 && col < numCols, _T("Column index out of bounds") );
+ wxASSERT_MSG( col >= 0 && col < numCols, wxT("Column index out of bounds") );
wxArrayInt indexArray(numCols);
if ( !ListView_GetColumnOrderArray(GetHwnd(), numCols, &indexArray[0]) )
return pos;
}
- wxFAIL_MSG( _T("no column with with given order?") );
+ wxFAIL_MSG( wxT("no column with with given order?") );
return -1;
}
const int numCols = GetColumnCount();
wxCHECK_MSG( orders.size() == (size_t)numCols, false,
- _T("wrong number of elements in column orders array") );
+ wxT("wrong number of elements in column orders array") );
return ListView_SetColumnOrderArray(GetHwnd(), numCols, &orders[0]) != 0;
}
{
const long id = info.GetId();
wxCHECK_MSG( id >= 0 && id < GetItemCount(), false,
- _T("invalid item index in SetItem") );
+ wxT("invalid item index in SetItem") );
LV_ITEM item;
wxConvertToMSWListItem(this, info, item);
{
if ( !ListView_SetItem(GetHwnd(), &item) )
{
- wxLogDebug(_T("ListView_SetItem() failed"));
+ wxLogDebug(wxT("ListView_SetItem() failed"));
return false;
}
if ( !::SendMessage(GetHwnd(), LVM_SETITEMSTATE,
(WPARAM)item, (LPARAM)&lvItem) )
{
- wxLogLastError(_T("ListView_SetItemState"));
+ wxLogLastError(wxT("ListView_SetItemState"));
return false;
}
}
// Gets the item text
-wxString wxListCtrl::GetItemText(long item) const
+wxString wxListCtrl::GetItemText(long item, int col) const
{
wxListItem info;
info.m_mask = wxLIST_MASK_TEXT;
info.m_itemId = item;
+ info.m_col = col;
if (!GetItem(info))
return wxEmptyString;
RECT rc;
if ( !ListView_GetViewRect(GetHwnd(), &rc) )
{
- wxLogDebug(_T("ListView_GetViewRect() failed."));
+ wxLogDebug(wxT("ListView_GetViewRect() failed."));
wxZeroMemory(rc);
}
}
else
{
- wxFAIL_MSG( _T("not implemented in this mode") );
+ wxFAIL_MSG( wxT("not implemented in this mode") );
}
return rect;
// completely bogus in this case), so we check item validity ourselves
wxCHECK_MSG( subItem == wxLIST_GETSUBITEMRECT_WHOLEITEM ||
(subItem >= 0 && subItem < GetColumnCount()),
- false, _T("invalid sub item index") );
+ false, wxT("invalid sub item index") );
// use wxCHECK_MSG against "item" too, for coherency with the generic implementation:
wxCHECK_MSG( item >= 0 && item < GetItemCount(), false,
- _T("invalid item in GetSubItemRect") );
+ wxT("invalid item in GetSubItemRect") );
int codeWin;
if ( code == wxLIST_RECT_BOUNDS )
codeWin = LVIR_LABEL;
else
{
- wxFAIL_MSG( _T("incorrect code in GetItemRect() / GetSubItemRect()") );
+ wxFAIL_MSG( wxT("incorrect code in GetItemRect() / GetSubItemRect()") );
codeWin = LVIR_BOUNDS;
}
// there is no way to retrieve the first sub item bounding rectangle using
// wxGetListCtrlSubItemRect() as 0 means the whole item, so we need to
// truncate it at first column ourselves
- if ( subItem == 0 )
+ if ( subItem == 0 && code == wxLIST_RECT_BOUNDS )
rect.width = GetColumnWidth(0);
return true;
{
if ( !ListView_DeleteItem(GetHwnd(), (int) item) )
{
- wxLogLastError(_T("ListView_DeleteItem"));
+ wxLogLastError(wxT("ListView_DeleteItem"));
return false;
}
// Deletes all items
bool wxListCtrl::DeleteAllItems()
{
- return ListView_DeleteAllItems(GetHwnd()) != 0;
+ // Calling ListView_DeleteAllItems() will always generate an event but we
+ // shouldn't do it if the control is empty
+ return !GetItemCount() || ListView_DeleteAllItems(GetHwnd()) != 0;
}
// Deletes all items
if ( !hWnd )
{
// failed to start editing
- delete m_textCtrl;
- m_textCtrl = NULL;
+ wxDELETE(m_textCtrl);
return NULL;
}
if ( !hwnd )
return false;
- if ( cancel )
- ::SetWindowText(hwnd, wxEmptyString); // dubious but better than nothing
-
- // we shouldn't destroy the control ourselves according to MSDN, which
- // proposes WM_CANCELMODE to do this, but it doesn't seem to work
- //
- // posting WM_CLOSE to it does seem to work without any side effects
- ::PostMessage(hwnd, WM_CLOSE, 0, 0);
+ // Newer versions of Windows have a special message for cancelling editing,
+ // use it if available.
+#ifdef ListView_CancelEditLabel
+ if ( cancel && (wxApp::GetComCtl32Version() >= 600) )
+ {
+ ListView_CancelEditLabel(GetHwnd());
+ }
+ else
+#endif // ListView_CancelEditLabel
+ {
+ // We shouldn't destroy the control ourselves according to MSDN, which
+ // proposes WM_CANCELMODE to do this, but it doesn't seem to work so
+ // emulate the corresponding user action instead.
+ ::SendMessage(hwnd, WM_KEYDOWN, cancel ? VK_ESCAPE : VK_RETURN, 0);
+ }
return true;
}
// -1 otherwise.
long wxListCtrl::InsertItem(const wxListItem& info)
{
- wxASSERT_MSG( !IsVirtual(), _T("can't be used with virtual controls") );
+ wxASSERT_MSG( !IsVirtual(), wxT("can't be used with virtual controls") );
LV_ITEM item;
wxConvertToMSWListItem(this, info, item);
}
}
- long rv = ListView_InsertItem(GetHwnd(), & item);
+ const long rv = ListView_InsertItem(GetHwnd(), & item);
+
+ // failing to insert the item is really unexpected
+ wxCHECK_MSG( rv != -1, rv, "failed to insert an item in wxListCtrl" );
m_count++;
wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),
{
if ( !ListView_Scroll(GetHwnd(), dx, dy) )
{
- wxLogDebug(_T("ListView_Scroll(%d, %d) failed"), dx, dy);
+ wxLogDebug(wxT("ListView_Scroll(%d, %d) failed"), dx, dy);
return false;
}
wxInternalDataCompareFunc,
(WPARAM) &internalData) )
{
- wxLogDebug(_T("ListView_SortItems() failed"));
+ wxLogDebug(wxT("ListView_SortItems() failed"));
return false;
}
#endif //__WXWINCE__
if ( !::GetCursorPos(ptClick) )
{
- wxLogLastError(_T("GetCursorPos"));
+ wxLogLastError(wxT("GetCursorPos"));
}
// we need to use listctrl coordinates for the event point so this is what
POINT ptClickHeader = *ptClick;
if ( !::ScreenToClient(nmhdr->hwndFrom, &ptClickHeader) )
{
- wxLogLastError(_T("ScreenToClient(listctrl header)"));
+ wxLogLastError(wxT("ScreenToClient(listctrl header)"));
}
if ( !::ScreenToClient(::GetParent(nmhdr->hwndFrom), ptClick) )
{
- wxLogLastError(_T("ScreenToClient(listctrl)"));
+ wxLogLastError(wxT("ScreenToClient(listctrl)"));
}
const int colCount = Header_GetItemCount(nmhdr->hwndFrom);
event.m_itemIndex = -1;
+ bool ignore = false;
switch ( nmhdr->code )
{
// yet another comctl32.dll bug: under NT/W2K it sends Unicode
// TRACK messages even to ANSI programs: on my system I get
- // HDN_BEGINTRACKW and HDN_ENDTRACKA and no HDN_TRACK at all!
+ // HDN_BEGINTRACKW and HDN_ENDTRACKA!
//
// work around is to simply catch both versions and hope that it
// works (why should this message exist in ANSI and Unicode is
// beyond me as it doesn't deal with strings at all...)
//
- // note that fr HDN_TRACK another possibility could be to use
- // HDN_ITEMCHANGING but it is sent even after HDN_ENDTRACK and when
- // something other than the item width changes so we'd have to
- // filter out the unwanted events then
+ // another problem is that HDN_TRACK is not sent at all by header
+ // with HDS_FULLDRAG style which is used by default by wxListCtrl
+ // under recent Windows versions (starting from at least XP) so we
+ // need to use HDN_ITEMCHANGING instead of it
case HDN_BEGINTRACKA:
case HDN_BEGINTRACKW:
eventType = wxEVT_COMMAND_LIST_COL_BEGIN_DRAG;
// fall through
- case HDN_TRACKA:
- case HDN_TRACKW:
+ case HDN_ITEMCHANGING:
if ( eventType == wxEVT_NULL )
+ {
+ if ( !nmHDR->pitem || !(nmHDR->pitem->mask & HDI_WIDTH) )
+ {
+ // something other than the width is being changed,
+ // ignore it
+ ignore = true;
+ break;
+ }
+
+ // also ignore the events sent when the width didn't really
+ // change: this is not just an optimization but also gets
+ // rid of a useless and unexpected DRAGGING event which
+ // would otherwise be sent after the END_DRAG one as we get
+ // an HDN_ITEMCHANGING after HDN_ENDTRACK for some reason
+ if ( nmHDR->pitem->cxy == GetColumnWidth(nmHDR->iItem) )
+ {
+ ignore = true;
+ break;
+ }
+
eventType = wxEVT_COMMAND_LIST_COL_DRAGGING;
+ }
// fall through
case HDN_ENDTRACKA:
return true;
default:
- return wxControl::MSWOnNotify(idCtrl, lParam, result);
+ ignore = true;
}
+
+ if ( ignore )
+ return wxControl::MSWOnNotify(idCtrl, lParam, result);
}
else
#endif // defined(HDN_BEGINTRACKA)
eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
event.m_itemIndex = iItem;
- // delete the associated internal data
- if ( wxMSWListItemData *data = MSWGetItemData(iItem) )
- {
- const unsigned count = m_internalData.size();
- for ( unsigned n = 0; n < count; n++ )
- {
- if ( m_internalData[n] == data )
- {
- m_internalData.erase(m_internalData.begin() + n);
- delete data;
- data = NULL;
- break;
- }
- }
-
- wxASSERT_MSG( data, "invalid internal data pointer?" );
- }
break;
case LVN_INSERTITEM:
{
eventType = wxEVT_COMMAND_LIST_KEY_DOWN;
- // wxCharCodeMSWToWX() returns 0 if the key is an ASCII
- // value which should be used as is
- int code = wxCharCodeMSWToWX(wVKey);
- event.m_code = code ? code : wVKey;
+ event.m_code = wxMSWKeyboard::VKToWX(wVKey);
+
+ if ( event.m_code == WXK_NONE )
+ {
+ // We can't translate this to a standard key code,
+ // until support for Unicode key codes is added to
+ // wxListEvent we just ignore them.
+ return false;
+ }
}
event.m_itemIndex =
wxZeroMemory(lvhti);
#if defined(__WXWINCE__) && !defined(__HANDHELDPC__) && _WIN32_WCE < 400
- if(nmhdr->code == GN_CONTEXTMENU) {
- lvhti.pt = ((NMRGINFO*)nmhdr)->ptAction;
- } else
+ 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 )
+ {
+ ::GetCursorPos(&(lvhti.pt));
+ }
+
+ ::ScreenToClient(GetHwnd(), &lvhti.pt);
+ if ( ListView_HitTest(GetHwnd(), &lvhti) != -1 )
{
if ( lvhti.flags & LVHT_ONITEM )
{
const int startPos = pFindInfo->iStart;
const int maxPos = GetItemCount();
wxCHECK_MSG( startPos <= maxPos, false,
- _T("bad starting position in LVN_ODFINDITEM") );
+ wxT("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
+ // somewhat surprisingly, LVFI_WRAP isn't set in
// flags but we still should wrap
currentPos = 0;
}
event.SetEventType(eventType);
+ // fill in the item before passing it to the event handler if we do have a
+ // valid item index and haven't filled it yet (e.g. for LVN_ITEMCHANGED)
+ if ( event.m_itemIndex != -1 && !event.m_item.GetMask() )
+ {
+ wxListItem& item = event.m_item;
+
+ item.SetId(event.m_itemIndex);
+ item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
+ GetItem(item);
+ }
+
bool processed = HandleWindowEvent(event);
// post processing
m_count = 0;
return true;
+ case LVN_DELETEITEM:
+ // Delete the associated internal data. Notice that this can be
+ // done only after the event has been handled as the data could be
+ // accessed during the handling of the event.
+ if ( wxMSWListItemData *data = MSWGetItemData(event.m_itemIndex) )
+ {
+ const unsigned count = m_internalData.size();
+ for ( unsigned n = 0; n < count; n++ )
+ {
+ if ( m_internalData[n] == data )
+ {
+ m_internalData.erase(m_internalData.begin() + n);
+ wxDELETE(data);
+ break;
+ }
+ }
+
+ wxASSERT_MSG( !data, "invalid internal data pointer?" );
+ }
+ break;
+
case LVN_ENDLABELEDITA:
case LVN_ENDLABELEDITW:
// logic here is inverted compared to all the other messages
numCols,
&indexArray[0]) )
{
- wxFAIL_MSG( _T("invalid column index array in OnPaint()") );
+ wxFAIL_MSG( wxT("invalid column index array in OnPaint()") );
return;
}
{
// 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("wxListCtrl::OnGetItemText not supposed to be called") );
+ wxFAIL_MSG( wxT("wxListCtrl::OnGetItemText not supposed to be called") );
return wxEmptyString;
}
wxListItemAttr *wxListCtrl::OnGetItemAttr(long WXUNUSED_UNLESS_DEBUG(item)) const
{
wxASSERT_MSG( item >= 0 && item < GetItemCount(),
- _T("invalid item index in OnGetItemAttr()") );
+ wxT("invalid item index in OnGetItemAttr()") );
// no attributes by default
return NULL;
void wxListCtrl::SetItemCount(long count)
{
- wxASSERT_MSG( IsVirtual(), _T("this is for virtual controls only") );
+ wxASSERT_MSG( IsVirtual(), wxT("this is for virtual controls only") );
if ( !::SendMessage(GetHwnd(), LVM_SETITEMCOUNT, (WPARAM)count,
LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL) )
{
- wxLogLastError(_T("ListView_SetItemCount"));
+ wxLogLastError(wxT("ListView_SetItemCount"));
}
m_count = count;
wxASSERT_MSG( m_count == ListView_GetItemCount(GetHwnd()),