+int MyTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
+ const wxTreeItemId& item2)
+{
+ if ( m_reverseSort )
+ {
+ // just exchange 1st and 2nd items
+ return wxTreeCtrl::OnCompareItems(item2, item1);
+ }
+ else
+ {
+ return wxTreeCtrl::OnCompareItems(item1, item2);
+ }
+}
+
+void MyTreeCtrl::AddItemsRecursively(const wxTreeItemId& idParent,
+ size_t numChildren,
+ size_t depth,
+ size_t folder)
+{
+ if ( depth > 0 )
+ {
+ bool hasChildren = depth > 1;
+
+ wxString str;
+ for ( size_t n = 0; n < numChildren; n++ )
+ {
+ // at depth 1 elements won't have any more children
+ if ( hasChildren )
+ str.Printf(wxT("%s child %u"), wxT("Folder"), unsigned(n + 1));
+ else
+ str.Printf(wxT("%s child %u.%u"), wxT("File"), unsigned(folder), unsigned(n + 1));
+
+ // here we pass to AppendItem() normal and selected item images (we
+ // suppose that selected image follows the normal one in the enum)
+ int image, imageSel;
+ if ( wxGetApp().ShowImages() )
+ {
+ image = depth == 1 ? TreeCtrlIcon_File : TreeCtrlIcon_Folder;
+ imageSel = image + 1;
+ }
+ else
+ {
+ image = imageSel = -1;
+ }
+ wxTreeItemId id = AppendItem(idParent, str, image, imageSel,
+ new MyTreeItemData(str));
+
+ if ( wxGetApp().ShowStates() )
+ SetItemState(id, 0);
+
+ // and now we also set the expanded one (only for the folders)
+ if ( hasChildren && wxGetApp().ShowImages() )
+ {
+ SetItemImage(id, TreeCtrlIcon_FolderOpened,
+ wxTreeItemIcon_Expanded);
+ }
+
+ // remember the last child for OnEnsureVisible()
+ if ( !hasChildren && n == numChildren - 1 )
+ {
+ m_lastItem = id;
+ }
+
+ AddItemsRecursively(id, numChildren, depth - 1, n + 1);
+ }
+ }
+ //else: done!
+}
+
+void MyTreeCtrl::AddTestItemsToTree(size_t numChildren,
+ size_t depth)
+{
+ int image = wxGetApp().ShowImages() ? MyTreeCtrl::TreeCtrlIcon_Folder : -1;
+ wxTreeItemId rootId = AddRoot(wxT("Root"),
+ image, image,
+ new MyTreeItemData(wxT("Root item")));
+ if ( !HasFlag(wxTR_HIDE_ROOT) && image != -1 )
+ {
+ SetItemImage(rootId, TreeCtrlIcon_FolderOpened, wxTreeItemIcon_Expanded);
+ }
+
+ AddItemsRecursively(rootId, numChildren, depth, 0);
+
+ // set some colours/fonts for testing
+ if ( !HasFlag(wxTR_HIDE_ROOT) )
+ SetItemFont(rootId, *wxITALIC_FONT);
+
+ wxTreeItemIdValue cookie;
+ wxTreeItemId id = GetFirstChild(rootId, cookie);
+ SetItemTextColour(id, *wxBLUE);
+
+ id = GetNextChild(rootId, cookie);
+ if ( id )
+ id = GetNextChild(rootId, cookie);
+ if ( id )
+ {
+ SetItemTextColour(id, *wxRED);
+ SetItemBackgroundColour(id, *wxLIGHT_GREY);
+ }
+}
+
+void MyTreeCtrl::GetItemsRecursively(const wxTreeItemId& idParent,
+ wxTreeItemIdValue cookie)
+{
+ wxTreeItemId id;
+
+ if ( !cookie )
+ id = GetFirstChild(idParent, cookie);
+ else
+ id = GetNextChild(idParent, cookie);
+
+ if ( !id.IsOk() )
+ return;
+
+ wxString text = GetItemText(id);
+ wxLogMessage(text);
+
+ if (ItemHasChildren(id))
+ GetItemsRecursively(id);
+
+ GetItemsRecursively(idParent, cookie);
+}
+
+void MyTreeCtrl::DoToggleIcon(const wxTreeItemId& item)
+{
+ int image = GetItemImage(item) == TreeCtrlIcon_Folder
+ ? TreeCtrlIcon_File
+ : TreeCtrlIcon_Folder;
+ SetItemImage(item, image, wxTreeItemIcon_Normal);
+
+ image = GetItemImage(item, wxTreeItemIcon_Selected) == TreeCtrlIcon_FolderSelected
+ ? TreeCtrlIcon_FileSelected
+ : TreeCtrlIcon_FolderSelected;
+ SetItemImage(item, image, wxTreeItemIcon_Selected);
+}
+
+void MyTreeCtrl::DoToggleState(const wxTreeItemId& item)
+{
+ if ( m_alternateStates )
+ {
+ // sets random state unlike current
+ int state = GetItemState(item);
+ int nState;
+
+ srand (time(NULL));
+ do {
+ nState = rand() % GetStateImageList()->GetImageCount();
+ } while (nState == state);
+
+ SetItemState(item, nState);
+ }
+ else
+ {
+ // we have only 2 checkbox states, so next state will be reversed
+ SetItemState(item, wxTREE_ITEMSTATE_NEXT);
+ }
+}
+
+void MyTreeCtrl::DoResetBrokenStateImages(const wxTreeItemId& idParent,
+ wxTreeItemIdValue cookie, int state)
+{
+ wxTreeItemId id;
+
+ if ( !cookie )
+ id = GetFirstChild(idParent, cookie);
+ else
+ id = GetNextChild(idParent, cookie);
+
+ if ( !id.IsOk() )
+ return;
+
+ int curState = GetItemState(id);
+ if ( curState != wxTREE_ITEMSTATE_NONE && curState > state )
+ SetItemState(id, state);
+
+ if (ItemHasChildren(id))
+ DoResetBrokenStateImages(id, 0, state);
+
+ DoResetBrokenStateImages(idParent, cookie, state);
+}
+
+void MyTreeCtrl::LogEvent(const wxChar *name, const wxTreeEvent& event)
+{
+ wxTreeItemId item = event.GetItem();
+ wxString text;
+ if ( item.IsOk() )
+ text << wxT('"') << GetItemText(item).c_str() << wxT('"');
+ else
+ text = wxT("invalid item");
+ wxLogMessage(wxT("%s(%s)"), name, text.c_str());
+}
+
+// avoid repetition
+#define TREE_EVENT_HANDLER(name) \
+void MyTreeCtrl::name(wxTreeEvent& event) \
+{ \
+ LogEvent(wxT(#name), event); \
+ SetLastItem(wxTreeItemId()); \
+ event.Skip(); \
+}
+
+TREE_EVENT_HANDLER(OnBeginRDrag)
+TREE_EVENT_HANDLER(OnDeleteItem)
+TREE_EVENT_HANDLER(OnGetInfo)
+TREE_EVENT_HANDLER(OnSetInfo)
+TREE_EVENT_HANDLER(OnItemExpanded)
+TREE_EVENT_HANDLER(OnItemExpanding)
+TREE_EVENT_HANDLER(OnItemCollapsed)
+TREE_EVENT_HANDLER(OnSelChanged)
+TREE_EVENT_HANDLER(OnSelChanging)
+
+#undef TREE_EVENT_HANDLER
+
+void LogKeyEvent(const wxChar *name, const wxKeyEvent& event)
+{
+ wxString key;
+ long keycode = event.GetKeyCode();
+ {
+ switch ( keycode )
+ {
+ case WXK_BACK: key = wxT("BACK"); break;
+ case WXK_TAB: key = wxT("TAB"); break;
+ case WXK_RETURN: key = wxT("RETURN"); break;
+ case WXK_ESCAPE: key = wxT("ESCAPE"); break;
+ case WXK_SPACE: key = wxT("SPACE"); break;
+ case WXK_DELETE: key = wxT("DELETE"); break;
+ case WXK_START: key = wxT("START"); break;
+ case WXK_LBUTTON: key = wxT("LBUTTON"); break;
+ case WXK_RBUTTON: key = wxT("RBUTTON"); break;
+ case WXK_CANCEL: key = wxT("CANCEL"); break;
+ case WXK_MBUTTON: key = wxT("MBUTTON"); break;
+ case WXK_CLEAR: key = wxT("CLEAR"); break;
+ case WXK_SHIFT: key = wxT("SHIFT"); break;
+ case WXK_ALT: key = wxT("ALT"); break;
+ case WXK_CONTROL: key = wxT("CONTROL"); break;
+ case WXK_MENU: key = wxT("MENU"); break;
+ case WXK_PAUSE: key = wxT("PAUSE"); break;
+ case WXK_CAPITAL: key = wxT("CAPITAL"); break;
+ case WXK_END: key = wxT("END"); break;
+ case WXK_HOME: key = wxT("HOME"); break;
+ case WXK_LEFT: key = wxT("LEFT"); break;
+ case WXK_UP: key = wxT("UP"); break;
+ case WXK_RIGHT: key = wxT("RIGHT"); break;
+ case WXK_DOWN: key = wxT("DOWN"); break;
+ case WXK_SELECT: key = wxT("SELECT"); break;
+ case WXK_PRINT: key = wxT("PRINT"); break;
+ case WXK_EXECUTE: key = wxT("EXECUTE"); break;
+ case WXK_SNAPSHOT: key = wxT("SNAPSHOT"); break;
+ case WXK_INSERT: key = wxT("INSERT"); break;
+ case WXK_HELP: key = wxT("HELP"); break;
+ case WXK_NUMPAD0: key = wxT("NUMPAD0"); break;
+ case WXK_NUMPAD1: key = wxT("NUMPAD1"); break;
+ case WXK_NUMPAD2: key = wxT("NUMPAD2"); break;
+ case WXK_NUMPAD3: key = wxT("NUMPAD3"); break;
+ case WXK_NUMPAD4: key = wxT("NUMPAD4"); break;
+ case WXK_NUMPAD5: key = wxT("NUMPAD5"); break;
+ case WXK_NUMPAD6: key = wxT("NUMPAD6"); break;
+ case WXK_NUMPAD7: key = wxT("NUMPAD7"); break;
+ case WXK_NUMPAD8: key = wxT("NUMPAD8"); break;
+ case WXK_NUMPAD9: key = wxT("NUMPAD9"); break;
+ case WXK_MULTIPLY: key = wxT("MULTIPLY"); break;
+ case WXK_ADD: key = wxT("ADD"); break;
+ case WXK_SEPARATOR: key = wxT("SEPARATOR"); break;
+ case WXK_SUBTRACT: key = wxT("SUBTRACT"); break;
+ case WXK_DECIMAL: key = wxT("DECIMAL"); break;
+ case WXK_DIVIDE: key = wxT("DIVIDE"); break;
+ case WXK_F1: key = wxT("F1"); break;
+ case WXK_F2: key = wxT("F2"); break;
+ case WXK_F3: key = wxT("F3"); break;
+ case WXK_F4: key = wxT("F4"); break;
+ case WXK_F5: key = wxT("F5"); break;
+ case WXK_F6: key = wxT("F6"); break;
+ case WXK_F7: key = wxT("F7"); break;
+ case WXK_F8: key = wxT("F8"); break;
+ case WXK_F9: key = wxT("F9"); break;
+ case WXK_F10: key = wxT("F10"); break;
+ case WXK_F11: key = wxT("F11"); break;
+ case WXK_F12: key = wxT("F12"); break;
+ case WXK_F13: key = wxT("F13"); break;
+ case WXK_F14: key = wxT("F14"); break;
+ case WXK_F15: key = wxT("F15"); break;
+ case WXK_F16: key = wxT("F16"); break;
+ case WXK_F17: key = wxT("F17"); break;
+ case WXK_F18: key = wxT("F18"); break;
+ case WXK_F19: key = wxT("F19"); break;
+ case WXK_F20: key = wxT("F20"); break;
+ case WXK_F21: key = wxT("F21"); break;
+ case WXK_F22: key = wxT("F22"); break;
+ case WXK_F23: key = wxT("F23"); break;
+ case WXK_F24: key = wxT("F24"); break;
+ case WXK_NUMLOCK: key = wxT("NUMLOCK"); break;
+ case WXK_SCROLL: key = wxT("SCROLL"); break;
+ case WXK_PAGEUP: key = wxT("PAGEUP"); break;
+ case WXK_PAGEDOWN: key = wxT("PAGEDOWN"); break;
+ case WXK_NUMPAD_SPACE: key = wxT("NUMPAD_SPACE"); break;
+ case WXK_NUMPAD_TAB: key = wxT("NUMPAD_TAB"); break;
+ case WXK_NUMPAD_ENTER: key = wxT("NUMPAD_ENTER"); break;
+ case WXK_NUMPAD_F1: key = wxT("NUMPAD_F1"); break;
+ case WXK_NUMPAD_F2: key = wxT("NUMPAD_F2"); break;
+ case WXK_NUMPAD_F3: key = wxT("NUMPAD_F3"); break;
+ case WXK_NUMPAD_F4: key = wxT("NUMPAD_F4"); break;
+ case WXK_NUMPAD_HOME: key = wxT("NUMPAD_HOME"); break;
+ case WXK_NUMPAD_LEFT: key = wxT("NUMPAD_LEFT"); break;
+ case WXK_NUMPAD_UP: key = wxT("NUMPAD_UP"); break;
+ case WXK_NUMPAD_RIGHT: key = wxT("NUMPAD_RIGHT"); break;
+ case WXK_NUMPAD_DOWN: key = wxT("NUMPAD_DOWN"); break;
+ case WXK_NUMPAD_PAGEUP: key = wxT("NUMPAD_PAGEUP"); break;
+ case WXK_NUMPAD_PAGEDOWN: key = wxT("NUMPAD_PAGEDOWN"); break;
+ case WXK_NUMPAD_END: key = wxT("NUMPAD_END"); break;
+ case WXK_NUMPAD_BEGIN: key = wxT("NUMPAD_BEGIN"); break;
+ case WXK_NUMPAD_INSERT: key = wxT("NUMPAD_INSERT"); break;
+ case WXK_NUMPAD_DELETE: key = wxT("NUMPAD_DELETE"); break;
+ case WXK_NUMPAD_EQUAL: key = wxT("NUMPAD_EQUAL"); break;
+ case WXK_NUMPAD_MULTIPLY: key = wxT("NUMPAD_MULTIPLY"); break;
+ case WXK_NUMPAD_ADD: key = wxT("NUMPAD_ADD"); break;
+ case WXK_NUMPAD_SEPARATOR: key = wxT("NUMPAD_SEPARATOR"); break;
+ case WXK_NUMPAD_SUBTRACT: key = wxT("NUMPAD_SUBTRACT"); break;
+ case WXK_NUMPAD_DECIMAL: key = wxT("NUMPAD_DECIMAL"); break;
+
+ default:
+ {
+ if ( keycode < 128 && wxIsprint((int)keycode) )
+ key.Printf(wxT("'%c'"), (char)keycode);
+ else if ( keycode > 0 && keycode < 27 )
+ key.Printf(_("Ctrl-%c"), wxT('A') + keycode - 1);
+ else
+ key.Printf(wxT("unknown (%ld)"), keycode);
+ }
+ }
+ }
+
+ wxLogMessage( wxT("%s event: %s (flags = %c%c%c%c)"),
+ name,
+ key.c_str(),
+ event.ControlDown() ? wxT('C') : wxT('-'),
+ event.AltDown() ? wxT('A') : wxT('-'),
+ event.ShiftDown() ? wxT('S') : wxT('-'),
+ event.MetaDown() ? wxT('M') : wxT('-'));
+}
+
+void MyTreeCtrl::OnTreeKeyDown(wxTreeEvent& event)
+{
+ LogKeyEvent(wxT("Tree key down "), event.GetKeyEvent());
+
+ event.Skip();
+}
+
+void MyTreeCtrl::OnBeginDrag(wxTreeEvent& event)
+{
+ // need to explicitly allow drag
+ if ( event.GetItem() != GetRootItem() )
+ {
+ m_draggedItem = event.GetItem();
+
+ wxPoint clientpt = event.GetPoint();
+ wxPoint screenpt = ClientToScreen(clientpt);
+
+ wxLogMessage(wxT("OnBeginDrag: started dragging %s at screen coords (%i,%i)"),
+ GetItemText(m_draggedItem).c_str(),
+ screenpt.x, screenpt.y);
+
+ event.Allow();
+ }
+ else
+ {
+ wxLogMessage(wxT("OnBeginDrag: this item can't be dragged."));
+ }
+}
+
+void MyTreeCtrl::OnEndDrag(wxTreeEvent& event)
+{
+ wxTreeItemId itemSrc = m_draggedItem,
+ itemDst = event.GetItem();
+ m_draggedItem = (wxTreeItemId)0l;
+
+ // where to copy the item?
+ if ( itemDst.IsOk() && !ItemHasChildren(itemDst) )
+ {
+ // copy to the parent then
+ itemDst = GetItemParent(itemDst);
+ }
+
+ if ( !itemDst.IsOk() )
+ {
+ wxLogMessage(wxT("OnEndDrag: can't drop here."));
+
+ return;
+ }
+
+ wxString text = GetItemText(itemSrc);
+ wxLogMessage(wxT("OnEndDrag: '%s' copied to '%s'."),
+ text.c_str(), GetItemText(itemDst).c_str());
+
+ // just do append here - we could also insert it just before/after the item
+ // on which it was dropped, but this requires slightly more work... we also
+ // completely ignore the client data and icon of the old item but could
+ // copy them as well.
+ //
+ // Finally, we only copy one item here but we might copy the entire tree if
+ // we were dragging a folder.
+ int image = wxGetApp().ShowImages() ? TreeCtrlIcon_File : -1;
+ wxTreeItemId id = AppendItem(itemDst, text, image);
+
+ if ( wxGetApp().ShowStates() )
+ SetItemState(id, GetItemState(itemSrc));
+}
+
+void MyTreeCtrl::OnBeginLabelEdit(wxTreeEvent& event)
+{
+ wxLogMessage(wxT("OnBeginLabelEdit"));
+
+ // for testing, prevent this item's label editing
+ wxTreeItemId itemId = event.GetItem();
+ if ( IsTestItem(itemId) )
+ {
+ wxMessageBox(wxT("You can't edit this item."));
+
+ event.Veto();
+ }
+ else if ( itemId == GetRootItem() )
+ {
+ // test that it is possible to change the text of the item being edited
+ SetItemText(itemId, wxT("Editing root item"));
+ }
+}
+
+void MyTreeCtrl::OnEndLabelEdit(wxTreeEvent& event)
+{
+ wxLogMessage(wxT("OnEndLabelEdit"));
+
+ // don't allow anything except letters in the labels
+ if ( !event.GetLabel().IsWord() )
+ {
+ wxMessageBox(wxT("The new label should be a single word."));
+
+ event.Veto();
+ }
+}
+
+void MyTreeCtrl::OnItemCollapsing(wxTreeEvent& event)
+{
+ wxLogMessage(wxT("OnItemCollapsing"));
+
+ // for testing, prevent the user from collapsing the first child folder
+ wxTreeItemId itemId = event.GetItem();
+ if ( IsTestItem(itemId) )
+ {
+ wxMessageBox(wxT("You can't collapse this item."));
+
+ event.Veto();
+ }
+}
+
+void MyTreeCtrl::OnItemActivated(wxTreeEvent& event)
+{
+ // show some info about this item
+ wxTreeItemId itemId = event.GetItem();
+ MyTreeItemData *item = (MyTreeItemData *)GetItemData(itemId);
+
+ if ( item != NULL )
+ {
+ item->ShowInfo(this);
+ }
+
+ wxLogMessage(wxT("OnItemActivated"));
+}
+
+void MyTreeCtrl::OnItemStateClick(wxTreeEvent& event)
+{
+ // toggle item state
+ wxTreeItemId itemId = event.GetItem();
+ DoToggleState(itemId);
+
+ wxLogMessage(wxT("Item \"%s\" state changed to %d"),
+ GetItemText(itemId), GetItemState(itemId));
+}
+
+void MyTreeCtrl::OnItemMenu(wxTreeEvent& event)
+{
+ wxTreeItemId itemId = event.GetItem();
+ wxCHECK_RET( itemId.IsOk(), "should have a valid item" );
+
+ MyTreeItemData *item = (MyTreeItemData *)GetItemData(itemId);
+ wxPoint clientpt = event.GetPoint();
+ wxPoint screenpt = ClientToScreen(clientpt);
+
+ wxLogMessage(wxT("OnItemMenu for item \"%s\" at screen coords (%i, %i)"),
+ item->GetDesc(), screenpt.x, screenpt.y);
+
+ ShowMenu(itemId, clientpt);
+ event.Skip();
+}
+
+void MyTreeCtrl::OnContextMenu(wxContextMenuEvent& event)
+{
+ wxPoint pt = event.GetPosition();
+
+ wxLogMessage(wxT("OnContextMenu at screen coords (%i, %i)"), pt.x, pt.y);
+
+ event.Skip();
+}
+
+void MyTreeCtrl::ShowMenu(wxTreeItemId id, const wxPoint& pt)
+{
+ wxString title;
+ if ( id.IsOk() )
+ {
+ title << wxT("Menu for ") << GetItemText(id);
+ }
+ else
+ {
+ title = wxT("Menu for no particular item");
+ }
+
+#if wxUSE_MENUS
+ wxMenu menu(title);
+ menu.Append(TreeTest_About, wxT("&About..."));
+ menu.AppendSeparator();
+ menu.Append(TreeTest_Highlight, wxT("&Highlight item"));
+ menu.Append(TreeTest_Dump, wxT("&Dump"));
+
+ PopupMenu(&menu, pt);
+#endif // wxUSE_MENUS
+}
+
+void MyTreeCtrl::OnItemRClick(wxTreeEvent& event)
+{
+ wxTreeItemId itemId = event.GetItem();
+ wxCHECK_RET( itemId.IsOk(), "should have a valid item" );
+
+ MyTreeItemData *item = (MyTreeItemData *)GetItemData(itemId);
+
+ wxLogMessage(wxT("Item \"%s\" right clicked"), item->GetDesc());
+
+ event.Skip();
+}
+
+void MyTreeCtrl::OnRMouseDown(wxMouseEvent& event)
+{
+ wxLogMessage(wxT("Right mouse button down"));
+
+ event.Skip();
+}
+
+void MyTreeCtrl::OnRMouseUp(wxMouseEvent& event)
+{
+ wxLogMessage(wxT("Right mouse button up"));
+
+ event.Skip();
+}
+
+void MyTreeCtrl::OnRMouseDClick(wxMouseEvent& event)
+{
+ wxTreeItemId id = HitTest(event.GetPosition());
+ if ( !id )
+ {
+ wxLogMessage(wxT("No item under mouse"));
+ }
+ else
+ {
+ MyTreeItemData *item = (MyTreeItemData *)GetItemData(id);
+ if ( item )
+ {
+ wxLogMessage(wxT("Item '%s' under mouse"), item->GetDesc());
+ }
+ }
+
+ event.Skip();
+}
+
+static inline const wxChar *Bool2String(bool b)
+{
+ return b ? wxT("") : wxT("not ");
+}
+
+void MyTreeItemData::ShowInfo(wxTreeCtrl *tree)
+{
+ wxLogMessage(wxT("Item '%s': %sselected, %sexpanded, %sbold,\n")
+ wxT("%u children (%u immediately under this item)."),
+ m_desc.c_str(),
+ Bool2String(tree->IsSelected(GetId())),
+ Bool2String(tree->IsExpanded(GetId())),
+ Bool2String(tree->IsBold(GetId())),
+ unsigned(tree->GetChildrenCount(GetId())),
+ unsigned(tree->GetChildrenCount(GetId(), false)));
+}