+// process WM_NOTIFY Windows message
+bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
+{
+ wxTreeEvent event(wxEVT_NULL, m_windowId);
+ wxEventType eventType = wxEVT_NULL;
+ NMHDR *hdr = (NMHDR *)lParam;
+
+ switch ( hdr->code )
+ {
+ case TVN_BEGINDRAG:
+ eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
+ // fall through
+
+ case TVN_BEGINRDRAG:
+ {
+ if ( eventType == wxEVT_NULL )
+ eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
+ //else: left drag, already set above
+
+ NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
+
+ event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
+ event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
+
+ // don't allow dragging by default: the user code must
+ // explicitly say that it wants to allow it to avoid breaking
+ // the old apps
+ event.Veto();
+ }
+ break;
+
+ case TVN_BEGINLABELEDIT:
+ {
+ eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
+ TV_DISPINFO *info = (TV_DISPINFO *)lParam;
+
+ event.m_item = (WXHTREEITEM) info->item.hItem;
+ event.m_label = info->item.pszText;
+ }
+ break;
+
+ case TVN_DELETEITEM:
+ {
+ eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
+ NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
+
+ event.m_item = (WXHTREEITEM)tv->itemOld.hItem;
+
+ if ( m_hasAnyAttr )
+ {
+ delete (wxTreeItemAttr *)m_attrs.
+ Delete((long)tv->itemOld.hItem);
+ }
+ }
+ break;
+
+ case TVN_ENDLABELEDIT:
+ {
+ eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
+ TV_DISPINFO *info = (TV_DISPINFO *)lParam;
+
+ event.m_item = (WXHTREEITEM)info->item.hItem;
+ event.m_label = info->item.pszText;
+ if (info->item.pszText == NULL)
+ return FALSE;
+ break;
+ }
+
+ case TVN_GETDISPINFO:
+ eventType = wxEVT_COMMAND_TREE_GET_INFO;
+ // fall through
+
+ case TVN_SETDISPINFO:
+ {
+ if ( eventType == wxEVT_NULL )
+ eventType = wxEVT_COMMAND_TREE_SET_INFO;
+ //else: get, already set above
+
+ TV_DISPINFO *info = (TV_DISPINFO *)lParam;
+
+ event.m_item = (WXHTREEITEM) info->item.hItem;
+ break;
+ }
+
+ case TVN_ITEMEXPANDING:
+ case TVN_ITEMEXPANDED:
+ {
+ NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
+
+ bool expand = FALSE;
+ switch ( tv->action )
+ {
+ case TVE_EXPAND:
+ expand = TRUE;
+ break;
+
+ case TVE_COLLAPSE:
+ expand = FALSE;
+ break;
+
+ default:
+ wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv->action);
+ }
+
+ bool ing = ((int)hdr->code == TVN_ITEMEXPANDING);
+ eventType = g_events[expand][ing];
+
+ event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
+ }
+ break;
+
+ case TVN_KEYDOWN:
+ {
+ eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
+ TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
+
+ // we pass 0 as last CreateKeyEvent() parameter because we
+ // don't have access to the real key press flags here - but as
+ // it is only used to determin wxKeyEvent::m_altDown flag it's
+ // not too bad
+ event.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN,
+ wxCharCodeMSWToWX(info->wVKey),
+ 0);
+
+ // a separate event for Space/Return
+ if ( !wxIsCtrlDown() && !wxIsShiftDown() &&
+ ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) )
+ {
+ wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
+ m_windowId);
+ event2.SetEventObject(this);
+ if ( !(GetWindowStyle() & wxTR_MULTIPLE) )
+ {
+ event2.m_item = GetSelection();
+ }
+ //else: don't know how to get it
+
+ (void)GetEventHandler()->ProcessEvent(event2);
+ }
+ }
+ break;
+
+ case TVN_SELCHANGED:
+ eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
+ // fall through
+
+ case TVN_SELCHANGING:
+ {
+ if ( eventType == wxEVT_NULL )
+ eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
+ //else: already set above
+
+ NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
+
+ event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
+ event.m_itemOld = (WXHTREEITEM) tv->itemOld.hItem;
+ }
+ break;
+
+#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 && !wxUSE_COMCTL32_SAFELY && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
+ case NM_CUSTOMDRAW:
+ {
+ LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam;
+ NMCUSTOMDRAW& nmcd = lptvcd->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;
+ break;
+
+ case CDDS_ITEMPREPAINT:
+ {
+ wxTreeItemAttr *attr =
+ (wxTreeItemAttr *)m_attrs.Get(nmcd.dwItemSpec);
+
+ if ( !attr )
+ {
+ // nothing to do for this item
+ *result = CDRF_DODEFAULT;
+ break;
+ }
+
+ 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 = GetForegroundColour();
+ }
+
+ // selection colours should override ours
+ if ( nmcd.uItemState & CDIS_SELECTED )
+ {
+ DWORD clrBk = ::GetSysColor(COLOR_HIGHLIGHT);
+ lptvcd->clrTextBk = clrBk;
+
+ // try to make the text visible
+ lptvcd->clrText = wxColourToRGB(colText);
+ lptvcd->clrText |= ~clrBk;
+ lptvcd->clrText &= 0x00ffffff;
+ }
+ else
+ {
+ if ( attr->HasBackgroundColour() )
+ {
+ colBack = attr->GetBackgroundColour();
+ }
+ else
+ {
+ colBack = GetBackgroundColour();
+ }
+
+ lptvcd->clrText = wxColourToRGB(colText);
+ lptvcd->clrTextBk = wxColourToRGB(colBack);
+ }
+
+ // 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;
+ }
+ }
+ break;
+
+ default:
+ *result = CDRF_DODEFAULT;
+ }
+ }
+
+ // we always process it
+ return TRUE;
+#endif // _WIN32_IE >= 0x300
+
+ case NM_DBLCLK:
+ case NM_RCLICK:
+ {
+ TV_HITTESTINFO tvhti;
+ ::GetCursorPos(&tvhti.pt);
+ ::ScreenToClient(GetHwnd(), &tvhti.pt);
+ if ( TreeView_HitTest(GetHwnd(), &tvhti) )
+ {
+ if ( tvhti.flags & TVHT_ONITEM )
+ {
+ event.m_item = (WXHTREEITEM) tvhti.hItem;
+ eventType = (int)hdr->code == NM_DBLCLK
+ ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED
+ : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK;
+
+ event.m_pointDrag.x = tvhti.pt.x;
+ event.m_pointDrag.y = tvhti.pt.y;
+ }
+
+ break;
+ }
+ }
+ // fall through
+
+ default:
+ return wxControl::MSWOnNotify(idCtrl, lParam, result);
+ }
+
+ event.SetEventObject(this);
+ event.SetEventType(eventType);
+
+ bool processed = GetEventHandler()->ProcessEvent(event);
+
+ // post processing
+ switch ( hdr->code )
+ {
+ case NM_DBLCLK:
+ // we translate NM_DBLCLK into ACTIVATED event, so don't interpret
+ // the return code of this event handler as the return value for
+ // NM_DBLCLK - otherwise, double clicking the item to toggle its
+ // expanded status would never work
+ *result = FALSE;
+ break;
+
+ case TVN_BEGINDRAG:
+ case TVN_BEGINRDRAG:
+ if ( event.IsAllowed() )
+ {
+ // normally this is impossible because the m_dragImage is
+ // deleted once the drag operation is over
+ wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") );
+
+ m_dragImage = new wxDragImage(*this, event.m_item);
+ m_dragImage->BeginDrag(wxPoint(0, 0), this);
+ m_dragImage->Show();
+ }
+ break;
+
+ case TVN_DELETEITEM:
+ {
+ // NB: we might process this message using wxWindows event
+ // tables, but due to overhead of wxWin event system we
+ // prefer to do it here ourself (otherwise deleting a tree
+ // with many items is just too slow)
+ NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
+
+ wxTreeItemId item = event.m_item;
+ if ( HasIndirectData(item) )
+ {
+ wxTreeItemIndirectData *data = (wxTreeItemIndirectData *)
+ tv->itemOld.lParam;
+ delete data; // can't be NULL here
+
+ m_itemsWithIndirectData.Remove(item);
+#if 0
+ int iIndex = m_itemsWithIndirectData.Index(item);
+ wxASSERT( iIndex != wxNOT_FOUND) ;
+ m_itemsWithIndirectData.wxBaseArray::RemoveAt((size_t)iIndex);