X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1c92f2983de294f332d79b0ac3634646db03c561..dee059c469b87ac01db30fbf6dc069106b6150cc:/wxPython/wx/lib/flatnotebook.py diff --git a/wxPython/wx/lib/flatnotebook.py b/wxPython/wx/lib/flatnotebook.py index fc3199867c..214f6821c3 100644 --- a/wxPython/wx/lib/flatnotebook.py +++ b/wxPython/wx/lib/flatnotebook.py @@ -11,7 +11,7 @@ # Python Code By: # # Andrea Gavana, @ 02 Oct 2006 -# Latest Revision: 10 Oct 2006, 21.00 GMT +# Latest Revision: 28 Jun 2007, 21.00 GMT # # # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please @@ -37,7 +37,7 @@ Some features: - 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 + - Currently there are 5 differnt styles - VC8, VC 71, Standard, Fancy and Firefox 2; - 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) @@ -58,9 +58,9 @@ License And Version: FlatNotebook Is Freeware And Distributed Under The wxPython License. -Latest Revision: Andrea Gavana @ 10 Oct 2006, 21.00 GMT +Latest Revision: Andrea Gavana @ 28 Jun 2007, 21.00 GMT -Version 2.0. +Version 2.2. @undocumented: FNB_HEIGHT_SPACER, VERTICAL_BORDER_PADDING, VC8_SHAPE_LEN, wxEVT*, left_arrow_*, right_arrow*, x_button*, down_arrow*, @@ -79,7 +79,7 @@ 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) @@ -123,6 +123,10 @@ FNB_NODRAG = 128 FNB_VC8 = 256 """Use Visual Studio 2005 (VC8) style for tabs""" +# Firefox 2 tabs style +FNB_FF2 = 131072 +"""Use Firefox 2 style for tabs""" + # Place 'X' on a tab FNB_X_ON_TAB = 512 """Place 'X' close button on the active tab""" @@ -146,6 +150,9 @@ FNB_DROPDOWN_TABS_LIST = 16384 FNB_ALLOW_FOREIGN_DND = 32768 """Allows drag 'n' drop operations between different L{FlatNotebook}s""" +FNB_HIDE_ON_SINGLE_TAB = 65536 +"""Hides the Page Container when there is one or fewer tabs""" + VERTICAL_BORDER_PADDING = 4 # Button size is a 16x16 xpm bitmap @@ -181,7 +188,7 @@ FNB_DROP_DOWN_ARROW = 6 # On the drop down arrow button FNB_NOWHERE = 0 # Anywhere else """Indicates mouse coordinates not on any tab of the notebook""" -FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS +FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS | FNB_HIDE_ON_SINGLE_TAB """L{FlatNotebook} default style""" # FlatNotebook Events: @@ -195,8 +202,8 @@ FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS # 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_CHANGED = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED +wxEVT_FLATNOTEBOOK_PAGE_CHANGING = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING wxEVT_FLATNOTEBOOK_PAGE_CLOSING = wx.NewEventType() wxEVT_FLATNOTEBOOK_PAGE_CLOSED = wx.NewEventType() wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType() @@ -205,10 +212,10 @@ wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType() # FlatNotebookEvent #-----------------------------------# -EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGED, 1) +EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.EVT_NOTEBOOK_PAGE_CHANGED """Notify client objects when the active page in L{FlatNotebook} has changed.""" -EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, 1) +EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.EVT_NOTEBOOK_PAGE_CHANGING """Notify client objects when the active page in L{FlatNotebook} is about to change.""" EVT_FLATNOTEBOOK_PAGE_CLOSING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, 1) @@ -714,6 +721,86 @@ def PaintStraightGradientBox(dc, rect, startColor, endColor, vertical=True): +# ----------------------------------------------------------------------------- +# Util functions +# ----------------------------------------------------------------------------- + +def DrawButton(dc, rect, focus, upperTabs): + + # Define the rounded rectangle base on the given rect + # we need an array of 9 points for it + regPts = [wx.Point() for indx in xrange(9)] + + if focus: + if upperTabs: + leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*8) + rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*8) + else: + leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*5) + rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*5) + else: + leftPt = wx.Point(rect.x, rect.y + (rect.height / 2)) + rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 2)) + + # Define the top region + top = wx.RectPP(rect.GetTopLeft(), rightPt) + bottom = wx.RectPP(leftPt, rect.GetBottomRight()) + + topStartColor = wx.WHITE + + if not focus: + topStartColor = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 50) + + topEndColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + bottomStartColor = topEndColor + bottomEndColor = topEndColor + + # Incase we use bottom tabs, switch the colors + if upperTabs: + if focus: + PaintStraightGradientBox(dc, top, topStartColor, topEndColor) + PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor) + else: + PaintStraightGradientBox(dc, top, topEndColor , topStartColor) + PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor) + + else: + if focus: + PaintStraightGradientBox(dc, bottom, topEndColor, bottomEndColor) + PaintStraightGradientBox(dc, top,topStartColor, topStartColor) + else: + PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor) + PaintStraightGradientBox(dc, top, topEndColor, topStartColor) + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + + +# ---------------------------------------------------------------------------- # +# Class FNBDropSource +# Gives Some Custom UI Feedback during the DnD Operations +# ---------------------------------------------------------------------------- # + +class FNBDropSource(wx.DropSource): + """ + Give some custom UI feedback during the drag and drop operation in this + function. It is called on each mouse move, so your implementation must + not be too slow. + """ + + def __init__(self, win): + """ Default class constructor. Used internally. """ + + wx.DropSource.__init__(self, win) + self._win = win + + + def GiveFeedback(self, effect): + """ Provides user with a nice feedback when tab is being dragged. """ + + self._win.DrawDragHint() + return False + + # ---------------------------------------------------------------------------- # # Class FNBDragInfo # Stores All The Information To Allow Drag And Drop Between Different @@ -805,6 +892,7 @@ class PageInfo: self._region = wx.Region() self._xRect = wx.Rect() self._color = None + self._hasFocus = False def SetCaption(self, value): @@ -873,7 +961,7 @@ class PageInfo: return self._bEnabled - def Enable(self, enabled): + def EnableTab(self, enabled): """ Sets the tab enabled or disabled. """ self._bEnabled = enabled @@ -1120,7 +1208,7 @@ class TabNavigatorWindow(wx.Dialog): def OnItemSelected(self, event): """Handles the wx.EVT_LISTBOX_DCLICK event for the wx.ListBox inside L{TabNavigatorWindow}. """ - self.CloseDialog() + self.CloseDialog() def CloseDialog(self): @@ -1129,7 +1217,7 @@ class TabNavigatorWindow(wx.Dialog): bk = self.GetParent() self._selectedItem = self._listBox.GetSelection() iter = self._indexMap[self._selectedItem] - bk.SetSelection(iter) + bk._pages.FireEvent(iter) self.EndModal(wx.ID_OK) @@ -1194,6 +1282,14 @@ class FNBRenderer: self._xBgBmp = wx.EmptyBitmap(16, 14) self._leftBgBmp = wx.EmptyBitmap(16, 14) self._rightBgBmp = wx.EmptyBitmap(16, 14) + self._tabHeight = None + + if wx.Platform == "__WXMAC__": + self._focusPen = wx.Pen(wx.BLACK, 1, wx.SOLID) + else: + self._focusPen = wx.Pen(wx.BLACK, 1, wx.USER_DASH) + self._focusPen.SetDashes([1, 1]) + self._focusPen.SetCap(wx.CAP_BUTT) def GetLeftButtonPos(self, pageContainer): @@ -1371,7 +1467,7 @@ class FNBRenderer: # erase old bitmap posx = self.GetDropArrowButtonPos(pc) - dc.DrawBitmap(self._xBgBmp, posx, 6) + dc.DrawBitmap(self._rightBgBmp, posx, 6) # Draw the new bitmap dc.DrawBitmap(downBmp, posx, 6, True) @@ -1452,7 +1548,7 @@ class FNBRenderer: return bmp - def DrawTabsLine(self, pageContainer, dc): + def DrawTabsLine(self, pageContainer, dc, selTabX1=-1, selTabX2=-1): """ Draws a line over the tabs. """ pc = pageContainer @@ -1460,38 +1556,70 @@ class FNBRenderer: clntRect = pc.GetClientRect() clientRect3 = wx.Rect(0, 0, clntRect.width, clntRect.height) - if pc.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(pc.GetSingleLineBorderColour())) - dc.DrawRectangleRect(clientRect2) - dc.DrawRectangleRect(clientRect3) + if pc.HasFlag(FNB_FF2): + if not pc.HasFlag(FNB_BOTTOM): + fillColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE) + else: + fillColor = wx.WHITE - dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))) - dc.DrawRectangleRect(clientRect) + dc.SetPen(wx.Pen(fillColor)) - if not pc.HasFlag(FNB_TABS_BORDER_SIMPLE): - - dc.SetPen(wx.Pen((pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc._tabAreaColor])[0])) - dc.DrawLine(0, 0, 0, clientRect.height+1) + if pc.HasFlag(FNB_BOTTOM): + + dc.DrawLine(1, 0, clntRect.width-1, 0) + dc.DrawLine(1, 1, clntRect.width-1, 1) + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))) + dc.DrawLine(1, 2, clntRect.width-1, 2) + + dc.SetPen(wx.Pen(fillColor)) + dc.DrawLine(selTabX1 + 2, 2, selTabX2 - 1, 2) + + else: + + dc.DrawLine(1, clntRect.height, clntRect.width-1, clntRect.height) + dc.DrawLine(1, clntRect.height-1, clntRect.width-1, clntRect.height-1) + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))) + dc.DrawLine(1, clntRect.height-2, clntRect.width-1, clntRect.height-2) + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))) + dc.DrawLine(selTabX1 + 2, clntRect.height-2, selTabX2-1, clntRect.height-2) + + else: if pc.HasFlag(FNB_BOTTOM): - dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1) + 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(pc.GetSingleLineBorderColour())) + dc.DrawRectangleRect(clientRect2) + dc.DrawRectangleRect(clientRect3) + + dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))) + dc.DrawRectangleRect(clientRect) + + if not pc.HasFlag(FNB_TABS_BORDER_SIMPLE): + + dc.SetPen(wx.Pen((pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc._tabAreaColor])[0])) + dc.DrawLine(0, 0, 0, clientRect.height+1) + + if pc.HasFlag(FNB_BOTTOM): - dc.DrawLine(0, 0, clientRect.width, 0) + dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1) - dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1) + else: + + dc.DrawLine(0, 0, clientRect.width, 0) + + dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1) def CalcTabWidth(self, pageContainer, tabIdx, tabHeight): @@ -1549,6 +1677,9 @@ class FNBRenderer: def CalcTabHeight(self, pageContainer): """ Calculates the height of the input tab. """ + if self._tabHeight: + return self._tabHeight + pc = pageContainer dc = wx.MemoryDC() dc.SelectObject(wx.EmptyBitmap(1,1)) @@ -1558,8 +1689,10 @@ class FNBRenderer: # on MSW, preforming these steps yields wierd results normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) boldFont = normalFont - boldFont.SetWeight(wx.FONTWEIGHT_BOLD) - dc.SetFont(boldFont) + + if "__WXGTK__" in wx.PlatformInfo: + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(boldFont) height = dc.GetCharHeight() @@ -1568,11 +1701,8 @@ class FNBRenderer: # On GTK the tabs are should be larger tabHeight += 6 - if pc.HasFlag(FNB_VC71): - tabHeight = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 4] or [tabHeight])[0] - elif pc.HasFlag(FNB_FANCY_TABS): - tabHeight = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 2] or [tabHeight])[0] - + self._tabHeight = tabHeight + return tabHeight @@ -1619,7 +1749,12 @@ class FNBRenderer: else: colr = (pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0] dc.SetPen(wx.Pen(colr)) - + + if pc.HasFlag(FNB_FF2): + lightFactor = (pc.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0] + PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColor, LightColour(pc._tabAreaColor, lightFactor)) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangle(0, 0, size.x, size.y) # Take 3 bitmaps for the background for the buttons @@ -1652,7 +1787,9 @@ class FNBRenderer: # We always draw the bottom/upper line of the tabs # regradless the style dc.SetPen(borderPen) - self.DrawTabsLine(pc, dc) + + if not pc.HasFlag(FNB_FF2): + self.DrawTabsLine(pc, dc) # Restore the pen dc.SetPen(borderPen) @@ -1679,13 +1816,13 @@ class FNBRenderer: # Draw labels normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - boldFont = normalFont + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) boldFont.SetWeight(wx.FONTWEIGHT_BOLD) dc.SetFont(boldFont) posx = pc._pParent.GetPadding() - # Update all the tabs from 0 to 'pc.self._nFrom' to be non visible + # Update all the tabs from 0 to 'pc._nFrom' to be non visible for i in xrange(pc._nFrom): pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1)) @@ -1696,10 +1833,13 @@ class FNBRenderer: #---------------------------------------------------------- # Go over and draw the visible tabs #---------------------------------------------------------- + x1 = x2 = -1 for i in xrange(pc._nFrom, len(pc._pagesInfoVec)): dc.SetPen(borderPen) - dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0]) + + if not pc.HasFlag(FNB_FF2): + dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0]) # Now set the font to the correct font dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0]) @@ -1728,6 +1868,10 @@ class FNBRenderer: # Draw the tab (border, text, image & 'x' on tab) self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus) + if pc.GetSelection() == i: + x1 = posx + x2 = posx + tabWidth + 2 + # Restore the text forground dc.SetTextForeground(pc._activeTextColor) @@ -1736,6 +1880,8 @@ class FNBRenderer: pc._pagesInfoVec[i].SetPosition(wx.Point(posx, posy)) pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight)) + self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[i]) + posx += tabWidth # Update all tabs that can not fit into the screen as non-visible @@ -1750,7 +1896,71 @@ class FNBRenderer: self.DrawX(pc, dc) self.DrawDropDownArrow(pc, dc) + if pc.HasFlag(FNB_FF2): + self.DrawTabsLine(pc, dc, x1, x2) + + + def DrawFocusRectangle(self, dc, pageContainer, page): + """ Draws a focus rectangle like the native Notebooks. """ + + if not page._hasFocus: + return + + tabPos = page.GetPosition() + if pageContainer.GetParent().GetWindowStyleFlag() & FNB_VC8: + vc8ShapeLen = self.CalcTabHeight(pageContainer) - VERTICAL_BORDER_PADDING - 2 + tabPos.x += vc8ShapeLen + + rect = wx.RectPS(tabPos, page.GetSize()) + rect = wx.Rect(rect.x+2, rect.y+2, rect.width-4, rect.height-8) + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(self._focusPen) + dc.DrawRoundedRectangleRect(rect, 2) + + + def DrawDragHint(self, pc, tabIdx): + """ + Draws tab drag hint, the default implementation is to do nothing. + You can override this function to provide a nice feedback to user. + """ + + pass + + + def NumberTabsCanFit(self, pageContainer, fr=-1): + + pc = pageContainer + + rect = pc.GetClientRect() + clientWidth = rect.width + + vTabInfo = [] + + tabHeight = self.CalcTabHeight(pageContainer) + + # The drawing starts from posx + posx = pc._pParent.GetPadding() + + if fr < 0: + fr = pc._nFrom + + for i in xrange(fr, len(pc._pagesInfoVec)): + + tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight) + if posx + tabWidth + self.GetButtonsAreaLength(pc) >= 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 + + # ---------------------------------------------------------------------------- # # Class FNBRendererMgr # A manager that handles all the renderers defined below and calls the @@ -1773,16 +1983,12 @@ class FNBRendererMgr: self._renderers.update({FNB_VC71: FNBRendererVC71()}) self._renderers.update({FNB_FANCY_TABS: FNBRendererFancy()}) self._renderers.update({FNB_VC8: FNBRendererVC8()}) + self._renderers.update({FNB_FF2: FNBRendererFirefox2()}) def GetRenderer(self, style): """ Returns the current renderer based on the style selected. """ - # since we dont have a style for default tabs, we - # test for all others - FIXME: add style for default tabs - if not style & FNB_VC71 and not style & FNB_VC8 and not style & FNB_FANCY_TABS: - return self._renderers[-1] - if style & FNB_VC71: return self._renderers[FNB_VC71] @@ -1792,6 +1998,9 @@ class FNBRendererMgr: if style & FNB_VC8: return self._renderers[FNB_VC8] + if style & FNB_FF2: + return self._renderers[FNB_FF2] + # the default is to return the default renderer return self._renderers[-1] @@ -1919,6 +2128,106 @@ class FNBRendererDefault(FNBRenderer): self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus) +#------------------------------------------ +# Firefox2 renderer +#------------------------------------------ +class FNBRendererFirefox2(FNBRenderer): + """ + This class handles the drawing of tabs using the I{Firefox 2} renderer. + """ + + def __init__(self): + """ Default class constructor. """ + + FNBRenderer.__init__(self) + + + def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus): + """ Draws a tab using the I{Firefox 2} style. """ + + borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)) + pc = pageContainer + + tabPoints = [wx.Point() for indx in xrange(7)] + tabPoints[0].x = posx + 2 + tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0] + + tabPoints[1].x = tabPoints[0].x + tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0] + + tabPoints[2].x = tabPoints[1].x+2 + tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[3].x = posx + tabWidth - 2 + tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0] + + tabPoints[4].x = tabPoints[3].x + 2 + tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0] + + tabPoints[5].x = tabPoints[4].x + tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0] + + tabPoints[6].x = tabPoints[0].x + tabPoints[6].y = tabPoints[0].y + + #------------------------------------ + # Paint the tab with gradient + #------------------------------------ + rr = wx.RectPP(tabPoints[2], tabPoints[5]) + DrawButton(dc, rr, pc.GetSelection() == tabIdx , not pc.HasFlag(FNB_BOTTOM)) + + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.SetPen(borderPen) + + # Draw the tab as rounded rectangle + dc.DrawPolygon(tabPoints) + + # ----------------------------------- + # Text and image drawing + # ----------------------------------- + + # The width of the images are 16 pixels + padding = pc.GetParent().GetPadding() + shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi)) + hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1 + imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0] + + if hasImage: + textOffset = 2*padding + 16 + shapePoints/2 + else: + textOffset = padding + shapePoints/2 + + textOffset += 2 + + if tabIdx != pc.GetSelection(): + + # Set the text background to be like the vertical lines + dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour()) + + if hasImage: + imageXOffset = textOffset - 16 - padding + pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc, + posx + imageXOffset, imageYCoord, + wx.IMAGELIST_DRAW_TRANSPARENT, True) + + dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord) + + # draw 'x' on tab (if enabled) + if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection(): + + textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx)) + 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) + self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp) + + # Draw the tab + self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus) + + #------------------------------------------------------------------ # Visual studio 7.1 #------------------------------------------------------------------ @@ -1947,14 +2256,15 @@ class FNBRendererVC71(FNBRenderer): if tabIdx == pc.GetSelection(): posy = (pc.HasFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0] - dc.DrawRectangle(posx, posy, tabWidth, tabHeight - 1) + tabH = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 5] or [tabHeight - 3])[0] + dc.DrawRectangle(posx, posy, tabWidth, tabH) # Draw a black line on the left side of the # rectangle dc.SetPen(wx.BLACK_PEN) blackLineY1 = VERTICAL_BORDER_PADDING - blackLineY2 = (pc.HasFlag(FNB_BOTTOM) and [pc.GetSize().y - 5] or [pc.GetSize().y - 3])[0] + blackLineY2 = tabH dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2) # To give the tab more 3D look we do the following @@ -1993,7 +2303,7 @@ class FNBRendererVC71(FNBRenderer): # The width of the images are 16 pixels padding = pc.GetParent().GetPadding() hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1 - imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0] + imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [5] or [8])[0] if hasImage: textOffset = 2*pc._pParent._nPadding + 16 @@ -2058,7 +2368,7 @@ class FNBRendererFancy(FNBRenderer): if tabIdx == pc.GetSelection(): posy = (pc.HasFlag(FNB_BOTTOM) and [2] or [VERTICAL_BORDER_PADDING])[0] - th = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 2] or [tabHeight - 5])[0] + th = tabHeight - 5 rect = wx.Rect(posx, posy, tabWidth, th) @@ -2166,7 +2476,7 @@ class FNBRendererVC8(FNBRenderer): # Set the font for measuring the tab height normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - boldFont = normalFont + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) boldFont.SetWeight(wx.FONTWEIGHT_BOLD) # Calculate the number of rows required for drawing the tabs @@ -2455,6 +2765,8 @@ class FNBRendererVC8(FNBRenderer): # Draw the tab self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus) + self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[tabIdx]) + def FillVC8GradientColour(self, pageContainer, dc, tabPoints, bSelectedTab, tabIdx): """ Fills a tab with a gradient shading. """ @@ -2467,8 +2779,8 @@ class FNBRendererVC8(FNBRenderer): pc._colorTo = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 0) pc._colorFrom = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 60) - col2 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourTo()] or [pc._pParent.GetGradientColourFrom()])[0] - col1 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourFrom()] or [pc._pParent.GetGradientColourTo()])[0] + col2 = pc._pParent.GetGradientColourTo() + col1 = pc._pParent.GetGradientColourFrom() # If colorful tabs style is set, override the tab color if pc.HasFlag(FNB_COLORFUL_TABS): @@ -2694,7 +3006,7 @@ class FNBRendererVC8(FNBRenderer): # Class FlatNotebook # ---------------------------------------------------------------------------- # -class FlatNotebook(wx.Panel): +class FlatNotebook(wx.PyPanel): """ Display one or more windows in a notebook. @@ -2730,7 +3042,7 @@ class FlatNotebook(wx.Panel): self._windows = [] self._popupWin = None - wx.Panel.__init__(self, parent, id, pos, size, style) + wx.PyPanel.__init__(self, parent, id, pos, size, style) self._pages = PageContainer(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, style) @@ -2752,11 +3064,17 @@ class FlatNotebook(wx.Panel): # Set default page height dc = wx.ClientDC(self) - font = self.GetFont() - font.SetWeight(wx.FONTWEIGHT_BOLD) - dc.SetFont(font) - height = dc.GetCharHeight() + 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 + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.FONTWEIGHT_BOLD) + dc.SetFont(boldFont) + + height = dc.GetCharHeight() + tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding if "__WXGTK__" in wx.PlatformInfo: @@ -2772,6 +3090,24 @@ class FlatNotebook(wx.Panel): self.SetDropTarget(self._pDropTarget) + def DoGetBestSize(self): + """ Overrides DoGetBestSize to handle sizers nicely. """ + + if not self._windows: + # Something is better than nothing... no pages! + return wx.Size(20, 20) + + maxWidth = maxHeight = 0 + tabHeight = self.GetPageBestSize().height + + for win in self._windows: + # Loop over all the windows to get their best size + width, height = win.GetBestSize() + maxWidth, maxHeight = max(maxWidth, width), max(maxHeight, height) + + return wx.Size(maxWidth, maxHeight+tabHeight) + + def SetActiveTabTextColour(self, textColour): """ Sets the text colour for the active tab. """ @@ -2826,7 +3162,7 @@ class FlatNotebook(wx.Panel): event.SetOldSelection(oldSelection) event.SetEventObject(self) - if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed(): + if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0: bSelected = True curSel = self._pages.GetSelection() @@ -2866,21 +3202,24 @@ class FlatNotebook(wx.Panel): # Hide the page page.Hide() - + + self.Thaw() 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. - """ + def SetImageList(self, imageList): + """ Sets the image list for the page control. """ + + self._pages.SetImageList(imageList) + - self._pages.SetImageList(imglist) + def AssignImageList(self, imageList): + """ Assigns the image list for the page control. """ + + self._pages.AssignImageList(imageList) def GetImageList(self): @@ -2941,7 +3280,7 @@ class FlatNotebook(wx.Panel): event.SetOldSelection(oldSelection) event.SetEventObject(self) - if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed(): + if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0: bSelected = True self._pages.InsertPage(indx, text, bSelected, imageId) @@ -2971,9 +3310,9 @@ class FlatNotebook(wx.Panel): # Hide the page page.Hide() - + self.Thaw() - self._mainSizer.Layout() + self._mainSizer.Layout() self.Refresh() return True @@ -3063,6 +3402,7 @@ class FlatNotebook(wx.Panel): self._pages.DoDeletePage(page) self.Refresh() + self.Update() # Fire a closed event closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId()) @@ -3124,17 +3464,18 @@ class FlatNotebook(wx.Panel): return self._pages.GetSelection() - def AdvanceSelection(self, bForward=True): + def AdvanceSelection(self, forward=True): """ Cycles through the tabs. The call to this function generates the page changing events. """ - self._pages.AdvanceSelection(bForward) + self._pages.AdvanceSelection(forward) def GetPageCount(self): """ Returns the number of pages in the L{FlatNotebook} control. """ + return self._pages.GetPageCount() @@ -3142,6 +3483,8 @@ class FlatNotebook(wx.Panel): """ Handles the wx.EVT_NAVIGATION_KEY event for L{FlatNotebook}. """ if event.IsWindowChange(): + if len(self._windows) == 0: + return # change pages if self.HasFlag(FNB_SMART_TABS): if not self._popupWin: @@ -3157,11 +3500,9 @@ class FlatNotebook(wx.Panel): else: # change pages self.AdvanceSelection(event.GetDirection()) + else: - # pass to the parent - if self.GetParent(): - event.SetCurrentFocus(self) - self.GetParent().ProcessEvent(event) + event.Skip() def GetPageShapeAngle(self, page_index): @@ -3237,7 +3578,9 @@ class FlatNotebook(wx.Panel): def SetWindowStyleFlag(self, style): """ Sets the L{FlatNotebook} window style flags. """ - wx.Panel.SetWindowStyleFlag(self, style) + wx.PyPanel.SetWindowStyleFlag(self, style) + renderer = self._pages._mgr.GetRenderer(self.GetWindowStyleFlag()) + renderer._tabHeight = None if self._pages: @@ -3245,6 +3588,10 @@ class FlatNotebook(wx.Panel): # refreshing the tab container is not enough self.SetSelection(self._pages._iActivePage) + if not self._pages.HasFlag(FNB_HIDE_ON_SINGLE_TAB): + #For Redrawing the Tabs once you remove the Hide tyle + self._pages._ReShow() + def RemovePage(self, page): """ Deletes the specified page, without deleting the associated window. """ @@ -3287,10 +3634,10 @@ class FlatNotebook(wx.Panel): self._pages._pRightClickMenu = menu - def GetPageText(self, page): + def GetPageText(self, nPage): """ Returns the tab caption. """ - return self._pages.GetPageText(page) + return self._pages.GetPageText(nPage) def SetGradientColours(self, fr, to, border): @@ -3349,22 +3696,22 @@ class FlatNotebook(wx.Panel): return self._pages._activeTextColor - def SetPageImage(self, page, imgindex): + def SetPageImage(self, page, image): """ Sets the image index for the given page. Image is an index into the image list which was set with SetImageList. """ - self._pages.SetPageImage(page, imgindex) + self._pages.SetPageImage(page, image) - def GetPageImage(self, page): + def GetPageImage(self, nPage): """ Returns the image index for the given page. Image is an index into the image list which was set with SetImageList. """ - return self._pages.GetPageImage(page) + return self._pages.GetPageImage(nPage) def GetEnabled(self, page): @@ -3373,14 +3720,14 @@ class FlatNotebook(wx.Panel): return self._pages.GetEnabled(page) - def Enable(self, page, enabled=True): + def EnableTab(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) + self._pages.EnableTab(page, enabled) def GetNonActiveTabTextColour(self): @@ -3453,7 +3800,7 @@ class PageContainer(wx.Panel): 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._nonActiveTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT) self._tabAreaColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE) self._nFrom = 0 @@ -3462,16 +3809,17 @@ class PageContainer(wx.Panel): # Set default page height, this is done according to the system font memDc = wx.MemoryDC() memDc.SelectObject(wx.EmptyBitmap(1,1)) - normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) - boldFont = normalFont - boldFont.SetWeight(wx.BOLD) - memDc.SetFont(boldFont) + + if "__WXGTK__" in wx.PlatformInfo: + boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT) + boldFont.SetWeight(wx.BOLD) + memDc.SetFont(boldFont) height = memDc.GetCharHeight() tabHeight = height + FNB_HEIGHT_SPACER # We use 10 pixels as padding wx.Panel.__init__(self, parent, id, pos, wx.Size(size.x, tabHeight), - style|wx.NO_BORDER|wx.NO_FULL_REPAINT_ON_RESIZE) + style|wx.NO_BORDER|wx.NO_FULL_REPAINT_ON_RESIZE|wx.WANTS_CHARS) self._pDropTarget = FNBDropTarget(self) self.SetDropTarget(self._pDropTarget) @@ -3488,7 +3836,10 @@ class PageContainer(wx.Panel): self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave) self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow) self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) - + self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus) + self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) + def OnEraseBackground(self, event): """ Handles the wx.EVT_ERASE_BACKGROUND event for L{PageContainer} (does nothing).""" @@ -3496,23 +3847,25 @@ class PageContainer(wx.Panel): pass + def _ReShow(self): + """ Handles the Redraw of the tabs when the FNB_HIDE_ON_SINGLE_TAB has been removed """ + self.Show() + self.GetParent()._mainSizer.Layout() + self.Refresh() + + def OnPaint(self, event): """ Handles the wx.EVT_PAINT event for L{PageContainer}.""" - # Currently having problems with buffered DCs because of - # recent changes. Just do the buffering ourselves instead. - #dc = wx.BufferedPaintDC(self) - size = self.GetSize() - bmp = wx.EmptyBitmap(*size) - dc = wx.MemoryDC() - dc.SelectObject(bmp) - + dc = wx.BufferedPaintDC(self) renderer = self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag()) renderer.DrawTabs(self, dc) - pdc = wx.PaintDC(self) - pdc.Blit(0,0, size.width, size.height, dc, 0,0) - + if self.HasFlag(FNB_HIDE_ON_SINGLE_TAB) and len(self._pagesInfoVec) <= 1: + self.Hide() + self.GetParent()._mainSizer.Layout() + self.Refresh() + def AddPage(self, caption, selected=True, imgindex=-1): """ @@ -3566,6 +3919,21 @@ class PageContainer(wx.Panel): def OnSize(self, event): """ Handles the wx.EVT_SIZE events for L{PageContainer}. """ + # When resizing the control, try to fit to screen as many tabs as we can + style = self.GetParent().GetWindowStyleFlag() + renderer = self._mgr.GetRenderer(style) + + fr = 0 + page = self.GetSelection() + + for fr in xrange(self._nFrom): + vTabInfo = renderer.NumberTabsCanFit(self, fr) + if page - fr >= len(vTabInfo): + continue + break + + self._nFrom = fr + self.Refresh() # Call on paint event.Skip() @@ -3589,30 +3957,26 @@ class PageContainer(wx.Panel): def OnRightDown(self, event): """ Handles the wx.EVT_RIGHT_DOWN events for L{PageContainer}. """ - - if self._pRightClickMenu: - where, tabIdx = self.HitTest(event.GetPosition()) + where, tabIdx = self.HitTest(event.GetPosition()) - if where in [FNB_TAB, FNB_TAB_X]: + 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() + if self._pagesInfoVec[tabIdx].GetEnabled(): + # Fire events and eventually (if allowed) change selection + self.FireEvent(tabIdx) + + # 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) + + if self._pRightClickMenu: + self.PopupMenu(self._pRightClickMenu) + + event.Skip() def OnLeftDown(self, event): @@ -3646,79 +4010,66 @@ class PageContainer(wx.Panel): 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(): + self.FireEvent(tabIdx) - 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) + def RotateLeft(self): - # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event - event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED) - event.SetOldSelection(oldSelection) - self.GetParent().GetEventHandler().ProcessEvent(event) + if self._nFrom == 0: + return + # Make sure that the button was pressed before + if self._nLeftButtonStatus != FNB_BTN_PRESSED: + return - def OnLeftUp(self, event): - """ Handles the wx.EVT_LEFT_UP events for L{PageContainer}. """ + self._nLeftButtonStatus = FNB_BTN_HOVER - # forget the zone that was initially clicked - self._nLeftClickZone = FNB_NOWHERE + # We scroll left with bulks of 5 + scrollLeft = self.GetNumTabsCanScrollLeft() - where, tabIdx = self.HitTest(event.GetPosition()) - - if where == FNB_LEFT_ARROW: - - if self._nFrom == 0: - return + self._nFrom -= scrollLeft + if self._nFrom < 0: + self._nFrom = 0 - # Make sure that the button was pressed before - if self._nLeftButtonStatus != FNB_BTN_PRESSED: - return + self.Refresh() - self._nLeftButtonStatus = FNB_BTN_HOVER - # We scroll left with bulks of 5 - scrollLeft = self.GetNumTabsCanScrollLeft() + def RotateRight(self): - self._nFrom -= scrollLeft - if self._nFrom < 0: - self._nFrom = 0 + if self._nFrom >= len(self._pagesInfoVec) - 1: + return - 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 - # Make sure that the button was pressed before - if self._nRightButtonStatus != FNB_BTN_PRESSED: - return + self._nRightButtonStatus = FNB_BTN_HOVER - self._nRightButtonStatus = FNB_BTN_HOVER + # Check if the right most tab is visible, if it is + # don't rotate right anymore + if self._pagesInfoVec[len(self._pagesInfoVec)-1].GetPosition() != wx.Point(-1, -1): + return - # 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 + self._nFrom += 1 + self.Refresh() - 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() + def OnLeftUp(self, event): + """ Handles the wx.EVT_LEFT_UP events for L{PageContainer}. """ + + # forget the zone that was initially clicked + self._nLeftClickZone = FNB_NOWHERE + + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_LEFT_ARROW: + self.RotateLeft() + + elif where == FNB_RIGHT_ARROW: + self.RotateRight() elif where == FNB_X: @@ -3755,6 +4106,8 @@ class PageContainer(wx.Panel): self.PopupTabsMenu() + event.Skip() + def HitTest(self, pt): """ @@ -3867,22 +4220,18 @@ class PageContainer(wx.Panel): 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: + # Try to remove one tab from start and try again + + if not self.CanFitToScreen(page): - if not self.CanFitToScreen(page): - # Redraw the tabs starting from page + if self._nFrom > page: self._nFrom = page - + else: + while self._nFrom < page: + self._nFrom += 1 + if self.CanFitToScreen(page): + break + self.Refresh() @@ -4033,14 +4382,14 @@ class PageContainer(wx.Panel): wx.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY)) # Support for drag and drop - if event.LeftIsDown() and not (style & FNB_NODRAG): + if event.Dragging() 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 = FNBDropSource(self) dragSource.SetData(dataobject) dragSource.DoDragDrop(wx.Drag_DefaultMove) @@ -4057,7 +4406,7 @@ class PageContainer(wx.Panel): dc = wx.ClientDC(self) if bRedrawX: - + render.DrawX(self, dc) if bRedrawLeft: @@ -4070,7 +4419,7 @@ class PageContainer(wx.Panel): if bRedrawTabX: - render.DrawTabX(self, dc, self._pagesInfoVec[tabIdx].GetXRect(), tabIdx, self._nTabXButtonStatus) + self.Refresh() if bRedrawDropArrow: @@ -4082,6 +4431,9 @@ class PageContainer(wx.Panel): def GetLastVisibleTab(self): """ Returns the last visible tab. """ + if self._nFrom < 0: + return -1 + ii = 0 for ii in xrange(self._nFrom, len(self._pagesInfoVec)): @@ -4095,66 +4447,10 @@ class PageContainer(wx.Panel): 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 - - # In case we have error prevent crash - if self._nFrom < 0: - return 0 - - dc = wx.ClientDC(self) - - style = self.GetParent().GetWindowStyleFlag() - render = self._mgr.GetRenderer(style) - - 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) + if self._nFrom - 1 >= 0: + return 1 - height = dc.GetCharHeight() - - 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 = 2*self._pParent._nPadding + 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 + render.GetButtonsAreaLength(self) >= clientWidth: - break - - numTabs = numTabs + 1 - posx += tabWidth - - return numTabs + return 0 def IsDefaultTabs(self): @@ -4178,7 +4474,6 @@ class PageContainer(wx.Panel): nMax = self.GetPageCount() - 1 - oldSelection = self._iActivePage if bForward: newSelection = (nSel == nMax and [0] or [nSel + 1])[0] else: @@ -4186,20 +4481,8 @@ class PageContainer(wx.Panel): if not self._pagesInfoVec[newSelection].GetEnabled(): return - - event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId()) - event.SetSelection(newSelection) - event.SetOldSelection(oldSelection) - event.SetEventObject(self.GetParent()) - - if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed(): - - self.SetSelection(newSelection) - # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event - event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED) - event.SetOldSelection(oldSelection) - self.GetParent().GetEventHandler().ProcessEvent(event) + self.FireEvent(newSelection) def OnMouseLeave(self, event): @@ -4304,10 +4587,10 @@ class PageContainer(wx.Panel): if where == FNB_TAB: self.MoveTabPage(nTabPage, nIndex) - + elif self.GetParent().GetWindowStyleFlag() & FNB_ALLOW_FOREIGN_DND: - if wx.Platform in ["__WXMSW__", "__WXGTK__"]: + if wx.Platform in ["__WXMSW__", "__WXGTK__", "__WXMAC__"]: if nTabPage >= 0: window = oldNotebook.GetPage(nTabPage) @@ -4321,7 +4604,7 @@ class PageContainer(wx.Panel): if imageindex >= 0: - bmp = oldNotebook.GetImageList().GetBitmap(imageindex) + bmp = oldNotebook.GetImageList().GetIcon(imageindex) newImageList = newNotebook.GetImageList() if not newImageList: @@ -4331,7 +4614,7 @@ class PageContainer(wx.Panel): else: imageindex = newImageList.GetImageCount() - newImageList.Add(bmp) + newImageList.AddIcon(bmp) newNotebook.SetImageList(newImageList) newNotebook.InsertPage(nIndex, window, caption, True, imageindex) @@ -4401,39 +4684,12 @@ class PageContainer(wx.Panel): style = self.GetParent().GetWindowStyleFlag() render = self._mgr.GetRenderer(style) - if not self.HasFlag(FNB_VC8): - rect = self.GetClientRect(); - clientWidth = rect.width; - tabHeight = render.CalcTabHeight(self) - tabWidth = render.CalcTabWidth(self, page, tabHeight) - - posx = self._pParent._nPadding + vTabInfo = render.NumberTabsCanFit(self) - if self._nFrom >= 0: - - for i in xrange(self._nFrom, len(self._pagesInfoVec)): - - if self._pagesInfoVec[i].GetPosition() == wx.Point(-1, -1): - break - - posx += self._pagesInfoVec[i].GetSize().x - - if posx + tabWidth + render.GetButtonsAreaLength(self) >= clientWidth: - return False - - return True - - else: - - # TODO:: this is ugly and should be improved, we should *never* access the - # raw pointer directly like we do here (render.Get()) - vc8_render = render - vTabInfo = vc8_render.NumberTabsCanFit(self) - - if page - self._nFrom >= len(vTabInfo): - return False - - return True + if page - self._nFrom >= len(vTabInfo): + return False + + return True def GetNumOfVisibleTabs(self): @@ -4457,13 +4713,13 @@ class PageContainer(wx.Panel): return self._pagesInfoVec[page].GetEnabled() - def Enable(self, page, enabled=True): + def EnableTab(self, page, enabled=True): """ Enables or disables a tab. """ if page >= len(self._pagesInfoVec): return - self._pagesInfoVec[page].Enable(enabled) + self._pagesInfoVec[page].EnableTab(enabled) def GetSingleLineBorderColour(self): @@ -4503,10 +4759,16 @@ class PageContainer(wx.Panel): def OnLeftDClick(self, event): """ Handles the wx.EVT_LEFT_DCLICK event for L{PageContainer}. """ - if self.HasFlag(FNB_DCLICK_CLOSES_TABS): + where, tabIdx = self.HitTest(event.GetPosition()) + + if where == FNB_RIGHT_ARROW: + self.RotateRight() + + elif where == FNB_LEFT_ARROW: + self.RotateLeft() + + elif self.HasFlag(FNB_DCLICK_CLOSES_TABS): - where, tabIdx = self.HitTest(event.GetPosition()) - if where == FNB_TAB: self.DeletePage(tabIdx) @@ -4515,6 +4777,57 @@ class PageContainer(wx.Panel): event.Skip() + def OnSetFocus(self, event): + """ Handles the wx.EVT_SET_FOCUS event for L{PageContainer}. """ + + if self._iActivePage < 0: + event.Skip() + return + + self.SetFocusedPage(self._iActivePage) + + + def OnKillFocus(self, event): + """ Handles the wx.EVT_KILL_FOCUS event for L{PageContainer}. """ + + self.SetFocusedPage() + + + def OnKeyDown(self, event): + """ + When the PageContainer has the focus tabs can be changed with + the left/right arrow keys. + """ + key = event.GetKeyCode() + print key + if key == wx.WXK_LEFT: + self.GetParent().AdvanceSelection(False) + elif key == wx.WXK_RIGHT: + self.GetParent().AdvanceSelection(True) + elif key == wx.WXK_TAB: + flags = 0 + if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward + if event.CmdDown(): flags |= wx.NavigationKeyEvent.WinChange + self.Navigate(flags) + else: + event.Skip() + + + def SetFocusedPage(self, pageIndex=-1): + """ + Sets/Unsets the focus on the appropriate page. + If pageIndex is defaulted, we have lost focus and no focus indicator is drawn. + """ + + for indx, page in enumerate(self._pagesInfoVec): + if indx == pageIndex: + page._hasFocus = True + else: + page._hasFocus = False + + self.Refresh() + + def PopupTabsMenu(self): """ Pops up the menu activated with the drop down arrow in the navigation area. """ @@ -4522,14 +4835,16 @@ class PageContainer(wx.Panel): for i in xrange(len(self._pagesInfoVec)): pi = self._pagesInfoVec[i] - item = wx.MenuItem(popupMenu, i, pi.GetCaption(), pi.GetCaption(), wx.ITEM_NORMAL) + item = wx.MenuItem(popupMenu, i+1, pi.GetCaption(), pi.GetCaption(), wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.OnTabMenuSelection, item) - # This code is commented, since there is an alignment problem with wx2.6.3 & Menus - # if self.TabHasImage(ii): - # item.SetBitmaps( (*m_ImageList)[pi.GetImageIndex()] ); + # There is an alignment problem with wx2.6.3 & Menus so only use + # images for versions above 2.6.3 + if wx.VERSION > (2, 6, 3, 0) and self.TabHasImage(i): + item.SetBitmap(self.GetImageList().GetBitmap(pi.GetImageIndex())) popupMenu.AppendItem(item) + item.EnableTab(pi.GetEnabled()) self.PopupMenu(popupMenu) @@ -4537,9 +4852,38 @@ class PageContainer(wx.Panel): def OnTabMenuSelection(self, event): """ Handles the wx.EVT_MENU event for L{PageContainer}. """ - selection = event.GetId() - self._pParent.SetSelection(selection) + selection = event.GetId() - 1 + self.FireEvent(selection) + + + def FireEvent(self, selection): + """ + Fires the wxEVT_FLATNOTEBOOK_PAGE_CHANGING and wxEVT_FLATNOTEBOOK_PAGE_CHANGED events + called from other methods (from menu selection or Smart Tabbing). + Utility function. + """ + + if selection == self._iActivePage: + # No events for the same selection + return + + oldSelection = self._iActivePage + + event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId()) + event.SetSelection(selection) + event.SetOldSelection(oldSelection) + event.SetEventObject(self.GetParent()) + + if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed(): + + self.SetSelection(selection) + # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event + event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED) + event.SetOldSelection(oldSelection) + self.GetParent().GetEventHandler().ProcessEvent(event) + self.SetFocus() + def SetImageList(self, imglist): """ Sets the image list for the page control. """ @@ -4547,6 +4891,12 @@ class PageContainer(wx.Panel): self._ImageList = imglist + def AssignImageList(self, imglist): + """ Assigns the image list for the page control. """ + + self._ImageList = imglist + + def GetImageList(self): """ Returns the image list for the page control. """ @@ -4578,3 +4928,13 @@ class PageContainer(wx.Panel): return True + def DrawDragHint(self): + """ Draws small arrow at the place that the tab will be placed. """ + + # get the index of tab that will be replaced with the dragged tab + pt = wx.GetMousePosition() + client_pt = self.ScreenToClient(pt) + where, tabIdx = self.HitTest(client_pt) + self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag()).DrawDragHint(self, tabIdx) + +