#ifdef __GNUG__
#pragma implementation "listctrl.h"
+ #pragma implementation "listctrlbase.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/msw/private.h"
-#ifdef __GNUWIN32__
+#if defined(__GNUWIN32__) && !defined(wxUSE_NORLANDER_HEADERS)
#include "wx/msw/gnuwin32/extra.h"
#else
#include <commctrl.h>
#endif
+#ifndef LVHT_ONITEM
+ #define LVHT_ONITEM \
+ (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON)
+#endif
+
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
// macros
// ----------------------------------------------------------------------------
-#if !USE_SHARED_LIBRARY
- IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
- IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
-#endif // USE_SHARED_LIBRARY
+IMPLEMENT_DYNAMIC_CLASS(wxListCtrl, wxControl)
+IMPLEMENT_DYNAMIC_CLASS(wxListItem, wxObject)
// ============================================================================
// implementation
// ============================================================================
+// ----------------------------------------------------------------------------
+// wxListEvent
+// ----------------------------------------------------------------------------
+
+void wxListEvent::CopyObject(wxObject& object_dest) const
+{
+ wxListEvent *obj = (wxListEvent *)&object_dest;
+
+ wxNotifyEvent::CopyObject(object_dest);
+
+ obj->m_code = m_code;
+ obj->m_itemIndex = m_itemIndex;
+ obj->m_oldItemIndex = m_oldItemIndex;
+ obj->m_col = m_col;
+ obj->m_cancelled = m_cancelled;
+ obj->m_pointDrag = m_pointDrag;
+ obj->m_item.m_mask = m_item.m_mask;
+ obj->m_item.m_itemId = m_item.m_itemId;
+ obj->m_item.m_col = m_item.m_col;
+ obj->m_item.m_state = m_item.m_state;
+ obj->m_item.m_stateMask = m_item.m_stateMask;
+ obj->m_item.m_text = m_item.m_text;
+ obj->m_item.m_image = m_item.m_image;
+ obj->m_item.m_data = m_item.m_data;
+ obj->m_item.m_format = m_item.m_format;
+ obj->m_item.m_width = m_item.m_width;
+
+ if ( m_item.HasAttributes() )
+ {
+ obj->m_item.SetTextColour(m_item.GetTextColour());
+ obj->m_item.SetBackgroundColour(m_item.GetBackgroundColour());
+ obj->m_item.SetFont(m_item.GetFont());
+ }
+}
+
// ----------------------------------------------------------------------------
// wxListCtrl construction
// ----------------------------------------------------------------------------
-wxListCtrl::wxListCtrl()
+void wxListCtrl::Init()
{
m_imageListNormal = NULL;
m_imageListSmall = NULL;
m_baseStyle = 0;
m_colCount = 0;
m_textCtrl = NULL;
+ m_hasAnyAttr = FALSE;
}
bool wxListCtrl::Create(wxWindow *parent,
const wxValidator& validator,
const wxString& name)
{
- m_imageListNormal = NULL;
- m_imageListSmall = NULL;
- m_imageListState = NULL;
- m_textCtrl = NULL;
- m_colCount = 0;
-
+#if wxUSE_VALIDATORS
SetValidator(validator);
+#endif // wxUSE_VALIDATORS
+
SetName(name);
int x = pos.x;
// Create the ListView control.
m_hWnd = (WXHWND)CreateWindowEx(exStyle,
WC_LISTVIEW,
- _T(""),
+ wxT(""),
wstyle,
x, y, w, h,
GetWinHwnd(GetParent()),
if ( !m_hWnd )
{
- wxLogError(_T("Can't create list control window."));
+ wxLogError(wxT("Can't create list control window."));
return FALSE;
}
}
#endif // ListView_SetExtendedListViewStyle
- wxSystemSettings settings;
- SetBackgroundColour(settings.GetSystemColour(wxSYS_COLOUR_WINDOW));
+ SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
SetForegroundColour(GetParent()->GetForegroundColour());
SubclassWin(m_hWnd);
wxListCtrl::~wxListCtrl()
{
- if (m_textCtrl)
+ if ( m_hasAnyAttr )
+ {
+ for ( wxNode *node = m_attrs.Next(); node; node = m_attrs.Next() )
+ {
+ delete (wxListItemAttr *)node->Data();
+ }
+ }
+
+ if ( m_textCtrl )
{
m_textCtrl->UnsubclassWin();
m_textCtrl->SetHWND(0);
bool wxListCtrl::GetItem(wxListItem& info) const
{
LV_ITEM lvItem;
-#ifdef __GNUWIN32__
- memset(&lvItem, 0, sizeof(lvItem));
-#else
- ZeroMemory(&lvItem, sizeof(lvItem)); // must set all fields to 0
-#endif
+ wxZeroMemory(lvItem);
lvItem.iItem = info.m_itemId;
lvItem.iSubItem = info.m_col;
{
LV_ITEM item;
wxConvertToMSWListItem(this, info, item);
+
+ // check whether it has any custom attributes
+ if ( info.HasAttributes() )
+ {
+ m_attrs.Put(item.iItem, (wxObject *)new wxListItemAttr(*info.GetAttributes()));
+
+ m_hasAnyAttr = TRUE;
+ }
+
item.cchTextMax = 0;
bool ok = ListView_SetItem(GetHwnd(), &item) != 0;
if ( ok && (info.m_mask & wxLIST_MASK_IMAGE) )
m_colCount--;
}
- wxASSERT_MSG( m_colCount == 0, _T("no columns should be left") );
+ wxASSERT_MSG( m_colCount == 0, wxT("no columns should be left") );
return TRUE;
}
LV_ITEM item;
wxConvertToMSWListItem(this, info, item);
+ // check whether it has any custom attributes
+ if ( info.HasAttributes() )
+ {
+ m_attrs.Put(item.iItem, (wxObject *)new wxListItemAttr(*info.GetAttributes()));
+
+ m_hasAnyAttr = TRUE;
+ }
+
return (long) ListView_InsertItem(GetHwnd(), & item);
}
}
else
{
- wxLogDebug(_T("Failed to insert the column '%s' into listview!"),
+ wxLogDebug(wxT("Failed to insert the column '%s' into listview!"),
lvCol.pszText);
}
return success;
}
-long wxListCtrl::InsertColumn(long col, const wxString& heading, int format,
- int width)
+long wxListCtrl::InsertColumn(long col,
+ const wxString& heading,
+ int format,
+ int width)
{
wxListItem item;
item.m_mask = wxLIST_MASK_TEXT | wxLIST_MASK_FORMAT;
bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
{
+ // prepare the event
+ // -----------------
+
wxListEvent event(wxEVT_NULL, m_windowId);
wxEventType eventType = wxEVT_NULL;
- NMHDR *hdr1 = (NMHDR *) lParam;
- switch ( hdr1->code )
+ NMHDR *nmhdr = (NMHDR *)lParam;
+ switch ( nmhdr->code )
{
case LVN_BEGINRDRAG:
eventType = wxEVT_COMMAND_LIST_BEGIN_RDRAG;
event.m_col = hdr->iSubItem;
break;
}
+
case LVN_DELETEALLITEMS:
- {
- eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
- // NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
- event.m_itemIndex = -1;
- break;
- }
+ // What's the sense of generating a wxWin event for this when
+ // it's absolutely not portable?
+ // This is perfectly portable, RR
+#if 1
+ eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS;
+ event.m_itemIndex = -1;
+#endif // 1
+
+ // return TRUE to suppress all additional LVN_DELETEITEM
+ // notifications - this makes deleting all items from a list ctrl
+ // much faster
+ *result = TRUE;
+
+ break;
+
case LVN_DELETEITEM:
{
eventType = wxEVT_COMMAND_LIST_DELETE_ITEM;
NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
event.m_itemIndex = hdr->iItem;
- break;
+
+ if ( m_hasAnyAttr )
+ {
+ delete (wxListItemAttr *)m_attrs.Delete(hdr->iItem);
+ }
}
+ break;
+
case LVN_ENDLABELEDIT:
{
eventType = wxEVT_COMMAND_LIST_END_LABEL_EDIT;
LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
+ wxConvertFromMSWListItem(this, event.m_item, info->item);
if ( info->item.pszText == NULL || info->item.iItem == -1 )
- event.m_cancelled = TRUE;
- break;
+ return FALSE;
}
- case LVN_GETDISPINFO:
- return FALSE;
+ break;
+ case LVN_SETDISPINFO:
+ {
+ eventType = wxEVT_COMMAND_LIST_SET_INFO;
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+ wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
+ }
+ break;
+
+ case LVN_GETDISPINFO:
// this provokes stack overflow: indeed, wxConvertFromMSWListItem()
// sends us WM_NOTIFY! As it doesn't do anything for now, just leave
// it out.
break;
}
#endif // 0
+ return FALSE;
+
case LVN_INSERTITEM:
{
event.m_itemIndex = hdr->iItem;
break;
}
+
case LVN_ITEMCHANGED:
{
// This needs to be sent to wxListCtrl as a rather more
}
// else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event
- eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
+ // if it happened on an item (and not on empty place)
+ {
+ NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam;
+ if ( hdr->iItem == -1 )
+ {
+ // not on item
+ return FALSE;
+ }
+
+ eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED;
+ event.m_itemIndex = hdr->iItem;
+ }
break;
- case LVN_SETDISPINFO:
+ case NM_RCLICK:
+ /* TECH NOTE: NM_RCLICK isn't really good enough here. We want to
+ subclass and check for the actual WM_RBUTTONDOWN message, because
+ NM_RCLICK waits for the WM_RBUTTONUP message as well before firing off.
+ We want to have notify events for both down -and- up. */
+ {
+ // if the user processes it in wxEVT_COMMAND_RIGHT_CLICK(), don't do
+ // anything else
+ if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) {
+ return TRUE;
+ }
+
+ // else translate it into wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK event
+ LV_HITTESTINFO lvhti;
+ wxZeroMemory(lvhti);
+
+ ::GetCursorPos(&(lvhti.pt));
+ ::ScreenToClient(GetHwnd(),&(lvhti.pt));
+ if ( ListView_HitTest(GetHwnd(),&lvhti) != -1 )
{
- eventType = wxEVT_COMMAND_LIST_SET_INFO;
- LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- wxConvertFromMSWListItem(this, event.m_item, info->item, GetHwnd());
- break;
+ if ( lvhti.flags & LVHT_ONITEM )
+ {
+ eventType = wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK;
+ event.m_itemIndex = lvhti.iItem;
+ }
}
+ }
+ break;
+
+#if 0
+ case NM_MCLICK: // ***** THERE IS NO NM_MCLICK. Subclass anyone? ******
+ {
+ // if the user processes it in wxEVT_COMMAND_MIDDLE_CLICK(), don't do
+ // anything else
+ if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
+ {
+ return TRUE;
+ }
+
+ // else translate it into wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK event
+ eventType = wxEVT_COMMAND_LIST_ITEM_MIDDLE_CLICK;
+ NMITEMACTIVATE* hdr = (NMITEMACTIVATE*)lParam;
+ event.m_itemIndex = hdr->iItem;
+ }
+ break;
+#endif // 0
+
+#if defined(_WIN32_IE) && _WIN32_IE >= 0x300
+ case NM_CUSTOMDRAW:
+ {
+ LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam;
+ NMCUSTOMDRAW& nmcd = lplvcd->nmcd;
+ switch( nmcd.dwDrawStage )
+ {
+ case CDDS_PREPAINT:
+ // if we've got any items with non standard attributes,
+ // notify us before painting each item
+ *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
+ : CDRF_DODEFAULT;
+ return TRUE;
+
+ case CDDS_ITEMPREPAINT:
+ {
+ wxListItemAttr *attr =
+ (wxListItemAttr *)m_attrs.Get(nmcd.dwItemSpec);
+
+ if ( !attr )
+ {
+ // nothing to do for this item
+ return CDRF_DODEFAULT;
+ }
+
+ HFONT hFont;
+ wxColour colText, colBack;
+ if ( attr->HasFont() )
+ {
+ wxFont font = attr->GetFont();
+ hFont = (HFONT)font.GetResourceHandle();
+ }
+ else
+ {
+ hFont = 0;
+ }
+
+ if ( attr->HasTextColour() )
+ {
+ colText = attr->GetTextColour();
+ }
+ else
+ {
+ colText = GetTextColour();
+ }
+
+ if ( attr->HasBackgroundColour() )
+ {
+ colBack = attr->GetBackgroundColour();
+ }
+ else
+ {
+ colBack = GetBackgroundColour();
+ }
+
+ // note that if we wanted to set colours for
+ // individual columns (subitems), we would have
+ // returned CDRF_NOTIFYSUBITEMREDRAW from here
+ if ( hFont )
+ {
+ ::SelectObject(nmcd.hdc, hFont);
+
+ *result = CDRF_NEWFONT;
+ }
+ else
+ {
+ *result = CDRF_DODEFAULT;
+ }
+
+ lplvcd->clrText = wxColourToRGB(colText);
+ lplvcd->clrTextBk = wxColourToRGB(colBack);
+
+ return TRUE;
+ }
+
+ default:
+ *result = CDRF_DODEFAULT;
+ return TRUE;
+ }
+ }
+ break;
+#endif // _WIN32_IE >= 0x300
default:
return wxControl::MSWOnNotify(idCtrl, lParam, result);
}
+ // process the event
+ // -----------------
+
event.SetEventObject( this );
event.SetEventType(eventType);
if ( !GetEventHandler()->ProcessEvent(event) )
return FALSE;
- if (hdr1->code == LVN_GETDISPINFO)
+ if (eventType == wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS)
{
- LV_DISPINFO *info = (LV_DISPINFO *)lParam;
- if ( info->item.mask & LVIF_TEXT )
- {
- if ( !event.m_item.m_text.IsNull() )
+ // No postprocessing, because we want *return to
+ // be TRUE so that no further DeleteItem events
+ // are sent, RR.
+ return TRUE;
+ }
+
+ // post processing
+ // ---------------
+
+ switch ( (int)nmhdr->code )
+ {
+ case LVN_GETDISPINFO:
+ {
+ LV_DISPINFO *info = (LV_DISPINFO *)lParam;
+ if ( info->item.mask & LVIF_TEXT )
+ {
+ if ( !event.m_item.m_text.IsNull() )
+ {
+ info->item.pszText = AddPool(event.m_item.m_text);
+ info->item.cchTextMax = wxStrlen(info->item.pszText) + 1;
+ }
+ }
+ // wxConvertToMSWListItem(this, event.m_item, info->item);
+ break;
+ }
+ case LVN_ENDLABELEDIT:
{
- info->item.pszText = AddPool(event.m_item.m_text);
- info->item.cchTextMax = wxStrlen(info->item.pszText) + 1;
+ *result = event.IsAllowed();
+ return TRUE;
}
- }
- // wxConvertToMSWListItem(this, event.m_item, info->item);
+ default:
+ break;
}
*result = !event.IsAllowed();
m_format = wxLIST_FORMAT_CENTRE;
m_width = 0;
+
+ m_attr = NULL;
+}
+
+void wxListItem::Clear()
+{
+ 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_text = wxEmptyString;
+
+ if (m_attr) delete m_attr;
+ m_attr = NULL;
+}
+
+void wxListItem::ClearAttributes()
+{
+ if (m_attr) delete m_attr;
+ m_attr = NULL;
}
static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& lvItem, HWND getFullInfo)
{
m_code = 0;
m_itemIndex = 0;
+ m_oldItemIndex = 0;
m_col = 0;
m_cancelled = FALSE;
}