view orpg/gametree/nodehandlers/dnd3e.py @ 183:0d9b746b5751 beta

Traipse Beta 'OpenRPG' {100115-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Beta) New Features: Added Bookmarks Added 'boot' command to remote admin Added confirmation window for sent nodes Minor changes to allow for portability to an OpenSUSE linux OS Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Zoom Mouse plugin added Images added to Plugin UI Switching to Element Tree Map efficiency, from FlexiRPG Added Status Bar to Update Manager New TrueDebug Class in orpg_log (See documentation for usage) Portable Mercurial Tip of the Day added, from Core and community New Reference Syntax added for custom PC sheets New Child Reference for gametree New Parent Reference for gametree New Gametree Recursion method, mapping, context sensitivity, and effeciency.. New Features node with bonus nodes and Node Referencing help added Dieroller structure from Core New DieRoller portability for odd Dice Added 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)] New 'Mythos' System die roller added Added new vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)]. Included for Mythos roller also New Warhammer FRPG Die Roller (Special thanks to Puu-san for the support) New EZ_Tree Reference system. Push a button, Traipse the tree, get a reference (Beta!) Fixes: Fix to Text based Server Fix to Remote Admin Commands Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Fix to Map from gametree not showing to all clients Fix to gametree about menus Fix to Password Manager check on startup Fix to PC Sheets from tool nodes. They now use the tabber_panel Fix to Whiteboard ID to prevent random line or text deleting. Fixes to Server, Remote Server, and Server GUI Fix to Update Manager; cleaner clode for saved repositories Fixes made to Settings Panel and now reactive settings when Ok is pressed Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of a Splice Fix to Use panel of Forms and Tabbers. Now longer enters design mode Fix made Image Fetching. New fetching image and new failed image Modified ID's to prevent non updated clients from ruining the fix. default_manifest.xml renamed to default_upmana.xml
author sirebral
date Fri, 15 Jan 2010 23:01:42 -0600
parents 3b6888bb53b5
children dcae32e219f1
line wrap: on
line source

# Copyright (C) 2000-2001 The OpenRPG Project
#
#        openrpg-dev@lists.sourceforge.net
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# --
#
# File: dnd3e.py
# Author: Chris Davis & Digitalxero
# Maintainer: leadb
# Version:
#   $Id: dnd3e.py,v 1.33 2006/11/04 21:24:21 digitalxero Exp $
#
# Description: The file contains code for the dnd3e nodehanlers
#

from core import *
from containers import *
from string import *  #a 1.6003
from inspect import *  #a 1.9001
from orpg.dirpath import dir_struct
from orpg.tools.orpg_log import debug
from xml.etree.ElementTree import parse

dnd3e_EXPORT = wx.NewId()
############Global Stuff##############

HP_CUR = wx.NewId()
HP_MAX = wx.NewId()
PP_CUR = wx.NewId()
PP_MAX = wx.NewId()
PP_FRE = wx.NewId()
PP_MFRE = wx.NewId()
HOWTO_MAX = wx.NewId()

def getRoot (node): # a 1.5002 this whole function is new.
    root = None
    target = node
    while target != None:
        root = target
        target = target.hparent
    return root

# if a method runs getRoot for its instance and assigns the
# value returned to self.root, you can get to instances X via Y
# instance handle   via     invocation
# ---------------   ---     -----------
# classes           via     self.root.classes
# abilities         via     self.root.abilities
# pp                via     self.root.pp
# general           via     self.root.general
# saves             via     self.root.saves
# attacks           via     self.root.attacks
# ac                via     self.root.ac
# feats             via     self.root.feats
# spells            via     self.root.spells
# divine            via     self.root.divine
# powers            via     self.root.powers
# inventory         via     self.root.inventory
# hp                via     self.root.hp
# skills            via     self.root.skills
#... if other instances are needed and the instance exists uniquely,
# add to the instance you need access to in the __init__ section the following:
#       self.hparent = parent # or handler if a wx instance, also add up chain
#       self.root = getRoot(self)
#       self.root.{instance handle} = self
#       # replace {instance handle} with your designation
# then, where you need access to the instance, simply add this to the instance
# that needs to reference
#       self.hparent = getRoot(self) # in the init section, if not already there
#       self.root = getRoot(self)    # also in the init
#   then to refer to the instance where you need it:
#       self.root.{instance handle}.{whatever you need}
#       # replace {instance handle} with your designation
#       # replace {whatever you need} with the attribute/method u want.

def safeGetAttr(node, label, defRetV=None):
    cna=node.attrib
    for key in cna:
        if key == label: return cna[key]
    return defRetV


########End of My global Stuff########
########Start of Main Node Handlers#######
class dnd3echar_handler(container_handler):
    """ Node handler for a dnd3e charactor
        <nodehandler name='?'  module='dnd3e' class='dnd3echar_handler2'  />
    """
    def __init__(self,xml_dom,tree_node):
        node_handler.__init__(self,xml_dom,tree_node)
        self.Version = "v1.901" #a 1.6000 general documentation, usage.

        print "dnd3echar_handler - version:", self.Version #m 1.6000

        self.hparent = None 
        self.frame = component.get('frame')
        self.child_handlers = {}
        self.new_child_handler('howtouse','HowTo use this tool',dnd3ehowto,'note')
        self.new_child_handler('general','GeneralInformation',dnd3egeneral,'gear')
        self.new_child_handler('inventory','MoneyAndInventory',dnd3einventory,'money')
        self.new_child_handler('character','ClassesAndStats',dnd3eclassnstats,'knight')
        self.new_child_handler('snf','SkillsAndFeats',dnd3eskillsnfeats,'book')
        self.new_child_handler('combat','Combat',dnd3ecombat,'spears')
        self.new_child_handler('snp','SpellsAndPowers',dnd3esnp,'flask')
        self.myeditor = None

    def new_child_handler(self,tag,text,handler_class,icon='gear'):
        node_list = self.xml.findall(tag)
        tree = self.tree
        i = self.tree.icons[icon]
        new_tree_node = tree.AppendItem(self.mytree_node,text,i,i)
        handler = handler_class(node_list[0],new_tree_node,self)
        tree.SetPyData(new_tree_node,handler)
        self.child_handlers[tag] = handler

    def get_design_panel(self,parent):
        return tabbed_panel(parent,self,1)

    def get_use_panel(self,parent):
        return tabbed_panel(parent,self,2)

    def tohtml(self):
        html_str = "<table><tr><td colspan=2 >"
        html_str += self.general.tohtml()+"</td></tr>"
        html_str += "<tr><td width='50%' valign=top >"+self.abilities.tohtml()
        html_str += "<P>" + self.saves.tohtml()
        html_str += "<P>" + self.attacks.tohtml()
        html_str += "<P>" + self.ac.tohtml()
        html_str += "<P>" + self.feats.tohtml()
        html_str += "<P>" + self.spells.tohtml()
        html_str += "<P>" + self.divine.tohtml()
        html_str += "<P>" + self.powers.tohtml()
        html_str += "<P>" + self.inventory.tohtml() +"</td>"
        html_str += "<td width='50%' valign=top >"+self.classes.tohtml()
        html_str += "<P>" + self.hp.tohtml()
        html_str += "<P>" + self.pp.tohtml()
        html_str += "<P>" + self.skills.tohtml() +"</td>"
        html_str += "</tr></table>"
        return html_str

    def about(self):
        """html_str = "<img src='" + dir_struct["icon"]
        html_str += "dnd3e_logo.gif' ><br><b>dnd3e Character Tool "
        html_str += self.Version+"</b>"
        html_str += "<br>by Dj Gilcrease<br>digitalxero@gmail.com"
        return html_str"""
        text = 'dnd3e Character Tool' + self.Version +'\n'
        text += 'by Dj Gilcrease digitalxero@gmail.com'
        return text

########Core Handlers are done now############
########Onto the Sub Nodes########
##Primary Sub Node##

class outline_panel(wx.Panel):
    def __init__(self, parent, handler, wnd, txt,):
        self.parent = parent #a 1.9001
        wx.Panel.__init__(self, parent, -1)
        self.panel = wnd(self,handler)
        self.sizer = wx.StaticBoxSizer(wx.StaticBox(self,-1,txt), wx.VERTICAL)

        self.sizer.Add(self.panel, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()

class dnd3e_char_child(node_handler):
    """ Node Handler for skill.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.char_hander = parent
        self.drag = False
        self.frame = component.get('frame')
        self.myeditor = None

    def on_drop(self,evt):
        pass

    def on_rclick(self,evt):
        pass

    def on_ldclick(self,evt):
        return

    def on_html(self,evt):
        html_str = self.tohtml()
        wnd = http_html_window(self.frame.note,-1)
        wnd.title = self.xml.get('name')
        self.frame.add_panel(wnd)
        wnd.SetPage(html_str)

    def get_design_panel(self,parent):
        pass

    def get_use_panel(self,parent):
        return self.get_design_panel(parent)

    def delete(self):
        pass

###Child Nodes Organized the way they are in the XML for easier viewing####  #m 1.5002 corrected typo.
class dnd3ehowto(dnd3e_char_child):  #m 1.5002 corrected string below to reflect "how to"
    """ Node Handler for how to instructions.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 

    def get_design_panel(self,parent):
        wnd = howto_panel(parent, self)
        wnd.title = "How To"
        return wnd

class howto_panel(wx.Panel):
    def __init__(self, parent, handler):
        wx.Panel.__init__(self, parent, -1)

        pname = handler.xml.set("name", 'How To')
        self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'How To'), wx.VERTICAL)
        self.xml = handler.xml
        n_list = self.xml.getchildren()
        for n in n_list:
            self.sizer.Add(wx.StaticText(self, -1, n.text), 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()

class dnd3egeneral(dnd3e_char_child):
    """ Node Handler for general information.   This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.root = getRoot(self) 
        self.root.general = self
        self.charName = self.get_char_name()

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,gen_grid,"General Information")
        wnd.title = "General Info"
        return wnd

    def tohtml(self):
        n_list = self.xml.getchildren()
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>General Information</th></tr><tr><td>"
        for n in n_list:
            debug(n)
            html_str += "<B>"+n.tag.capitalize() +":</B> "
            html_str += n.text + ", "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

    def on_name_change(self,name):
        self.char_hander.rename(name)
        self.charName = name 

    def get_char_name( self ):
        node = self.xml.findall( 'name' )[0]
        return node.text

class gen_grid(wx.grid.Grid):
    """grid for gen info"""
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'General')
        self.hparent = handler 

        wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.handler = handler
        n_list = handler.xml.getchildren()
        self.CreateGrid(len(n_list),2)
        self.SetRowLabelSize(0)
        self.SetColLabelSize(0)
        self.n_list = n_list
        i = 0
        for i in range(len(n_list)): self.refresh_row(i)

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.GetCellValue(row,col)
        self.n_list[row].text = value
        if row==0: self.handler.on_name_change(value)

    def refresh_row(self,rowi):
        self.SetCellValue(rowi,0,self.n_list[rowi].tag)
        self.SetReadOnly(rowi,0)
        self.SetCellValue(rowi,1,self.n_list[rowi].text)
        self.AutoSizeColumn(1)

class dnd3einventory(dnd3e_char_child):
    """ Node Handler for general information.   This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent
        self.root = getRoot(self)
        self.root.inventory = self

    def get_design_panel(self,parent):
        wnd = inventory_pane(parent, self) #outline_panel(parent,self,inventory_grid,"Inventory")
        wnd.title = "Inventory"
        return wnd

    def tohtml(self):
        n_list = self.xml.getchildren()
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>General Information</th></tr><tr><td>"
        for n in n_list:
            html_str += "<B>"+n.tag.capitalize()+":</B> "
            html_str += n.text + "<br>"
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

class inventory_pane(wx.Panel):
    def __init__(self, parent, handler):
        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.n_list = handler.xml.getchildren()
        self.autosize = False
        self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, "Inventory"), wx.VERTICAL)
        self.lang = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Languages")
        self.gear = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Gear")
        self.magic = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Magic")
        self.grid = wx.grid.Grid(self, wx.ID_ANY, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.grid.CreateGrid(len(self.n_list)-3,2)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelSize(0)
        for i in xrange(len(self.n_list)): self.refresh_row(i)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(self.grid, 1, wx.EXPAND)
        sizer1.Add(self.lang, 1, wx.EXPAND)
        self.sizer.Add(sizer1, 0, wx.EXPAND)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(self.gear, 1, wx.EXPAND)
        sizer2.Add(self.magic, 1, wx.EXPAND)
        self.sizer.Add(sizer2, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.lang)
        self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.gear)
        self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.magic)
        self.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self.on_cell_change, self.grid)

    def fillTextNode(self, name, value):
        if name == 'Languages': self.lang.SetValue(value)
        elif name == 'Gear': self.gear.SetValue(value)
        elif name == 'Magic': self.magic.SetValue(value)

    def onTextNodeChange(self, event):
        id = event.GetId()
        if id == self.gear.GetId():
            nodeName = 'Gear'
            value = self.gear.GetValue()
        elif id == self.magic.GetId():
            nodeName = 'Magic'
            value = self.magic.GetValue()
        elif id == self.lang.GetId():
            nodeName = 'Languages'
            value = self.lang.GetValue()
        for node in self.n_list:
            if node.tag == nodeName:
                debug(node)
                node.text = value

    def saveMoney(self, row, col):
        value = self.grid.GetCellValue(row, col)
        debug(self.n_list[row])
        self.n_list[row].text = value

    def on_cell_change(self, evt):
        row = evt.GetRow()
        col = evt.GetCol()
        self.grid.AutoSizeColumn(col)
        wx.CallAfter(self.saveMoney, row, col)

    def refresh_row(self, row):
        tagname = self.n_list[row].tag
        value = self.n_list[row].text
        if tagname == 'Gear': self.fillTextNode(tagname, value)
        elif tagname == 'Magic': self.fillTextNode(tagname, value)
        elif tagname == 'Languages': self.fillTextNode(tagname, value)
        else:
            self.grid.SetCellValue(row, 0, tagname)
            self.grid.SetReadOnly(row, 0)
            self.grid.SetCellValue(row, 1, value)
            self.grid.AutoSize()


class dnd3eclassnstats(dnd3e_char_child):
    """ Node handler for a dnd3e charactor
        <nodehandler name='?'  module='dnd3e' class='dnd3echar_handler2'  />
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.hparent = parent
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.frame = component.get('frame')
        self.child_handlers = {}
        self.new_child_handler('abilities','Abilities Scores',dnd3eability,'gear')
        self.new_child_handler('classes','Classes',dnd3eclasses,'knight')
        self.new_child_handler('saves','Saves',dnd3esaves,'skull')
        self.myeditor = None

    def new_child_handler(self,tag,text,handler_class,icon='gear'):
        node_list = self.xml.findall(tag)
        tree = self.tree
        i = self.tree.icons[icon]
        new_tree_node = tree.AppendItem(self.mytree_node,text,i,i)
        handler = handler_class(node_list[0],new_tree_node,self)
        tree.SetPyData(new_tree_node,handler)
        self.child_handlers[tag] = handler

    def get_design_panel(self,parent):
        return tabbed_panel(parent,self,1)

    def get_use_panel(self,parent):
        print 'here'
        return tabbed_panel(parent,self,2)

class class_char_child(node_handler):
    """ Node Handler for skill.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.char_hander = parent
        self.drag = False
        self.frame = component.get('frame')
        self.myeditor = None

    def on_drop(self,evt):
        pass

    def on_rclick(self,evt):
        pass

    def on_ldclick(self,evt):
        return

    def on_html(self,evt):
        html_str = self.tohtml()
        wnd = http_html_window(self.frame.note,-1)
        wnd.title = self.xml.get('name')
        self.frame.add_panel(wnd)
        wnd.SetPage(html_str)

    def get_design_panel(self,parent):
        pass

    def get_use_panel(self,parent):
        return self.get_design_panel(parent)

    def delete(self):
        pass

class dnd3eability(class_char_child):
    """ Node Handler for ability.   This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        class_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.root = getRoot(self)
        self.root.abilities = self 
        self.abilities = {}
        node_list = self.xml.findall('stat')
        tree = self.tree
        icons = tree.icons
        for n in node_list:
            name = n.get('abbr')
            self.abilities[name] = n
            new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] )
            tree.SetPyData( new_tree_node, self )

    def on_rclick( self, evt ):
        item = self.tree.GetSelection()
        name = self.tree.GetItemText( item )
        if not item == self.mytree_node:
            mod = self.get_mod( name )
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            chat = self.chat
            txt = '%s check: [1d20%s%s]' % ( name, mod1, mod )
            chat.ParsePost( txt, True, True )

    def get_mod(self,abbr):
        score = int(self.abilities[abbr].get('base'))
        mod = (score - 10) / 2
        mod = int(mod)
        return mod

    def set_score(self,abbr,score):
        if score >= 0:
            self.abilities[abbr].set("base",str(score))

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,abil_grid,"Abilities")
        wnd.title = "Abilities (edit)"
        return wnd

    def tohtml(self):
        html_str = """<table border='1' width=100%><tr BGCOLOR=#E9E9E9 ><th width='50%'>Ability</th>
                    <th>Base</th><th>Modifier</th></tr>"""
        node_list = self.xml.findall('stat')
        for n in node_list:
            name = n.get('name')
            abbr = n.get('abbr')
            base = n.get('base')
            mod = str(self.get_mod(abbr))
            if int(mod) >= 0:  mod1 = "+"
            else: mod1 = ""
            html_str = (html_str + "<tr ALIGN='center'><td>"+
                name+"</td><td>"+base+'</td><td>%s%s</td></tr>' % (mod1, mod))
        html_str = html_str + "</table>"
        return html_str

class abil_grid(wx.grid.Grid):
    """grid for abilities"""
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Stats')
        self.hparent = handler
        self.root = getRoot(self)

        wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.handler = handler
        stats = handler.xml.findall('stat')
        self.CreateGrid(len(stats),3)
        self.SetRowLabelSize(0)
        col_names = ['Ability','Score','Modifier']
        for i in range(len(col_names)): self.SetColLabelValue(i,col_names[i])
        self.stats = stats
        i = 0
        for i in range(len(stats)): self.refresh_row(i)
        self.char_wnd = None

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.GetCellValue(row,col)
        try:
            int(value)
            self.stats[row].set('base',value)
            self.refresh_row(row)
        except:
            self.SetCellValue(row,col,"0")
        if self.char_wnd: self.char_wnd.refresh_data()

    def refresh_row(self,rowi):
        s = self.stats[rowi]
        name = s.get('name')
        abbr = s.get('abbr')
        self.SetCellValue(rowi,0,name)
        self.SetReadOnly(rowi,0)
        self.SetCellValue(rowi,1,s.get('base'))
        self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr)))
        self.SetReadOnly(rowi,2)
        self.root.saves.refresh_data()
        self.root.attacks.refreshMRdata() 

    def on_size(self,evt):
        (w,h) = self.GetClientSizeTuple()
        cols = self.GetNumberCols()
        col_w = w/(cols+2)
        self.SetColSize(0,col_w*3)
        for i in range(1,cols): self.SetColSize(i,col_w)
        evt.Skip()
        self.Refresh()

    def refresh_data(self):
        for r in range(self.GetNumberRows()-1): self.refresh_row(r)

class dnd3eclasses(class_char_child):
    """ Node Handler for classes.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        class_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.root = getRoot(self)
        self.root.classes = self

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,class_panel,"Classes")
        wnd.title = "Classes"
        return wnd

    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Classes</th></tr><tr><td>"
        n_list = self.xml.getchildren()
        for n in n_list: html_str += n.get('name') + " ("+n.get('level')+"), "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

    def get_char_lvl( self, attr ):
        node_list = self.xml.findall('class')
        tot = 0 
        for n in node_list:
            lvl = n.get('level')
            tot += int(lvl)
            type = n.get('name')
            if attr == "level": return lvl
            elif attr == "class": return type
        if attr == "lvl":  return tot

    def get_class_lvl( self, classN ):
        node_list = self.xml.findall('class')
        for n in node_list:
            lvl = n.get('level')
            type = n.get('name')
            if classN == type: return lvl

class class_panel(wx.Panel):
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Class')

        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)

        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Class"), 0, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Class"), 0, wx.EXPAND)

        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()

        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)

        n_list = handler.xml.getchildren()
        self.n_list = n_list
        self.xml = handler.xml
        self.grid.CreateGrid(len(n_list),2,1)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelValue(0,"Class")
        self.grid.SetColLabelValue(1,"Level")
        for i in range(len(n_list)): self.refresh_row(i)
        self.temp_dom = None

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        try:
            int(value)
            self.n_list[row].set('level',value)
        except: self.grid.SetCellValue(row,col,"1")

    def refresh_row(self,i):
        n = self.n_list[i]
        name = n.get('name')
        level = n.get('level')
        self.grid.SetCellValue(i,0,name)
        self.grid.SetReadOnly(i,0)
        self.grid.SetCellValue(i,1,level)

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3eclasses.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        f_list = self.temp_dom.findall('class')
        opts = []
        for f in f_list: opts.append(f.get('name'))
        dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.refresh_row(self.grid.GetNumberRows()-1)
        dlg.Destroy()

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols)
        for i in range(0,cols): self.grid.SetColSize(i,col_w)


class dnd3esaves(class_char_child):
    """ Node Handler for saves.   This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        class_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.saveGridFrame = []

        tree = self.tree
        icons = self.tree.icons

        self.root = getRoot(self)
        self.root.saves = self 
        node_list = self.xml.findall('save')
        self.saves={}
        for n in node_list:
            name = n.get('name')
            self.saves[name] = n
            new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear'])
            tree.SetPyData(new_tree_node,self)


    def refresh_data(self): 
        # of the attack chart.
        # count backwards, maintains context despite "removes"
        for i in range(len(self.saveGridFrame)-1,-1,-1):
            x = self.saveGridFrame[i]
            if x == None: x.refresh_data()
            else: self.saveGridFrame.remove(x)

    def get_mod(self,name):
        save = self.saves[name]
        stat = save.get('stat')
        stat_mod = self.root.abilities.get_mod(stat)
        base = int(save.get('base'))
        miscmod = int(save.get('miscmod'))
        magmod = int(save.get('magmod'))
        total = stat_mod + base + miscmod + magmod
        return total

    def on_rclick(self,evt):
        item = self.tree.GetSelection()
        name = self.tree.GetItemText(item)
        if item == self.mytree_node:
            pass
            return 
        else:
            mod = self.get_mod(name)
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            chat = self.chat
            txt = '%s save: [1d20%s%s]' % (name, mod1, mod)
            chat.ParsePost( txt, True, True )

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,save_grid,"Saves")
        wnd.title = "Saves"
        return wnd

    def tohtml(self):
        html_str = """<table border='1' width=100% ><tr BGCOLOR=#E9E9E9 >
            <th width='30%'>Save</th>
            <th>Key</th><th>Base</th><th>Abil</th><th>Magic</th>
            <th>Misc</th><th>Total</th></tr>"""
        node_list = self.xml.findall('save')
        for n in node_list:
            name = n.get('name')
            stat = n.get('stat')
            base = n.get('base')
            html_str = html_str + "<tr ALIGN='center'><td>"+name+"</td><td>"+stat+"</td><td>"+base+"</td>"
            stat_mod = self.root.abilities.get_mod(stat)

            mag = n.get('magmod')
            misc = n.get('miscmod')
            mod = str(self.get_mod(name))
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            html_str = html_str + "<td>"+str(stat_mod)+"</td><td>"+mag+"</td>"
            html_str = html_str + '<td>'+misc+'</td><td>%s%s</td></tr>' % (mod1, mod)
        html_str = html_str + "</table>"
        return html_str

#mark6
class save_grid(wx.grid.Grid):
    """grid for saves"""
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Saves')
        self.hparent = handler 
        self.root = getRoot(self)
        wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.handler = handler
        saves = handler.xml.findall('save')
        self.CreateGrid(len(saves),7)
        self.SetRowLabelSize(0)
        col_names = ['Save','Key','base','Abil','Magic','Misc','Total']
        for i in range(len(col_names)): self.SetColLabelValue(i,col_names[i])
        self.saves = saves
        i = 0
        for i in range(len(saves)): self.refresh_row(i)
        climber = parent
        nameNode = climber.GetClassName()
        while nameNode != 'wxFrame':
            climber = climber.parent
            nameNode = climber.GetClassName()
        masterFrame=climber
        masterFrame.refresh_data=self.refresh_data
        handler.saveGridFrame.append(masterFrame)

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.GetCellValue(row,col)
        try:
            int(value)
            if col == 2: self.saves[row].set('base',value)
            elif col == 4: self.saves[row].set('magmod',value)
            elif col == 5: self.saves[row].set('miscmod',value)
            self.refresh_row(row)
        except: self.SetCellValue(row,col,"0")

    def refresh_row(self,rowi):
        s = self.saves[rowi]
        name = s.get('name')
        self.SetCellValue(rowi,0,name)
        self.SetReadOnly(rowi,0)
        stat = s.get('stat')
        self.SetCellValue(rowi,1,stat)
        self.SetReadOnly(rowi,1)
        self.SetCellValue(rowi,2,s.get('base'))
        self.SetCellValue(rowi,3,str(self.root.abilities.get_mod(stat)))
        self.SetReadOnly(rowi,3)
        self.SetCellValue(rowi,4,s.get('magmod'))
        self.SetCellValue(rowi,5,s.get('miscmod'))
        mod = str(self.handler.get_mod(name))
        self.SetCellValue(rowi,6,mod)
        self.SetReadOnly(rowi,6)

    def on_size(self,evt):
        (w,h) = self.GetClientSizeTuple()
        cols = self.GetNumberCols()
        col_w = w/(cols+2)
        self.SetColSize(0,col_w*3)
        for i in range(1,cols): self.SetColSize(i,col_w)
        evt.Skip()
        self.Refresh()

    def refresh_data(self):
        for r in range(self.GetNumberRows()): self.refresh_row(r)

class dnd3eskillsnfeats(dnd3e_char_child):
    """ Node handler for a dnd3e charactor
        <nodehandler name='?'  module='dnd3e' class='dnd3echar_handler2'  />
    """
    def __init__(self,xml_dom,tree_node,parent):
        self.hparent = parent 
        self.root = getRoot(self)
        node_handler.__init__(self,xml_dom,tree_node)
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.frame = component.get('frame')
        self.child_handlers = {}
        self.new_child_handler('skills','Skills',dnd3eskill,'book')
        self.new_child_handler('feats','Feats',dnd3efeats,'book')
        self.myeditor = None

    def new_child_handler(self,tag,text,handler_class,icon='gear'):
        node_list = self.xml.findall(tag)
        tree = self.tree
        i = self.tree.icons[icon]
        new_tree_node = tree.AppendItem(self.mytree_node,text,i,i)
        handler = handler_class(node_list[0],new_tree_node,self)
        tree.SetPyData(new_tree_node,handler)
        self.child_handlers[tag] = handler

    def get_design_panel(self,parent):
        return tabbed_panel(parent,self,1)

    def get_use_panel(self,parent):
        return tabbed_panel(parent,self,2)

class skills_char_child(node_handler):
    """ Node Handler for skill.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.char_hander = parent
        self.drag = False
        self.frame = component.get('frame')
        self.myeditor = None

    def on_drop(self,evt):
        pass

    def on_rclick(self,evt):
        pass

    def on_ldclick(self,evt):
        return

    def on_html(self,evt):
        html_str = self.tohtml()
        wnd = http_html_window(self.frame.note,-1)
        wnd.title = self.xml.get('name')
        self.frame.add_panel(wnd)
        wnd.SetPage(html_str)

    def get_design_panel(self,parent):
        pass

    def get_use_panel(self,parent):
        return self.get_design_panel(parent)

    def delete(self):
        pass

class dnd3eskill(skills_char_child):
    """ Node Handler for skill.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        self.hparent = parent
        self.root = getRoot(self)
        self.root.skills = self

        skills_char_child.__init__(self,xml_dom,tree_node,parent)
        tree = self.tree
        icons = self.tree.icons
        node_list = self.xml.findall('skill')

        self.skills={}
        for n in node_list:
            name = n.get('name')
            self.skills[name] = n
            skill_check = self.skills[name]
            ranks = int(skill_check.get('rank'))
            trained = int(skill_check.get('untrained'))
            if ranks > 0 or trained == 1:
                new_tree_node = tree.AppendItem(self.mytree_node,name,
                                            icons['gear'],icons['gear'])
            else: continue
            tree.SetPyData(new_tree_node,self)


    def refresh_skills(self):
        #Adding this so when you update the grid the tree will reflect
        #The change. -mgt
        tree = self.tree
        icons = self.tree.icons
        tree.CollapseAndReset(self.mytree_node)
        node_list = self.xml.findall('skill')
        self.skills={}
        for n in node_list:
            name = n.get('name')
            self.skills[name] = n
            skill_check = self.skills[name]
            ranks = int(skill_check.get('rank'))
            trained = int(skill_check.get('untrained'))
            if ranks > 0 or trained == 1:
                new_tree_node = tree.AppendItem(self.mytree_node,name,
                                            icons['gear'],icons['gear'])
            else: continue
            tree.SetPyData(new_tree_node,self)

    def get_mod(self,name):
        skill = self.skills[name]
        stat = skill.get('stat')
        stat_mod = self.root.abilities.get_mod(stat)
        rank = int(skill.get('rank'))
        misc = int(skill.get('misc'))
        total = stat_mod + rank + misc
        return total

    def on_rclick(self,evt):
        item = self.tree.GetSelection()
        name = self.tree.GetItemText(item)
        if item == self.mytree_node: return
        ac = self.root.ac.get_check_pen() 
        skill = self.skills[name]
        untr = skill.get('untrained')
        rank = skill.get('rank') 
        if eval('%s == 0' % (untr)):
            if eval('%s == 0' % (rank)): 
                res = 'You fumble around, accomplishing nothing'
                txt = '%s Skill Check: %s' % (name, res)
                chat = self.chat
                chat.Post(txt,True,True)
                return 
        armor = ''
        acCp = ''
        if ac < 0:
            armorCheck = int(skill.get('armorcheck'))
            if armorCheck == 1:
                acCp=ac
                armor = '(includes Armor Penalty of %s)' % (acCp)
        if item == self.mytree_node:
            dnd3e_char_child.on_ldclick(self,evt)
        else:
            mod = self.get_mod(name)
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            chat = self.chat
            txt = '%s Skill Check: [1d20%s%s%s] %s' % (
                    name, mod1, mod, acCp, armor)
            chat.ParsePost(txt,True,True)

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,skill_grid,"Skills")
        wnd.title = "Skills (edit)"
        return wnd

    def tohtml(self):
        html_str = """<table border='1' width=100% ><tr BGCOLOR=#E9E9E9 >
                    <th width='30%'>Skill</th><th>Key</th>
                    <th>Rank</th><th>Abil</th><th>Misc</th><th>Total</th></tr>"""
        node_list = self.xml.findall('skill')

        for n in node_list:
            name = n.get('name')
            stat = n.get('stat')
            rank = n.get('rank')
            untr = n.get('untrained') 
            #Filter unsuable skills out of pretty print -mgt
            if eval('%s > 0' % (rank)) or eval('%s == 1' % (untr)):
                if eval('%s >=1' % (rank)):
                    html_str += "<tr ALIGN='center' bgcolor='#CCCCFF'><td>"
                    html_str += name+"</td><td>"+stat+"</td><td>"+rank+"</td>"
                elif eval('%s == 1' % (untr)):
                    html_str += "<tr ALIGN='center' bgcolor='#C0FF40'><td>" 
                    html_str += name+"</td><td>"+stat+"</td><td>"+rank+"</td>"
                else:
                    html_str += "<tr ALIGN='center'><td>"+name+"</td><td>"
                    html_str += stat+"</td><td>"+rank+"</td>"
            else: continue
            stat_mod = self.root.abilities.get_mod(stat)
            misc = n.get('misc')
            mod = str(self.get_mod(name))
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            html_str += "<td>"+str(stat_mod)+"</td><td>"+misc #m 1.6009 str()
            html_str += '</td><td>%s%s</td></tr>' % (mod1, mod)
        html_str = html_str + "</table>"
        return html_str


class skill_grid(wx.grid.Grid):
    """ panel for skills """
    def __init__(self, parent, handler):
        self.hparent = handler
        self.root = getRoot(self)
        pname = handler.xml.set("name", 'Skills')

        wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.handler = handler
        skills = handler.xml.findall('skill')

        self.CreateGrid(len(skills),6)
        self.SetRowLabelSize(0)
        col_names = ['Skill','Key','Rank','Abil','Misc','Total']
        for i in range(len(col_names)):
            self.SetColLabelValue(i,col_names[i])
        rowi = 0
        self.skills = skills
        for i in range(len(skills)): self.refresh_row(i)

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.GetCellValue(row,col)
        try:
            int(value)
            if col == 2: self.skills[row].set('rank',value)
            elif col ==4: self.skills[row].set('misc',value)
            self.refresh_row(row)
        except: self.SetCellValue(row,col,"0")
        self.handler.refresh_skills()

    def refresh_row(self,rowi):
        s = self.skills[rowi]
        name = s.get('name')
        self.SetCellValue(rowi,0,name)
        self.SetReadOnly(rowi,0)
        stat = s.get('stat')
        self.SetCellValue(rowi,1,stat)
        self.SetReadOnly(rowi,1)
        self.SetCellValue(rowi,2,s.get('rank'))
        if self.root.abilities: stat_mod=self.root.abilities.get_mod(stat)
        else:
            stat_mod = -6 
            print "Please advise dnd3e maintainer alert 1.5002 raised"

        self.SetCellValue(rowi,3,str(stat_mod))
        self.SetReadOnly(rowi,3)
        self.SetCellValue(rowi,4,s.get('misc'))
        mod = str(self.handler.get_mod(name))
        self.SetCellValue(rowi,5,mod)
        self.SetReadOnly(rowi,5)

    def on_size(self,evt):
        (w,h) = self.GetClientSizeTuple()
        cols = self.GetNumberCols()
        col_w = w/(cols+2)
        self.SetColSize(0,col_w*3)
        for i in range(1,cols): self.SetColSize(i,col_w)
        evt.Skip()
        self.Refresh()

    def refresh_data(self):
        for r in range(self.GetNumberRows()): self.refresh_row(r)


class dnd3efeats(skills_char_child):
    """ Node Handler for classes.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        skills_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent
        self.root = getRoot(self) 
        self.root.feats = self

    def get_design_panel(self,parent):
        setTitle="Feats - " + self.root.general.charName
        wnd = outline_panel(parent,self,feat_panel,setTitle)
        wnd.title = "Feats"
        return wnd

    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Feats</th></tr><tr><td>"
        n_list = self.xml.getchildren()
        for n in n_list:
            html_str += n.get('name')+ ", "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

class feat_panel(wx.Panel):
    def __init__(self, parent, handler):

        self.hparent = handler 
        self.root = getRoot(self) 
        pname = handler.xml.set("name", 'Feats')
        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Feat"), 0, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Feat"), 0, wx.EXPAND)
        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        n_list = handler.xml.getchildren()
        self.n_list = n_list
        self.xml = handler.xml
        self.grid.CreateGrid(len(n_list),3,1)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelValue(0,"Feat")
        self.grid.SetColLabelValue(1,"Type")
        self.grid.SetColLabelValue(2,"Reference") 
        for i in range(len(n_list)): self.refresh_row(i)
        self.temp_dom = None

    def refresh_row(self,i):
        feat = self.n_list[i]
        name = feat.get('name')
        type = feat.get('type')
        desc = feat.get('desc')
        self.grid.SetCellValue(i,0,name)
        self.grid.SetReadOnly(i,0)
        self.grid.SetCellValue(i,1,type)
        self.grid.SetReadOnly(i,1)
        self.grid.SetCellValue(i,2,desc)
        self.grid.SetReadOnly(i,2)

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):

        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3efeats.xml")
            temp_dom = tree.getroot()
        f_list = temp_dom.findall('feat')
        opts = []
        for f in f_list:
            opts.append(f.get('name') + "  -  [" +
                     f.get('type') + "]  -  " + f.get('desc'))
        dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.refresh_row(self.grid.GetNumberRows()-1)
        dlg.Destroy()


    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols)
        for i in range(0,cols):
            self.grid.SetColSize(i,col_w)

class dnd3ecombat(dnd3e_char_child):
    """ Node handler for a dnd3e charactor
        <nodehandler name='?'  module='dnd3e' class='dnd3echar_handler2'  />
    """
    def __init__(self,xml_dom,tree_node,parent):

        node_handler.__init__(self,xml_dom,tree_node)

        self.hparent = parent 
        self.root = getRoot(self) 
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.frame = component.get('frame')
        self.child_handlers = {}
        self.new_child_handler('hp','Hit Points',dnd3ehp,'gear')
        self.new_child_handler('attacks','Attacks',dnd3eattacks,'spears')
        self.new_child_handler('ac','Armor',dnd3earmor,'spears')
        self.myeditor = None

    def new_child_handler(self,tag,text,handler_class,icon='gear'):
        node_list = self.xml.findall(tag)
        tree = self.tree
        i = self.tree.icons[icon]
        new_tree_node = tree.AppendItem(self.mytree_node,text,i,i)
        handler = handler_class(node_list[0],new_tree_node,self)
        tree.SetPyData(new_tree_node,handler)
        self.child_handlers[tag] = handler

    def get_design_panel(self,parent):
        return tabbed_panel(parent,self,1)

    def get_use_panel(self,parent):
        return tabbed_panel(parent,self,2)


class combat_char_child(node_handler):
    """ Node Handler for combat.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.char_hander = parent
        self.drag = False
        self.frame = component.get('frame')
        self.myeditor = None

    def on_drop(self,evt):
        pass

    def on_rclick(self,evt):
        pass

    def on_ldclick(self,evt):
        return

    def on_html(self,evt):
        html_str = self.tohtml()
        wnd = http_html_window(self.frame.note,-1)
        wnd.title = self.xml.get('name')
        self.frame.add_panel(wnd)
        wnd.SetPage(html_str)

    def get_design_panel(self,parent):
        pass

    def get_use_panel(self,parent):
        return self.get_design_panel(parent)

    def delete(self):
        pass

class dnd3ehp(combat_char_child):
    """ Node Handler for hit points.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        combat_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.root = getRoot(self)
        self.root.hp = self 

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,hp_panel,"Hit Points")
        wnd.title = "Hit Points"
        return wnd

    def on_rclick( self, evt ):
        chp = self.xml.get('current')
        mhp = self.xml.get('max')
        txt = '((HP: %s / %s))' % ( chp, mhp )
        self.chat.ParsePost( txt, True, True )

    def tohtml(self):
        html_str = "<table width=100% border=1 >"
        html_str += "<tr BGCOLOR=#E9E9E9 ><th colspan=4>Hit Points</th></tr>"
        html_str += "<tr><th>Max:</th>"
        html_str += "<td>"+self.xml.get('max')+"</td>"
        html_str += "<th>Current:</th>"
        html_str += "<td>"+self.xml.get('current')+"</td>"
        html_str += "</tr></table>"
        return html_str

class hp_panel(wx.Panel):
    def __init__(self, parent, handler):
        wx.Panel.__init__(self, parent, -1)
        self.hparent = handler 
        pname = handler.xml.set("name", 'HitPoints')
        self.sizer = wx.FlexGridSizer(2, 4, 2, 2)  # rows, cols, hgap, vgap
        self.xml = handler.xml
        self.sizer.AddMany([ (wx.StaticText(self, -1, "HP Current:"),   0,
           wx.ALIGN_CENTER_VERTICAL),
          (wx.TextCtrl(self, HP_CUR,
           self.xml.get('current')),   0, wx.EXPAND),
          (wx.StaticText(self, -1, "HP Max:"), 0, wx.ALIGN_CENTER_VERTICAL),
          (wx.TextCtrl(self, HP_MAX, self.xml.get('max')),
           0, wx.EXPAND),
         ])
        self.sizer.AddGrowableCol(1)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_TEXT, self.on_text, id=HP_MAX)
        self.Bind(wx.EVT_TEXT, self.on_text, id=HP_CUR)

    def on_text(self,evt):
        id = evt.GetId()
        if id == HP_CUR: self.xml.set('current',evt.GetString())
        elif id == HP_MAX: self.xml.set('max',evt.GetString())

    def on_size(self,evt):
        s = self.GetClientSizeTuple()
        self.sizer.SetDimension(0,0,s[0],s[1])

class dnd3eattacks(combat_char_child):
    """ Node Handler for attacks.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        combat_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent
        self.root = getRoot(self)
        self.root.attacks = self 
        self.mrFrame = [] 
        self.updateFootNotes = False
        self.updateFootNotes = False
        self.html_str = "<html><body>"
        self.html_str += ("<br>  This character has weapons with no "+
             "footnotes.  This program will "+
             "add footnotes to the weapons which have names that still match "+
             "the orginal names.  If you have changed the weapon name, you "+
             "will see some weapons with a footnote of 'X', you will have "+
             "to either delete and re-add the weapon, or research "+
             "and add the correct footnotes for the weapon.\n"+
             "<br>  Please be aware, that only the bow/sling footnote is "+
             "being used to affect changes to rolls; implemenation of other "+
             "footnotes to automaticly adjust rolls will be completed as "+
             "soon as time allows." +
             "<br><br>Update to character:"+self.root.general.charName+
             "<br><br>"+
             """<table border='1' width=100% ><tr BGCOLOR=#E9E9E9 >
              <th width='80%'>Weapon Name</th><th>Added Footnote</th></tr>\n""")
        self.temp_dom={}
        node_list = self.xml.findall('melee')
        self.melee = node_list[0]
        node_list = self.xml.findall('ranged')
        self.ranged = node_list[0]
        self.refresh_weapons() 
        if self.updateFootNotes == True:
            self.updateFootNotes = False
            name = self.root.general.charName
            self.html_str +=  "</table>"
            self.html_str +=  "</body> </html> "
            masterFrame = self.root.frame
            title = name+"'s weapons' update to have footnotes"
            fnFrame = wx.Frame(masterFrame, -1, title)
            fnFrame.panel = wx.html.HtmlWindow(fnFrame,-1)
            fnFrame.panel.SetPage(self.html_str)
            fnFrame.Show()



    def refreshMRdata(self): 
        # of the attack chart.
        # count backwards, maintains context despite "removes"
        for i in range(len(self.mrFrame)-1,-1,-1):
            x = self.mrFrame[i]
            if x == None: x.refreshMRdata() #a 1.9001
            else: self.mrFrame.remove(x)

    def refresh_weapons(self):
        self.weapons = {}

        tree = self.tree
        icons = self.tree.icons
        tree.CollapseAndReset(self.mytree_node)
        node_list = self.xml.findall('weapon')
        for n in node_list:
            name = n.get('name')
            fn = safeGetAttr(n,'fn')
            if fn == None:
                self.updateFootNotes=True
                self.updateFootN(n) 
            new_tree_node = tree.AppendItem(
                self.mytree_node,name,icons['sword'],icons['sword'])
            tree.SetPyData(new_tree_node,self)
            self.weapons[name]=n

    def updateFootN(self,n):
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml")
            self.temp_dom = tree.getroot()
        nameF = n.get('name')
        w_list = self.temp_dom.findall('weapon')
        found = False
        for w in w_list:
            if nameF == w.get('name'):
                found = True
                fnN = safeGetAttr(n,'fn')
                if fnN == None or fnN == 'None':
                    fnW = w.get('fn')
                    self.html_str += ("<tr ALIGN='center'><td>"+nameF+"</td>"+
                                     "<td>"+fnW+"</td></tr>\n")
                    n.set('fn',fnW)
                break
        if not found:
            self.html_str += ("<tr ALIGN='center'><td>"+nameF+" - Custom "+
              "Weapon, research "+
              "and update manually; setting footnote to indicate custom</td>"+
                                     "<td>"+'X'+"</td></tr>\n")
            n.set('fn','X')

    def get_mod(self,type='m'):
        (base, base2, base3, base4, base5, base6, stat_mod, misc) \
            = self.get_attack_data(type)
        return int(base + misc + int(stat_mod))

    def get_attack_data(self,type='m'):
        if type=='m' or type=='0':
            stat = 'Str' 
            temp = self.melee
        else:
            stat = 'Dex' 
            temp = self.ranged
        stat_mod = -7
        stat_mod = self.root.abilities.get_mod(stat)
        base = int(temp.get('base'))
        base2 = int(temp.get('second'))
        base3 = int(temp.get('third'))
        base4 = int(temp.get('forth'))
        base5 = int(temp.get('fifth'))
        base6 = int(temp.get('sixth'))
        misc = int(temp.get('misc'))
        return (base, base2, base3, base4, base5, base6, stat_mod ,misc)

    def on_rclick(self,evt):
        item = self.tree.GetSelection()

        name = self.tree.GetItemText(item)
        if item == self.mytree_node:
            return 
        else:

            mod = int(self.weapons[name].get('mod'))
            wepMod = mod #a 1.5008
            footNotes = safeGetAttr(self.weapons[name],'fn','')
            cat = self.weapons[name].get('category') #a1.6001
            result = split(cat,"-",2) #a 1.6001
            if len(result) < 2: #a 1.6021 this if & else
                print "warning: 1.6002 unable to interpret weapon category"
                print "format 'type weapon-[Range|Melee]', probably missing"
                print "the hyphen.  Assuming Melee"
                print "weapon name: ",name
                tres="Melee"
            else:
                tres=result[1]
            if tres == 'Melee': #a 1.6001   #m 1.6022 use of tres here and...
                rangeOrMelee = 'm' #a 1.5008 code demote for next comment block
            elif tres == 'Ranged': #m 1.6001 (was just else) #m 1.6022 here
                rangeOrMelee = 'r' #a 1.5008
            else:#a 1.6001 add this whole else clause.
                print "warning: 1.6001 unable to interpret weapon category"
                print "treating weapon as Melee, please correct xml"
                print "weapon name:",name
                rangeOrMelee ='m'
            mod = mod + self.get_mod(rangeOrMelee) #a 1.5008
            chat = self.chat
            dmg = self.weapons[name].get('damage')

            #a 1.6003 start code fix instance a
            result = split(dmg,"/",2)
            dmg = result[0]
            monkLvl = self.root.classes.get_class_lvl('Monk') # a 1.5002
            if dmg == "Monk Med":
                if monkLvl == None:     #a 1.5009
                    txt = 'Attempting to use monk attack, but has no monk '
                    txt += 'levels, please choose a different attack.'
                    chat.ParsePost( txt, True, True ) #a 1.5009
                    return #a 1.5009
                else:   #a 1.5009
                    lvl=int(monkLvl)
                    if lvl <= 3: dmg = "1d6"
                    elif lvl <= 7: dmg = "1d8"
                    elif lvl <= 11: dmg = "1d10"
                    elif lvl <= 15: dmg = "2d6"
                    elif lvl <= 19: dmg = "2d8"
                    elif lvl <= 20: dmg = "2d10"
            if dmg == "Monk Small":
                if monkLvl == None:
                    txt = 'Attempting to use monk attack, but has no monk '
                    txt += 'levels, please choose a different attack.'
                    chat.ParsePost( txt, True, True )
                    return
                else: 
                    lvl=int(monkLvl)
                    if lvl <= 3: dmg = "1d4"
                    elif lvl <= 7: dmg = "1d6"
                    elif lvl <= 11: dmg = "1d8"
                    elif lvl <= 15: dmg = "1d10"
                    elif lvl <= 20: dmg = "2d6"
            flu = ''
            str_mod = self.root.abilities.get_mod('Str') #a 1.5007,11,12,13
            if rangeOrMelee == 'r':                     #a 1.5008

                if find(footNotes,'b') > -1:
                    if str_mod >= 0: str_mod = 0 
                else:  str_mod = 0
            mod2 = ""
            if str_mod >= 0: mod2 = "+"
            aStrengthMod = mod2 + str(str_mod) #a 1.5008 applicable strength mod
            if find(name ,"Flurry of Blows") > -1: #a 1.6012
                flu = '-2'
            (base, base2, base3, base4, base5, base6, stat_mod, misc)\
                = self.get_attack_data(rangeOrMelee)  #a 1.5008
            self.sendRoll(base ,stat_mod,misc,wepMod,name,flu,dmg,
                aStrengthMod,'',True,rollAnyWay=True)
            if flu != '':
                self.sendRoll(base ,stat_mod,misc,wepMod,name,flu,dmg,
                    aStrengthMod) #a 1.6021

            self.sendRoll(base2,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod)
            self.sendRoll(base3,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod)
            self.sendRoll(base4,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod)
            self.sendRoll(base5,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod)
            self.sendRoll(base6,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod)


    def sendRoll(self,base,stat_mod,misc,wepMod,name,flu,dmg,aStrengthMod,
        spacer="",pname=False,rollAnyWay=False):
        if base != 0 or rollAnyWay:
            base = base + int(stat_mod) + misc + wepMod #m 1.5008
            if base >= 0: mod1 = "+"
            else: mod1 = ""
            txt = '%s ' % (spacer)
            txt += '%s Attack Roll: <b>[1d20%s%s%s]</b>' % (name, mod1, base, flu)
            txt += ' ===> Damage: <b>[%s%s]</b>' % (dmg, aStrengthMod)
            self.chat.ParsePost( txt, True, True )

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,attack_panel,"Attacks")
        wnd.title = "Attacks"
        return wnd

    def tohtml(self):
        melee = self.get_attack_data('m')
        ranged = self.get_attack_data('r')
        html_str = ("""<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 >"""+
          "<th>Attack</th><th>Total</th><th >Base</th>"+
          "<th>Abil</th><th>Misc</th></tr>")
        html_str += "<tr ALIGN='center' ><th >Melee:</th>"
        html_str += "<td>"+str(melee[0]+melee[1]+melee[2])+"</td>"
        html_str += "<td>"+str(melee[0])+"</td>"
        html_str += "<td>"+str(melee[1])+"</td>"
        html_str += "<td>"+str(melee[2])+"</td></tr>"

        html_str += "<tr ALIGN='center' ><th >Ranged:</th>"
        html_str += "<td>"+str(ranged[0]+ranged[1]+ranged[2])+"</td>"
        html_str += "<td>"+str(ranged[0])+"</td>"
        html_str += "<td>"+str(ranged[1])+"</td>"
        html_str += "<td>"+str(ranged[2])+"</td></tr></table>"

        n_list = self.xml.findall('weapon')
        for n in n_list:
            mod = n.get('mod')
            if mod >= 0: mod1 = "+"
            else: mod1 = ""
            ran = n.get('range')
            total = str(int(mod) + self.get_mod(ran))
            html_str += """<P><table width=100% border=1 ><tr BGCOLOR=#E9E9E9 >
                    <th colspan=2>Weapon</th>
                    <th>Attack</th><th >Damage</th><th>Critical</th></tr>"""
            html_str += "<tr ALIGN='center' ><td  colspan=2>"
            html_str += n.get('name')+"</td><td>"+total+"</td>"
            html_str += "<td>"+n.get('damage')+"</td><td>"
            html_str += n.get('critical')+"</td></tr>"
            html_str += """<tr BGCOLOR=#E9E9E9 ><th>Range</th><th>Weight</th>
                        <th>Type</th><th>Size</th><th>Misc Mod</th></tr>"""
            html_str += "<tr ALIGN='center'><td>"+ran+"</td><td>"
            html_str += n.get('weight')+"</td>"
            html_str += "<td>"+n.get('type')+"</td><td>"
            html_str += n.get('size')+"</td>"
            html_str += '<td>%s%s</td></tr>'  % (mod1, mod)
            html_str += """<tr><th BGCOLOR=#E9E9E9 colspan=2>Footnotes:</th>"""
            html_str += "<th colspan=3>"+safeGetAttr(n,'fn','')+"</th></tr>"
            html_str += '</table>'
        return html_str

class attack_grid(wx.grid.Grid):
    """grid for attacks"""
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Melee')
        self.hparent = handler 
        wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)

        self.root = getRoot(self) #a 1.9001
        self.parent = parent
        self.handler = handler
        self.rows = (self.handler.melee,self.handler.ranged)
        self.CreateGrid(2,10)
        self.SetRowLabelSize(0)
        col_names = ['Type','base','base 2','base 3','base 4','base 5',
            'base 6','abil','misc','Total']
        for i in range(len(col_names)): self.SetColLabelValue(i,col_names[i])
        self.SetCellValue(0,0,"Melee")
        self.SetCellValue(1,0,"Ranged")
        self.refresh_data()
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        climber = parent
        nameNode = climber.GetClassName()
        while nameNode != 'wxFrame':
            climber = climber.parent
            nameNode = climber.GetClassName()
        masterFrame=climber
        masterFrame.refreshMRdata=self.refresh_data
        handler.mrFrame.append(masterFrame)


    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.GetCellValue(row,col)
        try:
            int(value)
            if col==1: self.rows[row].set('base',value)
            elif col==2: self.rows[row].set('second',value)
            elif col==3: self.rows[row].set('third',value)
            elif col==4: self.rows[row].set('forth',value)
            elif col==5: self.rows[row].set('fifth',value)
            elif col==6: self.rows[row].set('sixth',value)
            elif col==8: self.rows[row].set('misc',value)
            self.parent.refresh_data()
        except: self.SetCellValue(row,col,"0")

    def refresh_data(self):
        melee = self.handler.get_attack_data('m')
        ranged = self.handler.get_attack_data('r')
        tmelee = int(melee[0]) + int(melee[6]) + int(melee[7])
        tranged = int(ranged[0]) + int(ranged[6]) + int(ranged[7])
        for i in range(0,8):    #a 1.5005
            self.SetCellValue(0,i+1,str(melee[i]))
            self.SetCellValue(1,i+1,str(ranged[i]))
        self.SetCellValue(0,9,str(tmelee))
        self.SetCellValue(1,9,str(tranged))
        self.SetReadOnly(0,0)
        self.SetReadOnly(1,0)
        self.SetReadOnly(0,7)
        self.SetReadOnly(1,7)
        self.SetReadOnly(0,9)
        self.SetReadOnly(1,9)

    def on_size(self,evt):
        (w,h) = self.GetClientSizeTuple()
        cols = self.GetNumberCols()
        col_w = w/(cols+1)
        self.SetColSize(0,col_w*2)
        for i in range(1,cols): self.SetColSize(i,col_w)
        evt.Skip()
        self.Refresh()

class weapon_panel(wx.Panel):
    def __init__(self, parent, handler):
        self.hparent = handler
        self.root = getRoot(self)
        pname = handler.xml.set("name", 'Weapons')
        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer2 = wx.BoxSizer(wx.HORIZONTAL)
        sizer2.Add(wx.Button(self, 10, "Remove Weapon"), 0, wx.EXPAND)
        sizer2.Add(wx.Size(10,10))
        sizer2.Add(wx.Button(self, 20, "Add Weapon"), 0, wx.EXPAND)
        sizer.Add(sizer2, 0, wx.EXPAND)
        sizer.Add(wx.StaticText(self, -1, "Right click a weapon's footnote to see what the footnotes mean."),0, wx.EXPAND)#a 1.5012
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.sizer2 = sizer2
        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.on_gridRclick)#a 1.5012

        n_list = handler.xml.findall('weapon')
        self.n_list = n_list
        self.xml = handler.xml
        self.handler = handler
        self.colAttr = ['name','damage','mod','critical','type','weight',
                    'range','size','Total','fn',    'comment']
        col_names = ['Name','Damage','To hit\nmod','Critical','Type','Weight',
                    'Range','Size','Total','Foot\nnotes','Comment']
        gridColCount=len(col_names)
        self.grid.CreateGrid(len(n_list),gridColCount,1)
        self.grid.SetRowLabelSize(0)
        for i in range(gridColCount):  self.grid.SetColLabelValue(i,col_names[i])
        self.refresh_data()
        self.temp_dom = None


    #mark4
    #a 1.5012 add entire method.
    def on_gridRclick(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        if col == 9 and value != 'None':
            n = self.n_list[row]
            name = n.get('name')
            handler = self.hparent
            # A handler is a node, and nodes have a reference to
            # the master frame
            masterFrame = handler.frame
            title = name+"'s Special Weapon Characteristics"
            fnFrame = wx.Frame(masterFrame, -1, title)
            fnFrame.panel = wx.html.HtmlWindow(fnFrame,-1)
            if not self.temp_dom:
                tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml")
                xml_dom = tree.getroot()
                self.temp_dom = xml_dom
            f_list = self.temp_dom.findall('f') # the footnotes
            n = self.n_list[row]
            name = n.get('name')
            footnotes = n.get('fn')
            html_str = "<html><body>"
            html_str += """<table border='1' width=100% ><tr BGCOLOR=#E9E9E9 >
                        <th width='10%'>Note</th><th>Description</th></tr>\n"""
            if footnotes == "":
                html_str += "<tr ALIGN='center'><td></td>"
                html_str += "  <td>This weapon has no footnotes</td></tr>"
            for i in range(len(footnotes)):
                aNote=footnotes[i]
                found=False
                for f in f_list:
                    if f.get('mark') == aNote:
                        found=True
                        text=f.get('txt')
                        html_str += ("<tr ALIGN='center'><td>"+aNote+"</td>"+
                                     "<td>"+text+"</td></tr>\n")
                if not found:
                    html_str += ("<tr ALIGN='center'><td>"+aNote+"</td>"+
                       "<td>is not a recognized footnote</td></tr>\n")

            html_str +=  "</table>"
            html_str +=  "</body> </html> "
            fnFrame.panel.SetPage(html_str)
            fnFrame.Show()
            return
        pass


    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        if col == 2 and not int(value): # special case for mod, demoted
            value = "0" #a 5.012 demoted
            self.n_list[row].set('mod',value) # a 5.012 demoted
        if not (col == 9 and value == "None" and
                self.n_list[row].get('fn') == "None"
                ): #a 5.012 special case for footnotes
            self.n_list[row].set(self.colAttr[col],value)#a 5.012


    def refresh_row(self,i):
        n = self.n_list[i]
        fn = n.get('fn')
        name = n.get('name')
        mod = n.get('mod')
        ran = n.get('range')
        total = str(int(mod) + self.handler.get_mod(ran))
        self.grid.SetCellValue(i,0,name)
        self.grid.SetCellValue(i,1,n.get('damage'))
        self.grid.SetCellValue(i,2,mod)
        self.grid.SetCellValue(i,3,n.get('critical'))
        self.grid.SetCellValue(i,4,n.get('type'))
        self.grid.SetCellValue(i,5,n.get('weight'))
        self.grid.SetCellValue(i,6,ran)
        self.grid.SetCellValue(i,7,n.get('size') )
        self.grid.SetCellValue(i,8,total)
        self.grid.SetCellValue(i,9,safeGetAttr(n,'fn','None')) #a 1.5012
        self.grid.SetCellValue(i,10,safeGetAttr(n,'comment','')) #a 1.5012
        self.grid.SetReadOnly(i,8)

    def on_remove(self,evt): #o 1.6011 correcting wrongful deletion
        rows = self.grid.GetNumberRows()
        for i in range(rows-1,-1,-1):   #a 1.6011 or you lose context
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])
                self.n_list = self.xml.findall('weapon')
                self.handler.refresh_weapons()

    def on_add(self,evt):
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        f_list = self.temp_dom.findall('weapon')
        opts = []
        for f in f_list:
            opts.append(f.get('name'))
        dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.n_list = self.xml.findall('weapon')
            self.refresh_row(self.grid.GetNumberRows()-1)
            self.handler.refresh_weapons()
        dlg.Destroy()

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-40)
        self.sizer.SetDimension(0,s[1]-40,s[0],25)
        self.sizer2.SetDimension(0,s[1]-15,s[0],15)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols+1)
        self.grid.SetColSize(0,col_w*2)
        for i in range(1,cols): self.grid.SetColSize(i,col_w)

    def refresh_data(self):
        for i in range(len(self.n_list)): self.refresh_row(i)


class attack_panel(wx.Panel):
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Melee')
        self.parent = parent #a 1.9001
        wx.Panel.__init__(self, parent, -1)
        self.a_grid = attack_grid(self, handler)
        self.w_panel = weapon_panel(self, handler)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.a_grid, 1, wx.EXPAND)
        self.sizer.Add(self.w_panel, 2, wx.EXPAND)
        self.Bind(wx.EVT_SIZE, self.on_size)

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.sizer.SetDimension(0,0,s[0],s[1])

    def refresh_data(self):
        self.w_panel.refresh_data()
        self.a_grid.refresh_data()


class dnd3earmor(combat_char_child):
    """ Node Handler for ac.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        combat_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.root = getRoot(self) 
        self.root.ac = self

    def get_spell_failure(self):
        return self.get_total('spellfailure')

    def get_total_weight(self):
        return self.get_total('weight')

    def get_check_pen(self):
        return self.get_total('checkpenalty')

    def get_armor_class(self):
        ac_total = 10

        ac_total += self.get_total('bonus')
        dex_mod = self.root.abilities.get_mod('Dex')
        max_dex = self.get_max_dex()
        if dex_mod < max_dex: ac_total += dex_mod
        else: ac_total += max_dex
        return ac_total

    def get_max_dex(self):
        armor_list = self.xml.findall('armor')
        dex = 10
        for a in armor_list:
            temp = int(a.get("maxdex"))
            if temp < dex: dex = temp
        return dex

    def get_total(self,attr):
        armor_list = self.xml.findall('armor')
        total = 0
        for a in armor_list: total += int(a.get(attr))
        return total

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,ac_panel,"Armor")
        wnd.title = "Armor"
        return wnd

    def on_rclick( self, evt ):
        ac = self.get_armor_class()
        fac = (int(ac)-(self.root.abilities.get_mod('Dex')))
        txt = '((AC: %s Normal, %s Flatfoot))' % ( ac, fac ) #a 1.5002
        self.chat.ParsePost( txt, True, True )

    def tohtml(self):
        html_str = """<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 >
            <th>AC</th><th>Check Penalty</th><th >Spell Failure</th>
            <th>Max Dex</th><th>Total Weight</th></tr>"""
        html_str += "<tr ALIGN='center' >"
        html_str += "<td>"+str(self.get_armor_class())+"</td>"
        html_str += "<td>"+str(self.get_check_pen())+"</td>"
        html_str += "<td>"+str(self.get_spell_failure())+"</td>"
        html_str += "<td>"+str(self.get_max_dex())+"</td>"
        html_str += "<td>"+str(self.get_total_weight())+"</td></tr></table>"
        n_list = self.xml.getchildren()
        for n in n_list:
            html_str += """<P><table width=100% border=1 ><tr BGCOLOR=#E9E9E9 >
                <th colspan=3>Armor</th><th>Type</th><th >Bonus</th></tr>"""
            html_str += "<tr ALIGN='center' >"
            html_str += "<td  colspan=3>"+n.get('name')+"</td>"
            html_str += "<td>"+n.get('type')+"</td>"
            html_str += "<td>"+n.get('bonus')+"</td></tr>"
            html_str += """<tr BGCOLOR=#E9E9E9 >"""
            html_str += "<th>Check Penalty</th><th>Spell Failure</th>"
            html_str += "<th>Max Dex</th><th>Speed</th><th>Weight</th></tr>"
            html_str += "<tr ALIGN='center'>"
            html_str += "<td>"+n.get('checkpenalty')+"</td>"
            html_str += "<td>"+n.get('spellfailure')+"</td>"
            html_str += "<td>"+n.get('maxdex')+"</td>"
            html_str += "<td>"+n.get('speed')+"</td>"
            html_str += "<td>"+n.get('weight')+"</td></tr></table>"
        return html_str


class ac_panel(wx.Panel):
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Armor')
        self.hparent = handler 
        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Armor"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Armor"), 1, wx.EXPAND)
        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        self.xml = handler.xml
        n_list = handler.xml.getchildren()
        self.n_list = n_list
        col_names = ['Armor','bonus','maxdex','cp','sf','weight','speed','type']
        self.grid.CreateGrid(len(n_list),len(col_names),1)
        self.grid.SetRowLabelSize(0)
        for i in range(len(col_names)): self.grid.SetColLabelValue(i,col_names[i])
        self.atts =['name','bonus','maxdex','checkpenalty',
            'spellfailure','weight','speed','type']
        for i in range(len(n_list)): self.refresh_row(i)
        self.temp_dom = None


    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        if col >= 1 and col <= 5:
            try:
                int(value)
                self.n_list[row].set(self.atts[col],value)
            except: self.grid.SetCellValue(row,col,"0")
        else: self.n_list[row].set(self.atts[col],value)

    def refresh_row(self,i):
        n = self.n_list[i]
        for y in range(len(self.atts)): self.grid.SetCellValue(i,y,n.get(self.atts[y]))

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3earmor.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        f_list = self.temp_dom.findall('armor')
        opts = []
        for f in f_list: opts.append(f.get('name'))
        dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.refresh_row(self.grid.GetNumberRows()-1)
        dlg.Destroy()

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols+2)
        self.grid.SetColSize(0,col_w*3)
        for i in range(1,cols): self.grid.SetColSize(i,col_w)


class dnd3esnp(dnd3e_char_child):
    """ Node Handler for power points.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent 
        self.frame = component.get('frame')
        self.child_handlers = {}
        self.new_child_handler('spells','Spells',dnd3espells,'book')
        self.new_child_handler('divine','Divine Spells',dnd3edivine,'book')
        self.new_child_handler('powers','Powers',dnd3epowers,'book')
        self.new_child_handler('pp','Power Points',dnd3epp,'gear')
        self.myeditor = None

    def new_child_handler(self,tag,text,handler_class,icon='gear'):
        node_list = self.xml.findall(tag)
        tree = self.tree
        i = self.tree.icons[icon]
        new_tree_node = tree.AppendItem(self.mytree_node,text,i,i)
        handler = handler_class(node_list[0],new_tree_node,self)
        tree.SetPyData(new_tree_node,handler)
        self.child_handlers[tag] = handler

    def get_design_panel(self,parent):
        return tabbed_panel(parent,self,1)

    def get_use_panel(self,parent):
        return tabbed_panel(parent,self,2)


class snp_char_child(node_handler):
    """ Node Handler for skill.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        node_handler.__init__(self,xml_dom,tree_node)
        self.char_hander = parent
        self.drag = False
        self.frame = component.get('frame')
        self.myeditor = None

    def on_drop(self,evt):
        pass

    def on_rclick(self,evt):
        pass

    def on_ldclick(self,evt):
        return

    def on_html(self,evt):
        html_str = self.tohtml()
        wnd = http_html_window(self.frame.note,-1)
        wnd.title = self.xml.get('name')
        self.frame.add_panel(wnd)
        wnd.SetPage(html_str)

    def get_design_panel(self,parent):
        pass

    def get_use_panel(self,parent):
        return self.get_design_panel(parent)

    def delete(self):
        pass


class dnd3espells(snp_char_child):
    """ Node Handler for classes.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        snp_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent #a 1.5002 allow ability to run up tree.
        self.root = getRoot(self) #a 1.5002
        self.root.spells = self #a 1.6009


        node_list = self.xml.findall( 'spell' )
        self.spells = {}
        tree = self.tree
        icons = self.tree.icons
        for n in node_list:
            name = n.get('name')
            self.spells[ name ] = n
            new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] )
            tree.SetPyData( new_tree_node, self )

    def on_rclick( self, evt ):
        item = self.tree.GetSelection()
        name = self.tree.GetItemText( item )
        if item == self.mytree_node:
            dnd3e_char_child.on_ldclick( self, evt )
        else:
            level = self.spells[ name ].get( 'level' )
            descr = self.spells[ name ].get( 'desc' )
            use = self.spells[ name ].get( 'used' )
            memrz = self.spells[ name ].get( 'memrz' )
            use += '+1'
            charNameL=self.root.general.charName #a  1.5002
            left = eval( '%s - ( %s )' % ( memrz, use ) )
            if left < 0:
                txt = '%s Tried to cast %s but has used all of them for today,'
                txt +='"Please rest so I can cast more."' % ( charNameL, name ) #a 1.5002
                self.chat.ParsePost( txt, True, False )
            else:
                txt = '%s casts %s ( level %s, "%s" )' % ( charNameL, name, level, descr )#a f 1.5002
                self.chat.ParsePost( txt, True, False )
                s = ''
                if left != 1: s = 's'
                txt = '%s can cast %s %d more time%s' % ( charNameL, name, left, s ) #a 1.5002
                self.chat.ParsePost( txt, False, False )
                self.spells[ name ].set( 'used', `eval( use )` )

    def refresh_spells(self):
        self.spells = {}
        tree = self.tree
        icons = self.tree.icons
        tree.CollapseAndReset(self.mytree_node)
        node_list = self.xml.findall('spell')
        for n in node_list:
            name = n.get('name')
            new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear'])
            tree.SetPyData(new_tree_node,self)
            self.spells[name]=n

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,spells_panel,"Spells")
        wnd.title = "Spells"
        return wnd

    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Arcane Spells</th></tr><tr><td><br>"
        n_list = self.xml.getchildren()
        for n in n_list: html_str += "(" + n.get('level') + ") " + n.get('name')+ ", "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

    def get_char_lvl( self, attr ):
        return self.char_hander.get_char_lvl(attr)

class spells_panel(wx.Panel):
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Arcane Spells')
        self.hparent = handler #a 1.5002 allow ability to run up tree.
        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.handler = handler
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Spell"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Spell"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 30, "Refresh Spells"), 1, wx.EXPAND)
        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        n_list = handler.xml.getchildren()
        self.n_list = n_list
        self.xml = handler.xml
        self.grid.CreateGrid(len(n_list),4,1)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelValue(0,"No.")
        self.grid.SetColLabelValue(1,"Lvl")
        self.grid.SetColLabelValue(2,"Spell")
        self.grid.SetColLabelValue(3,"Desc")
        for i in range(len(n_list)): self.refresh_row(i)
        self.temp_dom = None

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        if col == 0: self.n_list[row].set('memrz',value)

    def refresh_row(self,i):
        spell = self.n_list[i]
        memrz = spell.get('memrz')
        name = spell.get('name')
        type = spell.get('desc')
        level = spell.get('level')
        self.grid.SetCellValue(i,0,memrz)
        self.grid.SetCellValue(i,2,name)
        self.grid.SetReadOnly(i,2)
        self.grid.SetCellValue(i,3,type)
        self.grid.SetReadOnly(i,3)
        self.grid.SetCellValue(i,1,level)
        self.grid.SetReadOnly(i,1)

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):
        debug()
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3espells.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        debug(self.temp_dom)
        f_list = self.temp_dom.findall('spell')
        opts = []
        #lvl = int(dnd3e_char_child.get_char_lvl('level'))
        #castlvl = eval('%s/2' % (lvl))
        for f in f_list:
            spelllvl = f.get('level')
            opts.append("(" + f.get('level') + ")" + f.get('name'))
        dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.n_list = self.xml.findall('spell')
            self.refresh_row(self.grid.GetNumberRows()-1)
            self.handler.refresh_spells()
        dlg.Destroy()

    def on_refresh_spells( self, evt ):
        f_list = self.xml.findall('spell')
        for spell in f_list: spell.set( 'used', '0' )

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols)
        for i in range(0,cols): self.grid.SetColSize(i,col_w)
        self.grid.SetColSize(0,w * 0.10)
        self.grid.SetColSize(1,w * 0.10)
        self.grid.SetColSize(2,w * 0.30)
        self.grid.SetColSize(3,w * 0.50)

    def refresh_data(self):
        for i in range(len(self.n_list)): self.refresh_row(i)

class dnd3edivine(snp_char_child):
    """ Node Handler for classes.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        snp_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent #a 1.5002 allow ability to run up tree.
        self.root = getRoot(self) #a 1.5002
        self.root.divine = self #a 1.6009
        node_list = self.xml.findall( 'gift' )
        self.spells = {}
        tree = self.tree
        icons = self.tree.icons
        for n in node_list:
            name = n.get('name')
            self.spells[ name ] = n
            new_tree_node = tree.AppendItem( self.mytree_node, name, icons['flask'], icons['flask'] )
            tree.SetPyData( new_tree_node, self )

    def on_rclick( self, evt ):
        charNameL=self.root.general.charName #a f 1.5002
        item = self.tree.GetSelection()
        name = self.tree.GetItemText( item )
        if item == self.mytree_node:
            dnd3e_char_child.on_ldclick( self, evt )
        else:
            level = self.spells[ name ].get( 'level' )
            descr = self.spells[ name ].get( 'desc' )
            use = self.spells[ name ].get( 'used' )
            memrz = self.spells[ name ].get( 'memrz' )
            use += '+1'
            left = eval( '%s - ( %s )' % ( memrz, use ) )
            if left < 0:
                txt = '%s Tried to cast %s but has used all of them for today,' #m 1.5002 break in 2.
                txt += "Please rest so I can cast more."' % ( charNameL, name )' #a 1.5002
                self.chat.ParsePost( txt, True, False )
            else:
                txt = '%s casts %s ( level %s, "%s" )' % ( charNameL, name, level, descr ) #a 5002
                self.chat.ParsePost( txt, True, False )
                s = ''
                if left != 1: s = 's'
                txt = '%s can cast %s %d more time%s' % ( charNameL, name, left, s ) #a 1.5002
                self.chat.ParsePost( txt, False, False )
                self.spells[ name ].set( 'used', `eval( use )` )

    def refresh_spells(self):
        self.spells = {}
        tree = self.tree
        icons = self.tree.icons
        tree.CollapseAndReset(self.mytree_node)
        node_list = self.xml.findall('gift')
        for n in node_list:
            name = n.get('name')
            new_tree_node = tree.AppendItem(self.mytree_node,name,icons['flask'],icons['flask'])
            tree.SetPyData(new_tree_node,self)
            self.spells[name]=n

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,divine_panel,"Spells")
        wnd.title = "Spells"
        return wnd

    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Divine Spells</th></tr><tr><td><br>"
        n_list = self.xml.getchildren()
        for n in n_list: html_str += "(" + n.get('level') + ") " + n.get('name')+ ", "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str

    def get_char_lvl( self, attr ):
        return self.char_hander.get_char_lvl(attr)

class divine_panel(wx.Panel):
    def __init__(self, parent, handler):
        pname = handler.xml.set("name", 'Divine Spells')
        self.hparent = handler
        wx.Panel.__init__(self, parent, -1)
        self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.handler = handler
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)
        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Spell"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Spell"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 30, "Refresh Spells"), 1, wx.EXPAND)
        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()

        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)

        n_list = handler.xml.getchildren()
        self.n_list = n_list
        self.xml = handler.xml
        self.grid.CreateGrid(len(n_list),4,1)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelValue(0,"No.")
        self.grid.SetColLabelValue(1,"Lvl")
        self.grid.SetColLabelValue(2,"Spell")
        self.grid.SetColLabelValue(3,"Desc")
        for i in range(len(n_list)): self.refresh_row(i)
        self.temp_dom = None

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)
        if col == 0: self.n_list[row].set('memrz',value)

    def refresh_row(self,i):
        spell = self.n_list[i]
        memrz = spell.get('memrz')
        name = spell.get('name')
        type = spell.get('desc')
        level = spell.get('level')
        self.grid.SetCellValue(i,0,memrz)
        self.grid.SetCellValue(i,2,name)
        self.grid.SetReadOnly(i,2)
        self.grid.SetCellValue(i,3,type)
        self.grid.SetReadOnly(i,3)
        self.grid.SetCellValue(i,1,level)
        self.grid.SetReadOnly(i,1)

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3edivine.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        f_list = self.temp_dom.findall('gift')
        opts = []
        for f in f_list:
            spelllvl = f.get('level')
            opts.append("(" + f.get('level') + ")" + f.get('name'))
        dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.n_list = self.xml.findall('gift')
            self.refresh_row(self.grid.GetNumberRows()-1)
            self.handler.refresh_spells()
        dlg.Destroy()

    def on_refresh_spells( self, evt ):
        f_list = self.xml.findall('gift')
        for spell in f_list: spell.set( 'used', '0' )

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols)
        for i in range(0,cols): self.grid.SetColSize(i,col_w)
        self.grid.SetColSize(0,w * 0.10)
        self.grid.SetColSize(1,w * 0.10)
        self.grid.SetColSize(2,w * 0.30)
        self.grid.SetColSize(3,w * 0.50)

    def refresh_data(self):
        for i in range(len(self.n_list)): self.refresh_row(i)


class dnd3epowers(snp_char_child):
    """ Node Handler for classes.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        snp_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent #a 1.5002 allow ability to run up tree.
        self.root = getRoot(self) #a 1.5002
        self.root.powers = self #a 1.6009

        node_list = self.xml.findall( 'power' )
        self.powers = {}
        tree = self.tree
        icons = self.tree.icons
        for n in node_list:
            name = n.get('name')
            self.powers[ name ] = n
            new_tree_node = tree.AppendItem( self.mytree_node, name,
                                             icons['gear'], icons['gear'] )
            tree.SetPyData( new_tree_node, self )

    def on_rclick( self, evt ):
        charNameL = self.root.general.charName                  #a f 1.5002

        item = self.tree.GetSelection()
        name = self.tree.GetItemText( item )
        charNameL = self.root.general.charName                  #a 1.5002
        if item == self.mytree_node:
            dnd3e_char_child.on_ldclick( self, evt )
        else:
            level = int(self.powers[ name ].get( 'level' ))
            descr = self.powers[ name ].get( 'desc' )
            #use can be removed -mgt
            #use = self.powers[ name ].get( 'used' )
            points = self.powers[ name ].get( 'point' )
            #cpp and fre are strings without the eval -mgt
            cpp = eval(self.root.pp.get_char_pp('current1'))          #a 1.5002
            fre = eval(self.root.pp.get_char_pp('free'))              #a 1.5002
            if level == 0 and fre > 0:
                left = eval('%s - ( %s )' % ( fre, points ))
                numcast = eval('%s / %s' % (left, points))
                if left < 0:
                    #In theory you should never see this -mgt
                    txt = ('%s doesnt have enough PowerPoints to use %s'
                        % ( charNameL, name )) #a 1.5002
                    self.chat.ParsePost( txt, True, False )
                else:
                    txt = ('%s uses %s as a Free Talent ( level %s, "%s" )'
                        % ( charNameL, name, level, descr )) #a 1.5002
                    self.chat.ParsePost( txt, True, False )
                    s = ''
                    if left != 1: s = 's'
                    txt = '%s has %d Free Talent%s left' % ( charNameL, numcast, s ) #a 1.5002
                    self.chat.ParsePost( txt, False, False )
                    self.root.pp.set_char_pp('free',left)       #a 1.5002
            else:
                left = eval('%s - ( %s )' % ( cpp, points ))
                #numcast = eval('%s / %s' % (left, points))
                if left < 0:
                    txt = '%s doesnt have enough PowerPoints to use %s' % ( charNameL, name ) #m 1.5002
                    self.chat.ParsePost( txt, True, False )
                else:
                    txt = '%s uses %s ( level %s, "%s" )' % ( charNameL, name, level, descr ) #m 1.5002
                    self.chat.ParsePost( txt, True, False )
                    s = ''
                    if left != 1:
                        s = 's'
                    #numcast is meaningless here -mgt
                    #txt = '%s can use %s %d more time%s' % ( charNameL, name, numcast, s ) #m 1.5002
                    #txt += ' - And has %d more PowerpointsP left' % (left)
                    txt = '%s has %d more Powerpoint%s' % ( charNameL, left, s ) #m 1.5002
                    self.chat.ParsePost( txt, False, False )
                    self.root.pp.set_char_pp('current1',left)   #a 1.5002

    def refresh_powers(self):
        self.powers = {}
        tree = self.tree
        icons = self.tree.icons
        tree.CollapseAndReset(self.mytree_node)
        node_list = self.xml.findall('power')
        for n in node_list:
            name = n.get('name')
            new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear'])
            tree.SetPyData(new_tree_node,self)
            self.powers[name]=n

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,power_panel,"Powers")
        wnd.title = "Powers"
        return wnd

    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Powers</th></tr><tr><td><br>"
        n_list = self.xml.getchildren()
        for n in n_list:
            html_str += "(" + n.get('level') + ") " + n.get('name')+ ", "
        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
        return html_str


class power_panel(wx.Panel):
    def __init__(self, parent, handler):
        #m 1.5015 corrected typo, was Pionic.
        pname = handler.xml.set("name", 'Psionic Powers')
        self.hparent = handler 
        self.root = getRoot(self)               #a (debug) 1.5002,1.5014

        wx.Panel.__init__(self, parent, -1)
        self.grid = wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS)
        self.handler = handler
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.grid, 1, wx.EXPAND)

        sizer1 = wx.BoxSizer(wx.HORIZONTAL)
        sizer1.Add(wx.Button(self, 10, "Remove Power"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 20, "Add Power"), 1, wx.EXPAND)
        sizer1.Add(wx.Size(10,10))
        sizer1.Add(wx.Button(self, 30, "Refresh Power"), 1, wx.EXPAND)

        sizer.Add(sizer1, 0, wx.EXPAND)
        self.sizer = sizer
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        #self.Bind(wx.EVT_SIZE, self.on_size)

        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
        self.Bind(wx.EVT_BUTTON, self.on_refresh_powers, id=30)
        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
        n_list = handler.xml.getchildren()
        self.n_list = n_list
        self.xml = handler.xml
        self.grid.CreateGrid(len(n_list),5,1)
        self.grid.SetRowLabelSize(0)
        self.grid.SetColLabelValue(0,"PP")
        self.grid.SetColLabelValue(1,"Lvl")
        self.grid.SetColLabelValue(2,"Power")
        self.grid.SetColLabelValue(3,"Desc")
        self.grid.SetColLabelValue(4,"Type")
        for i in range(len(n_list)): self.refresh_row(i)
        self.refresh_data()
        self.temp_dom = None

    def on_cell_change(self,evt):
        row = evt.GetRow()
        col = evt.GetCol()
        value = self.grid.GetCellValue(row,col)

    def refresh_row(self,i):
        power = self.n_list[i]
        point = power.get('point')
        name = power.get('name')
        type = power.get('desc')
        test = power.get('test')
        level = power.get('level')
        self.grid.SetCellValue(i,0,point)
        self.grid.SetReadOnly(i,0)
        self.grid.SetCellValue(i,1,level)
        self.grid.SetReadOnly(i,1)
        self.grid.SetCellValue(i,2,name)
        self.grid.SetReadOnly(i,2)
        self.grid.SetCellValue(i,3,type)
        self.grid.SetReadOnly(i,3)
        self.grid.SetCellValue(i,4,test)
        self.grid.SetReadOnly(i,4)

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])

    def on_add(self,evt):
        debug()
        if not self.temp_dom:
            tree = parse(dir_struct["dnd3e"]+"dnd3epowers.xml")
            xml_dom = tree.getroot()
            self.temp_dom = xml_dom
        debug(self.temp)
        f_list = self.temp_dom.findall('power')
        opts = []
        for f in f_list:
            spelllvl = f.get('level')
            opts.append("(" + f.get('level') + ") - " +
                        f.get('name') + " - " + f.get('test'))
        dlg = wx.SingleChoiceDialog(self,'Choose Power','Powers',opts)
        if dlg.ShowModal() == wx.ID_OK:
            i = dlg.GetSelection()
            new_node = self.xml.append(f_list[i])
            self.grid.AppendRows(1)
            self.n_list = self.xml.findall('power')
            self.refresh_row(self.grid.GetNumberRows()-1)
            self.handler.refresh_powers()
        dlg.Destroy()

    def on_remove(self,evt):
        rows = self.grid.GetNumberRows()
        for i in range(rows):
            if self.grid.IsInSelection(i,0):
                self.grid.DeleteRows(i)
                self.xml.remove(self.n_list[i])
                self.n_list = self.xml.findall('weapon')
                self.handler.refresh_powers()

    def on_refresh_powers( self, evt ):
        self.root.pp.set_char_pp('current1',self.root.pp.get_char_pp('max1'))
        self.root.pp.set_char_pp('free',self.root.pp.get_char_pp('maxfree'))

    def on_size(self,event):
        s = self.GetClientSizeTuple()
        self.grid.SetDimensions(0,0,s[0],s[1]-25)
        self.sizer.SetDimension(0,s[1]-25,s[0],25)
        (w,h) = self.grid.GetClientSizeTuple()
        cols = self.grid.GetNumberCols()
        col_w = w/(cols)
        for i in range(0,cols): self.grid.SetColSize(i,col_w)
        self.grid.SetColSize(0,w * 0.05)
        self.grid.SetColSize(1,w * 0.05)
        self.grid.SetColSize(2,w * 0.30)
        self.grid.SetColSize(3,w * 0.30)
        self.grid.SetColSize(4,w * 0.30)

    def refresh_data(self):
        for i in range(len(self.n_list)): self.refresh_row(i)

class dnd3epp(snp_char_child):
    """ Node Handler for power points.  This handler will be
        created by dnd3echar_handler.
    """
    def __init__(self,xml_dom,tree_node,parent):
        snp_char_child.__init__(self,xml_dom,tree_node,parent)
        self.hparent = parent #a 1.5002 allow ability to run up tree.
        self.root = getRoot(self)
        self.root.pp = self
        self.ppPanel=None

    def get_design_panel(self,parent):
        wnd = outline_panel(parent,self,pp_panel,"Power Points")
        wnd.title = "Power Points"
        return wnd


    def tohtml(self):
        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 >"
        html_str += "<th colspan=7>Power Points</th>" #a 1.6010
        html_str += "</tr><tr>"
        html_str += "<th colspan=2>Max:</th>"
        html_str += "<td>"+self.xml.get('max1')+"</td>"
        html_str += "<th colspan=3>Max Talents/day:</th>"
        html_str += "<td>"+self.xml.get('maxfree')+"</td>"
        html_str += "</tr><tr>"
        html_str += "<th colspan=2>Current:</th>"
        html_str += "<td>"+self.xml.get('current1')+"</td>"
        html_str += "<th colspan=3>Current Talents/day:</th>"
        html_str += "<td>"+self.xml.get('free')+"</td>"
        html_str += "</tr></table>"
        return html_str

    def get_char_pp( self, attr ):
        pp = self.xml.get(attr)
        return pp

    def set_char_pp( self, attr, evl ):
        qSub = str(evl) #a 1.5014 must force it to be a string for next call.
        self.xml.set(attr, qSub)

class pp_panel(wx.Panel):
    def __init__(self, parent, handler):
        wx.Panel.__init__(self, parent, -1)
        self.hparent = handler #a 1.5002 allow ability to run up tree.
        self.hparent.ppPanel=self #a 1.5xx

        pname = handler.xml.set("name", 'PowerPoints')
        self.sizer = wx.FlexGridSizer(2, 4, 2, 2)  # rows, cols, hgap, vgap
        self.xml = handler.xml

        self.static1= wx.StaticText(self, -1, "PP Current:")  #a 1.5015
        self.dyn1= wx.TextCtrl(self, PP_CUR,
                self.xml.get('current1'))   #a 1.5015
        self.dyn3= wx.TextCtrl(self, PP_FRE,
                self.xml.get('free'))       #a 1.5015
        self.sizer.AddMany([ (self.static1, 0, wx.ALIGN_CENTER_VERTICAL),
            (self.dyn1,   0, wx.EXPAND),
            (wx.StaticText(self, -1, "PP Max:"), 0, wx.ALIGN_CENTER_VERTICAL),
            (wx.TextCtrl(self, PP_MAX,
                self.xml.get('max1')),  0, wx.EXPAND),
            (wx.StaticText(self, -1, "Current Free Talants per day:"),
                          0, wx.ALIGN_CENTER_VERTICAL),
            (self.dyn3,  0, wx.EXPAND),                          #a 1.5015
            (wx.StaticText(self, -1, "Max Free Talants per day:"),
                            0, wx.ALIGN_CENTER_VERTICAL),
            (wx.TextCtrl(self, PP_MFRE,
                self.xml.get('maxfree')),  0, wx.EXPAND),
            ])

        self.sizer.AddGrowableCol(1)
        self.SetSizer(self.sizer)
        self.SetAutoLayout(True)
        self.Fit()
        self.Bind(wx.EVT_TEXT, self.on_text, id=PP_MAX)
        self.Bind(wx.EVT_TEXT, self.on_text, id=PP_CUR)
        self.Bind(wx.EVT_TEXT, self.on_text, id=PP_FRE)
        self.Bind(wx.EVT_TEXT, self.on_text, id=PP_MFRE)

    def on_text(self,evt):
        id = evt.GetId()
        if id == PP_CUR: self.xml.set('current1',evt.GetString())
        elif id == PP_MAX: self.xml.set('max1',evt.GetString())
        elif id == PP_FRE: self.xml.set('free',evt.GetString())
        elif id == PP_MFRE: self.xml.set('maxfree',evt.GetString())

    def on_size(self,evt):
        s = self.GetClientSizeTuple()
        self.sizer.SetDimension(0,0,s[0],s[1])

    #a 5.015 this whole function.
    def on_refresh(self,attr,value):
        if attr == 'current1': self.dyn1.SetValue(value)
        else: self.dyn3.SetValue(value)