From 6cb4f153c300067e21eac198e4b57b47db9b9767 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Thu, 5 Oct 2006 22:24:18 +0000 Subject: [PATCH] Add FlatNotebook git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41643 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- wxPython/demo/FlatNotebook.py | 693 ++++ wxPython/demo/Main.py | 2 + wxPython/demo/bmp_source/book_blue.png | Bin 0 -> 833 bytes wxPython/demo/bmp_source/book_green.png | Bin 0 -> 840 bytes wxPython/demo/bmp_source/book_red.png | Bin 0 -> 847 bytes wxPython/demo/encode_bitmaps.py | 4 + wxPython/demo/images.py | 117 + wxPython/docs/CHANGES.txt | 3 + wxPython/wx/lib/flatnotebook.py | 4068 +++++++++++++++++++++++ 9 files changed, 4887 insertions(+) create mode 100644 wxPython/demo/FlatNotebook.py create mode 100644 wxPython/demo/bmp_source/book_blue.png create mode 100644 wxPython/demo/bmp_source/book_green.png create mode 100644 wxPython/demo/bmp_source/book_red.png create mode 100644 wxPython/wx/lib/flatnotebook.py diff --git a/wxPython/demo/FlatNotebook.py b/wxPython/demo/FlatNotebook.py new file mode 100644 index 0000000000..d90daf97b2 --- /dev/null +++ b/wxPython/demo/FlatNotebook.py @@ -0,0 +1,693 @@ + +import wx +import wx.lib.flatnotebook as fnb +import random +import images + +#---------------------------------------------------------------------- +def GetMondrianData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\ +\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\ +ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\ +o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\ +\xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\ +\x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\ +\x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82' + + +def GetMondrianBitmap(): + return wx.BitmapFromImage(GetMondrianImage()) + + +def GetMondrianImage(): + import cStringIO + stream = cStringIO.StringIO(GetMondrianData()) + return wx.ImageFromStream(stream) + + +def GetMondrianIcon(): + icon = wx.EmptyIcon() + icon.CopyFromBitmap(GetMondrianBitmap()) + return icon +#---------------------------------------------------------------------- + + +MENU_EDIT_DELETE_ALL = wx.ID_HIGHEST + 1000 +MENU_EDIT_ADD_PAGE = MENU_EDIT_DELETE_ALL + 1 +MENU_EDIT_DELETE_PAGE = MENU_EDIT_DELETE_ALL + 2 +MENU_EDIT_SET_SELECTION = MENU_EDIT_DELETE_ALL + 3 +MENU_EDIT_ADVANCE_SELECTION_FWD = MENU_EDIT_DELETE_ALL + 4 +MENU_EDIT_ADVANCE_SELECTION_BACK = MENU_EDIT_DELETE_ALL + 5 +MENU_SET_ALL_TABS_SHAPE_ANGLE = MENU_EDIT_DELETE_ALL + 6 +MENU_SHOW_IMAGES = MENU_EDIT_DELETE_ALL + 7 +MENU_USE_VC71_STYLE = MENU_EDIT_DELETE_ALL + 8 +MENU_USE_DEFAULT_STYLE = MENU_EDIT_DELETE_ALL + 9 +MENU_USE_FANCY_STYLE = MENU_EDIT_DELETE_ALL + 10 +MENU_SELECT_GRADIENT_COLOR_FROM = MENU_EDIT_DELETE_ALL + 11 +MENU_SELECT_GRADIENT_COLOR_TO = MENU_EDIT_DELETE_ALL + 12 +MENU_SELECT_GRADIENT_COLOR_BORDER = MENU_EDIT_DELETE_ALL + 13 +MENU_SET_PAGE_IMAGE_INDEX = MENU_EDIT_DELETE_ALL + 14 +MENU_HIDE_X = MENU_EDIT_DELETE_ALL + 15 +MENU_HIDE_NAV_BUTTONS = MENU_EDIT_DELETE_ALL + 16 +MENU_USE_MOUSE_MIDDLE_BTN = MENU_EDIT_DELETE_ALL + 17 +MENU_DRAW_BORDER = MENU_EDIT_DELETE_ALL + 18 +MENU_USE_BOTTOM_TABS = MENU_EDIT_DELETE_ALL + 19 +MENU_ENABLE_TAB = MENU_EDIT_DELETE_ALL + 20 +MENU_DISABLE_TAB = MENU_EDIT_DELETE_ALL + 21 +MENU_ENABLE_DRAG_N_DROP = MENU_EDIT_DELETE_ALL + 22 +MENU_DCLICK_CLOSES_TAB = MENU_EDIT_DELETE_ALL + 23 +MENU_USE_VC8_STYLE = MENU_EDIT_DELETE_ALL + 24 + +MENU_SET_ACTIVE_TEXT_COLOR = MENU_EDIT_DELETE_ALL + 27 +MENU_DRAW_TAB_X = MENU_EDIT_DELETE_ALL + 28 +MENU_SET_ACTIVE_TAB_COLOR = MENU_EDIT_DELETE_ALL + 29 +MENU_SET_TAB_AREA_COLOR = MENU_EDIT_DELETE_ALL + 30 +MENU_SELECT_NONACTIVE_TEXT_COLOR = MENU_EDIT_DELETE_ALL + 31 +MENU_GRADIENT_BACKGROUND = MENU_EDIT_DELETE_ALL + 32 +MENU_COLORFUL_TABS = MENU_EDIT_DELETE_ALL + 33 + + +class FlatNotebookDemo(wx.Frame): + + def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, size=(800, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.MAXIMIZE |wx.NO_FULL_REPAINT_ON_RESIZE): + + wx.Frame.__init__(self, parent, id, title, pos, size, style) + + self._bShowImages = False + self._bVCStyle = False + self._newPageCounter = 0 + + self._ImageList = wx.ImageList(16, 16) + self._ImageList.Add(images.get_book_redBitmap()) + self._ImageList.Add(images.get_book_greenBitmap()) + self._ImageList.Add(images.get_book_blueBitmap()) + + self.statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP) + self.statusbar.SetStatusWidths([-2, -1]) + # statusbar fields + statusbar_fields = [("FlatNotebook wxPython Demo, Andrea Gavana @ 02 Oct 2006"), + ("Welcome To wxPython!")] + + for i in range(len(statusbar_fields)): + self.statusbar.SetStatusText(statusbar_fields[i], i) + + self.SetIcon(GetMondrianIcon()) + self.CreateMenuBar() + self.CreateRightClickMenu() + self.LayoutItems() + + self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging) + self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged) + self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnPageClosing) + + + + def CreateMenuBar(self): + + self._menuBar = wx.MenuBar(wx.MB_DOCKABLE) + self._fileMenu = wx.Menu() + item = wx.MenuItem(self._fileMenu, wx.ID_ANY, "&Close\tCtrl-Q", "Close demo window") + self.Bind(wx.EVT_MENU, self.OnQuit, item) + self._fileMenu.AppendItem(item) + + self._editMenu = wx.Menu() + item = wx.MenuItem(self._editMenu, MENU_EDIT_ADD_PAGE, "New Page\tCtrl+N", "Add New Page") + self.Bind(wx.EVT_MENU, self.OnAddPage, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_EDIT_DELETE_PAGE, "Delete Page\tCtrl+F4", "Delete Page") + self.Bind(wx.EVT_MENU, self.OnDeletePage, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_EDIT_DELETE_ALL, "Delete All Pages", "Delete All Pages") + self.Bind(wx.EVT_MENU, self.OnDeleteAll, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_EDIT_SET_SELECTION, "Set Selection", "Set Selection") + self.Bind(wx.EVT_MENU, self.OnSetSelection, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_EDIT_ADVANCE_SELECTION_FWD, "Advance Selection Forward", + "Advance Selection Forward") + self.Bind(wx.EVT_MENU, self.OnAdvanceSelectionFwd, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_EDIT_ADVANCE_SELECTION_BACK, "Advance Selection Backward", + "Advance Selection Backward") + self.Bind(wx.EVT_MENU, self.OnAdvanceSelectionBack, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_SET_ALL_TABS_SHAPE_ANGLE, "Set an inclination of tab header borders", + "Set the shape of tab header") + self.Bind(wx.EVT_MENU, self.OnSetAllPagesShapeAngle, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_SET_PAGE_IMAGE_INDEX, "Set image index of selected page", + "Set image index") + self.Bind(wx.EVT_MENU, self.OnSetPageImageIndex, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_SHOW_IMAGES, "Show Images", "Show Images", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnShowImages, item) + self._editMenu.AppendItem(item) + + styleMenu = wx.Menu() + item = wx.MenuItem(styleMenu, MENU_USE_DEFAULT_STYLE, "Use Default Style", "Use VC71 Style", wx.ITEM_RADIO) + self.Bind(wx.EVT_MENU, self.OnDefaultStyle, item) + styleMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_USE_VC71_STYLE, "Use VC71 Style", "Use VC71 Style", wx.ITEM_RADIO) + self.Bind(wx.EVT_MENU, self.OnVC71Style, item) + styleMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_USE_VC8_STYLE, "Use VC8 Style", "Use VC8 Style", wx.ITEM_RADIO) + self.Bind(wx.EVT_MENU, self.OnVC8Style, item) + styleMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_USE_FANCY_STYLE, "Use Fancy Style", "Use Fancy Style", wx.ITEM_RADIO) + self.Bind(wx.EVT_MENU, self.OnFancyStyle, item) + styleMenu.AppendItem(item) + + self._editMenu.AppendMenu(wx.ID_ANY, "Tabs Style", styleMenu) + + item = wx.MenuItem(styleMenu, MENU_SELECT_GRADIENT_COLOR_FROM, "Select fancy tab style 'from' color", + "Select fancy tab style 'from' color") + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SELECT_GRADIENT_COLOR_TO, "Select fancy tab style 'to' color", + "Select fancy tab style 'to' color") + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SELECT_GRADIENT_COLOR_BORDER, "Select fancy tab style 'border' color", + "Select fancy tab style 'border' color") + self._editMenu.AppendItem(item) + + self.Bind(wx.EVT_MENU_RANGE, self.OnSelectColor, id=MENU_SELECT_GRADIENT_COLOR_FROM, + id2=MENU_SELECT_GRADIENT_COLOR_BORDER) + + item = wx.MenuItem(self._editMenu, MENU_HIDE_NAV_BUTTONS, "Hide Navigation Buttons", + "Hide Navigation Buttons", wx.ITEM_CHECK) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_HIDE_X, "Hide X Button", "Hide X Button", wx.ITEM_CHECK) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_USE_MOUSE_MIDDLE_BTN, "Use Mouse Middle Button as 'X' button", + "Use Mouse Middle Button as 'X' button", wx.ITEM_CHECK) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_USE_BOTTOM_TABS, "Use Bottoms Tabs", "Use Bottoms Tabs", + wx.ITEM_CHECK) + self._editMenu.AppendItem(item) + + self.Bind(wx.EVT_MENU_RANGE, self.OnStyle, id=MENU_HIDE_X, id2=MENU_USE_BOTTOM_TABS) + + item = wx.MenuItem(self._editMenu, MENU_ENABLE_TAB, "Enable Tab", "Enable Tab") + self.Bind(wx.EVT_MENU, self.OnEnableTab, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_DISABLE_TAB, "Disable Tab", "Disable Tab") + self.Bind(wx.EVT_MENU, self.OnDisableTab, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_ENABLE_DRAG_N_DROP, "Enable Drag And Drop of Tabs", + "Enable Drag And Drop of Tabs", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnEnableDrag, item) + self._editMenu.AppendItem(item) + item.Check(True) + + item = wx.MenuItem(self._editMenu, MENU_DRAW_BORDER, "Draw Border around tab area", + "Draw Border around tab area", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnStyle, item) + self._editMenu.AppendItem(item) + item.Check(True) + + item = wx.MenuItem(self._editMenu, MENU_DRAW_TAB_X, "Draw X button On Active Tab", + "Draw X button On Active Tab", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnDrawTabX, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SET_ACTIVE_TAB_COLOR, "Select Active Tab Color", + "Select Active Tab Color") + self.Bind(wx.EVT_MENU, self.OnSelectColor, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SET_TAB_AREA_COLOR, "Select Tab Area Color", + "Select Tab Area Color") + self.Bind(wx.EVT_MENU, self.OnSelectColor, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SET_ACTIVE_TEXT_COLOR, "Select active tab text color", + "Select active tab text color") + self.Bind(wx.EVT_MENU, self.OnSelectColor, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(styleMenu, MENU_SELECT_NONACTIVE_TEXT_COLOR, + "Select NON-active tab text color", "Select NON-active tab text color") + self.Bind(wx.EVT_MENU, self.OnSelectColor, item) + self._editMenu.AppendItem(item) + + item = wx.MenuItem(self._editMenu, MENU_DCLICK_CLOSES_TAB, "Mouse double click closes tab", + "Mouse double click closes tab", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnDClickCloseTab, item) + self._editMenu.AppendItem(item) + item.Check(False) + + item = wx.MenuItem(self._editMenu, MENU_GRADIENT_BACKGROUND, "Use Gradient Coloring for tab area", + "Use Gradient Coloring for tab area", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnGradientBack, item) + self._editMenu.AppendItem(item) + item.Check(False) + + item = wx.MenuItem(self._editMenu, MENU_COLORFUL_TABS, "Colorful tabs", "Colorful tabs", wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.OnColorfulTabs, item) + self._editMenu.AppendItem(item) + item.Check(False) + + help_menu = wx.Menu() + item = wx.MenuItem(help_menu, wx.ID_ANY, "About...", "Shows The About Dialog") + self.Bind(wx.EVT_MENU, self.OnAbout, item) + help_menu.AppendItem(item) + + self._menuBar.Append(self._fileMenu, "&File") + self._menuBar.Append(self._editMenu, "&Edit") + self._menuBar.Append(help_menu, "&Help") + + self.SetMenuBar(self._menuBar) + + + def CreateRightClickMenu(self): + + self._rmenu = wx.Menu() + item = wx.MenuItem(self._rmenu, MENU_EDIT_DELETE_PAGE, "Close Tab\tCtrl+F4", "Close Tab") + self._rmenu.AppendItem(item) + + + def LayoutItems(self): + + mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(mainSizer) + + bookStyle = fnb.FNB_TABS_BORDER_SIMPLE + + self.book = fnb.StyledNotebook(self, wx.ID_ANY, style=bookStyle) + self.secondBook = fnb.StyledNotebook(self, wx.ID_ANY, style=bookStyle) + + # Set right click menu to the notebook + self.book.SetRightClickMenu(self._rmenu) + + # Set the image list + self.book.SetImageList(self._ImageList) + mainSizer.Add(self.book, 6, wx.EXPAND) + + # Add spacer between the books + spacer = wx.Panel(self, -1) + spacer.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)) + mainSizer.Add(spacer, 0, wx.ALL | wx.EXPAND) + + mainSizer.Add(self.secondBook, 2, wx.EXPAND) + + # Add some pages to the second notebook + self.Freeze() + + text = wx.TextCtrl(self.secondBook, -1, "Second Book Page 1", style=wx.TE_MULTILINE) + self.secondBook.AddPage(text, "Second Book Page 1") + + text = wx.TextCtrl(self.secondBook, -1, "Second Book Page 2", style=wx.TE_MULTILINE) + self.secondBook.AddPage(text, "Second Book Page 2") + + self.Thaw() + + self.Centre() + mainSizer.Layout() + self.SendSizeEvent() + + + def OnStyle(self, event): + + style = self.book.GetWindowStyleFlag() + eventid = event.GetId() + + if eventid == MENU_HIDE_NAV_BUTTONS: + if event.IsChecked(): + # Hide the navigation buttons + style |= fnb.FNB_NO_NAV_BUTTONS + else: + if style & fnb.FNB_NO_NAV_BUTTONS: + style ^= fnb.FNB_NO_NAV_BUTTONS + + self.book.SetWindowStyleFlag(style) + + elif eventid == MENU_HIDE_X: + if event.IsChecked(): + # Hide the X button + style |= fnb.FNB_NO_X_BUTTON + else: + if style & fnb.FNB_NO_X_BUTTON: + style ^= fnb.FNB_NO_X_BUTTON + + self.book.SetWindowStyleFlag(style) + + elif eventid == MENU_DRAW_BORDER: + if event.IsChecked(): + style |= fnb.FNB_TABS_BORDER_SIMPLE + else: + if style & fnb.FNB_TABS_BORDER_SIMPLE: + style ^= fnb.FNB_TABS_BORDER_SIMPLE + + self.book.SetWindowStyleFlag(style) + + elif eventid == MENU_USE_MOUSE_MIDDLE_BTN: + if event.IsChecked(): + style |= fnb.FNB_MOUSE_MIDDLE_CLOSES_TABS + else: + if style & fnb.FNB_MOUSE_MIDDLE_CLOSES_TABS: + style ^= fnb.FNB_MOUSE_MIDDLE_CLOSES_TABS + + self.book.SetWindowStyleFlag(style) + + elif eventid == MENU_USE_BOTTOM_TABS: + if event.IsChecked(): + style |= fnb.FNB_BOTTOM + else: + if style & fnb.FNB_BOTTOM: + style ^= fnb.FNB_BOTTOM + + self.book.SetWindowStyleFlag(style) + self.book.Refresh() + + + def OnQuit(self, event): + + self.Destroy() + + + def OnDeleteAll(self, event): + + self.book.DeleteAllPages() + + + def OnShowImages(self, event): + + self._bShowImages = event.IsChecked() + + + def OnVC71Style(self, event): + + style = self.book.GetWindowStyleFlag() + + # remove old tabs style + mirror = ~(fnb.FNB_VC71 | fnb.FNB_VC8 | fnb.FNB_FANCY_TABS) + style &= mirror + + style |= fnb.FNB_VC71 + + self.book.SetWindowStyleFlag(style) + + + def OnVC8Style(self, event): + + style = self.book.GetWindowStyleFlag() + + # remove old tabs style + mirror = ~(fnb.FNB_VC71 | fnb.FNB_VC8 | fnb.FNB_FANCY_TABS) + style &= mirror + + # set new style + style |= fnb.FNB_VC8 + + self.book.SetWindowStyleFlag(style) + + + def OnDefaultStyle(self, event): + + style = self.book.GetWindowStyleFlag() + + # remove old tabs style + mirror = ~(fnb.FNB_VC71 | fnb.FNB_VC8 | fnb.FNB_FANCY_TABS) + style &= mirror + + self.book.SetWindowStyleFlag(style) + + + def OnFancyStyle(self, event): + + style = self.book.GetWindowStyleFlag() + + # remove old tabs style + mirror = ~(fnb.FNB_VC71 | fnb.FNB_VC8 | fnb.FNB_FANCY_TABS) + style &= mirror + + style |= fnb.FNB_FANCY_TABS + self.book.SetWindowStyleFlag(style) + + + def OnSelectColor(self, event): + + eventid = event.GetId() + + # Open a color dialog + data = wx.ColourData() + + dlg = wx.ColourDialog(self, data) + + if dlg.ShowModal() == wx.ID_OK: + + if eventid == MENU_SELECT_GRADIENT_COLOR_BORDER: + self.book.SetGradientColorBorder(dlg.GetColourData().GetColour()) + elif eventid == MENU_SELECT_GRADIENT_COLOR_FROM: + self.book.SetGradientColorFrom(dlg.GetColourData().GetColour()) + elif eventid == MENU_SELECT_GRADIENT_COLOR_TO: + self.book.SetGradientColorTo(dlg.GetColourData().GetColour()) + elif eventid == MENU_SET_ACTIVE_TEXT_COLOR: + self.book.SetActiveTabTextColour(dlg.GetColourData().GetColour()) + elif eventid == MENU_SELECT_NONACTIVE_TEXT_COLOR: + self.book.SetNonActiveTabTextColour(dlg.GetColourData().GetColour()) + elif eventid == MENU_SET_ACTIVE_TAB_COLOR: + self.book.SetActiveTabColour(dlg.GetColourData().GetColour()) + elif eventid == MENU_SET_TAB_AREA_COLOR: + self.book.SetTabAreaColour(dlg.GetColourData().GetColour()) + + self.book.Refresh() + + + def OnAddPage(self, event): + + caption = "New Page Added #" + str(self._newPageCounter) + + self.Freeze() + + image = -1 + if self._bShowImages: + image = random.randint(0, self._ImageList.GetImageCount()-1) + + self.book.AddPage(self.CreatePage(caption), caption, True, image) + self.book.SetSelection(self.book.GetPageCount()-1) + self.Thaw() + self._newPageCounter = self._newPageCounter + 1 + + + def CreatePage(self, caption): + + return wx.TextCtrl(self.book, -1, caption, wx.DefaultPosition, self.book.GetPageBestSize(), + wx.TE_MULTILINE) + + + def OnDeletePage(self, event): + + self.book.DeletePage(self.book.GetSelection()) + + + def OnSetSelection(self, event): + + dlg = wx.TextEntryDialog(self, "Enter Tab Number to select:", "Set Selection") + + if dlg.ShowModal() == wx.ID_OK: + + val = dlg.GetValue() + self.book.SetSelection(int(val)) + + + def OnEnableTab(self, event): + + dlg = wx.TextEntryDialog(self, "Enter Tab Number to enable:", "Enable Tab") + + if dlg.ShowModal() == wx.ID_OK: + + val = dlg.GetValue() + self.book.Enable(int(val)) + + + def OnDisableTab(self, event): + + dlg = wx.TextEntryDialog(self, "Enter Tab Number to disable:", "Disable Tab") + + if dlg.ShowModal() == wx.ID_OK: + + val = dlg.GetValue() + self.book.Enable(int(val), False) + + + def OnEnableDrag(self, event): + + style = self.book.GetWindowStyleFlag() + + if event.IsChecked(): + if style & fnb.FNB_NODRAG: + style ^= fnb.FNB_NODRAG + else: + style |= fnb.FNB_NODRAG + + self.book.SetWindowStyleFlag(style) + + + def OnSetAllPagesShapeAngle(self, event): + + + dlg = wx.TextEntryDialog(self, "Enter an inclination of header borders (0-45):", "Set Angle") + if dlg.ShowModal() == wx.ID_OK: + + val = dlg.GetValue() + self.book.SetAllPagesShapeAngle(int(val)) + + + def OnSetPageImageIndex(self, event): + + dlg = wx.TextEntryDialog(self, "Enter an image index (0-%i):"%(self.book.GetImageList().GetImageCount()-1), "Set Image Index") + if dlg.ShowModal() == wx.ID_OK: + val = dlg.GetValue() + self.book.SetPageImageIndex(self.book.GetSelection(), int(val)) + + + def OnAdvanceSelectionFwd(self, event): + + self.book.AdvanceSelection(True) + + + def OnAdvanceSelectionBack(self, event): + + self.book.AdvanceSelection(False) + + + def OnPageChanging(self, event): + + print "Page Changing From", event.GetOldSelection(), "To", event.GetSelection() + event.Skip() + + + def OnPageChanged(self, event): + + print "Page Changed To", event.GetSelection() + event.Skip() + + + def OnPageClosing(self, event): + + print "Page Closing, Selection:", event.GetSelection() + event.Skip() + + + def OnDrawTabX(self, event): + + style = self.book.GetWindowStyleFlag() + if event.IsChecked(): + style |= fnb.FNB_X_ON_TAB + else: + if style & fnb.FNB_X_ON_TAB: + style ^= fnb.FNB_X_ON_TAB + + self.book.SetWindowStyleFlag(style) + + + def OnDClickCloseTab(self, event): + + style = self.book.GetWindowStyleFlag() + if event.IsChecked(): + style |= fnb.FNB_DCLICK_CLOSES_TABS + else: + style &= ~(fnb.FNB_DCLICK_CLOSES_TABS) + + self.book.SetWindowStyleFlag(style) + + + def OnGradientBack(self, event): + + style = self.book.GetWindowStyleFlag() + if event.IsChecked(): + style |= fnb.FNB_BACKGROUND_GRADIENT + else: + style &= ~(fnb.FNB_BACKGROUND_GRADIENT) + + self.book.SetWindowStyleFlag(style) + self.book.Refresh() + + + def OnColorfulTabs(self, event): + + style = self.book.GetWindowStyleFlag() + if event.IsChecked(): + style |= fnb.FNB_COLORFUL_TABS + else: + style &= ~(fnb.FNB_COLORFUL_TABS) + + self.book.SetWindowStyleFlag(style) + self.book.Refresh() + + + def OnAbout(self, event): + + msg = "This Is The About Dialog Of The FlatNotebook Demo.\n\n" + \ + "Author: Andrea Gavana @ 02 Oct 2006\n\n" + \ + "Please Report Any Bug/Requests Of Improvements\n" + \ + "To Me At The Following Adresses:\n\n" + \ + "andrea.gavana@gmail.com\n" + "gavana@kpo.kz\n\n" + \ + "Based On Eran C++ Implementation (wxWidgets Forum).\n\n" + \ + "Welcome To wxPython " + wx.VERSION_STRING + "!!" + + dlg = wx.MessageDialog(self, msg, "FlatNotebook wxPython Demo", + wx.OK | wx.ICON_INFORMATION) + dlg.ShowModal() + dlg.Destroy() + + +#--------------------------------------------------------------------------- + + +class TestPanel(wx.Panel): + def __init__(self, parent, log): + self.log = log + wx.Panel.__init__(self, parent, -1) + + b = wx.Button(self, -1, " Test ButtonPanel ", (50,50)) + self.Bind(wx.EVT_BUTTON, self.OnButton, b) + + + def OnButton(self, evt): + self.win = FlatNotebookDemo(self, title="FlatNotebook Demo") + self.win.Show(True) + +#---------------------------------------------------------------------- + +def runTest(frame, nb, log): + win = TestPanel(nb, log) + return win + +#---------------------------------------------------------------------- + + + +overview = """ +

Say something nice here

+ + +""" + + + +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 f45ed72242..fa46693622 100644 --- a/wxPython/demo/Main.py +++ b/wxPython/demo/Main.py @@ -64,6 +64,7 @@ _treeList = [ 'DelayedResult', 'ExpandoTextCtrl', 'ButtonPanel', + 'FlatNotebook', ]), # managed windows == things with a (optional) caption you can close @@ -151,6 +152,7 @@ _treeList = [ 'ColourSelect', 'ComboTreeBox', 'Editor', + 'FlatNotebook', 'GenericButtons', 'GenericDirCtrl', 'LEDNumberCtrl', diff --git a/wxPython/demo/bmp_source/book_blue.png b/wxPython/demo/bmp_source/book_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..f20eb1ff95597226e6bf7543d46b9739d74cd34a GIT binary patch literal 833 zcmV-H1HSx;P)WdKuQAUPmLWnv&QFfcM8F*Z6dF*-FdAS*C2FfftZS~&m!00(qQO+^RP z2L}inFJ^eRO8@`>32;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yP*__>q!NJ<4H_>O8~k;EhKlz7l6U7U~O!cSk-E* z4fdvlF8Z$)H`OuH@*Ki4Wu~%`CYf76)&Me@pYORG3%>RQwB$VG*)SPsET$d~V&qeQ zoX&LC4i86=LBWOv5uDBfGCkvzFp@x9r6w>;ww2&oQW3XZN@(~I1{=;LgPnHI&@jRD z7pw-zDeQBt<9L;VlR+Cg8wL1$6{`Q)dk1k&tB2fnM9n;%2KtD}o@ zv>#;Qc8ieQZir41o)QVFuwOvc8C1Fc6T~1qHm7D*KjVEimil@`Z9yf zku@l@228`6gGG{JalenUr=uF>7kgpQ$0r8nq0Xd{k*!i!NDWdKuQAUPmLWnv&QFfcM8F*Z6dF*-FdAS*C2FfftZS~&m!00(qQO+^RP z2L}inF`RdbC;$Ke32;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yPSy7Dhj7yboq+!$Bx zje8BqN=O8J1Ok!(no21mQ2T(=X{ViOr{fti7{rL)Zd?H37Rel-NUbjBkJkDB(f(?ao9wILynp=VxsBTX}E@2VN z#O3!*xO}^Md9b%RG#U90uh)vT_&Q>81QYR5OeDuJmzjg2>0m7^WASm_(yo%kD!vjc z8^LmlS4*>QeYmZqmRX4vA&FZ^m6A|&jRN9n8y4k?4y9ZMYt`9uW4TXV*P|3^j0_4a zh4^-QYGYK;sYuJi#?%nNP_FdOXjGwKr?QJ^B7@b1EKcV;tmJ%_-~frQGFnmQ%a6tb zvGBk^z%o9c0C!x$W^xBR3Cd5ZLucg@E_(XWcd8dn4UKZ>en=orB(YU{VZ6cYmq6-y zsM&F6>-{!@{7Kw$^`pC{3vRy)cBdUSn+TWF4c1+;qKE$N0}2LPWu8V+TgKy8W6`0Z zwVRH<_Fja1LD=mMlsE++i<6j%zr$2&valJ;ESLl6CJFlnI;LqzTJTI2i}#k)MU1YD zU{;=~WVfXVqWFvzY*TP03dfj(Z{|_zzmIYVKAgi#YEHruVVg8{#l2ROqz%$KDNf3m z1C={yU@Qx%nbc3JHUR@n8^z|?7fIUwJ?cjO?UpkCQ}`1WpbY3s SM#;|r0000WdKuQAUPmLWnv&QFfcM8F*Z6dF*-FdAS*C2FfftZS~&m!00(qQO+^RP z2L}inG6jCyk^lez32;bRa{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yPK4*ZT*5(O<-0T+FK`t$ zK+^yV2b4sNJezrPDym(Tf!Xa4_FAWR%v9??Vd*^3#HyOLR2P6!8-Ut=3~I0sCb@`a zk_-4&)6NGXCa5PvrTqd7dd-8J%TZBM7_d?n*-$@7)ou`51x%^}!dZcp2-@A|Hghzi zyi_qwefdoYd(k5GR=4)ZOPfXaKIU9M?)>4oD){fpVc2)=&?;{(AB0-bUIU*r|vC(1s*Tw$0~< zPx|7QUoGU3^tK}1eFk!82u?wO)8Rm+-45O%z{BdcK@Ti9;}8w6soag?-F4imdl4VF zbNsa1*L4KV4SrNPU2roc1eQOddgeWZ@M|#~UH(MW4Ua&@Z36UR8cJUtw;S~0P%slg z_vjl`PLAuj$l~n1HGFxbAQe`j$~x9*{bIS5`fpb`2<*9H!$X>YG%RB7GkwxqQ>1%w zs-?14s0>J!Dz_D2EHhOTRWB94#gsCN%e^;%>gI552N=sp*li?SHW*>aJ5A)@rj+@g Z!XJu+2<}RQdhP%K002ovPDHLkV1gy)Zax42 literal 0 HcmV?d00001 diff --git a/wxPython/demo/encode_bitmaps.py b/wxPython/demo/encode_bitmaps.py index 6574d39229..d2b5402c36 100644 --- a/wxPython/demo/encode_bitmaps.py +++ b/wxPython/demo/encode_bitmaps.py @@ -96,6 +96,10 @@ command_lines = [ "-a -u -n _bp_btn3 bmp_source/bp_btn3.png images.py", "-a -u -n _bp_btn4 bmp_source/bp_btn4.png images.py", + "-a -u -n _book_red bmp_source/book_red.png images.py", + "-a -u -n _book_green bmp_source/book_green.png images.py", + "-a -u -n _book_blue bmp_source/book_blue.png images.py", + " -u -c bmp_source/001.png throbImages.py", "-a -u -c bmp_source/002.png throbImages.py", "-a -u -c bmp_source/003.png throbImages.py", diff --git a/wxPython/demo/images.py b/wxPython/demo/images.py index 5e852ddb55..86e033f4a8 100644 --- a/wxPython/demo/images.py +++ b/wxPython/demo/images.py @@ -13172,3 +13172,120 @@ def get_bp_btn4Image(): stream = cStringIO.StringIO(get_bp_btn4Data()) return ImageFromStream(stream) +#---------------------------------------------------------------------- +def get_book_redData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ +\x00\x02\x93IDAT8\x8d\x85\x93IL\x13\x01\x14\x86\xbf\x99i;]hiA\x10\xa4\xac\ +\x01!F\xa0\x88V0\xe8\x81\x8b&x0&xr\xbb\xe8\xd9\x03^8\x18\xaf&\xc6\xab\x07o\\\ +\x8d1j\x0c\xd1\xb8DM\x10\x8d\xa0\x18\x16\x03H*K(e\x11\xbaMg\xa63\xe3\xc1bPP\ +\xff\xe4%\xef\xf0\xde\x97\xbc\xf7\xbf\'\xf0o\t[r\xeb\x7f\x05\xc2u\x10\xba\ +\x83\xd4\x9b\x1aa\xc1\xcd!\xf7\xae@kZQ\xb5Kc\xe9\xf3o!\n\xa8\xdb\x00J\x88\ +\xa3\x96C\xec\x12\xf3\xfd\x07E\xa7\xbb\xd5t\xb9\xfdr\x9e\x0bL\x13\x1c2d\x15\ +\xfa\x9fM<95k^\xd5`\xf2O\x88t\xad%8h+\xad<\xe1h9P#\x19\xa63[V\x8b^Z\x8b\xd1t\ +\x0c#\xd4\x89M\xcbP\'&k\xcb\x97\xd6}\x0f\x14F\x808`\xfe\x02\xf4\x04\xed=r\ +\xbe7/\xab\x0bHj\x82D\xb8\x0b\xb1p7\xa2+\x0f\x9b\xa1#\xcaNX\x9c\xa2)`o\xb4\ +\xcf\xc6\xf5\x97\x06\xe3@j\x13"]t\xa8\x1dE\xf5\xe5\r\x89\xaf\xb38KK\xb0E#H\ +\xd1o\xc8\xc3\xcf\x11\x07\xfba\xfc\x03\xe8*\x82\xc3&\x84\xf2\xed\xe1\xb5\xf9\ +\xf4\xf2\x90\xc54\x90\x06,\xa9M\xb7\x96\xf7\xc9\xda9W\xb0XH\xac\xa5qgV\x91V\ +\x97@\xd7@\x94~\xee\xd94\xc1\xd0\x91=\x82xX\xca\xb6\x8f-\x1b3\xd3\x10\x01\ +\x14\xf1l\x9cw\xe3S+\xf7\x05K%\xb3\xf2\x1d<\x01\xb0L0\x0c0t\xc8*\x90Z\'9\xb9\ +\xc4\xd8@\x8c\x91\x92f\xb7!\x8a\xfb\x81j\xc0a\x03\x94\xc7\xcb\xdc\xa8\x18\ +\x9d\xef*n\xaev\xaeGb\xf8\xbd\x1a,\xae\x10\x8d\x1a\x8c\x17\x941S\xd9\x8e~\ +\xa1\x03g\xb8\x9d`e\x85\xf2to\xb5\x0cx\x00\xd1\x06\x98\xbd*\xa3\x8d3\xa9\xbe\ +\x93\xe5\xb1\xcb\xaf")V\x82\r\xac\xb7\x9dAk\t\xe3\xf3\xfb\xf1y\xbd\x14x<\xb8\ +\xbd~\x8a%\xcb\xd5S\x8c\xe3f\x0c\x050m972w7\xb8U\xf6"\xd6}\xe7\xf8\xe9@y\xeb\ +\x11\xea\xaa*)\xf4\xe5\x13\x10\xb2\xd4.|\xa4h\xe2=\xfe\xa1\xd7\xca\xd0\xec\ +\xc2\'\xd1\x00\xc0\x0f\xc8\x9b\x00\xab\x0f"\x9d)nWY\x99\xdeP|\x8a\xb6\x817\ +\x14\x8c\x0e[\xc9\xa9\xb9\xc9{k|y\xa4\xb3:d`h\x16\x1b\xc0\\\xceJ\xeb\xb7S\ +\x0e\xc1\x9e+.\x1ej\xa0\xf6)L\x7f\x065\x0e\x19 \x06\xcc\xe7b\x11\xd8\x00\x12\ +@r+\x00\xc0\x0e\xd4\x00\x1d\x80{K\xd3*\x90\xccy\x9f\x01\x0cr\xcf\xf5\'\x00@\ +\x06\x02\x80\x98kP\x00ms\xd4\x1d\xeaw\x94\xf0\x17\xf86\xfd\x00\x8a\x84\x08\ +\xee\xfb\x9c!f\x00\x00\x00\x00IEND\xaeB`\x82' + +def get_book_redBitmap(): + return BitmapFromImage(get_book_redImage()) + +def get_book_redImage(): + stream = cStringIO.StringIO(get_book_redData()) + return ImageFromStream(stream) + +#---------------------------------------------------------------------- +def get_book_greenData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ +\x00\x02\x86IDAT8\x8d\x8d\x93KO\x13\x01\x14\x85\xbf\x99\xd2\x16;}\xd2\xa0\ +\x04\xca\xa3\x14\x14\x8d\x90\xa0<\x95\x88\xba\xc0\x84\xc4\xc4\x10W\xb8\xd0\ +\x95\xbf@]\xf8\x0b\xdc\xb81\xae\xdc\x1b\xf6\x1a\x90\x95\x90\x08\xf8\x08\x04$\ +\x014\x95RD\x9em\xa1\xf41\xd3i;3nF\x82\x10\x1fgu\x93{\xef\x97\xdc\x9cs\x05\ +\xfe.\xe1Pm\xfck@`\x10\x91f\x9a\xc8\xd0!:h?\xe5w^\xcc\xc8\x059\xfdL\xbdK\x94\ +m@=\x0e\x18\xe2:\x1a\xfd\x95\x92\xa7\xcd\'Z[\xfd6\xbb\xdb\xeb\xb4\xa1\xcay,y\ +\'\x05)\xc7\xf8\xf0\xea\x9b\xc2s\x1e\xa1\x12>\n\xb1\xf4\xde\xaf\x9f\x0b\x9d\ +\xa8\xbd\xd6W_S\xa7\xc5\xad\xf6ZK\x13U\xfa9:+\xfa\xb8T\xd5Gb?\x0e\x81T\xe3\ +\xa6-\xebb\x96\xcf@\x1a\xd0\x0f\x00\xe57\x1d\x8f\xeb\x02n{"!\xa0\xe4S\xdc\ +\xa8\xbb\x85\xdfS\x8edw\xa2\x14\xb3H6\x89Hf\x11)X\xda\x12C.\xb0\xc0\x12\x90\ +\xfd\x05\xb1d[\xf2W\xfb\xbb\x1aB\x13\xe1\x1fTT8X\xddZ#*\x87\x19\xdf\x1aa<6\ +\xcalr\x16U3\x10\xad\xa2`\xad\xb1t\xa4\xf7\xd5\x18+|\x03d\xc0\xb0\xa8\x15ZB\ +\xf4\x19\x83\xcd\x8d\xe5\xc2r4GZ\xda$^\x88\xa1\x1a\x1a\x026tD\x8a\xba\x81Z,"\ +:\x0c\xb1X]\xec\xce\xaf\x19\x11\xb6\x89\x02\x8a\xc8\x10\xef\xa6&\xd7G\xec\ +\x86\x81\xaaeq\ne\x14u\x01\r\x03U\xd0PD\x95=}\x9f\xc8N\x92\x95\xf9\x14g\xb3\ +\xad\x0e\x04\xce\x03A\xc0V\x02\xc8\xea\\\xf1\xc9X0\xda\xd7\xdb\x1b\xb4\x8e\ +\xcd\xed`9\xa9\xb2\x11\xdbco\x07\x02\xe9J\xda\xf4+\\\xf6\xf7\xd0}\xa6\x8b\ +\xda@\xb5\x1cz\x18\xb2\x03\x12 \x96\x00:o\x99\xf9\x1a\x8c\xbf\x0c5\xb8\xefm~\ +\xd8\xa5\xdev\x9a;\xde\x1e:\xca\xda\xf1\xd5yqy\\8\x9d\x12^\x8f\x0f\xd1\xa7;\ +\xe8\xc4\xc6G\x14@/1\xdd\xc8iK<}=\x1a\x19\x18X\xbd\xed\xeen\xe9"TS\x8f\xcb\ +\xe5\xa6`Q\xf9\x94\x9b`z\xeb=\x93\xf1\te#\x9a\x9c7\x03\xe8\x03\xec\xbf\x00\ +\x06S\x84i\xe4E^\xca=X\xcc/0\xb2<\xcc\x8c<\xad\xa7v\xd3a"|a\x99]\xb6)\xa0\ +\x91\x06\xbe\x9bV\x1a\xbfG9D\r\x17x\x85B\x9aE"l\xa0\x92C\x01b\xc0:\xb0\x06l\ +\x01I3P\x99\xc3\x00\x00+\xd0\x00\xf4\x00\xa5\xe6\xc2:\x90\x002\xa6\xf79@\xc3\ +|\xae\xa3\x00\xccE\x9f\xd9\x93\x01\x05\xc8\x1f\x9c\xfa\x9f\x12\xfe\x00?\xa6\ +\x9f\x16\xa0\x0c\xe8\xa4\xc9\xa8\x14\x00\x00\x00\x00IEND\xaeB`\x82' + +def get_book_greenBitmap(): + return BitmapFromImage(get_book_greenImage()) + +def get_book_greenImage(): + stream = cStringIO.StringIO(get_book_greenData()) + return ImageFromStream(stream) + +#---------------------------------------------------------------------- +def get_book_blueData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ +\x00\x02\x82IDAT8\x8d\x8d\x93KHTa\x14\xc7\x7f\xf7\xce\x1d\xc7y\xdc\x19G\xd4L\ +\x1bS\xc7Wi\x90\xe4\xa3\x87A\xb6\x88H\x08\xb2U\x11\xd4\xae\x16\xed\x8a\x16-j\ +\xdb\xa6u\xd0\xaeE\x9b\xa2\x16\x82\xf6 #\x12E"\xf1\x85`\xf8`\xd4&\x1cu\xd4q\ +\xee\xdc\x87w\xe6\xde\x16\x8dbJ\x8f\xff\xea\x1c\xce9?\xce\xf9\xbes\x04\xfe.a\ +\x87m\xff+A\x80\x17"W\x1cu$\x85\x16\xdc\xdefd\xff1\xb4\r\x95\x9e\xfb\xd7I\ +\x0c\xc5\x00c/\xe0\xf6\xc2Y\x04\xf3\x02rn\x13.\xb5\x11\xdc~D7\xa8\x1a":\x96-\ +\xc1\xf8\xc77t\xdf\xba\x07\xc6\xd4n\x88#t\xe9\xceH\xb1l\xb7\'C\x07\xca=K+\ +\xae#>\x83\x16\x7f\x8a\x8eCN:\xeb=,&\x0cb\x14U\xe3\x0e\xcb\xcc\xbd\x1d\x05\ +\x92\x80\xb5\x05\x10\x17\xd65\xc1\x99\x9fO\xd1J\x14=#\xd0^\xef\xa5\xae\\&\ +\xcf-\xb1\xa6\xc1\xd12\x1fH\x12T\x9d\xbaF\xe3\x83\x9b@\t mw@\xd1\xf93fkC\xd8\ +53O\xca\x1b`1\xa60\x11\xcb\xf0z,E\xdfH\x82\xd1oq\xd0S`g\x04\xe4P\x0b\xa6\xb4\ +\xcc\xea\xf04\xa0\x02\xb6\x03gm\\\xf7\x85\xaf\xe6\xd6V\n\xf2\xf7\x19"\x8a\ +\xc4Z\xc2\x00\xc3\x00{\x132&\xa4\xd3\xbf|\xcb\x12qW\x9c@K\xcc\xa2LG\x00Md\ +\xfca\x1f\x83\x9fz\x96,\x11]t\x12\x946\xc14\xc0L\x83a\x82\x91\x01\xc5\x84%\ +\x15\xa2q\x1aK4\x8f d\x1a\x80\n G\x02T\xa6\x06\x1e\xd1W}.\xd1\xde\xea\x0c\ +\xf6\x0f\x80\xe9\x865\r\x94$\xfb\xfck\x1c/Mq\xb2\xddAk\x8d\x87P\xa8X\r?{\xef\ +\x02\xbc\x80(\x01\x16\xd1\'C\x8c\x1f~\x9e\x0e\xd7\xde\xd0f\x87i)\xd2ik\x80\ +\xa6\x83"\x81\x80\x0f9\x10\xc0\xe7\xf5\x11\xcc\x93Q=\xfb=\x94^\xce!\xfaJ\x03\ +\xac\xad\xd7\xd4\xf9\xf1\xf91\xdd\x85\x9d\x1d\xc5]\xfe\xe6\xbaz*\xabk\xf0\ +\xcb^t\xdb\xc3\x87\x88\x9b\xfe\x88\x83\xde\xc9\x94\xc6\xdc\xbb1,\x04 \x08\ +\xb8\xb6\x006\x8b/\xa7(8\xfdT\x15\n\xeeN,\xf9\xe8\x9e\x11\xf8\x1a\xb1\xacdly\ +\x8a\xe4\x97I6\x06WIM\x9b\xd8f\x12\x98\x07R\x80\xfd\xfb*\x07\x9b\xcb(\xbc\ +\xd8\x85\xa9$\x89\xf7\xce\xa2N\x1a\xa4\x15\rX\x06\xa2\xc0\x02\xb0\x08\xacg\ +\x17J\xd9\t\x00p\x02U@\x1b\x90\x9b-\x88\x02q@\xc9\xfe\xbd\x0ed\xc8\x1e\xd7n\ +\x00\xd9\xc2`6\xa6\x02\x1a\xb0\xb9=\xea\x7fJ\xf8\x03|\x8f~\x02x\xb0\xfe\xe3\ +\xe93\x0e\xf5\x00\x00\x00\x00IEND\xaeB`\x82' + +def get_book_blueBitmap(): + return BitmapFromImage(get_book_blueImage()) + +def get_book_blueImage(): + stream = cStringIO.StringIO(get_book_blueData()) + return ImageFromStream(stream) + diff --git a/wxPython/docs/CHANGES.txt b/wxPython/docs/CHANGES.txt index 8a907cbecd..d92c8795fe 100644 --- a/wxPython/docs/CHANGES.txt +++ b/wxPython/docs/CHANGES.txt @@ -261,6 +261,9 @@ resampling methods for upsampling and downsampling respectively. Added the wx.lib.buttonpanel module, which is a tweaked version of Andrea Gavana's FancyButtonPanel module. +Added the wx.lib.flatnotebook module, from Andrea Gavana. + + diff --git a/wxPython/wx/lib/flatnotebook.py b/wxPython/wx/lib/flatnotebook.py new file mode 100644 index 0000000000..b43def683a --- /dev/null +++ b/wxPython/wx/lib/flatnotebook.py @@ -0,0 +1,4068 @@ +# --------------------------------------------------------------------------- # +# FLATNOTEBOOK Widget wxPython IMPLEMENTATION +# +# Original C++ Code From Eran. You Can Find It At: +# +# http://wxforum.shadonet.com/viewtopic.php?t=5761&start=0 +# +# License: wxWidgets license +# +# +# Python Code By: +# +# Andrea Gavana, @ 02 Oct 2006 +# Latest Revision: 04 Oct 2006, 20.00 GMT +# +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# andrea.gavana@gmail.com +# gavana@kpo.kz +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# +# End Of Comments +# --------------------------------------------------------------------------- # + +""" +The FlatNotebook is a full implementation of the wx.Notebook, and designed to be +a drop-in replacement for wx.Notebook. The API functions are similar so one can +expect the function to behave in the same way. + +Some features: +- The buttons are highlighted a la Firefox style +- The scrolling is done for bulks of tabs (so, the scrolling is faster and better) +- The buttons area is never overdrawn by tabs (unlike many other implementations I saw) +- It is a generic control +- Currently there are 4 differnt styles - VC8, VC 71, Standard and Fancy. +- Mouse middle click can be used to close tabs +- A function to add right click menu for tabs (simple as SetRightClickMenu) +- All styles has bottom style as well (they can be drawn in the bottom of screen) +- An option to hide 'X' button or navigation buttons (separately) +- Gradient coloring of the selected tabs and border +- Support for drag 'n' drop of tabs, both in the same notebook or to another notebook +- Possibility to have closing button on the active tab directly +- Support for disabled tabs +- Colours for active/inactive tabs, and captions +- Background of tab area can be painted in gradient (VC8 style only) +- Colourful tabs - a random gentle colour is generated for each new tab (very cool, + VC8 style only) + +And much more. + + +Usage: + +The following example shows a simple implementation that uses FlatNotebook inside +a very simple frame:: + + import wx + import wx.lib.flatnotebook as FNB + + class MyFrame(wx.Frame): + + def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, size=(800, 600), + style=wx.DEFAULT_FRAME_STYLE | wx.MAXIMIZE |wx.NO_FULL_REPAINT_ON_RESIZE): + + wx.Frame.__init__(self, parent, id, title, pos, size, style) + + mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(mainSizer) + + bookStyle = FNB.FNB_TABS_BORDER_SIMPLE + + self.book = FNB.StyledNotebook(self, wx.ID_ANY, style=bookStyle) + mainSizer.Add(self.book, 1, wx.EXPAND) + + # Add some pages to the notebook + self.Freeze() + + text = wx.TextCtrl(self.book, -1, "Book Page 1", style=wx.TE_MULTILINE) + self.book.AddPage(text, "Book Page 1") + + text = wx.TextCtrl(self.book, -1, "Book Page 2", style=wx.TE_MULTILINE) + self.book.AddPage(text, "Book Page 2") + + self.Thaw() + + mainSizer.Layout() + self.SendSizeEvent() + + # our normal wxApp-derived class, as usual + + app = wx.PySimpleApp() + + frame = MyFrame(None) + app.SetTopWindow(frame) + frame.Show() + + app.MainLoop() + + +License And Version: + +FlatNotebook Is Freeware And Distributed Under The wxPython License. + +Latest Revision: Andrea Gavana @ 04 Oct 2006, 20.00 GMT +Version 0.3. + +""" + +import wx +import random +import math +import weakref +import cPickle + +# Check for the new method in 2.7 (not present in 2.6.3.3) +if wx.VERSION_STRING < "2.7": + wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point) + +FNB_HEIGHT_SPACER = 10 + +# Use Visual Studio 2003 (VC7.1) Style for tabs +FNB_VC71 = 1 + +# Use fancy style - square tabs filled with gradient coloring +FNB_FANCY_TABS = 2 + +# Draw thin border around the page +FNB_TABS_BORDER_SIMPLE = 4 + +# Do not display the 'X' button +FNB_NO_X_BUTTON = 8 + +# Do not display the Right / Left arrows +FNB_NO_NAV_BUTTONS = 16 + +# Use the mouse middle button for cloing tabs +FNB_MOUSE_MIDDLE_CLOSES_TABS = 32 + +# Place tabs at bottom - the default is to place them +# at top +FNB_BOTTOM = 64 + +# Disable dragging of tabs +FNB_NODRAG = 128 + +# Use Visual Studio 2005 (VC8) Style for tabs +FNB_VC8 = 256 + +# Place 'X' on a tab +# Note: This style is not supported on VC8 style +FNB_X_ON_TAB = 512 + +FNB_BACKGROUND_GRADIENT = 1024 + +FNB_COLORFUL_TABS = 2048 + +# Style to close tab using double click - styles 1024, 2048 are reserved +FNB_DCLICK_CLOSES_TABS = 4096 + +VERTICAL_BORDER_PADDING = 4 + +# Button size is a 16x16 xpm bitmap +BUTTON_SPACE = 16 + +VC8_SHAPE_LEN = 16 + +MASK_COLOR = wx.Color(0, 128, 128) + +# Button status +FNB_BTN_PRESSED = 2 +FNB_BTN_HOVER = 1 +FNB_BTN_NONE = 0 + + +# Hit Test results +FNB_TAB = 1 # On a tab +FNB_X = 2 # On the X button +FNB_TAB_X = 3 # On the 'X' button (tab's X button) +FNB_LEFT_ARROW = 4 # On the rotate left arrow button +FNB_RIGHT_ARROW = 5 # On the rotate right arrow button +FNB_NOWHERE = 0 # Anywhere else + +FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS + +# FlatNotebook Events: +# wxEVT_FLATNOTEBOOK_PAGE_CHANGED: Event Fired When You Switch Page; +# wxEVT_FLATNOTEBOOK_PAGE_CHANGING: Event Fired When You Are About To Switch +# Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call +# event.Skip() In Your Event Handler; +# wxEVT_FLATNOTEBOOK_PAGE_CLOSING: Event Fired When A Page Is Closing, But +# You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip() +# In Your Event Handler; +# wxEVT_FLATNOTEBOOK_PAGE_CLOSED: Event Fired When A Page Is Closed. +# wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU: Event Fired When A Menu Pops-up In A Tab. + +wxEVT_FLATNOTEBOOK_PAGE_CHANGED = wx.NewEventType() +wxEVT_FLATNOTEBOOK_PAGE_CHANGING = wx.NewEventType() +wxEVT_FLATNOTEBOOK_PAGE_CLOSING = wx.NewEventType() +wxEVT_FLATNOTEBOOK_PAGE_CLOSED = wx.NewEventType() +wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType() + +#-----------------------------------# +# FlatNotebookEvent +#-----------------------------------# + +EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGED, 1) +EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, 1) +EVT_FLATNOTEBOOK_PAGE_CLOSING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, 1) +EVT_FLATNOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, 1) +EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, 1) + +# Some icons in XPM format + +left_arrow_disabled_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #555555", + "# c #000000", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "````````````````", + "````````````````", + "````````.```````", + "```````..```````", + "``````.`.```````", + "`````.``.```````", + "````.```.```````", + "`````.``.```````", + "``````.`.```````", + "```````..```````", + "````````.```````", + "````````````````", + "````````````````", + "````````````````", + "````````````````" + ] + +x_button_pressed_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #9e9ede", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.############.`", + "`.############.`", + "`.###aa####aa#.`", + "`.####aa##aa##.`", + "`.#####aaaa###.`", + "`.######aa####.`", + "`.#####aaaa###.`", + "`.####aa##aa##.`", + "`.###aa####aa#.`", + "`.############.`", + "`..............`", + "````````````````", + "````````````````" + ] + + +left_arrow_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #555555", + "# c #000000", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "````````````````", + "````````````````", + "````````.```````", + "```````..```````", + "``````...```````", + "`````....```````", + "````.....```````", + "`````....```````", + "``````...```````", + "```````..```````", + "````````.```````", + "````````````````", + "````````````````", + "````````````````", + "````````````````" + ] + +x_button_hilite_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #c9dafb", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.############.`", + "`.##aa####aa##.`", + "`.###aa##aa###.`", + "`.####aaaa####.`", + "`.#####aa#####.`", + "`.####aaaa####.`", + "`.###aa##aa###.`", + "`.##aa####aa##.`", + "`.############.`", + "`.############.`", + "`..............`", + "````````````````", + "````````````````" + ] + +x_button_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #555555", + "# c #000000", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "````````````````", + "````````````````", + "````````````````", + "````..````..````", + "`````..``..`````", + "``````....``````", + "```````..```````", + "``````....``````", + "`````..``..`````", + "````..````..````", + "````````````````", + "````````````````", + "````````````````", + "````````````````", + "````````````````" + ] + +left_arrow_pressed_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #9e9ede", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.############.`", + "`.#######a####.`", + "`.######aa####.`", + "`.#####aaa####.`", + "`.####aaaa####.`", + "`.###aaaaa####.`", + "`.####aaaa####.`", + "`.#####aaa####.`", + "`.######aa####.`", + "`.#######a####.`", + "`..............`", + "````````````````", + "````````````````" + ] + +left_arrow_hilite_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #c9dafb", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.######a#####.`", + "`.#####aa#####.`", + "`.####aaa#####.`", + "`.###aaaa#####.`", + "`.##aaaaa#####.`", + "`.###aaaa#####.`", + "`.####aaa#####.`", + "`.#####aa#####.`", + "`.######a#####.`", + "`.############.`", + "`..............`", + "````````````````", + "````````````````" + ] + +right_arrow_disabled_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #555555", + "# c #000000", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "````````````````", + "````````````````", + "```````.````````", + "```````..```````", + "```````.`.``````", + "```````.``.`````", + "```````.```.````", + "```````.``.`````", + "```````.`.``````", + "```````..```````", + "```````.````````", + "````````````````", + "````````````````", + "````````````````", + "````````````````" + ] + +right_arrow_hilite_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #c9dafb", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.####a#######.`", + "`.####aa######.`", + "`.####aaa#####.`", + "`.####aaaa####.`", + "`.####aaaaa###.`", + "`.####aaaa####.`", + "`.####aaa#####.`", + "`.####aa######.`", + "`.####a#######.`", + "`.############.`", + "`..............`", + "````````````````", + "````````````````" + ] + +right_arrow_pressed_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #4766e0", + "# c #9e9ede", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "`..............`", + "`.############.`", + "`.############.`", + "`.#####a######.`", + "`.#####aa#####.`", + "`.#####aaa####.`", + "`.#####aaaa###.`", + "`.#####aaaaa##.`", + "`.#####aaaa###.`", + "`.#####aaa####.`", + "`.#####aa#####.`", + "`.#####a######.`", + "`..............`", + "````````````````", + "````````````````" + ] + + +right_arrow_xpm = [ + " 16 16 8 1", + "` c #008080", + ". c #555555", + "# c #000000", + "a c #000000", + "b c #000000", + "c c #000000", + "d c #000000", + "e c #000000", + "````````````````", + "````````````````", + "````````````````", + "```````.````````", + "```````..```````", + "```````...``````", + "```````....`````", + "```````.....````", + "```````....`````", + "```````...``````", + "```````..```````", + "```````.````````", + "````````````````", + "````````````````", + "````````````````", + "````````````````" + ] + + + +def LightColour(color, percent): + """ Brighten input colour by percent. """ + + end_color = wx.WHITE + + rd = end_color.Red() - color.Red() + gd = end_color.Green() - color.Green() + bd = end_color.Blue() - color.Blue() + + high = 100 + + # We take the percent way of the color from color -. white + i = percent + r = color.Red() + ((i*rd*100)/high)/100 + g = color.Green() + ((i*gd*100)/high)/100 + b = color.Blue() + ((i*bd*100)/high)/100 + return wx.Color(r, g, b) + + +def PaintStraightGradientBox(dc, rect, startColor, endColor, vertical=True): + """ Draws a gradient colored box from startColor to endColor. """ + + rd = endColor.Red() - startColor.Red() + gd = endColor.Green() - startColor.Green() + bd = endColor.Blue() - startColor.Blue() + + # Save the current pen and brush + savedPen = dc.GetPen() + savedBrush = dc.GetBrush() + + if vertical: + high = rect.GetHeight()-1 + else: + high = rect.GetWidth()-1 + + if high < 1: + return + + for i in xrange(high+1): + + r = startColor.Red() + ((i*rd*100)/high)/100 + g = startColor.Green() + ((i*gd*100)/high)/100 + b = startColor.Blue() + ((i*bd*100)/high)/100 + + p = wx.Pen(wx.Color(r, g, b)) + dc.SetPen(p) + + if vertical: + dc.DrawLine(rect.x, rect.y+i, rect.x+rect.width, rect.y+i) + else: + dc.DrawLine(rect.x+i, rect.y, rect.x+i, rect.y+rect.height) + + # Restore the pen and brush + dc.SetPen(savedPen) + dc.SetBrush(savedBrush) + + +def RandomColor(): + """ Creates a random colour. """ + + r = random.randint(0, 255) # Random value betweem 0-255 + g = random.randint(0, 255) # Random value betweem 0-255 + b = random.randint(0, 255) # Random value betweem 0-255 + + return wx.Color(r, g, b) + + +# ---------------------------------------------------------------------------- # +# Class FNBDragInfo +# Stores All The Information To Allow Drag And Drop Between Different +# FlatNotebooks. +# ---------------------------------------------------------------------------- # + +class FNBDragInfo: + + _map = weakref.WeakValueDictionary() + + def __init__(self, container, pageindex): + """ Default class constructor. """ + + self._id = id(container) + FNBDragInfo._map[self._id] = container + self._pageindex = pageindex + + + def GetContainer(self): + """ Returns the FlatNotebook page (usually a panel). """ + + return FNBDragInfo._map.get(self._id, None) + + + def GetPageIndex(self): + """ Returns the page index associated with a page. """ + + return self._pageindex + + +# ---------------------------------------------------------------------------- # +# Class FNBDropTarget +# Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between +# Different FlatNotebooks. +# ---------------------------------------------------------------------------- # + +class FNBDropTarget(wx.DropTarget): + + def __init__(self, parent): + """ Default class constructor. """ + + wx.DropTarget.__init__(self) + + self._parent = parent + self._dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook")) + self.SetDataObject(self._dataobject) + + + def OnData(self, x, y, dragres): + """ Handles the OnData() method to call the real DnD routine. """ + + if not self.GetData(): + return wx.DragNone + + draginfo = self._dataobject.GetData() + drginfo = cPickle.loads(draginfo) + + return self._parent.OnDropTarget(x, y, drginfo.GetPageIndex(), drginfo.GetContainer()) + + +# ---------------------------------------------------------------------------- # +# Class PageInfo +# Contains parameters for every FlatNotebook page +# ---------------------------------------------------------------------------- # + +class PageInfo: + + def __init__(self, caption="", imageindex=-1, tabangle=0, enabled=True): + """ + Default Class Constructor. + + Parameters: + - caption: the tab caption; + - imageindex: the tab image index based on the assigned (set) wx.ImageList (if any); + - tabangle: the tab angle (only on standard tabs, from 0 to 15 degrees); + - enabled: sets enabled or disabled the tab. + """ + + self._strCaption = caption + self._TabAngle = tabangle + self._ImageIndex = imageindex + self._bEnabled = enabled + self._pos = wx.Point(-1, -1) + self._size = wx.Size(-1, -1) + self._region = wx.Region() + self._xRect = wx.Rect() + self._color = None + + + def SetCaption(self, value): + """ Sets the tab caption. """ + + self._strCaption = value + + + def GetCaption(self): + """ Returns the tab caption. """ + + return self._strCaption + + + def SetPosition(self, value): + """ Sets the tab position. """ + + self._pos = value + + + def GetPosition(self): + """ Returns the tab position. """ + + return self._pos + + + def SetSize(self, value): + """ Sets the tab size. """ + + self._size = value + + + def GetSize(self): + """ Returns the tab size. """ + + return self._size + + + def SetTabAngle(self, value): + """ Sets the tab header angle (0 <= tab <= 15 degrees). """ + + self._TabAngle = min(45, value) + + + def GetTabAngle(self): + """ Returns the tab angle. """ + + return self._TabAngle + + + def SetImageIndex(self, value): + """ Sets the tab image index. """ + + self._ImageIndex = value + + + def GetImageIndex(self): + """ Returns the tab umage index. """ + + return self._ImageIndex + + + def GetEnabled(self): + """ Returns whether the tab is enabled or not. """ + + return self._bEnabled + + + def Enable(self, enabled): + """ Sets the tab enabled or disabled. """ + + self._bEnabled = enabled + + + def SetRegion(self, points=[]): + """ Sets the tab region. """ + + self._region = wx.RegionFromPoints(points) + + + def GetRegion(self): + """ Returns the tab region. """ + + return self._region + + + def SetXRect(self, xrect): + """ Sets the button 'X' area rect. """ + + self._xRect = xrect + + + def GetXRect(self): + """ Returns the button 'X' area rect. """ + + return self._xRect + + + def GetColor(self): + """ Returns the tab colour. """ + + return self._color + + + def SetColor(self, color): + """ Sets the tab colour. """ + + self._color = color + + +# ---------------------------------------------------------------------------- # +# Class FlatNotebookEvent +# ---------------------------------------------------------------------------- # + +class FlatNotebookEvent(wx.PyCommandEvent): + """ + This events will be sent when a EVT_FLATNOTEBOOK_PAGE_CHANGED, + EVT_FLATNOTEBOOK_PAGE_CHANGING And EVT_FLATNOTEBOOK_PAGE_CLOSING is mapped in + the parent. + """ + + def __init__(self, eventType, id=1, nSel=-1, nOldSel=-1): + """ Default class constructor. """ + + wx.PyCommandEvent.__init__(self, eventType, id) + self._eventType = eventType + + self.notify = wx.NotifyEvent(eventType, 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() + + + def SetSelection(self, nSel): + """ Sets event selection. """ + + self._selection = nSel + + + def SetOldSelection(self, nOldSel): + """ Sets old event selection. """ + + self._oldselection = nOldSel + + + def GetSelection(self): + """ Returns event selection. """ + + return self._selection + + + def GetOldSelection(self): + """ Returns old event selection """ + + return self._oldselection + + +# ---------------------------------------------------------------------------- # +# Class FlatNotebookBase +# ---------------------------------------------------------------------------- # + +class FlatNotebookBase(wx.Panel): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, name="FlatNotebook"): + """ + Default class constructor. + + All the parameters are as in wxPython class construction, except the + 'style': this can be assigned to whatever combination of FNB_* styles. + """ + + self._bForceSelection = False + self._nPadding = 6 + self._nFrom = 0 + style |= wx.TAB_TRAVERSAL + self._pages = None + self._windows = [] + + wx.Panel.__init__(self, parent, id, pos, size, style) + + self._pages = StyledTabsContainer(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, style) + + self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey) + + self._pages._colorBorder = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW) + + self._mainSizer = wx.BoxSizer(wx.VERTICAL) + self.SetSizer(self._mainSizer) + + self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_APPWORKSPACE)) + + # Add the tab container to the sizer + self._mainSizer.Insert(0, self._pages, 0, wx.EXPAND) + + # Set default page height + dc = wx.ClientDC(self) + + if "__WXGTK__" in wx.PlatformInfo: + # For GTK it seems that we must do this steps in order + # for the tabs will get the proper height on initialization + # on MSW, preforming these steps yields wierd results + normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(boldFont) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding + self._pages.SetSizeHints(-1, tabHeight) + + self._mainSizer.Layout() + + self._pages._nFrom = self._nFrom + self._pDropTarget = FNBDropTarget(self) + self.SetDropTarget(self._pDropTarget) + + + def CreatePageContainer(self): + """ Creates the page container for the tabs. """ + + return PageContainerBase(self, wx.ID_ANY) + + + def SetActiveTabTextColour(self, textColour): + """ Sets the text colour for the active tab. """ + + self._pages._activeTextColor = textColour + + + def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer): + """ Handles the drop action from a DND operation. """ + + return self._pages.OnDropTarget(x, y, nTabPage, wnd_oldContainer) + + + def AddPage(self, window, caption, selected=True, imgindex=-1): + """ + Add a page to the FlatNotebook. + + Parameters: + - window: Specifies the new page. + - caption: Specifies the text for the new page. + - selected: Specifies whether the page should be selected. + - imgindex: Specifies the optional image index for the new page. + + Return value: + True if successful, False otherwise. + """ + + # sanity check + if not window: + return False + + # reparent the window to us + window.Reparent(self) + + # Add tab + bSelected = selected or not self._windows + curSel = self._pages.GetSelection() + + if not self._pages.IsShown(): + self._pages.Show() + + self._pages.AddPage(caption, bSelected, imgindex) + self._windows.append(window) + + self.Freeze() + + # Check if a new selection was made + if bSelected: + + if curSel >= 0: + + # Remove the window from the main sizer + self._mainSizer.Detach(self._windows[curSel]) + self._windows[curSel].Hide() + + if self.GetWindowStyleFlag() & FNB_BOTTOM: + + self._mainSizer.Insert(0, window, 1, wx.EXPAND) + + else: + + # We leave a space of 1 pixel around the window + self._mainSizer.Add(window, 1, wx.EXPAND) + + else: + + # Hide the page + window.Hide() + + self._mainSizer.Layout() + self.Thaw() + self.Refresh() + + return True + + + def SetImageList(self, imglist): + """ + Sets the image list for the page control. It does not take ownership + of the image list, you must delete it yourself. + """ + + self._pages.SetImageList(imglist) + + + def GetImageList(self): + """ Returns the associated image list. """ + + return self._pages.GetImageList() + + + def InsertPage(self, indx, page, text, select=True, imgindex=-1): + """ + Inserts a new page at the specified position. + + Parameters: + - indx: Specifies the position of the new page. + - page: Specifies the new page. + - text: Specifies the text for the new page. + - select: Specifies whether the page should be selected. + - imgindex: Specifies the optional image index for the new page. + + Return value: + True if successful, False otherwise. + """ + + # sanity check + if not page: + return False + + # reparent the window to us + page.Reparent(self) + + if not self._windows: + + self.AddPage(page, text, select, imgindex) + return True + + # Insert tab + bSelected = select or not self._windows + curSel = self._pages.GetSelection() + + indx = max(0, min(indx, len(self._windows))) + + if indx <= len(self._windows): + + self._windows.insert(indx, page) + + else: + + self._windows.append(page) + + self._pages.InsertPage(indx, text, bSelected, imgindex) + + if indx <= curSel: + curSel = curSel + 1 + + self.Freeze() + + # Check if a new selection was made + if bSelected: + + if curSel >= 0: + + # Remove the window from the main sizer + self._mainSizer.Detach(self._windows[curSel]) + self._windows[curSel].Hide() + + self._pages.SetSelection(indx) + + else: + + # Hide the page + page.Hide() + + self.Thaw() + self._mainSizer.Layout() + self.Refresh() + + return True + + + def SetSelection(self, page): + """ + Sets the selection for the given page. + The call to this function generates the page changing events + """ + + if page >= len(self._windows) or not self._windows: + return + + # Support for disabed tabs + if not self._pages.GetEnabled(page) and len(self._windows) > 1 and not self._bForceSelection: + return + + curSel = self._pages.GetSelection() + + # program allows the page change + self.Freeze() + if curSel >= 0: + + # Remove the window from the main sizer + self._mainSizer.Detach(self._windows[curSel]) + self._windows[curSel].Hide() + + if self.GetWindowStyleFlag() & FNB_BOTTOM: + + self._mainSizer.Insert(0, self._windows[page], 1, wx.EXPAND) + + else: + + # We leave a space of 1 pixel around the window + self._mainSizer.Add(self._windows[page], 1, wx.EXPAND) + + self._windows[page].Show() + + self._mainSizer.Layout() + self._pages._iActivePage = page + + self.Thaw() + + self._pages.DoSetSelection(page) + + + def DeletePage(self, page): + """ + Deletes the specified page, and the associated window. + The call to this function generates the page changing events. + """ + + if page >= len(self._windows): + return + + # Fire a closing event + event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId()) + event.SetSelection(page) + event.SetEventObject(self) + self.GetEventHandler().ProcessEvent(event) + + # The event handler allows it? + if not event.IsAllowed(): + return + + self.Freeze() + + # Delete the requested page + pageRemoved = self._windows[page] + + # If the page is the current window, remove it from the sizer + # as well + if page == self._pages.GetSelection(): + self._mainSizer.Detach(pageRemoved) + + # Remove it from the array as well + self._windows.pop(page) + + # Now we can destroy it in wxWidgets use Destroy instead of delete + pageRemoved.Destroy() + + self.Thaw() + + self._pages.DoDeletePage(page) + self.Refresh() + + # Fire a closed event + closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId()) + closedEvent.SetSelection(page) + closedEvent.SetEventObject(self) + self.GetEventHandler().ProcessEvent(closedEvent) + + + def DeleteAllPages(self): + """ Deletes all the pages. """ + + if not self._windows: + return False + + self.Freeze() + + for page in self._windows: + page.Destroy() + + self._windows = [] + self.Thaw() + + # Clear the container of the tabs as well + self._pages.DeleteAllPages() + return True + + + def GetCurrentPage(self): + """ Returns the currently selected notebook page or None. """ + + sel = self._pages.GetSelection() + if sel < 0: + return None + + return self._windows[sel] + + + def GetPage(self, page): + """ Returns the window at the given page position, or None. """ + + if page >= len(self._windows): + return None + + return self._windows[page] + + + def GetPageIndex(self, win): + """ Returns the index at which the window is found. """ + + try: + return self._windows.index(win) + except: + return -1 + + + def GetSelection(self): + """ Returns the currently selected page, or -1 if none was selected. """ + + return self._pages.GetSelection() + + + def AdvanceSelection(self, bForward=True): + """ + Cycles through the tabs. + The call to this function generates the page changing events. + """ + + self._pages.AdvanceSelection(bForward) + + + def GetPageCount(self): + """ Returns the number of pages in the FlatNotebook control. """ + return self._pages.GetPageCount() + + + def OnNavigationKey(self, event): + """ Handles the wx.EVT_NAVIGATION_KEY event for FlatNotebook. """ + + if event.IsWindowChange(): + # change pages + self.AdvanceSelection(event.GetDirection()) + else: + # pass to the parent + if self.GetParent(): + event.SetCurrentFocus(self) + self.GetParent().ProcessEvent(event) + + + def GetPageShapeAngle(self, page_index): + """ Returns the angle associated to a tab. """ + + if page_index < 0 or page_index >= len(self._pages._pagesInfoVec): + return None, False + + result = self._pages._pagesInfoVec[page_index].GetTabAngle() + return result, True + + + def SetPageShapeAngle(self, page_index, angle): + """ Sets the angle associated to a tab. """ + + if page_index < 0 or page_index >= len(self._pages._pagesInfoVec): + return + + if angle > 15: + return + + self._pages._pagesInfoVec[page_index].SetTabAngle(angle) + + + def SetAllPagesShapeAngle(self, angle): + """ Sets the angle associated to all the tab. """ + + if angle > 15: + return + + for ii in xrange(len(self._pages._pagesInfoVec)): + self._pages._pagesInfoVec[ii].SetTabAngle(angle) + + self.Refresh() + + + def GetPageBestSize(self): + """ Return the page best size. """ + + return self._pages.GetClientSize() + + + def SetPageText(self, page, text): + """ Sets the text for the given page. """ + + bVal = self._pages.SetPageText(page, text) + self._pages.Refresh() + + return bVal + + + def SetPadding(self, padding): + """ + Sets the amount of space around each page's icon and label, in pixels. + NB: only the horizontal padding is considered. + """ + + self._nPadding = padding.GetWidth() + + + def GetTabArea(self): + """ Returns the associated page. """ + + return self._pages + + + def GetPadding(self): + """ Returns the amount of space around each page's icon and label, in pixels. """ + + return self._nPadding + + + def SetWindowStyleFlag(self, style): + """ Sets the FlatNotebook window style flags. """ + + wx.Panel.SetWindowStyleFlag(self, style) + + if self._pages: + + # For changing the tab position (i.e. placing them top/bottom) + # refreshing the tab container is not enough + self.SetSelection(self._pages._iActivePage) + + + def RemovePage(self, page): + """ Deletes the specified page, without deleting the associated window. """ + + if page >= len(self._windows): + return False + + # Fire a closing event + event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId()) + event.SetSelection(page) + event.SetEventObject(self) + self.GetEventHandler().ProcessEvent(event) + + # The event handler allows it? + if not event.IsAllowed(): + return False + + self.Freeze() + + # Remove the requested page + pageRemoved = self._windows[page] + + # If the page is the current window, remove it from the sizer + # as well + if page == self._pages.GetSelection(): + self._mainSizer.Detach(pageRemoved) + + # Remove it from the array as well + self._windows.pop(page) + self.Thaw() + + self._pages.DoDeletePage(page) + + return True + + + def SetRightClickMenu(self, menu): + """ Sets the popup menu associated to a right click on a tab. """ + + self._pages._pRightClickMenu = menu + + + def GetPageText(self, page): + """ Returns the tab caption. """ + + return self._pages.GetPageText(page) + + + def SetGradientColors(self, fr, to, border): + """ Sets the gradient colours for the tab. """ + + self._pages._colorFrom = fr + self._pages._colorTo = to + self._pages._colorBorder = border + + + def SetGradientColorFrom(self, fr): + """ Sets the starting colour for the gradient. """ + + self._pages._colorFrom = fr + + + def SetGradientColorTo(self, to): + """ Sets the ending colour for the gradient. """ + + self._pages._colorTo = to + + + def SetGradientColorBorder(self, border): + """ Sets the tab border colour. """ + + self._pages._colorBorder = border + + + def GetGradientColorFrom(self): + """ Gets first gradient colour. """ + + return self._pages._colorFrom + + + def GetGradientColorTo(self): + """ Gets second gradient colour. """ + + return self._pages._colorTo + + + def GetGradientColorBorder(self): + """ Gets the tab border colour. """ + + return self._pages._colorBorder + + + def GetActiveTabTextColour(self): + """ Get the active tab text colour. """ + + return self._pages._activeTextColor + + + def SetPageImageIndex(self, page, imgindex): + """ + Sets the image index for the given page. Image is an index into the + image list which was set with SetImageList. + """ + + self._pages.SetPageImageIndex(page, imgindex) + + + def GetPageImageIndex(self, page): + """ + Returns the image index for the given page. Image is an index into the + image list which was set with SetImageList. + """ + + return self._pages.GetPageImageIndex(page) + + + def GetEnabled(self, page): + """ Returns whether a tab is enabled or not. """ + + return self._pages.GetEnabled(page) + + + def Enable(self, page, enabled=True): + """ Enables or disables a tab. """ + + if page >= len(self._windows): + return + + self._windows[page].Enable(enabled) + self._pages.Enable(page, enabled) + + + def GetNonActiveTabTextColour(self): + """ Returns the non active tabs text colour. """ + + return self._pages._nonActiveTextColor + + + def SetNonActiveTabTextColour(self, color): + """ Sets the non active tabs text colour. """ + + self._pages._nonActiveTextColor = color + + + def SetTabAreaColour(self, color): + """ Sets the area behind the tabs colour. """ + + self._pages._tabAreaColor = color + + + def GetTabAreaColour(self): + """ Returns the area behind the tabs colour. """ + + return self._pages._tabAreaColor + + + def SetActiveTabColour(self, color): + """ Sets the active tab colour. """ + + self._pages._activeTabColor = color + + + def GetActiveTabColour(self): + """ Returns the active tab colour. """ + + return self._pages._activeTabColor + + +# ---------------------------------------------------------------------------- # +# Class PageContainerBase +# Acts as a container for the pages you add to FlatNotebook +# ---------------------------------------------------------------------------- # + +class PageContainerBase(wx.Panel): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, + size=wx.DefaultSize, style=0): + """ Default class constructor. """ + + self._ImageList = None + self._iActivePage = -1 + self._pDropTarget = None + self._nLeftClickZone = FNB_NOWHERE + self._tabXBgBmp = wx.EmptyBitmap(16, 16) + self._xBgBmp = wx.EmptyBitmap(16, 14) + self._leftBgBmp = wx.EmptyBitmap(16, 14) + self._rightBgBmp = wx.EmptyBitmap(16, 14) + + self._pRightClickMenu = None + self._nXButtonStatus = FNB_BTN_NONE + self._pParent = parent + self._nRightButtonStatus = FNB_BTN_NONE + self._nLeftButtonStatus = FNB_BTN_NONE + self._nTabXButtonStatus = FNB_BTN_NONE + + self._pagesInfoVec = [] + + self._colorTo = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION) + self._colorFrom = wx.WHITE + self._activeTabColor = wx.WHITE + self._activeTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT) + self._nonActiveTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW) + self._tabAreaColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) + + self._nFrom = 0 + self._isdragging = False + + wx.Panel.__init__(self, parent, id, pos, size, style) + + self._pDropTarget = FNBDropTarget(self) + self.SetDropTarget(self._pDropTarget) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) + self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown) + self.Bind(wx.EVT_MOTION, self.OnMouseMove) + self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) + self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) + self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow) + self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) + + + def GetButtonAreaWidth(self): + """ Returns the width of the navigation button area. """ + + style = self.GetParent().GetWindowStyleFlag() + btnareawidth = 2*self._pParent._nPadding + + if style & FNB_NO_X_BUTTON == 0: + btnareawidth += BUTTON_SPACE + + if style & FNB_NO_NAV_BUTTONS == 0: + btnareawidth += 2*BUTTON_SPACE + + return btnareawidth + + + def OnEraseBackground(self, event): + """ Handles the wx.EVT_ERASE_BACKGROUND event for PageContainerBase (does nothing).""" + + pass + + + def OnPaint(self, event): + """ Handles the wx.EVT_PAINT event for PageContainerBase.""" + + dc = wx.BufferedPaintDC(self) + + if "__WXMAC__" in wx.PlatformInfo: + # Works well on MSW & GTK, however this lines should be skipped on MAC + if len(self._pagesInfoVec) == 0 or self._nFrom >= len(self._pagesInfoVec): + self.Hide() + event.Skip() + return + + # Get the text hight + style = self.GetParent().GetWindowStyleFlag() + + # For GTK it seems that we must do this steps in order + # for the tabs will get the proper height on initialization + # on MSW, preforming these steps yields wierd results + normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + if "__WXGTK__" in wx.PlatformInfo: + dc.SetFont(boldFont) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding + + # Calculate the number of rows required for drawing the tabs + rect = self.GetClientRect() + clientWidth = rect.width + + # Set the maximum client size + self.SetSizeHints(self.GetButtonsAreaLength(), tabHeight) + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + + if style & FNB_VC71: + backBrush = wx.Brush(wx.Colour(247, 243, 233)) + else: + backBrush = wx.Brush(self._tabAreaColor) + + noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE)) + selBrush = wx.Brush(self._activeTabColor) + + size = self.GetSize() + + # Background + dc.SetTextBackground((style & FNB_VC71 and [wx.Colour(247, 243, 233)] or [self.GetBackgroundColour()])[0]) + dc.SetTextForeground(self._activeTextColor) + dc.SetBrush(backBrush) + + # If border style is set, set the pen to be border pen + if style & FNB_TABS_BORDER_SIMPLE: + dc.SetPen(borderPen) + else: + pc = (self.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [self.GetBackgroundColour()])[0] + dc.SetPen(wx.Pen(pc)) + + dc.DrawRectangle(0, 0, size.x, size.y) + + # Take 3 bitmaps for the background for the buttons + + mem_dc = wx.MemoryDC() + + #--------------------------------------- + # X button + #--------------------------------------- + rect = wx.Rect(self.GetXPos(), 6, 16, 14) + mem_dc.SelectObject(self._xBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + #--------------------------------------- + # Right button + #--------------------------------------- + rect = wx.Rect(self.GetRightButtonPos(), 6, 16, 14) + mem_dc.SelectObject(self._rightBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + #--------------------------------------- + # Left button + #--------------------------------------- + rect = wx.Rect(self.GetLeftButtonPos(), 6, 16, 14) + mem_dc.SelectObject(self._leftBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + # We always draw the bottom/upper line of the tabs + # regradless the style + dc.SetPen(borderPen) + self.DrawTabsLine(dc) + + # Restore the pen + dc.SetPen(borderPen) + + if self.HasFlag(FNB_VC71): + + greyLineYVal = self.HasFlag((FNB_BOTTOM and [0] or [size.y - 2])[0]) + whiteLineYVal = self.HasFlag((FNB_BOTTOM and [3] or [size.y - 3])[0]) + + pen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)) + dc.SetPen(pen) + + # Draw thik grey line between the windows area and + # the tab area + + for num in xrange(3): + dc.DrawLine(0, greyLineYVal + num, size.x, greyLineYVal + num) + + wbPen = wx.Pen((self.HasFlag(FNB_BOTTOM) and [wx.BLACK] or [wx.WHITE])[0]) + dc.SetPen(wbPen) + dc.DrawLine(1, whiteLineYVal, size.x - 1, whiteLineYVal) + + # Restore the pen + dc.SetPen(borderPen) + + if "__WXMAC__" in wx.PlatformInfo: + # On MAC, Add these lines so the tab background gets painted + if len(self._pagesInfoVec) == 0 or self._nFrom >= len(self._pagesInfoVec): + self.Hide() + return + + # Draw labels + dc.SetFont(boldFont) + posx = self._pParent.GetPadding() + + # Update all the tabs from 0 to '_nFrom' to be non visible + for ii in xrange(self._nFrom): + self._pagesInfoVec[ii].SetPosition(wx.Point(-1, -1)) + self._pagesInfoVec[ii].GetRegion().Clear() + + if style & FNB_VC71: + tabHeight = ((style & FNB_BOTTOM) and [tabHeight - 4] or [tabHeight])[0] + elif style & FNB_FANCY_TABS: + tabHeight = ((style & FNB_BOTTOM) and [tabHeight - 2] or [tabHeight])[0] + + #---------------------------------------------------------- + # Go over and draw the visible tabs + #---------------------------------------------------------- + + count = self._nFrom + + for ii in xrange(self._nFrom, len(self._pagesInfoVec)): + + if style != FNB_VC71: + shapePoints = int(tabHeight*math.tan(float(self._pagesInfoVec[ii].GetTabAngle())/180.0*math.pi)) + else: + shapePoints = 0 + + dc.SetPen(borderPen) + dc.SetBrush((ii==self.GetSelection() and [selBrush] or [noselBrush])[0]) + + # Calculate the text length using the bold font, so when selecting a tab + # its width will not change + dc.SetFont(boldFont) + width, pom = dc.GetTextExtent(self.GetPageText(ii)) + + # Now set the font to the correct font + dc.SetFont((ii==self.GetSelection() and [boldFont] or [normalFont])[0]) + + # Set a minimum size to a tab + if width < 20: + width = 20 + + # Add the padding to the tab width + # Tab width: + # +-----------------------------------------------------------+ + # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING | + # +-----------------------------------------------------------+ + + tabWidth = 2*self._pParent._nPadding + width + imageYCoord = (self.HasFlag(FNB_BOTTOM) and [6] or [8])[0] + + # Style to add a small 'x' button on the top right + # of the tab + if style & FNB_X_ON_TAB and ii == self.GetSelection(): + + # The xpm image that contains the 'x' button is 9 pixles + tabWidth += self._pParent._nPadding + 9 + + if not (style & FNB_VC71) and not (style & FNB_FANCY_TABS): + # Default style + tabWidth += 2*shapePoints + + hasImage = self._ImageList != None and self._pagesInfoVec[ii].GetImageIndex() != -1 + + # For VC71 style, we only add the icon size (16 pixels) + if hasImage: + + if style & FNB_VC71 or style & FNB_FANCY_TABS: + tabWidth += 16 + self._pParent._nPadding + else: + # Default style + tabWidth += 16 + self._pParent._nPadding + shapePoints/2 + + # Check if we can draw more + if posx + tabWidth + self.GetButtonsAreaLength() >= clientWidth: + break + + count = count + 1 + + # By default we clean the tab region + self._pagesInfoVec[ii].GetRegion().Clear() + + # Clean the 'x' buttn on the tab + # 'Clean' rectanlge is a rectangle with width or height + # with values lower than or equal to 0 + self._pagesInfoVec[ii].GetXRect().SetSize(wx.Size(-1, -1)) + + # Draw the tab + if style & FNB_FANCY_TABS: + self.DrawFancyTab(dc, posx, ii, tabWidth, tabHeight) + elif style & FNB_VC71: + self.DrawVC71Tab(dc, posx, ii, tabWidth, tabHeight) + else: + self.DrawStandardTab(dc, posx, ii, tabWidth, tabHeight) + + # The width of the images are 16 pixels + if hasImage: + textOffset = 2*self._pParent._nPadding + 16 + shapePoints/2 + else: + textOffset = self._pParent._nPadding + shapePoints/2 + + # After several testing, it seems that we can draw + # the text 2 pixles to the right - this is done only + # for the standard tabs + + if not self.HasFlag(FNB_FANCY_TABS): + textOffset += 2 + + if ii != self.GetSelection(): + # Set the text background to be like the vertical lines + dc.SetTextForeground(self._nonActiveTextColor) + + if hasImage: + + imageXOffset = textOffset - 16 - self._pParent._nPadding + self._ImageList.Draw(self._pagesInfoVec[ii].GetImageIndex(), dc, + posx + imageXOffset, imageYCoord, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + dc.DrawText(self.GetPageText(ii), posx + textOffset, imageYCoord) + + # From version 1.2 - a style to add 'x' button + # on a tab + + if self.HasFlag(FNB_X_ON_TAB) and ii == self.GetSelection(): + + textWidth, textHeight = dc.GetTextExtent(self.GetPageText(ii)) + tabCloseButtonXCoord = posx + textOffset + textWidth + 1 + + # take a bitmap from the position of the 'x' button (the x on tab button) + # this bitmap will be used later to delete old buttons + tabCloseButtonYCoord = imageYCoord + x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16) + mem_dc = wx.MemoryDC() + mem_dc.SelectObject(self._tabXBgBmp) + mem_dc.Blit(0, 0, x_rect.width, x_rect.height, dc, x_rect.x, x_rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + # Draw the tab + self.DrawTabX(dc, x_rect, ii) + + # Restore the text forground + dc.SetTextForeground(self._activeTextColor) + + # Update the tab position & size + posy = (style & FNB_BOTTOM and [0] or [VERTICAL_BORDER_PADDING])[0] + + self._pagesInfoVec[ii].SetPosition(wx.Point(posx, posy)) + self._pagesInfoVec[ii].SetSize(wx.Size(tabWidth, tabHeight)) + posx += tabWidth + + # Update all tabs that can not fit into the screen as non-visible + for ii in xrange(count, len(self._pagesInfoVec)): + + self._pagesInfoVec[ii].SetPosition(wx.Point(-1, -1)) + self._pagesInfoVec[ii].GetRegion().Clear() + + # Draw the left/right/close buttons + # Left arrow + self.DrawLeftArrow(dc) + self.DrawRightArrow(dc) + self.DrawX(dc) + + + def DrawFancyTab(self, dc, posx, tabIdx, tabWidth, tabHeight): + """ + Fancy tabs - like with VC71 but with the following differences: + - The Selected tab is colored with gradient color + """ + + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + pen = (tabIdx==self.GetSelection() and [wx.Pen(self._colorBorder)] \ + or [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))])[0] + + fnb_bottom = self.HasFlag(FNB_BOTTOM) + + if tabIdx == self.GetSelection(): + + posy = (fnb_bottom and [2] or [VERTICAL_BORDER_PADDING])[0] + th = (fnb_bottom and [tabHeight - 2] or [tabHeight - 5])[0] + + rect = wx.Rect(posx, posy, tabWidth, th) + self.FillGradientColor(dc, rect) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(pen) + dc.DrawRectangleRect(rect) + + # erase the bottom/top line of the rectangle + dc.SetPen(wx.Pen(self._colorFrom)) + if fnb_bottom: + dc.DrawLine(rect.x, 2, rect.x + rect.width, 2) + else: + dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1) + + else: + + # We dont draw a rectangle for non selected tabs, but only + # vertical line on the left + dc.SetPen(borderPen) + dc.DrawLine(posx + tabWidth, VERTICAL_BORDER_PADDING + 3, posx + tabWidth, tabHeight - 4) + + + def DrawVC71Tab(self, dc, posx, tabIdx, tabWidth, tabHeight): + """ Draws tabs with VC71 style. """ + + fnb_bottom = self.HasFlag(FNB_BOTTOM) + + # Visual studio 7.1 style + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + dc.SetPen((tabIdx==self.GetSelection() and [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or [borderPen])[0]) + dc.SetBrush((tabIdx==self.GetSelection() and [wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or \ + [wx.Brush(wx.Colour(247, 243, 233))])[0]) + + if tabIdx == self.GetSelection(): + + posy = (fnb_bottom and [0] or [VERTICAL_BORDER_PADDING])[0] + dc.DrawRectangle(posx, posy, tabWidth, tabHeight - 1) + + # Draw a black line on the left side of the + # rectangle + dc.SetPen(wx.BLACK_PEN) + + blackLineY1 = VERTICAL_BORDER_PADDING + blackLineY2 = (fnb_bottom and [self.GetSize().y - 5] or [self.GetSize().y - 3])[0] + dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2) + + # To give the tab more 3D look we do the following + # Incase the tab is on top, + # Draw a thick white line on top of the rectangle + # Otherwise, draw a thin (1 pixel) black line at the bottom + + pen = wx.Pen((fnb_bottom and [wx.BLACK] or [wx.WHITE])[0]) + dc.SetPen(pen) + whiteLinePosY = (fnb_bottom and [blackLineY2] or [VERTICAL_BORDER_PADDING])[0] + dc.DrawLine(posx , whiteLinePosY, posx + tabWidth + 1, whiteLinePosY) + + # Draw a white vertical line to the left of the tab + dc.SetPen(wx.WHITE_PEN) + if not fnb_bottom: + blackLineY2 += 1 + + dc.DrawLine(posx, blackLineY1, posx, blackLineY2) + + else: + + # We dont draw a rectangle for non selected tabs, but only + # vertical line on the left + + blackLineY1 = (fnb_bottom and [VERTICAL_BORDER_PADDING + 2] or [VERTICAL_BORDER_PADDING + 1])[0] + blackLineY2 = self.GetSize().y - 5 + dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2) + + + def DrawStandardTab(self, dc, posx, tabIdx, tabWidth, tabHeight): + """ Draws tabs with standard style. """ + + fnb_bottom = self.HasFlag(FNB_BOTTOM) + + # Default style + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + + tabPoints = [wx.Point() for ii in xrange(7)] + tabPoints[0].x = posx + tabPoints[0].y = (fnb_bottom and [2] or [tabHeight - 2])[0] + + tabPoints[1].x = int(posx+(tabHeight-2)*math.tan(float(self._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi)) + tabPoints[1].y = (fnb_bottom and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0] + + tabPoints[2].x = tabPoints[1].x+2 + tabPoints[2].y = (fnb_bottom and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[3].x = int(posx+tabWidth-(tabHeight-2)*math.tan(float(self._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))-2 + tabPoints[3].y = (fnb_bottom and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[4].x = tabPoints[3].x+2 + tabPoints[4].y = (fnb_bottom and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0] + + tabPoints[5].x = int(tabPoints[4].x+(tabHeight-2)*math.tan(float(self._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi)) + tabPoints[5].y = (fnb_bottom and [2] or [tabHeight - 2])[0] + + tabPoints[6].x = tabPoints[0].x + tabPoints[6].y = tabPoints[0].y + + if tabIdx == self.GetSelection(): + + # Draw the tab as rounded rectangle + dc.DrawPolygon(tabPoints) + + else: + + if tabIdx != self.GetSelection() - 1: + + # Draw a vertical line to the right of the text + pt1x = tabPoints[5].x + pt1y = (fnb_bottom and [4] or [tabHeight - 6])[0] + pt2x = tabPoints[5].x + pt2y = (fnb_bottom and [tabHeight - 4] or [4])[0] + dc.DrawLine(pt1x, pt1y, pt2x, pt2y) + + if tabIdx == self.GetSelection(): + + savePen = dc.GetPen() + whitePen = wx.Pen(wx.WHITE) + whitePen.SetWidth(1) + dc.SetPen(whitePen) + + secPt = wx.Point(tabPoints[5].x + 1, tabPoints[5].y) + dc.DrawLine(tabPoints[0].x, tabPoints[0].y, secPt.x, secPt.y) + + # Restore the pen + dc.SetPen(savePen) + + + def AddPage(self, caption, selected=True, imgindex=-1): + """ + Add a page to the FlatNotebook. + + Parameters: + - window: Specifies the new page. + - caption: Specifies the text for the new page. + - selected: Specifies whether the page should be selected. + - imgindex: Specifies the optional image index for the new page. + + Return value: + True if successful, False otherwise. + """ + + if selected: + + self._iActivePage = len(self._pagesInfoVec) + + # Create page info and add it to the vector + pageInfo = PageInfo(caption, imgindex) + self._pagesInfoVec.append(pageInfo) + self.Refresh() + + + def InsertPage(self, indx, text, selected=True, imgindex=-1): + """ + Inserts a new page at the specified position. + + Parameters: + - indx: Specifies the position of the new page. + - page: Specifies the new page. + - text: Specifies the text for the new page. + - select: Specifies whether the page should be selected. + - imgindex: Specifies the optional image index for the new page. + + Return value: + True if successful, False otherwise. + """ + + if selected: + + self._iActivePage = len(self._pagesInfoVec) + + self._pagesInfoVec.insert(indx, PageInfo(text, imgindex)) + + self.Refresh() + return True + + + def OnSize(self, event): + """ Handles the wx.EVT_SIZE events for PageContainerBase. """ + + self.Refresh() # Call on paint + event.Skip() + + + def OnMiddleDown(self, event): + """ Handles the wx.EVT_MIDDLE_DOWN events for PageContainerBase. """ + + # Test if this style is enabled + style = self.GetParent().GetWindowStyleFlag() + + if not style & FNB_MOUSE_MIDDLE_CLOSES_TABS: + return + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_TAB: + self.DeletePage(tabIdx) + + event.Skip() + + + def OnRightDown(self, event): + """ Handles the wx.EVT_RIGHT_DOWN events for PageContainerBase. """ + + if self._pRightClickMenu: + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where in [FNB_TAB, FNB_TAB_X]: + + if self._pagesInfoVec[tabIdx].GetEnabled(): + # Set the current tab to be active + self.SetSelection(tabIdx) + + # If the owner has defined a context menu for the tabs, + # popup the right click menu + if self._pRightClickMenu: + self.PopupMenu(self._pRightClickMenu) + else: + # send a message to popup a custom menu + event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, self.GetParent().GetId()) + event.SetSelection(tabIdx) + event.SetOldSelection(self._iActivePage) + event.SetEventObject(self.GetParent()) + self.GetParent().GetEventHandler().ProcessEvent(event) + + event.Skip() + + + def OnLeftDown(self, event): + """ Handles the wx.EVT_LEFT_DOWN events for PageContainerBase. """ + + # Reset buttons status + self._nXButtonStatus = FNB_BTN_NONE + self._nLeftButtonStatus = FNB_BTN_NONE + self._nRightButtonStatus = FNB_BTN_NONE + self._nTabXButtonStatus = FNB_BTN_NONE + + self._nLeftClickZone, tabIdx = self.HitTest(event.GetPosition()) + + if self._nLeftClickZone == FNB_LEFT_ARROW: + self._nLeftButtonStatus = FNB_BTN_PRESSED + self.Refresh() + elif self._nLeftClickZone == FNB_RIGHT_ARROW: + self._nRightButtonStatus = FNB_BTN_PRESSED + self.Refresh() + elif self._nLeftClickZone == FNB_X: + self._nXButtonStatus = FNB_BTN_PRESSED + self.Refresh() + elif self._nLeftClickZone == FNB_TAB_X: + self._nTabXButtonStatus = FNB_BTN_PRESSED + self.Refresh() + + elif self._nLeftClickZone == FNB_TAB: + + if self._iActivePage != tabIdx: + + # In case the tab is disabled, we dont allow to choose it + if self._pagesInfoVec[tabIdx].GetEnabled(): + + oldSelection = self._iActivePage + + event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId()) + event.SetSelection(tabIdx) + event.SetOldSelection(oldSelection) + event.SetEventObject(self.GetParent()) + if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed(): + + self.SetSelection(tabIdx) + + # Fire a wxEVT_TABBEDCTRL_PAGE_CHANGED event + event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED) + event.SetOldSelection(oldSelection) + self.GetParent().GetEventHandler().ProcessEvent(event) + + + def OnLeftUp(self, event): + """ Handles the wx.EVT_LEFT_UP events for PageContainerBase. """ + + # forget the zone that was initially clicked + self._nLeftClickZone = FNB_NOWHERE + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_LEFT_ARROW: + + if self._nFrom == 0: + return + + # Make sure that the button was pressed before + if self._nLeftButtonStatus != FNB_BTN_PRESSED: + return + + self._nLeftButtonStatus = FNB_BTN_HOVER + + # We scroll left with bulks of 5 + scrollLeft = self.GetNumTabsCanScrollLeft() + + self._nFrom -= scrollLeft + if self._nFrom < 0: + self._nFrom = 0 + + self.Refresh() + + elif where == FNB_RIGHT_ARROW: + + if self._nFrom >= len(self._pagesInfoVec) - 1: + return + + # Make sure that the button was pressed before + if self._nRightButtonStatus != FNB_BTN_PRESSED: + return + + self._nRightButtonStatus = FNB_BTN_HOVER + + # Check if the right most tab is visible, if it is + # don't rotate right anymore + if self._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1): + return + + lastVisibleTab = self.GetLastVisibleTab() + if lastVisibleTab < 0: + # Probably the screen is too small for displaying even a single + # tab, in this case we do nothing + return + + self._nFrom += self.GetNumOfVisibleTabs() + self.Refresh() + + elif where == FNB_X: + + # Make sure that the button was pressed before + if self._nXButtonStatus != FNB_BTN_PRESSED: + return + + self._nXButtonStatus = FNB_BTN_HOVER + + self.DeletePage(self._iActivePage) + + elif where == FNB_TAB_X: + + # Make sure that the button was pressed before + if self._nTabXButtonStatus != FNB_BTN_PRESSED: + return + + self._nTabXButtonStatus = FNB_BTN_HOVER + + self.DeletePage(self._iActivePage) + + + def HitTest(self, pt): + """ + HitTest method for PageContainerBase. + Returns the flag (if any) and the hit page (if any). + """ + + fullrect = self.GetClientRect() + btnLeftPos = self.GetLeftButtonPos() + btnRightPos = self.GetRightButtonPos() + btnXPos = self.GetXPos() + style = self.GetParent().GetWindowStyleFlag() + + tabIdx = -1 + + if not self._pagesInfoVec: + return FNB_NOWHERE, -1 + + rect = wx.Rect(btnXPos, 6, 16, 16) + if rect.Contains(pt): + return (style & FNB_NO_X_BUTTON and [FNB_NOWHERE] or [FNB_X])[0], -1 + + rect = wx.Rect(btnRightPos, 6, 16, 16) + if rect.Contains(pt): + return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_RIGHT_ARROW])[0], -1 + + rect = wx.Rect(btnLeftPos, 6, 16, 16) + if rect.Contains(pt): + return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_LEFT_ARROW])[0], -1 + + # Test whether a left click was made on a tab + for cur in xrange(self._nFrom, len(self._pagesInfoVec)): + + pgInfo = self._pagesInfoVec[cur] + + if pgInfo.GetPosition() == wx.Point(-1, -1): + continue + + if style & FNB_X_ON_TAB and cur == self.GetSelection(): + # 'x' button exists on a tab + if self._pagesInfoVec[cur].GetXRect().Contains(pt): + return FNB_TAB_X, cur + + tabRect = wx.Rect(pgInfo.GetPosition().x, pgInfo.GetPosition().y, pgInfo.GetSize().x, pgInfo.GetSize().y) + if tabRect.Contains(pt): + # We have a match + return FNB_TAB, cur + + if self._isdragging: + # We are doing DND, so check also the region outside the tabs + + # try before the first tab + pgInfo = self._pagesInfoVec[0] + tabRect = wx.Rect(0, pgInfo.GetPosition().y, pgInfo.GetPosition().x, self.GetParent().GetSize().y) + if tabRect.Contains(pt): + return FNB_TAB, 0 + + # try after the last tab + pgInfo = self._pagesInfoVec[-1] + startpos = pgInfo.GetPosition().x+pgInfo.GetSize().x + tabRect = wx.Rect(startpos, pgInfo.GetPosition().y, fullrect.width-startpos, self.GetParent().GetSize().y) + + if tabRect.Contains(pt): + return FNB_TAB, len(self._pagesInfoVec)-1 + + # Default + return FNB_NOWHERE, -1 + + + def SetSelection(self, page): + """ Sets the selected page. """ + + book = self.GetParent() + book.SetSelection(page) + self.DoSetSelection(page) + + + def DoSetSelection(self, page): + """ Does the actual selection of a page. """ + + # Make sure that the selection is visible + style = self.GetParent().GetWindowStyleFlag() + if style & FNB_NO_NAV_BUTTONS: + # Incase that we dont have navigation buttons, + # there is no point of checking if the tab is visible + # Just do the refresh + self.Refresh() + return + + if page < len(self._pagesInfoVec): + #! fix for tabfocus + da_page = self._pParent.GetPage(page) + + # THIS IS GIVING TROUBLES!! + if da_page != None: + da_page.SetFocus() + + if not self.IsTabVisible(page): + + if page == len(self._pagesInfoVec) - 1: + # Incase the added tab is last, + # the function IsTabVisible() will always return False + # and thus will cause an evil behaviour that the new + # tab will hide all other tabs, we need to check if the + # new selected tab can fit to the current screen + if not self.CanFitToScreen(page): + self._nFrom = page + + else: + + if not self.CanFitToScreen(page): + # Redraw the tabs starting from page + self._nFrom = page + + self.Refresh() + + + def DeletePage(self, page): + """ Delete the specified page from FlatNotebook. """ + + book = self.GetParent() + book.DeletePage(page) + book.Refresh() + + + def IsTabVisible(self, page): + """ Returns whether a tab is visible or not. """ + + iLastVisiblePage = self.GetLastVisibleTab() + return page <= iLastVisiblePage and page >= self._nFrom + + + def DoDeletePage(self, page): + """ Does the actual page deletion. """ + + # Remove the page from the vector + book = self.GetParent() + self._pagesInfoVec.pop(page) + + # Thanks to Yiaanis AKA Mandrav + if self._iActivePage >= page: + self._iActivePage = self._iActivePage - 1 + + # The delete page was the last first on the array, + # but the book still has more pages, so we set the + # active page to be the first one (0) + if self._iActivePage < 0 and self._pagesInfoVec: + self._iActivePage = 0 + + # Refresh the tabs + if self._iActivePage >= 0: + + book._bForceSelection = True + book.SetSelection(self._iActivePage) + book._bForceSelection = False + + if not self._pagesInfoVec: + # Erase the page container drawings + dc = wx.ClientDC(self) + dc.Clear() + + + def DeleteAllPages(self): + """ Deletes all the pages. """ + + self._iActivePage = -1 + self._nFrom = 0 + self._pagesInfoVec = [] + + # Erase the page container drawings + dc = wx.ClientDC(self) + dc.Clear() + + + def DrawTabX(self, dc, rect, tabIdx): + """ Draws the 'X' in the selected tab (VC8 style excluded). """ + + if not self.HasFlag(FNB_X_ON_TAB) or not self.CanDrawXOnTab(): + return + + # We draw the 'x' on the active tab only + if tabIdx != self.GetSelection() or tabIdx < 0 or not self.CanFitToScreen(tabIdx): + return + + # Set the bitmap according to the button status + if self._nTabXButtonStatus == FNB_BTN_HOVER: + xBmp = wx.BitmapFromXPMData(x_button_hilite_xpm) + elif self._nTabXButtonStatus == FNB_BTN_PRESSED: + xBmp = wx.BitmapFromXPMData(x_button_pressed_xpm) + else: + xBmp = wx.BitmapFromXPMData(x_button_xpm) + + # Set the masking + xBmp.SetMask(wx.Mask(xBmp, MASK_COLOR)) + + # erase old button + dc.DrawBitmap(self._tabXBgBmp, rect.x, rect.y) + + # Draw the new bitmap + dc.DrawBitmap(xBmp, rect.x, rect.y, True) + + # Update the vectpr + self._pagesInfoVec[tabIdx].SetXRect(rect) + + + def DrawLeftArrow(self, dc): + """ Draw the left navigation arrow. """ + + style = self.GetParent().GetWindowStyleFlag() + if style & FNB_NO_NAV_BUTTONS: + return + + # Make sure that there are pages in the container + if not self._pagesInfoVec: + return + + # Set the bitmap according to the button status + if self._nLeftButtonStatus == FNB_BTN_HOVER: + arrowBmp = wx.BitmapFromXPMData(left_arrow_hilite_xpm) + elif self._nLeftButtonStatus == FNB_BTN_PRESSED: + arrowBmp = wx.BitmapFromXPMData(left_arrow_pressed_xpm) + else: + arrowBmp = wx.BitmapFromXPMData(left_arrow_xpm) + + if self._nFrom == 0: + # Handle disabled arrow + arrowBmp = wx.BitmapFromXPMData(left_arrow_disabled_xpm) + + arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR)) + + # Erase old bitmap + posx = self.GetLeftButtonPos() + dc.DrawBitmap(self._leftBgBmp, posx, 6) + + # Draw the new bitmap + dc.DrawBitmap(arrowBmp, posx, 6, True) + + + def DrawRightArrow(self, dc): + """ Draw the right navigation arrow. """ + + style = self.GetParent().GetWindowStyleFlag() + if style & FNB_NO_NAV_BUTTONS: + return + + # Make sure that there are pages in the container + if not self._pagesInfoVec: + return + + # Set the bitmap according to the button status + if self._nRightButtonStatus == FNB_BTN_HOVER: + arrowBmp = wx.BitmapFromXPMData(right_arrow_hilite_xpm) + elif self._nRightButtonStatus == FNB_BTN_PRESSED: + arrowBmp = wx.BitmapFromXPMData(right_arrow_pressed_xpm) + else: + arrowBmp = wx.BitmapFromXPMData(right_arrow_xpm) + + # Check if the right most tab is visible, if it is + # don't rotate right anymore + if self._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1): + arrowBmp = wx.BitmapFromXPMData(right_arrow_disabled_xpm) + + arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR)) + + # erase old bitmap + posx = self.GetRightButtonPos() + dc.DrawBitmap(self._rightBgBmp, posx, 6) + + # Draw the new bitmap + dc.DrawBitmap(arrowBmp, posx, 6, True) + + + def DrawX(self, dc): + """ Draw the 'X' navigation button in the navigation area. """ + + # Check if this style is enabled + style = self.GetParent().GetWindowStyleFlag() + if style & FNB_NO_X_BUTTON: + return + + # Make sure that there are pages in the container + if not self._pagesInfoVec: + return + + # Set the bitmap according to the button status + if self._nXButtonStatus == FNB_BTN_HOVER: + xbmp = wx.BitmapFromXPMData(x_button_hilite_xpm) + elif self._nXButtonStatus == FNB_BTN_PRESSED: + xbmp = wx.BitmapFromXPMData(x_button_pressed_xpm) + else: + xbmp = wx.BitmapFromXPMData(x_button_xpm) + + xbmp.SetMask(wx.Mask(xbmp, MASK_COLOR)) + # erase old bitmap + + posx = self.GetXPos() + dc.DrawBitmap(self._xBgBmp, posx, 6) + + # Draw the new bitmap + dc.DrawBitmap(xbmp, posx, 6, True) + + + def OnMouseMove(self, event): + """ Handles the wx.EVT_MOTION for PageContainerBase. """ + + if self._pagesInfoVec and self.IsShown(): + + xButtonStatus = self._nXButtonStatus + xTabButtonStatus = self._nTabXButtonStatus + rightButtonStatus = self._nRightButtonStatus + leftButtonStatus = self._nLeftButtonStatus + style = self.GetParent().GetWindowStyleFlag() + + self._nXButtonStatus = FNB_BTN_NONE + self._nRightButtonStatus = FNB_BTN_NONE + self._nLeftButtonStatus = FNB_BTN_NONE + self._nTabXButtonStatus = FNB_BTN_NONE + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_X: + if event.LeftIsDown(): + + self._nXButtonStatus = (self._nLeftClickZone==FNB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0] + + else: + + self._nXButtonStatus = FNB_BTN_HOVER + + elif where == FNB_TAB_X: + if event.LeftIsDown(): + + self._nTabXButtonStatus = (self._nLeftClickZone==FNB_TAB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0] + + else: + + self._nTabXButtonStatus = FNB_BTN_HOVER + + elif where == FNB_RIGHT_ARROW: + if event.LeftIsDown(): + + self._nRightButtonStatus = (self._nLeftClickZone==FNB_RIGHT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0] + + else: + + self._nRightButtonStatus = FNB_BTN_HOVER + + elif where == FNB_LEFT_ARROW: + if event.LeftIsDown(): + + self._nLeftButtonStatus = (self._nLeftClickZone==FNB_LEFT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0] + + else: + + self._nLeftButtonStatus = FNB_BTN_HOVER + + elif where == FNB_TAB: + # Call virtual method for showing tooltip + self.ShowTabTooltip(tabIdx) + + if not self.GetEnabled(tabIdx): + # Set the cursor to be 'No-entry' + wx.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY)) + + # Support for drag and drop + if event.LeftIsDown() and not (style & FNB_NODRAG): + + self._isdragging = True + draginfo = FNBDragInfo(self, tabIdx) + drginfo = cPickle.dumps(draginfo) + dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook")) + dataobject.SetData(drginfo) + dragSource = wx.DropSource(self) + dragSource.SetData(dataobject) + dragSource.DoDragDrop(wx.Drag_DefaultMove) + + bRedrawX = self._nXButtonStatus != xButtonStatus + bRedrawRight = self._nRightButtonStatus != rightButtonStatus + bRedrawLeft = self._nLeftButtonStatus != leftButtonStatus + bRedrawTabX = self._nTabXButtonStatus != xTabButtonStatus + + if (bRedrawX or bRedrawRight or bRedrawLeft or bRedrawTabX): + + dc = wx.ClientDC(self) + if bRedrawX: + + self.DrawX(dc) + + if bRedrawLeft: + + self.DrawLeftArrow(dc) + + if bRedrawRight: + + self.DrawRightArrow(dc) + + if bRedrawTabX: + + self.DrawTabX(dc, self._pagesInfoVec[tabIdx].GetXRect(), tabIdx) + + event.Skip() + + + def GetLastVisibleTab(self): + """ Returns the last visible tab. """ + + ii = 0 + + for ii in xrange(self._nFrom, len(self._pagesInfoVec)): + + if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1): + break + + return ii-1 + + + def GetNumTabsCanScrollLeft(self): + """ Returns the number of tabs than can be scrolled left. """ + + # Reserved area for the buttons (<>x) + rect = self.GetClientRect() + clientWidth = rect.width + posx = self._pParent._nPadding + numTabs = 0 + pom = 0 + + dc = wx.ClientDC(self) + + # In case we have error prevent crash + if self._nFrom < 0: + return 0 + + style = self.GetParent().GetWindowStyleFlag() + + for ii in xrange(self._nFrom, -1, -1): + + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(boldFont) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 6 pixels as padding + if style & FNB_VC71: + tabHeight = (style & FNB_BOTTOM and [tabHeight - 4] or [tabHeight])[0] + elif style & FNB_FANCY_TABS: + tabHeight = (style & FNB_BOTTOM and [tabHeight - 3] or [tabHeight])[0] + + width, pom = dc.GetTextExtent(self.GetPageText(ii)) + if style != FNB_VC71: + shapePoints = int(tabHeight*math.tan(float(self._pagesInfoVec[ii].GetTabAngle())/180.0*math.pi)) + else: + shapePoints = 0 + + tabWidth = self._pParent._nPadding*2 + width + + if not (style & FNB_VC71): + # Default style + tabWidth += 2*shapePoints + + hasImage = self._ImageList != None and self._pagesInfoVec[ii].GetImageIndex() != -1 + + # For VC71 style, we only add the icon size (16 pixels) + if hasImage: + + if not self.IsDefaultTabs(): + tabWidth += 16 + self._pParent._nPadding + else: + # Default style + tabWidth += 16 + self._pParent._nPadding + shapePoints/2 + + if posx + tabWidth + self.GetButtonsAreaLength() >= clientWidth: + break + + numTabs = numTabs + 1 + posx += tabWidth + + return numTabs + + + def IsDefaultTabs(self): + """ Returns whether a tab has a default style. """ + + style = self.GetParent().GetWindowStyleFlag() + res = (style & FNB_VC71) or (style & FNB_FANCY_TABS) + return not res + + + def AdvanceSelection(self, bForward=True): + """ + Cycles through the tabs. + The call to this function generates the page changing events. + """ + + nSel = self.GetSelection() + + if nSel < 0: + return + + nMax = self.GetPageCount() - 1 + if bForward: + self.SetSelection((nSel == nMax and [0] or [nSel + 1])[0]) + else: + self.SetSelection((nSel == 0 and [nMax] or [nSel - 1])[0]) + + + def OnMouseLeave(self, event): + """ Handles the wx.EVT_LEAVE_WINDOW event for PageContainerBase. """ + + self._nLeftButtonStatus = FNB_BTN_NONE + self._nXButtonStatus = FNB_BTN_NONE + self._nRightButtonStatus = FNB_BTN_NONE + self._nTabXButtonStatus = FNB_BTN_NONE + + dc = wx.ClientDC(self) + self.DrawX(dc) + self.DrawLeftArrow(dc) + self.DrawRightArrow(dc) + + selection = self.GetSelection() + + if selection != -1: + self.DrawTabX(dc, self._pagesInfoVec[selection].GetXRect(), selection) + + event.Skip() + + + def OnMouseEnterWindow(self, event): + """ Handles the wx.EVT_ENTER_WINDOW event for PageContainerBase. """ + + self._nLeftButtonStatus = FNB_BTN_NONE + self._nXButtonStatus = FNB_BTN_NONE + self._nRightButtonStatus = FNB_BTN_NONE + self._nLeftClickZone = FNB_BTN_NONE + + event.Skip() + + + def ShowTabTooltip(self, tabIdx): + """ Shows a tab tooltip. """ + + pWindow = self._pParent.GetPage(tabIdx) + + if pWindow: + pToolTip = pWindow.GetToolTip() + if pToolTip and pToolTip.GetWindow() == pWindow: + self.SetToolTipString(pToolTip.GetTip()) + + + def FillGradientColor(self, dc, rect): + """ Gradient fill from colour 1 to colour 2 with top to bottom. """ + + if rect.height < 1 or rect.width < 1: + return + + size = rect.height + + # calculate gradient coefficients + style = self.GetParent().GetWindowStyleFlag() + col2 = ((style & FNB_BOTTOM) and [self._colorTo] or [self._colorFrom])[0] + col1 = ((style & FNB_BOTTOM) and [self._colorFrom] or [self._colorTo])[0] + + rf, gf, bf = 0, 0, 0 + rstep = float(col2.Red() - col1.Red())/float(size) + gstep = float(col2.Green() - col1.Green())/float(size) + bstep = float(col2.Blue() - col1.Blue())/float(size) + + for y in xrange(rect.y, rect.y + size): + currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf) + dc.SetBrush(wx.Brush(currCol)) + dc.SetPen(wx.Pen(currCol)) + dc.DrawLine(rect.x, y, rect.x + rect.width, y) + rf += rstep + gf += gstep + bf += bstep + + + def SetPageImageIndex(self, page, imgindex): + """ Sets the image index associated to a page. """ + + if page < len(self._pagesInfoVec): + + self._pagesInfoVec[page].SetImageIndex(imgindex) + self.Refresh() + + + def GetPageImageIndex(self, page): + """ Returns the image index associated to a page. """ + + if page < len(self._pagesInfoVec): + + return self._pagesInfoVec[page].GetImageIndex() + + return -1 + + + def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer): + """ Handles the drop action from a DND operation. """ + + # Disable drag'n'drop for disabled tab + if not wnd_oldContainer._pagesInfoVec[nTabPage].GetEnabled(): + return wx.DragCancel + + self._isdragging = True + oldContainer = wnd_oldContainer + nIndex = -1 + + where, nIndex = self.HitTest(wx.Point(x, y)) + + oldNotebook = oldContainer.GetParent() + newNotebook = self.GetParent() + + if oldNotebook == newNotebook: + + if nTabPage >= 0: + + if where == FNB_TAB: + self.MoveTabPage(nTabPage, nIndex) + + else: + + if wx.Platform in ["__WXMSW__", "__WXGTK__"]: + if nTabPage >= 0: + + window = oldNotebook.GetPage(nTabPage) + + if window: + where, nIndex = newNotebook._pages.HitTest(wx.Point(x, y)) + caption = oldContainer.GetPageText(nTabPage) + imageindex = oldContainer.GetPageImageIndex(nTabPage) + oldNotebook.RemovePage(nTabPage) + window.Reparent(newNotebook) + + newNotebook.InsertPage(nIndex, window, caption, True, imageindex) + + self._isdragging = False + + return wx.DragMove + + + def MoveTabPage(self, nMove, nMoveTo): + """ Moves a tab inside the same FlatNotebook. """ + + if nMove == nMoveTo: + return + + elif nMoveTo < len(self._pParent._windows): + nMoveTo = nMoveTo + 1 + + self._pParent.Freeze() + + # Remove the window from the main sizer + nCurSel = self._pParent._pages.GetSelection() + self._pParent._mainSizer.Detach(self._pParent._windows[nCurSel]) + self._pParent._windows[nCurSel].Hide() + + pWindow = self._pParent._windows[nMove] + self._pParent._windows.pop(nMove) + self._pParent._windows.insert(nMoveTo-1, pWindow) + + pgInfo = self._pagesInfoVec[nMove] + + self._pagesInfoVec.pop(nMove) + self._pagesInfoVec.insert(nMoveTo - 1, pgInfo) + + # Add the page according to the style + pSizer = self._pParent._mainSizer + style = self.GetParent().GetWindowStyleFlag() + + if style & FNB_BOTTOM: + + pSizer.Insert(0, pWindow, 1, wx.EXPAND) + + else: + + # We leave a space of 1 pixel around the window + pSizer.Add(pWindow, 1, wx.EXPAND) + + pWindow.Show() + + pSizer.Layout() + self._iActivePage = nMoveTo - 1 + self.DoSetSelection(self._iActivePage) + self.Refresh() + self._pParent.Thaw() + + + def CanFitToScreen(self, page): + """ Returns whether all the tabs can fit in the available space. """ + + # Incase the from is greater than page, + # we need to reset the self._nFrom, so in order + # to force the caller to do so, we return False + if self._nFrom > page: + return False + + # Calculate the tab width including borders and image if any + dc = wx.ClientDC(self) + + style = self.GetParent().GetWindowStyleFlag() + + width, height = dc.GetTextExtent("Tp") + width, pom = dc.GetTextExtent(self.GetPageText(page)) + + tabHeight = height + FNB_HEIGHT_SPACER # We use 6 pixels as padding + + if style & FNB_VC71: + tabHeight = (style & FNB_BOTTOM and [tabHeight - 4] or [tabHeight])[0] + elif style & FNB_FANCY_TABS: + tabHeight = (style & FNB_BOTTOM and [tabHeight - 2] or [tabHeight])[0] + + tabWidth = self._pParent._nPadding * 2 + width + + if not style & FNB_VC71: + shapePoints = int(tabHeight*math.tan(float(self._pagesInfoVec[page].GetTabAngle())/180.0*math.pi)) + else: + shapePoints = 0 + + if not style & FNB_VC71: + # Default style + tabWidth += 2*shapePoints + + hasImage = self._ImageList != None + + if hasImage: + hasImage &= self._pagesInfoVec[page].GetImageIndex() != -1 + + # For VC71 style, we only add the icon size (16 pixels) + if hasImage and (style & FNB_VC71 or style & FNB_FANCY_TABS): + tabWidth += 16 + else: + # Default style + tabWidth += 16 + shapePoints/2 + + # Check if we can draw more + posx = self._pParent._nPadding + + if self._nFrom >= 0: + for ii in xrange(self._nFrom, len(self._pagesInfoVec)): + if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1): + break + posx += self._pagesInfoVec[ii].GetSize().x + + rect = self.GetClientRect() + clientWidth = rect.width + + if posx + tabWidth + self.GetButtonsAreaLength() >= clientWidth: + return False + + return True + + + def GetNumOfVisibleTabs(self): + """ Returns the number of visible tabs. """ + + count = 0 + for ii in xrange(self._nFrom, len(self._pagesInfoVec)): + if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1): + break + count = count + 1 + + return count + + + def GetEnabled(self, page): + """ Returns whether a tab is enabled or not. """ + + if page >= len(self._pagesInfoVec): + return True # Seems strange, but this is the default + + return self._pagesInfoVec[page].GetEnabled() + + + def Enable(self, page, enabled=True): + """ Enables or disables a tab. """ + + if page >= len(self._pagesInfoVec): + return + + self._pagesInfoVec[page].Enable(enabled) + + + def GetLeftButtonPos(self): + """ Returns the left button position in the navigation area. """ + + style = self.GetParent().GetWindowStyleFlag() + rect = self.GetClientRect() + clientWidth = rect.width + + if style & FNB_NO_X_BUTTON: + return clientWidth - 38 + else: + return clientWidth - 54 + + + def GetRightButtonPos(self): + """ Returns the right button position in the navigation area. """ + + style = self.GetParent().GetWindowStyleFlag() + rect = self.GetClientRect() + clientWidth = rect.width + + if style & FNB_NO_X_BUTTON: + return clientWidth - 22 + else: + return clientWidth - 38 + + + def GetXPos(self): + """ Returns the 'X' button position in the navigation area. """ + + style = self.GetParent().GetWindowStyleFlag() + rect = self.GetClientRect() + clientWidth = rect.width + + if style & FNB_NO_X_BUTTON: + return clientWidth + else: + return clientWidth - 22 + + + def GetButtonsAreaLength(self): + """ Returns the navigation area width. """ + + style = self.GetParent().GetWindowStyleFlag() + + if style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON: + return 0 + elif style & FNB_NO_NAV_BUTTONS and not style & FNB_NO_X_BUTTON: + return 53 - 16 + elif not style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON: + return 53 - 16 + else: + # All buttons + return 53 + + + def GetSingleLineBorderColor(self): + + if self.HasFlag(FNB_FANCY_TABS): + return self._colorFrom + + return wx.WHITE + + + def DrawTabsLine(self, dc): + """ Draws a line over the tabs. """ + + clntRect = self.GetClientRect() + clientRect3 = wx.Rect(0, 0, clntRect.width, clntRect.height) + + if self.HasFlag(FNB_BOTTOM): + + clientRect = wx.Rect(0, 2, clntRect.width, clntRect.height - 2) + clientRect2 = wx.Rect(0, 1, clntRect.width, clntRect.height - 1) + + else: + + clientRect = wx.Rect(0, 0, clntRect.width, clntRect.height - 2) + clientRect2 = wx.Rect(0, 0, clntRect.width, clntRect.height - 1) + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(wx.Pen(self.GetSingleLineBorderColor())) + dc.DrawRectangleRect(clientRect2) + dc.DrawRectangleRect(clientRect3) + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))) + dc.DrawRectangleRect(clientRect) + + if not self.HasFlag(FNB_TABS_BORDER_SIMPLE): + + dc.SetPen(wx.Pen((self.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [self._tabAreaColor])[0])) + dc.DrawLine(0, 0, 0, clientRect.height+1) + + if self.HasFlag(FNB_BOTTOM): + + dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1) + + else: + dc.DrawLine(0, 0, clientRect.width, 0) + + dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1) + + + def HasFlag(self, flag): + """ Returns whether a flag is present in the FlatNotebook style. """ + + style = self.GetParent().GetWindowStyleFlag() + res = (style & flag and [True] or [False])[0] + return res + + + def ClearFlag(self, flag): + """ Deletes a flag from the FlatNotebook style. """ + + style = self.GetParent().GetWindowStyleFlag() + style &= ~flag + self.SetWindowStyleFlag(style) + + + def TabHasImage(self, tabIdx): + """ Returns whether a tab has an associated image index or not. """ + + if self._ImageList: + return self._pagesInfoVec[tabIdx].GetImageIndex() != -1 + + return False + + + def OnLeftDClick(self, event): + """ Handles the wx.EVT_LEFT_DCLICK event for PageContainerBase. """ + + if self.HasFlag(FNB_DCLICK_CLOSES_TABS): + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_TAB: + self.DeletePage(tabIdx) + + else: + + event.Skip() + + + def SetImageList(self, imglist): + """ Sets the image list for the page control. """ + + self._ImageList = imglist + + + def GetImageList(self): + """ Returns the image list for the page control. """ + + return self._ImageList + + + def GetSelection(self): + """ Returns the current selected page. """ + + return self._iActivePage + + + def GetPageCount(self): + """ Returns the number of tabs in the FlatNotebook control. """ + + return len(self._pagesInfoVec) + + + def GetPageText(self, page): + """ Returns the tab caption of the page. """ + + return self._pagesInfoVec[page].GetCaption() + + + def SetPageText(self, page, text): + """ Sets the tab caption of the page. """ + + self._pagesInfoVec[page].SetCaption(text) + return True + + + def CanDrawXOnTab(self): + """ Returns whether an 'X' can be drawn on a tab (all styles except VC8. """ + + return True + + +# ---------------------------------------------------------------------------- # +# Class FlatNotebook +# Simple super class based on PageContainerBase +# ---------------------------------------------------------------------------- # + +class FlatNotebook(FlatNotebookBase): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, name="FlatNotebook"): + """ + Default class constructor. + + It is better to use directly the StyledNotebook class (see below) and then + assigning the style you wish instead of calling FlatNotebook. + """ + + style |= wx.TAB_TRAVERSAL + + FlatNotebookBase.__init__(self, parent, id, pos, size, style, name) + self._pages = self.CreatePageContainer() + + + def CreatePageContainer(self): + """ Creates the page container. """ + + return FlatNotebookBase.CreatePageContainer(self) + + +#-------------------------------------------------------------------- +# StyledNotebook - a notebook with look n feel of Visual Studio 2005 +#-------------------------------------------------------------------- + +class StyledNotebook(FlatNotebookBase): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, name="StyledNotebook"): + """ Default class constructor. + + It is better to use directly the StyledNotebook class and then + assigning the style you wish instead of calling FlatNotebook. + """ + + style |= wx.TAB_TRAVERSAL + + FlatNotebookBase.__init__(self, parent, id, pos, size, style, name) + + # Custom initialization of the tab area + if style & FNB_VC8: + # Initialise the default style colors + self.SetNonActiveTabTextColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT)) + + + def CreatePageContainer(self): + """ Creates the page container. """ + + return StyledTabsContainer(self, wx.ID_ANY) + + +# ---------------------------------------------------------------------------- # +# Class StyledTabsContainer +# Acts as a container for the pages you add to FlatNotebook +# A more generic and more powerful implementation of PageContainerBase, can +# handle also VC8 tabs style +# ---------------------------------------------------------------------------- # + +class StyledTabsContainer(PageContainerBase): + + def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0): + """ Default class constructor. """ + + self._factor = 1 + + self._colorTo = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 0) + self._colorFrom = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 60) + + PageContainerBase.__init__(self, parent, id, pos, size, style) + + self.Bind(wx.EVT_PAINT, self.OnPaint) + + + def NumberTabsCanFit(self, dc): + """ Returns the number of tabs that can fit inside the available space. """ + + rect = self.GetClientRect() + clientWidth = rect.width + + # Empty results + vTabInfo = [] + + # We take the maxmimum font size, this is + # achieved by setting the font to be bold + font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + font.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(font) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels + # The drawing starts from posx + posx = self._pParent.GetPadding() + + for i in xrange(self._nFrom, len(self._pagesInfoVec)): + + width, pom = dc.GetTextExtent(self.GetPageText(i)) + + # Set a minimum size to a tab + if width < 20: + width = 20 + + tabWidth = self._pParent.GetPadding() * 2 + width + + # Add the image width if it exist + if self.TabHasImage(i): + tabWidth += 16 + self._pParent.GetPadding() + + vc8glitch = tabHeight + FNB_HEIGHT_SPACER + if posx + tabWidth + vc8glitch + self.GetButtonsAreaLength() >= clientWidth: + break + + # Add a result to the returned vector + tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth , tabHeight) + vTabInfo.append(tabRect) + + # Advance posx + posx += tabWidth + FNB_HEIGHT_SPACER + + return vTabInfo + + + def GetNumTabsCanScrollLeft(self): + """ Returns the number of tabs than can be scrolled left. """ + + # Reserved area for the buttons (<>x) + rect = self.GetClientRect() + clientWidth = rect.width + posx = self._pParent.GetPadding() + numTabs = 0 + pom = 0 + + dc = wx.ClientDC(self) + + # Incase we have error prevent crash + if self._nFrom < 0: + return 0 + + style = self.GetParent().GetWindowStyleFlag() + + for i in xrange(self._nFrom, -1, -1): + + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(boldFont) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 6 pixels as padding + + if style & FNB_VC71: + tabHeight = (self.HasFlag(FNB_BOTTOM) and [tabHeight - 4] or [tabHeight])[0] + elif style & FNB_FANCY_TABS: + tabHeight = (self.HasFlag(FNB_BOTTOM) and [tabHeight - 3] or [tabHeight])[0] + + width, pom = dc.GetTextExtent(self.GetPageText(i)) + + if style != FNB_VC71: + shapePoints = int(tabHeight*math.tan(float(self._pagesInfoVec[i].GetTabAngle())/180.0*math.pi)) + else: + shapePoints = 0 + + tabWidth = self._pParent.GetPadding() * 2 + width + if not style & FNB_VC71: + # Default style + tabWidth += 2*shapePoints + + # For VC71 style, we only add the icon size (16 pixels) + if self.TabHasImage(i): + + if not self.IsDefaultTabs(): + tabWidth += 16 + self._pParent.GetPadding() + else: + # Default style + tabWidth += 16 + self._pParent.GetPadding() + shapePoints/2 + + vc8glitch = (style & FNB_VC8 and [tabHeight + FNB_HEIGHT_SPACER] or [0])[0] + + if posx + tabWidth + vc8glitch + self.GetButtonsAreaLength() >= clientWidth: + break + + numTabs = numTabs + 1 + posx += tabWidth + + return numTabs + + + def CanDrawXOnTab(self): + """ Returns whether an 'X' button can be drawn on a tab (not VC8 style). """ + + style = self.GetParent().GetWindowStyleFlag() + isVC8 = (style & FNB_VC8 and [True] or [False])[0] + return not isVC8 + + + def IsDefaultTabs(self): + """ Returns whether a tab has a default style or not. """ + + style = self.GetParent().GetWindowStyleFlag() + res = (style & FNB_VC71) or (style & FNB_FANCY_TABS) or (style & FNB_VC8) + return not res + + + def HitTest(self, pt): + """ HitTest specific method for VC8 style. """ + + fullrect = self.GetClientRect() + btnLeftPos = self.GetLeftButtonPos() + btnRightPos = self.GetRightButtonPos() + btnXPos = self.GetXPos() + style = self.GetParent().GetWindowStyleFlag() + tabIdx = -1 + + if not self._pagesInfoVec: + return FNB_NOWHERE, -1 + + rect = wx.Rect(btnXPos, 8, 16, 16) + + if rect.Contains(pt): + return (style & FNB_NO_X_BUTTON and [FNB_NOWHERE] or [FNB_X])[0], -1 + + rect = wx.Rect(btnRightPos, 8, 16, 16) + + if rect.Contains(pt): + return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_RIGHT_ARROW])[0], -1 + + rect = wx.Rect(btnLeftPos, 8, 16, 16) + + if rect.Contains(pt): + return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_LEFT_ARROW])[0], -1 + + # Test whether a left click was made on a tab + bFoundMatch = False + + for cur in xrange(self._nFrom, len(self._pagesInfoVec)): + + pgInfo = self._pagesInfoVec[cur] + + if pgInfo.GetPosition() == wx.Point(-1, -1): + continue + + if style & FNB_VC8: + + if self._pagesInfoVec[cur].GetRegion().Contains(pt.x, pt.y): + + if bFoundMatch or cur == self.GetSelection(): + + return FNB_TAB, cur + + tabIdx = cur + bFoundMatch = True + + else: + + if style & FNB_X_ON_TAB and cur == self.GetSelection(): + + # 'x' button exists on a tab + if self._pagesInfoVec[cur].GetXRect().Contains(pt): + return FNB_TAB_X, cur + + tabRect = wx.Rect(pgInfo.GetPosition().x, pgInfo.GetPosition().y, pgInfo.GetSize().x, pgInfo.GetSize().y) + + if tabRect.Contains(pt): + return FNB_TAB, cur + + if bFoundMatch: + return FNB_TAB, tabIdx + + if self._isdragging: + # We are doing DND, so check also the region outside the tabs + # try before the first tab + pgInfo = self._pagesInfoVec[0] + tabRect = wx.Rect(0, pgInfo.GetPosition().y, pgInfo.GetPosition().x, self.GetParent().GetSize().y) + if tabRect.Contains(pt): + return FNB_TAB, 0 + + # try after the last tab + pgInfo = self._pagesInfoVec[-1] + startpos = pgInfo.GetPosition().x+pgInfo.GetSize().x + tabRect = wx.Rect(startpos, pgInfo.GetPosition().y, fullrect.width-startpos, self.GetParent().GetSize().y) + + if tabRect.Contains(pt): + return FNB_TAB, len(self._pagesInfoVec) + + # Default + return FNB_NOWHERE, -1 + + + def OnPaint(self, event): + """ + Handles the wx.EVT_PAINT event for StyledTabsContainer. + Switches to PageContainerBase.OnPaint() method if the style is not VC8. + """ + + if not self.HasFlag(FNB_VC8): + + PageContainerBase.OnPaint(self, event) + return + + # Visual studio 8 style + dc = wx.BufferedPaintDC(self) + + if "__WXMAC__" in wx.PlatformInfo: + # Works well on MSW & GTK, however this lines should be skipped on MAC + if not self._pagesInfoVec or self._nFrom >= len(self._pagesInfoVec): + self.Hide() + event.Skip() + return + + # Set the font for measuring the tab height + normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + + if "__WXGTK__" in wx.PlatformInfo: + dc.SetFont(boldFont) + + width, height = dc.GetTextExtent("Tp") + + tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding + + # Calculate the number of rows required for drawing the tabs + rect = self.GetClientRect() + + # Set the maximum client size + self.SetSizeHints(self.GetButtonsAreaLength(), tabHeight) + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + + # Create brushes + backBrush = wx.Brush(self._tabAreaColor) + noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE)) + selBrush = wx.Brush(self._activeTabColor) + size = self.GetSize() + + # Background + dc.SetTextBackground(self.GetBackgroundColour()) + dc.SetTextForeground(self._activeTextColor) + + # If border style is set, set the pen to be border pen + if self.HasFlag(FNB_TABS_BORDER_SIMPLE): + dc.SetPen(borderPen) + else: + dc.SetPen(wx.TRANSPARENT_PEN) + + lightFactor = (self.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0] + # For VC8 style, we color the tab area in gradient coloring + PaintStraightGradientBox(dc, self.GetClientRect(), self._tabAreaColor, LightColour(self._tabAreaColor, lightFactor)) + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangle(0, 0, size.x, size.y) + + # Take 3 bitmaps for the background for the buttons + + mem_dc = wx.MemoryDC() + + #--------------------------------------- + # X button + #--------------------------------------- + rect = wx.Rect(self.GetXPos(), 6, 16, 14) + mem_dc.SelectObject(self._xBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + #--------------------------------------- + # Right button + #--------------------------------------- + rect = wx.Rect(self.GetRightButtonPos(), 6, 16, 14) + mem_dc.SelectObject(self._rightBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + #--------------------------------------- + # Left button + #--------------------------------------- + rect = wx.Rect(self.GetLeftButtonPos(), 6, 16, 14) + mem_dc.SelectObject(self._leftBgBmp) + mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y) + mem_dc.SelectObject(wx.NullBitmap) + + # We always draw the bottom/upper line of the tabs + # regradless the style + dc.SetPen(borderPen) + self.DrawTabsLine(dc) + + # Restore the pen + dc.SetPen(borderPen) + + # Draw labels + dc.SetFont(boldFont) + activeTabPosx = 0 + + # Update all the tabs from 0 to 'self._nFrom' to be non visible + for i in xrange(self._nFrom): + + self._pagesInfoVec[i].SetPosition(wx.Point(-1, -1)) + self._pagesInfoVec[i].GetRegion().Clear() + + # Draw the visible tabs, in VC8 style, we draw them from right to left + vTabsInfo = self.NumberTabsCanFit(dc) + + for cur in xrange(len(vTabsInfo) - 1, -1, -1): + + # 'i' points to the index of the currently drawn tab + # in self._pagesInfoVec vector + i = self._nFrom + cur + dc.SetPen(borderPen) + dc.SetBrush((i==self.GetSelection() and [selBrush] or [noselBrush])[0]) + + # Calculate the text length using the bold font, so when selecting a tab + # its width will not change + dc.SetFont(boldFont) + width, pom = dc.GetTextExtent(self.GetPageText(i)) + + # Now set the font to the correct font + dc.SetFont((i==self.GetSelection() and [boldFont] or [normalFont])[0]) + + # Set a minimum size to a tab + if width < 20: + width = 20 + + # Add the padding to the tab width + # Tab width: + # +-----------------------------------------------------------+ + # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING | + # +-----------------------------------------------------------+ + + tabWidth = self._pParent.GetPadding() * 2 + width + imageYCoord = (self.HasFlag(FNB_BOTTOM) and [6] or [8])[0] + + if self.TabHasImage(i): + tabWidth += 16 + self._pParent.GetPadding() + + posx = vTabsInfo[cur].x + + # By default we clean the tab region + # incase we use the VC8 style which requires + # the region, it will be filled by the function + # drawVc8Tab + self._pagesInfoVec[i].GetRegion().Clear() + + # Clean the 'x' buttn on the tab + # 'Clean' rectanlge is a rectangle with width or height + # with values lower than or equal to 0 + self._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1)) + + # Draw the tab + # Incase we are drawing the active tab + # we need to redraw so it will appear on top + # of all other tabs + if i == self.GetSelection(): + + activeTabPosx = posx + + else: + + self.DrawVC8Tab(dc, posx, i, tabWidth, tabHeight) + + # Text drawing offset from the left border of the + # rectangle + # The width of the images are 16 pixels + vc8ShapeLen = tabHeight + + if self.TabHasImage(i): + textOffset = self._pParent.GetPadding() * 2 + 16 + vc8ShapeLen + else: + textOffset = self._pParent.GetPadding() + vc8ShapeLen + + # Set the non-active text color + if i != self.GetSelection(): + dc.SetTextForeground(self._nonActiveTextColor) + + if self.TabHasImage(i): + imageXOffset = textOffset - 16 - self._pParent.GetPadding() + self._ImageList.Draw(self._pagesInfoVec[i].GetImageIndex(), dc, + posx + imageXOffset, imageYCoord, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + dc.DrawText(self.GetPageText(i), posx + textOffset, imageYCoord) + + textWidth, textHeight = dc.GetTextExtent(self.GetPageText(i)) + + # Restore the text forground + dc.SetTextForeground(self._activeTextColor) + + # Update the tab position & size + self._pagesInfoVec[i].SetPosition(wx.Point(posx, VERTICAL_BORDER_PADDING)) + self._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight)) + + # Incase we are in VC8 style, redraw the active tab (incase it is visible) + if self.GetSelection() >= self._nFrom and self.GetSelection() < self._nFrom + len(vTabsInfo): + + hasImage = self.TabHasImage(self.GetSelection()) + + dc.SetFont(boldFont) + width, pom = dc.GetTextExtent(self.GetPageText(self.GetSelection())) + + tabWidth = self._pParent.GetPadding() * 2 + width + + if hasImage: + tabWidth += 16 + self._pParent.GetPadding() + + # Set the active tab font, pen brush and font-color + dc.SetPen(borderPen) + dc.SetBrush(selBrush) + dc.SetFont(boldFont) + dc.SetTextForeground(self._activeTextColor) + self.DrawVC8Tab(dc, activeTabPosx, self.GetSelection(), tabWidth, tabHeight) + + # Text drawing offset from the left border of the + # rectangle + # The width of the images are 16 pixels + vc8ShapeLen = tabHeight - VERTICAL_BORDER_PADDING - 2 + if hasImage: + textOffset = self._pParent.GetPadding() * 2 + 16 + vc8ShapeLen + else: + textOffset = self._pParent.GetPadding() + vc8ShapeLen + + # Draw the image for the tab if any + imageYCoord = (self.HasFlag(FNB_BOTTOM) and [6] or [8])[0] + + if hasImage: + imageXOffset = textOffset - 16 - self._pParent.GetPadding() + self._ImageList.Draw(self._pagesInfoVec[self.GetSelection()].GetImageIndex(), dc, + activeTabPosx + imageXOffset, imageYCoord, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + dc.DrawText(self.GetPageText(self.GetSelection()), activeTabPosx + textOffset, imageYCoord) + + # Update all tabs that can not fit into the screen as non-visible + for xx in xrange(self._nFrom + len(vTabsInfo), len(self._pagesInfoVec)): + + self._pagesInfoVec[xx].SetPosition(wx.Point(-1, -1)) + self._pagesInfoVec[xx].GetRegion().Clear() + + # Draw the left/right/close buttons + # Left arrow + self.DrawLeftArrow(dc) + self.DrawRightArrow(dc) + self.DrawX(dc) + + + def DrawVC8Tab(self, dc, posx, tabIdx, tabWidth, tabHeight): + """ Draws the VC8 style tabs. """ + + borderPen = wx.Pen(self._colorBorder) + tabPoints = [wx.Point() for ii in xrange(8)] + + # If we draw the first tab or the active tab, + # we draw a full tab, else we draw a truncated tab + # + # X(2) X(3) + # X(1) X(4) + # + # X(5) + # + # X(0),(7) X(6) + # + # + + tabPoints[0].x = (self.HasFlag(FNB_BOTTOM) and [posx] or [posx+self._factor])[0] + tabPoints[0].y = (self.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 3])[0] + + tabPoints[1].x = tabPoints[0].x + tabHeight - VERTICAL_BORDER_PADDING - 3 - self._factor + tabPoints[1].y = (self.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0] + + tabPoints[2].x = tabPoints[1].x + 4 + tabPoints[2].y = (self.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[3].x = tabPoints[2].x + tabWidth - 2 + tabPoints[3].y = (self.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[4].x = tabPoints[3].x + 1 + tabPoints[4].y = (self.HasFlag(FNB_BOTTOM) and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0] + + tabPoints[5].x = tabPoints[4].x + 1 + tabPoints[5].y = (self.HasFlag(FNB_BOTTOM) and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0] + + tabPoints[6].x = tabPoints[2].x + tabWidth + tabPoints[6].y = tabPoints[0].y + + tabPoints[7].x = tabPoints[0].x + tabPoints[7].y = tabPoints[0].y + + self._pagesInfoVec[tabIdx].SetRegion(tabPoints) + + # Draw the polygon + br = dc.GetBrush() + dc.SetBrush(wx.Brush((tabIdx == self.GetSelection() and [self._activeTabColor] or [self._colorTo])[0])) + dc.SetPen(wx.Pen((tabIdx == self.GetSelection() and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [self._colorBorder])[0])) + dc.DrawPolygon(tabPoints) + + # Restore the brush + dc.SetBrush(br) + + rect = self.GetClientRect() + + if tabIdx != self.GetSelection() and not self.HasFlag(FNB_BOTTOM): + + # Top default tabs + dc.SetPen(wx.Pen(self._colorBorder)) + lineY = rect.height + curPen = dc.GetPen() + curPen.SetWidth(1) + dc.SetPen(curPen) + dc.DrawLine(posx, lineY, posx+rect.width, lineY) + + # In case we are drawing the selected tab, we draw the border of it as well + # but without the bottom (upper line incase of wxBOTTOM) + if tabIdx == self.GetSelection(): + + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + brush = wx.TRANSPARENT_BRUSH + dc.SetPen(borderPen) + dc.SetBrush(brush) + dc.DrawPolygon(tabPoints) + + # Delete the bottom line (or the upper one, incase we use wxBOTTOM) + dc.SetPen(wx.WHITE_PEN) + dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y) + + self.FillVC8GradientColor(dc, tabPoints, tabIdx == self.GetSelection(), tabIdx) + + # Draw a thin line to the right of the non-selected tab + if tabIdx != self.GetSelection(): + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))) + dc.DrawLine(tabPoints[4].x-1, tabPoints[4].y, tabPoints[5].x-1, tabPoints[5].y) + dc.DrawLine(tabPoints[5].x-1, tabPoints[5].y, tabPoints[6].x-1, tabPoints[6].y) + + + def FillVC8GradientColor(self, dc, tabPoints, bSelectedTab, tabIdx): + """ Fills a tab with a gradient colour. """ + + # calculate gradient coefficients + col2 = (self.HasFlag(FNB_BOTTOM) and [self._colorTo] or [self._colorFrom])[0] + col1 = (self.HasFlag(FNB_BOTTOM) and [self._colorFrom] or [self._colorTo])[0] + + # If colorful tabs style is set, override the tab color + if self.HasFlag(FNB_COLORFUL_TABS): + + if not self._pagesInfoVec[tabIdx].GetColor(): + + # First time, generate color, and keep it in the vector + tabColor = self.GenTabColour() + self._pagesInfoVec[tabIdx].SetColor(tabColor) + + if self.HasFlag(FNB_BOTTOM): + + col2 = LightColour(self._pagesInfoVec[tabIdx].GetColor(), 50 ) + col1 = LightColour(self._pagesInfoVec[tabIdx].GetColor(), 80 ) + + else: + + col1 = LightColour(self._pagesInfoVec[tabIdx].GetColor(), 50 ) + col2 = LightColour(self._pagesInfoVec[tabIdx].GetColor(), 80 ) + + size = abs(tabPoints[2].y - tabPoints[0].y) - 1 + + rf, gf, bf = 0, 0, 0 + rstep = float(col2.Red() - col1.Red())/float(size) + gstep = float(col2.Green() - col1.Green())/float(size) + bstep = float(col2.Blue() - col1.Blue())/float(size) + + y = tabPoints[0].y + + # If we are drawing the selected tab, we need also to draw a line + # from 0.tabPoints[0].x and tabPoints[6].x . end, we achieve this + # by drawing the rectangle with transparent brush + # the line under the selected tab will be deleted by the drwaing loop + if bSelectedTab: + self.DrawTabsLine(dc) + + while 1: + + if self.HasFlag(FNB_BOTTOM): + + if y > tabPoints[0].y + size: + break + + else: + + if y < tabPoints[0].y - size: + break + + currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf) + + dc.SetPen((bSelectedTab and [wx.Pen(self._activeTabColor)] or [wx.Pen(currCol)])[0]) + startX = self.GetStartX(tabPoints, y) + endX = self.GetEndX(tabPoints, y) + dc.DrawLine(startX, y, endX, y) + + # Draw the border using the 'edge' point + dc.SetPen(wx.Pen((bSelectedTab and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [self._colorBorder])[0])) + + dc.DrawPoint(startX, y) + dc.DrawPoint(endX, y) + + # Progress the color + rf += rstep + gf += gstep + bf += bstep + + if self.HasFlag(FNB_BOTTOM): + y = y + 1 + else: + y = y - 1 + + + def GetStartX(self, tabPoints, y): + """ Returns the x start position of a tab. """ + + x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0 + + # We check the 3 points to the left + style = self.GetParent().GetWindowStyleFlag() + bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0] + match = False + + if bBottomStyle: + + for i in xrange(3): + + if y >= tabPoints[i].y and y < tabPoints[i+1].y: + + x1 = tabPoints[i].x + x2 = tabPoints[i+1].x + y1 = tabPoints[i].y + y2 = tabPoints[i+1].y + match = True + break + + else: + + for i in xrange(3): + + if y <= tabPoints[i].y and y > tabPoints[i+1].y: + + x1 = tabPoints[i].x + x2 = tabPoints[i+1].x + y1 = tabPoints[i].y + y2 = tabPoints[i+1].y + match = True + break + + if not match: + return tabPoints[2].x + + # According to the equation y = ax + b => x = (y-b)/a + # We know the first 2 points + + if x2 == x1: + return x2 + else: + a = (y2 - y1)/(x2 - x1) + + b = y1 - ((y2 - y1)/(x2 - x1))*x1 + + if a == 0: + return int(x1) + + x = (y - b)/a + + return int(x) + + + def GetEndX(self, tabPoints, y): + """ Returns the x end position of a tab. """ + + x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0 + + # We check the 3 points to the left + style = self.GetParent().GetWindowStyleFlag() + bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0] + match = False + + if bBottomStyle: + + for i in xrange(7, 3, -1): + + if y >= tabPoints[i].y and y < tabPoints[i-1].y: + + x1 = tabPoints[i].x + x2 = tabPoints[i-1].x + y1 = tabPoints[i].y + y2 = tabPoints[i-1].y + match = True + break + + else: + + for i in xrange(7, 3, -1): + + if y <= tabPoints[i].y and y > tabPoints[i-1].y: + + x1 = tabPoints[i].x + x2 = tabPoints[i-1].x + y1 = tabPoints[i].y + y2 = tabPoints[i-1].y + match = True + break + + if not match: + return tabPoints[3].x + + # According to the equation y = ax + b => x = (y-b)/a + # We know the first 2 points + + # Vertical line + if x1 == x2: + return int(x1) + + a = (y2 - y1)/(x2 - x1) + b = y1 - ((y2 - y1)/(x2 - x1)) * x1 + + if a == 0: + return int(x1) + + x = (y - b)/a + + return int(x) + + + def GenTabColour(self): + """ Generates a random soft pleasant colour for a tab. """ + + return RandomColor() + + + def GetSingleLineBorderColor(self): + + if self.HasFlag(FNB_VC8): + return self._activeTabColor + else: + return PageContainerBase.GetSingleLineBorderColor(self) + + + def SetFactor(self, factor): + """ Sets the brighten colour factor. """ + + self._factor = factor + self.Refresh() + + + def GetFactor(self): + """ Returns the brighten colour factor. """ + + return self._factor + + + -- 2.45.2