diff orpg/gametree/nodehandlers/dnd3e.py @ 0:4385a7d0efd1 grumpy-goblin

Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author sirebral
date Tue, 14 Jul 2009 16:41:58 -0500
parents
children 3b2cfa13b610
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/orpg/gametree/nodehandlers/dnd3e.py	Tue Jul 14 16:41:58 2009 -0500
@@ -0,0 +1,3473 @@
+# 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
+#
+# qpatch by lead.b.  All modified lines denoted by # 1.500x[or] as below
+# 1.5001r fix for unable to update misc modifier for saves
+# 1.5002r fix for dnd_globals not scoping 1 character's data from another -or
+#        causing abends or other problems due to lack of intialization from
+#        char abilities.  getCharacterProp function added.
+#           This includes fix for "total on saves", making "rclick" on skill
+#           work to send roll to window,"total on skill rolls",
+# 1.5004r getting ac mod for
+#       those skill which have armour class adjustments was broken.
+# 1.5005r perhaps no lines marked.  Under dnd3eattacks, the misc. value for
+#       both Melee and Ranged do not get re-displayed if the panel is closed
+#       and re-opened, tho the adjustment still seems to be in total... perhap
+#       total just isn't getting recalculated?
+# 1.5006r on rclick on a weapon, on first attack roll is generated, additional
+#       crash,terminating the sequence.
+# 1.5008r extended bonuses for "extra" attacks don't include all items.
+# 1.5009r while 1.5008 is "resolved", not sure it is correct for special monk
+#       attacks.  I'm pretty sure I fixed this.
+# 1.5014r powerpoints are broken.
+# 1.5017r if you bring up the entire character in edit mode (dlclick on
+#       top node of character), and click
+#       "Money and Inventory" tab, then change the amount of "plat" a
+#       character has, the name on the tree will be updated to match
+#       the "plat" entry.
+# 1.5019r if str bonus was 0, forgot + for aStrengthMod.
+# ---v 1.6 cut. above corrected by 1.6.
+# -- items marked 1.5xxx were found prior 1.6 but not added (enhancements)
+#       or corrected (bugs, default if not stated) at that cut point
+# 1.5003r this is currently just a "this is busted" flag, this is an rclick on
+#       on the saves node (above the fort/wil/ref saves) itself.  It throws an
+#       error message into the python window, then nothing... Not even sure what
+#       it is -supposed- to do. set to do nothing, which I think is fine.
+# 1.5011r enhancement.  Damage for thrown weapon should get str adder.
+#       Don't think this is accounted for.  Remember, JUST damage, not to-hit.
+#       included into 1.6002, marking resolved to simplify list.
+# 1.5012r enhancement.  str penalty to dam for bow/slings should be applied.
+#       but it's not. Remember, this is just for damage, not to-hit.
+# 1.5015r if you bring up the entire character in edit mode (dlclick on
+#       top node of character), and click
+#       "Spells and Powers" tab, then "Psionic Powers", push the "Refresh
+#       Powers" button, the powers are refreshed but if you select the
+#       "PowerPoints" tab, the update is not reflected there.  Must close
+#       and reopen edit session for refresh. (Already corrected misspelling of
+#       "Pionic" -> "Psionic")
+# 1.6000r eliminate misc debug items, include things like getCharacterProp which
+#       never got used.  Documenting for completeness.  Minor changes for clarity
+# 1.6001r bug, melee weapons, such as dagger, which have a range are currently
+#       being treated simply as ranged weapons... this mean dex bonuses
+#       are being applied for to-hit Vs strength.  Melee thrown are treated
+#       different than ranged.
+# 1.6003r bug, if double weapon handle damage specified as 1d8/1d6 as 1d8
+#       for single weapon usage.  The correct usage for double weapon will
+#       for short term at least remove requirement to have to update
+#       memorized list.  - med priority
+# 1.6008r C:\Program Files\openrpg1\orpg\templates\nodes\dnd3e.xml minor
+#       typos corrected, added comment on psionics.  Remember to replace!
+# 1.6009r tohtml fails, thus send to chat fails and pretty print fails.
+# 1.6010r tohtml for power points needs some clean up.
+# 1.6011r if multiple weapons are chosen for deletion from character inventory,
+#       at least one wrong weapons will be deleted.
+# 1.6012r flurry attack negative only applied to med monk, applies to all.
+# 1.6013r penalties on stats on tohtml showed +-3, instead of just -3.
+# 1.6014r rclick on "Skills" node above actual skills throws an error msg to
+#       python window.
+# 1.6015r rclick on "Attacks" or "Abilities Score" node throws an error msg to
+#       python window.
+# 1.6016r enhancement add comment to rclick footnotes to get footnote details.
+# 1.6018r resolve saves not updating on panel if open when ability scores change
+#       change
+# 1.6021r didn't roll extra monk attacks if base to it bonus was 0.
+# 1.6022r monks always got d20 damage, reversed order of checks to fix.
+# v1.8 cut.
+# -- items marked 1.6xxx were found prior 1.8 but not added (enhancements)
+#       or corrected (bugs, default if not stated) at that cut point
+# 1.5007o enhancement. str bows not accounted for correctly.
+#       thoughts: use new element tag to indicate strBow(3).
+# 1.5010r proposed enhancement.  Adding character name to frames around stuff.
+#    - marking resolved... determined to not do.
+# 1.5013o enhancement. this is for all "off-hand" attacks stuff. Eg: str bonus
+#       only 1/2 for "off-hand" for both to-hit and damage (unless penalty! ;-)
+#       Probably other things, as I see nothing in here to account for them.
+# 1.5016o enhancement. swim check does not reflect weight check.
+# 1.5018o enhancement. actual psionic abilities list.
+# 1.6002o enhancement.  modify code to take advanage of new footnote field for
+#       indicating which weapons are Thrown, as opposed to improvised thrown
+#       weapons; which are treated differently.  Allow for throwing of melee
+#       weapons (with 1.6001 all melee are unthrowable) recast of 1.5011,
+#       which I'm marking resolved.
+# 1.6004o feature request from 541659 Ability to remove skills, at least those
+#       that can't be used untrained.  This would also require ability to re-add
+#       skills later.  Short term solution may be to ability to clearly mark
+#       skill which can't be used yet. - low priority.
+# 1.6005o feature request from 541659 Custom feats, without the need to edit
+#       data/dnd3e/dnd3efeats.xml  Note, while standard feats may be affecting
+#       how tool will generate rolls, etc; it is unlikely that custom feats will
+#       will be able to do this; but should be able to be include from a
+#       complete "character sheet" perspective. - low priority (since
+#       dnd3efeats can be edited to add feats)
+# 1.6006o feature request from 541659 Do sorcerer and bard spells right;
+#       for short term at least remove requirement to have to update
+#       memorized list.  - med priority
+# 1.6007o feature request from 541659 Make tabs optional to be able to remove
+#       tabs which are unused by a particular character.  Would need ability
+#       to add them back in, since character might later add a class which
+#       would need them. - very low priority
+# 1.6017o enhancement when editing footnotes for weapons,
+#       provide full table of footnotes in companion window to help.
+# 1.6019o enhancement Forum request to add "flatfooted" to ac matrix
+# 1.6020o enh add column to skills to allow tracking of skill points allocated.
+# 1.9000r clean up of excess comments from 1.6 and earlier.
+# 1.9001r if str or dex changes, Melee/Ranged combat ability does not update
+#        until refreshed by a change.
+# 1.9002r depending on what subwindows were open, changing stat scores could
+#        crash out entire orpg environment.
+#
+# r- resolved
+# o- open
+#
+import orpg.tools.orpg_settings
+import orpg.minidom
+from core import *
+from containers import *
+from string import *  #a 1.6003
+from inspect import *  #a 1.9001
+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
+#o 1.5002 (this whole comment block)
+# 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.
+
+#d 1.6000 not used.
+#def getCharacterProp(forgetIt):
+#    return None
+
+#a 1.6 convinience function added safeGetAttr
+def safeGetAttr(node,lable,defRetV=None):
+    cna=node.attributes
+    for i2 in range(len(cna)):
+        if cna.item(i2).name == lable:
+            return cna.item(i2).value
+    #retV=node.getAttribute(lable) # getAttribute does not distingish between
+    # the attribute not being present vs it having a value of ""
+    # This is bad for this routine, thus not used.
+    return defRetV
+#a 1.6... safeGetAttr end.
+
+########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 #a 1.5002 allow ability to run up tree, this is the
+        #a 1.5002 top of the handler tree, this is used to flag where to stop
+        #a 1.5002 on the way up.  Changing this will break getRoot(self)
+
+        self.frame = open_rpg.get_component('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')
+        #wxMenuItem(self.tree.std_menu, dnd3e_EXPORT, "Export...", "Export")
+        #print "dnd3echar_handler init - "+\
+        # "self.child_handlers:",self.child_handlers # a (debug) 1.5002
+        self.myeditor = None
+
+
+    def new_child_handler(self,tag,text,handler_class,icon='gear'):
+        node_list = self.master_dom.getElementsByTagName(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 >"
+        #d block for 1.6009 start
+        #html_str += self.child_handlers['general'].tohtml()+"</td></tr>"
+        #html_str += "<tr><td width='50%' valign=top >
+        #        "+self.child_handlers['abilities'].tohtml()
+        #html_str += "<P>" + self.child_handlers['saves'].tohtml()
+        #html_str += "<P>" + self.child_handlers['attacks'].tohtml()
+        #html_str += "<P>" + self.child_handlers['ac'].tohtml()
+        #html_str += "<P>" + self.child_handlers['feats'].tohtml()
+        #html_str += "<P>" + self.child_handlers['spells'].tohtml()
+        #html_str += "<P>" + self.child_handlers['divine'].tohtml()
+        #html_str += "<P>" + self.child_handlers['powers'].tohtml()
+        #html_str += "<P>" + self.child_handlers['inventory'].tohtml() +"</td>"
+        #html_str += "<td width='50%' valign=top >
+        #       "+self.child_handlers['classes'].tohtml()
+        #html_str += "<P>" + self.child_handlers['hp'].tohtml()
+        #html_str += "<P>" + self.child_handlers['pp'].tohtml()
+        #html_str += "<P>" + self.child_handlers['skills'].tohtml() +"</td>"
+        #d block for 1.6009 end
+        #a block for 1.6009 start
+        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>"
+        #a block for 1.6009 end
+
+        html_str += "</tr></table>"
+        return html_str
+
+    def about(self):
+        html_str = "<img src='" + orpg.dirpath.dir_struct["icon"]
+        html_str += "dnd3e_logo.gif' ><br><b>dnd3e Character Tool "
+        html_str += self.Version+"</b>" #m 1.6000 was hard coded.
+        html_str += "<br>by Dj Gilcrease<br>digitalxero@gmail.com"
+        return html_str
+
+########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 = open_rpg.get_component('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.master_dom.getAttribute('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 #a 1.5002 allow ability to run up tree.
+
+    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.master_dom.setAttribute("name", 'How To')
+        self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'How To'), wx.VERTICAL)
+        self.master_dom = handler.master_dom
+        n_list = self.master_dom._get_childNodes()
+        for n in n_list:
+            t_node = safe_get_text_node(n)
+        self.sizer.Add(wx.StaticText(self, -1, t_node._get_nodeValue()), 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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.5002
+        self.root.general = self  #a 1.5002
+        self.charName = self.get_char_name() # a 1.5002 make getting name easier.
+
+    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.master_dom._get_childNodes()
+        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>General Information</th></tr><tr><td>"
+        for n in n_list:
+            t_node = safe_get_text_node(n)
+            html_str += "<B>"+n._get_tagName().capitalize() +":</B> "
+            html_str += t_node._get_nodeValue() + ", "
+        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)
+        #o 1.5002 self.char_hander = parent in this case.
+        self.charName = name  #a 1.5002 make getting name easier.
+
+
+    def get_char_name( self ):
+        node = self.master_dom.getElementsByTagName( 'name' )[0]
+        t_node = safe_get_text_node( node )
+        return t_node._get_nodeValue()
+
+class gen_grid(wx.grid.Grid):
+    """grid for gen info"""
+    def __init__(self, parent, handler):
+        pname = handler.master_dom.setAttribute("name", 'General')
+        self.hparent = handler #a 1.5002 allow ability to run up tree, needed
+        # a 1.5002 parent is functional parent, not invoking parent.
+
+
+        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
+        n_list = handler.master_dom._get_childNodes()
+        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)
+        t_node = self.n_list[row]._get_firstChild()
+        t_node._set_nodeValue(value)
+        if row==0:
+            self.handler.on_name_change(value)
+        self.AutoSizeColumn(1)
+
+    def refresh_row(self,rowi):
+        t_node = safe_get_text_node(self.n_list[rowi])
+
+        self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName())
+        self.SetReadOnly(rowi,0)
+        self.SetCellValue(rowi,1,t_node._get_nodeValue())
+        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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.6009
+        self.root.inventory = self #a 1.6009
+
+    def get_design_panel(self,parent):
+        wnd = inventory_pane(parent, self) #outline_panel(parent,self,inventory_grid,"Inventory")
+        wnd.title = "General Info"
+        return wnd
+
+    def tohtml(self):
+        n_list = self.master_dom._get_childNodes()
+        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>General Information</th></tr><tr><td>"
+        for n in n_list:
+            t_node = safe_get_text_node(n)
+            html_str += "<B>"+n._get_tagName().capitalize() +":</B> "
+            html_str += t_node._get_nodeValue() + "<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.master_dom._get_childNodes()
+        self.autosize = False
+
+        self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, "Inventroy"), 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._get_tagName() == nodeName:
+                t_node = safe_get_text_node(node)
+                t_node._set_nodeValue(value)
+
+    def saveMoney(self, row, col):
+        value = self.grid.GetCellValue(row, col)
+        t_node = safe_get_text_node(self.n_list[row])
+        t_node._set_nodeValue(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):
+        t_node = safe_get_text_node(self.n_list[row])
+        tagname = self.n_list[row]._get_tagName()
+        value = t_node._get_nodeValue()
+        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 #a 1.5002 allow ability to run up tree.
+        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
+        self.frame = open_rpg.get_component('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.master_dom.getElementsByTagName(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 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 = open_rpg.get_component('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.master_dom.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self)  #a 1.5002 get top of our local function tree.
+        self.root.abilities = self #a 1.5002 let other classes find me.
+
+        self.abilities = {}
+        node_list = self.master_dom.getElementsByTagName('stat')
+        tree = self.tree
+        icons = tree.icons
+
+        for n in node_list:
+            name = n.getAttribute('abbr')
+            self.abilities[name] = n
+            new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] )
+            tree.SetPyData( new_tree_node, self )
+        #print "dnd3eability - init self.abilities",self.abilities #a (debug) 1.5002
+
+    def on_rclick( self, evt ):
+        item = self.tree.GetSelection()
+        name = self.tree.GetItemText( item )
+        #if item == self.mytree_node:   #d 1.6016
+        #    dnd3e_char_child.on_ldclick( self, evt ) #d 1.6016
+        if not item == self.mytree_node: #a 1.6016
+        #else: #d 1.6016
+            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].getAttribute('base'))
+        mod = (score - 10) / 2
+        mod = int(mod)
+        return mod
+
+    def set_score(self,abbr,score):
+        if score >= 0:
+            self.abilities[abbr].setAttribute("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.master_dom.getElementsByTagName('stat')
+        for n in node_list:
+            name = n.getAttribute('name')
+            abbr = n.getAttribute('abbr')
+            base = n.getAttribute('base')
+            mod = str(self.get_mod(abbr))
+            if int(mod) >= 0: #m 1.6013 added "int(" and ")"
+                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.master_dom.setAttribute("name", 'Stats')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self)
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+
+        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.master_dom.getElementsByTagName('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)
+        #print value
+        try:
+            int(value)
+            self.stats[row].setAttribute('base',value)
+            self.refresh_row(row)
+        except:
+            self.SetCellValue(row,col,"0")
+        if self.char_wnd:
+            self.char_wnd.refresh_data()
+
+    #mark5
+
+    def refresh_row(self,rowi):
+        s = self.stats[rowi]
+
+        name = s.getAttribute('name')
+        abbr = s.getAttribute('abbr')
+        self.SetCellValue(rowi,0,name)
+        self.SetReadOnly(rowi,0)
+        self.SetCellValue(rowi,1,s.getAttribute('base'))
+        self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr)))
+        self.SetReadOnly(rowi,2)
+        #if self.root.saves.saveGrid: #a 1.6018 d 1.9002 whole if clause
+            #print getmembers(self.root.saves.saveGrid)
+            #self.root.saves.saveGrid.refresh_data() #a 1.6018
+            #print "skipping saving throw update, put back in later"
+        self.root.saves.refresh_data() #a 1.9002
+        self.root.attacks.refreshMRdata() #a 1.9001 `
+
+    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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self)
+        self.root.classes = self
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+
+    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.master_dom._get_childNodes()
+        for n in n_list:
+            html_str += n.getAttribute('name') + " ("+n.getAttribute('level')+"), "
+        html_str = html_str[:len(html_str)-2] + "</td></tr></table>"
+        return html_str
+
+    def get_char_lvl( self, attr ):
+        node_list = self.master_dom.getElementsByTagName('class')
+        # print "eclasses - get_char_lvl node_list",node_list
+        tot = 0  #a 1.5009 actually, slipping in a quick enhancement ;-)
+        for n in node_list:
+            lvl = n.getAttribute('level') #o 1.5009 not sure of the value of this
+            tot += int(lvl) #a 1.5009
+            type = n.getAttribute('name') #o 1.5009 not sure of the value of this
+            #print type,lvl #a (debug) 1.5009
+            if attr == "level":
+                return lvl #o 1.5009 this returns the level of someone's first class. ???
+            elif attr == "class":
+                return type #o 1.5009 this returns one of the char's classes. ???
+        if attr == "lvl":   #a 1.5009 this has value, adding this.
+            return tot  #a 1.5009 return character's "overall" level.
+
+    def get_class_lvl( self, classN ): #a 1.5009 need to be able to get monk lvl
+        #a 1.5009 this function is new.
+        node_list = self.master_dom.getElementsByTagName('class')
+        #print "eclasses - get_class_lvl node_list",node_list
+        for n in node_list:
+            lvl = n.getAttribute('level')
+            type = n.getAttribute('name')
+            if classN == type:
+                return lvl
+
+class class_panel(wx.Panel):
+    def __init__(self, parent, handler):
+        pname = handler.master_dom.setAttribute("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_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.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
+
+        n_list = handler.master_dom._get_childNodes()
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        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].setAttribute('level',value)
+        except:
+            self.grid.SetCellValue(row,col,"1")
+
+
+    def refresh_row(self,i):
+        n = self.n_list[i]
+
+        name = n.getAttribute('name')
+        level = n.getAttribute('level')
+        self.grid.SetCellValue(i,0,name)
+        self.grid.SetReadOnly(i,0)
+        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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3eclasses.xml","r")
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('class')
+        opts = []
+        for f in f_list:
+            opts.append(f.getAttribute('name'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            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 #a 1.5002 allow ability to run up tree.
+        #self.saveGrid = None  #a 1.6018 d 1.9002
+        self.saveGridFrame = []  #a 1.9002 handle list, check frame for close.
+
+        tree = self.tree
+        icons = self.tree.icons
+
+        self.root = getRoot(self) #a 1.5002
+        self.root.saves = self #a 1.6009
+        node_list = self.master_dom.getElementsByTagName('save')
+        self.saves={}
+        for n in node_list:
+            name = n.getAttribute('name')
+            self.saves[name] = n
+            new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear'])
+            tree.SetPyData(new_tree_node,self)
+
+    #a 1.9002 this whole method
+    def refresh_data(self): # refresh the data in the melee/ranged section
+        # 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.getAttribute('stat')
+        #print "dnd3esaves, get_mod: self,root",self,self.root #a (debug) 1.5002
+        #print "and abilities",self.root.abilities      #a (debug) 1.5002
+        stat_mod = self.root.abilities.get_mod(stat)            #a 1.5002
+        base = int(save.getAttribute('base'))
+        miscmod = int(save.getAttribute('miscmod'))
+        magmod = int(save.getAttribute('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 #a 1.5003 syntatic place holder
+            return #a 1.5003
+            #print "failure mode!"
+            #dnd3e_char_child.on_ldclick(self,evt) #d 1.5003 this busted
+            #wnd = save_grid(self.frame.note,self)
+            #wnd.title = "Saves"
+            #self.frame.add_panel(wnd)
+        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.master_dom.getElementsByTagName('save')
+        for n in node_list:
+            name = n.getAttribute('name')
+            stat = n.getAttribute('stat')
+            base = n.getAttribute('base')
+            html_str = html_str + "<tr ALIGN='center'><td>"+name+"</td><td>"+stat+"</td><td>"+base+"</td>"
+            #stat_mod = str(dnd_globals["stats"][stat])         #d 1.5002
+            stat_mod = self.root.abilities.get_mod(stat)        #a 1.5002
+
+            mag = n.getAttribute('magmod')
+            misc = n.getAttribute('miscmod')
+            mod = str(self.get_mod(name))
+            if mod >= 0:
+                mod1 = "+"
+            else:
+                mod1 = ""
+            #m 1.5009 next line.  added str() around stat_mod
+            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.master_dom.setAttribute("name", 'Saves')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+        self.root = getRoot(self)
+
+        #self.hparent.saveGrid = self #a 1.6018 d 1.9001
+
+
+        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.master_dom.getElementsByTagName('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)
+
+
+        #a 1.9002 remainder of code in this method.
+        climber = parent
+        nameNode = climber.GetClassName()
+        while nameNode != 'wxFrame':
+            climber = climber.parent
+            nameNode = climber.GetClassName()
+        masterFrame=climber
+        masterFrame.refresh_data=self.refresh_data
+        #print getmembers(masterFrame)
+
+        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].setAttribute('base',value)
+            elif col ==4:
+                self.saves[row].setAttribute('magmod',value)
+            elif col ==5:                                       # 1.5001
+                self.saves[row].setAttribute('miscmod',value)
+            self.refresh_row(row)
+        except:
+            self.SetCellValue(row,col,"0")
+
+    def refresh_row(self,rowi):
+        s = self.saves[rowi]
+
+        name = s.getAttribute('name')
+        self.SetCellValue(rowi,0,name)
+        self.SetReadOnly(rowi,0)
+        stat = s.getAttribute('stat')
+        self.SetCellValue(rowi,1,stat)
+        self.SetReadOnly(rowi,1)
+        self.SetCellValue(rowi,2,s.getAttribute('base'))
+        self.SetCellValue(rowi,3,str(self.root.abilities.get_mod(stat)))
+        self.SetReadOnly(rowi,3)
+        self.SetCellValue(rowi,4,s.getAttribute('magmod'))
+        self.SetCellValue(rowi,5,s.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.6009
+
+        #print "dnd3eskillsnfeats - init, self ",self #a (debug) 1.5002
+        #print "dnd3eskillsnfeats - init, parent ",self.hparent #a (debug) 1.5002
+        #print "dnd3eskillsnfeats - init, parent ",parent.dnd3eclassnstats #a (debug) 1.5002
+
+        node_handler.__init__(self,xml_dom,tree_node)
+        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
+        self.frame = open_rpg.get_component('frame')
+        self.child_handlers = {}
+        self.new_child_handler('skills','Skills',dnd3eskill,'book')
+        self.new_child_handler('feats','Feats',dnd3efeats,'book')
+        #wxMenuItem(self.tree.std_menu, dnd3e_EXPORT, "Export...", "Export")
+        self.myeditor = None
+
+
+    def new_child_handler(self,tag,text,handler_class,icon='gear'):
+        node_list = self.master_dom.getElementsByTagName(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 = open_rpg.get_component('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.master_dom.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        #a 1.5002 Need the functional parent, not the invoking parent.
+        self.root = getRoot(self) #a 1.5002
+        self.root.skills = self #a 1.6009
+
+        skills_char_child.__init__(self,xml_dom,tree_node,parent)
+        tree = self.tree
+        icons = self.tree.icons
+        node_list = self.master_dom.getElementsByTagName('skill')
+
+        self.skills={}
+        #Adding code to not display skills you can not use -mgt
+        for n in node_list:
+            name = n.getAttribute('name')
+            self.skills[name] = n
+            skill_check = self.skills[name]
+            ranks = int(skill_check.getAttribute('rank'))
+            trained = int(skill_check.getAttribute('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.master_dom.getElementsByTagName('skill')
+
+        self.skills={}
+        for n in node_list:
+            name = n.getAttribute('name')
+            self.skills[name] = n
+            skill_check = self.skills[name]
+            ranks = int(skill_check.getAttribute('rank'))
+            trained = int(skill_check.getAttribute('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.getAttribute('stat')
+        #stat_mod = int(dnd_globals["stats"][stat])                 #d 1.5002
+        stat_mod = self.root.abilities.get_mod(stat)                #a 1.5002
+        rank = int(skill.getAttribute('rank'))
+        misc = int(skill.getAttribute('misc'))
+        total = stat_mod + rank + misc
+        return total
+
+    def on_rclick(self,evt):
+        item = self.tree.GetSelection()
+        name = self.tree.GetItemText(item)
+        #print "skill rc self",self                 #a 1.6004
+        #print "skill rc tree",self.mytree_node     #a 1.6004
+        #print "skill rc item",item                 #a 1.6004
+        if item == self.mytree_node:
+            return
+            # following line fails,
+            #dnd3e_char_child.on_ldclick(self,evt) #d 1.6014
+            # it's what it used to try to do.
+        ac = self.root.ac.get_check_pen() #a 1.5002 for 1.5004 verify fix.
+
+        skill = self.skills[name]
+
+        untr = skill.getAttribute('untrained')                         #a 1.6004
+        rank = skill.getAttribute('rank')                              #a 1.6004
+        if eval('%s == 0' % (untr)):                                   #a 1.6004
+            if eval('%s == 0' % (rank)):                               #a 1.6004
+                res = 'You fumble around, accomplishing nothing'       #a 1.6004
+                txt = '%s Skill Check: %s' % (name, res)               #a 1.6004
+                chat = self.chat                                       #a 1.6004
+                chat.Post(txt,True,True)                               #a 1.6004
+                return                                                 #a 1.6004
+
+        armor = ''
+        acCp = ''
+        if ac < 0:  #acCp >= 1 #m 1.5004 this is stored as negatives.
+            armorCheck = int(skill.getAttribute('armorcheck'))
+            #print "ac,armorCheck",ac,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)
+            #wnd = skill_grid(self.frame.note,self)
+            #wnd.title = "Skills"
+            #self.frame.add_panel(wnd)
+        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.master_dom.getElementsByTagName('skill')
+
+        for n in node_list:
+            name = n.getAttribute('name')
+            stat = n.getAttribute('stat')
+            rank = n.getAttribute('rank')
+            untr = n.getAttribute('untrained')                              #a 1.6004
+            #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>"     #a 1.6004
+                    #html_str += "<tr ALIGN='center' bgcolor='green'><td>"      #d 1.6004
+                    html_str += name+"</td><td>"+stat+"</td><td>"+rank+"</td>"
+                elif eval('%s == 1' % (untr)):                                  #a 1.6004
+                    html_str += "<tr ALIGN='center' bgcolor='#C0FF40'><td>"     #a 1.6004
+                    html_str += name+"</td><td>"+stat+"</td><td>"+rank+"</td>"  #a 1.6004
+                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)        #a 1.5002
+            #stat_mod = str(dnd_globals["stats"][stat])         #d 1.5002
+            misc = n.getAttribute('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    #a 1.5002 need function parent, not invoker
+        self.root = getRoot(self) #a 1.5002
+        pname = handler.master_dom.setAttribute("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.master_dom.getElementsByTagName('skill')
+        #xelf.stats = dnd_globals["stats"]                           #d 1.5002
+
+        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)
+        #print value
+        try:
+            int(value)
+            if col == 2:
+                self.skills[row].setAttribute('rank',value)
+            elif col ==4:
+                self.skills[row].setAttribute('misc',value)
+            self.refresh_row(row)
+        except:
+            self.SetCellValue(row,col,"0")
+
+                #call refresh_skills
+        self.handler.refresh_skills()
+
+    def refresh_row(self,rowi):
+        s = self.skills[rowi]
+        name = s.getAttribute('name')
+        self.SetCellValue(rowi,0,name)
+        self.SetReadOnly(rowi,0)
+        stat = s.getAttribute('stat')
+        self.SetCellValue(rowi,1,stat)
+        self.SetReadOnly(rowi,1)
+        self.SetCellValue(rowi,2,s.getAttribute('rank'))
+        #self.SetCellValue(rowi,3,str(dnd_globals["stats"][stat]))  #d 1.5002
+        if self.root.abilities: #a 1.5002 sanity check.
+            stat_mod=self.root.abilities.get_mod(stat)           #a 1.5002
+        else: #a 1.5002
+            stat_mod = -6 #a 1.5002 this can happen if code is changed so
+            #a 1.5002 that abilities are not defined prior invokation of init.
+            print "Please advise dnd3e maintainer alert 1.5002 raised"
+
+        self.SetCellValue(rowi,3,str(stat_mod))         #a 1.5002
+        self.SetReadOnly(rowi,3)
+        self.SetCellValue(rowi,4,s.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.5002
+        self.root.feats = self #a 1.6009
+
+
+    def get_design_panel(self,parent):
+        setTitle="Feats - " + self.root.general.charName    #a 1.5010
+        wnd = outline_panel(parent,self,feat_panel,setTitle) #a 1.5010
+        #wnd = outline_panel(parent,self,feat_panel,"Feats") #d 1.5010
+        wnd.title = "Feats" #d 1.5010
+        #wnd.title = "Feats - " + self.charName
+        return wnd
+
+    def tohtml(self):
+        html_str = "<table width=100% border=1 ><tr BGCOLOR=#E9E9E9 ><th>Feats</th></tr><tr><td>"
+        n_list = self.master_dom._get_childNodes()
+        for n in n_list:
+            html_str += n.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+        self.root = getRoot(self) #a 1.5002
+        #tempTitle= 'Feats - ' + self.root.general.charName #a 1.5010
+        #pname = handler.master_dom.setAttribute("name", tempTitle) #a 1.5010
+        pname = handler.master_dom.setAttribute("name", 'Feats') #d 1.5010
+
+        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_SIZE, self.on_size)
+        self.Bind(wx.EVT_BUTTON, self.on_remove, id=10)
+        self.Bind(wx.EVT_BUTTON, self.on_add, id=20)
+
+        n_list = handler.master_dom._get_childNodes()
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        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") #m 1.6 typo correction.
+        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.getAttribute('name')
+        type = feat.getAttribute('type')
+        desc = feat.getAttribute('desc') #m 1.6 correct typo
+        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) #m 1.6 correct typo
+        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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3efeats.xml","r")
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('feat')
+        opts = []
+        for f in f_list:
+            opts.append(f.getAttribute('name') + "  -  [" +
+                     f.getAttribute('type') + "]  -  " + f.getAttribute('desc'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.5012
+
+
+
+        #mark3
+        dnd3e_char_child.__init__(self,xml_dom,tree_node,parent)
+        self.frame = open_rpg.get_component('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')
+        #print "combat",self.child_handlers #a (debug) 1.5002
+        #wxMenuItem(self.tree.std_menu, dnd3e_EXPORT, "Export...", "Export")
+        self.myeditor = None
+
+
+    def new_child_handler(self,tag,text,handler_class,icon='gear'):
+        node_list = self.master_dom.getElementsByTagName(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 = open_rpg.get_component('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.master_dom.getAttribute('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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.6009
+        self.root.hp = self #a 1.6009
+
+    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.master_dom.getAttribute('current')
+        mhp = self.master_dom.getAttribute('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.master_dom.getAttribute('max')+"</td>"
+        html_str += "<th>Current:</th>"
+        html_str += "<td>"+self.master_dom.getAttribute('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 #a 1.5002 allow ability to run up tree.  In this
+        #a 1.5002 case, we need the functional parent, not the invoking parent.
+
+        pname = handler.master_dom.setAttribute("name", 'HitPoints')
+        self.sizer = wx.FlexGridSizer(2, 4, 2, 2)  # rows, cols, hgap, vgap
+        self.master_dom = handler.master_dom
+        self.sizer.AddMany([ (wx.StaticText(self, -1, "HP Current:"),   0,
+           wx.ALIGN_CENTER_VERTICAL),
+          (wx.TextCtrl(self, HP_CUR,
+           self.master_dom.getAttribute('current')),   0, wx.EXPAND),
+          (wx.StaticText(self, -1, "HP Max:"), 0, wx.ALIGN_CENTER_VERTICAL),
+          (wx.TextCtrl(self, HP_MAX, self.master_dom.getAttribute('max')),
+           0, wx.EXPAND),
+         ])
+        self.sizer.AddGrowableCol(1)
+        self.SetSizer(self.sizer)
+        self.SetAutoLayout(True)
+        self.Fit()
+
+        #self.Bind(wx.EVT_SIZE, self.on_size)
+        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.master_dom.setAttribute('current',evt.GetString())
+        elif id == HP_MAX:
+            self.master_dom.setAttribute('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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.5002
+        self.root.attacks = self #a 1.6009 so others can find me.
+        self.mrFrame = [] #a 1.9001
+
+        #a 1.5012 start a1b
+
+        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={}
+        #a 1.5012 end a1b
+
+        node_list = self.master_dom.getElementsByTagName('melee')
+        self.melee = node_list[0]
+        node_list = self.master_dom.getElementsByTagName('ranged')
+        self.ranged = node_list[0]
+        self.refresh_weapons() # this causes self.weapons to be loaded.
+
+        #a 1.5012 this whole if clause.
+        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()
+
+        #weaponsH = self.master_dom.getElementsByTagName('attacks')
+        #mark7
+
+    #a 1.9001 this whole method
+    def refreshMRdata(self): # refresh the data in the melee/ranged section
+        # of the attack chart.
+        # count backwards, maintains context despite "removes"
+        for i in range(len(self.mrFrame)-1,-1,-1):   #a 1.9001
+            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.master_dom.getElementsByTagName('weapon')
+        for n in node_list:
+            name = n.getAttribute('name')
+            fn = safeGetAttr(n,'fn') #a 1.5012 can be removed when
+            #a 1.5012 confident all characters in the world have footnotes.
+            #if self.updateFootNotes:
+            if fn == None:#a 1.5012
+                self.updateFootNotes=True
+                self.updateFootN(n) #a 1.5012
+            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):#a 1.5012 this whole function
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3eweapons.xml","r")
+            #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012
+            self.temp_dom = xml.dom.minidom.parse(tmp)
+
+            #self.temp_dom = parseXml_with_dlg(self,tmp.read())
+            self.temp_dom = self.temp_dom._get_firstChild()
+            tmp.close()
+        nameF = n.getAttribute('name')
+        w_list = self.temp_dom.getElementsByTagName('weapon')
+        found = False
+        for w in w_list:
+            if nameF == w.getAttribute('name'):
+                found = True
+                fnN = safeGetAttr(n,'fn')
+                if fnN == None or fnN == 'None':
+                    fnW = w.getAttribute('fn')
+                    #print "weapon",nameF,"footnotes are updated to",fnW
+                    self.html_str += ("<tr ALIGN='center'><td>"+nameF+"</td>"+
+                                     "<td>"+fnW+"</td></tr>\n")
+                    n.setAttribute('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.setAttribute('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'  #m was dnd_globals["stats"]['Str'] 1.5002
+            temp = self.melee
+        else:
+            stat = 'Dex'  #m was dnd_globals["stats"]['Dex'] 1.5002
+            temp = self.ranged
+        stat_mod = -7
+        stat_mod = self.root.abilities.get_mod(stat)    #a 1.5002
+        #print "Big test - stat_mod",stat_mod           #a (debug) 1.6000
+        base = int(temp.getAttribute('base'))
+        base2 = int(temp.getAttribute('second'))
+        base3 = int(temp.getAttribute('third'))
+        base4 = int(temp.getAttribute('forth'))
+        base5 = int(temp.getAttribute('fifth'))
+        base6 = int(temp.getAttribute('sixth'))
+        misc = int(temp.getAttribute('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:
+            #print "bail due to FUD"
+            return #a 1.6015
+            #dnd3e_char_child.on_ldclick(self,evt)#d 1.6015
+            #self.frame.add_panel(self.get_design_panel(self.frame.note))
+        else:
+            #print "entering attack phase"
+            mod = int(self.weapons[name].getAttribute('mod'))
+            wepMod = mod #a 1.5008
+            footNotes = safeGetAttr(self.weapons[name],'fn','')
+            cat = self.weapons[name].getAttribute('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]
+            #print "print FootNotes,tres",footNotes,tres
+            if tres == 'Melee': #a 1.6001   #m 1.6022 use of tres here and...
+            #if self.weapons[name].getAttribute('range') == '0':#d 1.6001
+                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].getAttribute('damage')
+
+            #a 1.6003 start code fix instance a
+            result = split(dmg,"/",2)
+            dmg = result[0]
+            #print "1.6003 check:dmg",dmg,";result",result
+            #o currently, only picking out dmg; rest are simply ignored.
+            #o May be usefull
+            #o later for two weapon attack correction.
+            #a 1.6003 end code fix instance a
+
+            monkLvl = self.root.classes.get_class_lvl('Monk') # a 1.5002
+            #print "monkLvl",monkLvl #a (debug) 1.5002
+            # monkLvl = dnd_globals["class"]["lvl"] #d 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:     #m 1.6022 reversed the order of checks.
+                        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:     #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:     #m 1.6022 reversed the order of the checks
+                        dmg = "1d4"
+                    elif lvl <= 7:
+                        dmg = "1d6"
+                    elif lvl <= 11:
+                        dmg = "1d8"
+                    elif lvl <= 15:
+                        dmg = "1d10"
+                    elif lvl <= 20:
+                        dmg = "2d6"
+            flu = ''
+            #print "adjusted weapon damage is:",dmg
+            #o 1.5007 str bow
+            #o 1.5011 start looking about here str dam bonus missed for thrown?
+            #o 1.5012 start looking about here str penalty missed for bow/sling?
+            #o 1.5013 off-hand attacks.? dam and all affects?
+            str_mod = self.root.abilities.get_mod('Str') #a 1.5007,11,12,13
+            if rangeOrMelee == 'r':                     #a 1.5008
+                #if off_hand == True then stat_mod = stat_mod/2 #o 1.5013
+                #c 1.5007 ranged weapons normally get no str mod
+                if find(footNotes,'b') > -1:#a 1.5012 if it's a bow
+                    if str_mod >= 0:        #a 1.5012 never a str bonus
+                        str_mod = 0         #a 1.5012 penalty,
+                else:                       #a 1.5012 if appropriate
+                    str_mod = 0
+                #  c 1.5007 (must adjust for str bows later and thown weapons)
+                #o 1.5007 include + for str bows
+                #o 1.5012 include any str penalty for bows/slings.
+            mod2 = ""                                   #a 1.5007,11-13
+            if str_mod >= 0: #1.6 tidy up code.
+                mod2 = "+"   #1.6 tidy up code.
+            aStrengthMod = mod2 + str(str_mod) #a 1.5008 applicable strength mod
+
+            #if name == "Flurry of Blows(Monk Med)": #d 1.6012
+            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.master_dom.getElementsByTagName('weapon')
+        for n in n_list:
+            mod = n.getAttribute('mod')
+            if mod >= 0:
+                mod1 = "+"
+            else:
+                mod1 = ""
+            ran = n.getAttribute('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.getAttribute('name')+"</td><td>"+total+"</td>"
+            html_str += "<td>"+n.getAttribute('damage')+"</td><td>"
+            html_str += n.getAttribute('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.getAttribute('weight')+"</td>"
+            html_str += "<td>"+n.getAttribute('type')+"</td><td>"
+            html_str += n.getAttribute('size')+"</td>"
+            html_str += '<td>%s%s</td></tr>'  % (mod1, mod)
+            #a 1.5012 add next two lines to pretty print footnotes.
+            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.master_dom.setAttribute("name", 'Melee')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        #a 1.5002 we need the functional parent, not the invoking parent.
+
+        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)
+        #print "looking for containing frame"
+
+        #a 1.9001 remainder of code in this method.
+        climber = parent
+        nameNode = climber.GetClassName()
+        while nameNode != 'wxFrame':
+            climber = climber.parent
+            nameNode = climber.GetClassName()
+        masterFrame=climber
+        masterFrame.refreshMRdata=self.refresh_data
+        #print getmembers(masterFrame)
+
+        handler.mrFrame.append(masterFrame)
+
+        #print "masterFrame=",masterFrame
+        #print "mr.Show=",masterFrame.Show()
+        #print "mf.GetName",masterFrame.GetName()
+        #print "mf.GetClassName",masterFrame.GetClassName()
+        #print "mf.GetId",masterFrame.GetId()
+        #print "mf.GetLabel",masterFrame.GetLabel()
+        #print "mf.GetHandle",masterFrame.GetHandle()
+        #print "mf.GetParent",masterFrame.GetParent()
+        # here, GetParent consistent returns the master frame of the app.
+        #print "mf.GetGParent",masterFrame.GetGrandParent() #here, always None
+        #print "mf.GetTitle",masterFrame.GetTitle()
+        #print "mf.IsEnabled",masterFrame.IsEnabled()
+        #print "mf.IsShown",masterFrame.IsShown()
+        #print "mf.IsTopLevel",masterFrame.IsTopLevel()
+        #print "self.frame=",self.frame
+
+
+    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].setAttribute('base',value)
+            elif col==2:
+                self.rows[row].setAttribute('second',value)
+            elif col==3:
+                self.rows[row].setAttribute('third',value)
+            elif col==4:
+                self.rows[row].setAttribute('forth',value)
+            elif col==5:
+                self.rows[row].setAttribute('fifth',value)
+            elif col==6:
+                self.rows[row].setAttribute('sixth',value)
+            elif col==8:
+                self.rows[row].setAttribute('misc',value)
+                #print "row:",row,"value",value,self.rows[row]
+            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,7):  #d 1.5005
+        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                          #a 1.5012
+        self.root = getRoot(self)
+
+        pname = handler.master_dom.setAttribute("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_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.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.master_dom.getElementsByTagName('weapon')
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        self.handler = handler
+        #trash=input("weapon panel init colnames")
+        self.colAttr = ['name','damage','mod','critical','type','weight',
+                    'range','size','Total','fn',    'comment'] #a 1.5012
+        col_names = ['Name','Damage','To hit\nmod','Critical','Type','Weight',
+                    'Range','Size','Total','Foot\nnotes','Comment'] #a 1.5012
+        gridColCount=len(col_names)#a 1.5012
+        self.grid.CreateGrid(len(n_list),gridColCount,1) #a 1.5012
+        #self.grid.CreateGrid(len(n_list),9,1) #d 1.5012
+        self.grid.SetRowLabelSize(0)
+        #col_names = ['Name','damage','mod','critical','type','weight','range',             'size','Total'] #d 1.5012
+        #for i in range(len(col_names)):   #d 1.5012
+        for i in range(gridColCount): #a 1.5012
+            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):
+        #print "weapon_panel, on_rclick: self,evt",self,evt
+        row = evt.GetRow()
+        col = evt.GetCol()
+        value = self.grid.GetCellValue(row,col)
+        #print "wp, on rclick,grid row,col,value",row,col,value
+        if col == 9 and value != 'None':
+            n = self.n_list[row]
+            name = n.getAttribute('name')
+            #print "we want a panel!"
+            handler = self.hparent
+            #print "handler:",handler
+            # A handler is a node, and nodes have a reference to
+            # the master frame
+            masterFrame = handler.frame
+            #print "masterFrame:",masterFrame
+            title = name+"'s Special Weapon Characteristics"
+            fnFrame = wx.Frame(masterFrame, -1, title)
+            fnFrame.panel = wx.html.HtmlWindow(fnFrame,-1)
+            if not self.temp_dom:
+                tmp = open(orpg.dirpath.dir_struct["dnd3e"]+
+                            "dnd3eweapons.xml","r")
+                #tmp = open("c:\clh\codeSamples\sample1.xml","r")
+                xml_dom = parseXml_with_dlg(self,tmp.read())
+                xml_dom = xml_dom._get_firstChild()
+                tmp.close()
+                self.temp_dom = xml_dom
+            f_list = self.temp_dom.getElementsByTagName('f') # the footnotes
+            #print "weapon_panel - on_rclick f_list",f_list#a 1.6
+            n = self.n_list[row]
+            name = n.getAttribute('name')
+            footnotes = n.getAttribute('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"""
+            #print "rclick,name,fn",name,footnotes
+            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.getAttribute('mark') == aNote:
+                        found=True
+                        text=f.getAttribute('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> "
+
+            #print html_str
+            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].setAttribute('mod',value) # a 5.012 demoted
+        if not (col == 9 and value == "None" and
+                self.n_list[row].getAttribute('fn') == "None"
+                ): #a 5.012 special case for footnotes
+            self.n_list[row].setAttribute(self.colAttr[col],value)#a 5.012
+        #print "cell change",row,col,value
+        #if col == 0:#d 5.012 use of colAttr removed need for this.
+        #    self.n_list[row].setAttribute('name',value) #d 5.012
+        #elif col == 2: #d 5.012
+        #    try:#d 5.012 simplifying... remove this block.
+        #        int(value)
+        #        self.n_list[row].setAttribute('mod',value)
+        #        #self.refresh_row(row) #d 5.012 did nothing.
+        #    except:
+        #       value = "0"
+        #       self.n_list[row].setAttribute('mod',value)
+        #else: #d 5.012 demoted self.n set.
+        #   self.n_list[row].setAttribute(self.grid.GetColLabelValue(col),value)
+
+
+    def refresh_row(self,i):
+        n = self.n_list[i]
+        fn = n.getAttribute('fn')
+        #print "fn=",fn
+        name = n.getAttribute('name')
+        mod = n.getAttribute('mod')
+        ran = n.getAttribute('range')
+        total = str(int(mod) + self.handler.get_mod(ran))
+        self.grid.SetCellValue(i,0,name)
+        self.grid.SetCellValue(i,1,n.getAttribute('damage'))
+        self.grid.SetCellValue(i,2,mod)
+        self.grid.SetCellValue(i,3,n.getAttribute('critical'))
+        self.grid.SetCellValue(i,4,n.getAttribute('type'))
+        self.grid.SetCellValue(i,5,n.getAttribute('weight'))
+        self.grid.SetCellValue(i,6,ran)
+        self.grid.SetCellValue(i,7,n.getAttribute('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
+        #fn=safeGetAttr(n,'fn','None') #a (debug) 1.5012
+        #print "fn ",fn,"<" #a (debug) 1.5012
+        #o 1.5012 original damage vs what someone has changed it to.
+
+        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):          #d 1.6011 do it backwards,
+        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.master_dom.removeChild(self.n_list[i])
+                self.n_list = self.master_dom.getElementsByTagName('weapon')
+                self.handler.refresh_weapons()
+
+    def on_add(self,evt):
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3eweapons.xml","r")
+            #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('weapon')
+        opts = []
+        #print "weapon_panel - on_add f_list",f_list#a 1.6
+        for f in f_list:
+            opts.append(f.getAttribute('name'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            #print f_list[i] # DOM Element: weapon.
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            #print self.grid.AppendRows # a bound method of wxGrid
+            self.grid.AppendRows(1)
+            self.n_list = self.master_dom.getElementsByTagName('weapon')
+            #print "self.n_list",self.n_list # list of DOM weapons
+            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.master_dom.setAttribute("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 #a 1.5002 allow ability to run up tree.
+        self.root = getRoot(self) #a 1.5002
+        self.root.ac = self #a 1.6009
+
+    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')
+        #m 1.5009 change to hardcode dex, was incorrect gv "stat"
+        dex_mod = self.root.abilities.get_mod('Dex')#m 1.5009 hardcode 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.master_dom.getElementsByTagName('armor')
+        dex = 10
+        for a in armor_list:
+            temp = int(a.getAttribute("maxdex"))
+            if temp < dex:
+                dex = temp
+        return dex
+
+    def get_total(self,attr):
+        armor_list = self.master_dom.getElementsByTagName('armor')
+        total = 0
+        for a in armor_list:
+            total += int(a.getAttribute(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.master_dom._get_childNodes()
+        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.getAttribute('name')+"</td>"
+            html_str += "<td>"+n.getAttribute('type')+"</td>"
+            html_str += "<td>"+n.getAttribute('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.getAttribute('checkpenalty')+"</td>"
+            html_str += "<td>"+n.getAttribute('spellfailure')+"</td>"
+            html_str += "<td>"+n.getAttribute('maxdex')+"</td>"
+            html_str += "<td>"+n.getAttribute('speed')+"</td>"
+            html_str += "<td>"+n.getAttribute('weight')+"</td></tr></table>"
+        return html_str
+
+
+class ac_panel(wx.Panel):
+    def __init__(self, parent, handler):
+        pname = handler.master_dom.setAttribute("name", 'Armor')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        #a 1.5002 we need the functional parent, not the invoking parent.
+
+        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_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.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
+        self.master_dom = handler.master_dom
+        n_list = handler.master_dom._get_childNodes()
+        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].setAttribute(self.atts[col],value)
+            except:
+                self.grid.SetCellValue(row,col,"0")
+        else:
+            self.n_list[row].setAttribute(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.getAttribute(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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3earmor.xml","r")
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('armor')
+        opts = []
+        for f in f_list:
+            opts.append(f.getAttribute('name'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            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 #a 1.5002 allow ability to run up tree.
+
+
+        self.frame = open_rpg.get_component('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.master_dom.getElementsByTagName(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 set_char_pp(self,attr,evl):     #d 1.5002 doesn't seem to be used, but
+#        dnd_globals["pp"][attr] = evl  #d 1.5002 uses dnd_globals, so tossing.
+
+
+#    def get_char_pp( self, attr ):     #d 1.5002 doesn't seem to be used, but
+#        return dnd_globals["pp"][attr]     #d 1.5002 doesn't seem to be used, but
+
+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 = open_rpg.get_component('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.master_dom.getAttribute('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.master_dom.getElementsByTagName( 'spell' )
+        self.spells = {}
+        tree = self.tree
+        icons = self.tree.icons
+        for n in node_list:
+            name = n.getAttribute('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 ].getAttribute( 'level' )
+            descr = self.spells[ name ].getAttribute( 'desc' )
+            use = self.spells[ name ].getAttribute( 'used' )
+            memrz = self.spells[ name ].getAttribute( '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."' % ( dnd_globals["gen"]["Name"], name )##d 1.5002
+                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" )' % ( dnd_globals["gen"]["Name"], name, level, descr )#d 1.5002
+                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' % ( dnd_globals["gen"]["Name"], name, left, s )#d 1.5002
+                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 ].setAttribute( 'used', `eval( use )` )
+
+    def refresh_spells(self):
+        self.spells = {}
+        tree = self.tree
+        icons = self.tree.icons
+        tree.CollapseAndReset(self.mytree_node)
+        node_list = self.master_dom.getElementsByTagName('spell')
+        for n in node_list:
+            name = n.getAttribute('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.master_dom._get_childNodes()
+        for n in n_list:
+            html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('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.master_dom.setAttribute("name", 'Arcane Spells')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+
+        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_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_spells, id=30)
+        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
+        n_list = handler.master_dom._get_childNodes()
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        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].setAttribute('memrz',value)
+
+
+    def refresh_row(self,i):
+        spell = self.n_list[i]
+
+        memrz = spell.getAttribute('memrz')
+        name = spell.getAttribute('name')
+        type = spell.getAttribute('desc')
+        level = spell.getAttribute('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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3espells.xml","r")
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('spell')
+        opts = []
+        #lvl = int(dnd3e_char_child.get_char_lvl('level'))
+        #castlvl = eval('%s/2' % (lvl))
+        for f in f_list:
+            spelllvl = f.getAttribute('level')
+            #if spelllvl <= "1":
+            #    opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name'))
+            #else:
+            #    if eval('%d >= %s' %(castlvl, spelllvl)):
+            opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            self.grid.AppendRows(1)
+            self.n_list = self.master_dom.getElementsByTagName('spell')
+            self.refresh_row(self.grid.GetNumberRows()-1)
+            self.handler.refresh_spells()
+        dlg.Destroy()
+
+    def on_refresh_spells( self, evt ):
+        f_list = self.master_dom.getElementsByTagName('spell')
+
+        for spell in f_list:
+            spell.setAttribute( '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.master_dom.getElementsByTagName( 'gift' )
+        self.spells = {}
+        tree = self.tree
+        icons = self.tree.icons
+        for n in node_list:
+            name = n.getAttribute('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 ].getAttribute( 'level' )
+            descr = self.spells[ name ].getAttribute( 'desc' )
+            use = self.spells[ name ].getAttribute( 'used' )
+            memrz = self.spells[ name ].getAttribute( '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
+                #txt += "Please rest so I can cast more."' % ( dnd_globals["gen"]["Name"], name ) #d 1.5002
+                self.chat.ParsePost( txt, True, False )
+            else:
+                #txt = '%s casts %s ( level %s, "%s" )' % ( dnd_globals["gen"]["Name"], name, level, descr ) #d 1.5002
+                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' % ( dnd_globals["gen"]["Name"], name, left, s ) #d 1.5002
+                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 ].setAttribute( 'used', `eval( use )` )
+
+    def refresh_spells(self):
+        self.spells = {}
+        tree = self.tree
+        icons = self.tree.icons
+        tree.CollapseAndReset(self.mytree_node)
+
+        node_list = self.master_dom.getElementsByTagName('gift')
+        for n in node_list:
+            name = n.getAttribute('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.master_dom._get_childNodes()
+        for n in n_list:
+            html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('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.master_dom.setAttribute("name", 'Divine Spells')
+        self.hparent = handler #a 1.5002 allow ability to run up tree.
+        #a 1.5002 in this case, we need the functional parent, not the invoking parent.
+
+        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_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_spells, id=30)
+        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change)
+
+        n_list = handler.master_dom._get_childNodes()
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        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].setAttribute('memrz',value)
+
+
+    def refresh_row(self,i):
+        spell = self.n_list[i]
+
+        memrz = spell.getAttribute('memrz')
+        name = spell.getAttribute('name')
+        type = spell.getAttribute('desc')
+        level = spell.getAttribute('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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3edivine.xml","r")
+
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('gift')
+        opts = []
+        #lvl = int(dnd3e_char_child.get_char_lvl('level'))
+        #castlvl = lvl / 2
+        for f in f_list:
+            spelllvl = f.getAttribute('level')
+            #if spelllvl <= "1":
+            #    opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name'))
+            #else:
+            #    if eval('%d >= %s' %(castlvl, spelllvl)):
+            opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            self.grid.AppendRows(1)
+            self.n_list = self.master_dom.getElementsByTagName('gift')
+            self.refresh_row(self.grid.GetNumberRows()-1)
+            self.handler.refresh_spells()
+        dlg.Destroy()
+
+    def on_refresh_spells( self, evt ):
+        f_list = self.master_dom.getElementsByTagName('gift')
+        for spell in f_list:
+            spell.setAttribute( '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.master_dom.getElementsByTagName( 'power' )
+        self.powers = {}
+        tree = self.tree
+        icons = self.tree.icons
+        for n in node_list:
+            name = n.getAttribute('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 ].getAttribute( 'level' ))
+            descr = self.powers[ name ].getAttribute( 'desc' )
+            #use can be removed -mgt
+            #use = self.powers[ name ].getAttribute( 'used' )
+            points = self.powers[ name ].getAttribute( '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.master_dom.getElementsByTagName('power')
+        for n in node_list:
+            name = n.getAttribute('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.master_dom._get_childNodes()
+        for n in n_list:
+            html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('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.master_dom.setAttribute("name", 'Psionic Powers')
+        self.hparent = handler #a 1.5002 allow ability to run up tree. In this
+        #a 1.5002 case, we need the functional parent, not the invoking parent.
+        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.master_dom._get_childNodes()
+        self.n_list = n_list
+        self.master_dom = handler.master_dom
+        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)
+        """if col == 0:
+            self.n_list[row].setAttribute('memrz',value)"""
+
+
+    def refresh_row(self,i):
+        power = self.n_list[i]
+
+        point = power.getAttribute('point')
+        name = power.getAttribute('name')
+        type = power.getAttribute('desc')
+        test = power.getAttribute('test')
+        level = power.getAttribute('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.master_dom.removeChild(self.n_list[i])
+
+    def on_add(self,evt):
+        if not self.temp_dom:
+            tmp = open(orpg.dirpath.dir_struct["dnd3e"]+"dnd3epowers.xml","r")
+
+            xml_dom = parseXml_with_dlg(self,tmp.read())
+            xml_dom = xml_dom._get_firstChild()
+            tmp.close()
+            self.temp_dom = xml_dom
+        f_list = self.temp_dom.getElementsByTagName('power')
+        opts = []
+        #lvl = int(dnd3e_char_child.get_char_lvl('level'))
+        #castlvl = lvl / 2
+        for f in f_list:
+            spelllvl = f.getAttribute('level')
+            #if spelllvl <= "1":
+            #    opts.append("(" + f.getAttribute('level') + ") - " + f.getAttribute('name') + " - " + f.getAttribute('test'))
+            #else:
+            #    if eval('%d >= %s' %(castlvl, spelllvl)):
+            opts.append("(" + f.getAttribute('level') + ") - " +
+                        f.getAttribute('name') + " - " + f.getAttribute('test'))
+        dlg = wx.SingleChoiceDialog(self,'Choose Power','Powers',opts)
+        if dlg.ShowModal() == wx.ID_OK:
+            i = dlg.GetSelection()
+            new_node = self.master_dom.appendChild(f_list[i].cloneNode(False))
+            self.grid.AppendRows(1)
+            self.n_list = self.master_dom.getElementsByTagName('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.master_dom.removeChild(self.n_list[i])
+                self.n_list = self.master_dom.getElementsByTagName('weapon')
+                self.handler.refresh_powers()
+
+    def on_refresh_powers( self, evt ):
+        #a 1.5002,1.5014 s
+        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'))
+        #a 1.5002,1.5014 e
+
+
+
+    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=8>Power Points</th></tr>" #d 1.6010
+        html_str += "<th colspan=7>Power Points</th>" #a 1.6010
+        #m 1.6010 rearanged everything below to "return html_str"
+        html_str += "</tr><tr>"
+        html_str += "<th colspan=2>Max:</th>"
+        html_str += "<td>"+self.master_dom.getAttribute('max1')+"</td>"
+        html_str += "<th colspan=3>Max Talents/day:</th>"
+        html_str += "<td>"+self.master_dom.getAttribute('maxfree')+"</td>"
+        html_str += "</tr><tr>"
+        html_str += "<th colspan=2>Current:</th>"
+        html_str += "<td>"+self.master_dom.getAttribute('current1')+"</td>"
+        html_str += "<th colspan=3>Current Talents/day:</th>"
+        html_str += "<td>"+self.master_dom.getAttribute('free')+"</td>"
+        html_str += "</tr></table>"
+        return html_str
+
+    def get_char_pp( self, attr ):
+        pp = self.master_dom.getAttribute(attr)
+        #print "dnd3epp -get_char_pp: attr,pp",attr,pp
+        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.master_dom.setAttribute(attr, qSub)
+        #This function needs to be looked at the idea is to refresh the power panel
+        #But it causes a seg fault when you refresh from powers -mgt
+        #if self.ppPanel:                #a 1.5015
+        #    self.ppPanel.on_refresh(attr,qSub)   #a 1.5015
+
+
+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.
+        #a 1.5002 we need the functional parent, not the invoking parent.
+        self.hparent.ppPanel=self #a 1.5xx
+
+        pname = handler.master_dom.setAttribute("name", 'PowerPoints')
+        self.sizer = wx.FlexGridSizer(2, 4, 2, 2)  # rows, cols, hgap, vgap
+        self.master_dom = handler.master_dom
+
+        self.static1= wx.StaticText(self, -1, "PP Current:")  #a 1.5015
+        self.dyn1= wx.TextCtrl(self, PP_CUR,
+                self.master_dom.getAttribute('current1'))   #a 1.5015
+        self.dyn3= wx.TextCtrl(self, PP_FRE,
+                self.master_dom.getAttribute('free'))       #a 1.5015
+#        self.sizer.AddMany([ (wx.StaticText(self, -1, "PP Current:"),  #d 1.5015
+#                                           0, wx.ALIGN_CENTER_VERTICAL),
+#            (wx.TextCtrl(self, PP_CUR,                                 #d 1.5015
+#                self.master_dom.getAttribute('current1')),   0, wx.EXPAND),
+        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.master_dom.getAttribute('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.TextCtrl(self, PP_FRE,
+#                self.master_dom.getAttribute('free')),  0, wx.EXPAND),#d 1.5015
+            (wx.StaticText(self, -1, "Max Free Talants per day:"),
+                            0, wx.ALIGN_CENTER_VERTICAL),
+            (wx.TextCtrl(self, PP_MFRE,
+                self.master_dom.getAttribute('maxfree')),  0, wx.EXPAND),
+            ])
+
+        self.sizer.AddGrowableCol(1)
+        self.SetSizer(self.sizer)
+        self.SetAutoLayout(True)
+        self.Fit()
+
+        #self.Bind(wx.EVT_SIZE, self.on_size)
+        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.master_dom.setAttribute('current1',evt.GetString())
+        elif id == PP_MAX:
+            self.master_dom.setAttribute('max1',evt.GetString())
+        elif id == PP_FRE:
+            self.master_dom.setAttribute('free',evt.GetString())
+        elif id == PP_MFRE:
+            self.master_dom.setAttribute('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)