* A new style FNB_FF2: my intentions were to make it like Firefox 2, however it turned out to be an hybrid between wxAUI notebook glose style & FF2 ...I still think it looks OK. The main purpose for making it more like wxAUI is to allow applications that uses both to have same look and feel (or as close as it can get...); * Changed the behavior of the left/right rotation arrows to rotate single tab at a time and not bulk of tabs; * Updated the demo module. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@45521 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
4837 lines
151 KiB
Python
4837 lines
151 KiB
Python
# --------------------------------------------------------------------------- #
|
|
# FLATNOTEBOOK Widget wxPython IMPLEMENTATION
|
|
#
|
|
# Original C++ Code From Eran. You Can Find It At:
|
|
#
|
|
# http://wxforum.shadonet.com/viewtopic.php?t=5761&start=0
|
|
#
|
|
# License: wxWidgets license
|
|
#
|
|
#
|
|
# Python Code By:
|
|
#
|
|
# Andrea Gavana, @ 02 Oct 2006
|
|
# Latest Revision: 16 Apr 2007, 11.00 GMT
|
|
#
|
|
#
|
|
# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please
|
|
# Write To Me At:
|
|
#
|
|
# andrea.gavana@gmail.com
|
|
# gavana@kpo.kz
|
|
#
|
|
# Or, Obviously, To The wxPython Mailing List!!!
|
|
#
|
|
#
|
|
# End Of Comments
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
"""
|
|
The FlatNotebook is a full implementation of the wx.Notebook, and designed to be
|
|
a drop-in replacement for wx.Notebook. The API functions are similar so one can
|
|
expect the function to behave in the same way.
|
|
|
|
Some features:
|
|
|
|
- The buttons are highlighted a la Firefox style
|
|
- The scrolling is done for bulks of tabs (so, the scrolling is faster and better)
|
|
- The buttons area is never overdrawn by tabs (unlike many other implementations I saw)
|
|
- It is a generic control
|
|
- Currently there are 5 differnt styles - VC8, VC 71, Standard, Fancy and Firefox 2;
|
|
- Mouse middle click can be used to close tabs
|
|
- A function to add right click menu for tabs (simple as SetRightClickMenu)
|
|
- All styles has bottom style as well (they can be drawn in the bottom of screen)
|
|
- An option to hide 'X' button or navigation buttons (separately)
|
|
- Gradient coloring of the selected tabs and border
|
|
- Support for drag 'n' drop of tabs, both in the same notebook or to another notebook
|
|
- Possibility to have closing button on the active tab directly
|
|
- Support for disabled tabs
|
|
- Colours for active/inactive tabs, and captions
|
|
- Background of tab area can be painted in gradient (VC8 style only)
|
|
- Colourful tabs - a random gentle colour is generated for each new tab (very cool, VC8 style only)
|
|
|
|
|
|
And much more.
|
|
|
|
|
|
License And Version:
|
|
|
|
FlatNotebook Is Freeware And Distributed Under The wxPython License.
|
|
|
|
Latest Revision: Andrea Gavana @ 16 Apr 2007, 11.00 GMT
|
|
|
|
Version 2.0.
|
|
|
|
@undocumented: FNB_HEIGHT_SPACER, VERTICAL_BORDER_PADDING, VC8_SHAPE_LEN,
|
|
wxEVT*, left_arrow_*, right_arrow*, x_button*, down_arrow*,
|
|
FNBDragInfo, FNBDropTarget, GetMondrian*
|
|
"""
|
|
|
|
__docformat__ = "epytext"
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
# Beginning Of FLATNOTEBOOK wxPython Code
|
|
#----------------------------------------------------------------------
|
|
|
|
import wx
|
|
import random
|
|
import math
|
|
import weakref
|
|
import cPickle
|
|
|
|
# Check for the new method in 2.7 (not present in 2.6.3.3)
|
|
if wx.VERSION_STRING < "2.7":
|
|
wx.Rect.Contains = lambda self, point: wx.Rect.Inside(self, point)
|
|
|
|
FNB_HEIGHT_SPACER = 10
|
|
|
|
# Use Visual Studio 2003 (VC7.1) style for tabs
|
|
FNB_VC71 = 1
|
|
"""Use Visual Studio 2003 (VC7.1) style for tabs"""
|
|
|
|
# Use fancy style - square tabs filled with gradient coloring
|
|
FNB_FANCY_TABS = 2
|
|
"""Use fancy style - square tabs filled with gradient coloring"""
|
|
|
|
# Draw thin border around the page
|
|
FNB_TABS_BORDER_SIMPLE = 4
|
|
"""Draw thin border around the page"""
|
|
|
|
# Do not display the 'X' button
|
|
FNB_NO_X_BUTTON = 8
|
|
"""Do not display the 'X' button"""
|
|
|
|
# Do not display the Right / Left arrows
|
|
FNB_NO_NAV_BUTTONS = 16
|
|
"""Do not display the right/left arrows"""
|
|
|
|
# Use the mouse middle button for cloing tabs
|
|
FNB_MOUSE_MIDDLE_CLOSES_TABS = 32
|
|
"""Use the mouse middle button for cloing tabs"""
|
|
|
|
# Place tabs at bottom - the default is to place them
|
|
# at top
|
|
FNB_BOTTOM = 64
|
|
"""Place tabs at bottom - the default is to place them at top"""
|
|
|
|
# Disable dragging of tabs
|
|
FNB_NODRAG = 128
|
|
"""Disable dragging of tabs"""
|
|
|
|
# Use Visual Studio 2005 (VC8) style for tabs
|
|
FNB_VC8 = 256
|
|
"""Use Visual Studio 2005 (VC8) style for tabs"""
|
|
|
|
# Firefox 2 tabs style
|
|
FNB_FF2 = 131072
|
|
"""Use Firefox 2 style for tabs"""
|
|
|
|
# Place 'X' on a tab
|
|
FNB_X_ON_TAB = 512
|
|
"""Place 'X' close button on the active tab"""
|
|
|
|
FNB_BACKGROUND_GRADIENT = 1024
|
|
"""Use gradients to paint the tabs background"""
|
|
|
|
FNB_COLORFUL_TABS = 2048
|
|
"""Use colourful tabs (VC8 style only)"""
|
|
|
|
# Style to close tab using double click - styles 1024, 2048 are reserved
|
|
FNB_DCLICK_CLOSES_TABS = 4096
|
|
"""Style to close tab using double click"""
|
|
|
|
FNB_SMART_TABS = 8192
|
|
"""Use Smart Tabbing, like Alt+Tab on Windows"""
|
|
|
|
FNB_DROPDOWN_TABS_LIST = 16384
|
|
"""Use a dropdown menu on the left in place of the arrows"""
|
|
|
|
FNB_ALLOW_FOREIGN_DND = 32768
|
|
"""Allows drag 'n' drop operations between different L{FlatNotebook}s"""
|
|
|
|
FNB_HIDE_ON_SINGLE_TAB = 65536
|
|
"""Hides the Page Container when there is one or fewer tabs"""
|
|
|
|
VERTICAL_BORDER_PADDING = 4
|
|
|
|
# Button size is a 16x16 xpm bitmap
|
|
BUTTON_SPACE = 16
|
|
"""Button size is a 16x16 xpm bitmap"""
|
|
|
|
VC8_SHAPE_LEN = 16
|
|
|
|
MASK_COLOR = wx.Colour(0, 128, 128)
|
|
"""Mask colour for the arrow bitmaps"""
|
|
|
|
# Button status
|
|
FNB_BTN_PRESSED = 2
|
|
"""Navigation button is pressed"""
|
|
FNB_BTN_HOVER = 1
|
|
"""Navigation button is hovered"""
|
|
FNB_BTN_NONE = 0
|
|
"""No navigation"""
|
|
|
|
# Hit Test results
|
|
FNB_TAB = 1 # On a tab
|
|
"""Indicates mouse coordinates inside a tab"""
|
|
FNB_X = 2 # On the X button
|
|
"""Indicates mouse coordinates inside the I{X} region"""
|
|
FNB_TAB_X = 3 # On the 'X' button (tab's X button)
|
|
"""Indicates mouse coordinates inside the I{X} region in a tab"""
|
|
FNB_LEFT_ARROW = 4 # On the rotate left arrow button
|
|
"""Indicates mouse coordinates inside the left arrow region"""
|
|
FNB_RIGHT_ARROW = 5 # On the rotate right arrow button
|
|
"""Indicates mouse coordinates inside the right arrow region"""
|
|
FNB_DROP_DOWN_ARROW = 6 # On the drop down arrow button
|
|
"""Indicates mouse coordinates inside the drop down arrow region"""
|
|
FNB_NOWHERE = 0 # Anywhere else
|
|
"""Indicates mouse coordinates not on any tab of the notebook"""
|
|
|
|
FNB_DEFAULT_STYLE = FNB_MOUSE_MIDDLE_CLOSES_TABS | FNB_HIDE_ON_SINGLE_TAB
|
|
"""L{FlatNotebook} default style"""
|
|
|
|
# FlatNotebook Events:
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CHANGED: Event Fired When You Switch Page;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CHANGING: Event Fired When You Are About To Switch
|
|
# Pages, But You Can Still "Veto" The Page Changing By Avoiding To Call
|
|
# event.Skip() In Your Event Handler;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CLOSING: Event Fired When A Page Is Closing, But
|
|
# You Can Still "Veto" The Page Changing By Avoiding To Call event.Skip()
|
|
# In Your Event Handler;
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CLOSED: Event Fired When A Page Is Closed.
|
|
# wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU: Event Fired When A Menu Pops-up In A Tab.
|
|
|
|
wxEVT_FLATNOTEBOOK_PAGE_CHANGED = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
|
|
wxEVT_FLATNOTEBOOK_PAGE_CHANGING = wx.wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGING
|
|
wxEVT_FLATNOTEBOOK_PAGE_CLOSING = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_CLOSED = wx.NewEventType()
|
|
wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.NewEventType()
|
|
|
|
#-----------------------------------#
|
|
# FlatNotebookEvent
|
|
#-----------------------------------#
|
|
|
|
EVT_FLATNOTEBOOK_PAGE_CHANGED = wx.EVT_NOTEBOOK_PAGE_CHANGED
|
|
"""Notify client objects when the active page in L{FlatNotebook}
|
|
has changed."""
|
|
EVT_FLATNOTEBOOK_PAGE_CHANGING = wx.EVT_NOTEBOOK_PAGE_CHANGING
|
|
"""Notify client objects when the active page in L{FlatNotebook}
|
|
is about to change."""
|
|
EVT_FLATNOTEBOOK_PAGE_CLOSING = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, 1)
|
|
"""Notify client objects when a page in L{FlatNotebook} is closing."""
|
|
EVT_FLATNOTEBOOK_PAGE_CLOSED = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, 1)
|
|
"""Notify client objects when a page in L{FlatNotebook} has been closed."""
|
|
EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU = wx.PyEventBinder(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, 1)
|
|
"""Notify client objects when a pop-up menu should appear next to a tab."""
|
|
|
|
|
|
# Some icons in XPM format
|
|
|
|
left_arrow_disabled_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #555555",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````.```````",
|
|
"```````..```````",
|
|
"``````.`.```````",
|
|
"`````.``.```````",
|
|
"````.```.```````",
|
|
"`````.``.```````",
|
|
"``````.`.```````",
|
|
"```````..```````",
|
|
"````````.```````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
x_button_pressed_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #9e9ede",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`.###aa####aa#.`",
|
|
"`.####aa##aa##.`",
|
|
"`.#####aaaa###.`",
|
|
"`.######aa####.`",
|
|
"`.#####aaaa###.`",
|
|
"`.####aa##aa##.`",
|
|
"`.###aa####aa#.`",
|
|
"`.############.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
|
|
left_arrow_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #555555",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````.```````",
|
|
"```````..```````",
|
|
"``````...```````",
|
|
"`````....```````",
|
|
"````.....```````",
|
|
"`````....```````",
|
|
"``````...```````",
|
|
"```````..```````",
|
|
"````````.```````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
x_button_hilite_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #c9dafb",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`.##aa####aa##.`",
|
|
"`.###aa##aa###.`",
|
|
"`.####aaaa####.`",
|
|
"`.#####aa#####.`",
|
|
"`.####aaaa####.`",
|
|
"`.###aa##aa###.`",
|
|
"`.##aa####aa##.`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
x_button_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #555555",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````..````..````",
|
|
"`````..``..`````",
|
|
"``````....``````",
|
|
"```````..```````",
|
|
"``````....``````",
|
|
"`````..``..`````",
|
|
"````..````..````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
left_arrow_pressed_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #9e9ede",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`.#######a####.`",
|
|
"`.######aa####.`",
|
|
"`.#####aaa####.`",
|
|
"`.####aaaa####.`",
|
|
"`.###aaaaa####.`",
|
|
"`.####aaaa####.`",
|
|
"`.#####aaa####.`",
|
|
"`.######aa####.`",
|
|
"`.#######a####.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
left_arrow_hilite_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #c9dafb",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.######a#####.`",
|
|
"`.#####aa#####.`",
|
|
"`.####aaa#####.`",
|
|
"`.###aaaa#####.`",
|
|
"`.##aaaaa#####.`",
|
|
"`.###aaaa#####.`",
|
|
"`.####aaa#####.`",
|
|
"`.#####aa#####.`",
|
|
"`.######a#####.`",
|
|
"`.############.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
right_arrow_disabled_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #555555",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"```````.````````",
|
|
"```````..```````",
|
|
"```````.`.``````",
|
|
"```````.``.`````",
|
|
"```````.```.````",
|
|
"```````.``.`````",
|
|
"```````.`.``````",
|
|
"```````..```````",
|
|
"```````.````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
right_arrow_hilite_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #c9dafb",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.####a#######.`",
|
|
"`.####aa######.`",
|
|
"`.####aaa#####.`",
|
|
"`.####aaaa####.`",
|
|
"`.####aaaaa###.`",
|
|
"`.####aaaa####.`",
|
|
"`.####aaa#####.`",
|
|
"`.####aa######.`",
|
|
"`.####a#######.`",
|
|
"`.############.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
right_arrow_pressed_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #9e9ede",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"`..............`",
|
|
"`.############.`",
|
|
"`.############.`",
|
|
"`.#####a######.`",
|
|
"`.#####aa#####.`",
|
|
"`.#####aaa####.`",
|
|
"`.#####aaaa###.`",
|
|
"`.#####aaaaa##.`",
|
|
"`.#####aaaa###.`",
|
|
"`.#####aaa####.`",
|
|
"`.#####aa#####.`",
|
|
"`.#####a######.`",
|
|
"`..............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
|
|
right_arrow_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #555555",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"```````.````````",
|
|
"```````..```````",
|
|
"```````...``````",
|
|
"```````....`````",
|
|
"```````.....````",
|
|
"```````....`````",
|
|
"```````...``````",
|
|
"```````..```````",
|
|
"```````.````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
down_arrow_hilite_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #c9dafb",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"``.............`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.#aaaaaaaaa#.`",
|
|
"``.##aaaaaaa##.`",
|
|
"``.###aaaaa###.`",
|
|
"``.####aaa####.`",
|
|
"``.#####a#####.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
down_arrow_pressed_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #4766e0",
|
|
"# c #9e9ede",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"``.............`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.###########.`",
|
|
"``.#aaaaaaaaa#.`",
|
|
"``.##aaaaaaa##.`",
|
|
"``.###aaaaa###.`",
|
|
"``.####aaa####.`",
|
|
"``.#####a#####.`",
|
|
"``.###########.`",
|
|
"``.............`",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
|
|
down_arrow_xpm = [
|
|
" 16 16 8 1",
|
|
"` c #008080",
|
|
". c #000000",
|
|
"# c #000000",
|
|
"a c #000000",
|
|
"b c #000000",
|
|
"c c #000000",
|
|
"d c #000000",
|
|
"e c #000000",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````.........```",
|
|
"`````.......````",
|
|
"``````.....`````",
|
|
"```````...``````",
|
|
"````````.```````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````",
|
|
"````````````````"
|
|
]
|
|
|
|
|
|
#----------------------------------------------------------------------
|
|
def GetMondrianData():
|
|
return \
|
|
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\
|
|
\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x00qID\
|
|
ATX\x85\xed\xd6;\n\x800\x10E\xd1{\xc5\x8d\xb9r\x97\x16\x0b\xad$\x8a\x82:\x16\
|
|
o\xda\x84pB2\x1f\x81Fa\x8c\x9c\x08\x04Z{\xcf\xa72\xbcv\xfa\xc5\x08 \x80r\x80\
|
|
\xfc\xa2\x0e\x1c\xe4\xba\xfaX\x1d\xd0\xde]S\x07\x02\xd8>\xe1wa-`\x9fQ\xe9\
|
|
\x86\x01\x04\x10\x00\\(Dk\x1b-\x04\xdc\x1d\x07\x14\x98;\x0bS\x7f\x7f\xf9\x13\
|
|
\x04\x10@\xf9X\xbe\x00\xc9 \x14K\xc1<={\x00\x00\x00\x00IEND\xaeB`\x82'
|
|
|
|
|
|
def GetMondrianBitmap():
|
|
return wx.BitmapFromImage(GetMondrianImage().Scale(16, 16))
|
|
|
|
|
|
def GetMondrianImage():
|
|
import cStringIO
|
|
stream = cStringIO.StringIO(GetMondrianData())
|
|
return wx.ImageFromStream(stream)
|
|
|
|
|
|
def GetMondrianIcon():
|
|
icon = wx.EmptyIcon()
|
|
icon.CopyFromBitmap(GetMondrianBitmap())
|
|
return icon
|
|
#----------------------------------------------------------------------
|
|
|
|
|
|
def LightColour(color, percent):
|
|
""" Brighten input colour by percent. """
|
|
|
|
end_color = wx.WHITE
|
|
|
|
rd = end_color.Red() - color.Red()
|
|
gd = end_color.Green() - color.Green()
|
|
bd = end_color.Blue() - color.Blue()
|
|
|
|
high = 100
|
|
|
|
# We take the percent way of the color from color -. white
|
|
i = percent
|
|
r = color.Red() + ((i*rd*100)/high)/100
|
|
g = color.Green() + ((i*gd*100)/high)/100
|
|
b = color.Blue() + ((i*bd*100)/high)/100
|
|
return wx.Colour(r, g, b)
|
|
|
|
|
|
def RandomColour():
|
|
""" Creates a random colour. """
|
|
|
|
r = random.randint(0, 255) # Random value betweem 0-255
|
|
g = random.randint(0, 255) # Random value betweem 0-255
|
|
b = random.randint(0, 255) # Random value betweem 0-255
|
|
|
|
return wx.Colour(r, g, b)
|
|
|
|
|
|
def PaintStraightGradientBox(dc, rect, startColor, endColor, vertical=True):
|
|
""" Draws a gradient colored box from startColor to endColor. """
|
|
|
|
rd = endColor.Red() - startColor.Red()
|
|
gd = endColor.Green() - startColor.Green()
|
|
bd = endColor.Blue() - startColor.Blue()
|
|
|
|
# Save the current pen and brush
|
|
savedPen = dc.GetPen()
|
|
savedBrush = dc.GetBrush()
|
|
|
|
if vertical:
|
|
high = rect.GetHeight()-1
|
|
else:
|
|
high = rect.GetWidth()-1
|
|
|
|
if high < 1:
|
|
return
|
|
|
|
for i in xrange(high+1):
|
|
|
|
r = startColor.Red() + ((i*rd*100)/high)/100
|
|
g = startColor.Green() + ((i*gd*100)/high)/100
|
|
b = startColor.Blue() + ((i*bd*100)/high)/100
|
|
|
|
p = wx.Pen(wx.Colour(r, g, b))
|
|
dc.SetPen(p)
|
|
|
|
if vertical:
|
|
dc.DrawLine(rect.x, rect.y+i, rect.x+rect.width, rect.y+i)
|
|
else:
|
|
dc.DrawLine(rect.x+i, rect.y, rect.x+i, rect.y+rect.height)
|
|
|
|
# Restore the pen and brush
|
|
dc.SetPen(savedPen)
|
|
dc.SetBrush(savedBrush)
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Util functions
|
|
# -----------------------------------------------------------------------------
|
|
|
|
def DrawButton(dc, rect, focus, upperTabs):
|
|
|
|
# Define the rounded rectangle base on the given rect
|
|
# we need an array of 9 points for it
|
|
regPts = [wx.Point() for indx in xrange(9)]
|
|
|
|
if focus:
|
|
if upperTabs:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*8)
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*8)
|
|
else:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 10)*5)
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 10)*5)
|
|
else:
|
|
leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
|
|
rightPt = wx.Point(rect.x + rect.width - 2, rect.y + (rect.height / 2))
|
|
|
|
# Define the top region
|
|
top = wx.RectPP(rect.GetTopLeft(), rightPt)
|
|
bottom = wx.RectPP(leftPt, rect.GetBottomRight())
|
|
|
|
topStartColor = wx.WHITE
|
|
|
|
if not focus:
|
|
topStartColor = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 50)
|
|
|
|
topEndColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
|
|
bottomStartColor = topEndColor
|
|
bottomEndColor = topEndColor
|
|
|
|
# Incase we use bottom tabs, switch the colors
|
|
if upperTabs:
|
|
if focus:
|
|
PaintStraightGradientBox(dc, top, topStartColor, topEndColor)
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor)
|
|
else:
|
|
PaintStraightGradientBox(dc, top, topEndColor , topStartColor)
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor)
|
|
|
|
else:
|
|
if focus:
|
|
PaintStraightGradientBox(dc, bottom, topEndColor, bottomEndColor)
|
|
PaintStraightGradientBox(dc, top,topStartColor, topStartColor)
|
|
else:
|
|
PaintStraightGradientBox(dc, bottom, bottomStartColor, bottomEndColor)
|
|
PaintStraightGradientBox(dc, top, topEndColor, topStartColor)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDropSource
|
|
# Gives Some Custom UI Feedback during the DnD Operations
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDropSource(wx.DropSource):
|
|
"""
|
|
Give some custom UI feedback during the drag and drop operation in this
|
|
function. It is called on each mouse move, so your implementation must
|
|
not be too slow.
|
|
"""
|
|
|
|
def __init__(self, win):
|
|
""" Default class constructor. Used internally. """
|
|
|
|
wx.DropSource.__init__(self, win)
|
|
self._win = win
|
|
|
|
|
|
def GiveFeedback(self, effect):
|
|
""" Provides user with a nice feedback when tab is being dragged. """
|
|
|
|
self._win.DrawDragHint()
|
|
return False
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDragInfo
|
|
# Stores All The Information To Allow Drag And Drop Between Different
|
|
# FlatNotebooks.
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDragInfo:
|
|
|
|
_map = weakref.WeakValueDictionary()
|
|
|
|
def __init__(self, container, pageindex):
|
|
""" Default class constructor. """
|
|
|
|
self._id = id(container)
|
|
FNBDragInfo._map[self._id] = container
|
|
self._pageindex = pageindex
|
|
|
|
|
|
def GetContainer(self):
|
|
""" Returns the L{FlatNotebook} page (usually a panel). """
|
|
|
|
return FNBDragInfo._map.get(self._id, None)
|
|
|
|
|
|
def GetPageIndex(self):
|
|
""" Returns the page index associated with a page. """
|
|
|
|
return self._pageindex
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBDropTarget
|
|
# Simply Used To Handle The OnDrop() Method When Dragging And Dropping Between
|
|
# Different FlatNotebooks.
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBDropTarget(wx.DropTarget):
|
|
|
|
def __init__(self, parent):
|
|
""" Default class constructor. """
|
|
|
|
wx.DropTarget.__init__(self)
|
|
|
|
self._parent = parent
|
|
self._dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook"))
|
|
self.SetDataObject(self._dataobject)
|
|
|
|
|
|
def OnData(self, x, y, dragres):
|
|
""" Handles the OnData() method to call the real DnD routine. """
|
|
|
|
if not self.GetData():
|
|
return wx.DragNone
|
|
|
|
draginfo = self._dataobject.GetData()
|
|
drginfo = cPickle.loads(draginfo)
|
|
|
|
return self._parent.OnDropTarget(x, y, drginfo.GetPageIndex(), drginfo.GetContainer())
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class PageInfo
|
|
# Contains parameters for every FlatNotebook page
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class PageInfo:
|
|
"""
|
|
This class holds all the information (caption, image, etc...) belonging to a
|
|
single tab in L{FlatNotebook}.
|
|
"""
|
|
|
|
def __init__(self, caption="", imageindex=-1, tabangle=0, enabled=True):
|
|
"""
|
|
Default Class Constructor.
|
|
|
|
Parameters:
|
|
@param caption: the tab caption;
|
|
@param imageindex: the tab image index based on the assigned (set) wx.ImageList (if any);
|
|
@param tabangle: the tab angle (only on standard tabs, from 0 to 15 degrees);
|
|
@param enabled: sets enabled or disabled the tab.
|
|
"""
|
|
|
|
self._strCaption = caption
|
|
self._TabAngle = tabangle
|
|
self._ImageIndex = imageindex
|
|
self._bEnabled = enabled
|
|
self._pos = wx.Point(-1, -1)
|
|
self._size = wx.Size(-1, -1)
|
|
self._region = wx.Region()
|
|
self._xRect = wx.Rect()
|
|
self._color = None
|
|
|
|
|
|
def SetCaption(self, value):
|
|
""" Sets the tab caption. """
|
|
|
|
self._strCaption = value
|
|
|
|
|
|
def GetCaption(self):
|
|
""" Returns the tab caption. """
|
|
|
|
return self._strCaption
|
|
|
|
|
|
def SetPosition(self, value):
|
|
""" Sets the tab position. """
|
|
|
|
self._pos = value
|
|
|
|
|
|
def GetPosition(self):
|
|
""" Returns the tab position. """
|
|
|
|
return self._pos
|
|
|
|
|
|
def SetSize(self, value):
|
|
""" Sets the tab size. """
|
|
|
|
self._size = value
|
|
|
|
|
|
def GetSize(self):
|
|
""" Returns the tab size. """
|
|
|
|
return self._size
|
|
|
|
|
|
def SetTabAngle(self, value):
|
|
""" Sets the tab header angle (0 <= tab <= 15 degrees). """
|
|
|
|
self._TabAngle = min(45, value)
|
|
|
|
|
|
def GetTabAngle(self):
|
|
""" Returns the tab angle. """
|
|
|
|
return self._TabAngle
|
|
|
|
|
|
def SetImageIndex(self, value):
|
|
""" Sets the tab image index. """
|
|
|
|
self._ImageIndex = value
|
|
|
|
|
|
def GetImageIndex(self):
|
|
""" Returns the tab umage index. """
|
|
|
|
return self._ImageIndex
|
|
|
|
|
|
def GetEnabled(self):
|
|
""" Returns whether the tab is enabled or not. """
|
|
|
|
return self._bEnabled
|
|
|
|
|
|
def Enable(self, enabled):
|
|
""" Sets the tab enabled or disabled. """
|
|
|
|
self._bEnabled = enabled
|
|
|
|
|
|
def SetRegion(self, points=[]):
|
|
""" Sets the tab region. """
|
|
|
|
self._region = wx.RegionFromPoints(points)
|
|
|
|
|
|
def GetRegion(self):
|
|
""" Returns the tab region. """
|
|
|
|
return self._region
|
|
|
|
|
|
def SetXRect(self, xrect):
|
|
""" Sets the button 'X' area rect. """
|
|
|
|
self._xRect = xrect
|
|
|
|
|
|
def GetXRect(self):
|
|
""" Returns the button 'X' area rect. """
|
|
|
|
return self._xRect
|
|
|
|
|
|
def GetColour(self):
|
|
""" Returns the tab colour. """
|
|
|
|
return self._color
|
|
|
|
|
|
def SetColour(self, color):
|
|
""" Sets the tab colour. """
|
|
|
|
self._color = color
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FlatNotebookEvent
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebookEvent(wx.PyCommandEvent):
|
|
"""
|
|
This events will be sent when a EVT_FLATNOTEBOOK_PAGE_CHANGED,
|
|
EVT_FLATNOTEBOOK_PAGE_CHANGING, EVT_FLATNOTEBOOK_PAGE_CLOSING,
|
|
EVT_FLATNOTEBOOK_PAGE_CLOSED and EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU is
|
|
mapped in the parent.
|
|
"""
|
|
|
|
def __init__(self, eventType, id=1, nSel=-1, nOldSel=-1):
|
|
""" Default class constructor. """
|
|
|
|
wx.PyCommandEvent.__init__(self, eventType, id)
|
|
self._eventType = eventType
|
|
|
|
self.notify = wx.NotifyEvent(eventType, id)
|
|
|
|
|
|
def GetNotifyEvent(self):
|
|
"""Returns the actual wx.NotifyEvent."""
|
|
|
|
return self.notify
|
|
|
|
|
|
def IsAllowed(self):
|
|
"""Returns whether the event is allowed or not."""
|
|
|
|
return self.notify.IsAllowed()
|
|
|
|
|
|
def Veto(self):
|
|
"""Vetos the event."""
|
|
|
|
self.notify.Veto()
|
|
|
|
|
|
def Allow(self):
|
|
"""The event is allowed."""
|
|
|
|
self.notify.Allow()
|
|
|
|
|
|
def SetSelection(self, nSel):
|
|
""" Sets event selection. """
|
|
|
|
self._selection = nSel
|
|
|
|
|
|
def SetOldSelection(self, nOldSel):
|
|
""" Sets old event selection. """
|
|
|
|
self._oldselection = nOldSel
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns event selection. """
|
|
|
|
return self._selection
|
|
|
|
|
|
def GetOldSelection(self):
|
|
""" Returns old event selection """
|
|
|
|
return self._oldselection
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class TabNavigatorWindow
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class TabNavigatorWindow(wx.Dialog):
|
|
"""
|
|
This class is used to create a modal dialog that enables "Smart Tabbing",
|
|
similar to what you would get by hitting Alt+Tab on Windows.
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
""" Default class constructor. Used internally."""
|
|
|
|
wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)
|
|
|
|
self._selectedItem = -1
|
|
self._indexMap = []
|
|
|
|
self._bmp = GetMondrianBitmap()
|
|
|
|
sz = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self._listBox = wx.ListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, 150), [], wx.LB_SINGLE | wx.NO_BORDER)
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
mem_dc.SelectObject(wx.EmptyBitmap(1,1))
|
|
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
font.SetWeight(wx.BOLD)
|
|
mem_dc.SetFont(font)
|
|
|
|
panelHeight = mem_dc.GetCharHeight()
|
|
panelHeight += 4 # Place a spacer of 2 pixels
|
|
|
|
# Out signpost bitmap is 24 pixels
|
|
if panelHeight < 24:
|
|
panelHeight = 24
|
|
|
|
self._panel = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.Size(200, panelHeight))
|
|
|
|
sz.Add(self._panel)
|
|
sz.Add(self._listBox, 1, wx.EXPAND)
|
|
|
|
self.SetSizer(sz)
|
|
|
|
# Connect events to the list box
|
|
self._listBox.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
|
|
self._listBox.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
|
|
self._listBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnItemSelected)
|
|
|
|
# Connect paint event to the panel
|
|
self._panel.Bind(wx.EVT_PAINT, self.OnPanelPaint)
|
|
self._panel.Bind(wx.EVT_ERASE_BACKGROUND, self.OnPanelEraseBg)
|
|
|
|
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
|
|
self._listBox.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
|
|
self.PopulateListControl(parent)
|
|
|
|
self.GetSizer().Fit(self)
|
|
self.GetSizer().SetSizeHints(self)
|
|
self.GetSizer().Layout()
|
|
self.Centre()
|
|
|
|
|
|
def OnKeyUp(self, event):
|
|
"""Handles the wx.EVT_KEY_UP for the L{TabNavigatorWindow}."""
|
|
|
|
if event.GetKeyCode() == wx.WXK_CONTROL:
|
|
self.CloseDialog()
|
|
|
|
|
|
def OnNavigationKey(self, event):
|
|
"""Handles the wx.EVT_NAVIGATION_KEY for the L{TabNavigatorWindow}. """
|
|
|
|
selected = self._listBox.GetSelection()
|
|
bk = self.GetParent()
|
|
maxItems = bk.GetPageCount()
|
|
|
|
if event.GetDirection():
|
|
|
|
# Select next page
|
|
if selected == maxItems - 1:
|
|
itemToSelect = 0
|
|
else:
|
|
itemToSelect = selected + 1
|
|
|
|
else:
|
|
|
|
# Previous page
|
|
if selected == 0:
|
|
itemToSelect = maxItems - 1
|
|
else:
|
|
itemToSelect = selected - 1
|
|
|
|
self._listBox.SetSelection(itemToSelect)
|
|
|
|
|
|
def PopulateListControl(self, book):
|
|
"""Populates the L{TabNavigatorWindow} listbox with a list of tabs."""
|
|
|
|
selection = book.GetSelection()
|
|
count = book.GetPageCount()
|
|
|
|
self._listBox.Append(book.GetPageText(selection))
|
|
self._indexMap.append(selection)
|
|
|
|
prevSel = book.GetPreviousSelection()
|
|
|
|
if prevSel != wx.NOT_FOUND:
|
|
|
|
# Insert the previous selection as second entry
|
|
self._listBox.Append(book.GetPageText(prevSel))
|
|
self._indexMap.append(prevSel)
|
|
|
|
for c in xrange(count):
|
|
|
|
# Skip selected page
|
|
if c == selection:
|
|
continue
|
|
|
|
# Skip previous selected page as well
|
|
if c == prevSel:
|
|
continue
|
|
|
|
self._listBox.Append(book.GetPageText(c))
|
|
self._indexMap.append(c)
|
|
|
|
# Select the next entry after the current selection
|
|
self._listBox.SetSelection(0)
|
|
dummy = wx.NavigationKeyEvent()
|
|
dummy.SetDirection(True)
|
|
self.OnNavigationKey(dummy)
|
|
|
|
|
|
def OnItemSelected(self, event):
|
|
"""Handles the wx.EVT_LISTBOX_DCLICK event for the wx.ListBox inside L{TabNavigatorWindow}. """
|
|
|
|
self.CloseDialog()
|
|
|
|
|
|
def CloseDialog(self):
|
|
"""Closes the L{TabNavigatorWindow} dialog, setting selection in L{FlatNotebook}."""
|
|
|
|
bk = self.GetParent()
|
|
self._selectedItem = self._listBox.GetSelection()
|
|
iter = self._indexMap[self._selectedItem]
|
|
bk._pages.FireEvent(iter)
|
|
self.EndModal(wx.ID_OK)
|
|
|
|
|
|
def OnPanelPaint(self, event):
|
|
"""Handles the wx.EVT_PAINT event for L{TabNavigatorWindow} top panel. """
|
|
|
|
dc = wx.PaintDC(self._panel)
|
|
rect = self._panel.GetClientRect()
|
|
|
|
bmp = wx.EmptyBitmap(rect.width, rect.height)
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
mem_dc.SelectObject(bmp)
|
|
|
|
endColour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
|
|
startColour = LightColour(endColour, 50)
|
|
PaintStraightGradientBox(mem_dc, rect, startColour, endColour)
|
|
|
|
# Draw the caption title and place the bitmap
|
|
# get the bitmap optimal position, and draw it
|
|
bmpPt, txtPt = wx.Point(), wx.Point()
|
|
bmpPt.y = (rect.height - self._bmp.GetHeight())/2
|
|
bmpPt.x = 3
|
|
mem_dc.DrawBitmap(self._bmp, bmpPt.x, bmpPt.y, True)
|
|
|
|
# get the text position, and draw it
|
|
font = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
font.SetWeight(wx.BOLD)
|
|
mem_dc.SetFont(font)
|
|
fontHeight = mem_dc.GetCharHeight()
|
|
|
|
txtPt.x = bmpPt.x + self._bmp.GetWidth() + 4
|
|
txtPt.y = (rect.height - fontHeight)/2
|
|
mem_dc.SetTextForeground(wx.WHITE)
|
|
mem_dc.DrawText("Opened tabs:", txtPt.x, txtPt.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
dc.DrawBitmap(bmp, 0, 0)
|
|
|
|
|
|
def OnPanelEraseBg(self, event):
|
|
"""Handles the wx.EVT_ERASE_BACKGROUND event for L{TabNavigatorWindow} top panel. """
|
|
|
|
pass
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBRenderer
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBRenderer:
|
|
"""
|
|
Parent class for the 4 renderers defined: I{Standard}, I{VC71}, I{Fancy}
|
|
and I{VC8}. This class implements the common methods of all 4 renderers.
|
|
@undocumented: _GetBitmap*
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""Default class constructor. """
|
|
|
|
self._tabXBgBmp = wx.EmptyBitmap(16, 16)
|
|
self._xBgBmp = wx.EmptyBitmap(16, 14)
|
|
self._leftBgBmp = wx.EmptyBitmap(16, 14)
|
|
self._rightBgBmp = wx.EmptyBitmap(16, 14)
|
|
self._tabHeight = None
|
|
|
|
|
|
def GetLeftButtonPos(self, pageContainer):
|
|
""" Returns the left button position in the navigation area. """
|
|
|
|
pc = pageContainer
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if style & FNB_NO_X_BUTTON:
|
|
return clientWidth - 38
|
|
else:
|
|
return clientWidth - 54
|
|
|
|
|
|
|
|
def GetRightButtonPos(self, pageContainer):
|
|
""" Returns the right button position in the navigation area. """
|
|
|
|
pc = pageContainer
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if style & FNB_NO_X_BUTTON:
|
|
return clientWidth - 22
|
|
else:
|
|
return clientWidth - 38
|
|
|
|
|
|
def GetDropArrowButtonPos(self, pageContainer):
|
|
""" Returns the drop down button position in the navigation area. """
|
|
|
|
return self.GetRightButtonPos(pageContainer)
|
|
|
|
|
|
def GetXPos(self, pageContainer):
|
|
""" Returns the 'X' button position in the navigation area. """
|
|
|
|
pc = pageContainer
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
if style & FNB_NO_X_BUTTON:
|
|
return clientWidth
|
|
else:
|
|
return clientWidth - 22
|
|
|
|
|
|
def GetButtonsAreaLength(self, pageContainer):
|
|
""" Returns the navigation area width. """
|
|
|
|
pc = pageContainer
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
|
|
# ''
|
|
if style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
|
|
return 0
|
|
|
|
# 'x'
|
|
elif style & FNB_NO_NAV_BUTTONS and not style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
|
|
return 22
|
|
|
|
# '<>'
|
|
if not style & FNB_NO_NAV_BUTTONS and style & FNB_NO_X_BUTTON and not style & FNB_DROPDOWN_TABS_LIST:
|
|
return 53 - 16
|
|
|
|
# 'vx'
|
|
if style & FNB_DROPDOWN_TABS_LIST and not style & FNB_NO_X_BUTTON:
|
|
return 22 + 16
|
|
|
|
# 'v'
|
|
if style & FNB_DROPDOWN_TABS_LIST and style & FNB_NO_X_BUTTON:
|
|
return 22
|
|
|
|
# '<>x'
|
|
return 53
|
|
|
|
|
|
def DrawLeftArrow(self, pageContainer, dc):
|
|
""" Draw the left navigation arrow. """
|
|
|
|
pc = pageContainer
|
|
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
if style & FNB_NO_NAV_BUTTONS:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nLeftButtonStatus == FNB_BTN_HOVER:
|
|
arrowBmp = wx.BitmapFromXPMData(left_arrow_hilite_xpm)
|
|
elif pc._nLeftButtonStatus == FNB_BTN_PRESSED:
|
|
arrowBmp = wx.BitmapFromXPMData(left_arrow_pressed_xpm)
|
|
else:
|
|
arrowBmp = wx.BitmapFromXPMData(left_arrow_xpm)
|
|
|
|
if pc._nFrom == 0:
|
|
# Handle disabled arrow
|
|
arrowBmp = wx.BitmapFromXPMData(left_arrow_disabled_xpm)
|
|
|
|
arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR))
|
|
|
|
# Erase old bitmap
|
|
posx = self.GetLeftButtonPos(pc)
|
|
dc.DrawBitmap(self._leftBgBmp, posx, 6)
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(arrowBmp, posx, 6, True)
|
|
|
|
|
|
def DrawRightArrow(self, pageContainer, dc):
|
|
""" Draw the right navigation arrow. """
|
|
|
|
pc = pageContainer
|
|
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
if style & FNB_NO_NAV_BUTTONS:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nRightButtonStatus == FNB_BTN_HOVER:
|
|
arrowBmp = wx.BitmapFromXPMData(right_arrow_hilite_xpm)
|
|
elif pc._nRightButtonStatus == FNB_BTN_PRESSED:
|
|
arrowBmp = wx.BitmapFromXPMData(right_arrow_pressed_xpm)
|
|
else:
|
|
arrowBmp = wx.BitmapFromXPMData(right_arrow_xpm)
|
|
|
|
# Check if the right most tab is visible, if it is
|
|
# don't rotate right anymore
|
|
if pc._pagesInfoVec[-1].GetPosition() != wx.Point(-1, -1):
|
|
arrowBmp = wx.BitmapFromXPMData(right_arrow_disabled_xpm)
|
|
|
|
arrowBmp.SetMask(wx.Mask(arrowBmp, MASK_COLOR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetRightButtonPos(pc)
|
|
dc.DrawBitmap(self._rightBgBmp, posx, 6)
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(arrowBmp, posx, 6, True)
|
|
|
|
|
|
def DrawDropDownArrow(self, pageContainer, dc):
|
|
""" Draws the drop-down arrow in the navigation area. """
|
|
|
|
pc = pageContainer
|
|
|
|
# Check if this style is enabled
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
if not style & FNB_DROPDOWN_TABS_LIST:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
if pc._nArrowDownButtonStatus == FNB_BTN_HOVER:
|
|
downBmp = wx.BitmapFromXPMData(down_arrow_hilite_xpm)
|
|
elif pc._nArrowDownButtonStatus == FNB_BTN_PRESSED:
|
|
downBmp = wx.BitmapFromXPMData(down_arrow_pressed_xpm)
|
|
else:
|
|
downBmp = wx.BitmapFromXPMData(down_arrow_xpm)
|
|
|
|
downBmp.SetMask(wx.Mask(downBmp, MASK_COLOR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetDropArrowButtonPos(pc)
|
|
dc.DrawBitmap(self._xBgBmp, posx, 6)
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(downBmp, posx, 6, True)
|
|
|
|
|
|
def DrawX(self, pageContainer, dc):
|
|
""" Draw the 'X' navigation button in the navigation area. """
|
|
|
|
pc = pageContainer
|
|
|
|
# Check if this style is enabled
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
if style & FNB_NO_X_BUTTON:
|
|
return
|
|
|
|
# Make sure that there are pages in the container
|
|
if not pc._pagesInfoVec:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
if pc._nXButtonStatus == FNB_BTN_HOVER:
|
|
xbmp = wx.BitmapFromXPMData(x_button_hilite_xpm)
|
|
elif pc._nXButtonStatus == FNB_BTN_PRESSED:
|
|
xbmp = wx.BitmapFromXPMData(x_button_pressed_xpm)
|
|
else:
|
|
xbmp = wx.BitmapFromXPMData(x_button_xpm)
|
|
|
|
xbmp.SetMask(wx.Mask(xbmp, MASK_COLOR))
|
|
|
|
# erase old bitmap
|
|
posx = self.GetXPos(pc)
|
|
dc.DrawBitmap(self._xBgBmp, posx, 6)
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(xbmp, posx, 6, True)
|
|
|
|
|
|
def DrawTabX(self, pageContainer, dc, rect, tabIdx, btnStatus):
|
|
""" Draws the 'X' in the selected tab. """
|
|
|
|
pc = pageContainer
|
|
if not pc.HasFlag(FNB_X_ON_TAB):
|
|
return
|
|
|
|
# We draw the 'x' on the active tab only
|
|
if tabIdx != pc.GetSelection() or tabIdx < 0:
|
|
return
|
|
|
|
# Set the bitmap according to the button status
|
|
|
|
if btnStatus == FNB_BTN_HOVER:
|
|
xBmp = wx.BitmapFromXPMData(x_button_hilite_xpm)
|
|
elif btnStatus == FNB_BTN_PRESSED:
|
|
xBmp = wx.BitmapFromXPMData(x_button_pressed_xpm)
|
|
else:
|
|
xBmp = wx.BitmapFromXPMData(x_button_xpm)
|
|
|
|
# Set the masking
|
|
xBmp.SetMask(wx.Mask(xBmp, MASK_COLOR))
|
|
|
|
# erase old button
|
|
dc.DrawBitmap(self._tabXBgBmp, rect.x, rect.y)
|
|
|
|
# Draw the new bitmap
|
|
dc.DrawBitmap(xBmp, rect.x, rect.y, True)
|
|
|
|
# Update the vector
|
|
rr = wx.Rect(rect.x, rect.y, 14, 13)
|
|
pc._pagesInfoVec[tabIdx].SetXRect(rr)
|
|
|
|
|
|
def _GetBitmap(self, dc, rect, bmp):
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
mem_dc.SelectObject(bmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
return bmp
|
|
|
|
|
|
def DrawTabsLine(self, pageContainer, dc, selTabX1=-1, selTabX2=-1):
|
|
""" Draws a line over the tabs. """
|
|
|
|
pc = pageContainer
|
|
|
|
clntRect = pc.GetClientRect()
|
|
clientRect3 = wx.Rect(0, 0, clntRect.width, clntRect.height)
|
|
|
|
if pc.HasFlag(FNB_FF2):
|
|
if not pc.HasFlag(FNB_BOTTOM):
|
|
fillColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)
|
|
else:
|
|
fillColor = wx.WHITE
|
|
|
|
dc.SetPen(wx.Pen(fillColor))
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
|
|
dc.DrawLine(1, 0, clntRect.width-1, 0)
|
|
dc.DrawLine(1, 1, clntRect.width-1, 1)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawLine(1, 2, clntRect.width-1, 2)
|
|
|
|
dc.SetPen(wx.Pen(fillColor))
|
|
dc.DrawLine(selTabX1 + 2, 2, selTabX2 - 1, 2)
|
|
|
|
else:
|
|
|
|
dc.DrawLine(1, clntRect.height, clntRect.width-1, clntRect.height)
|
|
dc.DrawLine(1, clntRect.height-1, clntRect.width-1, clntRect.height-1)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawLine(1, clntRect.height-2, clntRect.width-1, clntRect.height-2)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
|
|
dc.DrawLine(selTabX1 + 2, clntRect.height-2, selTabX2-1, clntRect.height-2)
|
|
|
|
else:
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
|
|
clientRect = wx.Rect(0, 2, clntRect.width, clntRect.height - 2)
|
|
clientRect2 = wx.Rect(0, 1, clntRect.width, clntRect.height - 1)
|
|
|
|
else:
|
|
|
|
clientRect = wx.Rect(0, 0, clntRect.width, clntRect.height - 2)
|
|
clientRect2 = wx.Rect(0, 0, clntRect.width, clntRect.height - 1)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(wx.Pen(pc.GetSingleLineBorderColour()))
|
|
dc.DrawRectangleRect(clientRect2)
|
|
dc.DrawRectangleRect(clientRect3)
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
|
|
dc.DrawRectangleRect(clientRect)
|
|
|
|
if not pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
|
|
|
|
dc.SetPen(wx.Pen((pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc._tabAreaColor])[0]))
|
|
dc.DrawLine(0, 0, 0, clientRect.height+1)
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
|
|
dc.DrawLine(0, clientRect.height+1, clientRect.width, clientRect.height+1)
|
|
|
|
else:
|
|
|
|
dc.DrawLine(0, 0, clientRect.width, 0)
|
|
|
|
dc.DrawLine(clientRect.width - 1, 0, clientRect.width - 1, clientRect.height+1)
|
|
|
|
|
|
def CalcTabWidth(self, pageContainer, tabIdx, tabHeight):
|
|
""" Calculates the width of the input tab. """
|
|
|
|
pc = pageContainer
|
|
dc = wx.MemoryDC()
|
|
dc.SelectObject(wx.EmptyBitmap(1,1))
|
|
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
if pc.IsDefaultTabs():
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
|
|
# Calculate the text length using the bold font, so when selecting a tab
|
|
# its width will not change
|
|
dc.SetFont(boldFont)
|
|
width, pom = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
|
|
# Set a minimum size to a tab
|
|
if width < 20:
|
|
width = 20
|
|
|
|
tabWidth = 2*pc._pParent.GetPadding() + width
|
|
|
|
# Style to add a small 'x' button on the top right
|
|
# of the tab
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
# The xpm image that contains the 'x' button is 9 pixels
|
|
spacer = 9
|
|
if pc.HasFlag(FNB_VC8):
|
|
spacer = 4
|
|
|
|
tabWidth += pc._pParent.GetPadding() + spacer
|
|
|
|
if pc.IsDefaultTabs():
|
|
# Default style
|
|
tabWidth += 2*shapePoints
|
|
|
|
hasImage = pc._ImageList != None and pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
|
|
# For VC71 style, we only add the icon size (16 pixels)
|
|
if hasImage:
|
|
|
|
if not pc.IsDefaultTabs():
|
|
tabWidth += 16 + pc._pParent.GetPadding()
|
|
else:
|
|
# Default style
|
|
tabWidth += 16 + pc._pParent.GetPadding() + shapePoints/2
|
|
|
|
return tabWidth
|
|
|
|
|
|
def CalcTabHeight(self, pageContainer):
|
|
""" Calculates the height of the input tab. """
|
|
|
|
if self._tabHeight:
|
|
return self._tabHeight
|
|
|
|
pc = pageContainer
|
|
dc = wx.MemoryDC()
|
|
dc.SelectObject(wx.EmptyBitmap(1,1))
|
|
|
|
# For GTK it seems that we must do this steps in order
|
|
# for the tabs will get the proper height on initialization
|
|
# on MSW, preforming these steps yields wierd results
|
|
normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = normalFont
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
height = dc.GetCharHeight()
|
|
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
# On GTK the tabs are should be larger
|
|
tabHeight += 6
|
|
|
|
self._tabHeight = tabHeight
|
|
|
|
return tabHeight
|
|
|
|
|
|
def DrawTabs(self, pageContainer, dc):
|
|
""" Actually draws the tabs in L{FlatNotebook}."""
|
|
|
|
pc = pageContainer
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
# Works well on MSW & GTK, however this lines should be skipped on MAC
|
|
if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
|
|
pc.Hide()
|
|
return
|
|
|
|
# Get the text hight
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
style = pc.GetParent().GetWindowStyleFlag()
|
|
|
|
# Calculate the number of rows required for drawing the tabs
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
# Set the maximum client size
|
|
pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
|
|
if style & FNB_VC71:
|
|
backBrush = wx.Brush(wx.Colour(247, 243, 233))
|
|
else:
|
|
backBrush = wx.Brush(pc._tabAreaColor)
|
|
|
|
noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE))
|
|
selBrush = wx.Brush(pc._activeTabColor)
|
|
|
|
size = pc.GetSize()
|
|
|
|
# Background
|
|
dc.SetTextBackground((style & FNB_VC71 and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0])
|
|
dc.SetTextForeground(pc._activeTextColor)
|
|
dc.SetBrush(backBrush)
|
|
|
|
# If border style is set, set the pen to be border pen
|
|
if pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
|
|
dc.SetPen(borderPen)
|
|
else:
|
|
colr = (pc.HasFlag(FNB_VC71) and [wx.Colour(247, 243, 233)] or [pc.GetBackgroundColour()])[0]
|
|
dc.SetPen(wx.Pen(colr))
|
|
|
|
if pc.HasFlag(FNB_FF2):
|
|
lightFactor = (pc.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
|
|
PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColor, LightColour(pc._tabAreaColor, lightFactor))
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
|
|
dc.DrawRectangle(0, 0, size.x, size.y)
|
|
|
|
# Take 3 bitmaps for the background for the buttons
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
#---------------------------------------
|
|
# X button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetXPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._xBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
#---------------------------------------
|
|
# Right button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetRightButtonPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._rightBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
#---------------------------------------
|
|
# Left button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetLeftButtonPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._leftBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
# We always draw the bottom/upper line of the tabs
|
|
# regradless the style
|
|
dc.SetPen(borderPen)
|
|
|
|
if not pc.HasFlag(FNB_FF2):
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
if pc.HasFlag(FNB_VC71):
|
|
|
|
greyLineYVal = (pc.HasFlag(FNB_BOTTOM) and [0] or [size.y - 2])[0]
|
|
whiteLineYVal = (pc.HasFlag(FNB_BOTTOM) and [3] or [size.y - 3])[0]
|
|
|
|
pen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))
|
|
dc.SetPen(pen)
|
|
|
|
# Draw thik grey line between the windows area and
|
|
# the tab area
|
|
for num in xrange(3):
|
|
dc.DrawLine(0, greyLineYVal + num, size.x, greyLineYVal + num)
|
|
|
|
wbPen = (pc.HasFlag(FNB_BOTTOM) and [wx.BLACK_PEN] or [wx.WHITE_PEN])[0]
|
|
dc.SetPen(wbPen)
|
|
dc.DrawLine(1, whiteLineYVal, size.x - 1, whiteLineYVal)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw labels
|
|
normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
# Update all the tabs from 0 to 'pc._nFrom' to be non visible
|
|
for i in xrange(pc._nFrom):
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
count = pc._nFrom
|
|
|
|
#----------------------------------------------------------
|
|
# Go over and draw the visible tabs
|
|
#----------------------------------------------------------
|
|
x1 = x2 = -1
|
|
for i in xrange(pc._nFrom, len(pc._pagesInfoVec)):
|
|
|
|
dc.SetPen(borderPen)
|
|
|
|
if not pc.HasFlag(FNB_FF2):
|
|
dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
|
|
|
|
# Now set the font to the correct font
|
|
dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
|
|
|
|
# Add the padding to the tab width
|
|
# Tab width:
|
|
# +-----------------------------------------------------------+
|
|
# | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
|
|
# +-----------------------------------------------------------+
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
|
|
# Check if we can draw more
|
|
if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break
|
|
|
|
count = count + 1
|
|
|
|
# By default we clean the tab region
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Clean the 'x' buttn on the tab.
|
|
# A 'Clean' rectangle, is a rectangle with width or height
|
|
# with values lower than or equal to 0
|
|
pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
|
|
|
|
# Draw the tab (border, text, image & 'x' on tab)
|
|
self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
|
|
|
|
if pc.GetSelection() == i:
|
|
x1 = posx
|
|
x2 = posx + tabWidth + 2
|
|
|
|
# Restore the text forground
|
|
dc.SetTextForeground(pc._activeTextColor)
|
|
|
|
# Update the tab position & size
|
|
posy = (pc.HasFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(posx, posy))
|
|
pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
|
|
posx += tabWidth
|
|
|
|
# Update all tabs that can not fit into the screen as non-visible
|
|
for i in xrange(count, len(pc._pagesInfoVec)):
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Draw the left/right/close buttons
|
|
# Left arrow
|
|
self.DrawLeftArrow(pc, dc)
|
|
self.DrawRightArrow(pc, dc)
|
|
self.DrawX(pc, dc)
|
|
self.DrawDropDownArrow(pc, dc)
|
|
|
|
if pc.HasFlag(FNB_FF2):
|
|
self.DrawTabsLine(pc, dc, x1, x2)
|
|
|
|
|
|
def DrawDragHint(self, pc, tabIdx):
|
|
"""
|
|
Draws tab drag hint, the default implementation is to do nothing.
|
|
You can override this function to provide a nice feedback to user.
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
def NumberTabsCanFit(self, pageContainer, fr=-1):
|
|
|
|
pc = pageContainer
|
|
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
vTabInfo = []
|
|
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# The drawing starts from posx
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
if fr < 0:
|
|
fr = pc._nFrom
|
|
|
|
for i in xrange(fr, len(pc._pagesInfoVec)):
|
|
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
if posx + tabWidth + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break;
|
|
|
|
# Add a result to the returned vector
|
|
tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth , tabHeight)
|
|
vTabInfo.append(tabRect)
|
|
|
|
# Advance posx
|
|
posx += tabWidth + FNB_HEIGHT_SPACER
|
|
|
|
return vTabInfo
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FNBRendererMgr
|
|
# A manager that handles all the renderers defined below and calls the
|
|
# appropriate one when drawing is needed
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FNBRendererMgr:
|
|
"""
|
|
This class represents a manager that handles all the 4 renderers defined
|
|
and calls the appropriate one when drawing is needed.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
# register renderers
|
|
|
|
self._renderers = {}
|
|
self._renderers.update({-1: FNBRendererDefault()})
|
|
self._renderers.update({FNB_VC71: FNBRendererVC71()})
|
|
self._renderers.update({FNB_FANCY_TABS: FNBRendererFancy()})
|
|
self._renderers.update({FNB_VC8: FNBRendererVC8()})
|
|
self._renderers.update({FNB_FF2: FNBRendererFirefox2()})
|
|
|
|
|
|
def GetRenderer(self, style):
|
|
""" Returns the current renderer based on the style selected. """
|
|
|
|
if style & FNB_VC71:
|
|
return self._renderers[FNB_VC71]
|
|
|
|
if style & FNB_FANCY_TABS:
|
|
return self._renderers[FNB_FANCY_TABS]
|
|
|
|
if style & FNB_VC8:
|
|
return self._renderers[FNB_VC8]
|
|
|
|
if style & FNB_FF2:
|
|
return self._renderers[FNB_FF2]
|
|
|
|
# the default is to return the default renderer
|
|
return self._renderers[-1]
|
|
|
|
|
|
#------------------------------------------
|
|
# Default renderer
|
|
#------------------------------------------
|
|
|
|
class FNBRendererDefault(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the I{Standard} renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
""" Draws a tab using the I{Standard} style. """
|
|
|
|
# Default style
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
tabPoints = [wx.Point() for ii in xrange(7)]
|
|
tabPoints[0].x = posx
|
|
tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[1].x = int(posx+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x+2
|
|
tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = int(posx+tabWidth-(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))-2
|
|
tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x+2
|
|
tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[5].x = int(tabPoints[4].x+(tabHeight-2)*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[6].x = tabPoints[0].x
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
# Draw the tab as rounded rectangle
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
else:
|
|
|
|
if tabIdx != pc.GetSelection() - 1:
|
|
|
|
# Draw a vertical line to the right of the text
|
|
pt1x = tabPoints[5].x
|
|
pt1y = (pc.HasFlag(FNB_BOTTOM) and [4] or [tabHeight - 6])[0]
|
|
pt2x = tabPoints[5].x
|
|
pt2y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 4] or [4])[0]
|
|
dc.DrawLine(pt1x, pt1y, pt2x, pt2y)
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
savePen = dc.GetPen()
|
|
whitePen = wx.Pen(wx.WHITE)
|
|
whitePen.SetWidth(1)
|
|
dc.SetPen(whitePen)
|
|
|
|
secPt = wx.Point(tabPoints[5].x + 1, tabPoints[5].y)
|
|
dc.DrawLine(tabPoints[0].x, tabPoints[0].y, secPt.x, secPt.y)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(savePen)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16 + shapePoints/2
|
|
else:
|
|
textOffset = pc._pParent._nPadding + shapePoints/2
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------
|
|
# Firefox2 renderer
|
|
#------------------------------------------
|
|
class FNBRendererFirefox2(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the I{Firefox 2} renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
""" Draws a tab using the I{Firefox 2} style. """
|
|
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
tabPoints = [wx.Point() for indx in xrange(7)]
|
|
tabPoints[0].x = posx + 2
|
|
tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x
|
|
tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x+2
|
|
tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = posx + tabWidth - 2
|
|
tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 2
|
|
tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[5].x = tabPoints[4].x
|
|
tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 2])[0]
|
|
|
|
tabPoints[6].x = tabPoints[0].x
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
#------------------------------------
|
|
# Paint the tab with gradient
|
|
#------------------------------------
|
|
rr = wx.RectPP(tabPoints[2], tabPoints[5])
|
|
DrawButton(dc, rr, pc.GetSelection() == tabIdx , not pc.HasFlag(FNB_BOTTOM))
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw the tab as rounded rectangle
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
shapePoints = int(tabHeight*math.tan(float(pc._pagesInfoVec[tabIdx].GetTabAngle())/180.0*math.pi))
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*padding + 16 + shapePoints/2
|
|
else:
|
|
textOffset = padding + shapePoints/2
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Visual studio 7.1
|
|
#------------------------------------------------------------------
|
|
|
|
class FNBRendererVC71(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the I{VC71} renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
""" Draws a tab using the I{VC71} style. """
|
|
|
|
# Visual studio 7.1 style
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
dc.SetPen((tabIdx == pc.GetSelection() and [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or [borderPen])[0])
|
|
dc.SetBrush((tabIdx == pc.GetSelection() and [wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))] or [wx.Brush(wx.Colour(247, 243, 233))])[0])
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
posy = (pc.HasFlag(FNB_BOTTOM) and [0] or [VERTICAL_BORDER_PADDING])[0]
|
|
tabH = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - 5] or [tabHeight - 3])[0]
|
|
dc.DrawRectangle(posx, posy, tabWidth, tabH)
|
|
|
|
# Draw a black line on the left side of the
|
|
# rectangle
|
|
dc.SetPen(wx.BLACK_PEN)
|
|
|
|
blackLineY1 = VERTICAL_BORDER_PADDING
|
|
blackLineY2 = tabH
|
|
dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
|
|
|
|
# To give the tab more 3D look we do the following
|
|
# Incase the tab is on top,
|
|
# Draw a thik white line on topof the rectangle
|
|
# Otherwise, draw a thin (1 pixel) black line at the bottom
|
|
|
|
pen = wx.Pen((pc.HasFlag(FNB_BOTTOM) and [wx.BLACK] or [wx.WHITE])[0])
|
|
dc.SetPen(pen)
|
|
whiteLinePosY = (pc.HasFlag(FNB_BOTTOM) and [blackLineY2] or [VERTICAL_BORDER_PADDING ])[0]
|
|
dc.DrawLine(posx , whiteLinePosY, posx + tabWidth + 1, whiteLinePosY)
|
|
|
|
# Draw a white vertical line to the left of the tab
|
|
dc.SetPen(wx.WHITE_PEN)
|
|
if not pc.HasFlag(FNB_BOTTOM):
|
|
blackLineY2 += 1
|
|
|
|
dc.DrawLine(posx, blackLineY1, posx, blackLineY2)
|
|
|
|
else:
|
|
|
|
# We dont draw a rectangle for non selected tabs, but only
|
|
# vertical line on the left
|
|
|
|
blackLineY1 = (pc.HasFlag(FNB_BOTTOM) and [VERTICAL_BORDER_PADDING + 2] or [VERTICAL_BORDER_PADDING + 1])[0]
|
|
blackLineY2 = pc.GetSize().y - 5
|
|
dc.DrawLine(posx + tabWidth, blackLineY1, posx + tabWidth, blackLineY2)
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [5] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16
|
|
else:
|
|
textOffset = pc._pParent._nPadding
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Fancy style
|
|
#------------------------------------------------------------------
|
|
|
|
class FNBRendererFancy(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the I{Fancy} renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
""" Draws a tab using the I{Fancy} style, similar to VC71 but with gradients. """
|
|
|
|
# Fancy tabs - like with VC71 but with the following differences:
|
|
# - The Selected tab is colored with gradient color
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
pc = pageContainer
|
|
|
|
pen = (tabIdx == pc.GetSelection() and [wx.Pen(pc._pParent.GetBorderColour())] or [wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE))])[0]
|
|
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
posy = (pc.HasFlag(FNB_BOTTOM) and [2] or [VERTICAL_BORDER_PADDING])[0]
|
|
th = tabHeight - 5
|
|
|
|
rect = wx.Rect(posx, posy, tabWidth, th)
|
|
|
|
col2 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourTo()] or [pc._pParent.GetGradientColourFrom()])[0]
|
|
col1 = (pc.HasFlag(FNB_BOTTOM) and [pc._pParent.GetGradientColourFrom()] or [pc._pParent.GetGradientColourTo()])[0]
|
|
|
|
PaintStraightGradientBox(dc, rect, col1, col2)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.SetPen(pen)
|
|
dc.DrawRectangleRect(rect)
|
|
|
|
# erase the bottom/top line of the rectangle
|
|
dc.SetPen(wx.Pen(pc._pParent.GetGradientColourFrom()))
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
dc.DrawLine(rect.x, 2, rect.x + rect.width, 2)
|
|
else:
|
|
dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height - 1)
|
|
|
|
else:
|
|
|
|
# We dont draw a rectangle for non selected tabs, but only
|
|
# vertical line on the left
|
|
dc.SetPen(borderPen)
|
|
dc.DrawLine(posx + tabWidth, VERTICAL_BORDER_PADDING + 3, posx + tabWidth, tabHeight - 4)
|
|
|
|
|
|
# -----------------------------------
|
|
# Text and image drawing
|
|
# -----------------------------------
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
padding = pc.GetParent().GetPadding()
|
|
hasImage = pc._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if hasImage:
|
|
textOffset = 2*pc._pParent._nPadding + 16
|
|
else:
|
|
textOffset = pc._pParent._nPadding
|
|
|
|
textOffset += 2
|
|
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
# Set the text background to be like the vertical lines
|
|
dc.SetTextForeground(pc._pParent.GetNonActiveTabTextColour())
|
|
|
|
if hasImage:
|
|
|
|
imageXOffset = textOffset - 16 - padding
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
|
|
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
# Visual studio 2005 (VS8)
|
|
#------------------------------------------------------------------
|
|
class FNBRendererVC8(FNBRenderer):
|
|
"""
|
|
This class handles the drawing of tabs using the I{VC8} renderer.
|
|
"""
|
|
|
|
def __init__(self):
|
|
""" Default class constructor. """
|
|
|
|
FNBRenderer.__init__(self)
|
|
self._first = True
|
|
self._factor = 1
|
|
|
|
|
|
def DrawTabs(self, pageContainer, dc):
|
|
""" Draws all the tabs using VC8 style. Overloads The DrawTabs method in parent class. """
|
|
|
|
pc = pageContainer
|
|
|
|
if "__WXMAC__" in wx.PlatformInfo:
|
|
# Works well on MSW & GTK, however this lines should be skipped on MAC
|
|
if not pc._pagesInfoVec or pc._nFrom >= len(pc._pagesInfoVec):
|
|
pc.Hide()
|
|
return
|
|
|
|
# Get the text hight
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# Set the font for measuring the tab height
|
|
normalFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
# Calculate the number of rows required for drawing the tabs
|
|
rect = pc.GetClientRect()
|
|
|
|
# Set the maximum client size
|
|
pc.SetSizeHints(self.GetButtonsAreaLength(pc), tabHeight)
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
|
|
# Create brushes
|
|
backBrush = wx.Brush(pc._tabAreaColor)
|
|
noselBrush = wx.Brush(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE))
|
|
selBrush = wx.Brush(pc._activeTabColor)
|
|
size = pc.GetSize()
|
|
|
|
# Background
|
|
dc.SetTextBackground(pc.GetBackgroundColour())
|
|
dc.SetTextForeground(pc._activeTextColor)
|
|
|
|
# If border style is set, set the pen to be border pen
|
|
if pc.HasFlag(FNB_TABS_BORDER_SIMPLE):
|
|
dc.SetPen(borderPen)
|
|
else:
|
|
dc.SetPen(wx.TRANSPARENT_PEN)
|
|
|
|
lightFactor = (pc.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
|
|
|
|
# For VC8 style, we color the tab area in gradient coloring
|
|
lightcolour = LightColour(pc._tabAreaColor, lightFactor)
|
|
PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColor, lightcolour)
|
|
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawRectangle(0, 0, size.x, size.y)
|
|
|
|
# Take 3 bitmaps for the background for the buttons
|
|
|
|
mem_dc = wx.MemoryDC()
|
|
#---------------------------------------
|
|
# X button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetXPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._xBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
#---------------------------------------
|
|
# Right button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetRightButtonPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._rightBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
#---------------------------------------
|
|
# Left button
|
|
#---------------------------------------
|
|
rect = wx.Rect(self.GetLeftButtonPos(pc), 6, 16, 14)
|
|
mem_dc.SelectObject(self._leftBgBmp)
|
|
mem_dc.Blit(0, 0, rect.width, rect.height, dc, rect.x, rect.y)
|
|
mem_dc.SelectObject(wx.NullBitmap)
|
|
|
|
# We always draw the bottom/upper line of the tabs
|
|
# regradless the style
|
|
dc.SetPen(borderPen)
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
# Restore the pen
|
|
dc.SetPen(borderPen)
|
|
|
|
# Draw labels
|
|
dc.SetFont(boldFont)
|
|
|
|
# Update all the tabs from 0 to 'pc.self._nFrom' to be non visible
|
|
for i in xrange(pc._nFrom):
|
|
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Draw the visible tabs, in VC8 style, we draw them from right to left
|
|
vTabsInfo = self.NumberTabsCanFit(pc)
|
|
|
|
activeTabPosx = 0
|
|
activeTabWidth = 0
|
|
activeTabHeight = 0
|
|
|
|
for cur in xrange(len(vTabsInfo)-1, -1, -1):
|
|
|
|
# 'i' points to the index of the currently drawn tab
|
|
# in pc.GetPageInfoVector() vector
|
|
i = pc._nFrom + cur
|
|
dc.SetPen(borderPen)
|
|
dc.SetBrush((i==pc.GetSelection() and [selBrush] or [noselBrush])[0])
|
|
|
|
# Now set the font to the correct font
|
|
dc.SetFont((i==pc.GetSelection() and [boldFont] or [normalFont])[0])
|
|
|
|
# Add the padding to the tab width
|
|
# Tab width:
|
|
# +-----------------------------------------------------------+
|
|
# | PADDING | IMG | IMG_PADDING | TEXT | PADDING | x |PADDING |
|
|
# +-----------------------------------------------------------+
|
|
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
posx = vTabsInfo[cur].x
|
|
|
|
# By default we clean the tab region
|
|
# incase we use the VC8 style which requires
|
|
# the region, it will be filled by the function
|
|
# drawVc8Tab
|
|
pc._pagesInfoVec[i].GetRegion().Clear()
|
|
|
|
# Clean the 'x' buttn on the tab
|
|
# 'Clean' rectanlge is a rectangle with width or height
|
|
# with values lower than or equal to 0
|
|
pc._pagesInfoVec[i].GetXRect().SetSize(wx.Size(-1, -1))
|
|
|
|
# Draw the tab
|
|
# Incase we are drawing the active tab
|
|
# we need to redraw so it will appear on top
|
|
# of all other tabs
|
|
|
|
# when using the vc8 style, we keep the position of the active tab so we will draw it again later
|
|
if i == pc.GetSelection() and pc.HasFlag(FNB_VC8):
|
|
|
|
activeTabPosx = posx
|
|
activeTabWidth = tabWidth
|
|
activeTabHeight = tabHeight
|
|
|
|
else:
|
|
|
|
self.DrawTab(pc, dc, posx, i, tabWidth, tabHeight, pc._nTabXButtonStatus)
|
|
|
|
# Restore the text forground
|
|
dc.SetTextForeground(pc._activeTextColor)
|
|
|
|
# Update the tab position & size
|
|
pc._pagesInfoVec[i].SetPosition(wx.Point(posx, VERTICAL_BORDER_PADDING))
|
|
pc._pagesInfoVec[i].SetSize(wx.Size(tabWidth, tabHeight))
|
|
|
|
# Incase we are in VC8 style, redraw the active tab (incase it is visible)
|
|
if pc.GetSelection() >= pc._nFrom and pc.GetSelection() < pc._nFrom + len(vTabsInfo):
|
|
|
|
self.DrawTab(pc, dc, activeTabPosx, pc.GetSelection(), activeTabWidth, activeTabHeight, pc._nTabXButtonStatus)
|
|
|
|
# Update all tabs that can not fit into the screen as non-visible
|
|
for xx in xrange(pc._nFrom + len(vTabsInfo), len(pc._pagesInfoVec)):
|
|
|
|
pc._pagesInfoVec[xx].SetPosition(wx.Point(-1, -1))
|
|
pc._pagesInfoVec[xx].GetRegion().Clear()
|
|
|
|
# Draw the left/right/close buttons
|
|
# Left arrow
|
|
self.DrawLeftArrow(pc, dc)
|
|
self.DrawRightArrow(pc, dc)
|
|
self.DrawX(pc, dc)
|
|
self.DrawDropDownArrow(pc, dc)
|
|
|
|
|
|
def DrawTab(self, pageContainer, dc, posx, tabIdx, tabWidth, tabHeight, btnStatus):
|
|
""" Draws a tab using VC8 style. """
|
|
|
|
pc = pageContainer
|
|
borderPen = wx.Pen(pc._pParent.GetBorderColour())
|
|
tabPoints = [wx.Point() for ii in xrange(8)]
|
|
|
|
# If we draw the first tab or the active tab,
|
|
# we draw a full tab, else we draw a truncated tab
|
|
#
|
|
# X(2) X(3)
|
|
# X(1) X(4)
|
|
#
|
|
# X(5)
|
|
#
|
|
# X(0),(7) X(6)
|
|
#
|
|
#
|
|
|
|
tabPoints[0].x = (pc.HasFlag(FNB_BOTTOM) and [posx] or [posx+self._factor])[0]
|
|
tabPoints[0].y = (pc.HasFlag(FNB_BOTTOM) and [2] or [tabHeight - 3])[0]
|
|
|
|
tabPoints[1].x = tabPoints[0].x + tabHeight - VERTICAL_BORDER_PADDING - 3 - self._factor
|
|
tabPoints[1].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - (VERTICAL_BORDER_PADDING+2)] or [(VERTICAL_BORDER_PADDING+2)])[0]
|
|
|
|
tabPoints[2].x = tabPoints[1].x + 4
|
|
tabPoints[2].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[3].x = tabPoints[2].x + tabWidth - 2
|
|
tabPoints[3].y = (pc.HasFlag(FNB_BOTTOM) and [tabHeight - VERTICAL_BORDER_PADDING] or [VERTICAL_BORDER_PADDING])[0]
|
|
|
|
tabPoints[4].x = tabPoints[3].x + 1
|
|
tabPoints[4].y = (pc.HasFlag(FNB_BOTTOM) and [tabPoints[3].y - 1] or [tabPoints[3].y + 1])[0]
|
|
|
|
tabPoints[5].x = tabPoints[4].x + 1
|
|
tabPoints[5].y = (pc.HasFlag(FNB_BOTTOM) and [(tabPoints[4].y - 1)] or [tabPoints[4].y + 1])[0]
|
|
|
|
tabPoints[6].x = tabPoints[2].x + tabWidth
|
|
tabPoints[6].y = tabPoints[0].y
|
|
|
|
tabPoints[7].x = tabPoints[0].x
|
|
tabPoints[7].y = tabPoints[0].y
|
|
|
|
pc._pagesInfoVec[tabIdx].SetRegion(tabPoints)
|
|
|
|
# Draw the polygon
|
|
br = dc.GetBrush()
|
|
dc.SetBrush(wx.Brush((tabIdx == pc.GetSelection() and [pc._activeTabColor] or [pc._colorTo])[0]))
|
|
dc.SetPen(wx.Pen((tabIdx == pc.GetSelection() and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colorBorder])[0]))
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# Restore the brush
|
|
dc.SetBrush(br)
|
|
rect = pc.GetClientRect()
|
|
|
|
if tabIdx != pc.GetSelection() and not pc.HasFlag(FNB_BOTTOM):
|
|
|
|
# Top default tabs
|
|
dc.SetPen(wx.Pen(pc._pParent.GetBorderColour()))
|
|
lineY = rect.height
|
|
curPen = dc.GetPen()
|
|
curPen.SetWidth(1)
|
|
dc.SetPen(curPen)
|
|
dc.DrawLine(posx, lineY, posx+rect.width, lineY)
|
|
|
|
# Incase we are drawing the selected tab, we draw the border of it as well
|
|
# but without the bottom (upper line incase of wxBOTTOM)
|
|
if tabIdx == pc.GetSelection():
|
|
|
|
borderPen = wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW))
|
|
dc.SetPen(borderPen)
|
|
dc.SetBrush(wx.TRANSPARENT_BRUSH)
|
|
dc.DrawPolygon(tabPoints)
|
|
|
|
# Delete the bottom line (or the upper one, incase we use wxBOTTOM)
|
|
dc.SetPen(wx.WHITE_PEN)
|
|
dc.DrawLine(tabPoints[0].x, tabPoints[0].y, tabPoints[6].x, tabPoints[6].y)
|
|
|
|
self.FillVC8GradientColour(pc, dc, tabPoints, tabIdx == pc.GetSelection(), tabIdx)
|
|
|
|
# Draw a thin line to the right of the non-selected tab
|
|
if tabIdx != pc.GetSelection():
|
|
|
|
dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)))
|
|
dc.DrawLine(tabPoints[4].x-1, tabPoints[4].y, tabPoints[5].x-1, tabPoints[5].y)
|
|
dc.DrawLine(tabPoints[5].x-1, tabPoints[5].y, tabPoints[6].x-1, tabPoints[6].y)
|
|
|
|
# Text drawing offset from the left border of the
|
|
# rectangle
|
|
|
|
# The width of the images are 16 pixels
|
|
vc8ShapeLen = tabHeight - VERTICAL_BORDER_PADDING - 2
|
|
if pc.TabHasImage(tabIdx):
|
|
textOffset = 2*pc._pParent.GetPadding() + 16 + vc8ShapeLen
|
|
else:
|
|
textOffset = pc._pParent.GetPadding() + vc8ShapeLen
|
|
|
|
# Draw the image for the tab if any
|
|
imageYCoord = (pc.HasFlag(FNB_BOTTOM) and [6] or [8])[0]
|
|
|
|
if pc.TabHasImage(tabIdx):
|
|
|
|
imageXOffset = textOffset - 16 - pc._pParent.GetPadding()
|
|
pc._ImageList.Draw(pc._pagesInfoVec[tabIdx].GetImageIndex(), dc,
|
|
posx + imageXOffset, imageYCoord,
|
|
wx.IMAGELIST_DRAW_TRANSPARENT, True)
|
|
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
|
|
# if selected tab, draw text in bold
|
|
if tabIdx == pc.GetSelection():
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
|
|
dc.SetFont(boldFont)
|
|
dc.DrawText(pc.GetPageText(tabIdx), posx + textOffset, imageYCoord)
|
|
|
|
# draw 'x' on tab (if enabled)
|
|
if pc.HasFlag(FNB_X_ON_TAB) and tabIdx == pc.GetSelection():
|
|
|
|
textWidth, textHeight = dc.GetTextExtent(pc.GetPageText(tabIdx))
|
|
tabCloseButtonXCoord = posx + textOffset + textWidth + 1
|
|
|
|
# take a bitmap from the position of the 'x' button (the x on tab button)
|
|
# this bitmap will be used later to delete old buttons
|
|
tabCloseButtonYCoord = imageYCoord
|
|
x_rect = wx.Rect(tabCloseButtonXCoord, tabCloseButtonYCoord, 16, 16)
|
|
self._tabXBgBmp = self._GetBitmap(dc, x_rect, self._tabXBgBmp)
|
|
# Draw the tab
|
|
self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
|
|
|
|
|
|
def FillVC8GradientColour(self, pageContainer, dc, tabPoints, bSelectedTab, tabIdx):
|
|
""" Fills a tab with a gradient shading. """
|
|
|
|
# calculate gradient coefficients
|
|
pc = pageContainer
|
|
|
|
if self._first:
|
|
self._first = False
|
|
pc._colorTo = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 0)
|
|
pc._colorFrom = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 60)
|
|
|
|
col2 = pc._pParent.GetGradientColourTo()
|
|
col1 = pc._pParent.GetGradientColourFrom()
|
|
|
|
# If colorful tabs style is set, override the tab color
|
|
if pc.HasFlag(FNB_COLORFUL_TABS):
|
|
|
|
if not pc._pagesInfoVec[tabIdx].GetColour():
|
|
|
|
# First time, generate color, and keep it in the vector
|
|
tabColor = RandomColour()
|
|
pc._pagesInfoVec[tabIdx].SetColour(tabColor)
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
|
|
col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
|
|
col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
|
|
|
|
else:
|
|
|
|
col1 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 50)
|
|
col2 = LightColour(pc._pagesInfoVec[tabIdx].GetColour(), 80)
|
|
|
|
size = abs(tabPoints[2].y - tabPoints[0].y) - 1
|
|
|
|
rf, gf, bf = 0, 0, 0
|
|
rstep = float(col2.Red() - col1.Red())/float(size)
|
|
gstep = float(col2.Green() - col1.Green())/float(size)
|
|
bstep = float(col2.Blue() - col1.Blue())/float(size)
|
|
|
|
y = tabPoints[0].y
|
|
|
|
# If we are drawing the selected tab, we need also to draw a line
|
|
# from 0.tabPoints[0].x and tabPoints[6].x . end, we achieve this
|
|
# by drawing the rectangle with transparent brush
|
|
# the line under the selected tab will be deleted by the drwaing loop
|
|
if bSelectedTab:
|
|
self.DrawTabsLine(pc, dc)
|
|
|
|
while 1:
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
|
|
if y > tabPoints[0].y + size:
|
|
break
|
|
|
|
else:
|
|
|
|
if y < tabPoints[0].y - size:
|
|
break
|
|
|
|
currCol = wx.Colour(col1.Red() + rf, col1.Green() + gf, col1.Blue() + bf)
|
|
|
|
dc.SetPen((bSelectedTab and [wx.Pen(pc._activeTabColor)] or [wx.Pen(currCol)])[0])
|
|
startX = self.GetStartX(tabPoints, y, pc.GetParent().GetWindowStyleFlag())
|
|
endX = self.GetEndX(tabPoints, y, pc.GetParent().GetWindowStyleFlag())
|
|
dc.DrawLine(startX, y, endX, y)
|
|
|
|
# Draw the border using the 'edge' point
|
|
dc.SetPen(wx.Pen((bSelectedTab and [wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)] or [pc._colorBorder])[0]))
|
|
|
|
dc.DrawPoint(startX, y)
|
|
dc.DrawPoint(endX, y)
|
|
|
|
# Progress the color
|
|
rf += rstep
|
|
gf += gstep
|
|
bf += bstep
|
|
|
|
if pc.HasFlag(FNB_BOTTOM):
|
|
y = y + 1
|
|
else:
|
|
y = y - 1
|
|
|
|
|
|
def GetStartX(self, tabPoints, y, style):
|
|
""" Returns the x start position of a tab. """
|
|
|
|
x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
|
|
|
|
# We check the 3 points to the left
|
|
|
|
bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
|
|
match = False
|
|
|
|
if bBottomStyle:
|
|
|
|
for i in xrange(3):
|
|
|
|
if y >= tabPoints[i].y and y < tabPoints[i+1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i+1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i+1].y
|
|
match = True
|
|
break
|
|
|
|
else:
|
|
|
|
for i in xrange(3):
|
|
|
|
if y <= tabPoints[i].y and y > tabPoints[i+1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i+1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i+1].y
|
|
match = True
|
|
break
|
|
|
|
if not match:
|
|
return tabPoints[2].x
|
|
|
|
# According to the equation y = ax + b => x = (y-b)/a
|
|
# We know the first 2 points
|
|
|
|
if x2 == x1:
|
|
return x2
|
|
else:
|
|
a = (y2 - y1)/(x2 - x1)
|
|
|
|
b = y1 - ((y2 - y1)/(x2 - x1))*x1
|
|
|
|
if a == 0:
|
|
return int(x1)
|
|
|
|
x = (y - b)/a
|
|
|
|
return int(x)
|
|
|
|
|
|
def GetEndX(self, tabPoints, y, style):
|
|
""" Returns the x end position of a tab. """
|
|
|
|
x1, x2, y1, y2 = 0.0, 0.0, 0.0, 0.0
|
|
|
|
# We check the 3 points to the left
|
|
bBottomStyle = (style & FNB_BOTTOM and [True] or [False])[0]
|
|
match = False
|
|
|
|
if bBottomStyle:
|
|
|
|
for i in xrange(7, 3, -1):
|
|
|
|
if y >= tabPoints[i].y and y < tabPoints[i-1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i-1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i-1].y
|
|
match = True
|
|
break
|
|
|
|
else:
|
|
|
|
for i in xrange(7, 3, -1):
|
|
|
|
if y <= tabPoints[i].y and y > tabPoints[i-1].y:
|
|
|
|
x1 = tabPoints[i].x
|
|
x2 = tabPoints[i-1].x
|
|
y1 = tabPoints[i].y
|
|
y2 = tabPoints[i-1].y
|
|
match = True
|
|
break
|
|
|
|
if not match:
|
|
return tabPoints[3].x
|
|
|
|
# According to the equation y = ax + b => x = (y-b)/a
|
|
# We know the first 2 points
|
|
|
|
# Vertical line
|
|
if x1 == x2:
|
|
return int(x1)
|
|
|
|
a = (y2 - y1)/(x2 - x1)
|
|
b = y1 - ((y2 - y1)/(x2 - x1))*x1
|
|
|
|
if a == 0:
|
|
return int(x1)
|
|
|
|
x = (y - b)/a
|
|
|
|
return int(x)
|
|
|
|
|
|
def NumberTabsCanFit(self, pageContainer, fr=-1):
|
|
""" Returns the number of tabs that can fit in the visible area. """
|
|
|
|
pc = pageContainer
|
|
|
|
rect = pc.GetClientRect()
|
|
clientWidth = rect.width
|
|
|
|
# Empty results
|
|
vTabInfo = []
|
|
tabHeight = self.CalcTabHeight(pageContainer)
|
|
|
|
# The drawing starts from posx
|
|
posx = pc._pParent.GetPadding()
|
|
|
|
if fr < 0:
|
|
fr = pc._nFrom
|
|
|
|
for i in xrange(fr, len(pc._pagesInfoVec)):
|
|
|
|
vc8glitch = tabHeight + FNB_HEIGHT_SPACER
|
|
tabWidth = self.CalcTabWidth(pageContainer, i, tabHeight)
|
|
|
|
if posx + tabWidth + vc8glitch + self.GetButtonsAreaLength(pc) >= clientWidth:
|
|
break
|
|
|
|
# Add a result to the returned vector
|
|
tabRect = wx.Rect(posx, VERTICAL_BORDER_PADDING, tabWidth, tabHeight)
|
|
vTabInfo.append(tabRect)
|
|
|
|
# Advance posx
|
|
posx += tabWidth + FNB_HEIGHT_SPACER
|
|
|
|
return vTabInfo
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class FlatNotebook
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class FlatNotebook(wx.Panel):
|
|
"""
|
|
Display one or more windows in a notebook.
|
|
|
|
B{Events}:
|
|
- B{EVT_FLATNOTEBOOK_PAGE_CHANGING}: sent when the active
|
|
page in the notebook is changing
|
|
- B{EVT_FLATNOTEBOOK_PAGE_CHANGED}: sent when the active
|
|
page in the notebook has changed
|
|
- B{EVT_FLATNOTEBOOK_PAGE_CLOSING}: sent when a page in the
|
|
notebook is closing
|
|
- B{EVT_FLATNOTEBOOK_PAGE_CLOSED}: sent when a page in the
|
|
notebook has been closed
|
|
- B{EVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU}: sent when the user
|
|
clicks a tab in the notebook with the right mouse
|
|
button
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, size=wx.DefaultSize,
|
|
style=0, name="FlatNotebook"):
|
|
"""
|
|
Default class constructor.
|
|
|
|
All the parameters are as in wxPython class construction, except the
|
|
'style': this can be assigned to whatever combination of FNB_* styles.
|
|
|
|
"""
|
|
|
|
self._bForceSelection = False
|
|
self._nPadding = 6
|
|
self._nFrom = 0
|
|
style |= wx.TAB_TRAVERSAL
|
|
self._pages = None
|
|
self._windows = []
|
|
self._popupWin = None
|
|
|
|
wx.Panel.__init__(self, parent, id, pos, size, style)
|
|
|
|
self._pages = PageContainer(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, style)
|
|
|
|
self.Bind(wx.EVT_NAVIGATION_KEY, self.OnNavigationKey)
|
|
|
|
self.Init()
|
|
|
|
|
|
def Init(self):
|
|
""" Initializes all the class attributes. """
|
|
|
|
self._pages._colorBorder = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)
|
|
|
|
self._mainSizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.SetSizer(self._mainSizer)
|
|
|
|
# The child panels will inherit this bg color, so leave it at the default value
|
|
#self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_APPWORKSPACE))
|
|
|
|
# Set default page height
|
|
dc = wx.ClientDC(self)
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
# For GTK it seems that we must do this steps in order
|
|
# for the tabs will get the proper height on initialization
|
|
# on MSW, preforming these steps yields wierd results
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.FONTWEIGHT_BOLD)
|
|
dc.SetFont(boldFont)
|
|
|
|
height = dc.GetCharHeight()
|
|
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 8 pixels as padding
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
tabHeight += 6
|
|
|
|
self._pages.SetSizeHints(-1, tabHeight)
|
|
# Add the tab container to the sizer
|
|
self._mainSizer.Insert(0, self._pages, 0, wx.EXPAND)
|
|
self._mainSizer.Layout()
|
|
|
|
self._pages._nFrom = self._nFrom
|
|
self._pDropTarget = FNBDropTarget(self)
|
|
self.SetDropTarget(self._pDropTarget)
|
|
|
|
|
|
def SetActiveTabTextColour(self, textColour):
|
|
""" Sets the text colour for the active tab. """
|
|
|
|
self._pages._activeTextColor = textColour
|
|
|
|
|
|
def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
|
|
""" Handles the drop action from a DND operation. """
|
|
|
|
return self._pages.OnDropTarget(x, y, nTabPage, wnd_oldContainer)
|
|
|
|
|
|
def GetPreviousSelection(self):
|
|
""" Returns the previous selection. """
|
|
|
|
return self._pages._iPreviousActivePage
|
|
|
|
|
|
def AddPage(self, page, text, select=True, imageId=-1):
|
|
"""
|
|
Add a page to the L{FlatNotebook}.
|
|
|
|
@param page: Specifies the new page.
|
|
@param text: Specifies the text for the new page.
|
|
@param select: Specifies whether the page should be selected.
|
|
@param imageId: Specifies the optional image index for the new page.
|
|
|
|
Return value:
|
|
True if successful, False otherwise.
|
|
"""
|
|
|
|
# sanity check
|
|
if not page:
|
|
return False
|
|
|
|
# reparent the window to us
|
|
page.Reparent(self)
|
|
|
|
# Add tab
|
|
bSelected = select or len(self._windows) == 0
|
|
|
|
if bSelected:
|
|
|
|
bSelected = False
|
|
|
|
# Check for selection and send events
|
|
oldSelection = self._pages._iActivePage
|
|
tabIdx = len(self._windows)
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
|
|
event.SetSelection(tabIdx)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self)
|
|
|
|
if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
|
|
bSelected = True
|
|
|
|
curSel = self._pages.GetSelection()
|
|
|
|
if not self._pages.IsShown():
|
|
self._pages.Show()
|
|
|
|
self._pages.AddPage(text, bSelected, imageId)
|
|
self._windows.append(page)
|
|
|
|
self.Freeze()
|
|
|
|
# Check if a new selection was made
|
|
if bSelected:
|
|
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
if self.GetWindowStyleFlag() & FNB_BOTTOM:
|
|
|
|
self._mainSizer.Insert(0, page, 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
self._mainSizer.Add(page, 1, wx.EXPAND)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
else:
|
|
|
|
# Hide the page
|
|
page.Hide()
|
|
|
|
self.Thaw()
|
|
self._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
return True
|
|
|
|
|
|
def SetImageList(self, imageList):
|
|
""" Sets the image list for the page control. """
|
|
|
|
self._pages.SetImageList(imageList)
|
|
|
|
|
|
def AssignImageList(self, imageList):
|
|
""" Assigns the image list for the page control. """
|
|
|
|
self._pages.AssignImageList(imageList)
|
|
|
|
|
|
def GetImageList(self):
|
|
""" Returns the associated image list. """
|
|
|
|
return self._pages.GetImageList()
|
|
|
|
|
|
def InsertPage(self, indx, page, text, select=True, imageId=-1):
|
|
"""
|
|
Inserts a new page at the specified position.
|
|
|
|
@param indx: Specifies the position of the new page.
|
|
@param page: Specifies the new page.
|
|
@param text: Specifies the text for the new page.
|
|
@param select: Specifies whether the page should be selected.
|
|
@param imageId: Specifies the optional image index for the new page.
|
|
|
|
Return value:
|
|
True if successful, False otherwise.
|
|
"""
|
|
|
|
# sanity check
|
|
if not page:
|
|
return False
|
|
|
|
# reparent the window to us
|
|
page.Reparent(self)
|
|
|
|
if not self._windows:
|
|
|
|
self.AddPage(page, text, select, imageId)
|
|
return True
|
|
|
|
# Insert tab
|
|
bSelected = select or not self._windows
|
|
curSel = self._pages.GetSelection()
|
|
|
|
indx = max(0, min(indx, len(self._windows)))
|
|
|
|
if indx <= len(self._windows):
|
|
|
|
self._windows.insert(indx, page)
|
|
|
|
else:
|
|
|
|
self._windows.append(page)
|
|
|
|
if bSelected:
|
|
|
|
bSelected = False
|
|
|
|
# Check for selection and send events
|
|
oldSelection = self._pages._iActivePage
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetId())
|
|
event.SetSelection(indx)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self)
|
|
|
|
if not self.GetEventHandler().ProcessEvent(event) or event.IsAllowed() or len(self._windows) == 0:
|
|
bSelected = True
|
|
|
|
self._pages.InsertPage(indx, text, bSelected, imageId)
|
|
|
|
if indx <= curSel:
|
|
curSel = curSel + 1
|
|
|
|
self.Freeze()
|
|
|
|
# Check if a new selection was made
|
|
if bSelected:
|
|
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
self._pages.SetSelection(indx)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
else:
|
|
|
|
# Hide the page
|
|
page.Hide()
|
|
|
|
self.Thaw()
|
|
self._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
return True
|
|
|
|
|
|
def SetSelection(self, page):
|
|
"""
|
|
Sets the selection for the given page.
|
|
The call to this function generates the page changing events
|
|
"""
|
|
|
|
if page >= len(self._windows) or not self._windows:
|
|
return
|
|
|
|
# Support for disabed tabs
|
|
if not self._pages.GetEnabled(page) and len(self._windows) > 1 and not self._bForceSelection:
|
|
return
|
|
|
|
curSel = self._pages.GetSelection()
|
|
|
|
# program allows the page change
|
|
self.Freeze()
|
|
if curSel >= 0:
|
|
|
|
# Remove the window from the main sizer
|
|
self._mainSizer.Detach(self._windows[curSel])
|
|
self._windows[curSel].Hide()
|
|
|
|
if self.GetWindowStyleFlag() & FNB_BOTTOM:
|
|
|
|
self._mainSizer.Insert(0, self._windows[page], 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
self._mainSizer.Add(self._windows[page], 1, wx.EXPAND)
|
|
|
|
self._windows[page].Show()
|
|
self.Thaw()
|
|
|
|
self._mainSizer.Layout()
|
|
|
|
if page != self._pages._iActivePage:
|
|
# there is a real page changing
|
|
self._pages._iPreviousActivePage = self._pages._iActivePage
|
|
|
|
self._pages._iActivePage = page
|
|
self._pages.DoSetSelection(page)
|
|
|
|
|
|
def DeletePage(self, page):
|
|
"""
|
|
Deletes the specified page, and the associated window.
|
|
The call to this function generates the page changing events.
|
|
"""
|
|
|
|
if page >= len(self._windows) or page < 0:
|
|
return
|
|
|
|
# Fire a closing event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
|
|
event.SetSelection(page)
|
|
event.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
# The event handler allows it?
|
|
if not event.IsAllowed():
|
|
return
|
|
|
|
self.Freeze()
|
|
|
|
# Delete the requested page
|
|
pageRemoved = self._windows[page]
|
|
|
|
# If the page is the current window, remove it from the sizer
|
|
# as well
|
|
if page == self._pages.GetSelection():
|
|
self._mainSizer.Detach(pageRemoved)
|
|
|
|
# Remove it from the array as well
|
|
self._windows.pop(page)
|
|
|
|
# Now we can destroy it in wxWidgets use Destroy instead of delete
|
|
pageRemoved.Destroy()
|
|
|
|
self.Thaw()
|
|
|
|
self._pages.DoDeletePage(page)
|
|
self.Refresh()
|
|
|
|
# Fire a closed event
|
|
closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId())
|
|
closedEvent.SetSelection(page)
|
|
closedEvent.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(closedEvent)
|
|
|
|
|
|
def DeleteAllPages(self):
|
|
""" Deletes all the pages. """
|
|
|
|
if not self._windows:
|
|
return False
|
|
|
|
self.Freeze()
|
|
|
|
for page in self._windows:
|
|
page.Destroy()
|
|
|
|
self._windows = []
|
|
self.Thaw()
|
|
|
|
# Clear the container of the tabs as well
|
|
self._pages.DeleteAllPages()
|
|
return True
|
|
|
|
|
|
def GetCurrentPage(self):
|
|
""" Returns the currently selected notebook page or None. """
|
|
|
|
sel = self._pages.GetSelection()
|
|
if sel < 0:
|
|
return None
|
|
|
|
return self._windows[sel]
|
|
|
|
|
|
def GetPage(self, page):
|
|
""" Returns the window at the given page position, or None. """
|
|
|
|
if page >= len(self._windows):
|
|
return None
|
|
|
|
return self._windows[page]
|
|
|
|
|
|
def GetPageIndex(self, win):
|
|
""" Returns the index at which the window is found. """
|
|
|
|
try:
|
|
return self._windows.index(win)
|
|
except:
|
|
return -1
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns the currently selected page, or -1 if none was selected. """
|
|
|
|
return self._pages.GetSelection()
|
|
|
|
|
|
def AdvanceSelection(self, forward=True):
|
|
"""
|
|
Cycles through the tabs.
|
|
The call to this function generates the page changing events.
|
|
"""
|
|
|
|
self._pages.AdvanceSelection(forward)
|
|
|
|
|
|
def GetPageCount(self):
|
|
""" Returns the number of pages in the L{FlatNotebook} control. """
|
|
|
|
return self._pages.GetPageCount()
|
|
|
|
|
|
def OnNavigationKey(self, event):
|
|
""" Handles the wx.EVT_NAVIGATION_KEY event for L{FlatNotebook}. """
|
|
|
|
if event.IsWindowChange():
|
|
if len(self._windows) == 0:
|
|
return
|
|
# change pages
|
|
if self.HasFlag(FNB_SMART_TABS):
|
|
if not self._popupWin:
|
|
self._popupWin = TabNavigatorWindow(self)
|
|
self._popupWin.SetReturnCode(wx.ID_OK)
|
|
self._popupWin.ShowModal()
|
|
self._popupWin.Destroy()
|
|
self._popupWin = None
|
|
else:
|
|
# a dialog is already opened
|
|
self._popupWin.OnNavigationKey(event)
|
|
return
|
|
else:
|
|
# change pages
|
|
self.AdvanceSelection(event.GetDirection())
|
|
else:
|
|
# pass to the parent
|
|
if self.GetParent():
|
|
event.SetCurrentFocus(self)
|
|
self.GetParent().ProcessEvent(event)
|
|
|
|
|
|
def GetPageShapeAngle(self, page_index):
|
|
""" Returns the angle associated to a tab. """
|
|
|
|
if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
|
|
return None, False
|
|
|
|
result = self._pages._pagesInfoVec[page_index].GetTabAngle()
|
|
return result, True
|
|
|
|
|
|
def SetPageShapeAngle(self, page_index, angle):
|
|
""" Sets the angle associated to a tab. """
|
|
|
|
if page_index < 0 or page_index >= len(self._pages._pagesInfoVec):
|
|
return
|
|
|
|
if angle > 15:
|
|
return
|
|
|
|
self._pages._pagesInfoVec[page_index].SetTabAngle(angle)
|
|
|
|
|
|
def SetAllPagesShapeAngle(self, angle):
|
|
""" Sets the angle associated to all the tab. """
|
|
|
|
if angle > 15:
|
|
return
|
|
|
|
for ii in xrange(len(self._pages._pagesInfoVec)):
|
|
self._pages._pagesInfoVec[ii].SetTabAngle(angle)
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def GetPageBestSize(self):
|
|
""" Return the page best size. """
|
|
|
|
return self._pages.GetClientSize()
|
|
|
|
|
|
def SetPageText(self, page, text):
|
|
""" Sets the text for the given page. """
|
|
|
|
bVal = self._pages.SetPageText(page, text)
|
|
self._pages.Refresh()
|
|
|
|
return bVal
|
|
|
|
|
|
def SetPadding(self, padding):
|
|
"""
|
|
Sets the amount of space around each page's icon and label, in pixels.
|
|
NB: only the horizontal padding is considered.
|
|
"""
|
|
|
|
self._nPadding = padding.GetWidth()
|
|
|
|
|
|
def GetTabArea(self):
|
|
""" Returns the associated page. """
|
|
|
|
return self._pages
|
|
|
|
|
|
def GetPadding(self):
|
|
""" Returns the amount of space around each page's icon and label, in pixels. """
|
|
|
|
return self._nPadding
|
|
|
|
|
|
def SetWindowStyleFlag(self, style):
|
|
""" Sets the L{FlatNotebook} window style flags. """
|
|
|
|
wx.Panel.SetWindowStyleFlag(self, style)
|
|
renderer = self._pages._mgr.GetRenderer(self.GetWindowStyleFlag())
|
|
renderer._tabHeight = None
|
|
|
|
if self._pages:
|
|
|
|
# For changing the tab position (i.e. placing them top/bottom)
|
|
# refreshing the tab container is not enough
|
|
self.SetSelection(self._pages._iActivePage)
|
|
|
|
if not self._pages.HasFlag(FNB_HIDE_ON_SINGLE_TAB):
|
|
#For Redrawing the Tabs once you remove the Hide tyle
|
|
self._pages._ReShow()
|
|
|
|
|
|
def RemovePage(self, page):
|
|
""" Deletes the specified page, without deleting the associated window. """
|
|
|
|
if page >= len(self._windows):
|
|
return False
|
|
|
|
# Fire a closing event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSING, self.GetId())
|
|
event.SetSelection(page)
|
|
event.SetEventObject(self)
|
|
self.GetEventHandler().ProcessEvent(event)
|
|
|
|
# The event handler allows it?
|
|
if not event.IsAllowed():
|
|
return False
|
|
|
|
self.Freeze()
|
|
|
|
# Remove the requested page
|
|
pageRemoved = self._windows[page]
|
|
|
|
# If the page is the current window, remove it from the sizer
|
|
# as well
|
|
if page == self._pages.GetSelection():
|
|
self._mainSizer.Detach(pageRemoved)
|
|
|
|
# Remove it from the array as well
|
|
self._windows.pop(page)
|
|
self.Thaw()
|
|
|
|
self._pages.DoDeletePage(page)
|
|
|
|
return True
|
|
|
|
|
|
def SetRightClickMenu(self, menu):
|
|
""" Sets the popup menu associated to a right click on a tab. """
|
|
|
|
self._pages._pRightClickMenu = menu
|
|
|
|
|
|
def GetPageText(self, nPage):
|
|
""" Returns the tab caption. """
|
|
|
|
return self._pages.GetPageText(nPage)
|
|
|
|
|
|
def SetGradientColours(self, fr, to, border):
|
|
""" Sets the gradient colours for the tab. """
|
|
|
|
self._pages._colorFrom = fr
|
|
self._pages._colorTo = to
|
|
self._pages._colorBorder = border
|
|
|
|
|
|
def SetGradientColourFrom(self, fr):
|
|
""" Sets the starting colour for the gradient. """
|
|
|
|
self._pages._colorFrom = fr
|
|
|
|
|
|
def SetGradientColourTo(self, to):
|
|
""" Sets the ending colour for the gradient. """
|
|
|
|
self._pages._colorTo = to
|
|
|
|
|
|
def SetGradientColourBorder(self, border):
|
|
""" Sets the tab border colour. """
|
|
|
|
self._pages._colorBorder = border
|
|
|
|
|
|
def GetGradientColourFrom(self):
|
|
""" Gets first gradient colour. """
|
|
|
|
return self._pages._colorFrom
|
|
|
|
|
|
def GetGradientColourTo(self):
|
|
""" Gets second gradient colour. """
|
|
|
|
return self._pages._colorTo
|
|
|
|
|
|
def GetGradientColourBorder(self):
|
|
""" Gets the tab border colour. """
|
|
|
|
return self._pages._colorBorder
|
|
|
|
|
|
def GetBorderColour(self):
|
|
""" Returns the border colour. """
|
|
|
|
return self._pages._colorBorder
|
|
|
|
|
|
def GetActiveTabTextColour(self):
|
|
""" Get the active tab text colour. """
|
|
|
|
return self._pages._activeTextColor
|
|
|
|
|
|
def SetPageImage(self, page, image):
|
|
"""
|
|
Sets the image index for the given page. Image is an index into the
|
|
image list which was set with SetImageList.
|
|
"""
|
|
|
|
self._pages.SetPageImage(page, image)
|
|
|
|
|
|
def GetPageImage(self, nPage):
|
|
"""
|
|
Returns the image index for the given page. Image is an index into the
|
|
image list which was set with SetImageList.
|
|
"""
|
|
|
|
return self._pages.GetPageImage(nPage)
|
|
|
|
|
|
def GetEnabled(self, page):
|
|
""" Returns whether a tab is enabled or not. """
|
|
|
|
return self._pages.GetEnabled(page)
|
|
|
|
|
|
def Enable(self, page, enabled=True):
|
|
""" Enables or disables a tab. """
|
|
|
|
if page >= len(self._windows):
|
|
return
|
|
|
|
self._windows[page].Enable(enabled)
|
|
self._pages.Enable(page, enabled)
|
|
|
|
|
|
def GetNonActiveTabTextColour(self):
|
|
""" Returns the non active tabs text colour. """
|
|
|
|
return self._pages._nonActiveTextColor
|
|
|
|
|
|
def SetNonActiveTabTextColour(self, color):
|
|
""" Sets the non active tabs text colour. """
|
|
|
|
self._pages._nonActiveTextColor = color
|
|
|
|
|
|
def SetTabAreaColour(self, color):
|
|
""" Sets the area behind the tabs colour. """
|
|
|
|
self._pages._tabAreaColor = color
|
|
|
|
|
|
def GetTabAreaColour(self):
|
|
""" Returns the area behind the tabs colour. """
|
|
|
|
return self._pages._tabAreaColor
|
|
|
|
|
|
def SetActiveTabColour(self, color):
|
|
""" Sets the active tab colour. """
|
|
|
|
self._pages._activeTabColor = color
|
|
|
|
|
|
def GetActiveTabColour(self):
|
|
""" Returns the active tab colour. """
|
|
|
|
return self._pages._activeTabColor
|
|
|
|
|
|
# ---------------------------------------------------------------------------- #
|
|
# Class PageContainer
|
|
# Acts as a container for the pages you add to FlatNotebook
|
|
# ---------------------------------------------------------------------------- #
|
|
|
|
class PageContainer(wx.Panel):
|
|
"""
|
|
This class acts as a container for the pages you add to L{FlatNotebook}.
|
|
"""
|
|
|
|
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
|
|
size=wx.DefaultSize, style=0):
|
|
""" Default class constructor. """
|
|
|
|
self._ImageList = None
|
|
self._iActivePage = -1
|
|
self._pDropTarget = None
|
|
self._nLeftClickZone = FNB_NOWHERE
|
|
self._iPreviousActivePage = -1
|
|
|
|
self._pRightClickMenu = None
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
self._pParent = parent
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
|
|
self._pagesInfoVec = []
|
|
|
|
self._colorTo = wx.SystemSettings_GetColour(wx.SYS_COLOUR_ACTIVECAPTION)
|
|
self._colorFrom = wx.WHITE
|
|
self._activeTabColor = wx.WHITE
|
|
self._activeTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT)
|
|
self._nonActiveTextColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNTEXT)
|
|
self._tabAreaColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE)
|
|
|
|
self._nFrom = 0
|
|
self._isdragging = False
|
|
|
|
# Set default page height, this is done according to the system font
|
|
memDc = wx.MemoryDC()
|
|
memDc.SelectObject(wx.EmptyBitmap(1,1))
|
|
|
|
if "__WXGTK__" in wx.PlatformInfo:
|
|
boldFont = wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT)
|
|
boldFont.SetWeight(wx.BOLD)
|
|
memDc.SetFont(boldFont)
|
|
|
|
height = memDc.GetCharHeight()
|
|
tabHeight = height + FNB_HEIGHT_SPACER # We use 10 pixels as padding
|
|
|
|
wx.Panel.__init__(self, parent, id, pos, wx.Size(size.x, tabHeight),
|
|
style|wx.NO_BORDER|wx.NO_FULL_REPAINT_ON_RESIZE)
|
|
|
|
self._pDropTarget = FNBDropTarget(self)
|
|
self.SetDropTarget(self._pDropTarget)
|
|
self._mgr = FNBRendererMgr()
|
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
|
|
self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
|
|
self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
|
|
self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown)
|
|
self.Bind(wx.EVT_MOTION, self.OnMouseMove)
|
|
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
|
|
self.Bind(wx.EVT_LEAVE_WINDOW, self.OnMouseLeave)
|
|
self.Bind(wx.EVT_ENTER_WINDOW, self.OnMouseEnterWindow)
|
|
self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
|
|
|
|
|
|
def OnEraseBackground(self, event):
|
|
""" Handles the wx.EVT_ERASE_BACKGROUND event for L{PageContainer} (does nothing)."""
|
|
|
|
pass
|
|
|
|
|
|
def _ReShow(self):
|
|
""" Handles the Redraw of the tabs when the FNB_HIDE_ON_SINGLE_TAB has been removed """
|
|
self.Show()
|
|
self.GetParent()._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
|
|
def OnPaint(self, event):
|
|
""" Handles the wx.EVT_PAINT event for L{PageContainer}."""
|
|
|
|
dc = wx.BufferedPaintDC(self)
|
|
renderer = self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag())
|
|
renderer.DrawTabs(self, dc)
|
|
|
|
if self.HasFlag(FNB_HIDE_ON_SINGLE_TAB) and len(self._pagesInfoVec) <= 1:
|
|
self.Hide()
|
|
self.GetParent()._mainSizer.Layout()
|
|
self.Refresh()
|
|
|
|
|
|
def AddPage(self, caption, selected=True, imgindex=-1):
|
|
"""
|
|
Add a page to the L{FlatNotebook}.
|
|
|
|
@param window: Specifies the new page.
|
|
@param caption: Specifies the text for the new page.
|
|
@param selected: Specifies whether the page should be selected.
|
|
@param imgindex: Specifies the optional image index for the new page.
|
|
|
|
Return value:
|
|
True if successful, False otherwise.
|
|
"""
|
|
|
|
if selected:
|
|
|
|
self._iPreviousActivePage = self._iActivePage
|
|
self._iActivePage = len(self._pagesInfoVec)
|
|
|
|
# Create page info and add it to the vector
|
|
pageInfo = PageInfo(caption, imgindex)
|
|
self._pagesInfoVec.append(pageInfo)
|
|
self.Refresh()
|
|
|
|
|
|
def InsertPage(self, indx, text, selected=True, imgindex=-1):
|
|
"""
|
|
Inserts a new page at the specified position.
|
|
|
|
@param indx: Specifies the position of the new page.
|
|
@param page: Specifies the new page.
|
|
@param text: Specifies the text for the new page.
|
|
@param select: Specifies whether the page should be selected.
|
|
@param imgindex: Specifies the optional image index for the new page.
|
|
|
|
Return value:
|
|
True if successful, False otherwise.
|
|
"""
|
|
|
|
if selected:
|
|
|
|
self._iPreviousActivePage = self._iActivePage
|
|
self._iActivePage = len(self._pagesInfoVec)
|
|
|
|
self._pagesInfoVec.insert(indx, PageInfo(text, imgindex))
|
|
|
|
self.Refresh()
|
|
return True
|
|
|
|
|
|
def OnSize(self, event):
|
|
""" Handles the wx.EVT_SIZE events for L{PageContainer}. """
|
|
|
|
# When resizing the control, try to fit to screen as many tabs as we can
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
renderer = self._mgr.GetRenderer(style)
|
|
|
|
fr = 0
|
|
page = self.GetSelection()
|
|
|
|
for fr in xrange(self._nFrom):
|
|
vTabInfo = renderer.NumberTabsCanFit(self, fr)
|
|
if page - fr >= len(vTabInfo):
|
|
continue
|
|
break
|
|
|
|
self._nFrom = fr
|
|
|
|
self.Refresh() # Call on paint
|
|
event.Skip()
|
|
|
|
|
|
def OnMiddleDown(self, event):
|
|
""" Handles the wx.EVT_MIDDLE_DOWN events for L{PageContainer}. """
|
|
|
|
# Test if this style is enabled
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
|
|
if not style & FNB_MOUSE_MIDDLE_CLOSES_TABS:
|
|
return
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_TAB:
|
|
self.DeletePage(tabIdx)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnRightDown(self, event):
|
|
""" Handles the wx.EVT_RIGHT_DOWN events for L{PageContainer}. """
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where in [FNB_TAB, FNB_TAB_X]:
|
|
|
|
if self._pagesInfoVec[tabIdx].GetEnabled():
|
|
# Fire events and eventually (if allowed) change selection
|
|
self.FireEvent(tabIdx)
|
|
|
|
# send a message to popup a custom menu
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CONTEXT_MENU, self.GetParent().GetId())
|
|
event.SetSelection(tabIdx)
|
|
event.SetOldSelection(self._iActivePage)
|
|
event.SetEventObject(self.GetParent())
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
if self._pRightClickMenu:
|
|
self.PopupMenu(self._pRightClickMenu)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnLeftDown(self, event):
|
|
""" Handles the wx.EVT_LEFT_DOWN events for L{PageContainer}. """
|
|
|
|
# Reset buttons status
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
self._nLeftClickZone, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if self._nLeftClickZone == FNB_DROP_DOWN_ARROW:
|
|
self._nArrowDownButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_LEFT_ARROW:
|
|
self._nLeftButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_RIGHT_ARROW:
|
|
self._nRightButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_X:
|
|
self._nXButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
elif self._nLeftClickZone == FNB_TAB_X:
|
|
self._nTabXButtonStatus = FNB_BTN_PRESSED
|
|
self.Refresh()
|
|
|
|
elif self._nLeftClickZone == FNB_TAB:
|
|
|
|
if self._iActivePage != tabIdx:
|
|
|
|
# In case the tab is disabled, we dont allow to choose it
|
|
if self._pagesInfoVec[tabIdx].GetEnabled():
|
|
self.FireEvent(tabIdx)
|
|
|
|
|
|
def RotateLeft(self):
|
|
|
|
if self._nFrom == 0:
|
|
return
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nLeftButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_HOVER
|
|
|
|
# We scroll left with bulks of 5
|
|
scrollLeft = self.GetNumTabsCanScrollLeft()
|
|
|
|
self._nFrom -= scrollLeft
|
|
if self._nFrom < 0:
|
|
self._nFrom = 0
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def RotateRight(self):
|
|
|
|
if self._nFrom >= len(self._pagesInfoVec) - 1:
|
|
return
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nRightButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nRightButtonStatus = FNB_BTN_HOVER
|
|
|
|
# Check if the right most tab is visible, if it is
|
|
# don't rotate right anymore
|
|
if self._pagesInfoVec[len(self._pagesInfoVec)-1].GetPosition() != wx.Point(-1, -1):
|
|
return
|
|
|
|
self._nFrom += 1
|
|
self.Refresh()
|
|
|
|
|
|
def OnLeftUp(self, event):
|
|
""" Handles the wx.EVT_LEFT_UP events for L{PageContainer}. """
|
|
|
|
# forget the zone that was initially clicked
|
|
self._nLeftClickZone = FNB_NOWHERE
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_LEFT_ARROW:
|
|
self.RotateLeft()
|
|
|
|
elif where == FNB_RIGHT_ARROW:
|
|
self.RotateRight()
|
|
|
|
elif where == FNB_X:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nXButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nXButtonStatus = FNB_BTN_HOVER
|
|
|
|
self.DeletePage(self._iActivePage)
|
|
|
|
elif where == FNB_TAB_X:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nTabXButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nTabXButtonStatus = FNB_BTN_HOVER
|
|
|
|
self.DeletePage(self._iActivePage)
|
|
|
|
elif where == FNB_DROP_DOWN_ARROW:
|
|
|
|
# Make sure that the button was pressed before
|
|
if self._nArrowDownButtonStatus != FNB_BTN_PRESSED:
|
|
return
|
|
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
# Refresh the button status
|
|
renderer = self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag())
|
|
dc = wx.ClientDC(self)
|
|
renderer.DrawDropDownArrow(self, dc)
|
|
|
|
self.PopupTabsMenu()
|
|
|
|
event.Skip()
|
|
|
|
|
|
def HitTest(self, pt):
|
|
"""
|
|
HitTest method for L{PageContainer}.
|
|
Returns the flag (if any) and the hit page (if any).
|
|
"""
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(style)
|
|
|
|
fullrect = self.GetClientRect()
|
|
btnLeftPos = render.GetLeftButtonPos(self)
|
|
btnRightPos = render.GetRightButtonPos(self)
|
|
btnXPos = render.GetXPos(self)
|
|
|
|
tabIdx = -1
|
|
|
|
if len(self._pagesInfoVec) == 0:
|
|
return FNB_NOWHERE, tabIdx
|
|
|
|
rect = wx.Rect(btnXPos, 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return (style & FNB_NO_X_BUTTON and [FNB_NOWHERE] or [FNB_X])[0], tabIdx
|
|
|
|
rect = wx.Rect(btnRightPos, 8, 16, 16)
|
|
if style & FNB_DROPDOWN_TABS_LIST:
|
|
rect = wx.Rect(render.GetDropArrowButtonPos(self), 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return FNB_DROP_DOWN_ARROW, tabIdx
|
|
|
|
if rect.Contains(pt):
|
|
return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_RIGHT_ARROW])[0], tabIdx
|
|
|
|
rect = wx.Rect(btnLeftPos, 8, 16, 16)
|
|
if rect.Contains(pt):
|
|
return (style & FNB_NO_NAV_BUTTONS and [FNB_NOWHERE] or [FNB_LEFT_ARROW])[0], tabIdx
|
|
|
|
# Test whether a left click was made on a tab
|
|
bFoundMatch = False
|
|
|
|
for cur in xrange(self._nFrom, len(self._pagesInfoVec)):
|
|
|
|
pgInfo = self._pagesInfoVec[cur]
|
|
|
|
if pgInfo.GetPosition() == wx.Point(-1, -1):
|
|
continue
|
|
|
|
if style & FNB_X_ON_TAB and cur == self.GetSelection():
|
|
# 'x' button exists on a tab
|
|
if self._pagesInfoVec[cur].GetXRect().Contains(pt):
|
|
return FNB_TAB_X, cur
|
|
|
|
if style & FNB_VC8:
|
|
|
|
if self._pagesInfoVec[cur].GetRegion().Contains(pt.x, pt.y):
|
|
if bFoundMatch or cur == self.GetSelection():
|
|
return FNB_TAB, cur
|
|
|
|
tabIdx = cur
|
|
bFoundMatch = True
|
|
|
|
else:
|
|
|
|
tabRect = wx.Rect(pgInfo.GetPosition().x, pgInfo.GetPosition().y,
|
|
pgInfo.GetSize().x, pgInfo.GetSize().y)
|
|
|
|
if tabRect.Contains(pt):
|
|
# We have a match
|
|
return FNB_TAB, cur
|
|
|
|
if bFoundMatch:
|
|
return FNB_TAB, tabIdx
|
|
|
|
if self._isdragging:
|
|
# We are doing DND, so check also the region outside the tabs
|
|
# try before the first tab
|
|
pgInfo = self._pagesInfoVec[0]
|
|
tabRect = wx.Rect(0, pgInfo.GetPosition().y, pgInfo.GetPosition().x, self.GetParent().GetSize().y)
|
|
if tabRect.Contains(pt):
|
|
return FNB_TAB, 0
|
|
|
|
# try after the last tab
|
|
pgInfo = self._pagesInfoVec[-1]
|
|
startpos = pgInfo.GetPosition().x+pgInfo.GetSize().x
|
|
tabRect = wx.Rect(startpos, pgInfo.GetPosition().y, fullrect.width-startpos, self.GetParent().GetSize().y)
|
|
|
|
if tabRect.Contains(pt):
|
|
return FNB_TAB, len(self._pagesInfoVec)
|
|
|
|
# Default
|
|
return FNB_NOWHERE, -1
|
|
|
|
|
|
def SetSelection(self, page):
|
|
""" Sets the selected page. """
|
|
|
|
book = self.GetParent()
|
|
book.SetSelection(page)
|
|
self.DoSetSelection(page)
|
|
|
|
|
|
def DoSetSelection(self, page):
|
|
""" Does the actual selection of a page. """
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
#! fix for tabfocus
|
|
da_page = self._pParent.GetPage(page)
|
|
|
|
if da_page != None:
|
|
da_page.SetFocus()
|
|
|
|
if not self.IsTabVisible(page):
|
|
# Try to remove one tab from start and try again
|
|
|
|
if not self.CanFitToScreen(page):
|
|
|
|
if self._nFrom > page:
|
|
self._nFrom = page
|
|
else:
|
|
while self._nFrom < page:
|
|
self._nFrom += 1
|
|
if self.CanFitToScreen(page):
|
|
break
|
|
|
|
self.Refresh()
|
|
|
|
|
|
def DeletePage(self, page):
|
|
""" Delete the specified page from L{FlatNotebook}. """
|
|
|
|
book = self.GetParent()
|
|
book.DeletePage(page)
|
|
book.Refresh()
|
|
|
|
|
|
def IsTabVisible(self, page):
|
|
""" Returns whether a tab is visible or not. """
|
|
|
|
iLastVisiblePage = self.GetLastVisibleTab()
|
|
return page <= iLastVisiblePage and page >= self._nFrom
|
|
|
|
|
|
def DoDeletePage(self, page):
|
|
""" Does the actual page deletion. """
|
|
|
|
# Remove the page from the vector
|
|
book = self.GetParent()
|
|
self._pagesInfoVec.pop(page)
|
|
|
|
# Thanks to Yiaanis AKA Mandrav
|
|
if self._iActivePage >= page:
|
|
self._iActivePage = self._iActivePage - 1
|
|
self._iPreviousActivePage = -1
|
|
|
|
# The delete page was the last first on the array,
|
|
# but the book still has more pages, so we set the
|
|
# active page to be the first one (0)
|
|
if self._iActivePage < 0 and len(self._pagesInfoVec) > 0:
|
|
self._iActivePage = 0
|
|
self._iPreviousActivePage = -1
|
|
|
|
# Refresh the tabs
|
|
if self._iActivePage >= 0:
|
|
|
|
book._bForceSelection = True
|
|
|
|
# Check for selection and send event
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
|
|
event.SetSelection(self._iActivePage)
|
|
event.SetOldSelection(self._iPreviousActivePage)
|
|
event.SetEventObject(self.GetParent())
|
|
|
|
book.SetSelection(self._iActivePage)
|
|
book._bForceSelection = False
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(self._iPreviousActivePage)
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
if not self._pagesInfoVec:
|
|
# Erase the page container drawings
|
|
dc = wx.ClientDC(self)
|
|
dc.Clear()
|
|
|
|
|
|
def DeleteAllPages(self):
|
|
""" Deletes all the pages. """
|
|
|
|
self._iActivePage = -1
|
|
self._iPreviousActivePage = -1
|
|
self._nFrom = 0
|
|
self._pagesInfoVec = []
|
|
|
|
# Erase the page container drawings
|
|
dc = wx.ClientDC(self)
|
|
dc.Clear()
|
|
|
|
|
|
def OnMouseMove(self, event):
|
|
""" Handles the wx.EVT_MOTION for L{PageContainer}. """
|
|
|
|
if self._pagesInfoVec and self.IsShown():
|
|
|
|
xButtonStatus = self._nXButtonStatus
|
|
xTabButtonStatus = self._nTabXButtonStatus
|
|
rightButtonStatus = self._nRightButtonStatus
|
|
leftButtonStatus = self._nLeftButtonStatus
|
|
dropDownButtonStatus = self._nArrowDownButtonStatus
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_X:
|
|
if event.LeftIsDown():
|
|
|
|
self._nXButtonStatus = (self._nLeftClickZone==FNB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nXButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_DROP_DOWN_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nArrowDownButtonStatus = (self._nLeftClickZone==FNB_DROP_DOWN_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nArrowDownButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_TAB_X:
|
|
if event.LeftIsDown():
|
|
|
|
self._nTabXButtonStatus = (self._nLeftClickZone==FNB_TAB_X and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nTabXButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_RIGHT_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nRightButtonStatus = (self._nLeftClickZone==FNB_RIGHT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nRightButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_LEFT_ARROW:
|
|
if event.LeftIsDown():
|
|
|
|
self._nLeftButtonStatus = (self._nLeftClickZone==FNB_LEFT_ARROW and [FNB_BTN_PRESSED] or [FNB_BTN_NONE])[0]
|
|
|
|
else:
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_HOVER
|
|
|
|
elif where == FNB_TAB:
|
|
# Call virtual method for showing tooltip
|
|
self.ShowTabTooltip(tabIdx)
|
|
|
|
if not self.GetEnabled(tabIdx):
|
|
# Set the cursor to be 'No-entry'
|
|
wx.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
|
|
|
|
# Support for drag and drop
|
|
if event.Dragging() and not (style & FNB_NODRAG):
|
|
|
|
self._isdragging = True
|
|
draginfo = FNBDragInfo(self, tabIdx)
|
|
drginfo = cPickle.dumps(draginfo)
|
|
dataobject = wx.CustomDataObject(wx.CustomDataFormat("FlatNotebook"))
|
|
dataobject.SetData(drginfo)
|
|
dragSource = FNBDropSource(self)
|
|
dragSource.SetData(dataobject)
|
|
dragSource.DoDragDrop(wx.Drag_DefaultMove)
|
|
|
|
bRedrawX = self._nXButtonStatus != xButtonStatus
|
|
bRedrawRight = self._nRightButtonStatus != rightButtonStatus
|
|
bRedrawLeft = self._nLeftButtonStatus != leftButtonStatus
|
|
bRedrawTabX = self._nTabXButtonStatus != xTabButtonStatus
|
|
bRedrawDropArrow = self._nArrowDownButtonStatus != dropDownButtonStatus
|
|
|
|
render = self._mgr.GetRenderer(style)
|
|
|
|
if (bRedrawX or bRedrawRight or bRedrawLeft or bRedrawTabX or bRedrawDropArrow):
|
|
|
|
dc = wx.ClientDC(self)
|
|
|
|
if bRedrawX:
|
|
|
|
render.DrawX(self, dc)
|
|
|
|
if bRedrawLeft:
|
|
|
|
render.DrawLeftArrow(self, dc)
|
|
|
|
if bRedrawRight:
|
|
|
|
render.DrawRightArrow(self, dc)
|
|
|
|
if bRedrawTabX:
|
|
|
|
render.DrawTabX(self, dc, self._pagesInfoVec[tabIdx].GetXRect(), tabIdx, self._nTabXButtonStatus)
|
|
|
|
if bRedrawDropArrow:
|
|
|
|
render.DrawDropDownArrow(self, dc)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def GetLastVisibleTab(self):
|
|
""" Returns the last visible tab. """
|
|
|
|
if self._nFrom < 0:
|
|
return -1
|
|
|
|
ii = 0
|
|
|
|
for ii in xrange(self._nFrom, len(self._pagesInfoVec)):
|
|
|
|
if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
|
|
break
|
|
|
|
return ii-1
|
|
|
|
|
|
def GetNumTabsCanScrollLeft(self):
|
|
""" Returns the number of tabs than can be scrolled left. """
|
|
|
|
if self._nFrom - 1 >= 0:
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
def IsDefaultTabs(self):
|
|
""" Returns whether a tab has a default style. """
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
res = (style & FNB_VC71) or (style & FNB_FANCY_TABS) or (style & FNB_VC8)
|
|
return not res
|
|
|
|
|
|
def AdvanceSelection(self, bForward=True):
|
|
"""
|
|
Cycles through the tabs.
|
|
The call to this function generates the page changing events.
|
|
"""
|
|
|
|
nSel = self.GetSelection()
|
|
|
|
if nSel < 0:
|
|
return
|
|
|
|
nMax = self.GetPageCount() - 1
|
|
|
|
if bForward:
|
|
newSelection = (nSel == nMax and [0] or [nSel + 1])[0]
|
|
else:
|
|
newSelection = (nSel == 0 and [nMax] or [nSel - 1])[0]
|
|
|
|
if not self._pagesInfoVec[newSelection].GetEnabled():
|
|
return
|
|
|
|
self.FireEvent(newSelection)
|
|
|
|
|
|
def OnMouseLeave(self, event):
|
|
""" Handles the wx.EVT_LEAVE_WINDOW event for L{PageContainer}. """
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nTabXButtonStatus = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(style)
|
|
|
|
dc = wx.ClientDC(self)
|
|
|
|
render.DrawX(self, dc)
|
|
render.DrawLeftArrow(self, dc)
|
|
render.DrawRightArrow(self, dc)
|
|
|
|
selection = self.GetSelection()
|
|
|
|
if selection == -1:
|
|
event.Skip()
|
|
return
|
|
|
|
if not self.IsTabVisible(selection):
|
|
if selection == len(self._pagesInfoVec) - 1:
|
|
if not self.CanFitToScreen(selection):
|
|
event.Skip()
|
|
return
|
|
else:
|
|
event.Skip()
|
|
return
|
|
|
|
render.DrawTabX(self, dc, self._pagesInfoVec[selection].GetXRect(), selection, self._nTabXButtonStatus)
|
|
|
|
event.Skip()
|
|
|
|
|
|
def OnMouseEnterWindow(self, event):
|
|
""" Handles the wx.EVT_ENTER_WINDOW event for L{PageContainer}. """
|
|
|
|
self._nLeftButtonStatus = FNB_BTN_NONE
|
|
self._nXButtonStatus = FNB_BTN_NONE
|
|
self._nRightButtonStatus = FNB_BTN_NONE
|
|
self._nLeftClickZone = FNB_BTN_NONE
|
|
self._nArrowDownButtonStatus = FNB_BTN_NONE
|
|
|
|
event.Skip()
|
|
|
|
|
|
def ShowTabTooltip(self, tabIdx):
|
|
""" Shows a tab tooltip. """
|
|
|
|
pWindow = self._pParent.GetPage(tabIdx)
|
|
|
|
if pWindow:
|
|
pToolTip = pWindow.GetToolTip()
|
|
if pToolTip and pToolTip.GetWindow() == pWindow:
|
|
self.SetToolTipString(pToolTip.GetTip())
|
|
|
|
|
|
def SetPageImage(self, page, imgindex):
|
|
""" Sets the image index associated to a page. """
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
|
|
self._pagesInfoVec[page].SetImageIndex(imgindex)
|
|
self.Refresh()
|
|
|
|
|
|
def GetPageImage(self, page):
|
|
""" Returns the image index associated to a page. """
|
|
|
|
if page < len(self._pagesInfoVec):
|
|
|
|
return self._pagesInfoVec[page].GetImageIndex()
|
|
|
|
return -1
|
|
|
|
|
|
def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
|
|
""" Handles the drop action from a DND operation. """
|
|
|
|
# Disable drag'n'drop for disabled tab
|
|
if not wnd_oldContainer._pagesInfoVec[nTabPage].GetEnabled():
|
|
return wx.DragCancel
|
|
|
|
self._isdragging = True
|
|
oldContainer = wnd_oldContainer
|
|
nIndex = -1
|
|
|
|
where, nIndex = self.HitTest(wx.Point(x, y))
|
|
|
|
oldNotebook = oldContainer.GetParent()
|
|
newNotebook = self.GetParent()
|
|
|
|
if oldNotebook == newNotebook:
|
|
|
|
if nTabPage >= 0:
|
|
|
|
if where == FNB_TAB:
|
|
self.MoveTabPage(nTabPage, nIndex)
|
|
|
|
elif self.GetParent().GetWindowStyleFlag() & FNB_ALLOW_FOREIGN_DND:
|
|
|
|
if wx.Platform in ["__WXMSW__", "__WXGTK__", "__WXMAC__"]:
|
|
if nTabPage >= 0:
|
|
|
|
window = oldNotebook.GetPage(nTabPage)
|
|
|
|
if window:
|
|
where, nIndex = newNotebook._pages.HitTest(wx.Point(x, y))
|
|
caption = oldContainer.GetPageText(nTabPage)
|
|
imageindex = oldContainer.GetPageImage(nTabPage)
|
|
oldNotebook.RemovePage(nTabPage)
|
|
window.Reparent(newNotebook)
|
|
|
|
if imageindex >= 0:
|
|
|
|
bmp = oldNotebook.GetImageList().GetIcon(imageindex)
|
|
newImageList = newNotebook.GetImageList()
|
|
|
|
if not newImageList:
|
|
xbmp, ybmp = bmp.GetWidth(), bmp.GetHeight()
|
|
newImageList = wx.ImageList(xbmp, ybmp)
|
|
imageindex = 0
|
|
else:
|
|
imageindex = newImageList.GetImageCount()
|
|
|
|
newImageList.AddIcon(bmp)
|
|
newNotebook.SetImageList(newImageList)
|
|
|
|
newNotebook.InsertPage(nIndex, window, caption, True, imageindex)
|
|
|
|
self._isdragging = False
|
|
|
|
return wx.DragMove
|
|
|
|
|
|
def MoveTabPage(self, nMove, nMoveTo):
|
|
""" Moves a tab inside the same L{FlatNotebook}. """
|
|
|
|
if nMove == nMoveTo:
|
|
return
|
|
|
|
elif nMoveTo < len(self._pParent._windows):
|
|
nMoveTo = nMoveTo + 1
|
|
|
|
self._pParent.Freeze()
|
|
|
|
# Remove the window from the main sizer
|
|
nCurSel = self._pParent._pages.GetSelection()
|
|
self._pParent._mainSizer.Detach(self._pParent._windows[nCurSel])
|
|
self._pParent._windows[nCurSel].Hide()
|
|
|
|
pWindow = self._pParent._windows[nMove]
|
|
self._pParent._windows.pop(nMove)
|
|
self._pParent._windows.insert(nMoveTo-1, pWindow)
|
|
|
|
pgInfo = self._pagesInfoVec[nMove]
|
|
|
|
self._pagesInfoVec.pop(nMove)
|
|
self._pagesInfoVec.insert(nMoveTo - 1, pgInfo)
|
|
|
|
# Add the page according to the style
|
|
pSizer = self._pParent._mainSizer
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
|
|
if style & FNB_BOTTOM:
|
|
|
|
pSizer.Insert(0, pWindow, 1, wx.EXPAND)
|
|
|
|
else:
|
|
|
|
# We leave a space of 1 pixel around the window
|
|
pSizer.Add(pWindow, 1, wx.EXPAND)
|
|
|
|
pWindow.Show()
|
|
|
|
pSizer.Layout()
|
|
self._iActivePage = nMoveTo - 1
|
|
self._iPreviousActivePage = -1
|
|
self.DoSetSelection(self._iActivePage)
|
|
self.Refresh()
|
|
self._pParent.Thaw()
|
|
|
|
|
|
def CanFitToScreen(self, page):
|
|
""" Returns wheter a tab can fit in the left space in the screen or not. """
|
|
|
|
# Incase the from is greater than page,
|
|
# we need to reset the self._nFrom, so in order
|
|
# to force the caller to do so, we return false
|
|
if self._nFrom > page:
|
|
return False
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
render = self._mgr.GetRenderer(style)
|
|
|
|
vTabInfo = render.NumberTabsCanFit(self)
|
|
|
|
if page - self._nFrom >= len(vTabInfo):
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def GetNumOfVisibleTabs(self):
|
|
""" Returns the number of visible tabs. """
|
|
|
|
count = 0
|
|
for ii in xrange(self._nFrom, len(self._pagesInfoVec)):
|
|
if self._pagesInfoVec[ii].GetPosition() == wx.Point(-1, -1):
|
|
break
|
|
count = count + 1
|
|
|
|
return count
|
|
|
|
|
|
def GetEnabled(self, page):
|
|
""" Returns whether a tab is enabled or not. """
|
|
|
|
if page >= len(self._pagesInfoVec):
|
|
return True # Seems strange, but this is the default
|
|
|
|
return self._pagesInfoVec[page].GetEnabled()
|
|
|
|
|
|
def Enable(self, page, enabled=True):
|
|
""" Enables or disables a tab. """
|
|
|
|
if page >= len(self._pagesInfoVec):
|
|
return
|
|
|
|
self._pagesInfoVec[page].Enable(enabled)
|
|
|
|
|
|
def GetSingleLineBorderColour(self):
|
|
""" Returns the colour for the single line border. """
|
|
|
|
if self.HasFlag(FNB_FANCY_TABS):
|
|
return self._colorFrom
|
|
|
|
return wx.WHITE
|
|
|
|
|
|
def HasFlag(self, flag):
|
|
""" Returns whether a flag is present in the L{FlatNotebook} style. """
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
res = (style & flag and [True] or [False])[0]
|
|
return res
|
|
|
|
|
|
def ClearFlag(self, flag):
|
|
""" Deletes a flag from the L{FlatNotebook} style. """
|
|
|
|
style = self.GetParent().GetWindowStyleFlag()
|
|
style &= ~flag
|
|
self.SetWindowStyleFlag(style)
|
|
|
|
|
|
def TabHasImage(self, tabIdx):
|
|
""" Returns whether a tab has an associated image index or not. """
|
|
|
|
if self._ImageList:
|
|
return self._pagesInfoVec[tabIdx].GetImageIndex() != -1
|
|
|
|
return False
|
|
|
|
|
|
def OnLeftDClick(self, event):
|
|
""" Handles the wx.EVT_LEFT_DCLICK event for L{PageContainer}. """
|
|
|
|
where, tabIdx = self.HitTest(event.GetPosition())
|
|
|
|
if where == FNB_RIGHT_ARROW:
|
|
self.RotateRight()
|
|
|
|
elif where == FNB_LEFT_ARROW:
|
|
self.RotateLeft()
|
|
|
|
elif self.HasFlag(FNB_DCLICK_CLOSES_TABS):
|
|
|
|
if where == FNB_TAB:
|
|
self.DeletePage(tabIdx)
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
def PopupTabsMenu(self):
|
|
""" Pops up the menu activated with the drop down arrow in the navigation area. """
|
|
|
|
popupMenu = wx.Menu()
|
|
|
|
for i in xrange(len(self._pagesInfoVec)):
|
|
pi = self._pagesInfoVec[i]
|
|
item = wx.MenuItem(popupMenu, i, pi.GetCaption(), pi.GetCaption(), wx.ITEM_NORMAL)
|
|
self.Bind(wx.EVT_MENU, self.OnTabMenuSelection, item)
|
|
|
|
# This code is commented, since there is an alignment problem with wx2.6.3 & Menus
|
|
# if self.TabHasImage(ii):
|
|
# item.SetBitmaps( (*m_ImageList)[pi.GetImageIndex()] );
|
|
|
|
popupMenu.AppendItem(item)
|
|
item.Enable(pi.GetEnabled())
|
|
|
|
self.PopupMenu(popupMenu)
|
|
|
|
|
|
def OnTabMenuSelection(self, event):
|
|
""" Handles the wx.EVT_MENU event for L{PageContainer}. """
|
|
|
|
selection = event.GetId()
|
|
self.FireEvent(selection)
|
|
|
|
|
|
def FireEvent(self, selection):
|
|
"""
|
|
Fires the wxEVT_FLATNOTEBOOK_PAGE_CHANGING and wxEVT_FLATNOTEBOOK_PAGE_CHANGED events
|
|
called from other methods (from menu selection or Smart Tabbing).
|
|
Utility function.
|
|
"""
|
|
|
|
if selection == self._iActivePage:
|
|
# No events for the same selection
|
|
return
|
|
|
|
oldSelection = self._iActivePage
|
|
|
|
event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
|
|
event.SetSelection(selection)
|
|
event.SetOldSelection(oldSelection)
|
|
event.SetEventObject(self.GetParent())
|
|
|
|
if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed():
|
|
|
|
self.SetSelection(selection)
|
|
|
|
# Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
|
|
event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
|
|
event.SetOldSelection(oldSelection)
|
|
self.GetParent().GetEventHandler().ProcessEvent(event)
|
|
|
|
|
|
def SetImageList(self, imglist):
|
|
""" Sets the image list for the page control. """
|
|
|
|
self._ImageList = imglist
|
|
|
|
|
|
def AssignImageList(self, imglist):
|
|
""" Assigns the image list for the page control. """
|
|
|
|
self._ImageList = imglist
|
|
|
|
|
|
def GetImageList(self):
|
|
""" Returns the image list for the page control. """
|
|
|
|
return self._ImageList
|
|
|
|
|
|
def GetSelection(self):
|
|
""" Returns the current selected page. """
|
|
|
|
return self._iActivePage
|
|
|
|
|
|
def GetPageCount(self):
|
|
""" Returns the number of tabs in the L{FlatNotebook} control. """
|
|
|
|
return len(self._pagesInfoVec)
|
|
|
|
|
|
def GetPageText(self, page):
|
|
""" Returns the tab caption of the page. """
|
|
|
|
return self._pagesInfoVec[page].GetCaption()
|
|
|
|
|
|
def SetPageText(self, page, text):
|
|
""" Sets the tab caption of the page. """
|
|
|
|
self._pagesInfoVec[page].SetCaption(text)
|
|
return True
|
|
|
|
|
|
def DrawDragHint(self):
|
|
""" Draws small arrow at the place that the tab will be placed. """
|
|
|
|
# get the index of tab that will be replaced with the dragged tab
|
|
pt = wx.GetMousePosition()
|
|
client_pt = self.ScreenToClient(pt)
|
|
where, tabIdx = self.HitTest(client_pt)
|
|
self._mgr.GetRenderer(self.GetParent().GetWindowStyleFlag()).DrawDragHint(self, tabIdx)
|
|
|
|
|