Logo Search packages:      
Sourcecode: editra version File versions  Download package

flatnotebook.py

# --------------------------------------------------------------------------- #
# 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: 22 Nov 2007, 14.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 @ 22 Nov 2007, 14.00 GMT

Version 2.4.

@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

# Used on OSX to get access to carbon api constants
if wx.Platform == '__WXMAC__':
    import Carbon.Appearance

# 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:
            # TODO: Serious problem and potentially bug in wxPython2.8.9.2
            #       some odd cases can cause the width attribute to be an
            #       empty tuple!! The repr and GetWidth however have the
            #       correct value.
            dc.DrawLine(rect.x, rect.y+i, rect.x+rect.GetWidth(), 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.GetWidth() - 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.GetWidth() - 2, rect.y + (rect.height / 10)*5)
    else:
        leftPt = wx.Point(rect.x, rect.y + (rect.height / 2))
        rightPt = wx.Point(rect.x + rect.GetWidth() - 2, rect.y + (rect.height / 2))

    # Define the top region
    top = wx.RectPP(rect.GetTopLeft(), rightPt)
    bottom = wx.RectPP(leftPt, rect.GetBottomRight())

    topEndColor = wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE)

    if wx.Platform != '__WXGTK__':
        # This type of gradient causes visibility issues with some dark
        # GTK themes so don't use on GTK for now.
        topStartColor = wx.WHITE
        if not focus:
            topStartColor = LightColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_3DFACE), 50)

        bottomStartColor = topEndColor
        bottomEndColor = topEndColor
    else:
        # Make whole tab same color on gtk
        if not focus:
            topEndColour = LightColour(topEndColor, 30)
        bottomEndColor = bottomStartColor = topStartColor = 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
# ---------------------------------------------------------------------------- #

00795 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.
    """
    
00802     def __init__(self, win):
        """ Default class constructor. Used internally. """
        
        wx.DropSource.__init__(self, win)
        self._win = win


00809     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. """
        try:
            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())
        except wx.PyAssertionError:
            return wx.DragCancel


# ---------------------------------------------------------------------------- #
# Class PageInfo
# Contains parameters for every FlatNotebook page
# ---------------------------------------------------------------------------- #

00883 class PageInfo:
    """
    This class holds all the information (caption, image, etc...) belonging to a
    single tab in L{FlatNotebook}.
    """
    
00889     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
        self._hasFocus = False


00912     def SetCaption(self, value):
        """ Sets the tab caption. """
        
        self._strCaption = value


00918     def GetCaption(self):
        """ Returns the tab caption. """

        return self._strCaption


00924     def SetPosition(self, value):
        """ Sets the tab position. """

        self._pos = value


00930     def GetPosition(self):
        """ Returns the tab position. """

        return self._pos


00936     def SetSize(self, value):
        """ Sets the tab size. """

        self._size = value


00942     def GetSize(self):
        """ Returns the tab size. """

        return self._size


00948     def SetTabAngle(self, value):
        """ Sets the tab header angle (0 <= tab <= 15 degrees). """

        self._TabAngle = min(45, value)


00954     def GetTabAngle(self):
        """ Returns the tab angle. """

        return self._TabAngle

    
00960     def SetImageIndex(self, value):
        """ Sets the tab image index. """

        self._ImageIndex = value


00966     def GetImageIndex(self):
        """ Returns the tab umage index. """

        return self._ImageIndex


00972     def GetEnabled(self):
        """ Returns whether the tab is enabled or not. """

        return self._bEnabled 


00978     def EnableTab(self, enabled):
        """ Sets the tab enabled or disabled. """

        self._bEnabled = enabled 


00984     def SetRegion(self, points=[]):
        """ Sets the tab region. """
        
        self._region = wx.RegionFromPoints(points) 


00990     def GetRegion(self):
        """ Returns the tab region. """

        return self._region  


00996     def SetXRect(self, xrect):
        """ Sets the button 'X' area rect. """

        self._xRect = xrect 


01002     def GetXRect(self):
        """ Returns the button 'X' area rect. """

        return self._xRect 


01008     def GetColour(self):
        """ Returns the tab colour. """

        return self._color 


01014     def SetColour(self, color):
        """ Sets the tab colour. """

        self._color = color 


# ---------------------------------------------------------------------------- #
# Class FlatNotebookEvent
# ---------------------------------------------------------------------------- #

01024 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.
    """
        
01032     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)


01041     def GetNotifyEvent(self):
        """Returns the actual wx.NotifyEvent."""
        
        return self.notify


01047     def IsAllowed(self):
        """Returns whether the event is allowed or not."""

        return self.notify.IsAllowed()


01053     def Veto(self):
        """Vetos the event."""

        self.notify.Veto()


01059     def Allow(self):
        """The event is allowed."""

        self.notify.Allow()


01065     def SetSelection(self, nSel):
        """ Sets event selection. """
        
        self._selection = nSel
        

01071     def SetOldSelection(self, nOldSel):
        """ Sets old event selection. """
        
        self._oldselection = nOldSel


01077     def GetSelection(self):
        """ Returns event selection. """
        
        return self._selection
        

01083     def GetOldSelection(self):
        """ Returns old event selection """
        
        return self._oldselection


# ---------------------------------------------------------------------------- #
# Class TabNavigatorWindow
# ---------------------------------------------------------------------------- #

01093 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.
    """

01099     def __init__(self, parent=None, icon=None):
        """ Default class constructor. Used internally."""

        wx.Dialog.__init__(self, parent, wx.ID_ANY, "", style=0)

        self._selectedItem = -1
        self._indexMap = []
        self._sel = 0
        
        if icon is None:
            self._bmp = GetMondrianBitmap()
        else:
            self._bmp = icon

        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()

        # Set focus on the list box to avoid having to click on it to change
        # the tab selection under GTK.
        self._listBox.SetFocus()


01160     def OnKeyUp(self, event):
        """Handles the wx.EVT_KEY_UP for the L{TabNavigatorWindow}."""
        
        if event.GetKeyCode() == wx.WXK_CONTROL:
            self.CloseDialog()


01167     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)


01193     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)


01230     def OnItemSelected(self, event):
        """Handles the wx.EVT_LISTBOX_DCLICK event for the wx.ListBox inside L{TabNavigatorWindow}. """

        self.CloseDialog()


01236     def CloseDialog(self):
        """Closes the L{TabNavigatorWindow} dialog, setting selection in L{FlatNotebook}."""

        bk = self.GetParent()
        self._selectedItem = self._listBox.GetSelection()
        self._sel = self._indexMap[self._selectedItem]
        self.EndModal(wx.ID_OK)
        

01245     def GetSelection(self):
        """Get the index of the selected page"""
        return self._sel


01250     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.GetWidth(), 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)


01287     def OnPanelEraseBg(self, event):
        """Handles the wx.EVT_ERASE_BACKGROUND event for L{TabNavigatorWindow} top panel. """

        pass


# ---------------------------------------------------------------------------- #
# Class FNBRenderer
# ---------------------------------------------------------------------------- #

01297 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.
    """

01303     def __init__(self):
        """Default class constructor. """
        
        self._tabHeight = None

        if wx.Platform == "__WXMAC__":
            # Hack to get proper highlight color for focus rectangle from
            # current theme by creating a theme brush and getting its color.
            # kThemeBrushFocusHighlight is available on Mac OS 8.5 and higher
            brush = wx.Brush(wx.BLACK)
            brush.MacSetTheme(Carbon.Appearance.kThemeBrushFocusHighlight)
            self._focusPen = wx.Pen(brush.GetColour(), 2, wx.SOLID)
        else:
            self._focusPen = wx.Pen(wx.BLACK, 1, wx.USER_DASH)
            self._focusPen.SetDashes([1, 1])
            self._focusPen.SetCap(wx.CAP_BUTT)


01321     def GetLeftButtonPos(self, pageContainer):
        """ Returns the left button position in the navigation area. """
        return 2


01326     def GetRightButtonPos(self, pageContainer):
        """ Returns the right button position in the navigation area. """

        pc = pageContainer
        style = pc.GetParent().GetWindowStyleFlag()
        rect = pc.GetClientRect()
        clientWidth = rect.GetWidth()
        
        if style & FNB_NO_X_BUTTON:
            return clientWidth - 20
        else:
            return clientWidth - 36


01340     def GetDropArrowButtonPos(self, pageContainer):
        """ Returns the drop down button position in the navigation area. """
        pc = pageContainer
        style = pc.GetParent().GetWindowStyleFlag()
        rect = pc.GetClientRect()
        clientWidth = rect.GetWidth()
        
        if style & FNB_NO_X_BUTTON and style & FNB_NO_NAV_BUTTONS:
            return clientWidth - 20
        elif style & FNB_NO_X_BUTTON or style & FNB_NO_NAV_BUTTONS:
            return clientWidth - 36
        else:
            return clientWidth - 52


01355     def GetXPos(self, pageContainer):
        """ Returns the 'X' button position in the navigation area. """

        pc = pageContainer
        style = pc.GetParent().GetWindowStyleFlag()
        rect = pc.GetClientRect()
        clientWidth = rect.GetWidth()
        
        if style & FNB_NO_X_BUTTON:
            return clientWidth
        else:
            return clientWidth - 20


01369     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 DrawArrowAccordingToState(self, dc, pc, rect):
        lightFactor = (pc.HasFlag(FNB_BACKGROUND_GRADIENT) and [70] or [0])[0]
        PaintStraightGradientBox(dc, rect, pc._tabAreaColor, LightColour(pc._tabAreaColor, lightFactor))

01402     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)
        self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))

        # Draw the new bitmap
        dc.DrawBitmap(arrowBmp, posx + 2, 6, True)


01437     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)
        self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))

        # Draw the new bitmap
        dc.DrawBitmap(arrowBmp, posx, 6, True)


01473     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)
        self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))

        # Draw the new bitmap
        dc.DrawBitmap(downBmp, posx, 6, True)


01504     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) 
        self.DrawArrowAccordingToState(dc, pc, wx.Rect(posx, 6, 16, 14))

        # Draw the new bitmap
        dc.DrawBitmap(xbmp, posx, 6, True)


01536     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))

        # 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)


01567     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.GetWidth(), 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.GetWidth()-1, 0)
                dc.DrawLine(1, 1, clntRect.GetWidth()-1, 1)

                dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
                dc.DrawLine(1, 2, clntRect.GetWidth()-1, 2)

                dc.SetPen(wx.Pen(fillColor))
                dc.DrawLine(selTabX1 + 2, 2, selTabX2 - 1, 2)
                
            else:
                
                dc.DrawLine(1, clntRect.height, clntRect.GetWidth()-1, clntRect.height)
                dc.DrawLine(1, clntRect.height-1, clntRect.GetWidth()-1, clntRect.height-1)

                dc.SetPen(wx.Pen(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNSHADOW)))
                dc.DrawLine(1, clntRect.height-2, clntRect.GetWidth()-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.GetWidth(), clntRect.height - 2)
                clientRect2 = wx.Rect(0, 1, clntRect.GetWidth(), clntRect.height - 1)
            
            else:
            
                clientRect = wx.Rect(0, 0, clntRect.GetWidth(), clntRect.height - 2)
                clientRect2 = wx.Rect(0, 0, clntRect.GetWidth(), 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.GetWidth(), clientRect.height+1)
                
                else:
                    
                    dc.DrawLine(0, 0, clientRect.GetWidth(), 0)
                    
                dc.DrawLine(clientRect.GetWidth() - 1, 0, clientRect.GetWidth() - 1, clientRect.height+1)


01641     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


01693     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

        self._tabHeight = tabHeight
        
        return tabHeight


01722     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.GetWidth()

        # 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]
            rect = pc.GetClientRect()
            PaintStraightGradientBox(dc, pc.GetClientRect(), pc._tabAreaColor, LightColour(pc._tabAreaColor, lightFactor))
            dc.SetBrush(wx.TRANSPARENT_BRUSH)

        dc.DrawRectangle(0, 0, size.x, size.y)

        # 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()

        # Add padding for LeftArrow button if necessary
        if not pc.HasFlag(FNB_NO_NAV_BUTTONS):
            posx += 12

        # 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))
            self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[i])

            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)


01894     def DrawFocusRectangle(self, dc, pageContainer, page):
        """ Draws a focus rectangle like the native Notebooks. """
        
        if not page._hasFocus:
            return

        tabPos = page.GetPosition()
        if pageContainer.GetParent().GetWindowStyleFlag() & FNB_VC8:
            vc8ShapeLen = self.CalcTabHeight(pageContainer) - VERTICAL_BORDER_PADDING - 2
            tabPos.x += vc8ShapeLen
            
        rect = wx.RectPS(tabPos, page.GetSize())
        rect = wx.Rect(rect.x+5, rect.y+2, rect.GetWidth()-6, rect.height-8)

        if wx.Platform == '__WXMAC__':
            rect.SetWidth(rect.GetWidth() + 3)
            rect.SetX(rect.GetX() - 2)

        if pageContainer.GetParent().GetWindowStyleFlag() & FNB_BOTTOM:
            rect.SetY(rect.GetY() + 3)

        dc.SetBrush(wx.TRANSPARENT_BRUSH)
        dc.SetPen(self._focusPen)
        dc.DrawRoundedRectangleRect(rect, 2)
        

01920     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.GetWidth()

        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
# ---------------------------------------------------------------------------- #

01968 class FNBRendererMgr:
    """
    This class represents a manager that handles all the 4 renderers defined
    and calls the appropriate one when drawing is needed.
    """

01974     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()})


01987     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 
#------------------------------------------

02010 class FNBRendererDefault(FNBRenderer):
    """
    This class handles the drawing of tabs using the I{Standard} renderer.
    """
    
02015     def __init__(self):
        """ Default class constructor. """

        FNBRenderer.__init__(self)
        

02021     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)

            # Draw the tab
            self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)            
        

#------------------------------------------
# Firefox2 renderer 
#------------------------------------------
02131 class FNBRendererFirefox2(FNBRenderer):
    """
    This class handles the drawing of tabs using the I{Firefox 2} renderer.
    """
    
02136     def __init__(self):
        """ Default class constructor. """

        FNBRenderer.__init__(self)

        
02142     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)

            # Draw the tab
            self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)
        

#------------------------------------------------------------------
# Visual studio 7.1 
#------------------------------------------------------------------

02231 class FNBRendererVC71(FNBRenderer):
    """
    This class handles the drawing of tabs using the I{VC71} renderer.
    """

02236     def __init__(self):
        """ Default class constructor. """

        FNBRenderer.__init__(self)


02242     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)

            # Draw the tab
            self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)                    


#------------------------------------------------------------------
# Fancy style
#------------------------------------------------------------------

02342 class FNBRendererFancy(FNBRenderer):
    """
    This class handles the drawing of tabs using the I{Fancy} renderer.
    """

02347     def __init__(self):
        """ Default class constructor. """

        FNBRenderer.__init__(self)


02353     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.GetWidth(), 2)
            else:
                dc.DrawLine(rect.x, rect.y + rect.height - 1, rect.x + rect.GetWidth(), 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)

            # Draw the tab
            self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)            
        

#------------------------------------------------------------------
# Visual studio 2005 (VS8)
#------------------------------------------------------------------
02444 class FNBRendererVC8(FNBRenderer):    
    """
    This class handles the drawing of tabs using the I{VC8} renderer.
    """

02449     def __init__(self):
        """ Default class constructor. """

        FNBRenderer.__init__(self)
        self._first = True
        self._factor = 1

        
02457     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)

        # 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)


02605     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.GetWidth(), 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)

            # Draw the tab
            self.DrawTabX(pc, dc, x_rect, tabIdx, btnStatus)

        self.DrawFocusRectangle(dc, pc, pc._pagesInfoVec[tabIdx])


02738     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


02823     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)


02880     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)


02936     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.GetWidth()

        # 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
# ---------------------------------------------------------------------------- #

02976 class FlatNotebook(wx.PyPanel):
    """
    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
    """
    
02994     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
        self._naviIcon = None

        wx.PyPanel.__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()


03022     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
        
        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)


03058     def DoGetBestSize(self):
        """ Overrides DoGetBestSize to handle sizers nicely. """

        if not self._windows:
            # Something is better than nothing... no pages!
            return wx.Size(20, 20)

        maxWidth = maxHeight = 0
        tabHeight = self.GetPageBestSize().height

        for win in self._windows:
            # Loop over all the windows to get their best size
            width, height = win.GetBestSize()
            maxWidth, maxHeight = max(maxWidth, width), max(maxHeight, height)

        return wx.Size(maxWidth, maxHeight+tabHeight)
    

03076     def SetActiveTabTextColour(self, textColour):
        """ Sets the text colour for the active tab. """

        self._pages._activeTextColor = textColour


03082     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)


03088     def GetPreviousSelection(self):
        """ Returns the previous selection. """

        return self._pages._iPreviousActivePage


03094     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        


03178     def SetImageList(self, imageList):
        """ Sets the image list for the page control. """

        self._pages.SetImageList(imageList)


03184     def AssignImageList(self, imageList):
        """ Assigns the image list for the page control. """

        self._pages.AssignImageList(imageList)


03190     def GetImageList(self):
        """ Returns the associated image list. """
        
        return self._pages.GetImageList()


03196     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


03286     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)


03331     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()
        self.Update()  

        # Fire a closed event
        closedEvent = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CLOSED, self.GetId())
        closedEvent.SetSelection(page)
        closedEvent.SetEventObject(self)
        self.GetEventHandler().ProcessEvent(closedEvent)


03379     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


03398     def GetCurrentPage(self):
        """ Returns the currently selected notebook page or None. """
        
        sel = self._pages.GetSelection()
        if sel < 0 or sel >= len(self._windows):
            return None

        return self._windows[sel]


03408     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]


03417     def GetPageIndex(self, win):
        """ Returns the index at which the window is found. """

        try:
            return self._windows.index(win)
        except:
            return -1


03426     def GetSelection(self):
        """ Returns the currently selected page, or -1 if none was selected. """
        
        return self._pages.GetSelection()


03432     def AdvanceSelection(self, forward=True):
        """
        Cycles through the tabs.
        The call to this function generates the page changing events.
        """

        self._pages.AdvanceSelection(forward)


03441     def GetPageCount(self):
        """ Returns the number of pages in the L{FlatNotebook} control. """

        return self._pages.GetPageCount()

03446     def SetNavigatorIcon(self, bmp):
        """ Set the icon used by the L{TabNavigatorWindow} """
        if isinstance(bmp, wx.Bitmap) and bmp.IsOk():
            # Make sure image is proper size
            if bmp.GetSize() != (16, 16):
                img = bmp.ConvertToImage()
                img.Rescale(16, 16, wx.IMAGE_QUALITY_HIGH)
                bmp = wx.BitmapFromImage(img)
            self._naviIcon = bmp
        else:
            raise TypeError, "SetNavigatorIcon requires a valid bitmap"

03458     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._naviIcon)
                    self._popupWin.SetReturnCode(wx.ID_OK)
                    self._popupWin.ShowModal()
                    self._pages.FireEvent(self._popupWin.GetSelection())
                    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:
            event.Skip()
            

03485     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


03495     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)


03507     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()


03519     def GetPageBestSize(self):
        """ Return the page best size. """

        return self._pages.GetClientSize()


03525     def SetPageText(self, page, text):
        """ Sets the text for the given page. """

        bVal = self._pages.SetPageText(page, text)
        self._pages.Refresh()

        return bVal


03534     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()


03543     def GetTabArea(self):
        """ Returns the associated page. """

        return self._pages


03549     def GetPadding(self):
        """ Returns the amount of space around each page's icon and label, in pixels. """
        
        return self._nPadding 


03555     def SetWindowStyleFlag(self, style):
        """ Sets the L{FlatNotebook} window style flags. """
            
        wx.PyPanel.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()


03573     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


03608     def SetRightClickMenu(self, menu):
        """ Sets the popup menu associated to a right click on a tab. """

        self._pages._pRightClickMenu = menu


03614     def GetPageText(self, nPage):
        """ Returns the tab caption. """

        return self._pages.GetPageText(nPage)


03620     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


03628     def SetGradientColourFrom(self, fr):
        """ Sets the starting colour for the gradient. """

        self._pages._colorFrom = fr


03634     def SetGradientColourTo(self, to):
        """ Sets the ending colour for the gradient. """

        self._pages._colorTo = to


03640     def SetGradientColourBorder(self, border):
        """ Sets the tab border colour. """

        self._pages._colorBorder = border


03646     def GetGradientColourFrom(self):
        """ Gets first gradient colour. """

        return self._pages._colorFrom


03652     def GetGradientColourTo(self):
        """ Gets second gradient colour. """

        return self._pages._colorTo


03658     def GetGradientColourBorder(self):
        """ Gets the tab border colour. """

        return self._pages._colorBorder


03664     def GetBorderColour(self):
        """ Returns the border colour. """

        return self._pages._colorBorder
    

03670     def GetActiveTabTextColour(self):
        """ Get the active tab text colour. """

        return self._pages._activeTextColor


03676     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)


03685     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)


03694     def GetEnabled(self, page):
        """ Returns whether a tab is enabled or not. """

        return self._pages.GetEnabled(page)


03700     def EnableTab(self, page, enabled=True):
        """ Enables or disables a tab. """

        if page >= len(self._windows):
            return

        self._windows[page].Enable(enabled)
        self._pages.EnableTab(page, enabled)


03710     def GetNonActiveTabTextColour(self):
        """ Returns the non active tabs text colour. """

        return self._pages._nonActiveTextColor


03716     def SetNonActiveTabTextColour(self, color):
        """ Sets the non active tabs text colour. """

        self._pages._nonActiveTextColor = color


03722     def SetTabAreaColour(self, color):
        """ Sets the area behind the tabs colour. """

        self._pages._tabAreaColor = color


03728     def GetTabAreaColour(self):
        """ Returns the area behind the tabs colour. """

        return self._pages._tabAreaColor


03734     def SetActiveTabColour(self, color):
        """ Sets the active tab colour. """

        self._pages._activeTabColor = color


03740     def GetActiveTabColour(self):
        """ Returns the active tab colour. """

        return self._pages._activeTabColor

03745     def EnsureVisible(self, page):
        """ Ensures that a tab is visible. """
        self._pages.DoSetSelection(page)

# ---------------------------------------------------------------------------- #
# Class PageContainer
# Acts as a container for the pages you add to FlatNotebook
# ---------------------------------------------------------------------------- #

03754 class PageContainer(wx.Panel):
    """
    This class acts as a container for the pages you add to L{FlatNotebook}.
    """

03759     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._setCursor = False

        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|wx.WANTS_CHARS)

        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_MOUSEWHEEL, self.OnMouseWheel)
        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)
        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        

03827     def OnEraseBackground(self, event):
        """ Handles the wx.EVT_ERASE_BACKGROUND event for L{PageContainer} (does nothing)."""

        pass

    
03833     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()


03840     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()


03853     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()


03877     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


03902     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()


03924     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()


03941     def OnMouseWheel(self, event):
        """ Scroll tabs when the mouse whee """
        delta = event.GetWheelRotation()
        for tab in range(abs(delta/event.GetWheelDelta())):
            if delta > 0:
                before = self._nLeftButtonStatus
                self._nLeftButtonStatus = FNB_BTN_PRESSED
                self.RotateLeft()
                self._nLeftButtonStatus = before
            else:
                before = self._nRightButtonStatus
                self._nRightButtonStatus = FNB_BTN_PRESSED
                self.RotateRight()
                self._nRightButtonStatus = before

        event.Skip()


03959     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] and tabIdx < len(self._pagesInfoVec):

            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()


03983     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 len(self._pagesInfoVec) > tabIdx and \
                   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()


04062     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())
        
        # Make sure selected tab has focus
#        self.SetFocus()

        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()
        

04117     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:
            ddrect = wx.Rect(render.GetDropArrowButtonPos(self), 8, 16, 16)
            if ddrect.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.GetWidth()-startpos, self.GetParent().GetSize().y)

            if tabRect.Contains(pt):
                return FNB_TAB, len(self._pagesInfoVec)        

        # Default
        return FNB_NOWHERE, -1


04209     def SetSelection(self, page):
        """ Sets the selected page. """

        book = self.GetParent()
        book.SetSelection(page)
        self.DoSetSelection(page)


04217     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()


04243     def DeletePage(self, page):
        """ Delete the specified page from L{FlatNotebook}. """

        book = self.GetParent()
        book.DeletePage(page)
        book.Refresh()


04251     def IsTabVisible(self, page):
        """ Returns whether a tab is visible or not. """

        iLastVisiblePage = self.GetLastVisibleTab()
        return page <= iLastVisiblePage and page >= self._nFrom


04258     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())
            self.GetParent().GetEventHandler().ProcessEvent(event)

            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()
        

04303     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()


04316     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))
                    self._setCursor = True
                else:
                    if self._setCursor:
                        wx.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
                        self._setCursor = False
                
                # 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:
                
                    self.Refresh()

                if bRedrawDropArrow:

                    render.DrawDropDownArrow(self, dc)

        event.Skip()


04442     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


04458     def GetNumTabsCanScrollLeft(self):
        """ Returns the number of tabs than can be scrolled left. """

        if self._nFrom - 1 >= 0:
            return 1

        return 0


04467     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


04475     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)


04499     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)
        render.DrawFocusRectangle(dc, self, self._pagesInfoVec[selection])

        event.Skip()


04538     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()


04550     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())
        

04561     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()


04570     def GetPageImage(self, page):
        """ Returns the image index associated to a page. """

        if page < len(self._pagesInfoVec):
        
            return self._pagesInfoVec[page].GetImageIndex()
        
        return -1


04580     def OnDropTarget(self, x, y, nTabPage, wnd_oldContainer):
        """ Handles the drop action from a DND operation. """

        # Disable drag'n'drop for disabled tab
        if len(wnd_oldContainer._pagesInfoVec) > nTabPage and \
           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().GetBitmap(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.Add(bmp)
                            newNotebook.SetImageList(newImageList)
                                
                        newNotebook.InsertPage(nIndex, window, caption, True, imageindex)

        self._isdragging = False
        
        return wx.DragMove


04640     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()


04688     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


04708     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


04720     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()


04729     def EnableTab(self, page, enabled=True):
        """ Enables or disables a tab. """

        if page >= len(self._pagesInfoVec):
            return
        
        self._pagesInfoVec[page].EnableTab(enabled)
        

04738     def GetSingleLineBorderColour(self):
        """ Returns the colour for the single line border. """

        if self.HasFlag(FNB_FANCY_TABS):
            return self._colorFrom
        
        return wx.WHITE


04747     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


04755     def ClearFlag(self, flag):
        """ Deletes a flag from the L{FlatNotebook} style. """

        style = self.GetParent().GetWindowStyleFlag()
        style &= ~flag 
        self.SetWindowStyleFlag(style)


04763     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


04772     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._nRightButtonStatus = FNB_BTN_PRESSED
            self.RotateRight()

        elif where == FNB_LEFT_ARROW:
            self._nLeftButtonStatus = FNB_BTN_PRESSED
            self.RotateLeft()

        elif self.HasFlag(FNB_DCLICK_CLOSES_TABS):
        
            if where == FNB_TAB:
                self.DeletePage(tabIdx)
        
        else:
        
            event.Skip()
        

04795     def OnSetFocus(self, event):
        """ Handles the wx.EVT_SET_FOCUS event for L{PageContainer}. """

        if self._iActivePage < 0:
            event.Skip()
            return

        self.SetFocusedPage(self._iActivePage)


04805     def OnKillFocus(self, event):
        """ Handles the wx.EVT_KILL_FOCUS event for L{PageContainer}. """

        self.SetFocusedPage()


04811     def OnKeyDown(self, event):
        """
        When the PageContainer has the focus tabs can be changed with
        the left/right arrow keys.
        """
        key = event.GetKeyCode()
        if key == wx.WXK_LEFT:
            self.GetParent().AdvanceSelection(False)
            self.SetFocus()
        elif key == wx.WXK_RIGHT:
            self.GetParent().AdvanceSelection(True)
            self.SetFocus()
        elif key == wx.WXK_TAB and not event.ControlDown():
            flags = 0
            if not event.ShiftDown(): flags |= wx.NavigationKeyEvent.IsForward
            if event.CmdDown():       flags |= wx.NavigationKeyEvent.WinChange
            self.Navigate(flags)
        else:
            event.Skip()

            
04832     def SetFocusedPage(self, pageIndex=-1):
        """
        Sets/Unsets the focus on the appropriate page.
        If pageIndex is defaulted, we have lost focus and no focus indicator is drawn.
        """

        for indx, page in enumerate(self._pagesInfoVec):
            if indx == pageIndex:
                page._hasFocus = True
            else:
                page._hasFocus = False
            
        self.Refresh()
                

04847     def PopupTabsMenu(self):
        """ Pops up the menu activated with the drop down arrow in the navigation area. """

        popupMenu = wx.Menu()

        longest = 0
        has_bmp = False
        for i in xrange(len(self._pagesInfoVec)):
            pi = self._pagesInfoVec[i]
            caption = pi.GetCaption()
            item = wx.MenuItem(popupMenu, i+1, caption, caption, wx.ITEM_NORMAL)

            # Save longest caption width for calculating menu width with
            width = self.GetTextExtent(caption)[0]
            if width > longest:
                longest = width

            self.Bind(wx.EVT_MENU, self.OnTabMenuSelection, item)

            # There is an alignment problem with wx2.6.3 & Menus so only use
            # images for versions above 2.6.3
            if wx.VERSION > (2, 6, 3, 0) and self.TabHasImage(i):
                has_bmp = True
                item.SetBitmap(self.GetImageList().GetBitmap(pi.GetImageIndex()))

            popupMenu.AppendItem(item)
            item.Enable(pi.GetEnabled())

        # Calculate the approximate size of the popupmenu for setting the
        # position of the menu when its shown.
        # Account for extra padding on left/right of text on mac menus
        if wx.Platform in ['__WXMAC__', '__WXMSW__']:
            longest += 32

        # Bitmap width + padding
        if has_bmp:
            longest += 20

        if not self.HasFlag(FNB_NO_X_BUTTON) or \
           not self.HasFlag(FNB_NO_NAV_BUTTONS):
            longest += 16

        rect = self.GetClientRect()
        self.PopupMenu(popupMenu,
                       wx.Point(rect.x + rect.GetWidth() - longest,
                                rect.y + rect.height))


04895     def OnTabMenuSelection(self, event):
        """ Handles the wx.EVT_MENU event for L{PageContainer}. """

        selection = event.GetId() - 1
        self.FireEvent(selection)


04902     def FireEvent(self, selection):
        """
        Fires the wxEVT_FLATNOTEBOOK_PAGE_CHANGING and wxEVT_FLATNOTEBOOK_PAGE_CHANGED events
        called from other methods (from menu selection or Smart Tabbing).
        Utility function.
        """

        if selection == self._iActivePage:
            # No events for the same selection
            return
        
        oldSelection = self._iActivePage

        event = FlatNotebookEvent(wxEVT_FLATNOTEBOOK_PAGE_CHANGING, self.GetParent().GetId())
        event.SetSelection(selection)
        event.SetOldSelection(oldSelection)
        event.SetEventObject(self.GetParent())
        
        if not self.GetParent().GetEventHandler().ProcessEvent(event) or event.IsAllowed():
        
            self.SetSelection(selection)

            # Fire a wxEVT_FLATNOTEBOOK_PAGE_CHANGED event
            event.SetEventType(wxEVT_FLATNOTEBOOK_PAGE_CHANGED)
            event.SetOldSelection(oldSelection)
            self.GetParent().GetEventHandler().ProcessEvent(event)
#             self.SetFocus()
            

04931     def SetImageList(self, imglist):
        """ Sets the image list for the page control. """

        self._ImageList = imglist


04937     def AssignImageList(self, imglist):
        """ Assigns the image list for the page control. """

        self._ImageList = imglist


04943     def GetImageList(self):
        """ Returns the image list for the page control. """

        return self._ImageList


04949     def GetSelection(self):
        """ Returns the current selected page. """

        return self._iActivePage 


04955     def GetPageCount(self):
        """ Returns the number of tabs in the L{FlatNotebook} control. """

        return len(self._pagesInfoVec)


04961     def GetPageText(self, page):
        """ Returns the tab caption of the page. """
        if page < len(self._pagesInfoVec):
            return self._pagesInfoVec[page].GetCaption()
        else:
            return u''


04969     def SetPageText(self, page, text):
        """ Sets the tab caption of the page. """
        if page < len(self._pagesInfoVec):
            self._pagesInfoVec[page].SetCaption(text)
            return True
        else:
            return False


04978     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)



Generated by  Doxygen 1.6.0   Back to index