X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7cc98b3e3fbe91688e1ff8852a03e589aec5c866..58b1642453c5858a4dd29425b065e9215cb8ec79:/src/msw/listctrl.cpp diff --git a/src/msw/listctrl.cpp b/src/msw/listctrl.cpp index 66a752db4c..3d4eb964bb 100644 --- a/src/msw/listctrl.cpp +++ b/src/msw/listctrl.cpp @@ -39,12 +39,17 @@ #include "wx/msw/private.h" -#ifdef __GNUWIN32__ +#if defined(__GNUWIN32__) && !defined(wxUSE_NORLANDER_HEADERS) #include "wx/msw/gnuwin32/extra.h" #else #include #endif +#ifndef LVHT_ONITEM + #define LVHT_ONITEM \ + (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON) +#endif + // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- @@ -69,7 +74,7 @@ static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, L // wxListCtrl construction // ---------------------------------------------------------------------------- -wxListCtrl::wxListCtrl() +void wxListCtrl::Init() { m_imageListNormal = NULL; m_imageListSmall = NULL; @@ -77,6 +82,7 @@ wxListCtrl::wxListCtrl() m_baseStyle = 0; m_colCount = 0; m_textCtrl = NULL; + m_hasAnyAttr = FALSE; } bool wxListCtrl::Create(wxWindow *parent, @@ -87,12 +93,6 @@ 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; - SetValidator(validator); SetName(name); @@ -149,7 +149,7 @@ bool wxListCtrl::DoCreateControl(int x, int y, int w, int h) // Create the ListView control. m_hWnd = (WXHWND)CreateWindowEx(exStyle, WC_LISTVIEW, - _T(""), + wxT(""), wstyle, x, y, w, h, GetWinHwnd(GetParent()), @@ -159,7 +159,7 @@ bool wxListCtrl::DoCreateControl(int x, int y, int w, int h) if ( !m_hWnd ) { - wxLogError(_T("Can't create list control window.")); + wxLogError(wxT("Can't create list control window.")); return FALSE; } @@ -174,8 +174,7 @@ bool wxListCtrl::DoCreateControl(int x, int y, int w, int h) } #endif // ListView_SetExtendedListViewStyle - wxSystemSettings settings; - SetBackgroundColour(settings.GetSystemColour(wxSYS_COLOUR_WINDOW)); + SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); SetForegroundColour(GetParent()->GetForegroundColour()); SubclassWin(m_hWnd); @@ -192,10 +191,10 @@ void wxListCtrl::UpdateStyle() DWORD dwStyleNew = ConvertToMSWStyle(dummy, m_windowStyle); dwStyleNew |= m_baseStyle; - // Get the current window style. + // Get the current window style. DWORD dwStyleOld = ::GetWindowLong(GetHwnd(), GWL_STYLE); - // Only set the window style if the view bits have changed. + // Only set the window style if the view bits have changed. if ( dwStyleOld != dwStyleNew ) { ::SetWindowLong(GetHwnd(), GWL_STYLE, dwStyleNew); @@ -205,7 +204,15 @@ void wxListCtrl::UpdateStyle() 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); @@ -498,11 +505,7 @@ wxTextCtrl* wxListCtrl::GetEditControl(void) const 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; @@ -550,6 +553,15 @@ bool wxListCtrl::SetItem(wxListItem& info) { 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) ) @@ -741,7 +753,7 @@ wxColour wxListCtrl::GetTextColour(void) const // Sets the text colour of the listview void wxListCtrl::SetTextColour(const wxColour& col) { - ListView_SetTextColor(GetHwnd(), PALETTERGB(col.Red(), col.Blue(), col.Green())); + ListView_SetTextColor(GetHwnd(), PALETTERGB(col.Red(), col.Green(), col.Blue())); } // Gets the index of the topmost visible item when in @@ -872,7 +884,7 @@ bool wxListCtrl::DeleteAllColumns() 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; } @@ -1031,6 +1043,14 @@ long wxListCtrl::InsertItem(wxListItem& info) 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); } @@ -1117,15 +1137,17 @@ long wxListCtrl::InsertColumn(long col, wxListItem& 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; @@ -1192,10 +1214,13 @@ bool wxListCtrl::MSWCommand(WXUINT cmd, WXWORD id) 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; @@ -1231,32 +1256,54 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) 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? +#if 0 + eventType = wxEVT_COMMAND_LIST_DELETE_ALL_ITEMS; + event.m_itemIndex = -1; +#endif // 0 + + // return TRUE to suppress all additional LVN_DELETEITEM + // notifications - this makes deleting all items from a list ctrl + // much faster + *result = TRUE; + + return TRUE; + 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 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. @@ -1271,6 +1318,8 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; } #endif // 0 + return FALSE; + case LVN_INSERTITEM: { @@ -1279,6 +1328,7 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.m_itemIndex = hdr->iItem; break; } + case LVN_ITEMCHANGED: { // This needs to be sent to wxListCtrl as a rather more @@ -1334,39 +1384,182 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } // else translate it into wxEVT_COMMAND_LIST_ITEM_ACTIVATED event - eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED; + { + eventType = wxEVT_COMMAND_LIST_ITEM_ACTIVATED; + NM_LISTVIEW* hdr = (NM_LISTVIEW*)lParam; + 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 + +#ifdef NM_CUSTOMDRAW + 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 // NM_CUSTOMDRAW 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) + // post processing + // --------------- + + switch ( (int)nmhdr->code ) { - LV_DISPINFO *info = (LV_DISPINFO *)lParam; - if ( info->item.mask & LVIF_TEXT ) - { - if ( !event.m_item.m_text.IsNull() ) + case LVN_GETDISPINFO: { - info->item.pszText = AddPool(event.m_item.m_text); - info->item.cchTextMax = wxStrlen(info->item.pszText) + 1; + 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; } - } - // wxConvertToMSWListItem(this, event.m_item, info->item); + case LVN_ENDLABELEDIT: + { + *result = event.IsAllowed(); + return TRUE; + } + default: + break; } *result = !event.IsAllowed(); @@ -1404,6 +1597,8 @@ wxListItem::wxListItem() m_format = wxLIST_FORMAT_CENTRE; m_width = 0; + + m_attr = NULL; } static void wxConvertFromMSWListItem(const wxListCtrl *ctrl, wxListItem& info, LV_ITEM& lvItem, HWND getFullInfo) @@ -1561,6 +1756,7 @@ wxListEvent::wxListEvent(wxEventType commandType, int id) { m_code = 0; m_itemIndex = 0; + m_oldItemIndex = 0; m_col = 0; m_cancelled = FALSE; }