From: Robin Dunn Date: Mon, 9 Oct 2006 02:23:32 +0000 (+0000) Subject: Add CustomTreeCtrl from Andrea Gavana X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/c8f129d0675af4f473f92b90aeb187c90e4053c9 Add CustomTreeCtrl from Andrea Gavana git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41768 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/wxPython/demo/CustomTreeCtrl.py b/wxPython/demo/CustomTreeCtrl.py new file mode 100644 index 0000000000..805a4385e3 --- /dev/null +++ b/wxPython/demo/CustomTreeCtrl.py @@ -0,0 +1,1978 @@ +import wx +import string +import os +import wx.lib.colourselect as csel + +import wx.lib.customtreectrl as CT + +from Main import opj +import images + +#--------------------------------------------------------------------------- + + +penstyle = ["wx.SOLID", "wx.TRANSPARENT", "wx.DOT", "wx.LONG_DASH", "wx.DOT_DASH", "wx.USER_DASH", + "wx.BDIAGONAL_HATCH", "wx.CROSSDIAG_HATCH", "wx.FDIAGONAL_HATCH", "wx.CROSS_HATCH", + "wx.HORIZONTAL_HATCH", "wx.VERTICAL_HATCH"] + +ArtIDs = [ "None", + "wx.ART_ADD_BOOKMARK", + "wx.ART_DEL_BOOKMARK", + "wx.ART_HELP_SIDE_PANEL", + "wx.ART_HELP_SETTINGS", + "wx.ART_HELP_BOOK", + "wx.ART_HELP_FOLDER", + "wx.ART_HELP_PAGE", + "wx.ART_GO_BACK", + "wx.ART_GO_FORWARD", + "wx.ART_GO_UP", + "wx.ART_GO_DOWN", + "wx.ART_GO_TO_PARENT", + "wx.ART_GO_HOME", + "wx.ART_FILE_OPEN", + "wx.ART_PRINT", + "wx.ART_HELP", + "wx.ART_TIP", + "wx.ART_REPORT_VIEW", + "wx.ART_LIST_VIEW", + "wx.ART_NEW_DIR", + "wx.ART_HARDDISK", + "wx.ART_FLOPPY", + "wx.ART_CDROM", + "wx.ART_REMOVABLE", + "wx.ART_FOLDER", + "wx.ART_FOLDER_OPEN", + "wx.ART_GO_DIR_UP", + "wx.ART_EXECUTABLE_FILE", + "wx.ART_NORMAL_FILE", + "wx.ART_TICK_MARK", + "wx.ART_CROSS_MARK", + "wx.ART_ERROR", + "wx.ART_QUESTION", + "wx.ART_WARNING", + "wx.ART_INFORMATION", + "wx.ART_MISSING_IMAGE", + "SmileBitmap" + ] + +keyMap = { + wx.WXK_BACK : "WXK_BACK", + wx.WXK_TAB : "WXK_TAB", + wx.WXK_RETURN : "WXK_RETURN", + wx.WXK_ESCAPE : "WXK_ESCAPE", + wx.WXK_SPACE : "WXK_SPACE", + wx.WXK_DELETE : "WXK_DELETE", + wx.WXK_START : "WXK_START", + wx.WXK_LBUTTON : "WXK_LBUTTON", + wx.WXK_RBUTTON : "WXK_RBUTTON", + wx.WXK_CANCEL : "WXK_CANCEL", + wx.WXK_MBUTTON : "WXK_MBUTTON", + wx.WXK_CLEAR : "WXK_CLEAR", + wx.WXK_SHIFT : "WXK_SHIFT", + wx.WXK_ALT : "WXK_ALT", + wx.WXK_CONTROL : "WXK_CONTROL", + wx.WXK_MENU : "WXK_MENU", + wx.WXK_PAUSE : "WXK_PAUSE", + wx.WXK_CAPITAL : "WXK_CAPITAL", + wx.WXK_PRIOR : "WXK_PRIOR", + wx.WXK_NEXT : "WXK_NEXT", + wx.WXK_END : "WXK_END", + wx.WXK_HOME : "WXK_HOME", + wx.WXK_LEFT : "WXK_LEFT", + wx.WXK_UP : "WXK_UP", + wx.WXK_RIGHT : "WXK_RIGHT", + wx.WXK_DOWN : "WXK_DOWN", + wx.WXK_SELECT : "WXK_SELECT", + wx.WXK_PRINT : "WXK_PRINT", + wx.WXK_EXECUTE : "WXK_EXECUTE", + wx.WXK_SNAPSHOT : "WXK_SNAPSHOT", + wx.WXK_INSERT : "WXK_INSERT", + wx.WXK_HELP : "WXK_HELP", + wx.WXK_NUMPAD0 : "WXK_NUMPAD0", + wx.WXK_NUMPAD1 : "WXK_NUMPAD1", + wx.WXK_NUMPAD2 : "WXK_NUMPAD2", + wx.WXK_NUMPAD3 : "WXK_NUMPAD3", + wx.WXK_NUMPAD4 : "WXK_NUMPAD4", + wx.WXK_NUMPAD5 : "WXK_NUMPAD5", + wx.WXK_NUMPAD6 : "WXK_NUMPAD6", + wx.WXK_NUMPAD7 : "WXK_NUMPAD7", + wx.WXK_NUMPAD8 : "WXK_NUMPAD8", + wx.WXK_NUMPAD9 : "WXK_NUMPAD9", + wx.WXK_MULTIPLY : "WXK_MULTIPLY", + wx.WXK_ADD : "WXK_ADD", + wx.WXK_SEPARATOR : "WXK_SEPARATOR", + wx.WXK_SUBTRACT : "WXK_SUBTRACT", + wx.WXK_DECIMAL : "WXK_DECIMAL", + wx.WXK_DIVIDE : "WXK_DIVIDE", + wx.WXK_F1 : "WXK_F1", + wx.WXK_F2 : "WXK_F2", + wx.WXK_F3 : "WXK_F3", + wx.WXK_F4 : "WXK_F4", + wx.WXK_F5 : "WXK_F5", + wx.WXK_F6 : "WXK_F6", + wx.WXK_F7 : "WXK_F7", + wx.WXK_F8 : "WXK_F8", + wx.WXK_F9 : "WXK_F9", + wx.WXK_F10 : "WXK_F10", + wx.WXK_F11 : "WXK_F11", + wx.WXK_F12 : "WXK_F12", + wx.WXK_F13 : "WXK_F13", + wx.WXK_F14 : "WXK_F14", + wx.WXK_F15 : "WXK_F15", + wx.WXK_F16 : "WXK_F16", + wx.WXK_F17 : "WXK_F17", + wx.WXK_F18 : "WXK_F18", + wx.WXK_F19 : "WXK_F19", + wx.WXK_F20 : "WXK_F20", + wx.WXK_F21 : "WXK_F21", + wx.WXK_F22 : "WXK_F22", + wx.WXK_F23 : "WXK_F23", + wx.WXK_F24 : "WXK_F24", + wx.WXK_NUMLOCK : "WXK_NUMLOCK", + wx.WXK_SCROLL : "WXK_SCROLL", + wx.WXK_PAGEUP : "WXK_PAGEUP", + wx.WXK_PAGEDOWN : "WXK_PAGEDOWN", + wx.WXK_NUMPAD_SPACE : "WXK_NUMPAD_SPACE", + wx.WXK_NUMPAD_TAB : "WXK_NUMPAD_TAB", + wx.WXK_NUMPAD_ENTER : "WXK_NUMPAD_ENTER", + wx.WXK_NUMPAD_F1 : "WXK_NUMPAD_F1", + wx.WXK_NUMPAD_F2 : "WXK_NUMPAD_F2", + wx.WXK_NUMPAD_F3 : "WXK_NUMPAD_F3", + wx.WXK_NUMPAD_F4 : "WXK_NUMPAD_F4", + wx.WXK_NUMPAD_HOME : "WXK_NUMPAD_HOME", + wx.WXK_NUMPAD_LEFT : "WXK_NUMPAD_LEFT", + wx.WXK_NUMPAD_UP : "WXK_NUMPAD_UP", + wx.WXK_NUMPAD_RIGHT : "WXK_NUMPAD_RIGHT", + wx.WXK_NUMPAD_DOWN : "WXK_NUMPAD_DOWN", + wx.WXK_NUMPAD_PRIOR : "WXK_NUMPAD_PRIOR", + wx.WXK_NUMPAD_PAGEUP : "WXK_NUMPAD_PAGEUP", + wx.WXK_NUMPAD_NEXT : "WXK_NUMPAD_NEXT", + wx.WXK_NUMPAD_PAGEDOWN : "WXK_NUMPAD_PAGEDOWN", + wx.WXK_NUMPAD_END : "WXK_NUMPAD_END", + wx.WXK_NUMPAD_BEGIN : "WXK_NUMPAD_BEGIN", + wx.WXK_NUMPAD_INSERT : "WXK_NUMPAD_INSERT", + wx.WXK_NUMPAD_DELETE : "WXK_NUMPAD_DELETE", + wx.WXK_NUMPAD_EQUAL : "WXK_NUMPAD_EQUAL", + wx.WXK_NUMPAD_MULTIPLY : "WXK_NUMPAD_MULTIPLY", + wx.WXK_NUMPAD_ADD : "WXK_NUMPAD_ADD", + wx.WXK_NUMPAD_SEPARATOR : "WXK_NUMPAD_SEPARATOR", + wx.WXK_NUMPAD_SUBTRACT : "WXK_NUMPAD_SUBTRACT", + wx.WXK_NUMPAD_DECIMAL : "WXK_NUMPAD_DECIMAL", + wx.WXK_NUMPAD_DIVIDE : "WXK_NUMPAD_DIVIDE" + } + + + +#--------------------------------------------------------------------------- +# Just A Dialog To Select Pen Styles +#--------------------------------------------------------------------------- +class PenDialog(wx.Dialog): + + def __init__(self, parent=None, id=-1, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, oldpen=None, + pentype=0): + + wx.Dialog.__init__(self, parent, id, title, pos, size, style) + + self.colourbutton = csel.ColourSelect(self) + self.spinwidth = wx.SpinCtrl(self, -1, "1", min=1, max=3, style=wx.SP_ARROW_KEYS) + + self.combostyle = wx.ComboBox(self, -1, choices=penstyle, style=wx.CB_DROPDOWN|wx.CB_READONLY) + + choices = ["[1, 1]", "[2, 2]", "[3, 3]", "[4, 4]"] + self.combodash = wx.ComboBox(self, -1, choices=choices, style=wx.CB_DROPDOWN|wx.CB_READONLY) + + self.okbutton = wx.Button(self, wx.ID_OK) + self.cancelbutton = wx.Button(self, wx.ID_CANCEL) + + self.oldpen = oldpen + self.parent = parent + self.pentype = pentype + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_COMBOBOX, self.OnStyle, self.combostyle) + self.Bind(wx.EVT_BUTTON, self.OnOk, self.okbutton) + self.Bind(wx.EVT_BUTTON, self.OnCancel, self.cancelbutton) + + + def __set_properties(self): + + self.SetTitle("Pen Dialog Selector") + self.colourbutton.SetMinSize((25, 25)) + self.colourbutton.SetColour(self.oldpen.GetColour()) + + style = self.oldpen.GetStyle() + for count, st in enumerate(penstyle): + if eval(st) == style: + self.combostyle.SetSelection(count) + if count == 5: + self.combodash.Enable(True) + else: + self.combodash.Enable(False) + break + + if self.combodash.IsEnabled(): + dashes = repr(self.oldpen.GetDashes()) + self.combodash.SetValue(dashes) + + self.spinwidth.SetValue(self.oldpen.GetWidth()) + self.okbutton.SetDefault() + + if self.pentype == 1: + self.spinwidth.Enable(False) + + + def __do_layout(self): + + mainsizer = wx.BoxSizer(wx.VERTICAL) + bottomsizer = wx.BoxSizer(wx.HORIZONTAL) + middlesizer = wx.BoxSizer(wx.VERTICAL) + stylesizer = wx.BoxSizer(wx.HORIZONTAL) + widthsizer = wx.BoxSizer(wx.HORIZONTAL) + coloursizer = wx.BoxSizer(wx.HORIZONTAL) + label_1 = wx.StaticText(self, -1, "Please Choose The Pen Settings:") + label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + mainsizer.Add(label_1, 0, wx.ALL|wx.ADJUST_MINSIZE, 10) + label_2 = wx.StaticText(self, -1, "Pen Colour") + coloursizer.Add(label_2, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + coloursizer.Add((5, 5), 1, wx.ADJUST_MINSIZE, 0) + coloursizer.Add(self.colourbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 5) + middlesizer.Add(coloursizer, 0, wx.EXPAND, 0) + label_3 = wx.StaticText(self, -1, "Pen Width") + widthsizer.Add(label_3, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + widthsizer.Add((5, 5), 1, wx.ADJUST_MINSIZE, 0) + widthsizer.Add(self.spinwidth, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + middlesizer.Add(widthsizer, 0, wx.EXPAND, 0) + label_4 = wx.StaticText(self, -1, "Pen Style") + stylesizer.Add(label_4, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + stylesizer.Add((5, 5), 1, wx.ADJUST_MINSIZE, 0) + stylesizer.Add(self.combostyle, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + stylesizer.Add(self.combodash, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + middlesizer.Add(stylesizer, 0, wx.BOTTOM|wx.EXPAND, 5) + mainsizer.Add(middlesizer, 1, wx.EXPAND, 0) + bottomsizer.Add(self.okbutton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 20) + bottomsizer.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + bottomsizer.Add(self.cancelbutton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 20) + mainsizer.Add(bottomsizer, 0, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(mainsizer) + mainsizer.Fit(self) + mainsizer.SetSizeHints(self) + self.Layout() + self.Centre() + + + + + def OnStyle(self, event): + + choice = event.GetEventObject().GetValue() + self.combodash.Enable(choice==5) + event.Skip() + + + def OnOk(self, event): + + colour = self.colourbutton.GetColour() + style = eval(self.combostyle.GetValue()) + width = int(self.spinwidth.GetValue()) + + dashes = None + if self.combostyle.GetSelection() == 5: + dashes = eval(self.combodash.GetValue()) + + pen = wx.Pen(colour, width, style) + + if dashes: + pen.SetDashes(dashes) + + pen.SetCap(wx.CAP_BUTT) + + if self.pentype == 0: + self.parent.SetConnectionPen(pen) + else: + self.parent.SetBorderPen(pen) + + self.Destroy() + event.Skip() + + + def OnCancel(self, event): + + self.Destroy() + event.Skip() + + +#--------------------------------------------------------------------------- +# Just A Dialog To Select Tree Buttons Icons +#--------------------------------------------------------------------------- +class TreeButtonsDialog(wx.Dialog): + + def __init__(self, parent=None, id=-1, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, oldicons=None): + + wx.Dialog.__init__(self, parent, id, title, pos, size, style) + + self.listicons = wx.ListBox(self, -1, choices=["Set 1", "Set 2", "Set 3", "Set 4", "Set 5"], style=wx.LB_SINGLE) + + bitmap_plus = opj("bitmaps/plus" + str(oldicons+1) + ".ico") + bitmap_minus = opj("bitmaps/minus" + str(oldicons+1) + ".ico") + + bitmap_plus = wx.Image(bitmap_plus, wx.BITMAP_TYPE_ICO) + bitmap_plus.Rescale(24, 24) + bitmap_plus = bitmap_plus.ConvertToBitmap() + bitmap_minus = wx.Image(bitmap_minus, wx.BITMAP_TYPE_ICO) + bitmap_minus.Rescale(24, 24) + bitmap_minus = bitmap_minus.ConvertToBitmap() + + self.bitmap_plus = wx.StaticBitmap(self, -1, bitmap_plus) + self.bitmap_minus = wx.StaticBitmap(self, -1, bitmap_minus) + + self.okbutton = wx.Button(self, wx.ID_OK) + self.cancelbutton = wx.Button(self, wx.ID_CANCEL) + + self.parent = parent + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_BUTTON, self.OnOk, self.okbutton) + self.Bind(wx.EVT_BUTTON, self.OnCancel, self.cancelbutton) + self.Bind(wx.EVT_LISTBOX, self.OnListBox, self.listicons) + + + def __set_properties(self): + + self.SetTitle("Tree Buttons Selector") + self.listicons.SetSelection(0) + self.okbutton.SetDefault() + + + def __do_layout(self): + + mainsizer = wx.BoxSizer(wx.VERTICAL) + bottomsizer = wx.BoxSizer(wx.HORIZONTAL) + topsizer = wx.BoxSizer(wx.HORIZONTAL) + rightsizer = wx.BoxSizer(wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + label_1 = wx.StaticText(self, -1, "Please Choose One Of These Sets Of Icons:") + label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + mainsizer.Add(label_1, 0, wx.ALL|wx.ADJUST_MINSIZE, 10) + topsizer.Add(self.listicons, 0, wx.ALL|wx.EXPAND|wx.ADJUST_MINSIZE, 5) + label_2 = wx.StaticText(self, -1, "Collapsed") + sizer_1.Add(label_2, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_1.Add((20, 20), 1, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0) + sizer_1.Add(self.bitmap_plus, 1, wx.RIGHT|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_1, 0, wx.EXPAND, 0) + label_3 = wx.StaticText(self, -1, "Expanded") + sizer_2.Add(label_3, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_2.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.bitmap_minus, 1, wx.RIGHT|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_2, 0, wx.EXPAND, 0) + topsizer.Add(rightsizer, 0, wx.ALL|wx.EXPAND, 5) + mainsizer.Add(topsizer, 1, wx.EXPAND, 0) + bottomsizer.Add(self.okbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 20) + bottomsizer.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + bottomsizer.Add(self.cancelbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 20) + mainsizer.Add(bottomsizer, 0, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(mainsizer) + mainsizer.Fit(self) + mainsizer.SetSizeHints(self) + self.Layout() + + + def OnListBox(self, event): + + selection = self.listicons.GetSelection() + bitmap_plus = opj("bitmaps/plus" + str(selection+1) + ".ico") + bitmap_minus = opj("bitmaps/minus" + str(selection+1) + ".ico") + + bitmap_plus = wx.Image(bitmap_plus, wx.BITMAP_TYPE_ICO) + bitmap_plus.Rescale(24, 24) + bitmap_plus = bitmap_plus.ConvertToBitmap() + bitmap_minus = wx.Image(bitmap_minus, wx.BITMAP_TYPE_ICO) + bitmap_minus.Rescale(24, 24) + bitmap_minus = bitmap_minus.ConvertToBitmap() + + self.bitmap_plus.SetBitmap(bitmap_plus) + self.bitmap_minus.SetBitmap(bitmap_minus) + + self.bitmap_plus.Refresh() + self.bitmap_minus.Refresh() + event.Skip() + + + def OnOk(self, event): + + selection = self.listicons.GetSelection() + self.parent.SetTreeButtons(selection) + self.Destroy() + event.Skip() + + + def OnCancel(self, event): + + self.Destroy() + event.Skip() + + +#--------------------------------------------------------------------------- +# Just A Dialog To Select Tree Check/Radio Item Icons +#--------------------------------------------------------------------------- +class CheckDialog(wx.Dialog): + + def __init__(self, parent=None, id=-1, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE): + + wx.Dialog.__init__(self, parent, id, title, pos, size, style) + + self.listicons = wx.ListBox(self, -1, choices=["Set 1", "Set 2"], style=wx.LB_SINGLE) + + bitmap_check = wx.Bitmap(opj("bitmaps/checked.ico"), wx.BITMAP_TYPE_ICO) + bitmap_uncheck = wx.Bitmap(opj("bitmaps/notchecked.ico"), wx.BITMAP_TYPE_ICO) + bitmap_flag = wx.Bitmap(opj("bitmaps/flagged.ico"), wx.BITMAP_TYPE_ICO) + bitmap_unflag = wx.Bitmap(opj("bitmaps/notflagged.ico"), wx.BITMAP_TYPE_ICO) + + self.bitmap_check = wx.StaticBitmap(self, -1, bitmap_check) + self.bitmap_uncheck = wx.StaticBitmap(self, -1, bitmap_uncheck) + self.bitmap_flag = wx.StaticBitmap(self, -1, bitmap_flag) + self.bitmap_unflag = wx.StaticBitmap(self, -1, bitmap_unflag) + + self.okbutton = wx.Button(self, wx.ID_OK) + self.cancelbutton = wx.Button(self, wx.ID_CANCEL) + + self.parent = parent + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_BUTTON, self.OnOk, self.okbutton) + self.Bind(wx.EVT_BUTTON, self.OnCancel, self.cancelbutton) + self.Bind(wx.EVT_LISTBOX, self.OnListBox, self.listicons) + + + def __set_properties(self): + + self.SetTitle("Check/Radio Icon Selector") + self.listicons.SetSelection(0) + self.okbutton.SetDefault() + + + def __do_layout(self): + + mainsizer = wx.BoxSizer(wx.VERTICAL) + bottomsizer = wx.BoxSizer(wx.HORIZONTAL) + topsizer = wx.BoxSizer(wx.HORIZONTAL) + rightsizer = wx.BoxSizer(wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + sizer_1 = wx.BoxSizer(wx.HORIZONTAL) + sizer_3 = wx.BoxSizer(wx.HORIZONTAL) + sizer_4 = wx.BoxSizer(wx.HORIZONTAL) + label_1 = wx.StaticText(self, -1, "Please Choose One Of These Sets Of Icons:") + label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + mainsizer.Add(label_1, 0, wx.ALL|wx.ADJUST_MINSIZE, 10) + topsizer.Add(self.listicons, 0, wx.ALL|wx.EXPAND|wx.ADJUST_MINSIZE, 5) + label_2 = wx.StaticText(self, -1, "Checked") + sizer_1.Add(label_2, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_1.Add((20, 20), 1, wx.ALIGN_RIGHT|wx.ADJUST_MINSIZE, 0) + sizer_1.Add(self.bitmap_check, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_1, 0, wx.EXPAND, 0) + label_3 = wx.StaticText(self, -1, "Not Checked") + sizer_2.Add(label_3, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_2.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.bitmap_uncheck, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_2, 0, wx.EXPAND, 0) + label_4 = wx.StaticText(self, -1, "Flagged") + sizer_3.Add(label_4, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_3.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + sizer_3.Add(self.bitmap_flag, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_3, 0, wx.EXPAND, 0) + label_5 = wx.StaticText(self, -1, "Not Flagged") + sizer_4.Add(label_5, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + sizer_4.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + sizer_4.Add(self.bitmap_unflag, 0, wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 10) + rightsizer.Add(sizer_4, 0, wx.EXPAND, 0) + + topsizer.Add(rightsizer, 0, wx.ALL|wx.EXPAND, 5) + mainsizer.Add(topsizer, 1, wx.EXPAND, 0) + bottomsizer.Add(self.okbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 20) + bottomsizer.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + bottomsizer.Add(self.cancelbutton, 0, wx.ALL|wx.ADJUST_MINSIZE, 20) + mainsizer.Add(bottomsizer, 0, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(mainsizer) + mainsizer.Fit(self) + mainsizer.SetSizeHints(self) + self.Layout() + + + def OnListBox(self, event): + + selection = self.listicons.GetSelection() + + if selection == 0: + bitmap_check = opj("bitmaps/checked.ico") + bitmap_uncheck = opj("bitmaps/notchecked.ico") + bitmap_flag = opj("bitmaps/flagged.ico") + bitmap_unflag = opj("bitmaps/notflagged.ico") + else: + bitmap_check = opj("bitmaps/aquachecked.ico") + bitmap_uncheck = opj("bitmaps/aquanotchecked.ico") + bitmap_flag = opj("bitmaps/aquaflagged.ico") + bitmap_unflag = opj("bitmaps/aquanotflagged.ico") + + bitmap_check = wx.Bitmap(bitmap_check, wx.BITMAP_TYPE_ICO) + bitmap_uncheck = wx.Bitmap(bitmap_uncheck, wx.BITMAP_TYPE_ICO) + bitmap_flag = wx.Bitmap(bitmap_flag, wx.BITMAP_TYPE_ICO) + bitmap_unflag = wx.Bitmap(bitmap_unflag, wx.BITMAP_TYPE_ICO) + + self.bitmap_uncheck.SetBitmap(bitmap_uncheck) + self.bitmap_check.SetBitmap(bitmap_check) + self.bitmap_unflag.SetBitmap(bitmap_unflag) + self.bitmap_flag.SetBitmap(bitmap_flag) + + self.bitmap_check.Refresh() + self.bitmap_uncheck.Refresh() + self.bitmap_flag.Refresh() + self.bitmap_unflag.Refresh() + + event.Skip() + + + def OnOk(self, event): + + selection = self.listicons.GetSelection() + self.parent.SetCheckRadio(selection) + self.Destroy() + event.Skip() + + + def OnCancel(self, event): + + self.Destroy() + event.Skip() + + +#--------------------------------------------------------------------------- +# Just A Dialog To Select Tree Items Icons +#--------------------------------------------------------------------------- +class TreeIcons(wx.Dialog): + + def __init__(self, parent=None, id=-1, title="", pos=wx.DefaultPosition, + size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, oldpen=None, + bitmaps=None): + + wx.Dialog.__init__(self, parent, id, title, pos, size, style) + + self.bitmaps = [None, None, None, None] + empty = wx.EmptyBitmap(16, 16) + self.parent = parent + + self.bitmaps[0] = wx.StaticBitmap(self, -1, empty) + self.combonormal = wx.ComboBox(self, -1, choices=ArtIDs, style=wx.CB_DROPDOWN|wx.CB_READONLY) + self.bitmaps[1] = wx.StaticBitmap(self, -1, empty) + self.comboselected = wx.ComboBox(self, -1, choices=ArtIDs, style=wx.CB_DROPDOWN|wx.CB_READONLY) + self.bitmaps[2] = wx.StaticBitmap(self, -1, empty) + self.comboexpanded = wx.ComboBox(self, -1, choices=ArtIDs, style=wx.CB_DROPDOWN|wx.CB_READONLY) + self.bitmaps[3] = wx.StaticBitmap(self, -1, empty) + self.comboselectedexpanded = wx.ComboBox(self, -1, choices=ArtIDs, style=wx.CB_DROPDOWN|wx.CB_READONLY) + self.okbutton = wx.Button(self, wx.ID_OK) + self.cancelbutton = wx.Button(self, wx.ID_CANCEL) + + self.combonormal.SetSelection(bitmaps[0] >= 0 and bitmaps[0]+1 or 0) + self.comboselected.SetSelection(bitmaps[1] >= 0 and bitmaps[1]+1 or 0) + self.comboexpanded.SetSelection(bitmaps[2] >= 0 and bitmaps[2]+1 or 0) + self.comboselectedexpanded.SetSelection(bitmaps[3] >= 0 and bitmaps[3]+1 or 0) + + self.GetBitmaps(bitmaps) + + self.__set_properties() + self.__do_layout() + + self.Bind(wx.EVT_COMBOBOX, self.OnComboNormal, self.combonormal) + self.Bind(wx.EVT_COMBOBOX, self.OnComboSelected, self.comboselected) + self.Bind(wx.EVT_COMBOBOX, self.OnComboExpanded, self.comboexpanded) + self.Bind(wx.EVT_COMBOBOX, self.OnComboSelectedExpanded, self.comboselectedexpanded) + self.Bind(wx.EVT_BUTTON, self.OnOk, self.okbutton) + self.Bind(wx.EVT_BUTTON, self.OnCancel, self.cancelbutton) + + + def __set_properties(self): + + self.SetTitle("Item Icon Selector") + self.okbutton.SetDefault() + + + def __do_layout(self): + + mainsizer = wx.BoxSizer(wx.VERTICAL) + sizer_2 = wx.BoxSizer(wx.HORIZONTAL) + gridsizer = wx.FlexGridSizer(4, 3, 5, 5) + label_1 = wx.StaticText(self, -1, "Please Choose The Icons For This Item (All Are Optional):") + label_1.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "")) + mainsizer.Add(label_1, 0, wx.ALL|wx.ADJUST_MINSIZE, 10) + label_2 = wx.StaticText(self, -1, "TreeIcon_Normal:") + gridsizer.Add(label_2, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.bitmaps[0], 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.combonormal, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0) + label_3 = wx.StaticText(self, -1, "TreeIcon_Selected:") + gridsizer.Add(label_3, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.bitmaps[1], 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.comboselected, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0) + label_4 = wx.StaticText(self, -1, "TreeIcon_Expanded:") + gridsizer.Add(label_4, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.bitmaps[2], 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.comboexpanded, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0) + label_5 = wx.StaticText(self, -1, "TreeIcon_SelectedExpanded:") + gridsizer.Add(label_5, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.bitmaps[3], 0, wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 5) + gridsizer.Add(self.comboselectedexpanded, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0) + gridsizer.AddGrowableCol(0) + gridsizer.AddGrowableCol(1) + gridsizer.AddGrowableCol(2) + mainsizer.Add(gridsizer, 0, wx.ALL|wx.EXPAND, 5) + sizer_2.Add(self.okbutton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 20) + sizer_2.Add((20, 20), 1, wx.ADJUST_MINSIZE, 0) + sizer_2.Add(self.cancelbutton, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 20) + mainsizer.Add(sizer_2, 1, wx.EXPAND, 0) + self.SetAutoLayout(True) + self.SetSizer(mainsizer) + mainsizer.Fit(self) + mainsizer.SetSizeHints(self) + self.Layout() + self.Centre() + + + def OnComboNormal(self, event): + + input = event.GetSelection() + self.GetBitmap(input, 0) + event.Skip() + + + def OnComboSelected(self, event): + + input = event.GetSelection() + self.GetBitmap(input, 1) + event.Skip() + + + def OnComboExpanded(self, event): + + input = event.GetSelection() + self.GetBitmap(input, 2) + event.Skip() + + + def OnComboSelectedExpanded(self, event): + + input = event.GetSelection() + self.GetBitmap(input, 3) + event.Skip() + + + def OnOk(self, event): + + bitmaps = [-1, -1, -1, -1] + normal = self.combonormal.GetSelection() + selected = self.comboselected.GetSelection() + expanded = self.comboexpanded.GetSelection() + selexp = self.comboselectedexpanded.GetSelection() + + bitmaps = [(normal > 0 and normal or -1), (selected > 0 and selected or -1), + (expanded > 0 and expanded or -1), (selexp > 0 and selexp or -1)] + + newbitmaps = [] + + for bmp in bitmaps: + if bmp > 0: + newbitmaps.append(bmp-1) + else: + newbitmaps.append(bmp) + + self.parent.SetNewIcons(newbitmaps) + + self.Destroy() + event.Skip() + + + def OnCancel(self, event): + + self.Destroy() + event.Skip() + + + def GetBitmap(self, input, which): + + if input == 0: + bmp = wx.EmptyBitmap(16,16) + self.ClearBmp(bmp) + elif input > 36: + bmp = images.getSmilesBitmap() + else: + bmp = wx.ArtProvider_GetBitmap(eval(ArtIDs[input]), wx.ART_OTHER, (16,16)) + if not bmp.Ok(): + bmp = wx.EmptyBitmap(16,16) + self.ClearBmp(bmp) + + self.bitmaps[which].SetBitmap(bmp) + self.bitmaps[which].Refresh() + + + def GetBitmaps(self, bitmaps): + + output = [] + + for count, input in enumerate(bitmaps): + if input < 0: + bmp = wx.EmptyBitmap(16,16) + self.ClearBmp(bmp) + elif input > 35: + bmp = images.getSmilesBitmap() + else: + bmp = wx.ArtProvider_GetBitmap(eval(ArtIDs[input+1]), wx.ART_OTHER, (16,16)) + if not bmp.Ok(): + bmp = wx.EmptyBitmap(16,16) + self.ClearBmp(bmp) + + self.bitmaps[count].SetBitmap(bmp) + + + def ClearBmp(self, bmp): + + dc = wx.MemoryDC() + dc.SelectObject(bmp) + dc.SetBackground(wx.Brush("white")) + dc.Clear() + + +#--------------------------------------------------------------------------- +# CustomTreeCtrl Demo Implementation +#--------------------------------------------------------------------------- +class CustomTreeCtrlDemo(wx.Panel): + + def __init__(self, parent, log): + wx.Panel.__init__(self, parent) + + self.log = log + self.oldicons = 0 + + splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D) + + # Create the CustomTreeCtrl, using a derived class defined below + self.tree = CustomTreeCtrl(splitter, -1, log=self.log, style=wx.SUNKEN_BORDER) + + self.leftpanel = wx.ScrolledWindow(splitter, -1, style=wx.SUNKEN_BORDER) + self.leftpanel.SetScrollRate(20,20) + width = self.PopulateLeftPanel(self.tree.styles, self.tree.events) + + # add the windows to the splitter and split it. + splitter.SplitVertically(self.leftpanel, self.tree, 300) + splitter.SetMinimumPaneSize(width+5) + + sizer = wx.BoxSizer() + sizer.Add(splitter, 1, wx.EXPAND) + self.SetSizer(sizer) + + + def PopulateLeftPanel(self, styles, events): + pnl = wx.Panel(self.leftpanel) + mainsizer = wx.BoxSizer(wx.VERTICAL) + recreatetree = wx.Button(pnl, -1, "Recreate CustomTreeCtrl") + mainsizer.Add(recreatetree, 0, wx.ALL|wx.ALIGN_CENTER, 10) + recreatetree.Bind(wx.EVT_BUTTON, self.OnRecreateTree) + + staticboxstyles = wx.StaticBox(pnl, -1, "CustomTreeCtrl Styles") + stylesizer = wx.StaticBoxSizer(staticboxstyles, wx.VERTICAL) + staticboxevents = wx.StaticBox(pnl, -1, "CustomTreeCtrl Events") + eventssizer = wx.StaticBoxSizer(staticboxevents, wx.VERTICAL) + staticboxcolours = wx.StaticBox(pnl, -1, "CustomTreeCtrl Images/Colours") + colourssizer = wx.StaticBoxSizer(staticboxcolours, wx.VERTICAL) + staticboxthemes = wx.StaticBox(pnl, -1, "CustomTreeCtrl Themes/Gradients") + themessizer = wx.StaticBoxSizer(staticboxthemes, wx.VERTICAL) + + self.treestyles = [] + self.treeevents = [] + + for count, style in enumerate(styles): + + if count == 0: + tags = wx.ALL + else: + tags = wx.LEFT|wx.RIGHT|wx.BOTTOM + + if style != "TR_DEFAULT_STYLE": + check = wx.CheckBox(pnl, -1, style) + stylesizer.Add(check, 0, tags, 3) + + if style in ["TR_HAS_BUTTONS", "TR_HAS_VARIABLE_ROW_HEIGHT"]: + check.SetValue(1) + + if style == "TR_HAS_VARIABLE_ROW_HEIGHT": + check.Enable(False) + + check.Bind(wx.EVT_CHECKBOX, self.OnCheckStyle) + self.treestyles.append(check) + + + for count, event in enumerate(events): + + if count == 0: + tags = wx.ALL + else: + tags = wx.LEFT|wx.RIGHT|wx.BOTTOM + + if count not in [6, 17, 22, 23]: + check = wx.CheckBox(pnl, -1, event) + eventssizer.Add(check, 0, tags, 3) + + if event in ["EVT_TREE_ITEM_EXPANDED", "EVT_TREE_ITEM_COLLAPSED", + "EVT_TREE_SEL_CHANGED", "EVT_TREE_SEL_CHANGING"]: + + check.SetValue(1) + + check.Bind(wx.EVT_CHECKBOX, self.OnCheckEvent) + self.treeevents.append(check) + + sizer1 = wx.BoxSizer(wx.HORIZONTAL) + label = wx.StaticText(pnl, -1, "Connection Pen") + font = label.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + label.SetFont(font) + buttonconnection = wx.Button(pnl, -1, "Choose...") + buttonconnection.Bind(wx.EVT_BUTTON, self.OnButtonConnection) + sizer1.Add(label, 0, wx.ALL|wx.ALIGN_CENTER, 5) + sizer1.Add((1,0), 1, wx.EXPAND) + sizer1.Add(buttonconnection, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_RIGHT, 5) + + sizer2 = wx.BoxSizer(wx.HORIZONTAL) + label = wx.StaticText(pnl, -1, "Border Pen") + font = label.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + label.SetFont(font) + buttonborder = wx.Button(pnl, -1, "Choose...") + buttonborder.Bind(wx.EVT_BUTTON, self.OnButtonBorder) + sizer2.Add(label, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER, 5) + sizer2.Add((1,0), 1, wx.EXPAND) + sizer2.Add(buttonborder, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_RIGHT, 5) + + sizer3 = wx.BoxSizer(wx.HORIZONTAL) + label = wx.StaticText(pnl, -1, "Tree Buttons") + font = label.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + label.SetFont(font) + buttontree = wx.Button(pnl, -1, "Choose...") + buttontree.Bind(wx.EVT_BUTTON, self.OnButtonTree) + sizer3.Add(label, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER, 5) + sizer3.Add((1,0), 1, wx.EXPAND) + sizer3.Add(buttontree, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_RIGHT, 5) + + sizer4 = wx.BoxSizer(wx.HORIZONTAL) + label = wx.StaticText(pnl, -1, "Check/Radio Buttons") + font = label.GetFont() + font.SetWeight(wx.FONTWEIGHT_BOLD) + label.SetFont(font) + buttoncr = wx.Button(pnl, -1, "Choose...") + buttoncr.Bind(wx.EVT_BUTTON, self.OnButtonCheckRadio) + sizer4.Add(label, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER, 5) + sizer4.Add((1,0), 1, wx.EXPAND) + sizer4.Add(buttoncr, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_RIGHT, 5) + + sizer5 = wx.BoxSizer(wx.HORIZONTAL) + radioimage = wx.RadioButton(pnl, -1, "Image Background", style=wx.RB_GROUP) + radioimage.Bind(wx.EVT_RADIOBUTTON, self.OnBackgroundImage) + self.imagebutton = wx.Button(pnl, -1, "Choose...") + self.imagebutton.Bind(wx.EVT_BUTTON, self.OnChooseImage) + sizer5.Add(radioimage, 0, wx.ALL|wx.ALIGN_CENTER, 5) + sizer5.Add((1,0), 1, wx.EXPAND) + sizer5.Add(self.imagebutton, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_RIGHT, 5) + + sizer6 = wx.BoxSizer(wx.HORIZONTAL) + radiobackground = wx.RadioButton(pnl, -1, "Background Colour") + radiobackground.Bind(wx.EVT_RADIOBUTTON, self.OnBackgroundColour) + self.backbutton = csel.ColourSelect(pnl, -1, "Choose...", wx.WHITE) + self.backbutton.Bind(csel.EVT_COLOURSELECT, self.OnChooseBackground) + sizer6.Add(radiobackground, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER, 5) + sizer6.Add((1,0), 1, wx.EXPAND) + sizer6.Add(self.backbutton, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_CENTER, 5) + + colourssizer.Add(sizer1, 0, wx.EXPAND) + colourssizer.Add(sizer2, 0, wx.EXPAND) + colourssizer.Add(sizer3, 0, wx.EXPAND) + colourssizer.Add(sizer4, 0, wx.EXPAND) + colourssizer.Add(sizer5, 0, wx.EXPAND) + colourssizer.Add(sizer6, 0, wx.EXPAND) + + sizera = wx.BoxSizer(wx.HORIZONTAL) + self.checknormal = wx.CheckBox(pnl, -1, "Standard Colours") + self.focus = csel.ColourSelect(pnl, -1, "Focus", + self.tree.GetHilightFocusColour()) + self.unfocus = csel.ColourSelect(pnl, -1, "Non-Focus", + self.tree.GetHilightNonFocusColour()) + self.checknormal.Bind(wx.EVT_CHECKBOX, self.OnCheckNormal) + self.focus.Bind(csel.EVT_COLOURSELECT, self.OnFocusColour) + self.unfocus.Bind(csel.EVT_COLOURSELECT, self.OnNonFocusColour) + sizera1 = wx.BoxSizer(wx.VERTICAL) + sizera1.Add(self.focus, 0, wx.BOTTOM, 2) + sizera1.Add(self.unfocus, 0) + sizera.Add(self.checknormal, 0, wx.ALL, 3) + sizera.Add((1, 0), 1, wx.EXPAND) + sizera.Add(sizera1, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.EXPAND, 3) + + sizerb = wx.BoxSizer(wx.VERTICAL) + self.checkgradient = wx.CheckBox(pnl, -1, "Gradient Theme") + self.checkgradient.Bind(wx.EVT_CHECKBOX, self.OnCheckGradient) + sizerb1 = wx.BoxSizer(wx.HORIZONTAL) + sizerb1.Add((10, 0)) + self.radiohorizontal = wx.RadioButton(pnl, -1, "Horizontal", style=wx.RB_GROUP) + self.radiohorizontal.Bind(wx.EVT_RADIOBUTTON, self.OnHorizontal) + sizerb1.Add(self.radiohorizontal, 0, wx.TOP|wx.BOTTOM, 3) + sizerb2 = wx.BoxSizer(wx.HORIZONTAL) + sizerb2.Add((10, 0)) + self.radiovertical = wx.RadioButton(pnl, -1, "Vertical") + self.radiovertical.Bind(wx.EVT_RADIOBUTTON, self.OnVertical) + sizerb2.Add(self.radiovertical, 0, wx.BOTTOM, 3) + sizerb3 = wx.BoxSizer(wx.HORIZONTAL) + self.firstcolour = csel.ColourSelect(pnl, -1, "First Colour", + self.tree.GetFirstGradientColour()) + self.secondcolour = csel.ColourSelect(pnl, -1, "Second Colour", + self.tree.GetSecondGradientColour()) + self.firstcolour.Bind(csel.EVT_COLOURSELECT, self.OnFirstColour) + self.secondcolour.Bind(csel.EVT_COLOURSELECT, self.OnSecondColour) + sizerb3.Add(self.firstcolour, 0, wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 3) + sizerb3.Add(self.secondcolour, 0, wx.LEFT|wx.TOP|wx.BOTTOM|wx.ALIGN_CENTER_HORIZONTAL, 3) + sizerb.Add(self.checkgradient, 0, wx.ALL, 3) + sizerb.Add(sizerb1, 0) + sizerb.Add(sizerb2, 0) + sizerb.Add(sizerb3, 0, wx.ALIGN_CENTER) + + self.checkvista = wx.CheckBox(pnl, -1, "Windows Vista Theme") + self.checkvista.Bind(wx.EVT_CHECKBOX, self.OnVista) + + themessizer.Add(sizera, 0, wx.EXPAND) + themessizer.Add(sizerb, 0, wx.EXPAND) + themessizer.Add((0, 5)) + themessizer.Add(self.checkvista, 0, wx.EXPAND|wx.ALL, 3) + + mainsizer.Add(stylesizer, 0, wx.EXPAND|wx.ALL, 5) + mainsizer.Add(colourssizer, 0, wx.EXPAND|wx.ALL, 5) + mainsizer.Add(themessizer, 0, wx.EXPAND|wx.ALL, 5) + mainsizer.Add(eventssizer, 0, wx.EXPAND|wx.ALL, 5) + + pnl.SetSizer(mainsizer) + pnl.Fit() + + swsizer = wx.BoxSizer(wx.VERTICAL) + swsizer.Add(pnl, 0, wx.EXPAND) + self.leftpanel.SetSizer(swsizer) + swsizer.Layout() + + radiobackground.SetValue(1) + self.checknormal.SetValue(1) + self.radiohorizontal.Enable(False) + self.radiovertical.Enable(False) + self.firstcolour.Enable(False) + self.secondcolour.Enable(False) + self.imagebutton.Enable(False) + + return mainsizer.CalcMin().width + wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + + + def OnRecreateTree(self, event): + + splitter = self.tree.GetParent() + newtree = CustomTreeCtrl(splitter, -1, log=self.log) + splitter.ReplaceWindow(self.tree, newtree) + self.tree.Destroy() + self.tree = newtree + # Todo: The settings in the leftpanel should be reset too + + + def OnCheckStyle(self, event): + + self.tree.ChangeStyle(self.treestyles) + event.Skip() + + + def OnCheckEvent(self, event): + + obj = event.GetEventObject() + self.tree.BindEvents(obj) + + event.Skip() + + + def OnButtonConnection(self, event): + + pen = self.tree.GetConnectionPen() + dlg = PenDialog(self, -1, oldpen=pen, pentype=0) + + dlg.ShowModal() + + event.Skip() + + + def SetConnectionPen(self, pen): + + self.tree.SetConnectionPen(pen) + + + def OnButtonBorder(self, event): + + pen = self.tree.GetBorderPen() + dlg = PenDialog(self, -1, oldpen=pen, pentype=1) + + dlg.ShowModal() + event.Skip() + + + def SetBorderPen(self, pen): + + self.tree.SetBorderPen(pen) + + + def OnButtonTree(self, event): + + dlg = TreeButtonsDialog(self, -1, oldicons=self.oldicons) + dlg.ShowModal() + + event.Skip() + + + def OnButtonCheckRadio(self, event): + + dlg = CheckDialog(self, -1) + dlg.ShowModal() + + event.Skip() + + + def SetTreeButtons(self, selection): + + bitmap_plus = opj("bitmaps/plus" + str(selection+1) + ".ico") + bitmap_minus = opj("bitmaps/minus" + str(selection+1) + ".ico") + + bitmap = wx.Bitmap(bitmap_plus, wx.BITMAP_TYPE_ICO) + width = bitmap.GetWidth() + + il = wx.ImageList(width, width) + + il.Add(wx.Bitmap(bitmap_plus, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_plus, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_minus, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_minus, wx.BITMAP_TYPE_ICO)) + + self.il = il + self.tree.SetButtonsImageList(il) + + + def SetCheckRadio(self, selection): + + if selection == 0: + self.tree.SetImageListCheck(13, 13) + else: + bitmap_check = opj("bitmaps/aquachecked.ico") + bitmap_uncheck = opj("bitmaps/aquanotchecked.ico") + bitmap_flag = opj("bitmaps/aquaflagged.ico") + bitmap_unflag = opj("bitmaps/aquanotflagged.ico") + + il = wx.ImageList(16, 16) + + il.Add(wx.Bitmap(bitmap_check, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_uncheck, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_flag, wx.BITMAP_TYPE_ICO)) + il.Add(wx.Bitmap(bitmap_unflag, wx.BITMAP_TYPE_ICO)) + self.tree.SetImageListCheck(16, 16, il) + + + def OnBackgroundImage(self, event): + + if hasattr(self, "backgroundimage"): + self.tree.SetBackgroundImage(self.backgroundimage) + + self.backbutton.Enable(False) + self.imagebutton.Enable(True) + + event.Skip() + + + def OnChooseImage(self, event): + + wildcard = "JPEG Files (*.jpg)|*.jpg|" \ + "Bitmap Files (*.bmp)|*.bmp|" \ + "PNG Files (*.png)|*.png|" \ + "Icon Files (*.ico)|*.ico|" \ + "GIF Files (*.gif)|*.gif|" \ + "All files (*.*)|*.*" + + dlg = wx.FileDialog(self, "Choose An Image File", ".", "", wildcard, wx.OPEN) + + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + else: + dlg.Destroy() + return + + dlg.Destroy() + bitmap = wx.Bitmap(path, wx.BITMAP_TYPE_ANY) + self.tree.SetBackgroundImage(bitmap) + self.backgroundimage = bitmap + + event.Skip() + + + def OnBackgroundColour(self, event): + + self.imagebutton.Enable(False) + self.backbutton.Enable(True) + self.tree.SetBackgroundImage(None) + + event.Skip() + + + def OnChooseBackground(self, event): + + col1 = event.GetValue() + self.tree.SetBackgroundColour(col1) + event.Skip() + + + def OnCheckNormal(self, event): + + self.radiohorizontal.Enable(False) + self.radiovertical.Enable(False) + self.firstcolour.Enable(False) + self.secondcolour.Enable(False) + self.focus.Enable(True) + self.unfocus.Enable(True) + self.checkgradient.SetValue(0) + self.checkvista.SetValue(0) + self.tree.EnableSelectionGradient(False) + self.tree.EnableSelectionVista(False) + event.Skip() + + + def OnFocusColour(self, event): + + col1 = event.GetValue() + self.tree.SetHilightFocusColour(col1) + event.Skip() + + + def OnNonFocusColour(self, event): + + col1 = event.GetValue() + self.tree.SetHilightNonFocusColour(col1) + event.Skip() + + + def OnCheckGradient(self, event): + + self.radiohorizontal.Enable(True) + self.radiovertical.Enable(True) + self.firstcolour.Enable(True) + self.secondcolour.Enable(True) + self.checknormal.SetValue(0) + self.checkvista.SetValue(0) + self.focus.Enable(False) + self.unfocus.Enable(False) + self.tree.SetGradientStyle(self.radiovertical.GetValue()) + self.tree.EnableSelectionVista(False) + self.tree.EnableSelectionGradient(True) + + event.Skip() + + + def OnHorizontal(self, event): + + self.tree.SetGradientStyle(self.radiovertical.GetValue()) + event.Skip() + + + def OnVertical(self, event): + + self.tree.SetGradientStyle(self.radiovertical.GetValue()) + event.Skip() + + + def OnFirstColour(self, event): + + col1 = event.GetValue() + self.tree.SetFirstGradientColour(wx.Colour(col1[0], col1[1], col1[2])) + event.Skip() + + + def OnSecondColour(self, event): + + col1 = event.GetValue() + self.tree.SetSecondGradientColour(wx.Colour(col1[0], col1[1], col1[2])) + event.Skip() + + + def OnVista(self, event): + + self.radiohorizontal.Enable(False) + self.radiovertical.Enable(False) + self.firstcolour.Enable(False) + self.secondcolour.Enable(False) + self.checknormal.SetValue(0) + self.checkgradient.SetValue(0) + self.focus.Enable(False) + self.unfocus.Enable(False) + self.tree.EnableSelectionGradient(False) + self.tree.EnableSelectionVista(True) + + event.Skip() + + + + + +#--------------------------------------------------------------------------- +# CustomTreeCtrl Demo Implementation +#--------------------------------------------------------------------------- +class CustomTreeCtrl(CT.CustomTreeCtrl): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, + style=wx.SUNKEN_BORDER, + ctstyle=CT.TR_HAS_BUTTONS | CT.TR_HAS_VARIABLE_ROW_HEIGHT, + log=None): + + CT.CustomTreeCtrl.__init__(self, parent, id, pos, size, style, ctstyle) + + alldata = dir(CT) + + treestyles = [] + events = [] + for data in alldata: + if data.startswith("TR_"): + treestyles.append(data) + elif data.startswith("EVT_"): + events.append(data) + + self.events = events + self.styles = treestyles + self.item = None + + il = wx.ImageList(16, 16) + + for items in ArtIDs[1:-1]: + bmp = wx.ArtProvider_GetBitmap(eval(items), wx.ART_TOOLBAR, (16, 16)) + il.Add(bmp) + + smileidx = il.Add(images.getSmilesBitmap()) + numicons = il.GetImageCount() + + self.AssignImageList(il) + self.count = 0 + self.log = log + + # NOTE: For some reason tree items have to have a data object in + # order to be sorted. Since our compare just uses the labels + # we don't need any real data, so we'll just use None below for + # the item data. + + self.root = self.AddRoot("The Root Item") + + if not(self.GetTreeStyle() & CT.TR_HIDE_ROOT): + self.SetPyData(self.root, None) + self.SetItemImage(self.root, 24, CT.TreeItemIcon_Normal) + self.SetItemImage(self.root, 13, CT.TreeItemIcon_Expanded) + + textctrl = wx.TextCtrl(self, -1, "I Am A Simple\nMultiline wx.TexCtrl", style=wx.TE_MULTILINE) + self.gauge = wx.Gauge(self, -1, 50, style=wx.GA_HORIZONTAL|wx.GA_SMOOTH) + self.gauge.SetValue(0) + combobox = wx.ComboBox(self, -1, choices=["That", "Was", "A", "Nice", "Holyday!"], style=wx.CB_READONLY|wx.CB_DROPDOWN) + + textctrl.Bind(wx.EVT_CHAR, self.OnTextCtrl) + combobox.Bind(wx.EVT_COMBOBOX, self.OnComboBox) + + for x in range(15): + if x == 1: + child = self.AppendItem(self.root, "Item %d" % x + "\nHello World\nHappy wxPython-ing!") + self.SetItemBold(child, True) + else: + child = self.AppendItem(self.root, "Item %d" % x) + self.SetPyData(child, None) + self.SetItemImage(child, 24, CT.TreeItemIcon_Normal) + self.SetItemImage(child, 13, CT.TreeItemIcon_Expanded) + + for y in range(5): + if y == 0 and x == 1: + last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=2, wnd=self.gauge) + elif y == 1 and x == 2: + last = self.AppendItem(child, "Item %d-%s" % (x, chr(ord("a")+y)), ct_type=1, wnd=textctrl) + elif 2 < y < 4: + last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y))) + elif y == 4 and x == 1: + last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), wnd=combobox) + else: + last = self.AppendItem(child, "item %d-%s" % (x, chr(ord("a")+y)), ct_type=2) + + self.SetPyData(last, None) + self.SetItemImage(last, 24, CT.TreeItemIcon_Normal) + self.SetItemImage(last, 13, CT.TreeItemIcon_Expanded) + + for z in range(5): + if z > 2: + item = self.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=1) + elif 0 < z <= 2: + item = self.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z), ct_type=2) + elif z == 0: + item = self.AppendItem(last, "item %d-%s-%d" % (x, chr(ord("a")+y), z)) + self.SetItemHyperText(item, True) + self.SetPyData(item, None) + self.SetItemImage(item, 28, CT.TreeItemIcon_Normal) + self.SetItemImage(item, numicons-1, CT.TreeItemIcon_Selected) + + self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) + self.Bind(wx.EVT_IDLE, self.OnIdle) + + + self.eventdict = {'EVT_TREE_BEGIN_DRAG': self.OnBeginDrag, 'EVT_TREE_BEGIN_LABEL_EDIT': self.OnBeginEdit, + 'EVT_TREE_BEGIN_RDRAG': self.OnBeginRDrag, 'EVT_TREE_DELETE_ITEM': self.OnDeleteItem, + 'EVT_TREE_END_DRAG': self.OnEndDrag, 'EVT_TREE_END_LABEL_EDIT': self.OnEndEdit, + 'EVT_TREE_ITEM_ACTIVATED': self.OnActivate, 'EVT_TREE_ITEM_CHECKED': self.OnItemCheck, + 'EVT_TREE_ITEM_CHECKING': self.OnItemChecking, 'EVT_TREE_ITEM_COLLAPSED': self.OnItemCollapsed, + 'EVT_TREE_ITEM_COLLAPSING': self.OnItemCollapsing, 'EVT_TREE_ITEM_EXPANDED': self.OnItemExpanded, + 'EVT_TREE_ITEM_EXPANDING': self.OnItemExpanding, 'EVT_TREE_ITEM_GETTOOLTIP': self.OnToolTip, + 'EVT_TREE_ITEM_MENU': self.OnItemMenu, 'EVT_TREE_ITEM_RIGHT_CLICK': self.OnRightDown, + 'EVT_TREE_KEY_DOWN': self.OnKey, 'EVT_TREE_SEL_CHANGED': self.OnSelChanged, + 'EVT_TREE_SEL_CHANGING': self.OnSelChanging, "EVT_TREE_ITEM_HYPERLINK": self.OnHyperLink} + + mainframe = wx.GetTopLevelParent(self) + + if not hasattr(mainframe, "leftpanel"): + self.Bind(CT.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded) + self.Bind(CT.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed) + self.Bind(CT.EVT_TREE_SEL_CHANGED, self.OnSelChanged) + self.Bind(CT.EVT_TREE_SEL_CHANGING, self.OnSelChanging) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) + self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) + else: + for combos in mainframe.treeevents: + self.BindEvents(combos) + + if hasattr(mainframe, "leftpanel"): + self.ChangeStyle(mainframe.treestyles) + + if not(self.GetTreeStyle() & CT.TR_HIDE_ROOT): + self.SelectItem(self.root) + self.Expand(self.root) + + + def BindEvents(self, choice, recreate=False): + + value = choice.GetValue() + text = choice.GetLabel() + + evt = "CT." + text + binder = self.eventdict[text] + + if value == 1: + if evt == "CT.EVT_TREE_BEGIN_RDRAG": + self.Bind(wx.EVT_RIGHT_DOWN, None) + self.Bind(wx.EVT_RIGHT_UP, None) + self.Bind(eval(evt), binder) + else: + self.Bind(eval(evt), None) + if evt == "CT.EVT_TREE_BEGIN_RDRAG": + self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) + self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp) + + + def ChangeStyle(self, combos): + + style = 0 + for combo in combos: + if combo.GetValue() == 1: + style = style | eval("CT." + combo.GetLabel()) + + if self.GetTreeStyle() != style: + self.SetTreeStyle(style) + + + def OnCompareItems(self, item1, item2): + + t1 = self.GetItemText(item1) + t2 = self.GetItemText(item2) + + self.log.write('compare: ' + t1 + ' <> ' + t2 + "\n") + + if t1 < t2: + return -1 + if t1 == t2: + return 0 + + return 1 + + + def OnIdle(self, event): + + if self.gauge: + try: + if self.gauge.IsEnabled() and self.gauge.IsShown(): + self.count = self.count + 1 + + if self.count >= 50: + self.count = 0 + + self.gauge.SetValue(self.count) + + except: + self.gauge = None + + event.Skip() + + + def OnRightDown(self, event): + + pt = event.GetPosition() + item, flags = self.HitTest(pt) + + if item: + self.item = item + self.log.write("OnRightClick: %s, %s, %s" % (self.GetItemText(item), type(item), item.__class__) + "\n") + self.SelectItem(item) + + + def OnRightUp(self, event): + + item = self.item + + if not item: + event.Skip() + return + + if not self.IsEnabled(item): + event.Skip() + return + + # Item Text Appearance + ishtml = self.IsItemHyperText(item) + back = self.GetItemBackgroundColour(item) + fore = self.GetItemTextColour(item) + isbold = self.IsBold(item) + font = self.GetItemFont(item) + + # Icons On Item + normal = self.GetItemImage(item, CT.TreeItemIcon_Normal) + selected = self.GetItemImage(item, CT.TreeItemIcon_Selected) + expanded = self.GetItemImage(item, CT.TreeItemIcon_Expanded) + selexp = self.GetItemImage(item, CT.TreeItemIcon_SelectedExpanded) + + # Enabling/Disabling Windows Associated To An Item + haswin = self.GetItemWindow(item) + + # Enabling/Disabling Items + enabled = self.IsEnabled(item) + + # Generic Item's Info + children = self.GetChildrenCount(item) + itemtype = self.GetItemType(item) + text = self.GetItemText(item) + pydata = self.GetPyData(item) + + self.current = item + self.itemdict = {"ishtml": ishtml, "back": back, "fore": fore, "isbold": isbold, + "font": font, "normal": normal, "selected": selected, "expanded": expanded, + "selexp": selexp, "haswin": haswin, "children": children, + "itemtype": itemtype, "text": text, "pydata": pydata, "enabled": enabled} + + menu = wx.Menu() + + item1 = menu.Append(wx.ID_ANY, "Change Item Background Colour") + item2 = menu.Append(wx.ID_ANY, "Modify Item Text Colour") + menu.AppendSeparator() + if isbold: + strs = "Make Item Text Not Bold" + else: + strs = "Make Item Text Bold" + item3 = menu.Append(wx.ID_ANY, strs) + item4 = menu.Append(wx.ID_ANY, "Change Item Font") + menu.AppendSeparator() + if ishtml: + strs = "Set Item As Non-Hyperlink" + else: + strs = "Set Item As Hyperlink" + item5 = menu.Append(wx.ID_ANY, strs) + menu.AppendSeparator() + if haswin: + enabled = self.GetItemWindowEnabled(item) + if enabled: + strs = "Disable Associated Widget" + else: + strs = "Enable Associated Widget" + else: + strs = "Enable Associated Widget" + item6 = menu.Append(wx.ID_ANY, strs) + + if not haswin: + item6.Enable(False) + + item7 = menu.Append(wx.ID_ANY, "Disable Item") + + menu.AppendSeparator() + item8 = menu.Append(wx.ID_ANY, "Change Item Icons") + menu.AppendSeparator() + item9 = menu.Append(wx.ID_ANY, "Get Other Information For This Item") + menu.AppendSeparator() + + item10 = menu.Append(wx.ID_ANY, "Delete Item") + if item == self.GetRootItem(): + item10.Enable(False) + item11 = menu.Append(wx.ID_ANY, "Prepend An Item") + item12 = menu.Append(wx.ID_ANY, "Append An Item") + + self.Bind(wx.EVT_MENU, self.OnItemBackground, item1) + self.Bind(wx.EVT_MENU, self.OnItemForeground, item2) + self.Bind(wx.EVT_MENU, self.OnItemBold, item3) + self.Bind(wx.EVT_MENU, self.OnItemFont, item4) + self.Bind(wx.EVT_MENU, self.OnItemHyperText, item5) + self.Bind(wx.EVT_MENU, self.OnEnableWindow, item6) + self.Bind(wx.EVT_MENU, self.OnDisableItem, item7) + self.Bind(wx.EVT_MENU, self.OnItemIcons, item8) + self.Bind(wx.EVT_MENU, self.OnItemInfo, item9) + self.Bind(wx.EVT_MENU, self.OnItemDelete, item10) + self.Bind(wx.EVT_MENU, self.OnItemPrepend, item11) + self.Bind(wx.EVT_MENU, self.OnItemAppend, item12) + + self.PopupMenu(menu) + menu.Destroy() + event.Skip() + + + def OnItemBackground(self, event): + + colourdata = wx.ColourData() + colourdata.SetColour(self.itemdict["back"]) + dlg = wx.ColourDialog(self, colourdata) + + dlg.GetColourData().SetChooseFull(True) + + if dlg.ShowModal() == wx.ID_OK: + data = dlg.GetColourData() + col1 = data.GetColour().Get() + self.SetItemBackgroundColour(self.current, col1) + dlg.Destroy() + event.Skip() + + + def OnItemForeground(self, event): + + colourdata = wx.ColourData() + colourdata.SetColour(self.itemdict["fore"]) + dlg = wx.ColourDialog(self, colourdata) + + dlg.GetColourData().SetChooseFull(True) + + if dlg.ShowModal() == wx.ID_OK: + data = dlg.GetColourData() + col1 = data.GetColour().Get() + self.SetItemTextColour(self.current, col1) + dlg.Destroy() + event.Skip() + + + def OnItemBold(self, event): + + self.SetItemBold(self.current, not self.itemdict["isbold"]) + event.Skip() + + + def OnItemFont(self, event): + + data = wx.FontData() + font = self.itemdict["font"] + + if font is None: + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + + data.SetInitialFont(font) + + dlg = wx.FontDialog(self, data) + + if dlg.ShowModal() == wx.ID_OK: + data = dlg.GetFontData() + font = data.GetChosenFont() + self.SetItemFont(self.current, font) + + dlg.Destroy() + event.Skip() + + + def OnItemHyperText(self, event): + + self.SetItemHyperText(self.current, not self.itemdict["ishtml"]) + event.Skip() + + + def OnEnableWindow(self, event): + + enable = self.GetItemWindowEnabled(self.current) + self.SetItemWindowEnabled(self.current, not enable) + + event.Skip() + + + def OnDisableItem(self, event): + + self.EnableItem(self.current, False) + event.Skip() + + + def OnItemIcons(self, event): + + bitmaps = [self.itemdict["normal"], self.itemdict["selected"], + self.itemdict["expanded"], self.itemdict["selexp"]] + + wx.BeginBusyCursor() + dlg = TreeIcons(self, -1, bitmaps=bitmaps) + wx.EndBusyCursor() + dlg.ShowModal() + event.Skip() + + + def SetNewIcons(self, bitmaps): + + self.SetItemImage(self.current, bitmaps[0], CT.TreeItemIcon_Normal) + self.SetItemImage(self.current, bitmaps[1], CT.TreeItemIcon_Selected) + self.SetItemImage(self.current, bitmaps[2], CT.TreeItemIcon_Expanded) + self.SetItemImage(self.current, bitmaps[3], CT.TreeItemIcon_SelectedExpanded) + + + def OnItemInfo(self, event): + + itemtext = self.itemdict["text"] + numchildren = str(self.itemdict["children"]) + itemtype = self.itemdict["itemtype"] + pydata = repr(type(self.itemdict["pydata"])) + + if itemtype == 0: + itemtype = "Normal" + elif itemtype == 1: + itemtype = "CheckBox" + else: + itemtype = "RadioButton" + + strs = "Information On Selected Item:\n\n" + "Text: " + itemtext + "\n" \ + "Number Of Children: " + numchildren + "\n" \ + "Item Type: " + itemtype + "\n" \ + "Item Data Type: " + pydata + "\n" + + dlg = wx.MessageDialog(self, strs, "CustomTreeCtrlDemo Info", wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + event.Skip() + + + def OnItemDelete(self, event): + + strs = "Are You Sure You Want To Delete Item " + self.GetItemText(self.current) + "?" + dlg = wx.MessageDialog(None, strs, 'Deleting Item', wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_QUESTION) + + if dlg.ShowModal() in [wx.ID_NO, wx.ID_CANCEL]: + dlg.Destroy() + return + + dlg.Destroy() + + self.DeleteChildren(self.current) + self.Delete(self.current) + self.current = None + + event.Skip() + + + def OnItemPrepend(self, event): + + dlg = wx.TextEntryDialog(self, "Please Enter The New Item Name", 'Item Naming', 'Python') + + if dlg.ShowModal() == wx.ID_OK: + newname = dlg.GetValue() + newitem = self.PrependItem(self.current, newname) + self.EnsureVisible(newitem) + + dlg.Destroy() + event.Skip() + + + def OnItemAppend(self, event): + + dlg = wx.TextEntryDialog(self, "Please Enter The New Item Name", 'Item Naming', 'Python') + + if dlg.ShowModal() == wx.ID_OK: + newname = dlg.GetValue() + newitem = self.AppendItem(self.current, newname) + self.EnsureVisible(newitem) + + dlg.Destroy() + event.Skip() + + + def OnBeginEdit(self, event): + + self.log.write("OnBeginEdit" + "\n") + # show how to prevent edit... + item = event.GetItem() + if item and self.GetItemText(item) == "The Root Item": + wx.Bell() + self.log.write("You can't edit this one..." + "\n") + + # Lets just see what's visible of its children + cookie = 0 + root = event.GetItem() + (child, cookie) = self.GetFirstChild(root) + + while child: + self.log.write("Child [%s] visible = %d" % (self.GetItemText(child), self.IsVisible(child)) + "\n") + (child, cookie) = self.GetNextChild(root, cookie) + + event.Veto() + + + def OnEndEdit(self, event): + + self.log.write("OnEndEdit: %s %s" %(event.IsEditCancelled(), event.GetLabel())) + # show how to reject edit, we'll not allow any digits + for x in event.GetLabel(): + if x in string.digits: + self.log.write(", You can't enter digits..." + "\n") + event.Veto() + return + + self.log.write("\n") + + + def OnLeftDClick(self, event): + + pt = event.GetPosition() + item, flags = self.HitTest(pt) + if item and (flags & CT.TREE_HITTEST_ONITEMLABEL): + if self.GetTreeStyle() & CT.TR_EDIT_LABELS: + self.log.write("OnLeftDClick: %s (manually starting label edit)"% self.GetItemText(item) + "\n") + self.EditLabel(item) + else: + self.log.write("OnLeftDClick: Cannot Start Manual Editing, Missing Style TR_EDIT_LABELS\n") + + event.Skip() + + + def OnItemExpanded(self, event): + + item = event.GetItem() + if item: + self.log.write("OnItemExpanded: %s" % self.GetItemText(item) + "\n") + + + def OnItemExpanding(self, event): + + item = event.GetItem() + if item: + self.log.write("OnItemExpanding: %s" % self.GetItemText(item) + "\n") + + event.Skip() + + + def OnItemCollapsed(self, event): + + item = event.GetItem() + if item: + self.log.write("OnItemCollapsed: %s" % self.GetItemText(item) + "\n") + + + def OnItemCollapsing(self, event): + + item = event.GetItem() + if item: + self.log.write("OnItemCollapsing: %s" % self.GetItemText(item) + "\n") + + event.Skip() + + + def OnSelChanged(self, event): + + self.item = event.GetItem() + if self.item: + self.log.write("OnSelChanged: %s" % self.GetItemText(self.item)) + if wx.Platform == '__WXMSW__': + self.log.write(", BoundingRect: %s" % self.GetBoundingRect(self.item, True) + "\n") + else: + self.log.write("\n") + + event.Skip() + + + def OnSelChanging(self, event): + + item = event.GetItem() + olditem = event.GetOldItem() + + if item: + if not olditem: + olditemtext = "None" + else: + olditemtext = self.GetItemText(olditem) + self.log.write("OnSelChanging: From %s" % olditemtext + " To %s" % self.GetItemText(item) + "\n") + + event.Skip() + + + def OnBeginDrag(self, event): + + self.item = event.GetItem() + if self.item: + self.log.write("Beginning Drag..." + "\n") + + event.Allow() + event.Skip() + + + def OnBeginRDrag(self, event): + + self.item = event.GetItem() + if self.item: + self.log.write("Beginning Right Drag..." + "\n") + + event.Allow() + event.Skip() + + + def OnEndDrag(self, event): + + self.item = event.GetItem() + if self.item: + self.log.write("Ending Drag!" + "\n") + + event.Skip() + + + def OnDeleteItem(self, event): + + item = event.GetItem() + + if not item: + return + + self.log.write("Deleting Item: %s" % self.GetItemText(item) + "\n") + event.Skip() + + + def OnItemCheck(self, event): + + item = event.GetItem() + self.log.write("Item " + self.GetItemText(item) + " Has Been Checked!\n") + event.Skip() + + + def OnItemChecking(self, event): + + item = event.GetItem() + self.log.write("Item " + self.GetItemText(item) + " Is Being Checked...\n") + event.Skip() + + + def OnToolTip(self, event): + + item = event.GetItem() + if item: + event.SetToolTip(wx.ToolTip(self.GetItemText(item))) + + + def OnItemMenu(self, event): + + item = event.GetItem() + if item: + self.log.write("OnItemMenu: %s" % self.GetItemText(item) + "\n") + + event.Skip() + + + def OnKey(self, event): + + keycode = event.GetKeyCode() + keyname = keyMap.get(keycode, None) + + if keycode == wx.WXK_BACK: + self.log.write("OnKeyDown: HAHAHAHA! I Vetoed Your Backspace! HAHAHAHA\n") + return + + if keyname is None: + if "unicode" in wx.PlatformInfo: + keycode = event.GetUnicodeKey() + if keycode <= 127: + keycode = event.GetKeyCode() + keyname = "\"" + unichr(event.GetUnicodeKey()) + "\"" + if keycode < 27: + keyname = "Ctrl-%s" % chr(ord('A') + keycode-1) + + elif keycode < 256: + if keycode == 0: + keyname = "NUL" + elif keycode < 27: + keyname = "Ctrl-%s" % chr(ord('A') + keycode-1) + else: + keyname = "\"%s\"" % chr(keycode) + else: + keyname = "unknown (%s)" % keycode + + self.log.write("OnKeyDown: You Pressed '" + keyname + "'\n") + + event.Skip() + + + def OnActivate(self, event): + + if self.item: + self.log.write("OnActivate: %s" % self.GetItemText(self.item) + "\n") + + event.Skip() + + + def OnHyperLink(self, event): + + item = event.GetItem() + if item: + self.log.write("OnHyperLink: %s" % self.GetItemText(self.item) + "\n") + + + def OnTextCtrl(self, event): + + char = chr(event.GetKeyCode()) + self.log.write("EDITING THE TEXTCTRL: You Wrote '" + char + \ + "' (KeyCode = " + str(event.GetKeyCode()) + ")\n") + event.Skip() + + + def OnComboBox(self, event): + + selection = event.GetEventObject().GetValue() + self.log.write("CHOICE FROM COMBOBOX: You Chose '" + selection + "'\n") + event.Skip() + + + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = CustomTreeCtrlDemo(nb, log) + return win + +#---------------------------------------------------------------------- + + + +overview = CT.__doc__ + + +if __name__ == '__main__': + import sys,os + import run + run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) + diff --git a/wxPython/demo/Main.py b/wxPython/demo/Main.py index fa46693622..c0a19c56b0 100644 --- a/wxPython/demo/Main.py +++ b/wxPython/demo/Main.py @@ -65,6 +65,7 @@ _treeList = [ 'ExpandoTextCtrl', 'ButtonPanel', 'FlatNotebook', + 'CustomTreeCtrl', ]), # managed windows == things with a (optional) caption you can close @@ -151,6 +152,7 @@ _treeList = [ 'ButtonPanel', 'ColourSelect', 'ComboTreeBox', + 'CustomTreeCtrl', 'Editor', 'FlatNotebook', 'GenericButtons', diff --git a/wxPython/demo/bitmaps/aquachecked.ico b/wxPython/demo/bitmaps/aquachecked.ico new file mode 100644 index 0000000000..fd50f45b02 Binary files /dev/null and b/wxPython/demo/bitmaps/aquachecked.ico differ diff --git a/wxPython/demo/bitmaps/aquaflagged.ico b/wxPython/demo/bitmaps/aquaflagged.ico new file mode 100644 index 0000000000..b3b4816f91 Binary files /dev/null and b/wxPython/demo/bitmaps/aquaflagged.ico differ diff --git a/wxPython/demo/bitmaps/aquanotchecked.ico b/wxPython/demo/bitmaps/aquanotchecked.ico new file mode 100644 index 0000000000..2861033967 Binary files /dev/null and b/wxPython/demo/bitmaps/aquanotchecked.ico differ diff --git a/wxPython/demo/bitmaps/aquanotflagged.ico b/wxPython/demo/bitmaps/aquanotflagged.ico new file mode 100644 index 0000000000..030dc1c702 Binary files /dev/null and b/wxPython/demo/bitmaps/aquanotflagged.ico differ diff --git a/wxPython/demo/bitmaps/checked.ico b/wxPython/demo/bitmaps/checked.ico new file mode 100644 index 0000000000..e63306ff91 Binary files /dev/null and b/wxPython/demo/bitmaps/checked.ico differ diff --git a/wxPython/demo/bitmaps/flagged.ico b/wxPython/demo/bitmaps/flagged.ico new file mode 100644 index 0000000000..5edb063a0f Binary files /dev/null and b/wxPython/demo/bitmaps/flagged.ico differ diff --git a/wxPython/demo/bitmaps/minus1.ico b/wxPython/demo/bitmaps/minus1.ico new file mode 100644 index 0000000000..18893b1467 Binary files /dev/null and b/wxPython/demo/bitmaps/minus1.ico differ diff --git a/wxPython/demo/bitmaps/minus2.ico b/wxPython/demo/bitmaps/minus2.ico new file mode 100644 index 0000000000..60dbadc0d5 Binary files /dev/null and b/wxPython/demo/bitmaps/minus2.ico differ diff --git a/wxPython/demo/bitmaps/minus3.ico b/wxPython/demo/bitmaps/minus3.ico new file mode 100644 index 0000000000..ea65ef84d8 Binary files /dev/null and b/wxPython/demo/bitmaps/minus3.ico differ diff --git a/wxPython/demo/bitmaps/minus4.ico b/wxPython/demo/bitmaps/minus4.ico new file mode 100644 index 0000000000..17f8d434e5 Binary files /dev/null and b/wxPython/demo/bitmaps/minus4.ico differ diff --git a/wxPython/demo/bitmaps/minus5.ico b/wxPython/demo/bitmaps/minus5.ico new file mode 100644 index 0000000000..e748ca5830 Binary files /dev/null and b/wxPython/demo/bitmaps/minus5.ico differ diff --git a/wxPython/demo/bitmaps/notchecked.ico b/wxPython/demo/bitmaps/notchecked.ico new file mode 100644 index 0000000000..d69bc99e1e Binary files /dev/null and b/wxPython/demo/bitmaps/notchecked.ico differ diff --git a/wxPython/demo/bitmaps/notflagged.ico b/wxPython/demo/bitmaps/notflagged.ico new file mode 100644 index 0000000000..d0ccc4cdcf Binary files /dev/null and b/wxPython/demo/bitmaps/notflagged.ico differ diff --git a/wxPython/demo/bitmaps/plus1.ico b/wxPython/demo/bitmaps/plus1.ico new file mode 100644 index 0000000000..a7435fe815 Binary files /dev/null and b/wxPython/demo/bitmaps/plus1.ico differ diff --git a/wxPython/demo/bitmaps/plus2.ico b/wxPython/demo/bitmaps/plus2.ico new file mode 100644 index 0000000000..0cf031a907 Binary files /dev/null and b/wxPython/demo/bitmaps/plus2.ico differ diff --git a/wxPython/demo/bitmaps/plus3.ico b/wxPython/demo/bitmaps/plus3.ico new file mode 100644 index 0000000000..4ddbd45535 Binary files /dev/null and b/wxPython/demo/bitmaps/plus3.ico differ diff --git a/wxPython/demo/bitmaps/plus4.ico b/wxPython/demo/bitmaps/plus4.ico new file mode 100644 index 0000000000..e61dbac6a0 Binary files /dev/null and b/wxPython/demo/bitmaps/plus4.ico differ diff --git a/wxPython/demo/bitmaps/plus5.ico b/wxPython/demo/bitmaps/plus5.ico new file mode 100644 index 0000000000..6acf059ea6 Binary files /dev/null and b/wxPython/demo/bitmaps/plus5.ico differ diff --git a/wxPython/wx/lib/customtreectrl.py b/wxPython/wx/lib/customtreectrl.py new file mode 100644 index 0000000000..1616f48adb --- /dev/null +++ b/wxPython/wx/lib/customtreectrl.py @@ -0,0 +1,5686 @@ +# --------------------------------------------------------------------------------- # +# CUSTOMTREECTRL wxPython IMPLEMENTATION +# Inspired By And Heavily Based On wxGenericTreeCtrl. +# +# Andrea Gavana, @ 17 May 2006 +# Latest Revision: 26 May 2006, 22.30 CET +# +# +# TODO List +# +# Almost All The Features Of wx.TreeCtrl Are Available, And There Is Practically +# No Limit In What Could Be Added To This Class. The First Things That Comes +# To My Mind Are: +# +# 1. Implement The Style TR_EXTENDED (I Have Never Used It, But It May Be Useful). +# +# 2. Add Support For 3-State CheckBoxes (Is That Really Useful?). +# +# 3. Try To Implement A More Flicker-Free Background Image In Cases Like +# Centered Or Stretched Image (Now CustomTreeCtrl Supports Only Tiled +# Background Images). +# +# 4. Try To Mimic Windows wx.TreeCtrl Expanding/Collapsing behaviour: CustomTreeCtrl +# Suddenly Expands/Collapses The Nodes On Mouse Click While The Native Control +# Has Some Kind Of "Smooth" Expanding/Collapsing, Like A Wave. I Don't Even +# Know Where To Start To Do That. +# +# 5. Speed Up General OnPaint Things? I Have No Idea, Here CustomTreeCtrl Is Quite +# Fast, But We Should See On Slower Machines. +# +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# gavana@kpo.kz +# andrea.gavana@gmail.com +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# +# End Of Comments +# --------------------------------------------------------------------------------- # + + +""" +Description +=========== + +CustomTreeCtrl is a class that mimics the behaviour of wx.TreeCtrl, with almost the +same base functionalities plus some more enhancements. This class does not rely on +the native control, as it is a full owner-drawn tree control. +Apart of the base functionalities of CustomTreeCtrl (described below), in addition +to the standard wx.TreeCtrl behaviour this class supports: + +* CheckBox-type items: checkboxes are easy to handle, just selected or unselected + state with no particular issues in handling the item's children; + +* RadioButton-type items: since I elected to put radiobuttons in CustomTreeCtrl, I + needed some way to handle them, that made sense. So, I used the following approach: + - All peer-nodes that are radiobuttons will be mutually exclusive. In other words, + only one of a set of radiobuttons that share a common parent can be checked at + once. If a radiobutton node becomes checked, then all of its peer radiobuttons + must be unchecked. + - If a radiobutton node becomes unchecked, then all of its child nodes will become + inactive. + +* Hyperlink-type items: they look like an hyperlink, with the proper mouse cursor on + hovering. + +* Multiline text items. + +* Enabling/disabling items (together with their plain or grayed out icons). + +* Whatever non-toplevel widget can be attached next to an item. + +* Default selection style, gradient (horizontal/vertical) selection style and Windows + Vista selection style. + +* Customized drag and drop images built on the fly. + +* Setting the CustomTreeCtrl item buttons to a personalized imagelist. + +* Setting the CustomTreeCtrl check/radio item icons to a personalized imagelist. + +* Changing the style of the lines that connect the items (in terms of wx.Pen styles). + +* Using an image as a CustomTreeCtrl background (currently only in "tile" mode). + +And a lot more. Check the demo for an almost complete review of the functionalities. + + +Base Functionalities +==================== + +CustomTreeCtrl supports all the wx.TreeCtrl styles, except: + - TR_EXTENDED: supports for this style is on the todo list (Am I sure of this?). + +Plus it has 2 more styles to handle checkbox-type items: + - TR_AUTO_CHECK_CHILD : automatically checks/unchecks the item children; + - TR_AUTO_TOGGLE_CHILD: automatically toggles the item children. + +All the methods available in wx.TreeCtrl are also available in CustomTreeCtrl. + + +Events +====== + +All the events supported by wx.TreeCtrl are also available in CustomTreeCtrl, with +a few exceptions: + + - EVT_TREE_GET_INFO (don't know what this means); + - EVT_TREE_SET_INFO (don't know what this means); + - EVT_TREE_ITEM_MIDDLE_CLICK (not implemented, but easy to add); + - EVT_TREE_STATE_IMAGE_CLICK: no need for that, look at the checking events below. + +Plus, CustomTreeCtrl supports the events related to the checkbutton-type items: + + - EVT_TREE_ITEM_CHECKING: an item is being checked; + - EVT_TREE_ITEM_CHECKED: an item has been checked. + +And to hyperlink-type items: + + - EVT_TREE_ITEM_HYPERLINK: an hyperlink item has been clicked (this event is sent + after the EVT_TREE_SEL_CHANGED event). + + +Supported Platforms +=================== + +CustomTreeCtrl has been tested on the following platforms: + * Windows (Windows XP); + * GTK (Thanks to Michele Petrazzo); + * Mac OS (Thanks to John Jackson). + + +Latest Revision: Andrea Gavana @ 26 May 2006, 22.30 CET +Version 0.8 + +""" + + +import wx +import zlib +import cStringIO + +# ---------------------------------------------------------------------------- +# Constants +# ---------------------------------------------------------------------------- + +_NO_IMAGE = -1 +_PIXELS_PER_UNIT = 10 + +# Start editing the current item after half a second (if the mouse hasn't +# been clicked/moved) +_DELAY = 500 + +# ---------------------------------------------------------------------------- +# Constants +# ---------------------------------------------------------------------------- + +# Enum for different images associated with a treectrl item +TreeItemIcon_Normal = 0 # not selected, not expanded +TreeItemIcon_Selected = 1 # selected, not expanded +TreeItemIcon_Expanded = 2 # not selected, expanded +TreeItemIcon_SelectedExpanded = 3 # selected, expanded + +TreeItemIcon_Checked = 0 # check button, checked +TreeItemIcon_NotChecked = 1 # check button, not checked +TreeItemIcon_Flagged = 2 # radio button, selected +TreeItemIcon_NotFlagged = 3 # radio button, not selected + +# ---------------------------------------------------------------------------- +# CustomTreeCtrl flags +# ---------------------------------------------------------------------------- + +TR_NO_BUTTONS = wx.TR_NO_BUTTONS # for convenience +TR_HAS_BUTTONS = wx.TR_HAS_BUTTONS # draw collapsed/expanded btns +TR_NO_LINES = wx.TR_NO_LINES # don't draw lines at all +TR_LINES_AT_ROOT = wx.TR_LINES_AT_ROOT # connect top-level nodes +TR_TWIST_BUTTONS = wx.TR_TWIST_BUTTONS # still used by wxTreeListCtrl + +TR_SINGLE = wx.TR_SINGLE # for convenience +TR_MULTIPLE = wx.TR_MULTIPLE # can select multiple items +TR_EXTENDED = wx.TR_EXTENDED # TODO: allow extended selection +TR_HAS_VARIABLE_ROW_HEIGHT = wx.TR_HAS_VARIABLE_ROW_HEIGHT # what it says + +TR_EDIT_LABELS = wx.TR_EDIT_LABELS # can edit item labels +TR_ROW_LINES = wx.TR_ROW_LINES # put border around items +TR_HIDE_ROOT = wx.TR_HIDE_ROOT # don't display root node + +TR_FULL_ROW_HIGHLIGHT = wx.TR_FULL_ROW_HIGHLIGHT # highlight full horz space + +TR_AUTO_CHECK_CHILD = 0x4000 # only meaningful for checkboxes +TR_AUTO_TOGGLE_CHILD = 0x8000 # only meaningful for checkboxes + +TR_DEFAULT_STYLE = wx.TR_DEFAULT_STYLE # default style for the tree control + +# Values for the `flags' parameter of CustomTreeCtrl.HitTest() which determine +# where exactly the specified point is situated: + +TREE_HITTEST_ABOVE = wx.TREE_HITTEST_ABOVE +TREE_HITTEST_BELOW = wx.TREE_HITTEST_BELOW +TREE_HITTEST_NOWHERE = wx.TREE_HITTEST_NOWHERE +# on the button associated with an item. +TREE_HITTEST_ONITEMBUTTON = wx.TREE_HITTEST_ONITEMBUTTON +# on the bitmap associated with an item. +TREE_HITTEST_ONITEMICON = wx.TREE_HITTEST_ONITEMICON +# on the indent associated with an item. +TREE_HITTEST_ONITEMINDENT = wx.TREE_HITTEST_ONITEMINDENT +# on the label (string) associated with an item. +TREE_HITTEST_ONITEMLABEL = wx.TREE_HITTEST_ONITEMLABEL +# on the right of the label associated with an item. +TREE_HITTEST_ONITEMRIGHT = wx.TREE_HITTEST_ONITEMRIGHT +# on the label (string) associated with an item. +TREE_HITTEST_ONITEMSTATEICON = wx.TREE_HITTEST_ONITEMSTATEICON +# on the left of the CustomTreeCtrl. +TREE_HITTEST_TOLEFT = wx.TREE_HITTEST_TOLEFT +# on the right of the CustomTreeCtrl. +TREE_HITTEST_TORIGHT = wx.TREE_HITTEST_TORIGHT +# on the upper part (first half) of the item. +TREE_HITTEST_ONITEMUPPERPART = wx.TREE_HITTEST_ONITEMUPPERPART +# on the lower part (second half) of the item. +TREE_HITTEST_ONITEMLOWERPART = wx.TREE_HITTEST_ONITEMLOWERPART +# on the check icon, if present +TREE_HITTEST_ONITEMCHECKICON = 0x4000 +# anywhere on the item +TREE_HITTEST_ONITEM = TREE_HITTEST_ONITEMICON | TREE_HITTEST_ONITEMLABEL | TREE_HITTEST_ONITEMCHECKICON + + +# Background Image Style +_StyleTile = 0 +_StyleStretch = 1 + +# Windows Vista Colours +_rgbSelectOuter = wx.Colour(170, 200, 245) +_rgbSelectInner = wx.Colour(230, 250, 250) +_rgbSelectTop = wx.Colour(210, 240, 250) +_rgbSelectBottom = wx.Colour(185, 215, 250) +_rgbNoFocusTop = wx.Colour(250, 250, 250) +_rgbNoFocusBottom = wx.Colour(235, 235, 235) +_rgbNoFocusOuter = wx.Colour(220, 220, 220) +_rgbNoFocusInner = wx.Colour(245, 245, 245) + +# Flags for wx.RendererNative +_CONTROL_EXPANDED = 8 +_CONTROL_CURRENT = 16 + +# Version Info +__version__ = "0.8" + + +# ---------------------------------------------------------------------------- +# CustomTreeCtrl events and binding for handling them +# ---------------------------------------------------------------------------- + +wxEVT_TREE_BEGIN_DRAG = wx.wxEVT_COMMAND_TREE_BEGIN_DRAG +wxEVT_TREE_BEGIN_RDRAG = wx.wxEVT_COMMAND_TREE_BEGIN_RDRAG +wxEVT_TREE_BEGIN_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT +wxEVT_TREE_END_LABEL_EDIT = wx.wxEVT_COMMAND_TREE_END_LABEL_EDIT +wxEVT_TREE_DELETE_ITEM = wx.wxEVT_COMMAND_TREE_DELETE_ITEM +wxEVT_TREE_GET_INFO = wx.wxEVT_COMMAND_TREE_GET_INFO +wxEVT_TREE_SET_INFO = wx.wxEVT_COMMAND_TREE_SET_INFO +wxEVT_TREE_ITEM_EXPANDED = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDED +wxEVT_TREE_ITEM_EXPANDING = wx.wxEVT_COMMAND_TREE_ITEM_EXPANDING +wxEVT_TREE_ITEM_COLLAPSED = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSED +wxEVT_TREE_ITEM_COLLAPSING = wx.wxEVT_COMMAND_TREE_ITEM_COLLAPSING +wxEVT_TREE_SEL_CHANGED = wx.wxEVT_COMMAND_TREE_SEL_CHANGED +wxEVT_TREE_SEL_CHANGING = wx.wxEVT_COMMAND_TREE_SEL_CHANGING +wxEVT_TREE_KEY_DOWN = wx.wxEVT_COMMAND_TREE_KEY_DOWN +wxEVT_TREE_ITEM_ACTIVATED = wx.wxEVT_COMMAND_TREE_ITEM_ACTIVATED +wxEVT_TREE_ITEM_RIGHT_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK +wxEVT_TREE_ITEM_MIDDLE_CLICK = wx.wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK +wxEVT_TREE_END_DRAG = wx.wxEVT_COMMAND_TREE_END_DRAG +wxEVT_TREE_STATE_IMAGE_CLICK = wx.wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK +wxEVT_TREE_ITEM_GETTOOLTIP = wx.wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP +wxEVT_TREE_ITEM_MENU = wx.wxEVT_COMMAND_TREE_ITEM_MENU +wxEVT_TREE_ITEM_CHECKING = wx.NewEventType() +wxEVT_TREE_ITEM_CHECKED = wx.NewEventType() +wxEVT_TREE_ITEM_HYPERLINK = wx.NewEventType() + +EVT_TREE_BEGIN_DRAG = wx.EVT_TREE_BEGIN_DRAG +EVT_TREE_BEGIN_RDRAG = wx.EVT_TREE_BEGIN_RDRAG +EVT_TREE_BEGIN_LABEL_EDIT = wx.EVT_TREE_BEGIN_LABEL_EDIT +EVT_TREE_END_LABEL_EDIT = wx.EVT_TREE_END_LABEL_EDIT +EVT_TREE_DELETE_ITEM = wx.EVT_TREE_DELETE_ITEM +EVT_TREE_GET_INFO = wx.EVT_TREE_GET_INFO +EVT_TREE_SET_INFO = wx.EVT_TREE_SET_INFO +EVT_TREE_ITEM_EXPANDED = wx.EVT_TREE_ITEM_EXPANDED +EVT_TREE_ITEM_EXPANDING = wx.EVT_TREE_ITEM_EXPANDING +EVT_TREE_ITEM_COLLAPSED = wx.EVT_TREE_ITEM_COLLAPSED +EVT_TREE_ITEM_COLLAPSING = wx.EVT_TREE_ITEM_COLLAPSING +EVT_TREE_SEL_CHANGED = wx.EVT_TREE_SEL_CHANGED +EVT_TREE_SEL_CHANGING = wx.EVT_TREE_SEL_CHANGING +EVT_TREE_KEY_DOWN = wx.EVT_TREE_KEY_DOWN +EVT_TREE_ITEM_ACTIVATED = wx.EVT_TREE_ITEM_ACTIVATED +EVT_TREE_ITEM_RIGHT_CLICK = wx.EVT_TREE_ITEM_RIGHT_CLICK +EVT_TREE_ITEM_MIDDLE_CLICK = wx.EVT_TREE_ITEM_MIDDLE_CLICK +EVT_TREE_END_DRAG = wx.EVT_TREE_END_DRAG +EVT_TREE_STATE_IMAGE_CLICK = wx.EVT_TREE_STATE_IMAGE_CLICK +EVT_TREE_ITEM_GETTOOLTIP = wx.EVT_TREE_ITEM_GETTOOLTIP +EVT_TREE_ITEM_MENU = wx.EVT_TREE_ITEM_MENU +EVT_TREE_ITEM_CHECKING = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKING, 1) +EVT_TREE_ITEM_CHECKED = wx.PyEventBinder(wxEVT_TREE_ITEM_CHECKED, 1) +EVT_TREE_ITEM_HYPERLINK = wx.PyEventBinder(wxEVT_TREE_ITEM_HYPERLINK, 1) + + +def GetFlaggedData(): + return zlib.decompress( +'x\xda\x012\x02\xcd\xfd\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\ +\x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\ +|\x08d\x88\x00\x00\x01\xe9IDAT(\x91u\x92\xd1K\xd3a\x14\x86\x9f\xef|J2J\xc3%\ +\x85\x8e\x1cb\x93Hl\xd9,\x06F]4\x10\tD3\x83\x88\xc8\xbf\xc0\xb4\xaeBP1\xe9\ +\xa2(\xec\xaan\xc3\x82pD\xa1\x84\xb0\x88@3\x8c\xc9\xa2bT\xa2^\x8c\x81V3\xb6\ +\xb5\x9f\xce9\xbe.j\xb20\xdf\xeb\xf7\xe19\x07^\xa5D\x93\x9f\x9ea\xbf\t\x04\ +\xbf\x12\x8b[\xd8Kl\xf8<.\xeet\xb5\xab\xfc\x8e\xca\x87*ZzM\xf3\xb1j|G\xab\ +\xf0\xd4\x94\x13\x9a_&0\xbb\xc8\xd8\xf4g\xa2\xcfo\xa8-P\xc7\xf5\x07\xa6\xedD\ +\r\x8d\xb5\xfb\x11\x11\xb4\xd6\x88h\xb4\xd6L}\x8a\xf0\xe4\xd5G\x1e\rt*\x00\ +\xc9\x19\xb6\x03D4\xa7\xdcU\\8\xed\xa6\xa2\xa5\xd7\x00\xe8\xab\xf7\x9e\x9a\ +\xca\xb2\x9d\\\xf2\xd5!"dT\x86\xc9\xe4\x14\x83s\x83HF\xe3\xdc\xe5\xa4\xa8\ +\xb0\x88\xaa\xf2=D\x7f$il>\xdf\xafSe\xf5\xfd\x9dM\x87\xa9\xdc\xb7\x1b\xad5\ +\x93\xc9)\xfc\xe9Q\x12\xe9\x04\x13\x0b\x13\x94\xaaR\xdc{\x8f "\xec(,\xe0\xfe\ +\xb3\xb7H,a\xe1\xa9)\xdf36\x192\ +\xc9d\xdc\xa4RI\xb3\xbaj\x99tz\xcd\xac\xaf\xa7\xcd\xc6F\xc6d\xb3Y\xf32\xf8\ +\xc58Z\xfb\x8c\x12\xfd\x07R\xa2\xb98\xf0\xd0\xbcx\xf3a[\xe0\xf2\xd0c\x93\xeb\ +nYD\xdb\xc9:\xcex\x0f\xe2\xadu2\x13\x8e0>\x1d\xc6\xff\xfa\xfd\xff\x17\x91K\ +\xf7\xf0\xa8\t\x04\xe7X\x89[\x94\x96\xd8\xf0y\x0ep\xb7\xeb\xdc?\xdb\xfb\r|\ +\xd0\xd1]\x98\xbdm\xdc\x00\x00\x00\x00IEND\xaeB`\x82\x91\xe2\x08\x8f' ) + +def GetFlaggedBitmap(): + return wx.BitmapFromImage(GetFlaggedImage()) + +def GetFlaggedImage(): + stream = cStringIO.StringIO(GetFlaggedData()) + return wx.ImageFromStream(stream) + +#---------------------------------------------------------------------- +def GetNotFlaggedData(): + return zlib.decompress( +'x\xda\x01\xad\x01R\xfe\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\r\x00\ +\x00\x00\r\x08\x06\x00\x00\x00r\xeb\xe4|\x00\x00\x00\x04sBIT\x08\x08\x08\x08\ +|\x08d\x88\x00\x00\x01dIDAT(\x91\x95\xd21K\x82a\x14\x86\xe1\xe7=\xef798\xb8\ +\x89\x0e"|Cd\x94\x88\x83\x065\x88\x108\x88Q\x8b-\xd1\x1f\x88\x9a\n\x04\x11j\ +\x8eh\x08\xdaZ\x84(\x82\xc2 0\xc1 $\xb4P\xa1\x10\x11D\xb061\xd4\xd4\xcc\xe44\ +\x84 \xa8Hg~.\xcer\x0bA\x12\x83\xb7ux\xce\xd1T\x01\xd5z\x0b:\xad\x06n\xbb\ +\x8a\x83\xcdU1\xb8\x11\x83\xc8\xe0\r\xf0\x92\xdd\x0c\x97\xd5\x04\x9b\xaaG\ +\xb6XA,]B\xe41\x8f\xf7\xab=1\x84Vv\x8e\xd97\xaf\xc29m\x04\x91\x84\x94\n\xa4\ +\x94P\x14\x05\x89\xd77\x9c\xc5_\x10\x0em\x08\x00\xa0\xfe\x87q@J\x89\xc593\ +\xfc\xaeY\x18\xbc\x01\x06\x00\xb1}t\xc9\xf5F\x03\x01\xbfs$ \x92 "\x10I\xec\ +\x9e\xdcBQ\x08\x14M\x15\xe0\xb2\x9a&\x02"\x82\xc71\x85h\xaa\x00\xaa\xd6[\xb0\ +\xa9\xfa\x89\x80\x88\xe0\xb0\x98P\xad\xb7@:\xad\x06\xd9be" "$se\xe8\xb4\x1a\ +\x90\xdb\xae"\x96.M\x04D\x84H"\x07\xb7]\x05\x04I\x18}A\xbe\xbe\x7f\xe6Z\xed\ +\x83\x1b\x8d\x1a7\x9b\x9f\xdcn\xb7\xb8\xd3\xf9\xe2n\xf7\x9b{\xbd\x1f\xbe{\ +\xca\xb3\xd1\x17dA\xf2\x0f\t\x92X\x0b\x9d\xf2\xcdCf,X\xdf\x0fs\x7f;T\xc4\xf2\ +\xc2\x0c<\x8e)8,&$seD\x129\\\xc43\xa3\x8b\xf8O{\xbf\xf1\xb5\xa5\x990\x0co\ +\xd6\x00\x00\x00\x00IEND\xaeB`\x82&\x11\xab!' ) + +def GetNotFlaggedBitmap(): + return wx.BitmapFromImage(GetNotFlaggedImage()) + +def GetNotFlaggedImage(): + stream = cStringIO.StringIO(GetNotFlaggedData()) + return wx.ImageFromStream(stream) + +#---------------------------------------------------------------------- +def GetCheckedData(): + return zlib.decompress( +"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\ +\x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xaf\xf4tq\x0c\xd1\x98\ +\x98<\x853\xe7\xc7y\x07\xa5\x84\xc4\x84\x84\x04\x0b3C1\xbd\x03'N\x1c9p\x84\ +\xe5\xe0\x993gx||\xce\x14\xcc\xea\xec\xect4^7\xbf\x91\xf3&\x8b\x93\xd4\x8c\ +\x19\n\xa7fv\\L\xd8p\x90C\xebx\xcf\x05\x17\x0ff \xb8c\xb6Cm\x06\xdb\xea\xd8\ +\xb2\x08\xd3\x03W\x0c\x8c\x8c\x16e%\xa5\xb5E\xe4\xee\xba\xca\xe4|\xb8\xb7\ +\xe35OOO\xcf\n\xb3\x83>m\x8c1R\x12\x92\x81s\xd8\x0b/\xb56\x14k|l\\\xc7x\xb4\ +\xf2\xc4\xc1*\xd5'B~\xbc\x19uNG\x98\x85\x85\x8d\xe3x%\x16\xb2_\xee\xf1\x07\ +\x99\xcb\xacl\x99\xc9\xcf\xb0\xc0_.\x87+\xff\x99\x05\xd0\xd1\x0c\x9e\xae~.\ +\xeb\x9c\x12\x9a\x00\x92\xccS\x9f" ) + +def GetCheckedBitmap(): + return wx.BitmapFromImage(GetCheckedImage()) + +def GetCheckedImage(): + stream = cStringIO.StringIO(GetCheckedData()) + return wx.ImageFromStream(stream) + +#---------------------------------------------------------------------- +def GetNotCheckedData(): + return zlib.decompress( +"x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd1 \xcc\xc1\x06$\ +\x8b^?\xa9\x01R,\xc5N\x9e!\x1c@P\xc3\x91\xd2\x01\xe4\xe7z\xba8\x86hL\x9c{\ +\xe9 o\x83\x01\x07\xeb\x85\xf3\xed\x86w\x0ed\xdaT\x96\x8a\xbc\x9fw\xe7\xc4\ +\xd9/\x01\x8b\x97\x8a\xd7\xab*\xfar\xf0Ob\x93^\xf6\xd5%\x9d\x85A\xe6\xf6\x1f\ +\x11\x8f{/\x0b\xf8wX+\x9d\xf2\xb6:\x96\xca\xfe\x9a3\xbeA\xe7\xed\x1b\xc6%\ +\xfb=X3'sI-il\t\xb9\xa0\xc0;#\xd4\x835m\x9a\xf9J\x85\xda\x16.\x86\x03\xff\ +\xee\xdcc\xdd\xc0\xce\xf9\xc8\xcc(\xbe\x1bh1\x83\xa7\xab\x9f\xcb:\xa7\x84&\ +\x00\x87S=\xbe" ) + +def GetNotCheckedBitmap(): + return wx.BitmapFromImage(GetNotCheckedImage()) + +def GetNotCheckedImage(): + stream = cStringIO.StringIO(GetNotCheckedData()) + return wx.ImageFromStream(stream) + + +def GrayOut(anImage): + """ + Convert the given image (in place) to a grayed-out version, + appropriate for a 'disabled' appearance. + """ + + factor = 0.7 # 0 < f < 1. Higher Is Grayer + + if anImage.HasMask(): + maskColor = (anImage.GetMaskRed(), anImage.GetMaskGreen(), anImage.GetMaskBlue()) + else: + maskColor = None + + data = map(ord, list(anImage.GetData())) + + for i in range(0, len(data), 3): + + pixel = (data[i], data[i+1], data[i+2]) + pixel = MakeGray(pixel, factor, maskColor) + + for x in range(3): + data[i+x] = pixel[x] + + anImage.SetData(''.join(map(chr, data))) + + return anImage + + +def MakeGray((r,g,b), factor, maskColor): + """ + Make a pixel grayed-out. If the pixel matches the maskcolor, it won't be + changed. + """ + + if (r,g,b) != maskColor: + return map(lambda x: int((230 - x) * factor) + x, (r,g,b)) + else: + return (r,g,b) + + +def DrawTreeItemButton(win, dc, rect, flags): + """ A simple replacement of wx.RendererNative.DrawTreeItemButton. """ + + # white background + dc.SetPen(wx.GREY_PEN) + dc.SetBrush(wx.WHITE_BRUSH) + dc.DrawRectangleRect(rect) + + # black lines + xMiddle = rect.x + rect.width/2 + yMiddle = rect.y + rect.height/2 + + # half of the length of the horz lines in "-" and "+" + halfWidth = rect.width/2 - 2 + dc.SetPen(wx.BLACK_PEN) + dc.DrawLine(xMiddle - halfWidth, yMiddle, + xMiddle + halfWidth + 1, yMiddle) + + if not flags & _CONTROL_EXPANDED: + + # turn "-" into "+" + halfHeight = rect.height/2 - 2 + dc.DrawLine(xMiddle, yMiddle - halfHeight, + xMiddle, yMiddle + halfHeight + 1) + + +#--------------------------------------------------------------------------- +# DragImage Implementation +# This Class Handles The Creation Of A Custom Image In Case Of Item Drag +# And Drop. +#--------------------------------------------------------------------------- + +class DragImage(wx.DragImage): + """ + This class handles the creation of a custom image in case of item drag + and drop. + """ + + def __init__(self, treeCtrl, item): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + text = item.GetText() + font = item.Attr().GetFont() + colour = item.Attr().GetTextColour() + if colour is None: + colour = wx.BLACK + if font is None: + font = treeCtrl._normalFont + + backcolour = treeCtrl.GetBackgroundColour() + r, g, b = int(backcolour.Red()), int(backcolour.Green()), int(backcolour.Blue()) + backcolour = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2]) + self._backgroundColour = backcolour + + tempdc = wx.ClientDC(treeCtrl) + tempdc.SetFont(font) + width, height, dummy = tempdc.GetMultiLineTextExtent(text + "M") + + image = item.GetCurrentImage() + + image_w, image_h = 0, 0 + wcheck, hcheck = 0, 0 + itemcheck = None + itemimage = None + ximagepos = 0 + yimagepos = 0 + xcheckpos = 0 + ycheckpos = 0 + + if image != _NO_IMAGE: + if treeCtrl._imageListNormal: + image_w, image_h = treeCtrl._imageListNormal.GetSize(image) + image_w += 4 + itemimage = treeCtrl._imageListNormal.GetBitmap(image) + + checkimage = item.GetCurrentCheckedImage() + + if checkimage is not None: + if treeCtrl._imageListCheck: + wcheck, hcheck = treeCtrl._imageListCheck.GetSize(checkimage) + wcheck += 4 + itemcheck = treeCtrl._imageListCheck.GetBitmap(checkimage) + + total_h = max(hcheck, height) + total_h = max(image_h, total_h) + + if image_w: + ximagepos = wcheck + yimagepos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] + + if checkimage is not None: + xcheckpos = 2 + ycheckpos = ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0] + 2 + + extraH = ((total_h > height) and [(total_h - height)/2] or [0])[0] + + xtextpos = wcheck + image_w + ytextpos = extraH + + total_h = max(image_h, hcheck) + total_h = max(total_h, height) + + if total_h < 30: + total_h += 2 # at least 2 pixels + else: + total_h += total_h/10 # otherwise 10% extra spacing + + total_w = image_w + wcheck + width + + self._total_w = total_w + self._total_h = total_h + self._itemimage = itemimage + self._itemcheck = itemcheck + self._text = text + self._colour = colour + self._font = font + self._xtextpos = xtextpos + self._ytextpos = ytextpos + self._ximagepos = ximagepos + self._yimagepos = yimagepos + self._xcheckpos = xcheckpos + self._ycheckpos = ycheckpos + self._textwidth = width + self._textheight = height + self._extraH = extraH + + self._bitmap = self.CreateBitmap() + + wx.DragImage.__init__(self, self._bitmap) + + + def CreateBitmap(self): + """Actually creates the dnd bitmap.""" + + memory = wx.MemoryDC() + + bitmap = wx.EmptyBitmap(self._total_w, self._total_h) + memory.SelectObject(bitmap) + + memory.SetTextBackground(self._backgroundColour) + memory.SetBackground(wx.Brush(self._backgroundColour)) + memory.SetFont(self._font) + memory.SetTextForeground(self._colour) + memory.Clear() + + if self._itemimage: + memory.DrawBitmap(self._itemimage, self._ximagepos, self._yimagepos, True) + + if self._itemcheck: + memory.DrawBitmap(self._itemcheck, self._xcheckpos, self._ycheckpos, True) + + textrect = wx.Rect(self._xtextpos, self._ytextpos+self._extraH, self._textwidth, self._textheight) + memory.DrawLabel(self._text, textrect) + + memory.SelectObject(wx.NullBitmap) + + return bitmap + + +# ---------------------------------------------------------------------------- +# TreeItemAttr: a structure containing the visual attributes of an item +# ---------------------------------------------------------------------------- + +class TreeItemAttr: + """Creates the item attributes (text colour, background colour and font).""" + + def __init__(self, colText=wx.NullColour, colBack=wx.NullColour, font=wx.NullFont): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + self._colText = colText + self._colBack = colBack + self._font = font + + # setters + def SetTextColour(self, colText): + """Sets the attribute text colour.""" + + self._colText = colText + + + def SetBackgroundColour(self, colBack): + """Sets the attribute background colour.""" + + self._colBack = colBack + + + def SetFont(self, font): + """Sets the attribute font.""" + + self._font = font + + + # accessors + def HasTextColour(self): + """Returns whether the attribute has text colour.""" + + return self._colText != wx.NullColour + + + def HasBackgroundColour(self): + """Returns whether the attribute has background colour.""" + + return self._colBack != wx.NullColour + + + def HasFont(self): + """Returns whether the attribute has font.""" + + return self._font != wx.NullFont + + + # getters + def GetTextColour(self): + """Returns the attribute text colour.""" + + return self._colText + + + def GetBackgroundColour(self): + """Returns the attribute background colour.""" + + return self._colBack + + + def GetFont(self): + """Returns the attribute font.""" + + return self._font + + +# ---------------------------------------------------------------------------- +# CommandTreeEvent Is A Special Subclassing Of wx.PyCommandEvent +# +# NB: Note That Not All The Accessors Make Sense For All The Events, See The +# Event Description Below. +# ---------------------------------------------------------------------------- + +class CommandTreeEvent(wx.PyCommandEvent): + """ + CommandTreeEvent is a special subclassing of wx.PyCommandEvent. + NB: note that not all the accessors make sense for all the events, see the + event description for every method in this class. + """ + + def __init__(self, type, id, item=None, evtKey=None, point=None, + label=None, **kwargs): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + wx.PyCommandEvent.__init__(self, type, id, **kwargs) + self._item = item + self._evtKey = evtKey + self._pointDrag = point + self._label = label + + + def GetItem(self): + """ + Gets the item on which the operation was performed or the newly selected + item for EVT_TREE_SEL_CHANGED/ING events. + """ + + return self._item + + + def SetItem(self, item): + """ + Sets the item on which the operation was performed or the newly selected + item for EVT_TREE_SEL_CHANGED/ING events. + """ + + self._item = item + + + def GetOldItem(self): + """For EVT_TREE_SEL_CHANGED/ING events, gets the previously selected item.""" + + return self._itemOld + + + def SetOldItem(self, item): + """For EVT_TREE_SEL_CHANGED/ING events, sets the previously selected item.""" + + self._itemOld = item + + + def GetPoint(self): + """ + Returns the point where the mouse was when the drag operation started + (for EVT_TREE_BEGIN(R)DRAG events only) or the click position. + """ + + return self._pointDrag + + + def SetPoint(self, pt): + """ + Sets the point where the mouse was when the drag operation started + (for EVT_TREE_BEGIN(R)DRAG events only) or the click position. + """ + + self._pointDrag = pt + + + def GetKeyEvent(self): + """Keyboard data (for EVT_TREE_KEY_DOWN only).""" + + return self._evtKey + + + def GetKeyCode(self): + """Returns the integer key code (for EVT_TREE_KEY_DOWN only).""" + + return self._evtKey.GetKeyCode() + + + def SetKeyEvent(self, evt): + """Keyboard data (for EVT_TREE_KEY_DOWN only).""" + + self._evtKey = evt + + + def GetLabel(self): + """Returns the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" + + return self._label + + + def SetLabel(self, label): + """Sets the label-itemtext (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" + + self._label = label + + + def IsEditCancelled(self): + """Returns the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" + + return self._editCancelled + + + def SetEditCanceled(self, editCancelled): + """Sets the edit cancel flag (for EVT_TREE_BEGIN|END_LABEL_EDIT only).""" + + self._editCancelled = editCancelled + + + def SetToolTip(self, toolTip): + """Sets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events).""" + + self._label = toolTip + + + def GetToolTip(self): + """Gets the tooltip for the item (for EVT_TREE_ITEM_GETTOOLTIP events).""" + + return self._label + + +# ---------------------------------------------------------------------------- +# TreeEvent is a special class for all events associated with tree controls +# +# NB: note that not all accessors make sense for all events, see the event +# descriptions below +# ---------------------------------------------------------------------------- + +class TreeEvent(CommandTreeEvent): + + def __init__(self, type, id, item=None, evtKey=None, point=None, + label=None, **kwargs): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + CommandTreeEvent.__init__(self, type, id, item, evtKey, point, label, **kwargs) + self.notify = wx.NotifyEvent(type, id) + + + def GetNotifyEvent(self): + """Returns the actual wx.NotifyEvent.""" + + return self.notify + + + def IsAllowed(self): + """Returns whether the event is allowed or not.""" + + return self.notify.IsAllowed() + + + def Veto(self): + """Vetos the event.""" + + self.notify.Veto() + + + def Allow(self): + """The event is allowed.""" + + self.notify.Allow() + + +# ----------------------------------------------------------------------------- +# Auxiliary Classes: TreeRenameTimer +# ----------------------------------------------------------------------------- + +class TreeRenameTimer(wx.Timer): + """Timer used for enabling in-place edit.""" + + def __init__(self, owner): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + wx.Timer.__init__(self) + self._owner = owner + + + def Notify(self): + """The timer has expired.""" + + self._owner.OnRenameTimer() + + +# ----------------------------------------------------------------------------- +# Auxiliary Classes: TreeTextCtrl +# This Is The Temporary wx.TextCtrl Created When You Edit The Text Of An Item +# ----------------------------------------------------------------------------- + +class TreeTextCtrl(wx.TextCtrl): + """Control used for in-place edit.""" + + def __init__(self, owner, item=None): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + self._owner = owner + self._itemEdited = item + self._startValue = item.GetText() + self._finished = False + self._aboutToFinish = False + + w = self._itemEdited.GetWidth() + h = self._itemEdited.GetHeight() + + wnd = self._itemEdited.GetWindow() + if wnd: + w = w - self._itemEdited.GetWindowSize()[0] + h = 0 + + x, y = self._owner.CalcScrolledPosition(item.GetX(), item.GetY()) + + image_h = 0 + image_w = 0 + + image = item.GetCurrentImage() + + if image != _NO_IMAGE: + + if self._owner._imageListNormal: + image_w, image_h = self._owner._imageListNormal.GetSize(image) + image_w += 4 + + else: + + raise "\n ERROR: You Must Create An Image List To Use Images!" + + checkimage = item.GetCurrentCheckedImage() + + if checkimage is not None: + wcheck, hcheck = self._owner._imageListCheck.GetSize(checkimage) + wcheck += 4 + else: + wcheck = 0 + + if wnd: + h = max(hcheck, image_h) + dc = wx.ClientDC(self._owner) + h = max(h, dc.GetTextExtent("Aq")[1]) + h = h + 2 + + # FIXME: what are all these hardcoded 4, 8 and 11s really? + x += image_w + wcheck + w -= image_w + 4 + wcheck + + if wx.Platform == "__WXMAC__": + bs = self.DoGetBestSize() + # edit control height + if h > bs.y - 8: + diff = h - ( bs.y - 8 ) + h -= diff + y += diff / 2 + + wx.TextCtrl.__init__(self, self._owner, wx.ID_ANY, self._startValue, + wx.Point(x - 4, y), wx.Size(w + 15, h)) + + self.Bind(wx.EVT_CHAR, self.OnChar) + self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) + self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + + + def AcceptChanges(self): + """Accepts/refuses the changes made by the user.""" + + value = self.GetValue() + + if value == self._startValue: + # nothing changed, always accept + # when an item remains unchanged, the owner + # needs to be notified that the user decided + # not to change the tree item label, and that + # the edit has been cancelled + self._owner.OnRenameCancelled(self._itemEdited) + return True + + if not self._owner.OnRenameAccept(self._itemEdited, value): + # vetoed by the user + return False + + # accepted, do rename the item + self._owner.SetItemText(self._itemEdited, value) + + return True + + + def Finish(self): + """Finish editing.""" + + if not self._finished: + +## wxPendingDelete.Append(this) + self._finished = True + self._owner.SetFocusIgnoringChildren() + self._owner.ResetTextControl() + + + def OnChar(self, event): + """Handles the wx.EVT_CHAR event for TreeTextCtrl.""" + + keycode = event.GetKeyCode() + + if keycode == wx.WXK_RETURN: + self._aboutToFinish = True + # Notify the owner about the changes + self.AcceptChanges() + # Even if vetoed, close the control (consistent with MSW) + wx.CallAfter(self.Finish) + + elif keycode == wx.WXK_ESCAPE: + self.StopEditing() + + else: + event.Skip() + + + def OnKeyUp(self, event): + """Handles the wx.EVT_KEY_UP event for TreeTextCtrl.""" + + if not self._finished: + + # auto-grow the textctrl: + parentSize = self._owner.GetSize() + myPos = self.GetPosition() + mySize = self.GetSize() + + sx, sy = self.GetTextExtent(self.GetValue() + "M") + if myPos.x + sx > parentSize.x: + sx = parentSize.x - myPos.x + if mySize.x > sx: + sx = mySize.x + + self.SetSize((sx, -1)) + + event.Skip() + + + def OnKillFocus(self, event): + """Handles the wx.EVT_KILL_FOCUS event for TreeTextCtrl.""" + + # I commented out those lines, and everything seems to work fine. + # But why in the world are these lines of code here? Maybe GTK + # or MAC give troubles? + +## if not self._finished and not self._aboutToFinish: +## +## # We must finish regardless of success, otherwise we'll get +## # focus problems: +## +## if not self.AcceptChanges(): +## self._owner.OnRenameCancelled(self._itemEdited) + + # We must let the native text control handle focus, too, otherwise + # it could have problems with the cursor (e.g., in wxGTK). + event.Skip() + + + def StopEditing(self): + """Suddenly stops the editing.""" + + self._owner.OnRenameCancelled(self._itemEdited) + self.Finish() + + + def item(self): + """Returns the item currently edited.""" + + return self._itemEdited + + +# ----------------------------------------------------------------------------- +# Auxiliary Classes: TreeFindTimer +# Timer Used To Clear CustomTreeCtrl._findPrefix If No Key Was Pressed For A +# Sufficiently Long Time. +# ----------------------------------------------------------------------------- + +class TreeFindTimer(wx.Timer): + """ + Timer used to clear CustomTreeCtrl._findPrefix if no key was pressed + for a sufficiently long time. + """ + + def __init__(self, owner): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + wx.Timer.__init__(self) + self._owner = owner + + + def Notify(self): + """The timer has expired.""" + + self._owner._findPrefix = "" + + +# ----------------------------------------------------------------------------- +# GenericTreeItem Implementation. +# This Class Holds All The Information And Methods For Every Single Item In +# CustomTreeCtrl. +# ----------------------------------------------------------------------------- + +class GenericTreeItem: + """ + This class holds all the information and methods for every single item in + CustomTreeCtrl. No wx based. + """ + + def __init__(self, parent, text="", ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """ + Default class constructor. + For internal use: do not call it in your code! + """ + + # since there can be very many of these, we save size by chosing + # the smallest representation for the elements and by ordering + # the members to avoid padding. + self._text = text # label to be rendered for item + self._data = data # user-provided data + + self._children = [] # list of children + self._parent = parent # parent of this item + + self._attr = None # attributes??? + + # tree ctrl images for the normal, selected, expanded and + # expanded+selected states + self._images = [-1, -1, -1, -1] + self._images[TreeItemIcon_Normal] = image + self._images[TreeItemIcon_Selected] = selImage + self._images[TreeItemIcon_Expanded] = _NO_IMAGE + self._images[TreeItemIcon_SelectedExpanded] = _NO_IMAGE + + self._checkedimages = [None, None, None, None] + + self._x = 0 # (virtual) offset from top + self._y = 0 # (virtual) offset from left + self._width = 0 # width of this item + self._height = 0 # height of this item + + self._isCollapsed = True + self._hasHilight = False # same as focused + self._hasPlus = False # used for item which doesn't have + # children but has a [+] button + self._isBold = False # render the label in bold font + self._isItalic = False # render the label in italic font + self._ownsAttr = False # delete attribute when done + self._type = ct_type # item type: 0=normal, 1=check, 2=radio + self._checked = False # only meaningful for check and radio + self._enabled = True # flag to enable/disable an item + self._hypertext = False # indicates if the item is hypertext + self._visited = False # visited state for an hypertext item + + if self._type > 0: + # do not construct the array for normal items + self._checkedimages[TreeItemIcon_Checked] = 0 + self._checkedimages[TreeItemIcon_NotChecked] = 1 + self._checkedimages[TreeItemIcon_Flagged] = 2 + self._checkedimages[TreeItemIcon_NotFlagged] = 3 + + if parent: + if parent.GetType() == 2 and not parent.IsChecked(): + # if the node parent is a radio not enabled, we are disabled + self._enabled = False + + self._wnd = wnd # are we holding a window? + + if wnd: + if wnd.GetSizer(): # the window is a complex one hold by a sizer + size = wnd.GetBestSize() + else: # simple window, without sizers + size = wnd.GetSize() + + # We have to bind the wx.EVT_SET_FOCUS for the associated window + # No other solution to handle the focus changing from an item in + # CustomTreeCtrl and the window associated to an item + # Do better strategies exist? + self._wnd.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + + self._height = size.GetHeight() + 2 + self._width = size.GetWidth() + self._windowsize = size + + # We don't show the window if the item is collapsed + if self._isCollapsed: + self._wnd.Show(False) + + # The window is enabled only if the item is enabled + self._wnd.Enable(self._enabled) + self._windowenabled = self._enabled + + + def IsOk(self): + """ + Returns whether the item is ok or not. Useless on Python, but added for + backward compatibility with the C++ implementation. + """ + + return True + + + def GetChildren(self): + """Returns the item's children.""" + + return self._children + + + def GetText(self): + """Returns the item text.""" + + return self._text + + + def GetImage(self, which=TreeItemIcon_Normal): + """Returns the item image for a particular state.""" + + return self._images[which] + + + def GetCheckedImage(self, which=TreeItemIcon_Checked): + """Returns the item check image. Meaningful only for radio & check items.""" + + return self._checkedimages[which] + + + def GetData(self): + """Returns the data associated to this item.""" + + return self._data + + + def SetImage(self, image, which): + """Sets the item image.""" + + self._images[which] = image + + + def SetData(self, data): + """Sets the data associated to this item.""" + + self._data = data + + + def SetHasPlus(self, has=True): + """Sets whether an item has the 'plus' button.""" + + self._hasPlus = has + + + def SetBold(self, bold): + """Sets the item font bold.""" + + self._isBold = bold + + + def SetItalic(self, italic): + """Sets the item font italic.""" + + self._isItalic = italic + + + def GetX(self): + """Returns the x position on an item in the ScrolledWindow.""" + + return self._x + + + def GetY(self): + """Returns the y position on an item in the ScrolledWindow.""" + + return self._y + + + def SetX(self, x): + """Sets the x position on an item in the ScrolledWindow.""" + + self._x = x + + + def SetY(self, y): + """Sets the y position on an item in the ScrolledWindow.""" + + self._y = y + + + def GetHeight(self): + """Returns the height of the item.""" + + return self._height + + + def GetWidth(self): + """Returns the width of the item.""" + + return self._width + + + def SetHeight(self, h): + """Sets the height of the item.""" + + self._height = h + + + def SetWidth(self, w): + """Sets the width of the item.""" + + self._width = w + + + def SetWindow(self, wnd): + """Sets the window associated to the item.""" + + self._wnd = wnd + + + def GetWindow(self): + """Returns the window associated to the item.""" + + return self._wnd + + + def GetWindowEnabled(self): + """Returns whether the associated window is enabled or not.""" + + if not self._wnd: + raise "\nERROR: This Item Has No Window Associated" + + return self._windowenabled + + + def SetWindowEnabled(self, enable=True): + """Sets whether the associated window is enabled or not.""" + + if not self._wnd: + raise "\nERROR: This Item Has No Window Associated" + + self._windowenabled = enable + self._wnd.Enable(enable) + + + def GetWindowSize(self): + """Returns the associated window size.""" + + return self._windowsize + + + def OnSetFocus(self, event): + """Handles the wx.EVT_SET_FOCUS event for the associated window.""" + + treectrl = self._wnd.GetParent() + select = treectrl.GetSelection() + + # If the window is associated to an item that currently is selected + # (has focus) we don't kill the focus. Otherwise we do it. + if select != self: + treectrl._hasFocus = False + else: + treectrl._hasFocus = True + + event.Skip() + + + def GetType(self): + """ + Returns the item type. It should be one of: + 0: normal items + 1: checkbox item + 2: radiobutton item + """ + + return self._type + + + def SetHyperText(self, hyper=True): + """Sets whether the item is hypertext or not.""" + + self._hypertext = hyper + + + def SetVisited(self, visited=True): + """Sets whether an hypertext item was visited or not.""" + + self._visited = visited + + + def GetVisited(self): + """Returns whether an hypertext item was visited or not.""" + + return self._visited + + + def IsHyperText(self): + """Returns whether the item is hypetext or not.""" + + return self._hypertext + + + def GetParent(self): + """Gets the item parent.""" + + return self._parent + + + def Insert(self, child, index): + """Inserts an item in the item children.""" + + self._children.insert(index, child) + + + def Expand(self): + """Expand the item.""" + + self._isCollapsed = False + + + def Collapse(self): + """Collapse the item.""" + + self._isCollapsed = True + + + def SetHilight(self, set=True): + """Sets the item focus/unfocus.""" + + self._hasHilight = set + + + def HasChildren(self): + """Returns whether the item has children or not.""" + + return len(self._children) > 0 + + + def IsSelected(self): + """Returns whether the item is selected or not.""" + + return self._hasHilight != 0 + + + def IsExpanded(self): + """Returns whether the item is expanded or not.""" + + return not self._isCollapsed + + + def IsChecked(self): + """Returns whether the item is checked or not.""" + + return self._checked + + + def Check(self, checked=True): + """Check an item. Meaningful only for check and radio items.""" + + self._checked = checked + + + def HasPlus(self): + """Returns whether the item has the plus button or not.""" + + return self._hasPlus or self.HasChildren() + + + def IsBold(self): + """Returns whether the item font is bold or not.""" + + return self._isBold != 0 + + + def IsItalic(self): + """Returns whether the item font is italic or not.""" + + return self._isItalic != 0 + + + def Enable(self, enable=True): + """Enables/disables the item.""" + + self._enabled = enable + + + def IsEnabled(self): + """Returns whether the item is enabled or not.""" + + return self._enabled + + + def GetAttributes(self): + """Returns the item attributes (font, colours).""" + + return self._attr + + + def Attr(self): + """Creates a new attribute (font, colours).""" + + if not self._attr: + + self._attr = TreeItemAttr() + self._ownsAttr = True + + return self._attr + + + def SetAttributes(self, attr): + """Sets the item attributes (font, colours).""" + + if self._ownsAttr: + del self._attr + + self._attr = attr + self._ownsAttr = False + + + def AssignAttributes(self, attr): + """Assigns the item attributes (font, colours).""" + + self.SetAttributes(attr) + self._ownsAttr = True + + + def DeleteChildren(self, tree): + """Deletes the item children.""" + + for child in self._children: + if tree: + tree.SendDeleteEvent(child) + + child.DeleteChildren(tree) + + if child == tree._select_me: + tree._select_me = None + + # We have to destroy the associated window + wnd = child.GetWindow() + if wnd: + wnd.Destroy() + child._wnd = None + + if child in tree._itemWithWindow: + tree._itemWithWindow.remove(child) + + del child + + self._children = [] + + + def SetText(self, text): + """Sets the item text.""" + + self._text = text + + + def GetChildrenCount(self, recursively=True): + """Gets the number of children.""" + + count = len(self._children) + + if not recursively: + return count + + total = count + + for n in xrange(count): + total += self._children[n].GetChildrenCount() + + return total + + + def GetSize(self, x, y, theButton): + """Returns the item size.""" + + bottomY = self._y + theButton.GetLineHeight(self) + + if y < bottomY: + y = bottomY + + width = self._x + self._width + + if x < width: + x = width + + if self.IsExpanded(): + for child in self._children: + x, y = child.GetSize(x, y, theButton) + + return x, y + + + def HitTest(self, point, theCtrl, flags=0, level=0): + """ + HitTest method for an item. Called from the main window HitTest. + see the CustomTreeCtrl HitTest method for the flags explanation. + """ + + # for a hidden root node, don't evaluate it, but do evaluate children + if not (level == 0 and theCtrl.HasFlag(TR_HIDE_ROOT)): + + # evaluate the item + h = theCtrl.GetLineHeight(self) + + if point.y > self._y and point.y < self._y + h: + + y_mid = self._y + h/2 + + if point.y < y_mid: + flags |= TREE_HITTEST_ONITEMUPPERPART + else: + flags |= TREE_HITTEST_ONITEMLOWERPART + + xCross = self._x - theCtrl.GetSpacing() + + if wx.Platform == "__WXMAC__": + # according to the drawing code the triangels are drawn + # at -4 , -4 from the position up to +10/+10 max + if point.x > xCross-4 and point.x < xCross+10 and point.y > y_mid-4 and \ + point.y < y_mid+10 and self.HasPlus() and theCtrl.HasButtons(): + + flags |= TREE_HITTEST_ONITEMBUTTON + return self, flags + else: + # 5 is the size of the plus sign + if point.x > xCross-6 and point.x < xCross+6 and point.y > y_mid-6 and \ + point.y < y_mid+6 and self.HasPlus() and theCtrl.HasButtons(): + + flags |= TREE_HITTEST_ONITEMBUTTON + return self, flags + + if point.x >= self._x and point.x <= self._x + self._width: + + image_w = -1 + wcheck = 0 + + # assuming every image (normal and selected) has the same size! + if self.GetImage() != _NO_IMAGE and theCtrl._imageListNormal: + image_w, image_h = theCtrl._imageListNormal.GetSize(self.GetImage()) + + if self.GetCheckedImage() is not None: + wcheck, hcheck = theCtrl._imageListCheck.GetSize(self.GetCheckedImage()) + + if wcheck and point.x <= self._x + wcheck + 1: + flags |= TREE_HITTEST_ONITEMCHECKICON + return self, flags + + if image_w != -1 and point.x <= self._x + wcheck + image_w + 1: + flags |= TREE_HITTEST_ONITEMICON + else: + flags |= TREE_HITTEST_ONITEMLABEL + + return self, flags + + if point.x < self._x: + flags |= TREE_HITTEST_ONITEMINDENT + if point.x > self._x + self._width: + flags |= TREE_HITTEST_ONITEMRIGHT + + return self, flags + + # if children are expanded, fall through to evaluate them + if self._isCollapsed: + return None, 0 + + # evaluate children + for child in self._children: + res, flags = child.HitTest(point, theCtrl, flags, level + 1) + if res != None: + return res, flags + + return None, 0 + + + def GetCurrentImage(self): + """Returns the current item image.""" + + image = _NO_IMAGE + + if self.IsExpanded(): + + if self.IsSelected(): + + image = self.GetImage(TreeItemIcon_SelectedExpanded) + + if image == _NO_IMAGE: + + # we usually fall back to the normal item, but try just the + # expanded one (and not selected) first in this case + image = self.GetImage(TreeItemIcon_Expanded) + + else: # not expanded + + if self.IsSelected(): + image = self.GetImage(TreeItemIcon_Selected) + + # maybe it doesn't have the specific image we want, + # try the default one instead + if image == _NO_IMAGE: + image = self.GetImage() + + return image + + + def GetCurrentCheckedImage(self): + """Returns the current item check image.""" + + if self._type == 0: + return None + + if self.IsChecked(): + if self._type == 1: # Checkbox + return self._checkedimages[TreeItemIcon_Checked] + else: # Radiobutton + return self._checkedimages[TreeItemIcon_Flagged] + else: + if self._type == 1: # Checkbox + return self._checkedimages[TreeItemIcon_NotChecked] + else: # Radiobutton + return self._checkedimages[TreeItemIcon_NotFlagged] + + +def EventFlagsToSelType(style, shiftDown=False, ctrlDown=False): + """ + Translate the key or mouse event flag to the type of selection we + are dealing with. + """ + + is_multiple = (style & TR_MULTIPLE) != 0 + extended_select = shiftDown and is_multiple + unselect_others = not (extended_select or (ctrlDown and is_multiple)) + + return is_multiple, extended_select, unselect_others + + +# ----------------------------------------------------------------------------- +# CustomTreeCtrl Main Implementation. +# This Is The Main Class. +# ----------------------------------------------------------------------------- + +class CustomTreeCtrl(wx.ScrolledWindow): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, ctstyle=TR_DEFAULT_STYLE, validator=wx.DefaultValidator, + name="CustomTreeCtrl"): + """ + Default class constructor. + + parent: parent window. Must not be none. + + id: window identifier. A value of -1 indicates a default value. + + pos: window position. + + size: window size. If the default size (-1, -1) is specified then the window is sized appropriately. + + style: the underlying wx.ScrolledWindow style + + ctstyle: CustomTreeCtrl window style. This can be one of: + TR_NO_BUTTONS + TR_HAS_BUTTONS # draw collapsed/expanded btns + TR_NO_LINES # don't draw lines at all + TR_LINES_AT_ROOT # connect top-level nodes + TR_TWIST_BUTTONS # draw mac-like twist buttons + TR_SINGLE # single selection mode + TR_MULTIPLE # can select multiple items + TR_EXTENDED # todo: allow extended selection + TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height + TR_EDIT_LABELS # can edit item labels + TR_ROW_LINES # put border around items + TR_HIDE_ROOT # don't display root node + TR_FULL_ROW_HIGHLIGHT # highlight full horizontal space + TR_AUTO_CHECK_CHILD # only meaningful for checkboxes + TR_AUTO_TOGGLE_CHILD # only meaningful for checkboxes + + validator: window validator. + + name: window name. + """ + + self._current = self._key_current = self._anchor = self._select_me = None + self._hasFocus = False + self._dirty = False + + # Default line height: it will soon be changed + self._lineHeight = 10 + # Item indent wrt parent + self._indent = 15 + # item horizontal spacing between the start and the text + self._spacing = 18 + + # Brushes for focused/unfocused items (also gradient type) + self._hilightBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)) + btnshadow = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW) + self._hilightUnfocusedBrush = wx.Brush(btnshadow) + r, g, b = btnshadow.Red(), btnshadow.Green(), btnshadow.Blue() + backcolour = ((r >> 1) - 20, (g >> 1) - 20, (b >> 1) - 20) + backcolour = wx.Colour(backcolour[0], backcolour[1], backcolour[2]) + self._hilightUnfocusedBrush2 = wx.Brush(backcolour) + + # image list for icons + self._imageListNormal = self._imageListButtons = self._imageListState = self._imageListCheck = None + self._ownsImageListNormal = self._ownsImageListButtons = self._ownsImageListState = False + + # Drag and drop initial settings + self._dragCount = 0 + self._countDrag = 0 + self._isDragging = False + self._dropTarget = self._oldSelection = None + self._dragImage = None + self._underMouse = None + + # TextCtrl initial settings for editable items + self._textCtrl = None + self._renameTimer = None + + # This one allows us to handle Freeze() and Thaw() calls + self._freezeCount = 0 + + self._findPrefix = "" + self._findTimer = None + + self._dropEffectAboveItem = False + self._lastOnSame = False + + # Default normal and bold fonts for an item + self._hasFont = True + self._normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), + self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(), + self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) + + + # Hyperlinks things + self._hypertextfont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), + self._normalFont.GetStyle(), wx.NORMAL, True, + self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) + self._hypertextnewcolour = wx.BLUE + self._hypertextvisitedcolour = wx.Colour(200, 47, 200) + self._isonhyperlink = False + + # Default CustomTreeCtrl background colour. + self._backgroundColour = wx.WHITE + + # Background image settings + self._backgroundImage = None + self._imageStretchStyle = _StyleTile + + # Disabled items colour + self._disabledColour = wx.Colour(180, 180, 180) + + # Gradient selection colours + self._firstcolour = color= wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + self._secondcolour = wx.WHITE + self._usegradients = False + self._gradientstyle = 0 # Horizontal Gradient + + # Vista Selection Styles + self._vistaselection = False + + # Connection lines style + if wx.Platform != "__WXMAC__": + self._dottedPen = wx.Pen("grey", 1, wx.USER_DASH) + self._dottedPen.SetDashes([1,1]) + self._dottedPen.SetCap(wx.CAP_BUTT) + else: + self._dottedPen = wx.Pen("grey", 1) + + # Pen Used To Draw The Border Around Selected Items + self._borderPen = wx.BLACK_PEN + self._cursor = wx.StockCursor(wx.CURSOR_ARROW) + + # For Appended Windows + self._hasWindows = False + self._itemWithWindow = [] + + if wx.Platform == "__WXMAC__": + + platform, major, minor = wx.GetOsVersion() + + ctstyle &= ~TR_LINES_AT_ROOT + ctstyle |= TR_NO_LINES + + if major < 10: + ctstyle |= TR_ROW_LINES + + self._windowStyle = ctstyle + + # Create the default check image list + self.SetImageListCheck(13, 13) + + # A constant to use my translation of RendererNative.DrawTreeItemButton + # if the wxPython version is less than 2.6.2.1. + if wx.VERSION_STRING < "2.6.2.1": + self._drawingfunction = DrawTreeItemButton + else: + self._drawingfunction = wx.RendererNative.Get().DrawTreeItemButton + + # Create our container... at last! + wx.ScrolledWindow.__init__(self, parent, id, pos, size, style|wx.HSCROLL|wx.VSCROLL, name) + + # If the tree display has no buttons, but does have + # connecting lines, we can use a narrower layout. + # It may not be a good idea to force this... + if not self.HasButtons() and not self.HasFlag(TR_NO_LINES): + self._indent= 10 + self._spacing = 10 + + self.SetValidator(validator) + + attr = self.GetDefaultAttributes() + self.SetOwnForegroundColour(attr.colFg) + self.SetOwnBackgroundColour(wx.WHITE) + + if not self._hasFont: + self.SetOwnFont(attr.font) + + self.SetSize(size) + + # Bind the events + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + self.Bind(EVT_TREE_ITEM_GETTOOLTIP, self.OnGetToolTip) + self.Bind(wx.EVT_IDLE, self.OnInternalIdle) + self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy) + + # Sets the focus to ourselves: this is useful if you have items + # with associated widgets. + self.SetFocus() + + return True + + + def OnDestroy(self, event): + """Handles the wx.EVT_WINDOW_DESTROY event.""" + + # Here there may be something I miss... do I have to destroy + # something else? + if self._renameTimer and self._renameTimer.IsRunning(): + self._renameTimer.Stop() + del self._renameTimer + + if self._findTimer and self._findTimer.IsRunning(): + self._findTimer.Stop() + del self._findTimer + + event.Skip() + + + def GetCount(self): + """Returns the global number of items in the tree.""" + + if not self._anchor: + # the tree is empty + return 0 + + count = self._anchor.GetChildrenCount() + + if not self.HasFlag(TR_HIDE_ROOT): + # take the root itself into account + count = count + 1 + + return count + + + def GetIndent(self): + """Returns the item indentation.""" + + return self._indent + + + def GetSpacing(self): + """Returns the spacing between the start and the text.""" + + return self._spacing + + + def GetRootItem(self): + """Returns the root item.""" + + return self._anchor + + + def GetSelection(self): + """Returns the current selection: TR_SINGLE only.""" + + return self._current + + + def ToggleItemSelection(self, item): + """Toggles the item selection.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + self.SelectItem(item, not self.IsSelected(item)) + + + def EnableChildren(self, item, enable=True): + """Enables/disables item children. Used internally.""" + + torefresh = False + if item.IsExpanded(): + torefresh = True + + if item.GetType() == 2 and enable and not item.IsChecked(): + # We hit a radiobutton item not checked, we don't want to + # enable the children + return + + child, cookie = self.GetFirstChild(item) + while child: + self.EnableItem(child, enable, torefresh=torefresh) + # Recurse on tree + if child.GetType != 2 or (child.GetType() == 2 and item.IsChecked()): + self.EnableChildren(child, enable) + (child, cookie) = self.GetNextChild(item, cookie) + + + def EnableItem(self, item, enable=True, torefresh=True): + """Enables/disables an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if item.IsEnabled() == enable: + return + + if not enable and item.IsSelected(): + self.SelectItem(item, False) + + item.Enable(enable) + wnd = item.GetWindow() + + # Handles the eventual window associated to the item + if wnd: + wndenable = item.GetWindowEnabled() + if enable: + if wndenable: + wnd.Enable(enable) + else: + wnd.Enable(enable) + + if torefresh: + # We have to refresh the item line + dc = wx.ClientDC(self) + self.CalculateSize(item, dc) + self.RefreshLine(item) + + + def IsEnabled(self, item): + """Returns whether an item is enabled or disabled.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsEnabled() + + + def SetDisabledColour(self, colour): + """Sets the items disabled colour.""" + + self._disabledColour = colour + self._dirty = True + + + def GetDisabledColour(self): + """Returns the items disabled colour.""" + + return self._disabledColour + + + def IsItemChecked(self, item): + """Returns whether an item is checked or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsChecked() + + + def CheckItem2(self, item, checked=True, torefresh=False): + """Used internally to avoid EVT_TREE_ITEM_CHECKED events.""" + + if item.GetType() == 0: + return + + item.Check(checked) + + if torefresh: + dc = wx.ClientDC(self) + self.CalculateSize(item, dc) + self.RefreshLine(item) + + + def UnCheckRadioParent(self, item, checked=False): + """Used internally to handle radio node parent correctly.""" + + e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId()) + e.SetItem(item) + e.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(e): + return False + + item.Check(checked) + dc = wx.ClientDC(self) + self.RefreshLine(item) + self.EnableChildren(item, checked) + e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId()) + e.SetItem(item) + e.SetEventObject(self) + self.GetEventHandler().ProcessEvent(e) + + return True + + + def CheckItem(self, item, checked=True): + """ + Actually checks/uncheks an item, sending (eventually) the two + events EVT_TREE_ITEM_CHECKING/EVT_TREE_ITEM_CHECKED. + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + # Should we raise an error here?!? + if item.GetType() == 0: + return + + if item.GetType() == 2: # it's a radio button + if not checked and item.IsChecked(): # Try To Unckeck? + if item.HasChildren(): + self.UnCheckRadioParent(item, checked) + return + else: + if not self.UnCheckRadioParent(item, checked): + return + + self.CheckSameLevel(item, False) + return + + # Radiobuttons are done, let's handle checkbuttons... + e = TreeEvent(wxEVT_TREE_ITEM_CHECKING, self.GetId()) + e.SetItem(item) + e.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(e): + # Blocked by user + return + + item.Check(checked) + dc = wx.ClientDC(self) + self.RefreshLine(item) + + if self._windowStyle & TR_AUTO_CHECK_CHILD: + ischeck = self.IsItemChecked(item) + self.AutoCheckChild(item, ischeck) + elif self._windowStyle & TR_AUTO_TOGGLE_CHILD: + self.AutoToggleChild(item) + + e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId()) + e.SetItem(item) + e.SetEventObject(self) + self.GetEventHandler().ProcessEvent(e) + + + def AutoToggleChild(self, item): + """Transverses the tree and toggles the items. Meaningful only for check items.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + child, cookie = self.GetFirstChild(item) + + torefresh = False + if item.IsExpanded(): + torefresh = True + + # Recurse on tree + while child: + if child.GetType() == 1 and child.IsEnabled(): + self.CheckItem2(child, not child.IsChecked(), torefresh=torefresh) + self.AutoToggleChild(child) + (child, cookie) = self.GetNextChild(item, cookie) + + + def AutoCheckChild(self, item, checked): + """Transverses the tree and checks/unchecks the items. Meaningful only for check items.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + (child, cookie) = self.GetFirstChild(item) + + torefresh = False + if item.IsExpanded(): + torefresh = True + + while child: + if child.GetType() == 1 and child.IsEnabled(): + self.CheckItem2(child, checked, torefresh=torefresh) + self.AutoCheckChild(child, checked) + (child, cookie) = self.GetNextChild(item, cookie) + + + def CheckChilds(self, item, checked=True): + """Programatically check/uncheck item children. Does not generate EVT_TREE_CHECK* events.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if checked == None: + self.AutoToggleChild(item) + else: + self.AutoCheckChild(item, checked) + + + def CheckSameLevel(self, item, checked=False): + """ + Uncheck radio items which are on the same level of the checked one. + Used internally. + """ + + parent = item.GetParent() + + if not parent: + return + + torefresh = False + if parent.IsExpanded(): + torefresh = True + + (child, cookie) = self.GetFirstChild(parent) + while child: + if child.GetType() == 2 and child != item: + self.CheckItem2(child, checked, torefresh=torefresh) + if child.GetType != 2 or (child.GetType() == 2 and child.IsChecked()): + self.EnableChildren(child, checked) + (child, cookie) = self.GetNextChild(parent, cookie) + + + def EditLabel(self, item): + """Starts editing an item label.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + self.Edit(item) + + + def ShouldInheritColours(self): + """We don't inherit colours from anyone.""" + + return False + + + def SetIndent(self, indent): + """Sets item indentation.""" + + self._indent = indent + self._dirty = True + + + def SetSpacing(self, spacing): + """Sets item spacing.""" + + self._spacing = spacing + self._dirty = True + + + def HasFlag(self, flag): + """Returns whether CustomTreeCtrl has a flag.""" + + return self._windowStyle & flag + + + def HasChildren(self, item): + """Returns whether an item has children or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return len(item.GetChildren()) > 0 + + + def GetChildrenCount(self, item, recursively=True): + """Gets the item children count.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetChildrenCount(recursively) + + + def SetTreeStyle(self, styles): + """Sets the CustomTreeCtrl style. See __init__ method for the styles explanation.""" + + # Do not try to expand the root node if it hasn't been created yet + if self._anchor and not self.HasFlag(TR_HIDE_ROOT) and styles & TR_HIDE_ROOT: + + # if we will hide the root, make sure children are visible + self._anchor.SetHasPlus() + self._anchor.Expand() + self.CalculatePositions() + + # right now, just sets the styles. Eventually, we may + # want to update the inherited styles, but right now + # none of the parents has updatable styles + + if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE): + selections = self.GetSelections() + for select in selections[0:-1]: + self.SelectItem(select, False) + + self._windowStyle = styles + self._dirty = True + + + def GetTreeStyle(self): + """Returns the CustomTreeCtrl style.""" + + return self._windowStyle + + + def HasButtons(self): + """Returns whether CustomTreeCtrl has the TR_AHS_BUTTONS flag.""" + + return self.HasFlag(TR_HAS_BUTTONS) + + +# ----------------------------------------------------------------------------- +# functions to work with tree items +# ----------------------------------------------------------------------------- + + def GetItemText(self, item): + """Returns the item text.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetText() + + + def GetItemImage(self, item, which): + """Returns the item image.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetImage(which) + + + def GetPyData(self, item): + """Returns the data associated to an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetData() + + GetItemPyData = GetPyData + + + def GetItemTextColour(self, item): + """Returns the item text colour.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.Attr().GetTextColour() + + + def GetItemBackgroundColour(self, item): + """Returns the item background colour.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.Attr().GetBackgroundColour() + + + def GetItemFont(self, item): + """Returns the item font.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.Attr().GetFont() + + + def IsItemHyperText(self, item): + """Returns whether an item is hypertext or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsHyperText() + + + def SetItemText(self, item, text): + """Sets the item text.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + dc = wx.ClientDC(self) + item.SetText(text) + self.CalculateSize(item, dc) + self.RefreshLine(item) + + + def SetItemImage(self, item, image, which=TreeItemIcon_Normal): + """Sets the item image, depending on the item state.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.SetImage(image, which) + + dc = wx.ClientDC(self) + self.CalculateSize(item, dc) + self.RefreshLine(item) + + + def SetPyData(self, item, data): + """Sets the data associated to an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.SetData(data) + + SetItemPyData = SetPyData + + + def SetItemHasChildren(self, item, has=True): + """Forces the appearance of the button next to the item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.SetHasPlus(has) + self.RefreshLine(item) + + + def SetItemBold(self, item, bold=True): + """Sets the item font bold/unbold.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + # avoid redrawing the tree if no real change + if item.IsBold() != bold: + item.SetBold(bold) + self._dirty = True + + + def SetItemItalic(self, item, italic=True): + """Sets the item font italic/non-italic.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if item.IsItalic() != italic: + itemFont = self.GetItemFont(item) + if itemFont != wx.NullFont: + style = wx.ITALIC + if not italic: + style = ~style + + item.SetItalic(italic) + itemFont.SetStyle(style) + self.SetItemFont(item, itemFont) + self._dirty = True + + + def SetItemDropHighlight(self, item, highlight=True): + """ + Gives the item the visual feedback for drag and drop operations. + This is useful when something is dragged from outside the CustomTreeCtrl. + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if highlight: + bg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + fg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + + item.Attr().SetTextColour(fg) + item.Attr.SetBackgroundColour(bg) + self.RefreshLine(item) + + + def SetItemTextColour(self, item, col): + """Sets the item text colour.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if self.GetItemTextColour(item) == col: + return + + item.Attr().SetTextColour(col) + self.RefreshLine(item) + + + def SetItemBackgroundColour(self, item, col): + """Sets the item background colour.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.Attr().SetBackgroundColour(col) + self.RefreshLine(item) + + + def SetItemHyperText(self, item, hyper=True): + """Sets whether the item is hypertext or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.SetHyperText(hyper) + self.RefreshLine(item) + + + def SetItemFont(self, item, font): + """Sets the item font.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if self.GetItemFont(item) == font: + return + + item.Attr().SetFont(font) + self._dirty = True + + + def SetFont(self, font): + """Sets the CustomTreeCtrl font.""" + + wx.ScrolledWindow.SetFont(self, font) + + self._normalFont = font + self._boldFont = wx.Font(self._normalFont.GetPointSize(), self._normalFont.GetFamily(), + self._normalFont.GetStyle(), wx.BOLD, self._normalFont.GetUnderlined(), + self._normalFont.GetFaceName(), self._normalFont.GetEncoding()) + + return True + + + def GetHyperTextFont(self): + """Returns the font used to render an hypertext item.""" + + return self._hypertextfont + + + def SetHyperTextFont(self, font): + """Sets the font used to render an hypertext item.""" + + self._hypertextfont = font + self._dirty = True + + + def SetHyperTextNewColour(self, colour): + """Sets the colour used to render a non-visited hypertext item.""" + + self._hypertextnewcolour = colour + self._dirty = True + + + def GetHyperTextNewColour(self): + """Returns the colour used to render a non-visited hypertext item.""" + + return self._hypertextnewcolour + + + def SetHyperTextVisitedColour(self, colour): + """Sets the colour used to render a visited hypertext item.""" + + self._hypertextvisitedcolour = colour + self._dirty = True + + + def GetHyperTextVisitedColour(self): + """Returns the colour used to render a visited hypertext item.""" + + return self._hypertextvisitedcolour + + + def SetItemVisited(self, item, visited=True): + """Sets whether an hypertext item was visited.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + item.SetVisited(visited) + self.RefreshLine(item) + + + def GetItemVisited(self, item): + """Returns whether an hypertext item was visited.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetVisited() + + + def SetHilightFocusColour(self, colour): + """ + Sets the colour used to highlight focused selected items. + This is applied only if gradient and Windows Vista styles are disabled. + """ + + self._hilightBrush = wx.Brush(colour) + self.RefreshSelected() + + + def SetHilightNonFocusColour(self, colour): + """ + Sets the colour used to highlight unfocused selected items. + This is applied only if gradient and Windows Vista styles are disabled. + """ + + self._hilightUnfocusedBrush = wx.Brush(colour) + self.RefreshSelected() + + + def GetHilightFocusColour(self): + """ + Returns the colour used to highlight focused selected items. + This is applied only if gradient and Windows Vista styles are disabled. + """ + + return self._hilightBrush.GetColour() + + + def GetHilightNonFocusColour(self): + """ + Returns the colour used to highlight unfocused selected items. + This is applied only if gradient and Windows Vista styles are disabled. + """ + + return self._hilightUnfocusedBrush.GetColour() + + + def SetFirstGradientColour(self, colour=None): + """Sets the first gradient colour.""" + + if colour is None: + colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT) + + self._firstcolour = colour + if self._usegradients: + self.RefreshSelected() + + + def SetSecondGradientColour(self, colour=None): + """Sets the second gradient colour.""" + + if colour is None: + # No colour given, generate a slightly darker from the + # CustomTreeCtrl background colour + color = self.GetBackgroundColour() + r, g, b = int(color.Red()), int(color.Green()), int(color.Blue()) + color = ((r >> 1) + 20, (g >> 1) + 20, (b >> 1) + 20) + colour = wx.Colour(color[0], color[1], color[2]) + + self._secondcolour = colour + + if self._usegradients: + self.RefreshSelected() + + + def GetFirstGradientColour(self): + """Returns the first gradient colour.""" + + return self._firstcolour + + + def GetSecondGradientColour(self): + """Returns the second gradient colour.""" + + return self._secondcolour + + + def EnableSelectionGradient(self, enable=True): + """Globally enables/disables drawing of gradient selection.""" + + self._usegradients = enable + self._vistaselection = False + self.RefreshSelected() + + + def SetGradientStyle(self, vertical=0): + """ + Sets the gradient style: + 0: horizontal gradient + 1: vertical gradient + """ + + # 0 = Horizontal, 1 = Vertical + self._gradientstyle = vertical + + if self._usegradients: + self.RefreshSelected() + + + def GetGradientStyle(self): + """ + Returns the gradient style: + 0: horizontal gradient + 1: vertical gradient + """ + + return self._gradientstyle + + + def EnableSelectionVista(self, enable=True): + """Globally enables/disables drawing of Windows Vista selection.""" + + self._usegradients = False + self._vistaselection = enable + self.RefreshSelected() + + + def SetBorderPen(self, pen): + """ + Sets the pen used to draw the selected item border. + The border pen is not used if the Windows Vista style is applied. + """ + + self._borderPen = pen + self.RefreshSelected() + + + def GetBorderPen(self): + """ + Returns the pen used to draw the selected item border. + The border pen is not used if the Windows Vista style is applied. + """ + + return self._borderPen + + + def SetConnectionPen(self, pen): + """Sets the pen used to draw the connecting lines between items.""" + + self._dottedPen = pen + self._dirty = True + + + def GetConnectionPen(self): + """Returns the pen used to draw the connecting lines between items.""" + + return self._dottedPen + + + def SetBackgroundImage(self, image): + """Sets the CustomTreeCtrl background image (can be none).""" + + self._backgroundImage = image + self.Refresh() + + + def GetBackgroundImage(self): + """Returns the CustomTreeCtrl background image (can be none).""" + + return self._backgroundImage + + + def GetItemWindow(self, item): + """Returns the window associated to the item (if any).""" + + if not item: + raise "\nERROR: Invalid Item" + + return item.GetWindow() + + + def GetItemWindowEnabled(self, item): + """Returns whether the window associated to the item is enabled.""" + + if not item: + raise "\nERROR: Invalid Item" + + return item.GetWindowEnabled() + + + def SetItemWindowEnabled(self, item, enable=True): + """Enables/disables the window associated to the item.""" + + if not item: + raise "\nERROR: Invalid Item" + + item.SetWindowEnabled(enable) + + + def GetItemType(self, item): + """ + Returns the item type: + 0: normal + 1: checkbox item + 2: radiobutton item + """ + + if not item: + raise "\nERROR: Invalid Item" + + return item.GetType() + +# ----------------------------------------------------------------------------- +# item status inquiries +# ----------------------------------------------------------------------------- + + def IsVisible(self, item): + """Returns whether the item is visible or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + # An item is only visible if it's not a descendant of a collapsed item + parent = item.GetParent() + + while parent: + + if not parent.IsExpanded(): + return False + + parent = parent.GetParent() + + startX, startY = self.GetViewStart() + clientSize = self.GetClientSize() + + rect = self.GetBoundingRect(item) + + if not rect: + return False + if rect.GetWidth() == 0 or rect.GetHeight() == 0: + return False + if rect.GetBottom() < 0 or rect.GetTop() > clientSize.y: + return False + if rect.GetRight() < 0 or rect.GetLeft() > clientSize.x: + return False + + return True + + + def ItemHasChildren(self, item): + """Returns whether the item has children or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + # consider that the item does have children if it has the "+" button: it + # might not have them (if it had never been expanded yet) but then it + # could have them as well and it's better to err on this side rather than + # disabling some operations which are restricted to the items with + # children for an item which does have them + return item.HasPlus() + + + def IsExpanded(self, item): + """Returns whether the item is expanded or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsExpanded() + + + def IsSelected(self, item): + """Returns whether the item is selected or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsSelected() + + + def IsBold(self, item): + """Returns whether the item font is bold or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsBold() + + + def IsItalic(self, item): + """Returns whether the item font is italic or not.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.IsItalic() + + +# ----------------------------------------------------------------------------- +# navigation +# ----------------------------------------------------------------------------- + + def GetItemParent(self, item): + """Gets the item parent.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + return item.GetParent() + + + def GetFirstChild(self, item): + """Gets the item first child.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + cookie = 0 + return self.GetNextChild(item, cookie) + + + def GetNextChild(self, item, cookie): + """ + Gets the item next child based on the 'cookie' parameter. + This method has no sense if you do not call GetFirstChild() before. + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + children = item.GetChildren() + + # it's ok to cast cookie to size_t, we never have indices big enough to + # overflow "void *" + + if cookie < len(children): + + return children[cookie], cookie+1 + + else: + + # there are no more of them + return None, cookie + + + def GetLastChild(self, item): + """Gets the item last child.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + children = item.GetChildren() + return (len(children) == 0 and [None] or [children[-1]])[0] + + + def GetNextSibling(self, item): + """Gets the next sibling of an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + i = item + parent = i.GetParent() + + if parent == None: + + # root item doesn't have any siblings + return None + + siblings = parent.GetChildren() + index = siblings.index(i) + + n = index + 1 + return (n == len(siblings) and [None] or [siblings[n]])[0] + + + def GetPrevSibling(self, item): + """Gets the previous sibling of an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + i = item + parent = i.GetParent() + + if parent == None: + + # root item doesn't have any siblings + return None + + siblings = parent.GetChildren() + index = siblings.index(i) + + return (index == 0 and [None] or [siblings[index-1]])[0] + + + def GetNext(self, item): + """Gets the next item. Only for internal use right now.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + i = item + + # First see if there are any children. + children = i.GetChildren() + if len(children) > 0: + return children[0] + else: + # Try a sibling of this or ancestor instead + p = item + toFind = None + while p and not toFind: + toFind = self.GetNextSibling(p) + p = self.GetItemParent(p) + + return toFind + + + def GetFirstVisibleItem(self): + """Returns the first visible item.""" + + id = self.GetRootItem() + if not id: + return id + + while id: + if self.IsVisible(id): + return id + id = self.GetNext(id) + + return None + + + def GetNextVisible(self, item): + """Returns the next visible item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + id = item + + while id: + id = self.GetNext(id) + if id and self.IsVisible(id): + return id + + return None + + + def GetPrevVisible(self, item): + + if not item: + raise "\nERROR: Invalid Tree Item. " + + raise "\nERROR: Not Implemented" + + return None + + + def ResetTextControl(self): + """Called by TreeTextCtrl when it marks itself for deletion.""" + + self._textCtrl.Destroy() + self._textCtrl = None + + + def FindItem(self, idParent, prefixOrig): + """Finds the first item starting with the given prefix after the given item.""" + + # match is case insensitive as this is more convenient to the user: having + # to press Shift-letter to go to the item starting with a capital letter + # would be too bothersome + prefix = prefixOrig.lower() + + # determine the starting point: we shouldn't take the current item (this + # allows to switch between two items starting with the same letter just by + # pressing it) but we shouldn't jump to the next one if the user is + # continuing to type as otherwise he might easily skip the item he wanted + id = idParent + + if len(prefix) == 1: + id = self.GetNext(id) + + # look for the item starting with the given prefix after it + while id and not self.GetItemText(id).lower().startswith(prefix): + + id = self.GetNext(id) + + # if we haven't found anything... + if not id: + + # ... wrap to the beginning + id = self.GetRootItem() + if self.HasFlag(TR_HIDE_ROOT): + # can't select virtual root + id = self.GetNext(id) + + # and try all the items (stop when we get to the one we started from) + while id != idParent and not self.GetItemText(id).lower().startswith(prefix): + id = self.GetNext(id) + + return id + + +# ----------------------------------------------------------------------------- +# operations +# ----------------------------------------------------------------------------- + + def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Actually inserts an item in the tree.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if ct_type < 0 or ct_type > 2: + raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). " + + parent = parentId + + if not parent: + + # should we give a warning here? + return self.AddRoot(text, ct_type, wnd, image, selImage, data) + + self._dirty = True # do this first so stuff below doesn't cause flicker + + item = GenericTreeItem(parent, text, ct_type, wnd, image, selImage, data) + + if wnd is not None: + self._hasWindows = True + self._itemWithWindow.append(item) + + parent.Insert(item, previous) + + return item + + + def AddRoot(self, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Adds a root to the CustomTreeCtrl. Only one root must exist.""" + + if self._anchor: + raise "\nERROR: Tree Can Have Only One Root" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if ct_type < 0 or ct_type > 2: + raise "\nERROR: Item Type Should Be 0 (Normal), 1 (CheckBox) or 2 (RadioButton). " + + self._dirty = True # do this first so stuff below doesn't cause flicker + + self._anchor = GenericTreeItem(None, text, ct_type, wnd, image, selImage, data) + + if wnd is not None: + self._hasWindows = True + self._itemWithWindow.append(self._anchor) + + if self.HasFlag(TR_HIDE_ROOT): + + # if root is hidden, make sure we can navigate + # into children + self._anchor.SetHasPlus() + self._anchor.Expand() + self.CalculatePositions() + + if not self.HasFlag(TR_MULTIPLE): + + self._current = self._key_current = self._anchor + self._current.SetHilight(True) + + return self._anchor + + + def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Appends an item as a first child of parent.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data) + + + def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Auxiliary function to cope with the C++ hideous multifunction.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + parent = parentId + + if not parent: + # should we give a warning here? + return self.AddRoot(text, ct_type, wnd, image, selImage, data) + + index = -1 + if idPrevious: + + try: + index = parent.GetChildren().index(idPrevious) + except: + raise "ERROR: Previous Item In CustomTreeCtrl.InsertItem() Is Not A Sibling" + + return self.DoInsertItem(parentId, index+1, text, ct_type, wnd, image, selImage, data) + + + def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Auxiliary function to cope with the C++ hideous multifunction.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + parent = parentId + + if not parent: + # should we give a warning here? + return self.AddRoot(text, ct_type, wnd, image, selImage, data) + + return self.DoInsertItem(parentId, before, text, ct_type, wnd, image, selImage, data) + + + def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Inserts an item after the given previous.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if type(input) == type(1): + return self.InsertItemByIndex(parentId, input, text, ct_type, wnd, image, selImage, data) + else: + return self.InsertItemByItem(parentId, input, text, ct_type, wnd, image, selImage, data) + + + def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None): + """Appends an item as a last child of its parent.""" + + if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT): + raise "\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT" + + parent = parentId + + if not parent: + # should we give a warning here? + return self.AddRoot(text, ct_type, wnd, image, selImage, data) + + return self.DoInsertItem(parent, len(parent.GetChildren()), text, ct_type, wnd, image, selImage, data) + + + def SendDeleteEvent(self, item): + """Actully sends the EVT_TREE_DELETE_ITEM event.""" + + event = TreeEvent(wxEVT_TREE_DELETE_ITEM, self.GetId()) + event._item = item + event.SetEventObject(self) + self.ProcessEvent(event) + + + def IsDescendantOf(self, parent, item): + """Checks if the given item is under another one.""" + + while item: + + if item == parent: + + # item is a descendant of parent + return True + + item = item.GetParent() + + return False + + + # Don't leave edit or selection on a child which is about to disappear + def ChildrenClosing(self, item): + """We are about to destroy the item children.""" + + if self._textCtrl != None and item != self._textCtrl.item() and self.IsDescendantOf(item, self._textCtrl.item()): + self._textCtrl.StopEditing() + + if item != self._key_current and self.IsDescendantOf(item, self._key_current): + self._key_current = None + + if self.IsDescendantOf(item, self._select_me): + self._select_me = item + + if item != self._current and self.IsDescendantOf(item, self._current): + self._current.SetHilight(False) + self._current = None + self._select_me = item + + + def DeleteChildren(self, item): + """Delete item children.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + self._dirty = True # do this first so stuff below doesn't cause flicker + + self.ChildrenClosing(item) + item.DeleteChildren(self) + + + def Delete(self, item): + """Delete an item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + self._dirty = True # do this first so stuff below doesn't cause flicker + + if self._textCtrl != None and self.IsDescendantOf(item, self._textCtrl.item()): + # can't delete the item being edited, cancel editing it first + self._textCtrl.StopEditing() + + parent = item.GetParent() + + # don't keep stale pointers around! + if self.IsDescendantOf(item, self._key_current): + + # Don't silently change the selection: + # do it properly in idle time, so event + # handlers get called. + + # self._key_current = parent + self._key_current = None + + # self._select_me records whether we need to select + # a different item, in idle time. + if self._select_me and self.IsDescendantOf(item, self._select_me): + self._select_me = parent + + if self.IsDescendantOf(item, self._current): + + # Don't silently change the selection: + # do it properly in idle time, so event + # handlers get called. + + # self._current = parent + self._current = None + self._select_me = parent + + # remove the item from the tree + if parent: + + parent.GetChildren().remove(item) # remove by value + + else: # deleting the root + + # nothing will be left in the tree + self._anchor = None + + # and delete all of its children and the item itself now + item.DeleteChildren(self) + self.SendDeleteEvent(item) + + if item == self._select_me: + self._select_me = None + + # Remove the item with window + if item in self._itemWithWindow: + wnd = item.GetWindow() + wnd.Hide() + wnd.Destroy() + item._wnd = None + self._itemWithWindow.remove(item) + + del item + + + def DeleteAllItems(self): + """Delete all items in the CustomTreeCtrl.""" + + if self._anchor: + self.Delete(self._anchor) + + + def Expand(self, item): + """ + Expands an item, sending a EVT_TREE_ITEM_EXPANDING and + EVT_TREE_ITEM_EXPANDED events. + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem(): + raise "\nERROR: Can't Expand An Hidden Root. " + + if not item.HasPlus(): + return + + if item.IsExpanded(): + return + + event = TreeEvent(wxEVT_TREE_ITEM_EXPANDING, self.GetId()) + event._item = item + event.SetEventObject(self) + + if self.ProcessEvent(event) and not event.IsAllowed(): + # cancelled by program + return + + item.Expand() + self.CalculatePositions() + + self.RefreshSubtree(item) + + if self._hasWindows: + # We hide the associated window here, we may show it after + self.HideWindows() + + event.SetEventType(wxEVT_TREE_ITEM_EXPANDED) + self.ProcessEvent(event) + + + def ExpandAll(self, item): + """Expands all the items.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if not self.HasFlag(TR_HIDE_ROOT) or item != GetRootItem(): + self.Expand(item) + if not self.IsExpanded(item): + return + + child, cookie = self.GetFirstChild(item) + + while child: + self.ExpandAll(child) + child, cookie = self.GetNextChild(item, cookie) + + + def Collapse(self, item): + """ + Collapse an item, sending a EVT_TREE_ITEM_COLLAPSING and + EVT_TREE_ITEM_COLLAPSED events. + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + if self.HasFlag(TR_HIDE_ROOT) and item == self.GetRootItem(): + raise "\nERROR: Can't Collapse An Hidden Root. " + + if not item.IsExpanded(): + return + + event = TreeEvent(wxEVT_TREE_ITEM_COLLAPSING, self.GetId()) + event._item = item + event.SetEventObject(self) + if self.ProcessEvent(event) and not event.IsAllowed(): + # cancelled by program + return + + self.ChildrenClosing(item) + item.Collapse() + + self.CalculatePositions() + self.RefreshSubtree(item) + + if self._hasWindows: + self.HideWindows() + + event.SetEventType(wxEVT_TREE_ITEM_COLLAPSED) + self.ProcessEvent(event) + + + def CollapseAndReset(self, item): + """Collapse the given item and deletes its children.""" + + self.Collapse(item) + self.DeleteChildren(item) + + + def Toggle(self, item): + """Toggles the item state (collapsed/expanded).""" + + if item.IsExpanded(): + self.Collapse(item) + else: + self.Expand(item) + + + def HideWindows(self): + """Hides the windows associated to the items. Used internally.""" + + for child in self._itemWithWindow: + if not self.IsVisible(child): + wnd = child.GetWindow() + wnd.Hide() + + + def Unselect(self): + """Unselects the current selection.""" + + if self._current: + + self._current.SetHilight(False) + self.RefreshLine(self._current) + + self._current = None + self._select_me = None + + + def UnselectAllChildren(self, item): + """Unselects all the children of the given item.""" + + if item.IsSelected(): + + item.SetHilight(False) + self.RefreshLine(item) + + if item.HasChildren(): + for child in item.GetChildren(): + self.UnselectAllChildren(child) + + + def UnselectAll(self): + """Unselect all the items.""" + + rootItem = self.GetRootItem() + + # the tree might not have the root item at all + if rootItem: + self.UnselectAllChildren(rootItem) + + + # Recursive function ! + # To stop we must have crt_item start_y+client_h: + + # going up + x, y = self._anchor.GetSize(x, y, self) + y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels + x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels + item_y += _PIXELS_PER_UNIT+2 + x_pos = self.GetScrollPos(wx.HORIZONTAL) + # Item should appear at bottom + self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, (item_y+self.GetLineHeight(item)-client_h)/_PIXELS_PER_UNIT ) + + + def OnCompareItems(self, item1, item2): + """ + Returns whether 2 items have the same text. + Override this function in the derived class to change the sort order of the items + in the CustomTreeCtrl. The function should return a negative, zero or positive + value if the first item is less than, equal to or greater than the second one. + + The base class version compares items alphabetically. + """ + + return self.GetItemText(item1) == self.GetItemText(item2) + + + def SortChildren(self, item): + """ + Sorts the children of the given item using OnCompareItems method of CustomTreeCtrl. + You should override that method to change the sort order (the default is ascending + case-sensitive alphabetical order). + """ + + if not item: + raise "\nERROR: Invalid Tree Item. " + + children = item.GetChildren() + + if len(children) > 1: + self._dirty = True + children.sort(self.OnCompareItems) + + + def GetImageList(self): + """Returns the normal image list.""" + + return self._imageListNormal + + + def GetButtonsImageList(self): + """Returns the buttons image list (from which application-defined button images are taken).""" + + return self._imageListButtons + + + def GetStateImageList(self): + """Returns the state image list (from which application-defined state images are taken).""" + + return self._imageListState + + + def GetImageListCheck(self): + """Returns the image list used to build the check/radio buttons.""" + + return self._imageListCheck + + + def CalculateLineHeight(self): + """Calculates the height of a line.""" + + dc = wx.ClientDC(self) + self._lineHeight = dc.GetCharHeight() + + if self._imageListNormal: + + # Calculate a self._lineHeight value from the normal Image sizes. + # May be toggle off. Then CustomTreeCtrl will spread when + # necessary (which might look ugly). + n = self._imageListNormal.GetImageCount() + + for i in xrange(n): + + width, height = self._imageListNormal.GetSize(i) + + if height > self._lineHeight: + self._lineHeight = height + + if self._imageListButtons: + + # Calculate a self._lineHeight value from the Button image sizes. + # May be toggle off. Then CustomTreeCtrl will spread when + # necessary (which might look ugly). + n = self._imageListButtons.GetImageCount() + + for i in xrange(n): + + width, height = self._imageListButtons.GetSize(i) + + if height > self._lineHeight: + self._lineHeight = height + + if self._imageListCheck: + + # Calculate a self._lineHeight value from the check/radio image sizes. + # May be toggle off. Then CustomTreeCtrl will spread when + # necessary (which might look ugly). + n = self._imageListCheck.GetImageCount() + + for i in xrange(n): + + width, height = self._imageListCheck.GetSize(i) + + if height > self._lineHeight: + self._lineHeight = height + + if self._lineHeight < 30: + self._lineHeight += 2 # at least 2 pixels + else: + self._lineHeight += self._lineHeight/10 # otherwise 10% extra spacing + + + def SetImageList(self, imageList): + """Sets the normal image list.""" + + if self._ownsImageListNormal: + del self._imageListNormal + + self._imageListNormal = imageList + self._ownsImageListNormal = False + self._dirty = True + # Don't do any drawing if we're setting the list to NULL, + # since we may be in the process of deleting the tree control. + if imageList: + self.CalculateLineHeight() + + # We gray out the image list to use the grayed icons with disabled items + self._grayedImageList = wx.ImageList(16, 16, True, 0) + + for ii in xrange(imageList.GetImageCount()): + + bmp = imageList.GetBitmap(ii) + image = wx.ImageFromBitmap(bmp) + image = GrayOut(image) + newbmp = wx.BitmapFromImage(image) + self._grayedImageList.Add(newbmp) + + + def SetStateImageList(self, imageList): + """Sets the state image list (from which application-defined state images are taken).""" + + if self._ownsImageListState: + del self._imageListState + + self._imageListState = imageList + self._ownsImageListState = False + + + def SetButtonsImageList(self, imageList): + """Sets the buttons image list (from which application-defined button images are taken).""" + + if self._ownsImageListButtons: + del self._imageListButtons + + self._imageListButtons = imageList + self._ownsImageListButtons = False + self._dirty = True + self.CalculateLineHeight() + + + def SetImageListCheck(self, sizex, sizey, imglist=None): + """Sets the check image list.""" + + if imglist is None: + + self._imageListCheck = wx.ImageList(sizex, sizey) + self._imageListCheck.Add(GetCheckedBitmap()) + self._imageListCheck.Add(GetNotCheckedBitmap()) + self._imageListCheck.Add(GetFlaggedBitmap()) + self._imageListCheck.Add(GetNotFlaggedBitmap()) + + else: + + sizex, sizey = imglist.GetSize(0) + self._imageListCheck = imglist + + # We gray out the image list to use the grayed icons with disabled items + self._grayedCheckList = wx.ImageList(sizex, sizey, True, 0) + + for ii in xrange(self._imageListCheck.GetImageCount()): + + bmp = self._imageListCheck.GetBitmap(ii) + image = wx.ImageFromBitmap(bmp) + image = GrayOut(image) + newbmp = wx.BitmapFromImage(image) + self._grayedCheckList.Add(newbmp) + + self._dirty = True + + if imglist: + self.CalculateLineHeight() + + + def AssignImageList(self, imageList): + """Assigns the normal image list.""" + + self.SetImageList(imageList) + self._ownsImageListNormal = True + + + def AssignStateImageList(self, imageList): + """Assigns the state image list.""" + + self.SetStateImageList(imageList) + self._ownsImageListState = True + + + def AssignButtonsImageList(self, imageList): + """Assigns the button image list.""" + + self.SetButtonsImageList(imageList) + self._ownsImageListButtons = True + + +# ----------------------------------------------------------------------------- +# helpers +# ----------------------------------------------------------------------------- + + def AdjustMyScrollbars(self): + """Adjust the wx.ScrolledWindow scrollbars.""" + + if self._anchor: + + x, y = self._anchor.GetSize(0, 0, self) + y += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels + x += _PIXELS_PER_UNIT + 2 # one more scrollbar unit + 2 pixels + x_pos = self.GetScrollPos(wx.HORIZONTAL) + y_pos = self.GetScrollPos(wx.VERTICAL) + self.SetScrollbars(_PIXELS_PER_UNIT, _PIXELS_PER_UNIT, x/_PIXELS_PER_UNIT, y/_PIXELS_PER_UNIT, x_pos, y_pos) + + else: + + self.SetScrollbars(0, 0, 0, 0) + + + def GetLineHeight(self, item): + """Returns the line height for the given item.""" + + if self.GetTreeStyle() & TR_HAS_VARIABLE_ROW_HEIGHT: + return item.GetHeight() + else: + return self._lineHeight + + + def DrawVerticalGradient(self, dc, rect, hasfocus): + """Gradient fill from colour 1 to colour 2 from top to bottom.""" + + dc.DrawRectangleRect(rect) + border = self._borderPen.GetWidth() + + dc.SetPen(wx.TRANSPARENT_PEN) + + # calculate gradient coefficients + if hasfocus: + col2 = self._secondcolour + col1 = self._firstcolour + else: + col2 = self._hilightUnfocusedBrush.GetColour() + col1 = self._hilightUnfocusedBrush2.GetColour() + + r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) + r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) + + flrect = float(rect.height) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + for y in xrange(rect.y+border, rect.y + rect.height-border): + currCol = (r1 + rf, g1 + gf, b1 + bf) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + dc.DrawRectangle(rect.x+border, y, rect.width-2*border, 1) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + + def DrawHorizontalGradient(self, dc, rect, hasfocus): + """Gradient fill from colour 1 to colour 2 from left to right.""" + + dc.DrawRectangleRect(rect) + border = self._borderPen.GetWidth() + + dc.SetPen(wx.TRANSPARENT_PEN) + + # calculate gradient coefficients + + if hasfocus: + col2 = self._secondcolour + col1 = self._firstcolour + else: + col2 = self._hilightUnfocusedBrush.GetColour() + col1 = self._hilightUnfocusedBrush2.GetColour() + + r1, g1, b1 = int(col1.Red()), int(col1.Green()), int(col1.Blue()) + r2, g2, b2 = int(col2.Red()), int(col2.Green()), int(col2.Blue()) + + flrect = float(rect.width) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + + for x in xrange(rect.x+border, rect.x + rect.width-border): + currCol = (int(r1 + rf), int(g1 + gf), int(b1 + bf)) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + dc.DrawRectangle(x, rect.y+border, 1, rect.height-2*border) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + + def DrawVistaRectangle(self, dc, rect, hasfocus): + """Draw the selected item(s) with the Windows Vista style.""" + + if hasfocus: + + outer = _rgbSelectOuter + inner = _rgbSelectInner + top = _rgbSelectTop + bottom = _rgbSelectBottom + + else: + + outer = _rgbNoFocusOuter + inner = _rgbNoFocusInner + top = _rgbNoFocusTop + bottom = _rgbNoFocusBottom + + oldpen = dc.GetPen() + oldbrush = dc.GetBrush() + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(wx.Pen(outer)) + dc.DrawRoundedRectangleRect(rect, 3) + rect.Deflate(1, 1) + dc.SetPen(wx.Pen(inner)) + dc.DrawRoundedRectangleRect(rect, 2) + rect.Deflate(1, 1) + + r1, g1, b1 = int(top.Red()), int(top.Green()), int(top.Blue()) + r2, g2, b2 = int(bottom.Red()), int(bottom.Green()), int(bottom.Blue()) + + flrect = float(rect.height) + + rstep = float((r2 - r1)) / flrect + gstep = float((g2 - g1)) / flrect + bstep = float((b2 - b1)) / flrect + + rf, gf, bf = 0, 0, 0 + dc.SetPen(wx.TRANSPARENT_PEN) + + for y in xrange(rect.y, rect.y + rect.height): + currCol = (r1 + rf, g1 + gf, b1 + bf) + dc.SetBrush(wx.Brush(currCol, wx.SOLID)) + dc.DrawRectangle(rect.x, y, rect.width, 1) + rf = rf + rstep + gf = gf + gstep + bf = bf + bstep + + dc.SetPen(oldpen) + dc.SetBrush(oldbrush) + + + def PaintItem(self, item, dc): + """Actually paint an item.""" + + attr = item.GetAttributes() + + if attr and attr.HasFont(): + dc.SetFont(attr.GetFont()) + elif item.IsBold(): + dc.SetFont(self._boldFont) + if item.IsHyperText(): + dc.SetFont(self.GetHyperTextFont()) + if item.GetVisited(): + dc.SetTextForeground(self.GetHyperTextVisitedColour()) + else: + dc.SetTextForeground(self.GetHyperTextNewColour()) + + text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText()) + + image = item.GetCurrentImage() + checkimage = item.GetCurrentCheckedImage() + image_w, image_h = 0, 0 + + if image != _NO_IMAGE: + + if self._imageListNormal: + + image_w, image_h = self._imageListNormal.GetSize(image) + image_w += 4 + + else: + + image = _NO_IMAGE + + if item.GetType() != 0: + wcheck, hcheck = self._imageListCheck.GetSize(item.GetType()) + wcheck += 4 + else: + wcheck, hcheck = 0, 0 + + total_h = self.GetLineHeight(item) + drawItemBackground = False + + if item.IsSelected(): + + # under mac selections are only a rectangle in case they don't have the focus + if wx.Platform == "__WXMAC__": + if not self._hasFocus: + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT), 1, wx.SOLID)) + else: + dc.SetBrush(self._hilightBrush) + else: + dc.SetBrush((self._hasFocus and [self._hilightBrush] or [self._hilightUnfocusedBrush])[0]) + drawItemBackground = True + else: + if attr and attr.HasBackgroundColour(): + drawItemBackground = True + colBg = attr.GetBackgroundColour() + else: + colBg = self._backgroundColour + + dc.SetBrush(wx.Brush(colBg, wx.SOLID)) + dc.SetPen(wx.TRANSPARENT_PEN) + + offset = (self.HasFlag(TR_ROW_LINES) and [1] or [0])[0] + + if self.HasFlag(TR_FULL_ROW_HIGHLIGHT): + + oldpen = dc.GetPen() + dc.SetPen(wx.TRANSPARENT_PEN) + x, y = self.GetPosition() + w, h = self.GetSize() + + itemrect = wx.Rect(x, item.GetY()+offset, w, total_h-offset) + + if item.IsSelected(): + if self._usegradients: + if self._gradientstyle == 0: # Horizontal + self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) + else: # Vertical + self.DrawVerticalGradient(dc, itemrect, self._hasFocus) + elif self._vistaselection: + self.DrawVistaRectangle(dc, itemrect, self._hasFocus) + else: + dc.DrawRectangleRect(itemrect) + + dc.SetPen(oldpen) + + else: + + if item.IsSelected() and image != _NO_IMAGE: + + # If it's selected, and there's an image, then we should + # take care to leave the area under the image painted in the + # background colour. + + wnd = item.GetWindow() + wndx = 0 + if wnd: + wndx, wndy = item.GetWindowSize() + + itemrect = wx.Rect(item.GetX() + wcheck + image_w - 2, item.GetY()+offset, + item.GetWidth() - image_w - wcheck + 2 - wndx, total_h-offset) + + if self._usegradients: + if self._gradientstyle == 0: # Horizontal + self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) + else: # Vertical + self.DrawVerticalGradient(dc, itemrect, self._hasFocus) + elif self._vistaselection: + self.DrawVistaRectangle(dc, itemrect, self._hasFocus) + else: + dc.DrawRectangleRect(itemrect) + + # On GTK+ 2, drawing a 'normal' background is wrong for themes that + # don't allow backgrounds to be customized. Not drawing the background, + # except for custom item backgrounds, works for both kinds of theme. + elif drawItemBackground: + + minusicon = wcheck + image_w - 2 + itemrect = wx.Rect(item.GetX()+minusicon, item.GetY()+offset, item.GetWidth()-minusicon, total_h-offset) + + if self._usegradients and self._hasFocus: + if self._gradientstyle == 0: # Horizontal + self.DrawHorizontalGradient(dc, itemrect, self._hasFocus) + else: # Vertical + self.DrawVerticalGradient(dc, itemrect, self._hasFocus) + else: + dc.DrawRectangleRect(itemrect) + + if image != _NO_IMAGE: + + dc.SetClippingRegion(item.GetX(), item.GetY(), wcheck+image_w-2, total_h) + if item.IsEnabled(): + imglist = self._imageListNormal + else: + imglist = self._grayedImageList + + imglist.Draw(image, dc, + item.GetX() + wcheck, + item.GetY() + ((total_h > image_h) and [(total_h-image_h)/2] or [0])[0], + wx.IMAGELIST_DRAW_TRANSPARENT) + + dc.DestroyClippingRegion() + + if wcheck: + if item.IsEnabled(): + imglist = self._imageListCheck + else: + imglist = self._grayedCheckList + + imglist.Draw(checkimage, dc, + item.GetX(), + item.GetY() + ((total_h > hcheck) and [(total_h-hcheck)/2] or [0])[0], + wx.IMAGELIST_DRAW_TRANSPARENT) + + dc.SetBackgroundMode(wx.TRANSPARENT) + extraH = ((total_h > text_h) and [(total_h - text_h)/2] or [0])[0] + + textrect = wx.Rect(wcheck + image_w + item.GetX(), item.GetY() + extraH, text_w, text_h) + + if not item.IsEnabled(): + foreground = dc.GetTextForeground() + dc.SetTextForeground(self._disabledColour) + dc.DrawLabel(item.GetText(), textrect) + dc.SetTextForeground(foreground) + else: + dc.DrawLabel(item.GetText(), textrect) + + wnd = item.GetWindow() + if wnd: + wndx = wcheck + image_w + item.GetX() + text_w + 4 + xa, ya = self.CalcScrolledPosition((0, item.GetY())) + if not wnd.IsShown(): + wnd.Show() + if wnd.GetPosition() != (wndx, ya): + wnd.SetPosition((wndx, ya)) + + # restore normal font + dc.SetFont(self._normalFont) + + + # Now y stands for the top of the item, whereas it used to stand for middle ! + def PaintLevel(self, item, dc, level, y): + """Paint a level of CustomTreeCtrl.""" + + x = level*self._indent + + if not self.HasFlag(TR_HIDE_ROOT): + + x += self._indent + + elif level == 0: + + # always expand hidden root + origY = y + children = item.GetChildren() + count = len(children) + + if count > 0: + n = 0 + while n < count: + oldY = y + y = self.PaintLevel(children[n], dc, 1, y) + n = n + 1 + + if not self.HasFlag(TR_NO_LINES) and self.HasFlag(TR_LINES_AT_ROOT) and count > 0: + + # draw line down to last child + origY += self.GetLineHeight(children[0])>>1 + oldY += self.GetLineHeight(children[n-1])>>1 + dc.DrawLine(3, origY, 3, oldY) + + return y + + item.SetX(x+self._spacing) + item.SetY(y) + + h = self.GetLineHeight(item) + y_top = y + y_mid = y_top + (h>>1) + y += h + + exposed_x = dc.LogicalToDeviceX(0) + exposed_y = dc.LogicalToDeviceY(y_top) + + if self.IsExposed(exposed_x, exposed_y, 10000, h): # 10000 = very much + if wx.Platform == "__WXMAC__": + # don't draw rect outline if we already have the + # background color under Mac + pen = ((item.IsSelected() and self._hasFocus) and [self._borderPen] or [wx.TRANSPARENT_PEN])[0] + else: + pen = self._borderPen + + if item.IsSelected(): + if (wx.Platform == "__WXMAC__" and self._hasFocus): + colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + else: + colText = wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT) + else: + attr = item.GetAttributes() + if attr and attr.HasTextColour(): + colText = attr.GetTextColour() + else: + colText = self.GetForegroundColour() + + if self._vistaselection: + colText = wx.BLACK + + # prepare to draw + dc.SetTextForeground(colText) + dc.SetPen(pen) + oldpen = pen + + # draw + self.PaintItem(item, dc) + + if self.HasFlag(TR_ROW_LINES): + + # if the background colour is white, choose a + # contrasting color for the lines + medium_grey = wx.Pen(wx.Colour(200, 200, 200)) + dc.SetPen(((self.GetBackgroundColour() == wx.WHITE) and [medium_grey] or [wx.WHITE_PEN])[0]) + dc.DrawLine(0, y_top, 10000, y_top) + dc.DrawLine(0, y, 10000, y) + + # restore DC objects + dc.SetBrush(wx.WHITE_BRUSH) + dc.SetTextForeground(wx.BLACK) + + if not self.HasFlag(TR_NO_LINES): + + # draw the horizontal line here + dc.SetPen(self._dottedPen) + x_start = x + if x > self._indent: + x_start -= self._indent + elif self.HasFlag(TR_LINES_AT_ROOT): + x_start = 3 + dc.DrawLine(x_start, y_mid, x + self._spacing, y_mid) + dc.SetPen(oldpen) + + # should the item show a button? + if item.HasPlus() and self.HasButtons(): + + if self._imageListButtons: + + # draw the image button here + image_h = 0 + image_w = 0 + image = (item.IsExpanded() and [TreeItemIcon_Expanded] or [TreeItemIcon_Normal])[0] + if item.IsSelected(): + image += TreeItemIcon_Selected - TreeItemIcon_Normal + + image_w, image_h = self._imageListButtons.GetSize(image) + xx = x - image_w/2 + yy = y_mid - image_h/2 + + dc.SetClippingRegion(xx, yy, image_w, image_h) + self._imageListButtons.Draw(image, dc, xx, yy, + wx.IMAGELIST_DRAW_TRANSPARENT) + dc.DestroyClippingRegion() + + else: # no custom buttons + + if self._windowStyle & TR_TWIST_BUTTONS: + # We draw something like the Mac twist buttons + + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(self._hilightBrush) + button = [wx.Point(), wx.Point(), wx.Point()] + + if item.IsExpanded(): + button[0].x = x - 5 + button[0].y = y_mid - 3 + button[1].x = x + 5 + button[1].y = button[0].y + button[2].x = x + button[2].y = button[0].y + 6 + else: + button[0].x = x - 3 + button[0].y = y_mid - 5 + button[1].x = button[0].x + button[1].y = y_mid + 5 + button[2].x = button[0].x + 5 + button[2].y = y_mid + + dc.DrawPolygon(button) + + else: + # These are the standard wx.TreeCtrl buttons as wx.RendererNative knows + + wImage = 9 + hImage = 9 + + flag = 0 + + if item.IsExpanded(): + flag |= _CONTROL_EXPANDED + if item == self._underMouse: + flag |= _CONTROL_CURRENT + + self._drawingfunction(self, dc, wx.Rect(x - wImage/2, y_mid - hImage/2,wImage, hImage), flag) + + if item.IsExpanded(): + + children = item.GetChildren() + count = len(children) + + if count > 0: + + n = 0 + level = level + 1 + + while n < count: + oldY = y + y = self.PaintLevel(children[n], dc, level, y) + n = n + 1 + + if not self.HasFlag(TR_NO_LINES) and count > 0: + + # draw line down to last child + oldY += self.GetLineHeight(children[n-1])>>1 + if self.HasButtons(): + y_mid += 5 + + # Only draw the portion of the line that is visible, in case it is huge + xOrigin, yOrigin = dc.GetDeviceOrigin() + yOrigin = abs(yOrigin) + width, height = self.GetClientSize() + + # Move end points to the begining/end of the view? + if y_mid < yOrigin: + y_mid = yOrigin + if oldY > yOrigin + height: + oldY = yOrigin + height + + # after the adjustments if y_mid is larger than oldY then the line + # isn't visible at all so don't draw anything + if y_mid < oldY: + dc.SetPen(self._dottedPen) + dc.DrawLine(x, y_mid, x, oldY) + + return y + + +# ----------------------------------------------------------------------------- +# wxWidgets callbacks +# ----------------------------------------------------------------------------- + + def OnPaint(self, event): + """Handles the wx.EVT_PAINT event.""" + + dc = wx.PaintDC(self) + self.PrepareDC(dc) + + if not self._anchor: + return + + dc.SetFont(self._normalFont) + dc.SetPen(self._dottedPen) + + y = 2 + self.PaintLevel(self._anchor, dc, 0, y) + + + def OnEraseBackground(self, event): + """Handles the wx.EVT_ERASE_BACKGROUND event.""" + + # Can we actually do something here (or in OnPaint()) To Handle + # background images that are stretchable or always centered? + # I tried but I get enormous flickering... + + if not self._backgroundImage: + event.Skip() + return + + if self._imageStretchStyle == _StyleTile: + dc = event.GetDC() + + if not dc: + dc = wx.ClientDC(self) + rect = self.GetUpdateRegion().GetBox() + dc.SetClippingRect(rect) + + self.TileBackground(dc) + + + def TileBackground(self, dc): + """Tiles the background image to fill all the available area.""" + + sz = self.GetClientSize() + w = self._backgroundImage.GetWidth() + h = self._backgroundImage.GetHeight() + + x = 0 + + while x < sz.width: + y = 0 + + while y < sz.height: + dc.DrawBitmap(self._backgroundImage, x, y, True) + y = y + h + + x = x + w + + + def OnSetFocus(self, event): + """Handles the wx.EVT_SET_FOCUS event.""" + + self._hasFocus = True + self.RefreshSelected() + event.Skip() + + + def OnKillFocus(self, event): + """Handles the wx.EVT_KILL_FOCUS event.""" + + self._hasFocus = False + self.RefreshSelected() + event.Skip() + + + def OnKeyDown(self, event): + """Handles the wx.EVT_CHAR event, sending a EVT_TREE_KEY_DOWN event.""" + + te = TreeEvent(wxEVT_TREE_KEY_DOWN, self.GetId()) + te._evtKey = event + te.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(te): + # intercepted by the user code + return + + if self._current is None or self._key_current is None: + + event.Skip() + return + + # how should the selection work for this event? + is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), event.ShiftDown(), event.CmdDown()) + + # + : Expand + # - : Collaspe + # * : Expand all/Collapse all + # ' ' | return : activate + # up : go up (not last children!) + # down : go down + # left : go to parent + # right : open if parent and go next + # home : go to root + # end : go to last item without opening parents + # alnum : start or continue searching for the item with this prefix + + keyCode = event.GetKeyCode() + + if keyCode in [ord("+"), wx.WXK_ADD]: # "+" + if self._current.HasPlus() and not self.IsExpanded(self._current) and self.IsEnabled(self._current): + self.Expand(self._current) + + elif keyCode in [ord("*"), wx.WXK_MULTIPLY]: # "*" + if not self.IsExpanded(self._current) and self.IsEnabled(self._current): + # expand all + self.ExpandAll(self._current) + + elif keyCode in [ord("-"), wx.WXK_SUBTRACT]: # "-" + if self.IsExpanded(self._current): + self.Collapse(self._current) + + elif keyCode == wx.WXK_MENU: + # Use the item's bounding rectangle to determine position for the event + itemRect = self.GetBoundingRect(self._current, True) + event = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId()) + event._item = self._current + # Use the left edge, vertical middle + event._pointDrag = wx.Point(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight()/2) + event.SetEventObject(self) + self.GetEventHandler().ProcessEvent(event) + + elif keyCode in [wx.WXK_RETURN, wx.WXK_SPACE]: + + if not self.IsEnabled(self._current): + event.Skip() + return + + if not event.HasModifiers(): + event = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId()) + event._item = self._current + event.SetEventObject(self) + self.GetEventHandler().ProcessEvent(event) + + if keyCode == wx.WXK_SPACE and self.GetItemType(self._current) > 0: + checked = not self.IsItemChecked(self._current) + self.CheckItem(self._current, checked) + + # in any case, also generate the normal key event for this key, + # even if we generated the ACTIVATED event above: this is what + # wxMSW does and it makes sense because you might not want to + # process ACTIVATED event at all and handle Space and Return + # directly (and differently) which would be impossible otherwise + event.Skip() + + # up goes to the previous sibling or to the last + # of its children if it's expanded + elif keyCode == wx.WXK_UP: + prev = self.GetPrevSibling(self._key_current) + if not prev: + prev = self.GetItemParent(self._key_current) + if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT): + return + + if prev: + current = self._key_current + # TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be? + if current == self.GetFirstChild(prev)[0] and self.IsEnabled(prev): + # otherwise we return to where we came from + self.DoSelectItem(prev, unselect_others, extended_select) + self._key_current = prev + + else: + current = self._key_current + + # We are going to another parent node + while self.IsExpanded(prev) and self.HasChildren(prev): + child = self.GetLastChild(prev) + if child: + prev = child + current = prev + + # Try to get the previous siblings and see if they are active + while prev and not self.IsEnabled(prev): + prev = self.GetPrevSibling(prev) + + if not prev: + # No previous siblings active: go to the parent and up + prev = self.GetItemParent(current) + while prev and not self.IsEnabled(prev): + prev = self.GetItemParent(prev) + + if prev: + self.DoSelectItem(prev, unselect_others, extended_select) + self._key_current = prev + + # left arrow goes to the parent + elif keyCode == wx.WXK_LEFT: + + prev = self.GetItemParent(self._current) + if prev == self.GetRootItem() and self.HasFlag(TR_HIDE_ROOT): + # don't go to root if it is hidden + prev = self.GetPrevSibling(self._current) + + if self.IsExpanded(self._current): + self.Collapse(self._current) + else: + if prev and self.IsEnabled(prev): + self.DoSelectItem(prev, unselect_others, extended_select) + + elif keyCode == wx.WXK_RIGHT: + # this works the same as the down arrow except that we + # also expand the item if it wasn't expanded yet + if self.IsExpanded(self._current) and self.HasChildren(self._current): + child, cookie = self.GetFirstChild(self._key_current) + if self.IsEnabled(child): + self.DoSelectItem(child, unselect_others, extended_select) + self._key_current = child + else: + self.Expand(self._current) + # fall through + + elif keyCode == wx.WXK_DOWN: + if self.IsExpanded(self._key_current) and self.HasChildren(self._key_current): + + child = self.GetNextActiveItem(self._key_current) + + if child: + self.DoSelectItem(child, unselect_others, extended_select) + self._key_current = child + + else: + + next = self.GetNextSibling(self._key_current) + + if not next: + current = self._key_current + while current and not next: + current = self.GetItemParent(current) + if current: + next = self.GetNextSibling(current) + if not self.IsEnabled(next): + next = None + + else: + while next and not self.IsEnabled(next): + next = self.GetNext(next) + + if next: + self.DoSelectItem(next, unselect_others, extended_select) + self._key_current = next + + + # selects the last visible tree item + elif keyCode == wx.WXK_END: + + last = self.GetRootItem() + + while last and self.IsExpanded(last): + + lastChild = self.GetLastChild(last) + + # it may happen if the item was expanded but then all of + # its children have been deleted - so IsExpanded() returned + # true, but GetLastChild() returned invalid item + if not lastChild: + break + + last = lastChild + + if last and self.IsEnabled(last): + + self.DoSelectItem(last, unselect_others, extended_select) + + # selects the root item + elif keyCode == wx.WXK_HOME: + + prev = self.GetRootItem() + + if not prev: + return + + if self.HasFlag(TR_HIDE_ROOT): + prev, cookie = self.GetFirstChild(prev) + if not prev: + return + + if self.IsEnabled(prev): + self.DoSelectItem(prev, unselect_others, extended_select) + + else: + + if not event.HasModifiers() and ((keyCode >= ord('0') and keyCode <= ord('9')) or \ + (keyCode >= ord('a') and keyCode <= ord('z')) or \ + (keyCode >= ord('A') and keyCode <= ord('Z'))): + + # find the next item starting with the given prefix + ch = chr(keyCode) + id = self.FindItem(self._current, self._findPrefix + ch) + + if not id: + # no such item + return + + if self.IsEnabled(id): + self.SelectItem(id) + self._findPrefix += ch + + # also start the timer to reset the current prefix if the user + # doesn't press any more alnum keys soon -- we wouldn't want + # to use this prefix for a new item search + if not self._findTimer: + self._findTimer = TreeFindTimer(self) + + self._findTimer.Start(_DELAY, wx.TIMER_ONE_SHOT) + + else: + + event.Skip() + + + def GetNextActiveItem(self, item, down=True): + """Returns the next active item. Used Internally at present. """ + + if down: + sibling = self.GetNextSibling + else: + sibling = self.GetPrevSibling + + if self.GetItemType(item) == 2 and not self.IsItemChecked(item): + # Is an unchecked radiobutton... all its children are inactive + # try to get the next/previous sibling + found = 0 + + while 1: + child = sibling(item) + if (child and self.IsEnabled(child)) or not child: + break + item = child + + else: + # Tha's not a radiobutton... but some of its children can be + # inactive + child, cookie = self.GetFirstChild(item) + while child and not self.IsEnabled(child): + child, cookie = self.GetNextChild(item, cookie) + + if child and self.IsEnabled(child): + return child + + return None + + + def HitTest(self, point, flags=0): + """ + Calculates which (if any) item is under the given point, returning the tree item + at this point plus extra information flags. Flags is a bitlist of the following: + + TREE_HITTEST_ABOVE above the client area + TREE_HITTEST_BELOW below the client area + TREE_HITTEST_NOWHERE no item has been hit + TREE_HITTEST_ONITEMBUTTON on the button associated to an item + TREE_HITTEST_ONITEMICON on the icon associated to an item + TREE_HITTEST_ONITEMCHECKICON on the check/radio icon, if present + TREE_HITTEST_ONITEMINDENT on the indent associated to an item + TREE_HITTEST_ONITEMLABEL on the label (string) associated to an item + TREE_HITTEST_ONITEMRIGHT on the right of the label associated to an item + TREE_HITTEST_TOLEFT on the left of the client area + TREE_HITTEST_TORIGHT on the right of the client area + TREE_HITTEST_ONITEMUPPERPART on the upper part (first half) of the item + TREE_HITTEST_ONITEMLOWERPART on the lower part (second half) of the item + TREE_HITTEST_ONITEM anywhere on the item + + Note: both the item (if any, None otherwise) and the flag are always returned as a tuple. + """ + + w, h = self.GetSize() + flags = 0 + + if point.x < 0: + flags |= TREE_HITTEST_TOLEFT + if point.x > w: + flags |= TREE_HITTEST_TORIGHT + if point.y < 0: + flags |= TREE_HITTEST_ABOVE + if point.y > h: + flags |= TREE_HITTEST_BELOW + + if flags: + return None, flags + + if self._anchor == None: + flags = TREE_HITTEST_NOWHERE + return None, flags + + hit, flags = self._anchor.HitTest(self.CalcUnscrolledPosition(point), self, flags, 0) + + if hit == None: + flags = TREE_HITTEST_NOWHERE + return None, flags + + if not self.IsEnabled(hit): + return None, flags + + return hit, flags + + + def GetBoundingRect(self, item, textOnly=False): + """Gets the bounding rectangle of the item.""" + + if not item: + raise "\nERROR: Invalid Tree Item. " + + i = item + + startX, startY = self.GetViewStart() + rect = wx.Rect() + + rect.x = i.GetX() - startX*_PIXELS_PER_UNIT + rect.y = i.GetY() - startY*_PIXELS_PER_UNIT + rect.width = i.GetWidth() + rect.height = self.GetLineHeight(i) + + return rect + + + def Edit(self, item): + """ + Internal function. Starts the editing of an item label, sending a + EVT_TREE_BEGIN_LABEL_EDIT event. + """ + + te = TreeEvent(wxEVT_TREE_BEGIN_LABEL_EDIT, self.GetId()) + te._item = item + te.SetEventObject(self) + if self.GetEventHandler().ProcessEvent(te) and not te.IsAllowed(): + # vetoed by user + return + + # We have to call this here because the label in + # question might just have been added and no screen + # update taken place. + if self._dirty: + if wx.Platform in ["__WXMSW__", "__WXMAC__"]: + self.Update() + else: + wx.YieldIfNeeded() + + if self._textCtrl != None and item != self._textCtrl.item(): + self._textCtrl.StopEditing() + + self._textCtrl = TreeTextCtrl(self, item=item) + self._textCtrl.SetFocus() + + + def GetEditControl(self): + """ + Returns a pointer to the edit TextCtrl if the item is being edited or + None otherwise (it is assumed that no more than one item may be edited + simultaneously). + """ + + return self._textCtrl + + + def OnRenameAccept(self, item, value): + """ + Called by TreeTextCtrl, to accept the changes and to send the + EVT_TREE_END_LABEL_EDIT event. + """ + + le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId()) + le._item = item + le.SetEventObject(self) + le._label = value + le._editCancelled = False + + return not self.GetEventHandler().ProcessEvent(le) or le.IsAllowed() + + + def OnRenameCancelled(self, item): + """ + Called by TreeTextCtrl, to cancel the changes and to send the + EVT_TREE_END_LABEL_EDIT event. + """ + + # let owner know that the edit was cancelled + le = TreeEvent(wxEVT_TREE_END_LABEL_EDIT, self.GetId()) + le._item = item + le.SetEventObject(self) + le._label = "" + le._editCancelled = True + + self.GetEventHandler().ProcessEvent(le) + + + def OnRenameTimer(self): + """The timer for renaming has expired. Start editing.""" + + self.Edit(self._current) + + + def OnMouse(self, event): + """Handles a bunch of wx.EVT_MOUSE_EVENTS events.""" + + if not self._anchor: + return + + pt = self.CalcUnscrolledPosition(event.GetPosition()) + + # Is the mouse over a tree item button? + flags = 0 + thisItem, flags = self._anchor.HitTest(pt, self, flags, 0) + underMouse = thisItem + underMouseChanged = underMouse != self._underMouse + + if underMouse and (flags & TREE_HITTEST_ONITEMBUTTON) and not event.LeftIsDown() and \ + not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()): + underMouse = underMouse + else: + underMouse = None + + if underMouse != self._underMouse: + if self._underMouse: + # unhighlight old item + self._underMouse = None + + self._underMouse = underMouse + + # Determines what item we are hovering over and need a tooltip for + hoverItem = thisItem + + # We do not want a tooltip if we are dragging, or if the rename timer is running + if underMouseChanged and not self._isDragging and (not self._renameTimer or not self._renameTimer.IsRunning()): + + if hoverItem is not None: + # Ask the tree control what tooltip (if any) should be shown + hevent = TreeEvent(wxEVT_TREE_ITEM_GETTOOLTIP, self.GetId()) + hevent._item = hoverItem + hevent.SetEventObject(self) + + if self.GetEventHandler().ProcessEvent(hevent) and hevent.IsAllowed(): + self.SetToolTip(hevent._label) + + if hoverItem.IsHyperText() and (flags & TREE_HITTEST_ONITEMLABEL) and hoverItem.IsEnabled(): + self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) + self._isonhyperlink = True + else: + if self._isonhyperlink: + self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) + self._isonhyperlink = False + + # we process left mouse up event (enables in-place edit), right down + # (pass to the user code), left dbl click (activate item) and + # dragging/moving events for items drag-and-drop + + if not (event.LeftDown() or event.LeftUp() or event.RightDown() or event.LeftDClick() or \ + event.Dragging() or ((event.Moving() or event.RightUp()) and self._isDragging)): + + event.Skip() + return + + flags = 0 + item, flags = self._anchor.HitTest(pt, self, flags, 0) + + if event.Dragging() and not self._isDragging and ((flags & TREE_HITTEST_ONITEMICON) or (flags & TREE_HITTEST_ONITEMLABEL)): + + if self._dragCount == 0: + self._dragStart = pt + + self._countDrag = 0 + self._dragCount = self._dragCount + 1 + + if self._dragCount != 3: + # wait until user drags a bit further... + return + + command = (event.RightIsDown() and [wxEVT_TREE_BEGIN_RDRAG] or [wxEVT_TREE_BEGIN_DRAG])[0] + + nevent = TreeEvent(command, self.GetId()) + nevent._item = self._current + nevent.SetEventObject(self) + newpt = self.CalcScrolledPosition(pt) + nevent.SetPoint(newpt) + + # by default the dragging is not supported, the user code must + # explicitly allow the event for it to take place + nevent.Veto() + + if self.GetEventHandler().ProcessEvent(nevent) and nevent.IsAllowed(): + + # we're going to drag this item + self._isDragging = True + + # remember the old cursor because we will change it while + # dragging + self._oldCursor = self._cursor + + # in a single selection control, hide the selection temporarily + if not (self.GetTreeStyle() & TR_MULTIPLE): + self._oldSelection = self.GetSelection() + + if self._oldSelection: + + self._oldSelection.SetHilight(False) + self.RefreshLine(self._oldSelection) + else: + selections = self.GetSelections() + if len(selections) == 1: + self._oldSelection = selections[0] + self._oldSelection.SetHilight(False) + self.RefreshLine(self._oldSelection) + + if self._dragImage: + del self._dragImage + + # Create the custom draw image from the icons and the text of the item + self._dragImage = DragImage(self, self._current) + self._dragImage.BeginDrag(wx.Point(0,0), self) + self._dragImage.Show() + self._dragImage.Move(self.CalcScrolledPosition(pt)) + + elif event.Dragging() and self._isDragging: + + self._dragImage.Move(self.CalcScrolledPosition(pt)) + + if self._countDrag == 0 and item: + self._oldItem = item + + if item != self._dropTarget: + + # unhighlight the previous drop target + if self._dropTarget: + self._dropTarget.SetHilight(False) + self.RefreshLine(self._dropTarget) + if item: + item.SetHilight(True) + self.RefreshLine(item) + self._countDrag = self._countDrag + 1 + self._dropTarget = item + + self.Update() + + if self._countDrag >= 3: + # Here I am trying to avoid ugly repainting problems... hope it works + self.RefreshLine(self._oldItem) + self._countDrag = 0 + + elif (event.LeftUp() or event.RightUp()) and self._isDragging: + + if self._dragImage: + self._dragImage.EndDrag() + + if self._dropTarget: + self._dropTarget.SetHilight(False) + + if self._oldSelection: + + self._oldSelection.SetHilight(True) + self.RefreshLine(self._oldSelection) + self._oldSelection = None + + # generate the drag end event + event = TreeEvent(wxEVT_TREE_END_DRAG, self.GetId()) + event._item = item + event._pointDrag = self.CalcScrolledPosition(pt) + event.SetEventObject(self) + + self.GetEventHandler().ProcessEvent(event) + + self._isDragging = False + self._dropTarget = None + + self.SetCursor(self._oldCursor) + + if wx.Platform in ["__WXMSW__", "__WXMAC__"]: + self.Refresh() + else: + # Probably this is not enough on GTK. Try a Refresh() if it does not work. + wx.YieldIfNeeded() + + else: + + # If we got to this point, we are not dragging or moving the mouse. + # Because the code in carbon/toplevel.cpp will only set focus to the tree + # if we skip for EVT_LEFT_DOWN, we MUST skip this event here for focus to work. + # We skip even if we didn't hit an item because we still should + # restore focus to the tree control even if we didn't exactly hit an item. + if event.LeftDown(): + self._hasFocus = True + self.SetFocusIgnoringChildren() + event.Skip() + + # here we process only the messages which happen on tree items + + self._dragCount = 0 + + if item == None: + if self._textCtrl != None and item != self._textCtrl.item(): + self._textCtrl.StopEditing() + return # we hit the blank area + + if event.RightDown(): + + if self._textCtrl != None and item != self._textCtrl.item(): + self._textCtrl.StopEditing() + + self._hasFocus = True + self.SetFocusIgnoringChildren() + + # If the item is already selected, do not update the selection. + # Multi-selections should not be cleared if a selected item is clicked. + if not self.IsSelected(item): + + self.DoSelectItem(item, True, False) + + nevent = TreeEvent(wxEVT_TREE_ITEM_RIGHT_CLICK, self.GetId()) + nevent._item = item + nevent._pointDrag = self.CalcScrolledPosition(pt) + nevent.SetEventObject(self) + event.Skip(not self.GetEventHandler().ProcessEvent(nevent)) + + # Consistent with MSW (for now), send the ITEM_MENU *after* + # the RIGHT_CLICK event. TODO: This behaviour may change. + nevent2 = TreeEvent(wxEVT_TREE_ITEM_MENU, self.GetId()) + nevent2._item = item + nevent2._pointDrag = self.CalcScrolledPosition(pt) + nevent2.SetEventObject(self) + self.GetEventHandler().ProcessEvent(nevent2) + + elif event.LeftUp(): + + # this facilitates multiple-item drag-and-drop + + if self.HasFlag(TR_MULTIPLE): + + selections = self.GetSelections() + + if len(selections) > 1 and not event.CmdDown() and not event.ShiftDown(): + + self.DoSelectItem(item, True, False) + + if self._lastOnSame: + + if item == self._current and (flags & TREE_HITTEST_ONITEMLABEL) and self.HasFlag(TR_EDIT_LABELS): + + if self._renameTimer: + + if self._renameTimer.IsRunning(): + + self._renameTimer.Stop() + + else: + + self._renameTimer = TreeRenameTimer(self) + + self._renameTimer.Start(_DELAY, True) + + self._lastOnSame = False + + + else: # !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick() + + if not item or not item.IsEnabled(): + if self._textCtrl != None and item != self._textCtrl.item(): + self._textCtrl.StopEditing() + return + + if self._textCtrl != None and item != self._textCtrl.item(): + self._textCtrl.StopEditing() + + self._hasFocus = True + self.SetFocusIgnoringChildren() + + if event.LeftDown(): + + self._lastOnSame = item == self._current + + if flags & TREE_HITTEST_ONITEMBUTTON: + + # only toggle the item for a single click, double click on + # the button doesn't do anything (it toggles the item twice) + if event.LeftDown(): + + self.Toggle(item) + + # don't select the item if the button was clicked + return + + if item.GetType() > 0 and (flags & TREE_HITTEST_ONITEMCHECKICON): + + if event.LeftDown(): + + self.CheckItem(item, not self.IsItemChecked(item)) + + return + + # clear the previously selected items, if the + # user clicked outside of the present selection. + # otherwise, perform the deselection on mouse-up. + # this allows multiple drag and drop to work. + # but if Cmd is down, toggle selection of the clicked item + if not self.IsSelected(item) or event.CmdDown(): + + if flags & TREE_HITTEST_ONITEM: + # how should the selection work for this event? + if item.IsHyperText(): + self.SetItemVisited(item, True) + + is_multiple, extended_select, unselect_others = EventFlagsToSelType(self.GetTreeStyle(), + event.ShiftDown(), + event.CmdDown()) + + self.DoSelectItem(item, unselect_others, extended_select) + + # For some reason, Windows isn't recognizing a left double-click, + # so we need to simulate it here. Allow 200 milliseconds for now. + if event.LeftDClick(): + + # double clicking should not start editing the item label + if self._renameTimer: + self._renameTimer.Stop() + + self._lastOnSame = False + + # send activate event first + nevent = TreeEvent(wxEVT_TREE_ITEM_ACTIVATED, self.GetId()) + nevent._item = item + nevent._pointDrag = self.CalcScrolledPosition(pt) + nevent.SetEventObject(self) + if not self.GetEventHandler().ProcessEvent(nevent): + + # if the user code didn't process the activate event, + # handle it ourselves by toggling the item when it is + # double clicked +## if item.HasPlus(): + self.Toggle(item) + + + def OnInternalIdle(self, event): + """Performs operations in idle time (essentially drawing).""" + + # Check if we need to select the root item + # because nothing else has been selected. + # Delaying it means that we can invoke event handlers + # as required, when a first item is selected. + if not self.HasFlag(TR_MULTIPLE) and not self.GetSelection(): + + if self._select_me: + self.SelectItem(self._select_me) + elif self.GetRootItem(): + self.SelectItem(self.GetRootItem()) + + # after all changes have been done to the tree control, + # we actually redraw the tree when everything is over + + if not self._dirty: + return + if self._freezeCount: + return + + self._dirty = False + + self.CalculatePositions() + self.Refresh() + self.AdjustMyScrollbars() + +# event.Skip() + + + def CalculateSize(self, item, dc): + """Calculates overall position and size of an item.""" + + attr = item.GetAttributes() + + if attr and attr.HasFont(): + dc.SetFont(attr.GetFont()) + elif item.IsBold(): + dc.SetFont(self._boldFont) + else: + dc.SetFont(self._normalFont) + + text_w, text_h, dummy = dc.GetMultiLineTextExtent(item.GetText()) + text_h+=2 + + # restore normal font + dc.SetFont(self._normalFont) + + image_w, image_h = 0, 0 + image = item.GetCurrentImage() + + if image != _NO_IMAGE: + + if self._imageListNormal: + + image_w, image_h = self._imageListNormal.GetSize(image) + image_w += 4 + + total_h = ((image_h > text_h) and [image_h] or [text_h])[0] + + checkimage = item.GetCurrentCheckedImage() + if checkimage is not None: + wcheck, hcheck = self._imageListCheck.GetSize(checkimage) + wcheck += 4 + else: + wcheck = 0 + + if total_h < 30: + total_h += 2 # at least 2 pixels + else: + total_h += total_h/10 # otherwise 10% extra spacing + + if total_h > self._lineHeight: + self._lineHeight = total_h + + if not item.GetWindow(): + item.SetWidth(image_w+text_w+wcheck+2) + item.SetHeight(total_h) + else: + item.SetWidth(item.GetWindowSize()[0]+image_w+text_w+wcheck+2) + + + def CalculateLevel(self, item, dc, level, y): + """Calculates the level of an item.""" + + x = level*self._indent + + if not self.HasFlag(TR_HIDE_ROOT): + + x += self._indent + + elif level == 0: + + # a hidden root is not evaluated, but its + # children are always calculated + children = item.GetChildren() + count = len(children) + level = level + 1 + for n in xrange(count): + y = self.CalculateLevel(children[n], dc, level, y) # recurse + + return y + + self.CalculateSize(item, dc) + + # set its position + item.SetX(x+self._spacing) + item.SetY(y) + y += self.GetLineHeight(item) + + if not item.IsExpanded(): + # we don't need to calculate collapsed branches + return y + + children = item.GetChildren() + count = len(children) + level = level + 1 + for n in xrange(count): + y = self.CalculateLevel(children[n], dc, level, y) # recurse + + return y + + + def CalculatePositions(self): + """Calculates all the positions of the visible items.""" + + if not self._anchor: + return + + dc = wx.ClientDC(self) + self.PrepareDC(dc) + + dc.SetFont(self._normalFont) + dc.SetPen(self._dottedPen) + y = 2 + y = self.CalculateLevel(self._anchor, dc, 0, y) # start recursion + + + def RefreshSubtree(self, item): + """Refreshes a damaged subtree of an item.""" + + if self._dirty: + return + if self._freezeCount: + return + + client = self.GetClientSize() + + rect = wx.Rect() + x, rect.y = self.CalcScrolledPosition(0, item.GetY()) + rect.width = client.x + rect.height = client.y + + self.Refresh(True, rect) + self.AdjustMyScrollbars() + + + def RefreshLine(self, item): + """Refreshes a damaged item line.""" + + if self._dirty: + return + if self._freezeCount: + return + + rect = wx.Rect() + x, rect.y = self.CalcScrolledPosition(0, item.GetY()) + rect.width = self.GetClientSize().x + rect.height = self.GetLineHeight(item) + + self.Refresh(True, rect) + + + def RefreshSelected(self): + """Refreshes a damaged selected item line.""" + + if self._freezeCount: + return + + # TODO: this is awfully inefficient, we should keep the list of all + # selected items internally, should be much faster + if self._anchor: + self.RefreshSelectedUnder(self._anchor) + + + def RefreshSelectedUnder(self, item): + """Refreshes the selected items under the given item.""" + + if self._freezeCount: + return + + if item.IsSelected(): + self.RefreshLine(item) + + children = item.GetChildren() + for child in children: + self.RefreshSelectedUnder(child) + + + def Freeze(self): + """Freeze CustomTreeCtrl.""" + + self._freezeCount = self._freezeCount + 1 + + + def Thaw(self): + """Thaw CustomTreeCtrl.""" + + if self._freezeCount == 0: + raise "\nERROR: Thawing Unfrozen Tree Control?" + + self._freezeCount = self._freezeCount - 1 + + if not self._freezeCount: + self.Refresh() + + + # ---------------------------------------------------------------------------- + # changing colours: we need to refresh the tree control + # ---------------------------------------------------------------------------- + + def SetBackgroundColour(self, colour): + """Changes the background colour of CustomTreeCtrl.""" + + if not wx.Window.SetBackgroundColour(self, colour): + return False + + if self._freezeCount: + return True + + self.Refresh() + + return True + + + def SetForegroundColour(self, colour): + """Changes the foreground colour of CustomTreeCtrl.""" + + if not wx.Window.SetForegroundColour(self, colour): + return False + + if self._freezeCount: + return True + + self.Refresh() + + return True + + + def OnGetToolTip(self, event): + """ + Process the tooltip event, to speed up event processing. Does not actually + get a tooltip. + """ + + event.Veto() + + + def DoGetBestSize(self): + """Something is better than nothing...""" + + # something is better than nothing... + # 100x80 is what the MSW version will get from the default + # wxControl::DoGetBestSize + + return wx.Size(100, 80) + + + def GetClassDefaultAttributes(self): + """Gets the class default attributes.""" + + attr = wx.VisualAttributes() + attr.colFg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOWTEXT) + attr.colBg = wx.SystemSettings_GetColour(wx.SYS_COLOUR_LISTBOX) + attr.font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + return attr + +