Grid - Custom Renderer/Editor with Richtext and wordwrapping capabilities

Hello,

while I was able to code a custom renderer with richtext and wordwrapping capabilities, I found two issues and I like to get some input. For the renderer I append a reduced example. Tested with w2k, py-2.5, pywx-2.8.7.1 and XP,py-2.5.1,pywx-2.8.8.1

1) Taking the charakter style (start,end,fontstring, backgroundcolour, textcolour) from a string, the NativeFontInfoString sets the font size not correctly.
see Line 61 - 64 in the example :
    for style in styles:
            fn = wx.FontFromNativeInfoString(style['fontstring'])
            dc.SetFont(fn)

should work, but results in to small FontSizes. As a workaround I apended:
    for style in styles:
            fn = wx.FontFromNativeInfoString(style['fontstring'])
            fn.SetPointSize(fn.GetPointSize()) # without setting the PointSize again, the font size is false
            dc.SetFont(fn)

Is this a bug ?

2) The function GetBestSize of the renderer seems to be called always before the Draw method. As the calculation of the
best cell size is somewhat complex, if there are different font sizes and styles, I call the Draw method inside the GetBestSize method. Are there any reasons to avoid this ? Could you recommend a better way to calculate the BestSize ?

3) For the Editor I need to get the NativeFontInfoString from a TextAttr Object. There is a known bug that the font size is
always 15 x to large see:
http://trac.wxwidgets.org/ticket/2120

both wxFont::GetNativeFontInfoDesc and wxFont::GetNativeFontInfoUserDesc are affected. The bug is known for four years now. Is there any chance that that will corrected in the next few releases ?

Thanks in advance
Jürgen

Example: you can see the wordwrapping by changing the col size

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

import wx
import wx.grid

class MyRichTextWordwrapRenderer(wx.grid.PyGridCellRenderer):
    def __init__(self):
        wx.grid.PyGridCellRenderer.__init__(self)
        # example charakter styles
        # start,end, nativefontstring,bgcolour,textcolour
        # '#' is used as delimiter between the styles
        self.styles ="0,1,0;10;0;0;0;400;0;0;0;0;143;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                          1,44,0;10;0;0;0;400;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                          44,47,0;10;0;0;0;400;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;0;255,255;0;0;255,#\
                          47,63,0;10;0;0;0;400;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                          63,77,0;14;0;0;0;700;1;1;0;0;152;0;0;16;Times New Roman,255;255;255;255,0;0;255;255,#\
                          77,90,0;10;0;0;0;400;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                          90,94,0;10;0;0;0;700;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                          94,107,0;10;0;0;0;400;0;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255,#\
                         107,113,0;10;0;0;0;400;1;0;0;0;152;0;0;0;MS Shell Dlg 2,255;255;255;255,0;0;0;255"
        self.bestsize = (0,0)
           def Draw(self, grid, attr, dc, rect, row, col, isSelected):
               #prepare styledata
        styles = self.styles.split('#')
        styleattrs = ["start","end","fontstring","bgcol","txcol"]
        for num,item in enumerate(styles):
            style = dict(zip(styleattrs, item[:-1].split(',')))
            styles[num] = style
         # prepare DC
        dc.SetBackgroundMode(wx.SOLID)
        dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.DrawRectangleRect(rect)
               # calculate maxlineheight, maxlinebase
        maxfontsize =max( [x['fontstring'].split(';')[1] for x in styles])
        maxfonts = [x['fontstring'] for x in styles if x['fontstring'].split(';')[1] == maxfontsize]
        maxlineheight = 0
        maxlinebase = 0
        for f in maxfonts:
            fn = wx.FontFromNativeInfoString(f)
            fn.SetPointSize(fn.GetPointSize()) # without setting the PointSize again, the font size is false
            dc.SetFont(fn)
            h = dc.GetCharHeight()
            if maxlineheight < h:
                maxlineheight = h
                maxlinebase = maxlineheight - dc.GetFullTextExtent('T')[2]
               # prepare for wordwrapping
        text = grid.GetCellValue(row, col)
        linewidth = rect.x + 1
        maxlinewidth = rect.x + rect.width
        y = rect.y + 1
               # write Text with appropriate style
        for style in styles:
            fn = wx.FontFromNativeInfoString(style['fontstring'])
            fn.SetPointSize(fn.GetPointSize()) # without setting the PointSize again, the font size is false
            dc.SetFont(fn)
            h = dc.GetCharHeight()
            linebase = h - dc.GetFullTextExtent('T')[2]
                       # calc baseline
            if h < maxlineheight:
                vdiff = maxlinebase - linebase
            else:
                vdiff = 0
                           # set colours
            r,g,b,a = style['bgcol'].split(';')
            bgcol = wx.Colour(int(r),int(g),int(b),int(a))
            dc.SetTextBackground(bgcol)
            r,g,b,a = style['txcol'].split(';')
            tcol = wx.Colour(int(r),int(g),int(b),int(a)) dc.SetTextForeground(tcol)
                       # get partial Text for the current style
            ch = text[int(style['start']):int(style['end'])]

            # calc wordwrapping
            if linewidth + dc.GetTextExtent(ch)[0] <= maxlinewidth:
                dc.DrawText(ch, linewidth, y+vdiff )
                linewidth += dc.GetTextExtent(ch)[0]
            else:
                pte = dc.GetPartialTextExtents(ch)
                pos = 0
                while pos < len(pte):
                    if linewidth + pte[pos] > maxlinewidth:
                        break
                    pos += 1
                # current line
                dc.DrawText(ch[:pos], linewidth, y+vdiff )
                               #next line
                y += maxlineheight + 2
                linewidth = rect.x + 1
                dc.DrawText(ch[pos:], linewidth, y+vdiff )
                linewidth += dc.GetTextExtent(ch[pos:])[0]
               # set bestsize for the cell
        self.bestsize = (maxlinewidth, y + maxlineheight)

    def GetBestSize(self, grid, attr, dc, row, col):
        # as GetBestSize seems to be called always before Draw
        # it's needed to calculate self.bestsize before using it
        rect = wx.Rect( 0, 0, grid.GetColSize(col), grid.GetRowSize(row) )
        self.Draw( grid, attr, dc, rect,row, col,False)
        return self.bestsize

    def Clone(self):
        return MyCustomRenderer()

class SimpleGrid(wx.grid.Grid):
    def __init__(self, parent):
        wx.grid.Grid.__init__(self, parent, -1)
               self.CreateGrid(2, 3)
        self.colLabelWidths = [ 60, 60, 200 ]
        for num,width in enumerate(self.colLabelWidths):
            self.SetColSize(num, width)
            self.SetColMinimalWidth(num, width)

        self.SetCellValue(0,2,'''\
If supported by the native control, this is red, and this is a different font.And this is Bold.And this is Italic''')
               self.SetCellRenderer(0, 2, MyRichTextWordwrapRenderer())
               self.Bind(wx.grid.EVT_GRID_COL_SIZE,self.OnColSize)
        self.AutoSize()

    def OnColSize(self, evt):
        self.AutoSizeRows()

···

#---------------------------------------------------------------------------

class TestFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, -1, "Simple Grid", size=(640,480))
        self.grid = SimpleGrid(self)

#---------------------------------------------------------------------------

if __name__ == '__main__':
    app = wx.App(0)
    wx.InitAllImageHandlers()
    frame = TestFrame(None, )
    app.SetTopWindow(frame)
    frame.Show()
    app.MainLoop()
#---------------------------------------------------------------------------