1 # --------------------------------------------------------------------------- #
2 # FLATNOTEBOOK Widget wxPython IMPLEMENTATION
4 # Original C++ Code From Eran. You Can Find It At:
6 # http://wxforum.shadonet.com/viewtopic.php?t=5761&start=0
8 # License: wxWidgets license
13 # Andrea Gavana, @ 02 Oct 2006
14 # Latest Revision: 04 Oct 2006, 20.00 GMT
17 # For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
20 # andrea.gavana@gmail.com
23 # Or, Obviously, To The wxPython Mailing List!!!
27 # --------------------------------------------------------------------------- #
30 The FlatNotebook is a full implementation of the wx.Notebook, and designed to be
31 a drop-in replacement for wx.Notebook. The API functions are similar so one can
32 expect the function to behave in the same way.
35 - The buttons are highlighted a la Firefox style
36 - The scrolling is done for bulks of tabs (so, the scrolling is faster and better)
37 - The buttons area is never overdrawn by tabs (unlike many other implementations I saw)
38 - It is a generic control
39 - Currently there are 4 differnt styles - VC8, VC 71, Standard and Fancy.
40 - Mouse middle click can be used to close tabs
41 - A function to add right click menu for tabs (simple as SetRightClickMenu)
42 - All styles has bottom style as well (they can be drawn in the bottom of screen)
43 - An option to hide 'X' button or navigation buttons (separately)
44 - Gradient coloring of the selected tabs and border
45 - Support for drag 'n' drop of tabs, both in the same notebook or to another notebook
46 - Possibility to have closing button on the active tab directly
47 - Support for disabled tabs
48 - Colours for active/inactive tabs, and captions
49 - Background of tab area can be painted in gradient (VC8 style only)
50 - Colourful tabs - a random gentle colour is generated for each new tab (very cool,
58 The following example shows a simple implementation that uses FlatNotebook inside
62 import wx.lib.flatnotebook as FNB
64 class MyFrame(wx.Frame):
66 def __init__(self, parent, id=wx.ID_ANY, title="", pos=wx.DefaultPosition, size=(800, 600),
67 style=wx.DEFAULT_FRAME_STYLE | wx.MAXIMIZE |wx.NO_FULL_REPAINT_ON_RESIZE):
69 wx.Frame.__init__(self, parent, id, title, pos, size, style)
71 mainSizer = wx.BoxSizer(wx.VERTICAL)
72 self.SetSizer(mainSizer)
74 bookStyle = FNB.FNB_TABS_BORDER_SIMPLE
76 self.book = FNB.StyledNotebook(self, wx.ID_ANY, style=bookStyle)
77 mainSizer.Add(self.book, 1, wx.EXPAND)
79 # Add some pages to the notebook
82 text = wx.TextCtrl(self.book, -1, "Book Page 1", style=wx.TE_MULTILINE)
83 self.book.AddPage(text, "Book Page 1")
85 text = wx.TextCtrl(self.book, -1, "Book Page 2", style=wx.TE_MULTILINE)
86 self.book.AddPage(text, "Book Page 2")
93 # our normal wxApp-derived class, as usual
95 app = wx.PySimpleApp()
98 app.SetTopWindow(frame)
106 FlatNotebook Is Freeware And Distributed Under The wxPython License.
108 Latest Revision: Andrea Gavana @ 04 Oct 2006, 20.00 GMT
119 # Check for the new method in 2.7 (not present in 2.6.3.3)
120 if wx
.VERSION_STRING
< "2.7":
121 wx
.Rect
.Contains
= lambda self
, point
: wx
.Rect
.Inside(self
, point
)
123 FNB_HEIGHT_SPACER
= 10
125 # Use Visual Studio 2003 (VC7.1) Style for tabs
128 # Use fancy style - square tabs filled with gradient coloring
131 # Draw thin border around the page
132 FNB_TABS_BORDER_SIMPLE
= 4
134 # Do not display the 'X' button
137 # Do not display the Right / Left arrows
138 FNB_NO_NAV_BUTTONS
= 16
140 # Use the mouse middle button for cloing tabs
141 FNB_MOUSE_MIDDLE_CLOSES_TABS
= 32
143 # Place tabs at bottom - the default is to place them
147 # Disable dragging of tabs
150 # Use Visual Studio 2005 (VC8) Style for tabs
154 # Note: This style is not supported on VC8 style
157 FNB_BACKGROUND_GRADIENT
= 1024
159 FNB_COLORFUL_TABS
= 2048
161 # Style to close tab using double click - styles 1024, 2048 are reserved
162 FNB_DCLICK_CLOSES_TABS
= 4096
164 VERTICAL_BORDER_PADDING
= 4
166 # Button size is a 16x16 xpm bitmap
171 MASK_COLOR
= wx
.Color(0, 128, 128)
180 FNB_TAB
= 1 # On a tab
181 FNB_X
= 2 # On the X button
182 FNB_TAB_X
= 3 # On the 'X' button (tab's X button)
183 FNB_LEFT_ARROW
= 4 # On the rotate left arrow button
184 FNB_RIGHT_ARROW
= 5 # On the rotate right arrow button
185 FNB_NOWHERE
= 0 # Anywhere else
187 FNB_DEFAULT_STYLE
= FNB_MOUSE_MIDDLE_CLOSES_TABS
189 # FlatNotebook Events:
190 # wxEVT_FLATNOTEBOOK_PAGE_CHANGED: Event Fired When You Switch Page;
191 # wxEVT_FLATNOTEBOOK_PAGE_CHANGING: Event Fired When You Are About To Switch
192 # Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call
193 # event.Skip() In Your Event Handler;
194 # wxEVT_FLATNOTEBOOK_PAGE_CLOSING: Event Fired When A Page Is Closing, But
195 # You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip()
196 # In Your Event Handler;
197 # wxEVT_FLATNOTEBOOK_PAGE_CLOSED: Event Fired When A Page Is Closed.
198 # wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU: Event Fired When A Menu Pops-up In A Tab.
200 wxEVT_FLATNOTEBOOK_PAGE_CHANGED
= wx
.NewEventType()
201 wxEVT_FLATNOTEBOOK_PAGE_CHANGING
= wx
.NewEventType()
202 wxEVT_FLATNOTEBOOK_PAGE_CLOSING
= wx
.NewEventType()
203 wxEVT_FLATNOTEBOOK_PAGE_CLOSED
= wx
.NewEventType()
204 wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU
= wx
.NewEventType()
206 #-----------------------------------#
208 #-----------------------------------#
210 EVT_FLATNOTEBOOK_PAGE_CHANGED
= wx
.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGED
, 1)
211 EVT_FLATNOTEBOOK_PAGE_CHANGING
= wx
.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CHANGING
, 1)
212 EVT_FLATNOTEBOOK_PAGE_CLOSING
= wx
.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING
, 1)
213 EVT_FLATNOTEBOOK_PAGE_CLOSED
= wx
.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSED
, 1)
214 EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU
= wx
.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU
, 1)
216 # Some icons in XPM format
218 left_arrow_disabled_xpm
= [
246 x_button_pressed_xpm
= [
303 x_button_hilite_xpm
= [
359 left_arrow_pressed_xpm
= [
387 left_arrow_hilite_xpm
= [
415 right_arrow_disabled_xpm
= [
443 right_arrow_hilite_xpm
= [
471 right_arrow_pressed_xpm
= [
530 def LightColour(color
, percent
):
531 """ Brighten input colour by percent. """
535 rd
= end_color
.Red() - color
.Red()
536 gd
= end_color
.Green() - color
.Green()
537 bd
= end_color
.Blue() - color
.Blue()
541 # We take the percent way of the color from color -. white
543 r
= color
.Red() + ((i
*rd
*100)/high
)/100
544 g
= color
.Green() + ((i
*gd
*100)/high
)/100
545 b
= color
.Blue() + ((i
*bd
*100)/high
)/100
546 return wx
.Color(r
, g
, b
)
549 def PaintStraightGradientBox(dc
, rect
, startColor
, endColor
, vertical
=True):
550 """ Draws a gradient colored box from startColor to endColor. """
552 rd
= endColor
.Red() - startColor
.Red()
553 gd
= endColor
.Green() - startColor
.Green()
554 bd
= endColor
.Blue() - startColor
.Blue()
556 # Save the current pen and brush
557 savedPen
= dc
.GetPen()
558 savedBrush
= dc
.GetBrush()
561 high
= rect
.GetHeight()-1
563 high
= rect
.GetWidth()-1
568 for i
in xrange(high
+1):
570 r
= startColor
.Red() + ((i
*rd
*100)/high
)/100
571 g
= startColor
.Green() + ((i
*gd
*100)/high
)/100
572 b
= startColor
.Blue() + ((i
*bd
*100)/high
)/100
574 p
= wx
.Pen(wx
.Color(r
, g
, b
))
578 dc
.DrawLine(rect
.x
, rect
.y
+i
, rect
.x
+rect
.width
, rect
.y
+i
)
580 dc
.DrawLine(rect
.x
+i
, rect
.y
, rect
.x
+i
, rect
.y
+rect
.height
)
582 # Restore the pen and brush
584 dc
.SetBrush(savedBrush
)
588 """ Creates a random colour. """
590 r
= random
.randint(0, 255) # Random value betweem 0-255
591 g
= random
.randint(0, 255) # Random value betweem 0-255
592 b
= random
.randint(0, 255) # Random value betweem 0-255
594 return wx
.Color(r
, g
, b
)
597 # ---------------------------------------------------------------------------- #
599 # Stores All The Information To Allow Drag And Drop Between Different
601 # ---------------------------------------------------------------------------- #
605 _map
= weakref
.WeakValueDictionary()
607 def __init__(self
, container
, pageindex
):
608 """ Default class constructor. """
610 self
._id
= id(container
)
611 FNBDragInfo
._map
[self
._id
] = container
612 self
._pageindex
= pageindex
615 def GetContainer(self
):
616 """ Returns the FlatNotebook page (usually a panel). """
618 return FNBDragInfo
._map
.get(self
._id
, None)
621 def GetPageIndex(self
):
622 """ Returns the page index associated with a page. """
624 return self
._pageindex
627 # ---------------------------------------------------------------------------- #
628 # Class FNBDropTarget
629 # Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between
630 # Different FlatNotebooks.
631 # ---------------------------------------------------------------------------- #
633 class FNBDropTarget(wx
.DropTarget
):
635 def __init__(self
, parent
):
636 """ Default class constructor. """
638 wx
.DropTarget
.__init
__(self
)
640 self
._parent
= parent
641 self
._dataobject
= wx
.CustomDataObject(wx
.CustomDataFormat("FlatNotebook"))
642 self
.SetDataObject(self
._dataobject
)
645 def OnData(self
, x
, y
, dragres
):
646 """ Handles the OnData() method to call the real DnD routine. """
648 if not self
.GetData():
651 draginfo
= self
._dataobject
.GetData()
652 drginfo
= cPickle
.loads(draginfo
)
654 return self
._parent
.OnDropTarget(x
, y
, drginfo
.GetPageIndex(), drginfo
.GetContainer())
657 # ---------------------------------------------------------------------------- #
659 # Contains parameters for every FlatNotebook page
660 # ---------------------------------------------------------------------------- #
664 def __init__(self
, caption
="", imageindex
=-1, tabangle
=0, enabled
=True):
666 Default Class Constructor.
669 - caption: the tab caption;
670 - imageindex: the tab image index based on the assigned (set) wx.ImageList (if any);
671 - tabangle: the tab angle (only on standard tabs, from 0 to 15 degrees);
672 - enabled: sets enabled or disabled the tab.
675 self
._strCaption
= caption
676 self
._TabAngle
= tabangle
677 self
._ImageIndex
= imageindex
678 self
._bEnabled
= enabled
679 self
._pos
= wx
.Point(-1, -1)
680 self
._size
= wx
.Size(-1, -1)
681 self
._region
= wx
.Region()
682 self
._xRect
= wx
.Rect()
686 def SetCaption(self
, value
):
687 """ Sets the tab caption. """
689 self
._strCaption
= value
692 def GetCaption(self
):
693 """ Returns the tab caption. """
695 return self
._strCaption
698 def SetPosition(self
, value
):
699 """ Sets the tab position. """
704 def GetPosition(self
):
705 """ Returns the tab position. """
710 def SetSize(self
, value
):
711 """ Sets the tab size. """
717 """ Returns the tab size. """
722 def SetTabAngle(self
, value
):
723 """ Sets the tab header angle (0 <= tab <= 15 degrees). """
725 self
._TabAngle
= min(45, value
)
728 def GetTabAngle(self
):
729 """ Returns the tab angle. """
731 return self
._TabAngle
734 def SetImageIndex(self
, value
):
735 """ Sets the tab image index. """
737 self
._ImageIndex
= value
740 def GetImageIndex(self
):
741 """ Returns the tab umage index. """
743 return self
._ImageIndex
746 def GetEnabled(self
):
747 """ Returns whether the tab is enabled or not. """
749 return self
._bEnabled
752 def Enable(self
, enabled
):
753 """ Sets the tab enabled or disabled. """
755 self
._bEnabled
= enabled
758 def SetRegion(self
, points
=[]):
759 """ Sets the tab region. """
761 self
._region
= wx
.RegionFromPoints(points
)
765 """ Returns the tab region. """
770 def SetXRect(self
, xrect
):
771 """ Sets the button 'X' area rect. """
777 """ Returns the button 'X' area rect. """
783 """ Returns the tab colour. """
788 def SetColor(self
, color
):
789 """ Sets the tab colour. """
794 # ---------------------------------------------------------------------------- #
795 # Class FlatNotebookEvent
796 # ---------------------------------------------------------------------------- #
798 class FlatNotebookEvent(wx
.PyCommandEvent
):
800 This events will be sent when a EVT_FLATNOTEBOOK_PAGE_CHANGED,
801 EVT_FLATNOTEBOOK_PAGE_CHANGING And EVT_FLATNOTEBOOK_PAGE_CLOSING is mapped in
805 def __init__(self
, eventType
, id=1, nSel
=-1, nOldSel
=-1):
806 """ Default class constructor. """
808 wx
.PyCommandEvent
.__init
__(self
, eventType
, id)
809 self
._eventType
= eventType
811 self
.notify
= wx
.NotifyEvent(eventType
, id)
814 def GetNotifyEvent(self
):
815 """Returns the actual wx.NotifyEvent."""
821 """Returns whether the event is allowed or not."""
823 return self
.notify
.IsAllowed()
827 """Vetos the event."""
833 """The event is allowed."""
838 def SetSelection(self
, nSel
):
839 """ Sets event selection. """
841 self
._selection
= nSel
844 def SetOldSelection(self
, nOldSel
):
845 """ Sets old event selection. """
847 self
._oldselection
= nOldSel
850 def GetSelection(self
):
851 """ Returns event selection. """
853 return self
._selection
856 def GetOldSelection(self
):
857 """ Returns old event selection """
859 return self
._oldselection
862 # ---------------------------------------------------------------------------- #
863 # Class FlatNotebookBase
864 # ---------------------------------------------------------------------------- #
866 class FlatNotebookBase(wx
.Panel
):
868 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
869 style
=0, name
="FlatNotebook"):
871 Default class constructor.
873 All the parameters are as in wxPython class construction, except the
874 'style': this can be assigned to whatever combination of FNB_* styles.
877 self
._bForceSelection
= False
880 style |
= wx
.TAB_TRAVERSAL
884 wx
.Panel
.__init
__(self
, parent
, id, pos
, size
, style
)
886 self
._pages
= StyledTabsContainer(self
, wx
.ID_ANY
, wx
.DefaultPosition
, wx
.DefaultSize
, style
)
888 self
.Bind(wx
.EVT_NAVIGATION_KEY
, self
.OnNavigationKey
)
890 self
._pages
._colorBorder
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
892 self
._mainSizer
= wx
.BoxSizer(wx
.VERTICAL
)
893 self
.SetSizer(self
._mainSizer
)
895 self
.SetBackgroundColour(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_APPWORKSPACE
))
897 # Add the tab container to the sizer
898 self
._mainSizer
.Insert(0, self
._pages
, 0, wx
.EXPAND
)
900 # Set default page height
901 dc
= wx
.ClientDC(self
)
903 if "__WXGTK__" in wx
.PlatformInfo
:
904 # For GTK it seems that we must do this steps in order
905 # for the tabs will get the proper height on initialization
906 # on MSW, preforming these steps yields wierd results
907 normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
908 boldFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
909 boldFont
.SetWeight(wx
.FONTWEIGHT_BOLD
)
912 width
, height
= dc
.GetTextExtent("Tp")
914 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 8 pixels as padding
915 self
._pages
.SetSizeHints(-1, tabHeight
)
917 self
._mainSizer
.Layout()
919 self
._pages
._nFrom
= self
._nFrom
920 self
._pDropTarget
= FNBDropTarget(self
)
921 self
.SetDropTarget(self
._pDropTarget
)
924 def CreatePageContainer(self
):
925 """ Creates the page container for the tabs. """
927 return PageContainerBase(self
, wx
.ID_ANY
)
930 def SetActiveTabTextColour(self
, textColour
):
931 """ Sets the text colour for the active tab. """
933 self
._pages
._activeTextColor
= textColour
936 def OnDropTarget(self
, x
, y
, nTabPage
, wnd_oldContainer
):
937 """ Handles the drop action from a DND operation. """
939 return self
._pages
.OnDropTarget(x
, y
, nTabPage
, wnd_oldContainer
)
942 def AddPage(self
, window
, caption
, selected
=True, imgindex
=-1):
944 Add a page to the FlatNotebook.
947 - window: Specifies the new page.
948 - caption: Specifies the text for the new page.
949 - selected: Specifies whether the page should be selected.
950 - imgindex: Specifies the optional image index for the new page.
953 True if successful, False otherwise.
960 # reparent the window to us
961 window
.Reparent(self
)
964 bSelected
= selected
or not self
._windows
965 curSel
= self
._pages
.GetSelection()
967 if not self
._pages
.IsShown():
970 self
._pages
.AddPage(caption
, bSelected
, imgindex
)
971 self
._windows
.append(window
)
975 # Check if a new selection was made
980 # Remove the window from the main sizer
981 self
._mainSizer
.Detach(self
._windows
[curSel
])
982 self
._windows
[curSel
].Hide()
984 if self
.GetWindowStyleFlag() & FNB_BOTTOM
:
986 self
._mainSizer
.Insert(0, window
, 1, wx
.EXPAND
)
990 # We leave a space of 1 pixel around the window
991 self
._mainSizer
.Add(window
, 1, wx
.EXPAND
)
998 self
._mainSizer
.Layout()
1005 def SetImageList(self
, imglist
):
1007 Sets the image list for the page control. It does not take ownership
1008 of the image list, you must delete it yourself.
1011 self
._pages
.SetImageList(imglist
)
1014 def GetImageList(self
):
1015 """ Returns the associated image list. """
1017 return self
._pages
.GetImageList()
1020 def InsertPage(self
, indx
, page
, text
, select
=True, imgindex
=-1):
1022 Inserts a new page at the specified position.
1025 - indx: Specifies the position of the new page.
1026 - page: Specifies the new page.
1027 - text: Specifies the text for the new page.
1028 - select: Specifies whether the page should be selected.
1029 - imgindex: Specifies the optional image index for the new page.
1032 True if successful, False otherwise.
1039 # reparent the window to us
1042 if not self
._windows
:
1044 self
.AddPage(page
, text
, select
, imgindex
)
1048 bSelected
= select
or not self
._windows
1049 curSel
= self
._pages
.GetSelection()
1051 indx
= max(0, min(indx
, len(self
._windows
)))
1053 if indx
<= len(self
._windows
):
1055 self
._windows
.insert(indx
, page
)
1059 self
._windows
.append(page
)
1061 self
._pages
.InsertPage(indx
, text
, bSelected
, imgindex
)
1068 # Check if a new selection was made
1073 # Remove the window from the main sizer
1074 self
._mainSizer
.Detach(self
._windows
[curSel
])
1075 self
._windows
[curSel
].Hide()
1077 self
._pages
.SetSelection(indx
)
1085 self
._mainSizer
.Layout()
1091 def SetSelection(self
, page
):
1093 Sets the selection for the given page.
1094 The call to this function generates the page changing events
1097 if page
>= len(self
._windows
) or not self
._windows
:
1100 # Support for disabed tabs
1101 if not self
._pages
.GetEnabled(page
) and len(self
._windows
) > 1 and not self
._bForceSelection
:
1104 curSel
= self
._pages
.GetSelection()
1106 # program allows the page change
1110 # Remove the window from the main sizer
1111 self
._mainSizer
.Detach(self
._windows
[curSel
])
1112 self
._windows
[curSel
].Hide()
1114 if self
.GetWindowStyleFlag() & FNB_BOTTOM
:
1116 self
._mainSizer
.Insert(0, self
._windows
[page
], 1, wx
.EXPAND
)
1120 # We leave a space of 1 pixel around the window
1121 self
._mainSizer
.Add(self
._windows
[page
], 1, wx
.EXPAND
)
1123 self
._windows
[page
].Show()
1125 self
._mainSizer
.Layout()
1126 self
._pages
._iActivePage
= page
1130 self
._pages
.DoSetSelection(page
)
1133 def DeletePage(self
, page
):
1135 Deletes the specified page, and the associated window.
1136 The call to this function generates the page changing events.
1139 if page
>= len(self
._windows
):
1142 # Fire a closing event
1143 event
= FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING
, self
.GetId())
1144 event
.SetSelection(page
)
1145 event
.SetEventObject(self
)
1146 self
.GetEventHandler().ProcessEvent(event
)
1148 # The event handler allows it?
1149 if not event
.IsAllowed():
1154 # Delete the requested page
1155 pageRemoved
= self
._windows
[page
]
1157 # If the page is the current window, remove it from the sizer
1159 if page
== self
._pages
.GetSelection():
1160 self
._mainSizer
.Detach(pageRemoved
)
1162 # Remove it from the array as well
1163 self
._windows
.pop(page
)
1165 # Now we can destroy it in wxWidgets use Destroy instead of delete
1166 pageRemoved
.Destroy()
1170 self
._pages
.DoDeletePage(page
)
1173 # Fire a closed event
1174 closedEvent
= FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED
, self
.GetId())
1175 closedEvent
.SetSelection(page
)
1176 closedEvent
.SetEventObject(self
)
1177 self
.GetEventHandler().ProcessEvent(closedEvent
)
1180 def DeleteAllPages(self
):
1181 """ Deletes all the pages. """
1183 if not self
._windows
:
1188 for page
in self
._windows
:
1194 # Clear the container of the tabs as well
1195 self
._pages
.DeleteAllPages()
1199 def GetCurrentPage(self
):
1200 """ Returns the currently selected notebook page or None. """
1202 sel
= self
._pages
.GetSelection()
1206 return self
._windows
[sel
]
1209 def GetPage(self
, page
):
1210 """ Returns the window at the given page position, or None. """
1212 if page
>= len(self
._windows
):
1215 return self
._windows
[page
]
1218 def GetPageIndex(self
, win
):
1219 """ Returns the index at which the window is found. """
1222 return self
._windows
.index(win
)
1227 def GetSelection(self
):
1228 """ Returns the currently selected page, or -1 if none was selected. """
1230 return self
._pages
.GetSelection()
1233 def AdvanceSelection(self
, bForward
=True):
1235 Cycles through the tabs.
1236 The call to this function generates the page changing events.
1239 self
._pages
.AdvanceSelection(bForward
)
1242 def GetPageCount(self
):
1243 """ Returns the number of pages in the FlatNotebook control. """
1244 return self
._pages
.GetPageCount()
1247 def OnNavigationKey(self
, event
):
1248 """ Handles the wx.EVT_NAVIGATION_KEY event for FlatNotebook. """
1250 if event
.IsWindowChange():
1252 self
.AdvanceSelection(event
.GetDirection())
1254 # pass to the parent
1255 if self
.GetParent():
1256 event
.SetCurrentFocus(self
)
1257 self
.GetParent().ProcessEvent(event
)
1260 def GetPageShapeAngle(self
, page_index
):
1261 """ Returns the angle associated to a tab. """
1263 if page_index
< 0 or page_index
>= len(self
._pages
._pagesInfoVec
):
1266 result
= self
._pages
._pagesInfoVec
[page_index
].GetTabAngle()
1270 def SetPageShapeAngle(self
, page_index
, angle
):
1271 """ Sets the angle associated to a tab. """
1273 if page_index
< 0 or page_index
>= len(self
._pages
._pagesInfoVec
):
1279 self
._pages
._pagesInfoVec
[page_index
].SetTabAngle(angle
)
1282 def SetAllPagesShapeAngle(self
, angle
):
1283 """ Sets the angle associated to all the tab. """
1288 for ii
in xrange(len(self
._pages
._pagesInfoVec
)):
1289 self
._pages
._pagesInfoVec
[ii
].SetTabAngle(angle
)
1294 def GetPageBestSize(self
):
1295 """ Return the page best size. """
1297 return self
._pages
.GetClientSize()
1300 def SetPageText(self
, page
, text
):
1301 """ Sets the text for the given page. """
1303 bVal
= self
._pages
.SetPageText(page
, text
)
1304 self
._pages
.Refresh()
1309 def SetPadding(self
, padding
):
1311 Sets the amount of space around each page's icon and label, in pixels.
1312 NB: only the horizontal padding is considered.
1315 self
._nPadding
= padding
.GetWidth()
1318 def GetTabArea(self
):
1319 """ Returns the associated page. """
1324 def GetPadding(self
):
1325 """ Returns the amount of space around each page's icon and label, in pixels. """
1327 return self
._nPadding
1330 def SetWindowStyleFlag(self
, style
):
1331 """ Sets the FlatNotebook window style flags. """
1333 wx
.Panel
.SetWindowStyleFlag(self
, style
)
1337 # For changing the tab position (i.e. placing them top/bottom)
1338 # refreshing the tab container is not enough
1339 self
.SetSelection(self
._pages
._iActivePage
)
1342 def RemovePage(self
, page
):
1343 """ Deletes the specified page, without deleting the associated window. """
1345 if page
>= len(self
._windows
):
1348 # Fire a closing event
1349 event
= FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING
, self
.GetId())
1350 event
.SetSelection(page
)
1351 event
.SetEventObject(self
)
1352 self
.GetEventHandler().ProcessEvent(event
)
1354 # The event handler allows it?
1355 if not event
.IsAllowed():
1360 # Remove the requested page
1361 pageRemoved
= self
._windows
[page
]
1363 # If the page is the current window, remove it from the sizer
1365 if page
== self
._pages
.GetSelection():
1366 self
._mainSizer
.Detach(pageRemoved
)
1368 # Remove it from the array as well
1369 self
._windows
.pop(page
)
1372 self
._pages
.DoDeletePage(page
)
1377 def SetRightClickMenu(self
, menu
):
1378 """ Sets the popup menu associated to a right click on a tab. """
1380 self
._pages
._pRightClickMenu
= menu
1383 def GetPageText(self
, page
):
1384 """ Returns the tab caption. """
1386 return self
._pages
.GetPageText(page
)
1389 def SetGradientColors(self
, fr
, to
, border
):
1390 """ Sets the gradient colours for the tab. """
1392 self
._pages
._colorFrom
= fr
1393 self
._pages
._colorTo
= to
1394 self
._pages
._colorBorder
= border
1397 def SetGradientColorFrom(self
, fr
):
1398 """ Sets the starting colour for the gradient. """
1400 self
._pages
._colorFrom
= fr
1403 def SetGradientColorTo(self
, to
):
1404 """ Sets the ending colour for the gradient. """
1406 self
._pages
._colorTo
= to
1409 def SetGradientColorBorder(self
, border
):
1410 """ Sets the tab border colour. """
1412 self
._pages
._colorBorder
= border
1415 def GetGradientColorFrom(self
):
1416 """ Gets first gradient colour. """
1418 return self
._pages
._colorFrom
1421 def GetGradientColorTo(self
):
1422 """ Gets second gradient colour. """
1424 return self
._pages
._colorTo
1427 def GetGradientColorBorder(self
):
1428 """ Gets the tab border colour. """
1430 return self
._pages
._colorBorder
1433 def GetActiveTabTextColour(self
):
1434 """ Get the active tab text colour. """
1436 return self
._pages
._activeTextColor
1439 def SetPageImageIndex(self
, page
, imgindex
):
1441 Sets the image index for the given page. Image is an index into the
1442 image list which was set with SetImageList.
1445 self
._pages
.SetPageImageIndex(page
, imgindex
)
1448 def GetPageImageIndex(self
, page
):
1450 Returns the image index for the given page. Image is an index into the
1451 image list which was set with SetImageList.
1454 return self
._pages
.GetPageImageIndex(page
)
1457 def GetEnabled(self
, page
):
1458 """ Returns whether a tab is enabled or not. """
1460 return self
._pages
.GetEnabled(page
)
1463 def Enable(self
, page
, enabled
=True):
1464 """ Enables or disables a tab. """
1466 if page
>= len(self
._windows
):
1469 self
._windows
[page
].Enable(enabled
)
1470 self
._pages
.Enable(page
, enabled
)
1473 def GetNonActiveTabTextColour(self
):
1474 """ Returns the non active tabs text colour. """
1476 return self
._pages
._nonActiveTextColor
1479 def SetNonActiveTabTextColour(self
, color
):
1480 """ Sets the non active tabs text colour. """
1482 self
._pages
._nonActiveTextColor
= color
1485 def SetTabAreaColour(self
, color
):
1486 """ Sets the area behind the tabs colour. """
1488 self
._pages
._tabAreaColor
= color
1491 def GetTabAreaColour(self
):
1492 """ Returns the area behind the tabs colour. """
1494 return self
._pages
._tabAreaColor
1497 def SetActiveTabColour(self
, color
):
1498 """ Sets the active tab colour. """
1500 self
._pages
._activeTabColor
= color
1503 def GetActiveTabColour(self
):
1504 """ Returns the active tab colour. """
1506 return self
._pages
._activeTabColor
1509 # ---------------------------------------------------------------------------- #
1510 # Class PageContainerBase
1511 # Acts as a container for the pages you add to FlatNotebook
1512 # ---------------------------------------------------------------------------- #
1514 class PageContainerBase(wx
.Panel
):
1516 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
,
1517 size
=wx
.DefaultSize
, style
=0):
1518 """ Default class constructor. """
1520 self
._ImageList
= None
1521 self
._iActivePage
= -1
1522 self
._pDropTarget
= None
1523 self
._nLeftClickZone
= FNB_NOWHERE
1524 self
._tabXBgBmp
= wx
.EmptyBitmap(16, 16)
1525 self
._xBgBmp
= wx
.EmptyBitmap(16, 14)
1526 self
._leftBgBmp
= wx
.EmptyBitmap(16, 14)
1527 self
._rightBgBmp
= wx
.EmptyBitmap(16, 14)
1529 self
._pRightClickMenu
= None
1530 self
._nXButtonStatus
= FNB_BTN_NONE
1531 self
._pParent
= parent
1532 self
._nRightButtonStatus
= FNB_BTN_NONE
1533 self
._nLeftButtonStatus
= FNB_BTN_NONE
1534 self
._nTabXButtonStatus
= FNB_BTN_NONE
1536 self
._pagesInfoVec
= []
1538 self
._colorTo
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_ACTIVECAPTION
)
1539 self
._colorFrom
= wx
.WHITE
1540 self
._activeTabColor
= wx
.WHITE
1541 self
._activeTextColor
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNTEXT
)
1542 self
._nonActiveTextColor
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)
1543 self
._tabAreaColor
= wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNFACE
)
1546 self
._isdragging
= False
1548 wx
.Panel
.__init
__(self
, parent
, id, pos
, size
, style
)
1550 self
._pDropTarget
= FNBDropTarget(self
)
1551 self
.SetDropTarget(self
._pDropTarget
)
1553 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
1554 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
1555 self
.Bind(wx
.EVT_LEFT_DOWN
, self
.OnLeftDown
)
1556 self
.Bind(wx
.EVT_LEFT_UP
, self
.OnLeftUp
)
1557 self
.Bind(wx
.EVT_RIGHT_DOWN
, self
.OnRightDown
)
1558 self
.Bind(wx
.EVT_MIDDLE_DOWN
, self
.OnMiddleDown
)
1559 self
.Bind(wx
.EVT_MOTION
, self
.OnMouseMove
)
1560 self
.Bind(wx
.EVT_ERASE_BACKGROUND
, self
.OnEraseBackground
)
1561 self
.Bind(wx
.EVT_LEAVE_WINDOW
, self
.OnMouseLeave
)
1562 self
.Bind(wx
.EVT_ENTER_WINDOW
, self
.OnMouseEnterWindow
)
1563 self
.Bind(wx
.EVT_LEFT_DCLICK
, self
.OnLeftDClick
)
1566 def GetButtonAreaWidth(self
):
1567 """ Returns the width of the navigation button area. """
1569 style
= self
.GetParent().GetWindowStyleFlag()
1570 btnareawidth
= 2*self
._pParent
._nPadding
1572 if style
& FNB_NO_X_BUTTON
== 0:
1573 btnareawidth
+= BUTTON_SPACE
1575 if style
& FNB_NO_NAV_BUTTONS
== 0:
1576 btnareawidth
+= 2*BUTTON_SPACE
1581 def OnEraseBackground(self
, event
):
1582 """ Handles the wx.EVT_ERASE_BACKGROUND event for PageContainerBase (does nothing)."""
1587 def OnPaint(self
, event
):
1588 """ Handles the wx.EVT_PAINT event for PageContainerBase."""
1590 dc
= wx
.BufferedPaintDC(self
)
1592 if "__WXMAC__" in wx
.PlatformInfo
:
1593 # Works well on MSW & GTK, however this lines should be skipped on MAC
1594 if len(self
._pagesInfoVec
) == 0 or self
._nFrom
>= len(self
._pagesInfoVec
):
1599 # Get the text hight
1600 style
= self
.GetParent().GetWindowStyleFlag()
1602 # For GTK it seems that we must do this steps in order
1603 # for the tabs will get the proper height on initialization
1604 # on MSW, preforming these steps yields wierd results
1605 normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1606 boldFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
1607 boldFont
.SetWeight(wx
.FONTWEIGHT_BOLD
)
1608 if "__WXGTK__" in wx
.PlatformInfo
:
1609 dc
.SetFont(boldFont
)
1611 width
, height
= dc
.GetTextExtent("Tp")
1613 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 8 pixels as padding
1615 # Calculate the number of rows required for drawing the tabs
1616 rect
= self
.GetClientRect()
1617 clientWidth
= rect
.width
1619 # Set the maximum client size
1620 self
.SetSizeHints(self
.GetButtonsAreaLength(), tabHeight
)
1621 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
1623 if style
& FNB_VC71
:
1624 backBrush
= wx
.Brush(wx
.Colour(247, 243, 233))
1626 backBrush
= wx
.Brush(self
._tabAreaColor
)
1628 noselBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNFACE
))
1629 selBrush
= wx
.Brush(self
._activeTabColor
)
1631 size
= self
.GetSize()
1634 dc
.SetTextBackground((style
& FNB_VC71
and [wx
.Colour(247, 243, 233)] or [self
.GetBackgroundColour()])[0])
1635 dc
.SetTextForeground(self
._activeTextColor
)
1636 dc
.SetBrush(backBrush
)
1638 # If border style is set, set the pen to be border pen
1639 if style
& FNB_TABS_BORDER_SIMPLE
:
1640 dc
.SetPen(borderPen
)
1642 pc
= (self
.HasFlag(FNB_VC71
) and [wx
.Colour(247, 243, 233)] or [self
.GetBackgroundColour()])[0]
1643 dc
.SetPen(wx
.Pen(pc
))
1645 dc
.DrawRectangle(0, 0, size
.x
, size
.y
)
1647 # Take 3 bitmaps for the background for the buttons
1649 mem_dc
= wx
.MemoryDC()
1651 #---------------------------------------
1653 #---------------------------------------
1654 rect
= wx
.Rect(self
.GetXPos(), 6, 16, 14)
1655 mem_dc
.SelectObject(self
._xBgBmp
)
1656 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
1657 mem_dc
.SelectObject(wx
.NullBitmap
)
1659 #---------------------------------------
1661 #---------------------------------------
1662 rect
= wx
.Rect(self
.GetRightButtonPos(), 6, 16, 14)
1663 mem_dc
.SelectObject(self
._rightBgBmp
)
1664 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
1665 mem_dc
.SelectObject(wx
.NullBitmap
)
1667 #---------------------------------------
1669 #---------------------------------------
1670 rect
= wx
.Rect(self
.GetLeftButtonPos(), 6, 16, 14)
1671 mem_dc
.SelectObject(self
._leftBgBmp
)
1672 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
1673 mem_dc
.SelectObject(wx
.NullBitmap
)
1675 # We always draw the bottom/upper line of the tabs
1676 # regradless the style
1677 dc
.SetPen(borderPen
)
1678 self
.DrawTabsLine(dc
)
1681 dc
.SetPen(borderPen
)
1683 if self
.HasFlag(FNB_VC71
):
1685 greyLineYVal
= self
.HasFlag((FNB_BOTTOM
and [0] or [size
.y
- 2])[0])
1686 whiteLineYVal
= self
.HasFlag((FNB_BOTTOM
and [3] or [size
.y
- 3])[0])
1688 pen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
))
1691 # Draw thik grey line between the windows area and
1694 for num
in xrange(3):
1695 dc
.DrawLine(0, greyLineYVal
+ num
, size
.x
, greyLineYVal
+ num
)
1697 wbPen
= wx
.Pen((self
.HasFlag(FNB_BOTTOM
) and [wx
.BLACK
] or [wx
.WHITE
])[0])
1699 dc
.DrawLine(1, whiteLineYVal
, size
.x
- 1, whiteLineYVal
)
1702 dc
.SetPen(borderPen
)
1704 if "__WXMAC__" in wx
.PlatformInfo
:
1705 # On MAC, Add these lines so the tab background gets painted
1706 if len(self
._pagesInfoVec
) == 0 or self
._nFrom
>= len(self
._pagesInfoVec
):
1711 dc
.SetFont(boldFont
)
1712 posx
= self
._pParent
.GetPadding()
1714 # Update all the tabs from 0 to '_nFrom' to be non visible
1715 for ii
in xrange(self
._nFrom
):
1716 self
._pagesInfoVec
[ii
].SetPosition(wx
.Point(-1, -1))
1717 self
._pagesInfoVec
[ii
].GetRegion().Clear()
1719 if style
& FNB_VC71
:
1720 tabHeight
= ((style
& FNB_BOTTOM
) and [tabHeight
- 4] or [tabHeight
])[0]
1721 elif style
& FNB_FANCY_TABS
:
1722 tabHeight
= ((style
& FNB_BOTTOM
) and [tabHeight
- 2] or [tabHeight
])[0]
1724 #----------------------------------------------------------
1725 # Go over and draw the visible tabs
1726 #----------------------------------------------------------
1730 for ii
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
1732 if style
!= FNB_VC71
:
1733 shapePoints
= int(tabHeight
*math
.tan(float(self
._pagesInfoVec
[ii
].GetTabAngle())/180.0*math
.pi
))
1737 dc
.SetPen(borderPen
)
1738 dc
.SetBrush((ii
==self
.GetSelection() and [selBrush
] or [noselBrush
])[0])
1740 # Calculate the text length using the bold font, so when selecting a tab
1741 # its width will not change
1742 dc
.SetFont(boldFont
)
1743 width
, pom
= dc
.GetTextExtent(self
.GetPageText(ii
))
1745 # Now set the font to the correct font
1746 dc
.SetFont((ii
==self
.GetSelection() and [boldFont
] or [normalFont
])[0])
1748 # Set a minimum size to a tab
1752 # Add the padding to the tab width
1754 # +-----------------------------------------------------------+
1755 # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
1756 # +-----------------------------------------------------------+
1758 tabWidth
= 2*self
._pParent
._nPadding
+ width
1759 imageYCoord
= (self
.HasFlag(FNB_BOTTOM
) and [6] or [8])[0]
1761 # Style to add a small 'x' button on the top right
1763 if style
& FNB_X_ON_TAB
and ii
== self
.GetSelection():
1765 # The xpm image that contains the 'x' button is 9 pixles
1766 tabWidth
+= self
._pParent
._nPadding
+ 9
1768 if not (style
& FNB_VC71
) and not (style
& FNB_FANCY_TABS
):
1770 tabWidth
+= 2*shapePoints
1772 hasImage
= self
._ImageList
!= None and self
._pagesInfoVec
[ii
].GetImageIndex() != -1
1774 # For VC71 style, we only add the icon size (16 pixels)
1777 if style
& FNB_VC71
or style
& FNB_FANCY_TABS
:
1778 tabWidth
+= 16 + self
._pParent
._nPadding
1781 tabWidth
+= 16 + self
._pParent
._nPadding
+ shapePoints
/2
1783 # Check if we can draw more
1784 if posx
+ tabWidth
+ self
.GetButtonsAreaLength() >= clientWidth
:
1789 # By default we clean the tab region
1790 self
._pagesInfoVec
[ii
].GetRegion().Clear()
1792 # Clean the 'x' buttn on the tab
1793 # 'Clean' rectanlge is a rectangle with width or height
1794 # with values lower than or equal to 0
1795 self
._pagesInfoVec
[ii
].GetXRect().SetSize(wx
.Size(-1, -1))
1798 if style
& FNB_FANCY_TABS
:
1799 self
.DrawFancyTab(dc
, posx
, ii
, tabWidth
, tabHeight
)
1800 elif style
& FNB_VC71
:
1801 self
.DrawVC71Tab(dc
, posx
, ii
, tabWidth
, tabHeight
)
1803 self
.DrawStandardTab(dc
, posx
, ii
, tabWidth
, tabHeight
)
1805 # The width of the images are 16 pixels
1807 textOffset
= 2*self
._pParent
._nPadding
+ 16 + shapePoints
/2
1809 textOffset
= self
._pParent
._nPadding
+ shapePoints
/2
1811 # After several testing, it seems that we can draw
1812 # the text 2 pixles to the right - this is done only
1813 # for the standard tabs
1815 if not self
.HasFlag(FNB_FANCY_TABS
):
1818 if ii
!= self
.GetSelection():
1819 # Set the text background to be like the vertical lines
1820 dc
.SetTextForeground(self
._nonActiveTextColor
)
1824 imageXOffset
= textOffset
- 16 - self
._pParent
._nPadding
1825 self
._ImageList
.Draw(self
._pagesInfoVec
[ii
].GetImageIndex(), dc
,
1826 posx
+ imageXOffset
, imageYCoord
,
1827 wx
.IMAGELIST_DRAW_TRANSPARENT
, True)
1829 dc
.DrawText(self
.GetPageText(ii
), posx
+ textOffset
, imageYCoord
)
1831 # From version 1.2 - a style to add 'x' button
1834 if self
.HasFlag(FNB_X_ON_TAB
) and ii
== self
.GetSelection():
1836 textWidth
, textHeight
= dc
.GetTextExtent(self
.GetPageText(ii
))
1837 tabCloseButtonXCoord
= posx
+ textOffset
+ textWidth
+ 1
1839 # take a bitmap from the position of the 'x' button (the x on tab button)
1840 # this bitmap will be used later to delete old buttons
1841 tabCloseButtonYCoord
= imageYCoord
1842 x_rect
= wx
.Rect(tabCloseButtonXCoord
, tabCloseButtonYCoord
, 16, 16)
1843 mem_dc
= wx
.MemoryDC()
1844 mem_dc
.SelectObject(self
._tabXBgBmp
)
1845 mem_dc
.Blit(0, 0, x_rect
.width
, x_rect
.height
, dc
, x_rect
.x
, x_rect
.y
)
1846 mem_dc
.SelectObject(wx
.NullBitmap
)
1849 self
.DrawTabX(dc
, x_rect
, ii
)
1851 # Restore the text forground
1852 dc
.SetTextForeground(self
._activeTextColor
)
1854 # Update the tab position & size
1855 posy
= (style
& FNB_BOTTOM
and [0] or [VERTICAL_BORDER_PADDING
])[0]
1857 self
._pagesInfoVec
[ii
].SetPosition(wx
.Point(posx
, posy
))
1858 self
._pagesInfoVec
[ii
].SetSize(wx
.Size(tabWidth
, tabHeight
))
1861 # Update all tabs that can not fit into the screen as non-visible
1862 for ii
in xrange(count
, len(self
._pagesInfoVec
)):
1864 self
._pagesInfoVec
[ii
].SetPosition(wx
.Point(-1, -1))
1865 self
._pagesInfoVec
[ii
].GetRegion().Clear()
1867 # Draw the left/right/close buttons
1869 self
.DrawLeftArrow(dc
)
1870 self
.DrawRightArrow(dc
)
1874 def DrawFancyTab(self
, dc
, posx
, tabIdx
, tabWidth
, tabHeight
):
1876 Fancy tabs - like with VC71 but with the following differences:
1877 - The Selected tab is colored with gradient color
1880 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
1881 pen
= (tabIdx
==self
.GetSelection() and [wx
.Pen(self
._colorBorder
)] \
1882 or [wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
))])[0]
1884 fnb_bottom
= self
.HasFlag(FNB_BOTTOM
)
1886 if tabIdx
== self
.GetSelection():
1888 posy
= (fnb_bottom
and [2] or [VERTICAL_BORDER_PADDING
])[0]
1889 th
= (fnb_bottom
and [tabHeight
- 2] or [tabHeight
- 5])[0]
1891 rect
= wx
.Rect(posx
, posy
, tabWidth
, th
)
1892 self
.FillGradientColor(dc
, rect
)
1893 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1895 dc
.DrawRectangleRect(rect
)
1897 # erase the bottom/top line of the rectangle
1898 dc
.SetPen(wx
.Pen(self
._colorFrom
))
1900 dc
.DrawLine(rect
.x
, 2, rect
.x
+ rect
.width
, 2)
1902 dc
.DrawLine(rect
.x
, rect
.y
+ rect
.height
- 1, rect
.x
+ rect
.width
, rect
.y
+ rect
.height
- 1)
1906 # We dont draw a rectangle for non selected tabs, but only
1907 # vertical line on the left
1908 dc
.SetPen(borderPen
)
1909 dc
.DrawLine(posx
+ tabWidth
, VERTICAL_BORDER_PADDING
+ 3, posx
+ tabWidth
, tabHeight
- 4)
1912 def DrawVC71Tab(self
, dc
, posx
, tabIdx
, tabWidth
, tabHeight
):
1913 """ Draws tabs with VC71 style. """
1915 fnb_bottom
= self
.HasFlag(FNB_BOTTOM
)
1917 # Visual studio 7.1 style
1918 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
1919 dc
.SetPen((tabIdx
==self
.GetSelection() and [wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
))] or [borderPen
])[0])
1920 dc
.SetBrush((tabIdx
==self
.GetSelection() and [wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
))] or \
1921 [wx
.Brush(wx
.Colour(247, 243, 233))])[0])
1923 if tabIdx
== self
.GetSelection():
1925 posy
= (fnb_bottom
and [0] or [VERTICAL_BORDER_PADDING
])[0]
1926 dc
.DrawRectangle(posx
, posy
, tabWidth
, tabHeight
- 1)
1928 # Draw a black line on the left side of the
1930 dc
.SetPen(wx
.BLACK_PEN
)
1932 blackLineY1
= VERTICAL_BORDER_PADDING
1933 blackLineY2
= (fnb_bottom
and [self
.GetSize().y
- 5] or [self
.GetSize().y
- 3])[0]
1934 dc
.DrawLine(posx
+ tabWidth
, blackLineY1
, posx
+ tabWidth
, blackLineY2
)
1936 # To give the tab more 3D look we do the following
1937 # Incase the tab is on top,
1938 # Draw a thick white line on top of the rectangle
1939 # Otherwise, draw a thin (1 pixel) black line at the bottom
1941 pen
= wx
.Pen((fnb_bottom
and [wx
.BLACK
] or [wx
.WHITE
])[0])
1943 whiteLinePosY
= (fnb_bottom
and [blackLineY2
] or [VERTICAL_BORDER_PADDING
])[0]
1944 dc
.DrawLine(posx
, whiteLinePosY
, posx
+ tabWidth
+ 1, whiteLinePosY
)
1946 # Draw a white vertical line to the left of the tab
1947 dc
.SetPen(wx
.WHITE_PEN
)
1951 dc
.DrawLine(posx
, blackLineY1
, posx
, blackLineY2
)
1955 # We dont draw a rectangle for non selected tabs, but only
1956 # vertical line on the left
1958 blackLineY1
= (fnb_bottom
and [VERTICAL_BORDER_PADDING
+ 2] or [VERTICAL_BORDER_PADDING
+ 1])[0]
1959 blackLineY2
= self
.GetSize().y
- 5
1960 dc
.DrawLine(posx
+ tabWidth
, blackLineY1
, posx
+ tabWidth
, blackLineY2
)
1963 def DrawStandardTab(self
, dc
, posx
, tabIdx
, tabWidth
, tabHeight
):
1964 """ Draws tabs with standard style. """
1966 fnb_bottom
= self
.HasFlag(FNB_BOTTOM
)
1969 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
1971 tabPoints
= [wx
.Point() for ii
in xrange(7)]
1972 tabPoints
[0].x
= posx
1973 tabPoints
[0].y
= (fnb_bottom
and [2] or [tabHeight
- 2])[0]
1975 tabPoints
[1].x
= int(posx
+(tabHeight
-2)*math
.tan(float(self
._pagesInfoVec
[tabIdx
].GetTabAngle())/180.0*math
.pi
))
1976 tabPoints
[1].y
= (fnb_bottom
and [tabHeight
- (VERTICAL_BORDER_PADDING
+2)] or [(VERTICAL_BORDER_PADDING
+2)])[0]
1978 tabPoints
[2].x
= tabPoints
[1].x
+2
1979 tabPoints
[2].y
= (fnb_bottom
and [tabHeight
- VERTICAL_BORDER_PADDING
] or [VERTICAL_BORDER_PADDING
])[0]
1981 tabPoints
[3].x
= int(posx
+tabWidth
-(tabHeight
-2)*math
.tan(float(self
._pagesInfoVec
[tabIdx
].GetTabAngle())/180.0*math
.pi
))-2
1982 tabPoints
[3].y
= (fnb_bottom
and [tabHeight
- VERTICAL_BORDER_PADDING
] or [VERTICAL_BORDER_PADDING
])[0]
1984 tabPoints
[4].x
= tabPoints
[3].x
+2
1985 tabPoints
[4].y
= (fnb_bottom
and [tabHeight
- (VERTICAL_BORDER_PADDING
+2)] or [(VERTICAL_BORDER_PADDING
+2)])[0]
1987 tabPoints
[5].x
= int(tabPoints
[4].x
+(tabHeight
-2)*math
.tan(float(self
._pagesInfoVec
[tabIdx
].GetTabAngle())/180.0*math
.pi
))
1988 tabPoints
[5].y
= (fnb_bottom
and [2] or [tabHeight
- 2])[0]
1990 tabPoints
[6].x
= tabPoints
[0].x
1991 tabPoints
[6].y
= tabPoints
[0].y
1993 if tabIdx
== self
.GetSelection():
1995 # Draw the tab as rounded rectangle
1996 dc
.DrawPolygon(tabPoints
)
2000 if tabIdx
!= self
.GetSelection() - 1:
2002 # Draw a vertical line to the right of the text
2003 pt1x
= tabPoints
[5].x
2004 pt1y
= (fnb_bottom
and [4] or [tabHeight
- 6])[0]
2005 pt2x
= tabPoints
[5].x
2006 pt2y
= (fnb_bottom
and [tabHeight
- 4] or [4])[0]
2007 dc
.DrawLine(pt1x
, pt1y
, pt2x
, pt2y
)
2009 if tabIdx
== self
.GetSelection():
2011 savePen
= dc
.GetPen()
2012 whitePen
= wx
.Pen(wx
.WHITE
)
2013 whitePen
.SetWidth(1)
2016 secPt
= wx
.Point(tabPoints
[5].x
+ 1, tabPoints
[5].y
)
2017 dc
.DrawLine(tabPoints
[0].x
, tabPoints
[0].y
, secPt
.x
, secPt
.y
)
2023 def AddPage(self
, caption
, selected
=True, imgindex
=-1):
2025 Add a page to the FlatNotebook.
2028 - window: Specifies the new page.
2029 - caption: Specifies the text for the new page.
2030 - selected: Specifies whether the page should be selected.
2031 - imgindex: Specifies the optional image index for the new page.
2034 True if successful, False otherwise.
2039 self
._iActivePage
= len(self
._pagesInfoVec
)
2041 # Create page info and add it to the vector
2042 pageInfo
= PageInfo(caption
, imgindex
)
2043 self
._pagesInfoVec
.append(pageInfo
)
2047 def InsertPage(self
, indx
, text
, selected
=True, imgindex
=-1):
2049 Inserts a new page at the specified position.
2052 - indx: Specifies the position of the new page.
2053 - page: Specifies the new page.
2054 - text: Specifies the text for the new page.
2055 - select: Specifies whether the page should be selected.
2056 - imgindex: Specifies the optional image index for the new page.
2059 True if successful, False otherwise.
2064 self
._iActivePage
= len(self
._pagesInfoVec
)
2066 self
._pagesInfoVec
.insert(indx
, PageInfo(text
, imgindex
))
2072 def OnSize(self
, event
):
2073 """ Handles the wx.EVT_SIZE events for PageContainerBase. """
2075 self
.Refresh() # Call on paint
2079 def OnMiddleDown(self
, event
):
2080 """ Handles the wx.EVT_MIDDLE_DOWN events for PageContainerBase. """
2082 # Test if this style is enabled
2083 style
= self
.GetParent().GetWindowStyleFlag()
2085 if not style
& FNB_MOUSE_MIDDLE_CLOSES_TABS
:
2088 where
, tabIdx
= self
.HitTest(event
.GetPosition())
2090 if where
== FNB_TAB
:
2091 self
.DeletePage(tabIdx
)
2096 def OnRightDown(self
, event
):
2097 """ Handles the wx.EVT_RIGHT_DOWN events for PageContainerBase. """
2099 if self
._pRightClickMenu
:
2101 where
, tabIdx
= self
.HitTest(event
.GetPosition())
2103 if where
in [FNB_TAB
, FNB_TAB_X
]:
2105 if self
._pagesInfoVec
[tabIdx
].GetEnabled():
2106 # Set the current tab to be active
2107 self
.SetSelection(tabIdx
)
2109 # If the owner has defined a context menu for the tabs,
2110 # popup the right click menu
2111 if self
._pRightClickMenu
:
2112 self
.PopupMenu(self
._pRightClickMenu
)
2114 # send a message to popup a custom menu
2115 event
= FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU
, self
.GetParent().GetId())
2116 event
.SetSelection(tabIdx
)
2117 event
.SetOldSelection(self
._iActivePage
)
2118 event
.SetEventObject(self
.GetParent())
2119 self
.GetParent().GetEventHandler().ProcessEvent(event
)
2124 def OnLeftDown(self
, event
):
2125 """ Handles the wx.EVT_LEFT_DOWN events for PageContainerBase. """
2127 # Reset buttons status
2128 self
._nXButtonStatus
= FNB_BTN_NONE
2129 self
._nLeftButtonStatus
= FNB_BTN_NONE
2130 self
._nRightButtonStatus
= FNB_BTN_NONE
2131 self
._nTabXButtonStatus
= FNB_BTN_NONE
2133 self
._nLeftClickZone
, tabIdx
= self
.HitTest(event
.GetPosition())
2135 if self
._nLeftClickZone
== FNB_LEFT_ARROW
:
2136 self
._nLeftButtonStatus
= FNB_BTN_PRESSED
2138 elif self
._nLeftClickZone
== FNB_RIGHT_ARROW
:
2139 self
._nRightButtonStatus
= FNB_BTN_PRESSED
2141 elif self
._nLeftClickZone
== FNB_X
:
2142 self
._nXButtonStatus
= FNB_BTN_PRESSED
2144 elif self
._nLeftClickZone
== FNB_TAB_X
:
2145 self
._nTabXButtonStatus
= FNB_BTN_PRESSED
2148 elif self
._nLeftClickZone
== FNB_TAB
:
2150 if self
._iActivePage
!= tabIdx
:
2152 # In case the tab is disabled, we dont allow to choose it
2153 if self
._pagesInfoVec
[tabIdx
].GetEnabled():
2155 oldSelection
= self
._iActivePage
2157 event
= FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING
, self
.GetParent().GetId())
2158 event
.SetSelection(tabIdx
)
2159 event
.SetOldSelection(oldSelection
)
2160 event
.SetEventObject(self
.GetParent())
2161 if not self
.GetParent().GetEventHandler().ProcessEvent(event
) or event
.IsAllowed():
2163 self
.SetSelection(tabIdx
)
2165 # Fire a wxEVT_TABBEDCTRL_PAGE_CHANGED event
2166 event
.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED
)
2167 event
.SetOldSelection(oldSelection
)
2168 self
.GetParent().GetEventHandler().ProcessEvent(event
)
2171 def OnLeftUp(self
, event
):
2172 """ Handles the wx.EVT_LEFT_UP events for PageContainerBase. """
2174 # forget the zone that was initially clicked
2175 self
._nLeftClickZone
= FNB_NOWHERE
2177 where
, tabIdx
= self
.HitTest(event
.GetPosition())
2179 if where
== FNB_LEFT_ARROW
:
2181 if self
._nFrom
== 0:
2184 # Make sure that the button was pressed before
2185 if self
._nLeftButtonStatus
!= FNB_BTN_PRESSED
:
2188 self
._nLeftButtonStatus
= FNB_BTN_HOVER
2190 # We scroll left with bulks of 5
2191 scrollLeft
= self
.GetNumTabsCanScrollLeft()
2193 self
._nFrom
-= scrollLeft
2199 elif where
== FNB_RIGHT_ARROW
:
2201 if self
._nFrom
>= len(self
._pagesInfoVec
) - 1:
2204 # Make sure that the button was pressed before
2205 if self
._nRightButtonStatus
!= FNB_BTN_PRESSED
:
2208 self
._nRightButtonStatus
= FNB_BTN_HOVER
2210 # Check if the right most tab is visible, if it is
2211 # don't rotate right anymore
2212 if self
._pagesInfoVec
[-1].GetPosition() != wx
.Point(-1, -1):
2215 lastVisibleTab
= self
.GetLastVisibleTab()
2216 if lastVisibleTab
< 0:
2217 # Probably the screen is too small for displaying even a single
2218 # tab, in this case we do nothing
2221 self
._nFrom
+= self
.GetNumOfVisibleTabs()
2224 elif where
== FNB_X
:
2226 # Make sure that the button was pressed before
2227 if self
._nXButtonStatus
!= FNB_BTN_PRESSED
:
2230 self
._nXButtonStatus
= FNB_BTN_HOVER
2232 self
.DeletePage(self
._iActivePage
)
2234 elif where
== FNB_TAB_X
:
2236 # Make sure that the button was pressed before
2237 if self
._nTabXButtonStatus
!= FNB_BTN_PRESSED
:
2240 self
._nTabXButtonStatus
= FNB_BTN_HOVER
2242 self
.DeletePage(self
._iActivePage
)
2245 def HitTest(self
, pt
):
2247 HitTest method for PageContainerBase.
2248 Returns the flag (if any) and the hit page (if any).
2251 fullrect
= self
.GetClientRect()
2252 btnLeftPos
= self
.GetLeftButtonPos()
2253 btnRightPos
= self
.GetRightButtonPos()
2254 btnXPos
= self
.GetXPos()
2255 style
= self
.GetParent().GetWindowStyleFlag()
2259 if not self
._pagesInfoVec
:
2260 return FNB_NOWHERE
, -1
2262 rect
= wx
.Rect(btnXPos
, 6, 16, 16)
2263 if rect
.Contains(pt
):
2264 return (style
& FNB_NO_X_BUTTON
and [FNB_NOWHERE
] or [FNB_X
])[0], -1
2266 rect
= wx
.Rect(btnRightPos
, 6, 16, 16)
2267 if rect
.Contains(pt
):
2268 return (style
& FNB_NO_NAV_BUTTONS
and [FNB_NOWHERE
] or [FNB_RIGHT_ARROW
])[0], -1
2270 rect
= wx
.Rect(btnLeftPos
, 6, 16, 16)
2271 if rect
.Contains(pt
):
2272 return (style
& FNB_NO_NAV_BUTTONS
and [FNB_NOWHERE
] or [FNB_LEFT_ARROW
])[0], -1
2274 # Test whether a left click was made on a tab
2275 for cur
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
2277 pgInfo
= self
._pagesInfoVec
[cur
]
2279 if pgInfo
.GetPosition() == wx
.Point(-1, -1):
2282 if style
& FNB_X_ON_TAB
and cur
== self
.GetSelection():
2283 # 'x' button exists on a tab
2284 if self
._pagesInfoVec
[cur
].GetXRect().Contains(pt
):
2285 return FNB_TAB_X
, cur
2287 tabRect
= wx
.Rect(pgInfo
.GetPosition().x
, pgInfo
.GetPosition().y
, pgInfo
.GetSize().x
, pgInfo
.GetSize().y
)
2288 if tabRect
.Contains(pt
):
2292 if self
._isdragging
:
2293 # We are doing DND, so check also the region outside the tabs
2295 # try before the first tab
2296 pgInfo
= self
._pagesInfoVec
[0]
2297 tabRect
= wx
.Rect(0, pgInfo
.GetPosition().y
, pgInfo
.GetPosition().x
, self
.GetParent().GetSize().y
)
2298 if tabRect
.Contains(pt
):
2301 # try after the last tab
2302 pgInfo
= self
._pagesInfoVec
[-1]
2303 startpos
= pgInfo
.GetPosition().x
+pgInfo
.GetSize().x
2304 tabRect
= wx
.Rect(startpos
, pgInfo
.GetPosition().y
, fullrect
.width
-startpos
, self
.GetParent().GetSize().y
)
2306 if tabRect
.Contains(pt
):
2307 return FNB_TAB
, len(self
._pagesInfoVec
)-1
2310 return FNB_NOWHERE
, -1
2313 def SetSelection(self
, page
):
2314 """ Sets the selected page. """
2316 book
= self
.GetParent()
2317 book
.SetSelection(page
)
2318 self
.DoSetSelection(page
)
2321 def DoSetSelection(self
, page
):
2322 """ Does the actual selection of a page. """
2324 # Make sure that the selection is visible
2325 style
= self
.GetParent().GetWindowStyleFlag()
2326 if style
& FNB_NO_NAV_BUTTONS
:
2327 # Incase that we dont have navigation buttons,
2328 # there is no point of checking if the tab is visible
2329 # Just do the refresh
2333 if page
< len(self
._pagesInfoVec
):
2335 da_page
= self
._pParent
.GetPage(page
)
2337 # THIS IS GIVING TROUBLES!!
2341 if not self
.IsTabVisible(page
):
2343 if page
== len(self
._pagesInfoVec
) - 1:
2344 # Incase the added tab is last,
2345 # the function IsTabVisible() will always return False
2346 # and thus will cause an evil behaviour that the new
2347 # tab will hide all other tabs, we need to check if the
2348 # new selected tab can fit to the current screen
2349 if not self
.CanFitToScreen(page
):
2354 if not self
.CanFitToScreen(page
):
2355 # Redraw the tabs starting from page
2361 def DeletePage(self
, page
):
2362 """ Delete the specified page from FlatNotebook. """
2364 book
= self
.GetParent()
2365 book
.DeletePage(page
)
2369 def IsTabVisible(self
, page
):
2370 """ Returns whether a tab is visible or not. """
2372 iLastVisiblePage
= self
.GetLastVisibleTab()
2373 return page
<= iLastVisiblePage
and page
>= self
._nFrom
2376 def DoDeletePage(self
, page
):
2377 """ Does the actual page deletion. """
2379 # Remove the page from the vector
2380 book
= self
.GetParent()
2381 self
._pagesInfoVec
.pop(page
)
2383 # Thanks to Yiaanis AKA Mandrav
2384 if self
._iActivePage
>= page
:
2385 self
._iActivePage
= self
._iActivePage
- 1
2387 # The delete page was the last first on the array,
2388 # but the book still has more pages, so we set the
2389 # active page to be the first one (0)
2390 if self
._iActivePage
< 0 and self
._pagesInfoVec
:
2391 self
._iActivePage
= 0
2394 if self
._iActivePage
>= 0:
2396 book
._bForceSelection
= True
2397 book
.SetSelection(self
._iActivePage
)
2398 book
._bForceSelection
= False
2400 if not self
._pagesInfoVec
:
2401 # Erase the page container drawings
2402 dc
= wx
.ClientDC(self
)
2406 def DeleteAllPages(self
):
2407 """ Deletes all the pages. """
2409 self
._iActivePage
= -1
2411 self
._pagesInfoVec
= []
2413 # Erase the page container drawings
2414 dc
= wx
.ClientDC(self
)
2418 def DrawTabX(self
, dc
, rect
, tabIdx
):
2419 """ Draws the 'X' in the selected tab (VC8 style excluded). """
2421 if not self
.HasFlag(FNB_X_ON_TAB
) or not self
.CanDrawXOnTab():
2424 # We draw the 'x' on the active tab only
2425 if tabIdx
!= self
.GetSelection() or tabIdx
< 0 or not self
.CanFitToScreen(tabIdx
):
2428 # Set the bitmap according to the button status
2429 if self
._nTabXButtonStatus
== FNB_BTN_HOVER
:
2430 xBmp
= wx
.BitmapFromXPMData(x_button_hilite_xpm
)
2431 elif self
._nTabXButtonStatus
== FNB_BTN_PRESSED
:
2432 xBmp
= wx
.BitmapFromXPMData(x_button_pressed_xpm
)
2434 xBmp
= wx
.BitmapFromXPMData(x_button_xpm
)
2437 xBmp
.SetMask(wx
.Mask(xBmp
, MASK_COLOR
))
2440 dc
.DrawBitmap(self
._tabXBgBmp
, rect
.x
, rect
.y
)
2442 # Draw the new bitmap
2443 dc
.DrawBitmap(xBmp
, rect
.x
, rect
.y
, True)
2446 self
._pagesInfoVec
[tabIdx
].SetXRect(rect
)
2449 def DrawLeftArrow(self
, dc
):
2450 """ Draw the left navigation arrow. """
2452 style
= self
.GetParent().GetWindowStyleFlag()
2453 if style
& FNB_NO_NAV_BUTTONS
:
2456 # Make sure that there are pages in the container
2457 if not self
._pagesInfoVec
:
2460 # Set the bitmap according to the button status
2461 if self
._nLeftButtonStatus
== FNB_BTN_HOVER
:
2462 arrowBmp
= wx
.BitmapFromXPMData(left_arrow_hilite_xpm
)
2463 elif self
._nLeftButtonStatus
== FNB_BTN_PRESSED
:
2464 arrowBmp
= wx
.BitmapFromXPMData(left_arrow_pressed_xpm
)
2466 arrowBmp
= wx
.BitmapFromXPMData(left_arrow_xpm
)
2468 if self
._nFrom
== 0:
2469 # Handle disabled arrow
2470 arrowBmp
= wx
.BitmapFromXPMData(left_arrow_disabled_xpm
)
2472 arrowBmp
.SetMask(wx
.Mask(arrowBmp
, MASK_COLOR
))
2475 posx
= self
.GetLeftButtonPos()
2476 dc
.DrawBitmap(self
._leftBgBmp
, posx
, 6)
2478 # Draw the new bitmap
2479 dc
.DrawBitmap(arrowBmp
, posx
, 6, True)
2482 def DrawRightArrow(self
, dc
):
2483 """ Draw the right navigation arrow. """
2485 style
= self
.GetParent().GetWindowStyleFlag()
2486 if style
& FNB_NO_NAV_BUTTONS
:
2489 # Make sure that there are pages in the container
2490 if not self
._pagesInfoVec
:
2493 # Set the bitmap according to the button status
2494 if self
._nRightButtonStatus
== FNB_BTN_HOVER
:
2495 arrowBmp
= wx
.BitmapFromXPMData(right_arrow_hilite_xpm
)
2496 elif self
._nRightButtonStatus
== FNB_BTN_PRESSED
:
2497 arrowBmp
= wx
.BitmapFromXPMData(right_arrow_pressed_xpm
)
2499 arrowBmp
= wx
.BitmapFromXPMData(right_arrow_xpm
)
2501 # Check if the right most tab is visible, if it is
2502 # don't rotate right anymore
2503 if self
._pagesInfoVec
[-1].GetPosition() != wx
.Point(-1, -1):
2504 arrowBmp
= wx
.BitmapFromXPMData(right_arrow_disabled_xpm
)
2506 arrowBmp
.SetMask(wx
.Mask(arrowBmp
, MASK_COLOR
))
2509 posx
= self
.GetRightButtonPos()
2510 dc
.DrawBitmap(self
._rightBgBmp
, posx
, 6)
2512 # Draw the new bitmap
2513 dc
.DrawBitmap(arrowBmp
, posx
, 6, True)
2516 def DrawX(self
, dc
):
2517 """ Draw the 'X' navigation button in the navigation area. """
2519 # Check if this style is enabled
2520 style
= self
.GetParent().GetWindowStyleFlag()
2521 if style
& FNB_NO_X_BUTTON
:
2524 # Make sure that there are pages in the container
2525 if not self
._pagesInfoVec
:
2528 # Set the bitmap according to the button status
2529 if self
._nXButtonStatus
== FNB_BTN_HOVER
:
2530 xbmp
= wx
.BitmapFromXPMData(x_button_hilite_xpm
)
2531 elif self
._nXButtonStatus
== FNB_BTN_PRESSED
:
2532 xbmp
= wx
.BitmapFromXPMData(x_button_pressed_xpm
)
2534 xbmp
= wx
.BitmapFromXPMData(x_button_xpm
)
2536 xbmp
.SetMask(wx
.Mask(xbmp
, MASK_COLOR
))
2539 posx
= self
.GetXPos()
2540 dc
.DrawBitmap(self
._xBgBmp
, posx
, 6)
2542 # Draw the new bitmap
2543 dc
.DrawBitmap(xbmp
, posx
, 6, True)
2546 def OnMouseMove(self
, event
):
2547 """ Handles the wx.EVT_MOTION for PageContainerBase. """
2549 if self
._pagesInfoVec
and self
.IsShown():
2551 xButtonStatus
= self
._nXButtonStatus
2552 xTabButtonStatus
= self
._nTabXButtonStatus
2553 rightButtonStatus
= self
._nRightButtonStatus
2554 leftButtonStatus
= self
._nLeftButtonStatus
2555 style
= self
.GetParent().GetWindowStyleFlag()
2557 self
._nXButtonStatus
= FNB_BTN_NONE
2558 self
._nRightButtonStatus
= FNB_BTN_NONE
2559 self
._nLeftButtonStatus
= FNB_BTN_NONE
2560 self
._nTabXButtonStatus
= FNB_BTN_NONE
2562 where
, tabIdx
= self
.HitTest(event
.GetPosition())
2565 if event
.LeftIsDown():
2567 self
._nXButtonStatus
= (self
._nLeftClickZone
==FNB_X
and [FNB_BTN_PRESSED
] or [FNB_BTN_NONE
])[0]
2571 self
._nXButtonStatus
= FNB_BTN_HOVER
2573 elif where
== FNB_TAB_X
:
2574 if event
.LeftIsDown():
2576 self
._nTabXButtonStatus
= (self
._nLeftClickZone
==FNB_TAB_X
and [FNB_BTN_PRESSED
] or [FNB_BTN_NONE
])[0]
2580 self
._nTabXButtonStatus
= FNB_BTN_HOVER
2582 elif where
== FNB_RIGHT_ARROW
:
2583 if event
.LeftIsDown():
2585 self
._nRightButtonStatus
= (self
._nLeftClickZone
==FNB_RIGHT_ARROW
and [FNB_BTN_PRESSED
] or [FNB_BTN_NONE
])[0]
2589 self
._nRightButtonStatus
= FNB_BTN_HOVER
2591 elif where
== FNB_LEFT_ARROW
:
2592 if event
.LeftIsDown():
2594 self
._nLeftButtonStatus
= (self
._nLeftClickZone
==FNB_LEFT_ARROW
and [FNB_BTN_PRESSED
] or [FNB_BTN_NONE
])[0]
2598 self
._nLeftButtonStatus
= FNB_BTN_HOVER
2600 elif where
== FNB_TAB
:
2601 # Call virtual method for showing tooltip
2602 self
.ShowTabTooltip(tabIdx
)
2604 if not self
.GetEnabled(tabIdx
):
2605 # Set the cursor to be 'No-entry'
2606 wx
.SetCursor(wx
.StockCursor(wx
.CURSOR_NO_ENTRY
))
2608 # Support for drag and drop
2609 if event
.LeftIsDown() and not (style
& FNB_NODRAG
):
2611 self
._isdragging
= True
2612 draginfo
= FNBDragInfo(self
, tabIdx
)
2613 drginfo
= cPickle
.dumps(draginfo
)
2614 dataobject
= wx
.CustomDataObject(wx
.CustomDataFormat("FlatNotebook"))
2615 dataobject
.SetData(drginfo
)
2616 dragSource
= wx
.DropSource(self
)
2617 dragSource
.SetData(dataobject
)
2618 dragSource
.DoDragDrop(wx
.Drag_DefaultMove
)
2620 bRedrawX
= self
._nXButtonStatus
!= xButtonStatus
2621 bRedrawRight
= self
._nRightButtonStatus
!= rightButtonStatus
2622 bRedrawLeft
= self
._nLeftButtonStatus
!= leftButtonStatus
2623 bRedrawTabX
= self
._nTabXButtonStatus
!= xTabButtonStatus
2625 if (bRedrawX
or bRedrawRight
or bRedrawLeft
or bRedrawTabX
):
2627 dc
= wx
.ClientDC(self
)
2634 self
.DrawLeftArrow(dc
)
2638 self
.DrawRightArrow(dc
)
2642 self
.DrawTabX(dc
, self
._pagesInfoVec
[tabIdx
].GetXRect(), tabIdx
)
2647 def GetLastVisibleTab(self
):
2648 """ Returns the last visible tab. """
2652 for ii
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
2654 if self
._pagesInfoVec
[ii
].GetPosition() == wx
.Point(-1, -1):
2660 def GetNumTabsCanScrollLeft(self
):
2661 """ Returns the number of tabs than can be scrolled left. """
2663 # Reserved area for the buttons (<>x)
2664 rect
= self
.GetClientRect()
2665 clientWidth
= rect
.width
2666 posx
= self
._pParent
._nPadding
2670 dc
= wx
.ClientDC(self
)
2672 # In case we have error prevent crash
2676 style
= self
.GetParent().GetWindowStyleFlag()
2678 for ii
in xrange(self
._nFrom
, -1, -1):
2680 boldFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
2681 boldFont
.SetWeight(wx
.FONTWEIGHT_BOLD
)
2682 dc
.SetFont(boldFont
)
2684 width
, height
= dc
.GetTextExtent("Tp")
2686 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 6 pixels as padding
2687 if style
& FNB_VC71
:
2688 tabHeight
= (style
& FNB_BOTTOM
and [tabHeight
- 4] or [tabHeight
])[0]
2689 elif style
& FNB_FANCY_TABS
:
2690 tabHeight
= (style
& FNB_BOTTOM
and [tabHeight
- 3] or [tabHeight
])[0]
2692 width
, pom
= dc
.GetTextExtent(self
.GetPageText(ii
))
2693 if style
!= FNB_VC71
:
2694 shapePoints
= int(tabHeight
*math
.tan(float(self
._pagesInfoVec
[ii
].GetTabAngle())/180.0*math
.pi
))
2698 tabWidth
= self
._pParent
._nPadding
*2 + width
2700 if not (style
& FNB_VC71
):
2702 tabWidth
+= 2*shapePoints
2704 hasImage
= self
._ImageList
!= None and self
._pagesInfoVec
[ii
].GetImageIndex() != -1
2706 # For VC71 style, we only add the icon size (16 pixels)
2709 if not self
.IsDefaultTabs():
2710 tabWidth
+= 16 + self
._pParent
._nPadding
2713 tabWidth
+= 16 + self
._pParent
._nPadding
+ shapePoints
/2
2715 if posx
+ tabWidth
+ self
.GetButtonsAreaLength() >= clientWidth
:
2718 numTabs
= numTabs
+ 1
2724 def IsDefaultTabs(self
):
2725 """ Returns whether a tab has a default style. """
2727 style
= self
.GetParent().GetWindowStyleFlag()
2728 res
= (style
& FNB_VC71
) or (style
& FNB_FANCY_TABS
)
2732 def AdvanceSelection(self
, bForward
=True):
2734 Cycles through the tabs.
2735 The call to this function generates the page changing events.
2738 nSel
= self
.GetSelection()
2743 nMax
= self
.GetPageCount() - 1
2745 self
.SetSelection((nSel
== nMax
and [0] or [nSel
+ 1])[0])
2747 self
.SetSelection((nSel
== 0 and [nMax
] or [nSel
- 1])[0])
2750 def OnMouseLeave(self
, event
):
2751 """ Handles the wx.EVT_LEAVE_WINDOW event for PageContainerBase. """
2753 self
._nLeftButtonStatus
= FNB_BTN_NONE
2754 self
._nXButtonStatus
= FNB_BTN_NONE
2755 self
._nRightButtonStatus
= FNB_BTN_NONE
2756 self
._nTabXButtonStatus
= FNB_BTN_NONE
2758 dc
= wx
.ClientDC(self
)
2760 self
.DrawLeftArrow(dc
)
2761 self
.DrawRightArrow(dc
)
2763 selection
= self
.GetSelection()
2766 self
.DrawTabX(dc
, self
._pagesInfoVec
[selection
].GetXRect(), selection
)
2771 def OnMouseEnterWindow(self
, event
):
2772 """ Handles the wx.EVT_ENTER_WINDOW event for PageContainerBase. """
2774 self
._nLeftButtonStatus
= FNB_BTN_NONE
2775 self
._nXButtonStatus
= FNB_BTN_NONE
2776 self
._nRightButtonStatus
= FNB_BTN_NONE
2777 self
._nLeftClickZone
= FNB_BTN_NONE
2782 def ShowTabTooltip(self
, tabIdx
):
2783 """ Shows a tab tooltip. """
2785 pWindow
= self
._pParent
.GetPage(tabIdx
)
2788 pToolTip
= pWindow
.GetToolTip()
2789 if pToolTip
and pToolTip
.GetWindow() == pWindow
:
2790 self
.SetToolTipString(pToolTip
.GetTip())
2793 def FillGradientColor(self
, dc
, rect
):
2794 """ Gradient fill from colour 1 to colour 2 with top to bottom. """
2796 if rect
.height
< 1 or rect
.width
< 1:
2801 # calculate gradient coefficients
2802 style
= self
.GetParent().GetWindowStyleFlag()
2803 col2
= ((style
& FNB_BOTTOM
) and [self
._colorTo
] or [self
._colorFrom
])[0]
2804 col1
= ((style
& FNB_BOTTOM
) and [self
._colorFrom
] or [self
._colorTo
])[0]
2806 rf
, gf
, bf
= 0, 0, 0
2807 rstep
= float(col2
.Red() - col1
.Red())/float(size
)
2808 gstep
= float(col2
.Green() - col1
.Green())/float(size
)
2809 bstep
= float(col2
.Blue() - col1
.Blue())/float(size
)
2811 for y
in xrange(rect
.y
, rect
.y
+ size
):
2812 currCol
= wx
.Colour(col1
.Red() + rf
, col1
.Green() + gf
, col1
.Blue() + bf
)
2813 dc
.SetBrush(wx
.Brush(currCol
))
2814 dc
.SetPen(wx
.Pen(currCol
))
2815 dc
.DrawLine(rect
.x
, y
, rect
.x
+ rect
.width
, y
)
2821 def SetPageImageIndex(self
, page
, imgindex
):
2822 """ Sets the image index associated to a page. """
2824 if page
< len(self
._pagesInfoVec
):
2826 self
._pagesInfoVec
[page
].SetImageIndex(imgindex
)
2830 def GetPageImageIndex(self
, page
):
2831 """ Returns the image index associated to a page. """
2833 if page
< len(self
._pagesInfoVec
):
2835 return self
._pagesInfoVec
[page
].GetImageIndex()
2840 def OnDropTarget(self
, x
, y
, nTabPage
, wnd_oldContainer
):
2841 """ Handles the drop action from a DND operation. """
2843 # Disable drag'n'drop for disabled tab
2844 if not wnd_oldContainer
._pagesInfoVec
[nTabPage
].GetEnabled():
2845 return wx
.DragCancel
2847 self
._isdragging
= True
2848 oldContainer
= wnd_oldContainer
2851 where
, nIndex
= self
.HitTest(wx
.Point(x
, y
))
2853 oldNotebook
= oldContainer
.GetParent()
2854 newNotebook
= self
.GetParent()
2856 if oldNotebook
== newNotebook
:
2860 if where
== FNB_TAB
:
2861 self
.MoveTabPage(nTabPage
, nIndex
)
2865 if wx
.Platform
in ["__WXMSW__", "__WXGTK__"]:
2868 window
= oldNotebook
.GetPage(nTabPage
)
2871 where
, nIndex
= newNotebook
._pages
.HitTest(wx
.Point(x
, y
))
2872 caption
= oldContainer
.GetPageText(nTabPage
)
2873 imageindex
= oldContainer
.GetPageImageIndex(nTabPage
)
2874 oldNotebook
.RemovePage(nTabPage
)
2875 window
.Reparent(newNotebook
)
2877 newNotebook
.InsertPage(nIndex
, window
, caption
, True, imageindex
)
2879 self
._isdragging
= False
2884 def MoveTabPage(self
, nMove
, nMoveTo
):
2885 """ Moves a tab inside the same FlatNotebook. """
2887 if nMove
== nMoveTo
:
2890 elif nMoveTo
< len(self
._pParent
._windows
):
2891 nMoveTo
= nMoveTo
+ 1
2893 self
._pParent
.Freeze()
2895 # Remove the window from the main sizer
2896 nCurSel
= self
._pParent
._pages
.GetSelection()
2897 self
._pParent
._mainSizer
.Detach(self
._pParent
._windows
[nCurSel
])
2898 self
._pParent
._windows
[nCurSel
].Hide()
2900 pWindow
= self
._pParent
._windows
[nMove
]
2901 self
._pParent
._windows
.pop(nMove
)
2902 self
._pParent
._windows
.insert(nMoveTo
-1, pWindow
)
2904 pgInfo
= self
._pagesInfoVec
[nMove
]
2906 self
._pagesInfoVec
.pop(nMove
)
2907 self
._pagesInfoVec
.insert(nMoveTo
- 1, pgInfo
)
2909 # Add the page according to the style
2910 pSizer
= self
._pParent
._mainSizer
2911 style
= self
.GetParent().GetWindowStyleFlag()
2913 if style
& FNB_BOTTOM
:
2915 pSizer
.Insert(0, pWindow
, 1, wx
.EXPAND
)
2919 # We leave a space of 1 pixel around the window
2920 pSizer
.Add(pWindow
, 1, wx
.EXPAND
)
2925 self
._iActivePage
= nMoveTo
- 1
2926 self
.DoSetSelection(self
._iActivePage
)
2928 self
._pParent
.Thaw()
2931 def CanFitToScreen(self
, page
):
2932 """ Returns whether all the tabs can fit in the available space. """
2934 # Incase the from is greater than page,
2935 # we need to reset the self._nFrom, so in order
2936 # to force the caller to do so, we return False
2937 if self
._nFrom
> page
:
2940 # Calculate the tab width including borders and image if any
2941 dc
= wx
.ClientDC(self
)
2943 style
= self
.GetParent().GetWindowStyleFlag()
2945 width
, height
= dc
.GetTextExtent("Tp")
2946 width
, pom
= dc
.GetTextExtent(self
.GetPageText(page
))
2948 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 6 pixels as padding
2950 if style
& FNB_VC71
:
2951 tabHeight
= (style
& FNB_BOTTOM
and [tabHeight
- 4] or [tabHeight
])[0]
2952 elif style
& FNB_FANCY_TABS
:
2953 tabHeight
= (style
& FNB_BOTTOM
and [tabHeight
- 2] or [tabHeight
])[0]
2955 tabWidth
= self
._pParent
._nPadding
* 2 + width
2957 if not style
& FNB_VC71
:
2958 shapePoints
= int(tabHeight
*math
.tan(float(self
._pagesInfoVec
[page
].GetTabAngle())/180.0*math
.pi
))
2962 if not style
& FNB_VC71
:
2964 tabWidth
+= 2*shapePoints
2966 hasImage
= self
._ImageList
!= None
2969 hasImage
&= self
._pagesInfoVec
[page
].GetImageIndex() != -1
2971 # For VC71 style, we only add the icon size (16 pixels)
2972 if hasImage
and (style
& FNB_VC71
or style
& FNB_FANCY_TABS
):
2976 tabWidth
+= 16 + shapePoints
/2
2978 # Check if we can draw more
2979 posx
= self
._pParent
._nPadding
2981 if self
._nFrom
>= 0:
2982 for ii
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
2983 if self
._pagesInfoVec
[ii
].GetPosition() == wx
.Point(-1, -1):
2985 posx
+= self
._pagesInfoVec
[ii
].GetSize().x
2987 rect
= self
.GetClientRect()
2988 clientWidth
= rect
.width
2990 if posx
+ tabWidth
+ self
.GetButtonsAreaLength() >= clientWidth
:
2996 def GetNumOfVisibleTabs(self
):
2997 """ Returns the number of visible tabs. """
3000 for ii
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
3001 if self
._pagesInfoVec
[ii
].GetPosition() == wx
.Point(-1, -1):
3008 def GetEnabled(self
, page
):
3009 """ Returns whether a tab is enabled or not. """
3011 if page
>= len(self
._pagesInfoVec
):
3012 return True # Seems strange, but this is the default
3014 return self
._pagesInfoVec
[page
].GetEnabled()
3017 def Enable(self
, page
, enabled
=True):
3018 """ Enables or disables a tab. """
3020 if page
>= len(self
._pagesInfoVec
):
3023 self
._pagesInfoVec
[page
].Enable(enabled
)
3026 def GetLeftButtonPos(self
):
3027 """ Returns the left button position in the navigation area. """
3029 style
= self
.GetParent().GetWindowStyleFlag()
3030 rect
= self
.GetClientRect()
3031 clientWidth
= rect
.width
3033 if style
& FNB_NO_X_BUTTON
:
3034 return clientWidth
- 38
3036 return clientWidth
- 54
3039 def GetRightButtonPos(self
):
3040 """ Returns the right button position in the navigation area. """
3042 style
= self
.GetParent().GetWindowStyleFlag()
3043 rect
= self
.GetClientRect()
3044 clientWidth
= rect
.width
3046 if style
& FNB_NO_X_BUTTON
:
3047 return clientWidth
- 22
3049 return clientWidth
- 38
3053 """ Returns the 'X' button position in the navigation area. """
3055 style
= self
.GetParent().GetWindowStyleFlag()
3056 rect
= self
.GetClientRect()
3057 clientWidth
= rect
.width
3059 if style
& FNB_NO_X_BUTTON
:
3062 return clientWidth
- 22
3065 def GetButtonsAreaLength(self
):
3066 """ Returns the navigation area width. """
3068 style
= self
.GetParent().GetWindowStyleFlag()
3070 if style
& FNB_NO_NAV_BUTTONS
and style
& FNB_NO_X_BUTTON
:
3072 elif style
& FNB_NO_NAV_BUTTONS
and not style
& FNB_NO_X_BUTTON
:
3074 elif not style
& FNB_NO_NAV_BUTTONS
and style
& FNB_NO_X_BUTTON
:
3081 def GetSingleLineBorderColor(self
):
3083 if self
.HasFlag(FNB_FANCY_TABS
):
3084 return self
._colorFrom
3089 def DrawTabsLine(self
, dc
):
3090 """ Draws a line over the tabs. """
3092 clntRect
= self
.GetClientRect()
3093 clientRect3
= wx
.Rect(0, 0, clntRect
.width
, clntRect
.height
)
3095 if self
.HasFlag(FNB_BOTTOM
):
3097 clientRect
= wx
.Rect(0, 2, clntRect
.width
, clntRect
.height
- 2)
3098 clientRect2
= wx
.Rect(0, 1, clntRect
.width
, clntRect
.height
- 1)
3102 clientRect
= wx
.Rect(0, 0, clntRect
.width
, clntRect
.height
- 2)
3103 clientRect2
= wx
.Rect(0, 0, clntRect
.width
, clntRect
.height
- 1)
3105 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
3106 dc
.SetPen(wx
.Pen(self
.GetSingleLineBorderColor()))
3107 dc
.DrawRectangleRect(clientRect2
)
3108 dc
.DrawRectangleRect(clientRect3
)
3110 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)))
3111 dc
.DrawRectangleRect(clientRect
)
3113 if not self
.HasFlag(FNB_TABS_BORDER_SIMPLE
):
3115 dc
.SetPen(wx
.Pen((self
.HasFlag(FNB_VC71
) and [wx
.Colour(247, 243, 233)] or [self
._tabAreaColor
])[0]))
3116 dc
.DrawLine(0, 0, 0, clientRect
.height
+1)
3118 if self
.HasFlag(FNB_BOTTOM
):
3120 dc
.DrawLine(0, clientRect
.height
+1, clientRect
.width
, clientRect
.height
+1)
3123 dc
.DrawLine(0, 0, clientRect
.width
, 0)
3125 dc
.DrawLine(clientRect
.width
- 1, 0, clientRect
.width
- 1, clientRect
.height
+1)
3128 def HasFlag(self
, flag
):
3129 """ Returns whether a flag is present in the FlatNotebook style. """
3131 style
= self
.GetParent().GetWindowStyleFlag()
3132 res
= (style
& flag
and [True] or [False])[0]
3136 def ClearFlag(self
, flag
):
3137 """ Deletes a flag from the FlatNotebook style. """
3139 style
= self
.GetParent().GetWindowStyleFlag()
3141 self
.SetWindowStyleFlag(style
)
3144 def TabHasImage(self
, tabIdx
):
3145 """ Returns whether a tab has an associated image index or not. """
3148 return self
._pagesInfoVec
[tabIdx
].GetImageIndex() != -1
3153 def OnLeftDClick(self
, event
):
3154 """ Handles the wx.EVT_LEFT_DCLICK event for PageContainerBase. """
3156 if self
.HasFlag(FNB_DCLICK_CLOSES_TABS
):
3158 where
, tabIdx
= self
.HitTest(event
.GetPosition())
3160 if where
== FNB_TAB
:
3161 self
.DeletePage(tabIdx
)
3168 def SetImageList(self
, imglist
):
3169 """ Sets the image list for the page control. """
3171 self
._ImageList
= imglist
3174 def GetImageList(self
):
3175 """ Returns the image list for the page control. """
3177 return self
._ImageList
3180 def GetSelection(self
):
3181 """ Returns the current selected page. """
3183 return self
._iActivePage
3186 def GetPageCount(self
):
3187 """ Returns the number of tabs in the FlatNotebook control. """
3189 return len(self
._pagesInfoVec
)
3192 def GetPageText(self
, page
):
3193 """ Returns the tab caption of the page. """
3195 return self
._pagesInfoVec
[page
].GetCaption()
3198 def SetPageText(self
, page
, text
):
3199 """ Sets the tab caption of the page. """
3201 self
._pagesInfoVec
[page
].SetCaption(text
)
3205 def CanDrawXOnTab(self
):
3206 """ Returns whether an 'X' can be drawn on a tab (all styles except VC8. """
3211 # ---------------------------------------------------------------------------- #
3212 # Class FlatNotebook
3213 # Simple super class based on PageContainerBase
3214 # ---------------------------------------------------------------------------- #
3216 class FlatNotebook(FlatNotebookBase
):
3218 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
3219 style
=0, name
="FlatNotebook"):
3221 Default class constructor.
3223 It is better to use directly the StyledNotebook class (see below) and then
3224 assigning the style you wish instead of calling FlatNotebook.
3227 style |
= wx
.TAB_TRAVERSAL
3229 FlatNotebookBase
.__init
__(self
, parent
, id, pos
, size
, style
, name
)
3230 self
._pages
= self
.CreatePageContainer()
3233 def CreatePageContainer(self
):
3234 """ Creates the page container. """
3236 return FlatNotebookBase
.CreatePageContainer(self
)
3239 #--------------------------------------------------------------------
3240 # StyledNotebook - a notebook with look n feel of Visual Studio 2005
3241 #--------------------------------------------------------------------
3243 class StyledNotebook(FlatNotebookBase
):
3245 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
3246 style
=0, name
="StyledNotebook"):
3247 """ Default class constructor.
3249 It is better to use directly the StyledNotebook class and then
3250 assigning the style you wish instead of calling FlatNotebook.
3253 style |
= wx
.TAB_TRAVERSAL
3255 FlatNotebookBase
.__init
__(self
, parent
, id, pos
, size
, style
, name
)
3257 # Custom initialization of the tab area
3259 # Initialise the default style colors
3260 self
.SetNonActiveTabTextColour(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNTEXT
))
3263 def CreatePageContainer(self
):
3264 """ Creates the page container. """
3266 return StyledTabsContainer(self
, wx
.ID_ANY
)
3269 # ---------------------------------------------------------------------------- #
3270 # Class StyledTabsContainer
3271 # Acts as a container for the pages you add to FlatNotebook
3272 # A more generic and more powerful implementation of PageContainerBase, can
3273 # handle also VC8 tabs style
3274 # ---------------------------------------------------------------------------- #
3276 class StyledTabsContainer(PageContainerBase
):
3278 def __init__(self
, parent
, id=wx
.ID_ANY
, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
3280 """ Default class constructor. """
3284 self
._colorTo
= LightColour(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
), 0)
3285 self
._colorFrom
= LightColour(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
), 60)
3287 PageContainerBase
.__init
__(self
, parent
, id, pos
, size
, style
)
3289 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
3292 def NumberTabsCanFit(self
, dc
):
3293 """ Returns the number of tabs that can fit inside the available space. """
3295 rect
= self
.GetClientRect()
3296 clientWidth
= rect
.width
3301 # We take the maxmimum font size, this is
3302 # achieved by setting the font to be bold
3303 font
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
3304 font
.SetWeight(wx
.FONTWEIGHT_BOLD
)
3307 width
, height
= dc
.GetTextExtent("Tp")
3309 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 8 pixels
3310 # The drawing starts from posx
3311 posx
= self
._pParent
.GetPadding()
3313 for i
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
3315 width
, pom
= dc
.GetTextExtent(self
.GetPageText(i
))
3317 # Set a minimum size to a tab
3321 tabWidth
= self
._pParent
.GetPadding() * 2 + width
3323 # Add the image width if it exist
3324 if self
.TabHasImage(i
):
3325 tabWidth
+= 16 + self
._pParent
.GetPadding()
3327 vc8glitch
= tabHeight
+ FNB_HEIGHT_SPACER
3328 if posx
+ tabWidth
+ vc8glitch
+ self
.GetButtonsAreaLength() >= clientWidth
:
3331 # Add a result to the returned vector
3332 tabRect
= wx
.Rect(posx
, VERTICAL_BORDER_PADDING
, tabWidth
, tabHeight
)
3333 vTabInfo
.append(tabRect
)
3336 posx
+= tabWidth
+ FNB_HEIGHT_SPACER
3341 def GetNumTabsCanScrollLeft(self
):
3342 """ Returns the number of tabs than can be scrolled left. """
3344 # Reserved area for the buttons (<>x)
3345 rect
= self
.GetClientRect()
3346 clientWidth
= rect
.width
3347 posx
= self
._pParent
.GetPadding()
3351 dc
= wx
.ClientDC(self
)
3353 # Incase we have error prevent crash
3357 style
= self
.GetParent().GetWindowStyleFlag()
3359 for i
in xrange(self
._nFrom
, -1, -1):
3361 boldFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
3362 boldFont
.SetWeight(wx
.FONTWEIGHT_BOLD
)
3363 dc
.SetFont(boldFont
)
3365 width
, height
= dc
.GetTextExtent("Tp")
3367 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 6 pixels as padding
3369 if style
& FNB_VC71
:
3370 tabHeight
= (self
.HasFlag(FNB_BOTTOM
) and [tabHeight
- 4] or [tabHeight
])[0]
3371 elif style
& FNB_FANCY_TABS
:
3372 tabHeight
= (self
.HasFlag(FNB_BOTTOM
) and [tabHeight
- 3] or [tabHeight
])[0]
3374 width
, pom
= dc
.GetTextExtent(self
.GetPageText(i
))
3376 if style
!= FNB_VC71
:
3377 shapePoints
= int(tabHeight
*math
.tan(float(self
._pagesInfoVec
[i
].GetTabAngle())/180.0*math
.pi
))
3381 tabWidth
= self
._pParent
.GetPadding() * 2 + width
3382 if not style
& FNB_VC71
:
3384 tabWidth
+= 2*shapePoints
3386 # For VC71 style, we only add the icon size (16 pixels)
3387 if self
.TabHasImage(i
):
3389 if not self
.IsDefaultTabs():
3390 tabWidth
+= 16 + self
._pParent
.GetPadding()
3393 tabWidth
+= 16 + self
._pParent
.GetPadding() + shapePoints
/2
3395 vc8glitch
= (style
& FNB_VC8
and [tabHeight
+ FNB_HEIGHT_SPACER
] or [0])[0]
3397 if posx
+ tabWidth
+ vc8glitch
+ self
.GetButtonsAreaLength() >= clientWidth
:
3400 numTabs
= numTabs
+ 1
3406 def CanDrawXOnTab(self
):
3407 """ Returns whether an 'X' button can be drawn on a tab (not VC8 style). """
3409 style
= self
.GetParent().GetWindowStyleFlag()
3410 isVC8
= (style
& FNB_VC8
and [True] or [False])[0]
3414 def IsDefaultTabs(self
):
3415 """ Returns whether a tab has a default style or not. """
3417 style
= self
.GetParent().GetWindowStyleFlag()
3418 res
= (style
& FNB_VC71
) or (style
& FNB_FANCY_TABS
) or (style
& FNB_VC8
)
3422 def HitTest(self
, pt
):
3423 """ HitTest specific method for VC8 style. """
3425 fullrect
= self
.GetClientRect()
3426 btnLeftPos
= self
.GetLeftButtonPos()
3427 btnRightPos
= self
.GetRightButtonPos()
3428 btnXPos
= self
.GetXPos()
3429 style
= self
.GetParent().GetWindowStyleFlag()
3432 if not self
._pagesInfoVec
:
3433 return FNB_NOWHERE
, -1
3435 rect
= wx
.Rect(btnXPos
, 8, 16, 16)
3437 if rect
.Contains(pt
):
3438 return (style
& FNB_NO_X_BUTTON
and [FNB_NOWHERE
] or [FNB_X
])[0], -1
3440 rect
= wx
.Rect(btnRightPos
, 8, 16, 16)
3442 if rect
.Contains(pt
):
3443 return (style
& FNB_NO_NAV_BUTTONS
and [FNB_NOWHERE
] or [FNB_RIGHT_ARROW
])[0], -1
3445 rect
= wx
.Rect(btnLeftPos
, 8, 16, 16)
3447 if rect
.Contains(pt
):
3448 return (style
& FNB_NO_NAV_BUTTONS
and [FNB_NOWHERE
] or [FNB_LEFT_ARROW
])[0], -1
3450 # Test whether a left click was made on a tab
3453 for cur
in xrange(self
._nFrom
, len(self
._pagesInfoVec
)):
3455 pgInfo
= self
._pagesInfoVec
[cur
]
3457 if pgInfo
.GetPosition() == wx
.Point(-1, -1):
3462 if self
._pagesInfoVec
[cur
].GetRegion().Contains(pt
.x
, pt
.y
):
3464 if bFoundMatch
or cur
== self
.GetSelection():
3473 if style
& FNB_X_ON_TAB
and cur
== self
.GetSelection():
3475 # 'x' button exists on a tab
3476 if self
._pagesInfoVec
[cur
].GetXRect().Contains(pt
):
3477 return FNB_TAB_X
, cur
3479 tabRect
= wx
.Rect(pgInfo
.GetPosition().x
, pgInfo
.GetPosition().y
, pgInfo
.GetSize().x
, pgInfo
.GetSize().y
)
3481 if tabRect
.Contains(pt
):
3485 return FNB_TAB
, tabIdx
3487 if self
._isdragging
:
3488 # We are doing DND, so check also the region outside the tabs
3489 # try before the first tab
3490 pgInfo
= self
._pagesInfoVec
[0]
3491 tabRect
= wx
.Rect(0, pgInfo
.GetPosition().y
, pgInfo
.GetPosition().x
, self
.GetParent().GetSize().y
)
3492 if tabRect
.Contains(pt
):
3495 # try after the last tab
3496 pgInfo
= self
._pagesInfoVec
[-1]
3497 startpos
= pgInfo
.GetPosition().x
+pgInfo
.GetSize().x
3498 tabRect
= wx
.Rect(startpos
, pgInfo
.GetPosition().y
, fullrect
.width
-startpos
, self
.GetParent().GetSize().y
)
3500 if tabRect
.Contains(pt
):
3501 return FNB_TAB
, len(self
._pagesInfoVec
)
3504 return FNB_NOWHERE
, -1
3507 def OnPaint(self
, event
):
3509 Handles the wx.EVT_PAINT event for StyledTabsContainer.
3510 Switches to PageContainerBase.OnPaint() method if the style is not VC8.
3513 if not self
.HasFlag(FNB_VC8
):
3515 PageContainerBase
.OnPaint(self
, event
)
3518 # Visual studio 8 style
3519 dc
= wx
.BufferedPaintDC(self
)
3521 if "__WXMAC__" in wx
.PlatformInfo
:
3522 # Works well on MSW & GTK, however this lines should be skipped on MAC
3523 if not self
._pagesInfoVec
or self
._nFrom
>= len(self
._pagesInfoVec
):
3528 # Set the font for measuring the tab height
3529 normalFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
3530 boldFont
= wx
.SystemSettings_GetFont(wx
.SYS_DEFAULT_GUI_FONT
)
3531 boldFont
.SetWeight(wx
.FONTWEIGHT_BOLD
)
3533 if "__WXGTK__" in wx
.PlatformInfo
:
3534 dc
.SetFont(boldFont
)
3536 width
, height
= dc
.GetTextExtent("Tp")
3538 tabHeight
= height
+ FNB_HEIGHT_SPACER
# We use 8 pixels as padding
3540 # Calculate the number of rows required for drawing the tabs
3541 rect
= self
.GetClientRect()
3543 # Set the maximum client size
3544 self
.SetSizeHints(self
.GetButtonsAreaLength(), tabHeight
)
3545 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
3548 backBrush
= wx
.Brush(self
._tabAreaColor
)
3549 noselBrush
= wx
.Brush(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNFACE
))
3550 selBrush
= wx
.Brush(self
._activeTabColor
)
3551 size
= self
.GetSize()
3554 dc
.SetTextBackground(self
.GetBackgroundColour())
3555 dc
.SetTextForeground(self
._activeTextColor
)
3557 # If border style is set, set the pen to be border pen
3558 if self
.HasFlag(FNB_TABS_BORDER_SIMPLE
):
3559 dc
.SetPen(borderPen
)
3561 dc
.SetPen(wx
.TRANSPARENT_PEN
)
3563 lightFactor
= (self
.HasFlag(FNB_BACKGROUND_GRADIENT
) and [70] or [0])[0]
3564 # For VC8 style, we color the tab area in gradient coloring
3565 PaintStraightGradientBox(dc
, self
.GetClientRect(), self
._tabAreaColor
, LightColour(self
._tabAreaColor
, lightFactor
))
3567 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
3568 dc
.DrawRectangle(0, 0, size
.x
, size
.y
)
3570 # Take 3 bitmaps for the background for the buttons
3572 mem_dc
= wx
.MemoryDC()
3574 #---------------------------------------
3576 #---------------------------------------
3577 rect
= wx
.Rect(self
.GetXPos(), 6, 16, 14)
3578 mem_dc
.SelectObject(self
._xBgBmp
)
3579 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
3580 mem_dc
.SelectObject(wx
.NullBitmap
)
3582 #---------------------------------------
3584 #---------------------------------------
3585 rect
= wx
.Rect(self
.GetRightButtonPos(), 6, 16, 14)
3586 mem_dc
.SelectObject(self
._rightBgBmp
)
3587 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
3588 mem_dc
.SelectObject(wx
.NullBitmap
)
3590 #---------------------------------------
3592 #---------------------------------------
3593 rect
= wx
.Rect(self
.GetLeftButtonPos(), 6, 16, 14)
3594 mem_dc
.SelectObject(self
._leftBgBmp
)
3595 mem_dc
.Blit(0, 0, rect
.width
, rect
.height
, dc
, rect
.x
, rect
.y
)
3596 mem_dc
.SelectObject(wx
.NullBitmap
)
3598 # We always draw the bottom/upper line of the tabs
3599 # regradless the style
3600 dc
.SetPen(borderPen
)
3601 self
.DrawTabsLine(dc
)
3604 dc
.SetPen(borderPen
)
3607 dc
.SetFont(boldFont
)
3610 # Update all the tabs from 0 to 'self._nFrom' to be non visible
3611 for i
in xrange(self
._nFrom
):
3613 self
._pagesInfoVec
[i
].SetPosition(wx
.Point(-1, -1))
3614 self
._pagesInfoVec
[i
].GetRegion().Clear()
3616 # Draw the visible tabs, in VC8 style, we draw them from right to left
3617 vTabsInfo
= self
.NumberTabsCanFit(dc
)
3619 for cur
in xrange(len(vTabsInfo
) - 1, -1, -1):
3621 # 'i' points to the index of the currently drawn tab
3622 # in self._pagesInfoVec vector
3623 i
= self
._nFrom
+ cur
3624 dc
.SetPen(borderPen
)
3625 dc
.SetBrush((i
==self
.GetSelection() and [selBrush
] or [noselBrush
])[0])
3627 # Calculate the text length using the bold font, so when selecting a tab
3628 # its width will not change
3629 dc
.SetFont(boldFont
)
3630 width
, pom
= dc
.GetTextExtent(self
.GetPageText(i
))
3632 # Now set the font to the correct font
3633 dc
.SetFont((i
==self
.GetSelection() and [boldFont
] or [normalFont
])[0])
3635 # Set a minimum size to a tab
3639 # Add the padding to the tab width
3641 # +-----------------------------------------------------------+
3642 # | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
3643 # +-----------------------------------------------------------+
3645 tabWidth
= self
._pParent
.GetPadding() * 2 + width
3646 imageYCoord
= (self
.HasFlag(FNB_BOTTOM
) and [6] or [8])[0]
3648 if self
.TabHasImage(i
):
3649 tabWidth
+= 16 + self
._pParent
.GetPadding()
3651 posx
= vTabsInfo
[cur
].x
3653 # By default we clean the tab region
3654 # incase we use the VC8 style which requires
3655 # the region, it will be filled by the function
3657 self
._pagesInfoVec
[i
].GetRegion().Clear()
3659 # Clean the 'x' buttn on the tab
3660 # 'Clean' rectanlge is a rectangle with width or height
3661 # with values lower than or equal to 0
3662 self
._pagesInfoVec
[i
].GetXRect().SetSize(wx
.Size(-1, -1))
3665 # Incase we are drawing the active tab
3666 # we need to redraw so it will appear on top
3668 if i
== self
.GetSelection():
3670 activeTabPosx
= posx
3674 self
.DrawVC8Tab(dc
, posx
, i
, tabWidth
, tabHeight
)
3676 # Text drawing offset from the left border of the
3678 # The width of the images are 16 pixels
3679 vc8ShapeLen
= tabHeight
3681 if self
.TabHasImage(i
):
3682 textOffset
= self
._pParent
.GetPadding() * 2 + 16 + vc8ShapeLen
3684 textOffset
= self
._pParent
.GetPadding() + vc8ShapeLen
3686 # Set the non-active text color
3687 if i
!= self
.GetSelection():
3688 dc
.SetTextForeground(self
._nonActiveTextColor
)
3690 if self
.TabHasImage(i
):
3691 imageXOffset
= textOffset
- 16 - self
._pParent
.GetPadding()
3692 self
._ImageList
.Draw(self
._pagesInfoVec
[i
].GetImageIndex(), dc
,
3693 posx
+ imageXOffset
, imageYCoord
,
3694 wx
.IMAGELIST_DRAW_TRANSPARENT
, True)
3696 dc
.DrawText(self
.GetPageText(i
), posx
+ textOffset
, imageYCoord
)
3698 textWidth
, textHeight
= dc
.GetTextExtent(self
.GetPageText(i
))
3700 # Restore the text forground
3701 dc
.SetTextForeground(self
._activeTextColor
)
3703 # Update the tab position & size
3704 self
._pagesInfoVec
[i
].SetPosition(wx
.Point(posx
, VERTICAL_BORDER_PADDING
))
3705 self
._pagesInfoVec
[i
].SetSize(wx
.Size(tabWidth
, tabHeight
))
3707 # Incase we are in VC8 style, redraw the active tab (incase it is visible)
3708 if self
.GetSelection() >= self
._nFrom
and self
.GetSelection() < self
._nFrom
+ len(vTabsInfo
):
3710 hasImage
= self
.TabHasImage(self
.GetSelection())
3712 dc
.SetFont(boldFont
)
3713 width
, pom
= dc
.GetTextExtent(self
.GetPageText(self
.GetSelection()))
3715 tabWidth
= self
._pParent
.GetPadding() * 2 + width
3718 tabWidth
+= 16 + self
._pParent
.GetPadding()
3720 # Set the active tab font, pen brush and font-color
3721 dc
.SetPen(borderPen
)
3722 dc
.SetBrush(selBrush
)
3723 dc
.SetFont(boldFont
)
3724 dc
.SetTextForeground(self
._activeTextColor
)
3725 self
.DrawVC8Tab(dc
, activeTabPosx
, self
.GetSelection(), tabWidth
, tabHeight
)
3727 # Text drawing offset from the left border of the
3729 # The width of the images are 16 pixels
3730 vc8ShapeLen
= tabHeight
- VERTICAL_BORDER_PADDING
- 2
3732 textOffset
= self
._pParent
.GetPadding() * 2 + 16 + vc8ShapeLen
3734 textOffset
= self
._pParent
.GetPadding() + vc8ShapeLen
3736 # Draw the image for the tab if any
3737 imageYCoord
= (self
.HasFlag(FNB_BOTTOM
) and [6] or [8])[0]
3740 imageXOffset
= textOffset
- 16 - self
._pParent
.GetPadding()
3741 self
._ImageList
.Draw(self
._pagesInfoVec
[self
.GetSelection()].GetImageIndex(), dc
,
3742 activeTabPosx
+ imageXOffset
, imageYCoord
,
3743 wx
.IMAGELIST_DRAW_TRANSPARENT
, True)
3745 dc
.DrawText(self
.GetPageText(self
.GetSelection()), activeTabPosx
+ textOffset
, imageYCoord
)
3747 # Update all tabs that can not fit into the screen as non-visible
3748 for xx
in xrange(self
._nFrom
+ len(vTabsInfo
), len(self
._pagesInfoVec
)):
3750 self
._pagesInfoVec
[xx
].SetPosition(wx
.Point(-1, -1))
3751 self
._pagesInfoVec
[xx
].GetRegion().Clear()
3753 # Draw the left/right/close buttons
3755 self
.DrawLeftArrow(dc
)
3756 self
.DrawRightArrow(dc
)
3760 def DrawVC8Tab(self
, dc
, posx
, tabIdx
, tabWidth
, tabHeight
):
3761 """ Draws the VC8 style tabs. """
3763 borderPen
= wx
.Pen(self
._colorBorder
)
3764 tabPoints
= [wx
.Point() for ii
in xrange(8)]
3766 # If we draw the first tab or the active tab,
3767 # we draw a full tab, else we draw a truncated tab
3778 tabPoints
[0].x
= (self
.HasFlag(FNB_BOTTOM
) and [posx
] or [posx
+self
._factor
])[0]
3779 tabPoints
[0].y
= (self
.HasFlag(FNB_BOTTOM
) and [2] or [tabHeight
- 3])[0]
3781 tabPoints
[1].x
= tabPoints
[0].x
+ tabHeight
- VERTICAL_BORDER_PADDING
- 3 - self
._factor
3782 tabPoints
[1].y
= (self
.HasFlag(FNB_BOTTOM
) and [tabHeight
- (VERTICAL_BORDER_PADDING
+2)] or [(VERTICAL_BORDER_PADDING
+2)])[0]
3784 tabPoints
[2].x
= tabPoints
[1].x
+ 4
3785 tabPoints
[2].y
= (self
.HasFlag(FNB_BOTTOM
) and [tabHeight
- VERTICAL_BORDER_PADDING
] or [VERTICAL_BORDER_PADDING
])[0]
3787 tabPoints
[3].x
= tabPoints
[2].x
+ tabWidth
- 2
3788 tabPoints
[3].y
= (self
.HasFlag(FNB_BOTTOM
) and [tabHeight
- VERTICAL_BORDER_PADDING
] or [VERTICAL_BORDER_PADDING
])[0]
3790 tabPoints
[4].x
= tabPoints
[3].x
+ 1
3791 tabPoints
[4].y
= (self
.HasFlag(FNB_BOTTOM
) and [tabPoints
[3].y
- 1] or [tabPoints
[3].y
+ 1])[0]
3793 tabPoints
[5].x
= tabPoints
[4].x
+ 1
3794 tabPoints
[5].y
= (self
.HasFlag(FNB_BOTTOM
) and [(tabPoints
[4].y
- 1)] or [tabPoints
[4].y
+ 1])[0]
3796 tabPoints
[6].x
= tabPoints
[2].x
+ tabWidth
3797 tabPoints
[6].y
= tabPoints
[0].y
3799 tabPoints
[7].x
= tabPoints
[0].x
3800 tabPoints
[7].y
= tabPoints
[0].y
3802 self
._pagesInfoVec
[tabIdx
].SetRegion(tabPoints
)
3806 dc
.SetBrush(wx
.Brush((tabIdx
== self
.GetSelection() and [self
._activeTabColor
] or [self
._colorTo
])[0]))
3807 dc
.SetPen(wx
.Pen((tabIdx
== self
.GetSelection() and [wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)] or [self
._colorBorder
])[0]))
3808 dc
.DrawPolygon(tabPoints
)
3813 rect
= self
.GetClientRect()
3815 if tabIdx
!= self
.GetSelection() and not self
.HasFlag(FNB_BOTTOM
):
3818 dc
.SetPen(wx
.Pen(self
._colorBorder
))
3820 curPen
= dc
.GetPen()
3823 dc
.DrawLine(posx
, lineY
, posx
+rect
.width
, lineY
)
3825 # In case we are drawing the selected tab, we draw the border of it as well
3826 # but without the bottom (upper line incase of wxBOTTOM)
3827 if tabIdx
== self
.GetSelection():
3829 borderPen
= wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
))
3830 brush
= wx
.TRANSPARENT_BRUSH
3831 dc
.SetPen(borderPen
)
3833 dc
.DrawPolygon(tabPoints
)
3835 # Delete the bottom line (or the upper one, incase we use wxBOTTOM)
3836 dc
.SetPen(wx
.WHITE_PEN
)
3837 dc
.DrawLine(tabPoints
[0].x
, tabPoints
[0].y
, tabPoints
[6].x
, tabPoints
[6].y
)
3839 self
.FillVC8GradientColor(dc
, tabPoints
, tabIdx
== self
.GetSelection(), tabIdx
)
3841 # Draw a thin line to the right of the non-selected tab
3842 if tabIdx
!= self
.GetSelection():
3844 dc
.SetPen(wx
.Pen(wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_3DFACE
)))
3845 dc
.DrawLine(tabPoints
[4].x
-1, tabPoints
[4].y
, tabPoints
[5].x
-1, tabPoints
[5].y
)
3846 dc
.DrawLine(tabPoints
[5].x
-1, tabPoints
[5].y
, tabPoints
[6].x
-1, tabPoints
[6].y
)
3849 def FillVC8GradientColor(self
, dc
, tabPoints
, bSelectedTab
, tabIdx
):
3850 """ Fills a tab with a gradient colour. """
3852 # calculate gradient coefficients
3853 col2
= (self
.HasFlag(FNB_BOTTOM
) and [self
._colorTo
] or [self
._colorFrom
])[0]
3854 col1
= (self
.HasFlag(FNB_BOTTOM
) and [self
._colorFrom
] or [self
._colorTo
])[0]
3856 # If colorful tabs style is set, override the tab color
3857 if self
.HasFlag(FNB_COLORFUL_TABS
):
3859 if not self
._pagesInfoVec
[tabIdx
].GetColor():
3861 # First time, generate color, and keep it in the vector
3862 tabColor
= self
.GenTabColour()
3863 self
._pagesInfoVec
[tabIdx
].SetColor(tabColor
)
3865 if self
.HasFlag(FNB_BOTTOM
):
3867 col2
= LightColour(self
._pagesInfoVec
[tabIdx
].GetColor(), 50 )
3868 col1
= LightColour(self
._pagesInfoVec
[tabIdx
].GetColor(), 80 )
3872 col1
= LightColour(self
._pagesInfoVec
[tabIdx
].GetColor(), 50 )
3873 col2
= LightColour(self
._pagesInfoVec
[tabIdx
].GetColor(), 80 )
3875 size
= abs(tabPoints
[2].y
- tabPoints
[0].y
) - 1
3877 rf
, gf
, bf
= 0, 0, 0
3878 rstep
= float(col2
.Red() - col1
.Red())/float(size
)
3879 gstep
= float(col2
.Green() - col1
.Green())/float(size
)
3880 bstep
= float(col2
.Blue() - col1
.Blue())/float(size
)
3884 # If we are drawing the selected tab, we need also to draw a line
3885 # from 0.tabPoints[0].x and tabPoints[6].x . end, we achieve this
3886 # by drawing the rectangle with transparent brush
3887 # the line under the selected tab will be deleted by the drwaing loop
3889 self
.DrawTabsLine(dc
)
3893 if self
.HasFlag(FNB_BOTTOM
):
3895 if y
> tabPoints
[0].y
+ size
:
3900 if y
< tabPoints
[0].y
- size
:
3903 currCol
= wx
.Colour(col1
.Red() + rf
, col1
.Green() + gf
, col1
.Blue() + bf
)
3905 dc
.SetPen((bSelectedTab
and [wx
.Pen(self
._activeTabColor
)] or [wx
.Pen(currCol
)])[0])
3906 startX
= self
.GetStartX(tabPoints
, y
)
3907 endX
= self
.GetEndX(tabPoints
, y
)
3908 dc
.DrawLine(startX
, y
, endX
, y
)
3910 # Draw the border using the 'edge' point
3911 dc
.SetPen(wx
.Pen((bSelectedTab
and [wx
.SystemSettings_GetColour(wx
.SYS_COLOUR_BTNSHADOW
)] or [self
._colorBorder
])[0]))
3913 dc
.DrawPoint(startX
, y
)
3914 dc
.DrawPoint(endX
, y
)
3916 # Progress the color
3921 if self
.HasFlag(FNB_BOTTOM
):
3927 def GetStartX(self
, tabPoints
, y
):
3928 """ Returns the x start position of a tab. """
3930 x1
, x2
, y1
, y2
= 0.0, 0.0, 0.0, 0.0
3932 # We check the 3 points to the left
3933 style
= self
.GetParent().GetWindowStyleFlag()
3934 bBottomStyle
= (style
& FNB_BOTTOM
and [True] or [False])[0]
3941 if y
>= tabPoints
[i
].y
and y
< tabPoints
[i
+1].y
:
3944 x2
= tabPoints
[i
+1].x
3946 y2
= tabPoints
[i
+1].y
3954 if y
<= tabPoints
[i
].y
and y
> tabPoints
[i
+1].y
:
3957 x2
= tabPoints
[i
+1].x
3959 y2
= tabPoints
[i
+1].y
3964 return tabPoints
[2].x
3966 # According to the equation y = ax + b => x = (y-b)/a
3967 # We know the first 2 points
3972 a
= (y2
- y1
)/(x2
- x1
)
3974 b
= y1
- ((y2
- y1
)/(x2
- x1
))*x1
3984 def GetEndX(self
, tabPoints
, y
):
3985 """ Returns the x end position of a tab. """
3987 x1
, x2
, y1
, y2
= 0.0, 0.0, 0.0, 0.0
3989 # We check the 3 points to the left
3990 style
= self
.GetParent().GetWindowStyleFlag()
3991 bBottomStyle
= (style
& FNB_BOTTOM
and [True] or [False])[0]
3996 for i
in xrange(7, 3, -1):
3998 if y
>= tabPoints
[i
].y
and y
< tabPoints
[i
-1].y
:
4001 x2
= tabPoints
[i
-1].x
4003 y2
= tabPoints
[i
-1].y
4009 for i
in xrange(7, 3, -1):
4011 if y
<= tabPoints
[i
].y
and y
> tabPoints
[i
-1].y
:
4014 x2
= tabPoints
[i
-1].x
4016 y2
= tabPoints
[i
-1].y
4021 return tabPoints
[3].x
4023 # According to the equation y = ax + b => x = (y-b)/a
4024 # We know the first 2 points
4030 a
= (y2
- y1
)/(x2
- x1
)
4031 b
= y1
- ((y2
- y1
)/(x2
- x1
)) * x1
4041 def GenTabColour(self
):
4042 """ Generates a random soft pleasant colour for a tab. """
4044 return RandomColor()
4047 def GetSingleLineBorderColor(self
):
4049 if self
.HasFlag(FNB_VC8
):
4050 return self
._activeTabColor
4052 return PageContainerBase
.GetSingleLineBorderColor(self
)
4055 def SetFactor(self
, factor
):
4056 """ Sets the brighten colour factor. """
4058 self
._factor
= factor
4062 def GetFactor(self
):
4063 """ Returns the brighten colour factor. """