diff orpg/chat/chatwnd.py @ 155:bf799efe7a8a alpha

Traipse Alpha 'OpenRPG' {091125-02} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Cleaning up for Beta) Added Bookmarks Fix to Remote Admin Commands Minor fix to text based Server Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Added 'boot' command to remote admin Added confirmation window for sent nodes Minor changes to allow for portability to an OpenSUSE linux OS Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Zoom Mouse plugin added Images added to Plugin UI Switching to Element Tree Map efficiency, from FlexiRPG Added Status Bar to Update Manager default_manifest.xml renamed to default_upmana.xml Cleaner clode for saved repositories New TrueDebug Class in orpg_log (See documentation for usage) Mercurial's hgweb folder is ported to upmana Pretty important update that can help remove thousands of dead children from your gametree. Children, <forms />, <group_atts />, <horizontal />, <cols />, <rows />, <height />, etc... are all tags now. Check your gametree and look for dead children!! New Gametree Recursion method, mapping, and context sensitivity. !Infinite Loops return error instead of freezing the software! New Syntax added for custom PC sheets Tip of the Day added, from Core and community Fixed Whiteboard ID to prevent random line or text deleting. Modified ID's to prevent non updated clients from ruining the fix.
author sirebral
date Wed, 25 Nov 2009 12:32:34 -0600
parents 06f10429eedc
children a766e1cbcb7c
line wrap: on
line diff
--- a/orpg/chat/chatwnd.py	Wed Nov 25 06:50:52 2009 -0600
+++ b/orpg/chat/chatwnd.py	Wed Nov 25 12:32:34 2009 -0600
@@ -1,1876 +1,1876 @@
-# 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: chatutils.py
-# Author: Chris Davis
-# Maintainer:
-# Version:
-#   $Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $
-#
-# Description: This file contains some of the basic definitions for the chat
-# utilities in the orpg project.
-#
-# History
-# 2002-01-20 HeroMan
-#   + Added 4 dialog items on toolbar in support of Alias Library Functionallity
-#   + Shrunk the text view button to an image
-# 2005-04-25 Snowdog
-#   + Added simple_html_repair() to post() to fix malformed html in the chat window
-#   + Added strip_script_tags() to post() to remove crash point. See chat_util.py
-# 2005-04-25 Snowdog
-#   + Added simple_html_repair() to post() to fix malformed html in the chat window
-#
-
-__version__ = "$Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $"
-
-
-##
-## Module Loading
+# 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: chatutils.py
+# Author: Chris Davis
+# Maintainer:
+# Version:
+#   $Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $
+#
+# Description: This file contains some of the basic definitions for the chat
+# utilities in the orpg project.
+#
+# History
+# 2002-01-20 HeroMan
+#   + Added 4 dialog items on toolbar in support of Alias Library Functionallity
+#   + Shrunk the text view button to an image
+# 2005-04-25 Snowdog
+#   + Added simple_html_repair() to post() to fix malformed html in the chat window
+#   + Added strip_script_tags() to post() to remove crash point. See chat_util.py
+# 2005-04-25 Snowdog
+#   + Added simple_html_repair() to post() to fix malformed html in the chat window
+#
+
+__version__ = "$Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $"
+
+
+##
+## Module Loading
 ##
 import os, time, re, sys, traceback, webbrowser, commands, chat_msg, chat_util
 
-from orpg.orpg_version import VERSION
-from orpg.orpg_windows import *
-from orpg.player_list import WG_LIST
-from orpg.dirpath import dir_struct
-#from orpg.tools.metamenus import MenuEx #Needed?
-from string import *
+from orpg.orpg_version import VERSION
+from orpg.orpg_windows import *
+from orpg.player_list import WG_LIST
+from orpg.dirpath import dir_struct
+#from orpg.tools.metamenus import MenuEx #Needed?
+from string import *
 
-import cStringIO # for reading inline imagedata as a stream
-from HTMLParser import HTMLParser
-from wx.lib.expando import EVT_ETC_LAYOUT_NEEDED 
+import cStringIO # for reading inline imagedata as a stream
+from HTMLParser import HTMLParser
+from wx.lib.expando import EVT_ETC_LAYOUT_NEEDED 
 
-import orpg.tools.rgbhex
-import orpg.tools.inputValidator
-from orpg.tools.validate import validate
+import orpg.tools.rgbhex
+import orpg.tools.inputValidator
+from orpg.tools.validate import validate
 from orpg.tools.orpg_settings import settings
-import orpg.tools.predTextCtrl
+import orpg.tools.predTextCtrl
 from orpg.tools.orpg_log import logger, debug
 from orpg.orpgCore import component
 from xml.etree.ElementTree import tostring
 
-from orpg.networking.mplay_client import MPLAY_CONNECTED  # needed to only send typing/not_typing messages while connected
-
-NEWCHAT = False
-try:
-    import wx.webview
-    NEWCHAT = True
-except: pass
-NEWCHAT = False
-
-# Global parser for stripping HTML tags:
-# The 'tag stripping' is implicit, because this parser echoes every
-# type of html data *except* the tags.
-class HTMLStripper(HTMLParser):
-    
-    def __init__(self):
-        self.accum = ""
-        self.special_tags = ['hr', 'br', 'img']
-    
-    def handle_data(self, data):  # quote cdata literally
-        self.accum += data
-    
-    def handle_entityref(self, name): # entities must be preserved exactly
-        self.accum += "&" + name + ";"
-    
-    def handle_starttag(self, tag, attrs):
-        if tag in self.special_tags:
-            self.accum += '<' + tag
-            for attrib in attrs: self.accum += ' ' + attrib[0] + '="' + attrib[1] + '"'
-            self.accum += '>'
-    
-    def handle_charref(self, name):  # charrefs too
-        self.accum += "&#" + name + ";"
-htmlstripper = HTMLStripper()
-
-# utility function;  see Post().
-
-def strip_html(string):
-    "Return string tripped of html tags."
-    htmlstripper.reset()
-    htmlstripper.accum = ""
-    htmlstripper.feed(string)
-    htmlstripper.close()
-    return htmlstripper.accum
-
-
-def log( settings, c, text ):
-    filename = settings.get_setting('GameLogPrefix')
-    if filename > '' and filename[0] != commands.ANTI_LOG_CHAR:
-        filename = filename + time.strftime( '-%Y-%m-%d.html', time.localtime( time.time() ) )
-        #filename = time.strftime( filename, time.localtime( time.time() ) )
-        timestamp = time.ctime(time.time())
-        header = '[%s] : ' % ( timestamp );
-        if settings.get_setting('TimeStampGameLog') != '1': header = ''
-        try:
-            f = open( dir_struct["user"] + filename, 'a' )
-            f.write( '<div class="'+c+'">%s%s</div>\n' % ( header, text ) )
-            f.close()
-        except:
-            print "could not open " + dir_struct["user"] + filename + ", ignoring..."
-            pass
-
-# This class displayes the chat information in html?
-#
-# Defines:
-#   __init__(self, parent, id)
-#   OnLinkClicked(self, linkinfo)
-#   CalculateAllFonts(self, defaultsize)
-#   SetDefaultFontAndSize(self, fontname)
-#
-class chat_html_window(wx.html.HtmlWindow):
-    """ a wxHTMLwindow that will load links  """
-    # initialization subroutine
-    #
-    # !self : instance of self
-    # !parent :
-    # !id :
-    
-    def __init__(self, parent, id):
-        wx.html.HtmlWindow.__init__(self, parent, id, 
-                                    style=wx.SUNKEN_BORDER|wx.html.HW_SCROLLBAR_AUTO|wx.NO_FULL_REPAINT_ON_RESIZE)
-        self.parent = parent
-        self.build_menu()
-        self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
-        self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
-        if "gtk2" in wx.PlatformInfo: self.SetStandardFonts()
-        # def __init__ - end
-
-    
-    def onPopup(self, evt):
-        self.PopupMenu(self.menu)
-
-    
-    def LeftUp(self, event):
-        event.Skip()
-        wx.CallAfter(self.parent.set_chat_text_focus, None)
-
-    
-    def build_menu(self):
-        self.menu = wx.Menu()
-        item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
-        self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
-        self.menu.AppendItem(item)
-
-    
-    def OnM_EditCopy(self, evt):
-        wx.TheClipboard.UsePrimarySelection(False)
-        wx.TheClipboard.Open()
-        wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
-        wx.TheClipboard.Close()
-
-    
-    def scroll_down(self):
-        maxrange = self.GetScrollRange(wx.VERTICAL)
-        pagesize = self.GetScrollPageSize(wx.VERTICAL)
-        self.Scroll(-1, maxrange-pagesize)
-
-    
-    def mouse_wheel(self, event):
-        amt = event.GetWheelRotation()
-        units = amt/(-(event.GetWheelDelta()))
-        self.ScrollLines(units*3)
-
-    
-    def Header(self):
-        return '<html><body bgcolor="' + self.parent.bgcolor + '" text="' + self.parent.textcolor + '">'
-
-    
-    def StripHeader(self):
-        return self.GetPageSource().replace(self.Header(), '')
-
-    
-    def GetPageSource(self):
-        return self.GetParser().GetSource()
-
-    # This subroutine fires up the webbrowser when a link is clicked.
-    #
-    # !self : instance of self
-    # !linkinfo : instance of a class that contains the link information
-    
-    def OnLinkClicked(self, linkinfo):
-        href = linkinfo.GetHref()
-        wb = webbrowser.get()
-        wb.open(href)
-    # def OnLinkClicked - end
-
-    
-    def CalculateAllFonts(self, defaultsize):
-        return [int(defaultsize * 0.4),
-                int(defaultsize * 0.7),
-                int(defaultsize),
-                int(defaultsize * 1.3),
-                int(defaultsize * 1.7),
-                int(defaultsize * 2),
-                int(defaultsize * 2.5)]
-
-    
-    def SetDefaultFontAndSize(self, fontname, fontsize):
-        """Set 'fontname' to the default chat font.
-           Returns current font settings in a (fontname, fontsize) tuple."""
-        self.SetFonts(fontname, "", self.CalculateAllFonts(int(fontsize)))
-        return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
-
-# class chat_html_window - end
+from orpg.networking.mplay_client import MPLAY_CONNECTED  # needed to only send typing/not_typing messages while connected
+
+NEWCHAT = False
+try:
+    import wx.webview
+    NEWCHAT = True
+except: pass
+NEWCHAT = False
+
+# Global parser for stripping HTML tags:
+# The 'tag stripping' is implicit, because this parser echoes every
+# type of html data *except* the tags.
+class HTMLStripper(HTMLParser):
+    
+    def __init__(self):
+        self.accum = ""
+        self.special_tags = ['hr', 'br', 'img']
+    
+    def handle_data(self, data):  # quote cdata literally
+        self.accum += data
+    
+    def handle_entityref(self, name): # entities must be preserved exactly
+        self.accum += "&" + name + ";"
+    
+    def handle_starttag(self, tag, attrs):
+        if tag in self.special_tags:
+            self.accum += '<' + tag
+            for attrib in attrs: self.accum += ' ' + attrib[0] + '="' + attrib[1] + '"'
+            self.accum += '>'
+    
+    def handle_charref(self, name):  # charrefs too
+        self.accum += "&#" + name + ";"
+htmlstripper = HTMLStripper()
+
+# utility function;  see Post().
+
+def strip_html(string):
+    "Return string tripped of html tags."
+    htmlstripper.reset()
+    htmlstripper.accum = ""
+    htmlstripper.feed(string)
+    htmlstripper.close()
+    return htmlstripper.accum
+
+
+def log( settings, c, text ):
+    filename = settings.get_setting('GameLogPrefix')
+    if filename > '' and filename[0] != commands.ANTI_LOG_CHAR:
+        filename = filename + time.strftime( '-%Y-%m-%d.html', time.localtime( time.time() ) )
+        #filename = time.strftime( filename, time.localtime( time.time() ) )
+        timestamp = time.ctime(time.time())
+        header = '[%s] : ' % ( timestamp );
+        if settings.get_setting('TimeStampGameLog') != '1': header = ''
+        try:
+            f = open( dir_struct["user"] + filename, 'a' )
+            f.write( '<div class="'+c+'">%s%s</div>\n' % ( header, text ) )
+            f.close()
+        except:
+            print "could not open " + dir_struct["user"] + filename + ", ignoring..."
+            pass
+
+# This class displayes the chat information in html?
+#
+# Defines:
+#   __init__(self, parent, id)
+#   OnLinkClicked(self, linkinfo)
+#   CalculateAllFonts(self, defaultsize)
+#   SetDefaultFontAndSize(self, fontname)
+#
+class chat_html_window(wx.html.HtmlWindow):
+    """ a wxHTMLwindow that will load links  """
+    # initialization subroutine
+    #
+    # !self : instance of self
+    # !parent :
+    # !id :
+    
+    def __init__(self, parent, id):
+        wx.html.HtmlWindow.__init__(self, parent, id, 
+                                    style=wx.SUNKEN_BORDER|wx.html.HW_SCROLLBAR_AUTO|wx.NO_FULL_REPAINT_ON_RESIZE)
+        self.parent = parent
+        self.build_menu()
+        self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
+        self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
+        if "gtk2" in wx.PlatformInfo: self.SetStandardFonts()
+        # def __init__ - end
+
+    
+    def onPopup(self, evt):
+        self.PopupMenu(self.menu)
+
+    
+    def LeftUp(self, event):
+        event.Skip()
+        wx.CallAfter(self.parent.set_chat_text_focus, None)
+
+    
+    def build_menu(self):
+        self.menu = wx.Menu()
+        item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
+        self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
+        self.menu.AppendItem(item)
+
+    
+    def OnM_EditCopy(self, evt):
+        wx.TheClipboard.UsePrimarySelection(False)
+        wx.TheClipboard.Open()
+        wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
+        wx.TheClipboard.Close()
+
+    
+    def scroll_down(self):
+        maxrange = self.GetScrollRange(wx.VERTICAL)
+        pagesize = self.GetScrollPageSize(wx.VERTICAL)
+        self.Scroll(-1, maxrange-pagesize)
+
+    
+    def mouse_wheel(self, event):
+        amt = event.GetWheelRotation()
+        units = amt/(-(event.GetWheelDelta()))
+        self.ScrollLines(units*3)
+
+    
+    def Header(self):
+        return '<html><body bgcolor="' + self.parent.bgcolor + '" text="' + self.parent.textcolor + '">'
+
+    
+    def StripHeader(self):
+        return self.GetPageSource().replace(self.Header(), '')
+
+    
+    def GetPageSource(self):
+        return self.GetParser().GetSource()
+
+    # This subroutine fires up the webbrowser when a link is clicked.
+    #
+    # !self : instance of self
+    # !linkinfo : instance of a class that contains the link information
+    
+    def OnLinkClicked(self, linkinfo):
+        href = linkinfo.GetHref()
+        wb = webbrowser.get()
+        wb.open(href)
+    # def OnLinkClicked - end
+
+    
+    def CalculateAllFonts(self, defaultsize):
+        return [int(defaultsize * 0.4),
+                int(defaultsize * 0.7),
+                int(defaultsize),
+                int(defaultsize * 1.3),
+                int(defaultsize * 1.7),
+                int(defaultsize * 2),
+                int(defaultsize * 2.5)]
+
+    
+    def SetDefaultFontAndSize(self, fontname, fontsize):
+        """Set 'fontname' to the default chat font.
+           Returns current font settings in a (fontname, fontsize) tuple."""
+        self.SetFonts(fontname, "", self.CalculateAllFonts(int(fontsize)))
+        return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
+
+# class chat_html_window - end
 if NEWCHAT:
-    class ChatHtmlWindow(wx.webview.WebView):
-        
-        def __init__(self, parent, id):
-            wx.webview.WebView.__init__(self, parent, id)
-            self.parent = parent
-            self.__font = wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, faceName='Ariel')
-            self.build_menu()
-            self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
-            self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
-            self.Bind(wx.webview.EVT_WEBVIEW_BEFORE_LOAD, self.OnLinkClicked)
-
-        #Wrapers so I dont have to add special Code
-        def SetPage(self, htmlstring):
-            self.SetPageSource(htmlstring)
-
+    class ChatHtmlWindow(wx.webview.WebView):
+        
+        def __init__(self, parent, id):
+            wx.webview.WebView.__init__(self, parent, id)
+            self.parent = parent
+            self.__font = wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, faceName='Ariel')
+            self.build_menu()
+            self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
+            self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
+            self.Bind(wx.webview.EVT_WEBVIEW_BEFORE_LOAD, self.OnLinkClicked)
+
+        #Wrapers so I dont have to add special Code
+        def SetPage(self, htmlstring):
+            self.SetPageSource(htmlstring)
+
         def AppendToPage(self, htmlstring):
-            self.SetPageSource(self.GetPageSource() + htmlstring)
-
-        def GetFont(self):
-            return self.__font
-
-        def CalculateAllFonts(self, defaultsize):
-            return
-
-        def SetDefaultFontAndSize(self, fontname, fontsize):
-            self.__font = wx.Font(int(fontsize), 
-                            wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, 
-                            wx.FONTWEIGHT_NORMAL, faceName=fontname)
-            try: self.SetPageSource(self.Header() + self.StripHeader())
-            except Exception, e: print e
-            return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
-
-        #Events
-        def OnLinkClicked(self, linkinfo):
-            href = linkinfo.GetHref()
-            wb = webbrowser.get()
-            wb.open(href)
-
-        def onPopup(self, evt):
-            self.PopupMenu(self.menu)
-
-        def LeftUp(self, event):
-            event.Skip()
-            wx.CallAfter(self.parent.set_chat_text_focus, None)
-
-        def OnM_EditCopy(self, evt):
-            wx.TheClipboard.UsePrimarySelection(False)
-            wx.TheClipboard.Open()
-            wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
-            wx.TheClipboard.Close()
-
-        #Cutom Methods
-        def Header(self):
-            return "<html><head><style>body {font-size: " + str(self.GetFont().GetPointSize()) + "px;font-family: " + self.GetFont().GetFaceName() + ";color: " + self.parent.textcolor + ";background-color: " + self.parent.bgcolor + ";margin: 0;padding: 0 0;height: 100%;}</style></head><body>"
-
-        def StripHeader(self):
-            tmp = self.GetPageSource().split('<BODY>')
-            if tmp[-1].find('<body>') > -1: tmp = tmp[-1].split('<body>')
-            return tmp[-1]
-
-        def build_menu(self):
-            self.menu = wx.Menu()
-            item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
-            self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
-            self.menu.AppendItem(item)
-
-        def scroll_down(self):
-            maxrange = self.GetScrollRange(wx.VERTICAL)
-            pagesize = self.GetScrollPageSize(wx.VERTICAL)
-            self.Scroll(-1, maxrange-pagesize)
-
-        def mouse_wheel(self, event):
-            amt = event.GetWheelRotation()
-            units = amt/(-(event.GetWheelDelta()))
-            self.ScrollLines(units*3)
-    chat_html_window = ChatHtmlWindow
-
-#########################
-#chat frame window
-#########################
-# These are kinda global...and static..and should be located somewhere else
-# then the middle of a file between two classes.
-
-###################
-# Tab Types
-###################
-MAIN_TAB = wx.NewId()
-WHISPER_TAB = wx.NewId()
-GROUP_TAB = wx.NewId()
-NULL_TAB = wx.NewId()
-
-# This class defines the tabbed 'notebook' that holds multiple chatpanels.
-# It's the widget attached to the main application frame.
-#
-# Inherits:  wxNotebook
-#
-# Defines:
-#   create_private_tab(self, playerid)
-#   get_tab_index(self, chatpanel)
-#   destroy_private_tab(self, chatpanel)
-#   OnPageChanged(self, event)
-#   set_default_font(self, font, fontsize)
-
-class chat_notebook(orpgTabberWnd):
-    
-    def __init__(self, parent, size):
-        orpgTabberWnd.__init__(self, parent, True, size=size, 
-                style=FNB.FNB_DROPDOWN_TABS_LIST|FNB.FNB_NO_NAV_BUTTONS|FNB.FNB_MOUSE_MIDDLE_CLOSES_TABS)
-        self.settings = component.get("settings")
-        self.whisper_tabs = []
-        self.group_tabs = []
-        self.null_tabs = []
-        self.il = wx.ImageList(16, 16)
-        bmp = wx.Bitmap(dir_struct["icon"]+'player.gif')
-        self.il.Add(bmp)
-        bmp = wx.Bitmap(dir_struct["icon"]+'clear.gif')
-        self.il.Add(bmp)
-        self.SetImageList(self.il)
-        # Create "main" chatpanel tab, undeletable, connected to 'public' room.
-        self.MainChatPanel = chat_panel(self, -1, MAIN_TAB, 'all')
-        self.AddPage(self.MainChatPanel, "Main Room")
-        self.SetPageImage(0, 1)
-        self.chat_timer = wx.Timer(self, wx.NewId())
-        self.Bind(wx.EVT_TIMER, self.MainChatPanel.typingTimerFunc)
-        self.chat_timer.Start(1000)
-        # Hook up event handler for flipping tabs
-        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.onPageChanged)
-        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.onPageChanging)
-        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.onCloseTab)
-        # html font/fontsize is global to all the notebook tabs.
-        self.font, self.fontsize =  self.MainChatPanel.chatwnd.SetDefaultFontAndSize(self.settings.get_setting('defaultfont'), self.settings.get_setting('defaultfontsize'))
-        self.GMChatPanel = None
-        if self.settings.get_setting("GMWhisperTab") == '1':
-            self.create_gm_tab()
-        self.SetSelection(0)
-
-    def get_tab_index(self, chatpanel):
-        "Return the index of a chatpanel in the wxNotebook."
-
-        for i in xrange(self.GetPageCount()):
-            if (self.GetPage(i) == chatpanel):
-                return i
-
-    def create_gm_tab(self):
-        if self.GMChatPanel == None:
-            self.GMChatPanel = chat_panel(self, -1, MAIN_TAB, 'gm')
-            self.AddPage(self.GMChatPanel, "GM", False)
-            self.SetPageImage(self.GetPageCount()-1, 1)
-            self.GMChatPanel.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
-
-    def create_whisper_tab(self, playerid):
-        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
-        private_tab = chat_panel(self, -1, WHISPER_TAB, playerid)
-        playername = strip_html(self.MainChatPanel.session.get_player_by_player_id(playerid)[0])
-        self.AddPage(private_tab, playername, False)
-        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
-        self.whisper_tabs.append(private_tab)
-        self.newMsg(self.GetPageCount()-1)
-        self.AliasLib = component.get('alias')
-        wx.CallAfter(self.AliasLib.RefreshAliases)
-        return private_tab
-
-    def create_group_tab(self, group_name):
-        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
-        private_tab = chat_panel(self, -1, GROUP_TAB, group_name)
-        self.AddPage(private_tab, group_name, False)
-        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
-        self.group_tabs.append(private_tab)
-        self.newMsg(self.GetPageCount()-1)
-        self.AliasLib = component.get('alias')
-        wx.CallAfter(self.AliasLib.RefreshAliases)
-        return private_tab
-
-    def create_null_tab(self, tab_name):
-        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
-        private_tab = chat_panel(self, -1, NULL_TAB, tab_name)
-        self.AddPage(private_tab, tab_name, False)
-        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
-        self.null_tabs.append(private_tab)
-        self.newMsg(self.GetPageCount()-1)
-        self.AliasLib = component.get('alias')
-        wx.CallAfter(self.AliasLib.RefreshAliases)
-        return private_tab
-
-    def onCloseTab(self, evt):
-        try: tabid = evt.GetSelection()
-        except: tabid = self.GetSelection()
-        if self.GetPageText(tabid) == 'Main Room':
-            #send no close error to chat
-            evt.Veto()
-            return
-        if self.GetPageText(tabid) == 'GM':
-            msg = "Are You Sure You Want To Close This Page?"
-            dlg = wx.MessageDialog(self, msg, "NotebookCtrl Question",
-                                   wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
-            if wx.Platform != '__WXMAC__':
-                dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False))
-
-            if dlg.ShowModal() in [wx.ID_NO]:
-                dlg.Destroy()
-                evt.Veto()
-                return
-            dlg.Destroy()
-            self.GMChatPanel = None
-            self.settings.set_setting("GMWhisperTab", "0")
-        panel = self.GetPage(tabid)
-        if panel in self.whisper_tabs: self.whisper_tabs.remove(panel)
-        elif panel in self.group_tabs: self.group_tabs.remove(panel)
-        elif panel in self.null_tabs: self.null_tabs.remove(panel)
-
-    def newMsg(self, tabid):
-        if tabid != self.GetSelection(): self.SetPageImage(tabid, 0)
-
-    def onPageChanging(self, event):
-        """When private chattabs are selected, set the bitmap back to 'normal'."""
-        event.Skip()
-
-    def onPageChanged(self, event):
-        """When private chattabs are selected, set the bitmap back to 'normal'."""
-        selected_idx = event.GetSelection()
-        self.SetPageImage(selected_idx, 1)
-        page = self.GetPage(selected_idx)
-        #wx.CallAfter(page.set_chat_text_focus, 0)
-        event.Skip()
-
-"""
- This class defines and builds the Chat Frame for OpenRPG
-
- Inherits: wxPanel
-
- Defines:
-   __init__((self, parent, id, openrpg, sendtarget)
-   build_ctrls(self)
-   on_buffer_size(self,evt)
-   set_colors(self)
-   set_buffersize(self)
-   set_chat_text(self,txt)
-   OnChar(self,event)
-   on_chat_save(self,evt)
-   on_text_color(self,event)
-   colorize(self, color, text)
-   on_text_format(self,event)
-   OnSize(self,event)
-   scroll_down(self)
-   InfoPost(self,s)
-   Post(self,s="",send=False,myself=False)
-   ParsePost(self,s,send=False,myself=False)
-   ParseDice(self,s)
-   ParseNodes(self,s)
-   get_sha_checksum(self)
-   get_color(self)
-
-"""
-
-class chat_panel(wx.Panel):
-
-    """
-    This is the initialization subroutine
-    
-    !self : instance of self
-    !parent : parent that defines the chatframe
-    !id :
-    !openrpg :
-    !sendtarget:  who gets outbound messages: either 'all' or a playerid
-    """
-
-    
-    def __init__(self, parent, id, tab_type, sendtarget):
-        wx.Panel.__init__(self, parent, id)
-        logger._set_log_to_console(False)
-        self.session = component.get('session')
-        self.settings = component.get('settings')
-        self.activeplugins = component.get('plugins')
-        self.parent = parent
-        # who receives outbound messages, either "all" or "playerid" string
-        self.sendtarget = sendtarget
-        self.type = tab_type
-        #self.sound_player = component.get('sound') #Removing!
-        # create die roller manager
-        #self.DiceManager = component.get('DiceManager') #Removing!
-        # create rpghex tool
-        self.r_h = orpg.tools.rgbhex.RGBHex()
-        self.h = 0
-        self.set_colors()
-        self.version = VERSION
-        self.histidx = -1
-        self.temptext = ""
-        self.history = []
-        self.storedata = []
-        #self.lasthistevt = None
-        self.parsed=0
-        #chat commands
-        self.lockscroll = False      # set the default to scrolling on.
-        self.chat_cmds = commands.chat_commands(self)
-        self.html_strip = strip_html
-        #Alias Lib stuff
-        self.defaultAliasName = 'Use Real Name'
-        self.defaultFilterName = 'No Filter'
-        self.advancedFilter = False
-        self.lastSend = 0         #  this is used to help implement the player typing indicator
-        self.lastPress = 0        #  this is used to help implement the player typing indicator
-        self.Bind(wx.EVT_SIZE, self.OnSize)
-        self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnSize) #require to keep text at bottom of chat when text entry expands --SD
-        self.build_ctrls()
-        StartupFont = self.settings.get_setting("defaultfont")
-        StartupFontSize = self.settings.get_setting("defaultfontsize")
-        if(StartupFont != "") and (StartupFontSize != ""):
-            try: self.set_default_font(StartupFont, int(StartupFontSize))
-            except: pass
-        self.font = self.chatwnd.GetFont().GetFaceName()
-        self.fontsize = self.chatwnd.GetFont().GetPointSize()
-        self.scroll_down()
-
-    def set_default_font(self, fontname=None, fontsize=None):
-        """Set all chatpanels to new default fontname/fontsize. 
-        Returns current font settings in a (fontname, fontsize) tuple."""
-        if (fontname is not None): newfont = fontname
-        else: newfont = self.font
-        if (fontsize is not None): newfontsize = int(fontsize)
-        else: newfontsize = int(self.fontsize)
-        self.chatwnd.SetDefaultFontAndSize(newfont, newfontsize)
-        self.InfoPost("Font is now " + newfont + " point size " + `newfontsize`)
-        self.font = newfont
-        self.fontsize = newfontsize
-        return (self.font, self.fontsize)
-
-    def build_menu(self):
-        top_frame = component.get('frame')
-        menu = wx.Menu()
-        item = wx.MenuItem(menu, wx.ID_ANY, "&Background color", "Background color")
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_BackgroundColor, item)
-        menu.AppendItem(item)
-        item = wx.MenuItem(menu, wx.ID_ANY, "&Text color", "Text color")
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_TextColor, item)
-        menu.AppendItem(item)
-        menu.AppendSeparator()
-        item = wx.MenuItem(menu, wx.ID_ANY, "&Chat Focus\tCtrl-H", "Chat Focus")
-        self.setChatFocusMenu = item
-        top_frame.Bind(wx.EVT_MENU, self.set_chat_text_focus, item)
-        menu.AppendItem(item)
-        menu.AppendSeparator()
-        item = wx.MenuItem(menu, wx.ID_ANY, "Toggle &Scroll Lock", "Toggle Scroll Lock")
-        top_frame.Bind(wx.EVT_MENU, self.lock_scroll, item)
-        menu.AppendItem(item)
-        item = wx.MenuItem(menu, wx.ID_ANY, "Save Chat &Log", "Save Chat Log")
-        top_frame.Bind(wx.EVT_MENU, self.on_chat_save, item)
-        menu.AppendItem(item)
-        item = wx.MenuItem(menu, wx.ID_ANY, "Text &View", "Text View")
-        top_frame.Bind(wx.EVT_MENU, self.pop_textpop, item)
-        menu.AppendItem(item)
-        item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Tab", "Swap Tabs")
-        top_frame.Bind(wx.EVT_MENU, self.forward_tabs, item)
-        menu.AppendItem(item)
-        item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Shift+Tab", "Swap Tabs")
-        top_frame.Bind(wx.EVT_MENU, self.back_tabs, item)
-        menu.AppendItem(item)
-        menu.AppendSeparator()
-        settingmenu = wx.Menu()
-        wndmenu = wx.Menu()
-        tabmenu = wx.Menu()
-        toolmenu = wx.Menu()
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show Images", "Show Images", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowImages, item)
-
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("Show_Images_In_Chat") == '1': item.Check(True)
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Strip HTML", "Strip HTML", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_StripHTML, item)
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("striphtml") == '1': item.Check(True)
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Time Index", "Chat Time Index", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatTimeIndex, item)
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("Chat_Time_Indexing") == '1': item.Check(True)
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Auto Complete", "Chat Auto Complete", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatAutoComplete, item)
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("SuppressChatAutoComplete") == '0': item.Check(True)
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show ID in Chat", "Show ID in Chat", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowIDinChat, item)
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("ShowIDInChat") == '1': item.Check(True)
-        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Log Time Index", "Log Time Index", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_LogTimeIndex, item)
-        wndmenu.AppendItem(item)
-        if self.settings.get_setting("TimeStampGameLog") == '1': item.Check(True)
-        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Window', wndmenu )
-        item = wx.MenuItem(tabmenu, wx.ID_ANY, "Tabbed Whispers", "Tabbed Whispers", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_TabbedWhispers, item)
-        tabmenu.AppendItem(item)
-        if self.settings.get_setting("tabbedwhispers") == '1': item.Check(True)
-        item = wx.MenuItem(tabmenu, wx.ID_ANY, "GM Tab", "GM Tab", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_GMTab, item)
-        tabmenu.AppendItem(item)
-        if self.settings.get_setting("GMWhisperTab") == '1':item.Check(True)
-        item = wx.MenuItem(tabmenu, wx.ID_ANY, "Group Whisper Tabs", "Group Whisper Tabs", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_GroupWhisperTabs, item)
-        tabmenu.AppendItem(item)
-        if self.settings.get_setting("GroupWhisperTab") == '1': item.Check(True)
-        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tabs', tabmenu)
-        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Dice Bar", "Dice Bar", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_DiceBar, item)
-        toolmenu.AppendItem(item)
-        if self.settings.get_setting("DiceButtons_On") == '1': item.Check(True)
-        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Format Buttons", "Format Buttons", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_FormatButtons, item)
-        toolmenu.AppendItem(item)
-        if self.settings.get_setting("FormattingButtons_On") == '1': item.Check(True)
-        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Alias Tool", "Alias Tool", wx.ITEM_CHECK)
-        top_frame.Bind(wx.EVT_MENU, self.OnMB_AliasTool, item)
-        toolmenu.AppendItem(item)
-        if self.settings.get_setting("AliasTool_On") == '1': item.Check(True)
-        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tool Bars', toolmenu)
-        menu.AppendMenu(wx.ID_ANY, 'Chat Settings', settingmenu)
-        top_frame.mainmenu.Insert(2, menu, '&Chat')
-
-    ## Settings Menu Events
-    def OnMB_ShowImages(self, event):
-        if event.IsChecked(): self.settings.set_setting("Show_Images_In_Chat", '1')
-        else: self.settings.set_setting("Show_Images_In_Chat", '0')
-
-    def OnMB_StripHTML(self, event):
-        if event.IsChecked(): self.settings.set_setting("striphtml", '1')
-        else: self.settings.set_setting("striphtml", '0')
-
-    def OnMB_ChatTimeIndex(self, event):
-        if event.IsChecked(): self.settings.set_setting("Chat_Time_Indexing", '1')
-        else: self.settings.set_setting("Chat_Time_Indexing", '0')
-
-    def OnMB_ChatAutoComplete(self, event):
-        if event.IsChecked(): self.settings.set_setting("SuppressChatAutoComplete", '0')
-        else: self.settings.set_setting("SuppressChatAutoComplete", '1')
-
-    def OnMB_ShowIDinChat(self, event):
-        if event.IsChecked(): self.settings.set_setting("ShowIDInChat", '1')
-        else: self.settings.set_setting("ShowIDInChat", '0')
-
-    def OnMB_LogTimeIndex(self, event):
-        if event.IsChecked(): self.settings.set_setting("TimeStampGameLog", '1')
-        else: self.settings.set_setting("TimeStampGameLog", '0')
-
-    def OnMB_TabbedWhispers(self, event):
-        if event.IsChecked(): self.settings.set_setting("tabbedwhispers", '1')
-        else: self.settings.set_setting("tabbedwhispers", '0')
-
-    def OnMB_GMTab(self, event):
-        if event.IsChecked():
-            self.settings.set_setting("GMWhisperTab", '1')
-            self.parent.create_gm_tab()
-        else: self.settings.set_setting("GMWhisperTab", '0')
-
-    def OnMB_GroupWhisperTabs(self, event):
-        if event.IsChecked(): self.settings.set_setting("GroupWhisperTab", '1')
-        else: self.settings.set_setting("GroupWhisperTab", '0')
-
-    def OnMB_DiceBar(self, event):
-        act = '0'
-        if event.IsChecked():
-            self.settings.set_setting("DiceButtons_On", '1')
-            act = '1'
-        else: self.settings.set_setting("DiceButtons_On", '0')
-        self.toggle_dice(act)
-        try: self.parent.GMChatPanel.toggle_dice(act)
-        except: pass
-        for panel in self.parent.whisper_tabs: panel.toggle_dice(act)
-        for panel in self.parent.group_tabs: panel.toggle_dice(act)
-        for panel in self.parent.null_tabs: panel.toggle_dice(act)
-
-    def OnMB_FormatButtons(self, event):
-        act = '0'
-        if event.IsChecked():
-            self.settings.set_setting("FormattingButtons_On", '1')
-            act = '1'
-        else:
-            self.settings.set_setting("FormattingButtons_On", '0')
-        self.toggle_formating(act)
-        try: self.parent.GMChatPanel.toggle_formating(act)
-        except: pass
-        for panel in self.parent.whisper_tabs: panel.toggle_formating(act)
-        for panel in self.parent.group_tabs: panel.toggle_formating(act)
-        for panel in self.parent.null_tabs: panel.toggle_formating(act)
-
-    def OnMB_AliasTool(self, event):
-        act = '0'
-        if event.IsChecked():
-            self.settings.set_setting("AliasTool_On", '1')
-            act = '1'
-        else: self.settings.set_setting("AliasTool_On", '0')
-        self.toggle_alias(act)
-        try: self.parent.GMChatPanel.toggle_alias(act)
-        except: pass
-        for panel in self.parent.whisper_tabs: panel.toggle_alias(act)
-        for panel in self.parent.group_tabs: panel.toggle_alias(act)
-        for panel in self.parent.null_tabs:panel.toggle_alias(act)
-
-    def OnMB_BackgroundColor(self, event):
-        top_frame = component.get('frame')
-        hexcolor = self.get_color()
-        if hexcolor != None:
-            self.bgcolor = hexcolor
-            self.settings.set_setting('bgcolor', hexcolor)
-            self.chatwnd.SetPage(self.ResetPage())
-            if self.settings.get_setting('ColorTree') == '1':
-                top_frame.tree.SetBackgroundColour(self.settings.get_setting('bgcolor'))
-                top_frame.tree.Refresh()
-                top_frame.players.SetBackgroundColour(self.settings.get_setting('bgcolor'))
-                top_frame.players.Refresh()
-            else:
-                top_frame.tree.SetBackgroundColour('white')
-                top_frame.tree.SetForegroundColour('black')
-                top_frame.tree.Refresh()
-                top_frame.players.SetBackgroundColour('white')
-                top_frame.players.SetForegroundColour('black')
-                top_frame.players.Refresh()
-            self.chatwnd.scroll_down()
-
-    
-    def OnMB_TextColor(self, event):
-        top_frame = component.get('frame')
-        hexcolor = self.get_color()
-        if hexcolor != None:
-            self.textcolor = hexcolor
-            self.settings.set_setting('textcolor', hexcolor)
-            self.chatwnd.SetPage(self.ResetPage())
-            if self.settings.get_setting('ColorTree') == '1':
-                top_frame.tree.SetForegroundColour(self.settings.get_setting('textcolor'))
-                top_frame.tree.Refresh()
-                top_frame.players.SetForegroundColour(self.settings.get_setting('textcolor'))
-                top_frame.players.Refresh()
-            else:
-                top_frame.tree.SetBackgroundColour('white')
-                top_frame.tree.SetForegroundColour('black')
-                top_frame.tree.Refresh()
-                top_frame.players.SetBackgroundColour('white')
-                top_frame.players.SetForegroundColour('black')
-                top_frame.players.Refresh()
-            self.chatwnd.scroll_down()
-
-    
-    def get_hot_keys(self):
-        # dummy menus for hotkeys
-        self.build_menu()
-        entries = []
-        entries.append((wx.ACCEL_CTRL, ord('H'), self.setChatFocusMenu.GetId()))
-        #entries.append((wx.ACCEL_CTRL, wx.WXK_TAB, SWAP_TABS))
-        return entries
-
-    
-    def forward_tabs(self, evt):
-        self.parent.AdvanceSelection()
-
-    def back_tabs(self, evt):
-        self.parent.AdvanceSelection(False)
-
-    # This subroutine builds the controls for the chat frame
-    #
-    # !self : instance of self
-    
-    def build_ctrls(self):
-        self.chatwnd = chat_html_window(self,-1)
-        self.set_colors()
-        wx.CallAfter(self.chatwnd.SetPage, self.chatwnd.Header())
-        if (self.sendtarget == "all"):
-            wx.CallAfter(self.Post, self.colorize(self.syscolor, 
-                "<b>Welcome to <a href='http://www.openrpg.com'>OpenRPG</a> version " + self.version + "...  </b>"))
-            #self.chat_cmds.on_help()
-        self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "", 
-                        style=wx.TE_PROCESS_ENTER |wx.TE_PROCESS_TAB|wx.TE_LINEWRAP, 
-                        keyHook = self.myKeyHook, validator=None )
-        self.build_bar()
-        self.basesizer = wx.BoxSizer(wx.VERTICAL)
-        self.basesizer.Add( self.chatwnd, 1, wx.EXPAND )
-        self.basesizer.Add( self.toolbar_sizer, 0, wx.EXPAND )
+            self.SetPageSource(self.GetPageSource() + htmlstring)
+
+        def GetFont(self):
+            return self.__font
+
+        def CalculateAllFonts(self, defaultsize):
+            return
+
+        def SetDefaultFontAndSize(self, fontname, fontsize):
+            self.__font = wx.Font(int(fontsize), 
+                            wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, 
+                            wx.FONTWEIGHT_NORMAL, faceName=fontname)
+            try: self.SetPageSource(self.Header() + self.StripHeader())
+            except Exception, e: print e
+            return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
+
+        #Events
+        def OnLinkClicked(self, linkinfo):
+            href = linkinfo.GetHref()
+            wb = webbrowser.get()
+            wb.open(href)
+
+        def onPopup(self, evt):
+            self.PopupMenu(self.menu)
+
+        def LeftUp(self, event):
+            event.Skip()
+            wx.CallAfter(self.parent.set_chat_text_focus, None)
+
+        def OnM_EditCopy(self, evt):
+            wx.TheClipboard.UsePrimarySelection(False)
+            wx.TheClipboard.Open()
+            wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
+            wx.TheClipboard.Close()
+
+        #Cutom Methods
+        def Header(self):
+            return "<html><head><style>body {font-size: " + str(self.GetFont().GetPointSize()) + "px;font-family: " + self.GetFont().GetFaceName() + ";color: " + self.parent.textcolor + ";background-color: " + self.parent.bgcolor + ";margin: 0;padding: 0 0;height: 100%;}</style></head><body>"
+
+        def StripHeader(self):
+            tmp = self.GetPageSource().split('<BODY>')
+            if tmp[-1].find('<body>') > -1: tmp = tmp[-1].split('<body>')
+            return tmp[-1]
+
+        def build_menu(self):
+            self.menu = wx.Menu()
+            item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
+            self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
+            self.menu.AppendItem(item)
+
+        def scroll_down(self):
+            maxrange = self.GetScrollRange(wx.VERTICAL)
+            pagesize = self.GetScrollPageSize(wx.VERTICAL)
+            self.Scroll(-1, maxrange-pagesize)
+
+        def mouse_wheel(self, event):
+            amt = event.GetWheelRotation()
+            units = amt/(-(event.GetWheelDelta()))
+            self.ScrollLines(units*3)
+    chat_html_window = ChatHtmlWindow
+
+#########################
+#chat frame window
+#########################
+# These are kinda global...and static..and should be located somewhere else
+# then the middle of a file between two classes.
+
+###################
+# Tab Types
+###################
+MAIN_TAB = wx.NewId()
+WHISPER_TAB = wx.NewId()
+GROUP_TAB = wx.NewId()
+NULL_TAB = wx.NewId()
+
+# This class defines the tabbed 'notebook' that holds multiple chatpanels.
+# It's the widget attached to the main application frame.
+#
+# Inherits:  wxNotebook
+#
+# Defines:
+#   create_private_tab(self, playerid)
+#   get_tab_index(self, chatpanel)
+#   destroy_private_tab(self, chatpanel)
+#   OnPageChanged(self, event)
+#   set_default_font(self, font, fontsize)
+
+class chat_notebook(orpgTabberWnd):
+    
+    def __init__(self, parent, size):
+        orpgTabberWnd.__init__(self, parent, True, size=size, 
+                style=FNB.FNB_DROPDOWN_TABS_LIST|FNB.FNB_NO_NAV_BUTTONS|FNB.FNB_MOUSE_MIDDLE_CLOSES_TABS)
+        self.settings = component.get("settings")
+        self.whisper_tabs = []
+        self.group_tabs = []
+        self.null_tabs = []
+        self.il = wx.ImageList(16, 16)
+        bmp = wx.Bitmap(dir_struct["icon"]+'player.gif')
+        self.il.Add(bmp)
+        bmp = wx.Bitmap(dir_struct["icon"]+'clear.gif')
+        self.il.Add(bmp)
+        self.SetImageList(self.il)
+        # Create "main" chatpanel tab, undeletable, connected to 'public' room.
+        self.MainChatPanel = chat_panel(self, -1, MAIN_TAB, 'all')
+        self.AddPage(self.MainChatPanel, "Main Room")
+        self.SetPageImage(0, 1)
+        self.chat_timer = wx.Timer(self, wx.NewId())
+        self.Bind(wx.EVT_TIMER, self.MainChatPanel.typingTimerFunc)
+        self.chat_timer.Start(1000)
+        # Hook up event handler for flipping tabs
+        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.onPageChanged)
+        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.onPageChanging)
+        self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.onCloseTab)
+        # html font/fontsize is global to all the notebook tabs.
+        self.font, self.fontsize =  self.MainChatPanel.chatwnd.SetDefaultFontAndSize(self.settings.get_setting('defaultfont'), self.settings.get_setting('defaultfontsize'))
+        self.GMChatPanel = None
+        if self.settings.get_setting("GMWhisperTab") == '1':
+            self.create_gm_tab()
+        self.SetSelection(0)
+
+    def get_tab_index(self, chatpanel):
+        "Return the index of a chatpanel in the wxNotebook."
+
+        for i in xrange(self.GetPageCount()):
+            if (self.GetPage(i) == chatpanel):
+                return i
+
+    def create_gm_tab(self):
+        if self.GMChatPanel == None:
+            self.GMChatPanel = chat_panel(self, -1, MAIN_TAB, 'gm')
+            self.AddPage(self.GMChatPanel, "GM", False)
+            self.SetPageImage(self.GetPageCount()-1, 1)
+            self.GMChatPanel.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
+
+    def create_whisper_tab(self, playerid):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        private_tab = chat_panel(self, -1, WHISPER_TAB, playerid)
+        playername = strip_html(self.MainChatPanel.session.get_player_by_player_id(playerid)[0])
+        self.AddPage(private_tab, playername, False)
+        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
+        self.whisper_tabs.append(private_tab)
+        self.newMsg(self.GetPageCount()-1)
+        self.AliasLib = component.get('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        return private_tab
+
+    def create_group_tab(self, group_name):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        private_tab = chat_panel(self, -1, GROUP_TAB, group_name)
+        self.AddPage(private_tab, group_name, False)
+        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
+        self.group_tabs.append(private_tab)
+        self.newMsg(self.GetPageCount()-1)
+        self.AliasLib = component.get('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        return private_tab
+
+    def create_null_tab(self, tab_name):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        private_tab = chat_panel(self, -1, NULL_TAB, tab_name)
+        self.AddPage(private_tab, tab_name, False)
+        private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
+        self.null_tabs.append(private_tab)
+        self.newMsg(self.GetPageCount()-1)
+        self.AliasLib = component.get('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        return private_tab
+
+    def onCloseTab(self, evt):
+        try: tabid = evt.GetSelection()
+        except: tabid = self.GetSelection()
+        if self.GetPageText(tabid) == 'Main Room':
+            #send no close error to chat
+            evt.Veto()
+            return
+        if self.GetPageText(tabid) == 'GM':
+            msg = "Are You Sure You Want To Close This Page?"
+            dlg = wx.MessageDialog(self, msg, "NotebookCtrl Question",
+                                   wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+            if wx.Platform != '__WXMAC__':
+                dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False))
+
+            if dlg.ShowModal() in [wx.ID_NO]:
+                dlg.Destroy()
+                evt.Veto()
+                return
+            dlg.Destroy()
+            self.GMChatPanel = None
+            self.settings.set_setting("GMWhisperTab", "0")
+        panel = self.GetPage(tabid)
+        if panel in self.whisper_tabs: self.whisper_tabs.remove(panel)
+        elif panel in self.group_tabs: self.group_tabs.remove(panel)
+        elif panel in self.null_tabs: self.null_tabs.remove(panel)
+
+    def newMsg(self, tabid):
+        if tabid != self.GetSelection(): self.SetPageImage(tabid, 0)
+
+    def onPageChanging(self, event):
+        """When private chattabs are selected, set the bitmap back to 'normal'."""
+        event.Skip()
+
+    def onPageChanged(self, event):
+        """When private chattabs are selected, set the bitmap back to 'normal'."""
+        selected_idx = event.GetSelection()
+        self.SetPageImage(selected_idx, 1)
+        page = self.GetPage(selected_idx)
+        #wx.CallAfter(page.set_chat_text_focus, 0)
+        event.Skip()
+
+"""
+ This class defines and builds the Chat Frame for OpenRPG
+
+ Inherits: wxPanel
+
+ Defines:
+   __init__((self, parent, id, openrpg, sendtarget)
+   build_ctrls(self)
+   on_buffer_size(self,evt)
+   set_colors(self)
+   set_buffersize(self)
+   set_chat_text(self,txt)
+   OnChar(self,event)
+   on_chat_save(self,evt)
+   on_text_color(self,event)
+   colorize(self, color, text)
+   on_text_format(self,event)
+   OnSize(self,event)
+   scroll_down(self)
+   InfoPost(self,s)
+   Post(self,s="",send=False,myself=False)
+   ParsePost(self,s,send=False,myself=False)
+   ParseDice(self,s)
+   ParseNodes(self,s)
+   get_sha_checksum(self)
+   get_color(self)
+
+"""
+
+class chat_panel(wx.Panel):
+
+    """
+    This is the initialization subroutine
+    
+    !self : instance of self
+    !parent : parent that defines the chatframe
+    !id :
+    !openrpg :
+    !sendtarget:  who gets outbound messages: either 'all' or a playerid
+    """
+
+    
+    def __init__(self, parent, id, tab_type, sendtarget):
+        wx.Panel.__init__(self, parent, id)
+        logger._set_log_to_console(False)
+        self.session = component.get('session')
+        self.settings = component.get('settings')
+        self.activeplugins = component.get('plugins')
+        self.parent = parent
+        # who receives outbound messages, either "all" or "playerid" string
+        self.sendtarget = sendtarget
+        self.type = tab_type
+        #self.sound_player = component.get('sound') #Removing!
+        # create die roller manager
+        #self.DiceManager = component.get('DiceManager') #Removing!
+        # create rpghex tool
+        self.r_h = orpg.tools.rgbhex.RGBHex()
+        self.h = 0
+        self.set_colors()
+        self.version = VERSION
+        self.histidx = -1
+        self.temptext = ""
+        self.history = []
+        self.storedata = []
+        #self.lasthistevt = None
+        self.parsed=0
+        #chat commands
+        self.lockscroll = False      # set the default to scrolling on.
+        self.chat_cmds = commands.chat_commands(self)
+        self.html_strip = strip_html
+        #Alias Lib stuff
+        self.defaultAliasName = 'Use Real Name'
+        self.defaultFilterName = 'No Filter'
+        self.advancedFilter = False
+        self.lastSend = 0         #  this is used to help implement the player typing indicator
+        self.lastPress = 0        #  this is used to help implement the player typing indicator
+        self.Bind(wx.EVT_SIZE, self.OnSize)
+        self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnSize) #require to keep text at bottom of chat when text entry expands --SD
+        self.build_ctrls()
+        StartupFont = self.settings.get_setting("defaultfont")
+        StartupFontSize = self.settings.get_setting("defaultfontsize")
+        if(StartupFont != "") and (StartupFontSize != ""):
+            try: self.set_default_font(StartupFont, int(StartupFontSize))
+            except: pass
+        self.font = self.chatwnd.GetFont().GetFaceName()
+        self.fontsize = self.chatwnd.GetFont().GetPointSize()
+        self.scroll_down()
+
+    def set_default_font(self, fontname=None, fontsize=None):
+        """Set all chatpanels to new default fontname/fontsize. 
+        Returns current font settings in a (fontname, fontsize) tuple."""
+        if (fontname is not None): newfont = fontname
+        else: newfont = self.font
+        if (fontsize is not None): newfontsize = int(fontsize)
+        else: newfontsize = int(self.fontsize)
+        self.chatwnd.SetDefaultFontAndSize(newfont, newfontsize)
+        self.InfoPost("Font is now " + newfont + " point size " + `newfontsize`)
+        self.font = newfont
+        self.fontsize = newfontsize
+        return (self.font, self.fontsize)
+
+    def build_menu(self):
+        top_frame = component.get('frame')
+        menu = wx.Menu()
+        item = wx.MenuItem(menu, wx.ID_ANY, "&Background color", "Background color")
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_BackgroundColor, item)
+        menu.AppendItem(item)
+        item = wx.MenuItem(menu, wx.ID_ANY, "&Text color", "Text color")
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_TextColor, item)
+        menu.AppendItem(item)
+        menu.AppendSeparator()
+        item = wx.MenuItem(menu, wx.ID_ANY, "&Chat Focus\tCtrl-H", "Chat Focus")
+        self.setChatFocusMenu = item
+        top_frame.Bind(wx.EVT_MENU, self.set_chat_text_focus, item)
+        menu.AppendItem(item)
+        menu.AppendSeparator()
+        item = wx.MenuItem(menu, wx.ID_ANY, "Toggle &Scroll Lock", "Toggle Scroll Lock")
+        top_frame.Bind(wx.EVT_MENU, self.lock_scroll, item)
+        menu.AppendItem(item)
+        item = wx.MenuItem(menu, wx.ID_ANY, "Save Chat &Log", "Save Chat Log")
+        top_frame.Bind(wx.EVT_MENU, self.on_chat_save, item)
+        menu.AppendItem(item)
+        item = wx.MenuItem(menu, wx.ID_ANY, "Text &View", "Text View")
+        top_frame.Bind(wx.EVT_MENU, self.pop_textpop, item)
+        menu.AppendItem(item)
+        item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Tab", "Swap Tabs")
+        top_frame.Bind(wx.EVT_MENU, self.forward_tabs, item)
+        menu.AppendItem(item)
+        item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Shift+Tab", "Swap Tabs")
+        top_frame.Bind(wx.EVT_MENU, self.back_tabs, item)
+        menu.AppendItem(item)
+        menu.AppendSeparator()
+        settingmenu = wx.Menu()
+        wndmenu = wx.Menu()
+        tabmenu = wx.Menu()
+        toolmenu = wx.Menu()
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show Images", "Show Images", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowImages, item)
+
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("Show_Images_In_Chat") == '1': item.Check(True)
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Strip HTML", "Strip HTML", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_StripHTML, item)
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("striphtml") == '1': item.Check(True)
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Time Index", "Chat Time Index", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatTimeIndex, item)
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("Chat_Time_Indexing") == '1': item.Check(True)
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Auto Complete", "Chat Auto Complete", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatAutoComplete, item)
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("SuppressChatAutoComplete") == '0': item.Check(True)
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show ID in Chat", "Show ID in Chat", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowIDinChat, item)
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("ShowIDInChat") == '1': item.Check(True)
+        item = wx.MenuItem(wndmenu, wx.ID_ANY, "Log Time Index", "Log Time Index", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_LogTimeIndex, item)
+        wndmenu.AppendItem(item)
+        if self.settings.get_setting("TimeStampGameLog") == '1': item.Check(True)
+        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Window', wndmenu )
+        item = wx.MenuItem(tabmenu, wx.ID_ANY, "Tabbed Whispers", "Tabbed Whispers", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_TabbedWhispers, item)
+        tabmenu.AppendItem(item)
+        if self.settings.get_setting("tabbedwhispers") == '1': item.Check(True)
+        item = wx.MenuItem(tabmenu, wx.ID_ANY, "GM Tab", "GM Tab", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_GMTab, item)
+        tabmenu.AppendItem(item)
+        if self.settings.get_setting("GMWhisperTab") == '1':item.Check(True)
+        item = wx.MenuItem(tabmenu, wx.ID_ANY, "Group Whisper Tabs", "Group Whisper Tabs", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_GroupWhisperTabs, item)
+        tabmenu.AppendItem(item)
+        if self.settings.get_setting("GroupWhisperTab") == '1': item.Check(True)
+        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tabs', tabmenu)
+        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Dice Bar", "Dice Bar", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_DiceBar, item)
+        toolmenu.AppendItem(item)
+        if self.settings.get_setting("DiceButtons_On") == '1': item.Check(True)
+        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Format Buttons", "Format Buttons", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_FormatButtons, item)
+        toolmenu.AppendItem(item)
+        if self.settings.get_setting("FormattingButtons_On") == '1': item.Check(True)
+        item = wx.MenuItem(toolmenu, wx.ID_ANY, "Alias Tool", "Alias Tool", wx.ITEM_CHECK)
+        top_frame.Bind(wx.EVT_MENU, self.OnMB_AliasTool, item)
+        toolmenu.AppendItem(item)
+        if self.settings.get_setting("AliasTool_On") == '1': item.Check(True)
+        settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tool Bars', toolmenu)
+        menu.AppendMenu(wx.ID_ANY, 'Chat Settings', settingmenu)
+        top_frame.mainmenu.Insert(2, menu, '&Chat')
+
+    ## Settings Menu Events
+    def OnMB_ShowImages(self, event):
+        if event.IsChecked(): self.settings.set_setting("Show_Images_In_Chat", '1')
+        else: self.settings.set_setting("Show_Images_In_Chat", '0')
+
+    def OnMB_StripHTML(self, event):
+        if event.IsChecked(): self.settings.set_setting("striphtml", '1')
+        else: self.settings.set_setting("striphtml", '0')
+
+    def OnMB_ChatTimeIndex(self, event):
+        if event.IsChecked(): self.settings.set_setting("Chat_Time_Indexing", '1')
+        else: self.settings.set_setting("Chat_Time_Indexing", '0')
+
+    def OnMB_ChatAutoComplete(self, event):
+        if event.IsChecked(): self.settings.set_setting("SuppressChatAutoComplete", '0')
+        else: self.settings.set_setting("SuppressChatAutoComplete", '1')
+
+    def OnMB_ShowIDinChat(self, event):
+        if event.IsChecked(): self.settings.set_setting("ShowIDInChat", '1')
+        else: self.settings.set_setting("ShowIDInChat", '0')
+
+    def OnMB_LogTimeIndex(self, event):
+        if event.IsChecked(): self.settings.set_setting("TimeStampGameLog", '1')
+        else: self.settings.set_setting("TimeStampGameLog", '0')
+
+    def OnMB_TabbedWhispers(self, event):
+        if event.IsChecked(): self.settings.set_setting("tabbedwhispers", '1')
+        else: self.settings.set_setting("tabbedwhispers", '0')
+
+    def OnMB_GMTab(self, event):
+        if event.IsChecked():
+            self.settings.set_setting("GMWhisperTab", '1')
+            self.parent.create_gm_tab()
+        else: self.settings.set_setting("GMWhisperTab", '0')
+
+    def OnMB_GroupWhisperTabs(self, event):
+        if event.IsChecked(): self.settings.set_setting("GroupWhisperTab", '1')
+        else: self.settings.set_setting("GroupWhisperTab", '0')
+
+    def OnMB_DiceBar(self, event):
+        act = '0'
+        if event.IsChecked():
+            self.settings.set_setting("DiceButtons_On", '1')
+            act = '1'
+        else: self.settings.set_setting("DiceButtons_On", '0')
+        self.toggle_dice(act)
+        try: self.parent.GMChatPanel.toggle_dice(act)
+        except: pass
+        for panel in self.parent.whisper_tabs: panel.toggle_dice(act)
+        for panel in self.parent.group_tabs: panel.toggle_dice(act)
+        for panel in self.parent.null_tabs: panel.toggle_dice(act)
+
+    def OnMB_FormatButtons(self, event):
+        act = '0'
+        if event.IsChecked():
+            self.settings.set_setting("FormattingButtons_On", '1')
+            act = '1'
+        else:
+            self.settings.set_setting("FormattingButtons_On", '0')
+        self.toggle_formating(act)
+        try: self.parent.GMChatPanel.toggle_formating(act)
+        except: pass
+        for panel in self.parent.whisper_tabs: panel.toggle_formating(act)
+        for panel in self.parent.group_tabs: panel.toggle_formating(act)
+        for panel in self.parent.null_tabs: panel.toggle_formating(act)
+
+    def OnMB_AliasTool(self, event):
+        act = '0'
+        if event.IsChecked():
+            self.settings.set_setting("AliasTool_On", '1')
+            act = '1'
+        else: self.settings.set_setting("AliasTool_On", '0')
+        self.toggle_alias(act)
+        try: self.parent.GMChatPanel.toggle_alias(act)
+        except: pass
+        for panel in self.parent.whisper_tabs: panel.toggle_alias(act)
+        for panel in self.parent.group_tabs: panel.toggle_alias(act)
+        for panel in self.parent.null_tabs:panel.toggle_alias(act)
+
+    def OnMB_BackgroundColor(self, event):
+        top_frame = component.get('frame')
+        hexcolor = self.get_color()
+        if hexcolor != None:
+            self.bgcolor = hexcolor
+            self.settings.set_setting('bgcolor', hexcolor)
+            self.chatwnd.SetPage(self.ResetPage())
+            if self.settings.get_setting('ColorTree') == '1':
+                top_frame.tree.SetBackgroundColour(self.settings.get_setting('bgcolor'))
+                top_frame.tree.Refresh()
+                top_frame.players.SetBackgroundColour(self.settings.get_setting('bgcolor'))
+                top_frame.players.Refresh()
+            else:
+                top_frame.tree.SetBackgroundColour('white')
+                top_frame.tree.SetForegroundColour('black')
+                top_frame.tree.Refresh()
+                top_frame.players.SetBackgroundColour('white')
+                top_frame.players.SetForegroundColour('black')
+                top_frame.players.Refresh()
+            self.chatwnd.scroll_down()
+
+    
+    def OnMB_TextColor(self, event):
+        top_frame = component.get('frame')
+        hexcolor = self.get_color()
+        if hexcolor != None:
+            self.textcolor = hexcolor
+            self.settings.set_setting('textcolor', hexcolor)
+            self.chatwnd.SetPage(self.ResetPage())
+            if self.settings.get_setting('ColorTree') == '1':
+                top_frame.tree.SetForegroundColour(self.settings.get_setting('textcolor'))
+                top_frame.tree.Refresh()
+                top_frame.players.SetForegroundColour(self.settings.get_setting('textcolor'))
+                top_frame.players.Refresh()
+            else:
+                top_frame.tree.SetBackgroundColour('white')
+                top_frame.tree.SetForegroundColour('black')
+                top_frame.tree.Refresh()
+                top_frame.players.SetBackgroundColour('white')
+                top_frame.players.SetForegroundColour('black')
+                top_frame.players.Refresh()
+            self.chatwnd.scroll_down()
+
+    
+    def get_hot_keys(self):
+        # dummy menus for hotkeys
+        self.build_menu()
+        entries = []
+        entries.append((wx.ACCEL_CTRL, ord('H'), self.setChatFocusMenu.GetId()))
+        #entries.append((wx.ACCEL_CTRL, wx.WXK_TAB, SWAP_TABS))
+        return entries
+
+    
+    def forward_tabs(self, evt):
+        self.parent.AdvanceSelection()
+
+    def back_tabs(self, evt):
+        self.parent.AdvanceSelection(False)
+
+    # This subroutine builds the controls for the chat frame
+    #
+    # !self : instance of self
+    
+    def build_ctrls(self):
+        self.chatwnd = chat_html_window(self,-1)
+        self.set_colors()
+        wx.CallAfter(self.chatwnd.SetPage, self.chatwnd.Header())
+        if (self.sendtarget == "all"):
+            wx.CallAfter(self.Post, self.colorize(self.syscolor, 
+                "<b>Welcome to <a href='http://www.openrpg.com'>OpenRPG</a> version " + self.version + "...  </b>"))
+            #self.chat_cmds.on_help()
+        self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "", 
+                        style=wx.TE_PROCESS_ENTER |wx.TE_PROCESS_TAB|wx.TE_LINEWRAP, 
+                        keyHook = self.myKeyHook, validator=None )
+        self.build_bar()
+        self.basesizer = wx.BoxSizer(wx.VERTICAL)
+        self.basesizer.Add( self.chatwnd, 1, wx.EXPAND )
+        self.basesizer.Add( self.toolbar_sizer, 0, wx.EXPAND )
         self.basesizer.Add( self.chattxt, 0, wx.EXPAND )
-        self.SetSizer(self.basesizer)
-        self.SetAutoLayout(True)
-        self.Fit()
-        #events
-        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.boldButton)
-        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.italicButton)
-        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.underlineButton)
-        self.Bind(wx.EVT_BUTTON, self.on_text_color, self.color_button)
-        self.Bind(wx.EVT_BUTTON, self.on_chat_save, self.saveButton)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d4Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d6Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d8Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d10Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d12Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d20Button)
-        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d100Button)
-        self.dieIDs = {}
-        self.dieIDs[self.d4Button.GetId()] = 'd4'
-        self.dieIDs[self.d6Button.GetId()] = 'd6'
-        self.dieIDs[self.d8Button.GetId()] = 'd8'
-        self.dieIDs[self.d10Button.GetId()] = 'd10'
-        self.dieIDs[self.d12Button.GetId()] = 'd12'
-        self.dieIDs[self.d20Button.GetId()] = 'd20'
-        self.dieIDs[self.d100Button.GetId()] = 'd100'
-        self.Bind(wx.EVT_BUTTON, self.pop_textpop, self.textpop_lock)
-        self.Bind(wx.EVT_BUTTON, self.lock_scroll, self.scroll_lock)
-        self.chattxt.Bind(wx.EVT_MOUSEWHEEL, self.chatwnd.mouse_wheel)
-        self.chattxt.Bind(wx.EVT_CHAR, self.chattxt.OnChar)
-        self.chattxt.Bind(wx.EVT_TEXT_COPY, self.chatwnd.OnM_EditCopy)
-    # def build_ctrls - end
-
-    def build_bar(self):
-        self.toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.scroll_lock = None
-        self.numDieText = None
-        self.dieModText = None
-        if self.settings.get_setting('Toolbar_On') == "1":
-            self.build_alias()
-            self.build_dice()
-            self.build_scroll()
-            self.build_text()
-            self.toolbar_sizer.Add(self.textpop_lock, 0, wx.EXPAND)
-            self.toolbar_sizer.Add(self.scroll_lock, 0, wx.EXPAND)
-            self.build_formating()
-            self.build_colorbutton()
-
-    
-    def build_scroll(self):
-        self.scroll_lock = wx.Button( self, wx.ID_ANY, "Scroll ON",size= wx.Size(80,25))
-
-    
+        self.SetSizer(self.basesizer)
+        self.SetAutoLayout(True)
+        self.Fit()
+        #events
+        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.boldButton)
+        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.italicButton)
+        self.Bind(wx.EVT_BUTTON, self.on_text_format, self.underlineButton)
+        self.Bind(wx.EVT_BUTTON, self.on_text_color, self.color_button)
+        self.Bind(wx.EVT_BUTTON, self.on_chat_save, self.saveButton)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d4Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d6Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d8Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d10Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d12Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d20Button)
+        self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d100Button)
+        self.dieIDs = {}
+        self.dieIDs[self.d4Button.GetId()] = 'd4'
+        self.dieIDs[self.d6Button.GetId()] = 'd6'
+        self.dieIDs[self.d8Button.GetId()] = 'd8'
+        self.dieIDs[self.d10Button.GetId()] = 'd10'
+        self.dieIDs[self.d12Button.GetId()] = 'd12'
+        self.dieIDs[self.d20Button.GetId()] = 'd20'
+        self.dieIDs[self.d100Button.GetId()] = 'd100'
+        self.Bind(wx.EVT_BUTTON, self.pop_textpop, self.textpop_lock)
+        self.Bind(wx.EVT_BUTTON, self.lock_scroll, self.scroll_lock)
+        self.chattxt.Bind(wx.EVT_MOUSEWHEEL, self.chatwnd.mouse_wheel)
+        self.chattxt.Bind(wx.EVT_CHAR, self.chattxt.OnChar)
+        self.chattxt.Bind(wx.EVT_TEXT_COPY, self.chatwnd.OnM_EditCopy)
+    # def build_ctrls - end
+
+    def build_bar(self):
+        self.toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.scroll_lock = None
+        self.numDieText = None
+        self.dieModText = None
+        if self.settings.get_setting('Toolbar_On') == "1":
+            self.build_alias()
+            self.build_dice()
+            self.build_scroll()
+            self.build_text()
+            self.toolbar_sizer.Add(self.textpop_lock, 0, wx.EXPAND)
+            self.toolbar_sizer.Add(self.scroll_lock, 0, wx.EXPAND)
+            self.build_formating()
+            self.build_colorbutton()
+
+    
+    def build_scroll(self):
+        self.scroll_lock = wx.Button( self, wx.ID_ANY, "Scroll ON",size= wx.Size(80,25))
+
+    
     def build_alias(self):
-        self.aliasSizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName])
-        self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif', 
+        self.aliasSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName])
+        self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif', 
                                             'Refresh list of aliases from Game Tree', 
-                                            wx.ID_ANY, '#bdbdbd' )
-        self.aliasList.SetSelection(0)
-        self.filterList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultFilterName])
-        self.filterButton = createMaskedButton( self, dir_struct["icon"] + 'add_filter.gif', 
+                                            wx.ID_ANY, '#bdbdbd' )
+        self.aliasList.SetSelection(0)
+        self.filterList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultFilterName])
+        self.filterButton = createMaskedButton( self, dir_struct["icon"] + 'add_filter.gif', 
                                              'Refresh list of filters from Game Tree', 
-                                             wx.ID_ANY, '#bdbdbd' )
+                                             wx.ID_ANY, '#bdbdbd' )
         self.filterList.SetSelection(0)
 
-        self.aliasSizer.Add( self.aliasButton, 0, wx.EXPAND )
-        self.aliasSizer.Add( self.aliasList,0,wx.EXPAND)
-        self.aliasSizer.Add( self.filterButton, 0, wx.EXPAND )
+        self.aliasSizer.Add( self.aliasButton, 0, wx.EXPAND )
+        self.aliasSizer.Add( self.aliasList,0,wx.EXPAND)
+        self.aliasSizer.Add( self.filterButton, 0, wx.EXPAND )
         self.aliasSizer.Add( self.filterList,0,wx.EXPAND)
 
         self.toolbar_sizer.Add(self.aliasSizer, 0, wx.EXPAND)
 
-        if self.settings.get_setting('AliasTool_On') == '0': self.toggle_alias('0')
-        else: self.toggle_alias('1')
-    
+        if self.settings.get_setting('AliasTool_On') == '0': self.toggle_alias('0')
+        else: self.toggle_alias('1')
+    
     def toggle_alias(self, act):
         if act == '0': self.toolbar_sizer.Show(self.aliasSizer, False)
         else: self.toolbar_sizer.Show(self.aliasSizer, True)
-        self.toolbar_sizer.Layout()
-    
-    def build_text(self):
-        self.textpop_lock = createMaskedButton(self, dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', wx.ID_ANY, '#bdbdbd')
-
-    
+        self.toolbar_sizer.Layout()
+    
+    def build_text(self):
+        self.textpop_lock = createMaskedButton(self, dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', wx.ID_ANY, '#bdbdbd')
+
+    
     def build_dice(self):
-        self.diceSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.diceSizer = wx.BoxSizer(wx.HORIZONTAL)
         self.numDieText = wx.TextCtrl( self, wx.ID_ANY, "1", 
-                                    size= wx.Size(25, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
+                                    size= wx.Size(25, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
         self.dieModText = wx.TextCtrl( self, wx.ID_ANY, "", 
-                                    size= wx.Size(50, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
-        self.d4Button = createMaskedButton(self, dir_struct["icon"]+'b_d4.gif', 'Roll d4', wx.ID_ANY)
-        self.d6Button = createMaskedButton(self, dir_struct["icon"]+'b_d6.gif', 'Roll d6', wx.ID_ANY)
-        self.d8Button = createMaskedButton(self, dir_struct["icon"]+'b_d8.gif', 'Roll d8', wx.ID_ANY)
-        self.d10Button = createMaskedButton(self, dir_struct["icon"]+'b_d10.gif', 'Roll d10', wx.ID_ANY)
-        self.d12Button = createMaskedButton(self, dir_struct["icon"]+'b_d12.gif', 'Roll d12', wx.ID_ANY)
-        self.d20Button = createMaskedButton(self, dir_struct["icon"]+'b_d20.gif', 'Roll d20', wx.ID_ANY)
+                                    size= wx.Size(50, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
+        self.d4Button = createMaskedButton(self, dir_struct["icon"]+'b_d4.gif', 'Roll d4', wx.ID_ANY)
+        self.d6Button = createMaskedButton(self, dir_struct["icon"]+'b_d6.gif', 'Roll d6', wx.ID_ANY)
+        self.d8Button = createMaskedButton(self, dir_struct["icon"]+'b_d8.gif', 'Roll d8', wx.ID_ANY)
+        self.d10Button = createMaskedButton(self, dir_struct["icon"]+'b_d10.gif', 'Roll d10', wx.ID_ANY)
+        self.d12Button = createMaskedButton(self, dir_struct["icon"]+'b_d12.gif', 'Roll d12', wx.ID_ANY)
+        self.d20Button = createMaskedButton(self, dir_struct["icon"]+'b_d20.gif', 'Roll d20', wx.ID_ANY)
         self.d100Button = createMaskedButton(self, dir_struct["icon"]+'b_d100.gif', 'Roll d100', wx.ID_ANY)
-
-        self.diceSizer.Add( self.numDieText, 0, wx.ALIGN_CENTER | wx.EXPAND)
-        self.diceSizer.Add( self.d4Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d6Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d8Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d10Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d12Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d20Button, 0 ,wx.EXPAND)
-        self.diceSizer.Add( self.d100Button, 0 ,wx.EXPAND)
+
+        self.diceSizer.Add( self.numDieText, 0, wx.ALIGN_CENTER | wx.EXPAND)
+        self.diceSizer.Add( self.d4Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d6Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d8Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d10Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d12Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d20Button, 0 ,wx.EXPAND)
+        self.diceSizer.Add( self.d100Button, 0 ,wx.EXPAND)
         self.diceSizer.Add( self.dieModText, 0, wx.ALIGN_CENTER, 5 )
 
-        self.toolbar_sizer.Add( self.diceSizer, 0, wx.EXPAND)
-        if self.settings.get_setting('DiceButtons_On') == '0': self.toggle_dice('0')
-        else: self.toggle_dice('1')
-
-    
-    def toggle_dice(self, act):
+        self.toolbar_sizer.Add( self.diceSizer, 0, wx.EXPAND)
+        if self.settings.get_setting('DiceButtons_On') == '0': self.toggle_dice('0')
+        else: self.toggle_dice('1')
+
+    
+    def toggle_dice(self, act):
         if act == '0': self.toolbar_sizer.Show(self.diceSizer, False)
         else: self.toolbar_sizer.Show(self.diceSizer, True)
-        self.toolbar_sizer.Layout()
-
-    
+        self.toolbar_sizer.Layout()
+
+    
     def build_formating(self):
-        self.formatSizer = wx.BoxSizer(wx.HORIZONTAL)
-        self.boldButton = createMaskedButton( self, dir_struct["icon"]+'bold.gif', 
-                                                            'Make the selected text Bold', wx.ID_ANY, '#bdbdbd')
-        self.italicButton = createMaskedButton( self, dir_struct["icon"]+'italic.gif', 
-                                                            'Italicize the selected text', wx.ID_ANY, '#bdbdbd' )
-        self.underlineButton = createMaskedButton( self, dir_struct["icon"]+'underlined.gif', 
+        self.formatSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.boldButton = createMaskedButton( self, dir_struct["icon"]+'bold.gif', 
+                                                            'Make the selected text Bold', wx.ID_ANY, '#bdbdbd')
+        self.italicButton = createMaskedButton( self, dir_struct["icon"]+'italic.gif', 
+                                                            'Italicize the selected text', wx.ID_ANY, '#bdbdbd' )
+        self.underlineButton = createMaskedButton( self, dir_struct["icon"]+'underlined.gif', 
                                                             'Underline the selected text', wx.ID_ANY, '#bdbdbd' )
-
-        self.formatSizer.Add( self.boldButton, 0, wx.EXPAND )
-        self.formatSizer.Add( self.italicButton, 0, wx.EXPAND )
+
+        self.formatSizer.Add( self.boldButton, 0, wx.EXPAND )
+        self.formatSizer.Add( self.italicButton, 0, wx.EXPAND )
         self.formatSizer.Add( self.underlineButton, 0, wx.EXPAND )
-        self.toolbar_sizer.Add( self.formatSizer, 0, wx.EXPAND )
-        if self.settings.get_setting('FormattingButtons_On') == '0': self.toggle_formating('0')
-        else: self.toggle_formating('1')
-
-    
-    def toggle_formating(self, act):
+        self.toolbar_sizer.Add( self.formatSizer, 0, wx.EXPAND )
+        if self.settings.get_setting('FormattingButtons_On') == '0': self.toggle_formating('0')
+        else: self.toggle_formating('1')
+
+    
+    def toggle_formating(self, act):
         if act == '0': self.toolbar_sizer.Show(self.formatSizer, False)
         else: self.toolbar_sizer.Show(self.formatSizer, True)
-        self.toolbar_sizer.Layout()
-
-    # Heroman - Ideally, we would use static labels...
-    
-    def build_colorbutton(self):
-        self.color_button = createMaskedButton(self, dir_struct["icon"]+'textcolor.gif', 
-                                                    'Text Color', wx.ID_ANY, '#bdbdbd', 
-                                                    wx.BITMAP_TYPE_GIF)
-
-        self.saveButton = createMaskedButton(self, dir_struct["icon"]+'save.bmp', 
-                                                    'Save the chatbuffer', wx.ID_ANY, 
-                                                    '#c0c0c0', wx.BITMAP_TYPE_BMP )
-        self.color_button.SetBackgroundColour(self.settings.get_setting('mytextcolor'))
-        self.toolbar_sizer.Add(self.color_button, 0, wx.EXPAND)
-        self.toolbar_sizer.Add(self.saveButton, 0, wx.EXPAND)
-
-    
-    def OnMotion(self, evt):
-        contain = self.chatwnd.GetInternalRepresentation()
-        if contain:
-            sx = sy = 0
-            x = y = 0
-            (sx,sy) = self.chatwnd.GetViewStart()
-            (sx1,sy1) = self.chatwnd.GetScrollPixelsPerUnit()
-            sx = sx*sx1
-            sy = sy*sy1
-            (x,y) = evt.GetPosition()
-            lnk = contain.GetLink(sx+x,sy+y)
-            if lnk:
-                try:
-                    link = lnk.GetHref()
-                    self.session.set_status_url(link)
-                except: pass
-        else: logger.general("Error, self.chatwnd.GetInternalRepresentation() return None")
-        evt.Skip()
-
-    #  This subroutine is registered with predTextCtrl to be run for every OnChar event
-    #  It checks if we need to send a typing message
-
-    #
-    #  self:  duh
-    #  event:  raw KeyEvent from OnChar()
-    
-    def myKeyHook(self, event):
-        if self.session.get_status() == MPLAY_CONNECTED:   #  only do if we're connected
-            thisPress = time.time()                #  thisPress is local temp variable
-            if (thisPress - self.lastSend) > 4:    #  Check to see if it's been 5 seconds since our last notice
-                                                   #    If we're not already typing, then self.lastSend will be 0
-                self.sendTyping(1)                 #  send a not typing event here (1 for True)
-            self.lastPress = thisPress             #  either way, record the time of this keystroke for use in
-                                                   #  self.typingTimerFunc()
-        if self.settings.get_setting('SuppressChatAutoComplete') == '1':
-            logger.debug("Exit chat_panel->myKeyHook(self, event) return 1")
-            return 1
-        else:
-            logger.debug("Exit chat_panel->myKeyHook(self, event) return 0")
-            return 0
-
-    #  This subroutine gets called once a second by the typing Timer
-    #  It checks if we need to send a not_typing message
-    #
-    #  self:  duh
-    
-    def typingTimerFunc(self, event):
-        #following added by mDuo13
-        ##############refresh_counter()##############
-        for plugin_fname in self.activeplugins.keys():
-            plugin = self.activeplugins[plugin_fname]
-            try: plugin.refresh_counter()
-            except Exception, e:
-                if str(e) != "'module' object has no attribute 'refresh_counter'":
-                    logger.general(traceback.format_exc())
-                    logger.general("EXCEPTION: " + str(e))
-        #end mDuo13 added code
-        if self.lastSend:                          #  This will be zero when not typing, so equiv to if is_typing
-            thisTime = time.time()                 #  thisTime is a local temp variable
-            if (thisTime - self.lastPress) > 4:    #  Check to see if it's been 5 seconds since our last keystroke
-                                                   #  If we're not already typing, then self.lastSend will be 0
-
-                self.sendTyping(0)                 #  send a typing event here (0 for False)
-    #  This subroutine actually takes care of sending the messages for typing/not_typing events
-    #
-    #  self:  duh
-    #  typing:  boolean
-
-    
-    def sendTyping(self, typing):
-        if typing:
-            self.lastSend = time.time()  #  remember our send time for use in myKeyHook()
-            #I think this is cleaner
-            status_text = self.settings.get_setting('TypingStatusAlias')
-            if status_text == "" or status_text == None: status_text = "Typing"
-            self.session.set_text_status(status_text)
-        else:
-            self.lastSend = 0                            #  set lastSend to zero to indicate we're not typing
-            #I think this is cleaner
-            status_text = self.settings.get_setting('IdleStatusAlias')
-            if status_text == "" or status_text == None: status_text = "Idle"
-            self.session.set_text_status(status_text)
-
-    # This subroutine sets the colors of the chat based on the settings in the
-    # self instance.
-    #
-    # !self : instance of self
-    
-    def set_colors(self):
-        # chat window backround color
-        self.bgcolor = self.settings.get_setting('bgcolor')
-        # chat window normal text color
-        self.textcolor = self.settings.get_setting('textcolor')
-        # color of text player types
-        self.mytextcolor = self.settings.get_setting('mytextcolor')
-        # color of system warnings
-        self.syscolor = self.settings.get_setting('syscolor')
-        # color of system info messages
-        self.infocolor = self.settings.get_setting('infocolor')
-        # color of emotes
-        self.emotecolor = self.settings.get_setting('emotecolor')
-        # color of whispers
-        self.whispercolor = self.settings.get_setting('whispercolor')
-    # def set_colors - end
-
-    # This subroutine will insert text into the chat window
-    #
-    # !self : instance of self
-    # !txt : text to be inserted into the chat window
-    
-    def set_chat_text(self, txt):
-        self.chattxt.SetValue(txt)
-        self.chattxt.SetFocus()
-        self.chattxt.SetInsertionPointEnd()
-    # def set_chat_text - end
-
-    
-    def get_chat_text(self):
-        return self.chattxt.GetValue()
-
-    # This subroutine sets the focus to the chat window
-    
-    def set_chat_text_focus(self, event):
-        wx.CallAfter(self.chattxt.SetFocus)
-    # def set_chat_text_focus - end
-
-    # This subrtouine grabs the user input and make the special keys and
-    # modifiers work.
-    #
-    # !self : instance of self
-    # !event :
-    #
-    # Note:  self.chattxt now handles it's own Key events.  It does, however still
-    #        call it's parent's (self) OnChar to handle "default" behavior.
-    
-    def OnChar(self, event):
-        s = self.chattxt.GetValue()
-        #self.histlen = len(self.history) - 1
-
-        ## RETURN KEY (no matter if there is text in chattxt)
-        #  This section is run even if there is nothing in the chattxt (as opposed to the next wx.WXK_RETURN handler
-        if event.GetKeyCode() == wx.WXK_RETURN:
-            logger.debug("event.GetKeyCode() == wx.WXK_RETURN")
-            self.set_colors()
-            if self.session.get_status() == MPLAY_CONNECTED:          #  only do if we're connected
-                self.sendTyping(0)                                    #  Send a "not_typing" event on enter key press
-        macroText=""
-        recycle_bin = {wx.WXK_F1: 'event.GetKeyCode() == wx.WXK_F1', wx.WXK_F2: 'event.GetKeyCode() == wx.WXK_F2', 
-                    wx.WXK_F3: 'event.GetKeyCode() == wx.WXK_F3', wx.WXK_F4: 'event.GetKeyCode() == wx.WXK_F4', 
-                    wx.WXK_F5: 'event.GetKeyCode() == wx.WXK_F5', wx.WXK_F6: 'event.GetKeyCode() == wx.WXK_F6', 
-                    wx.WXK_F7: 'event.GetKeyCode() == wx.WXK_F7', wx.WXK_F8: 'event.GetKeyCode() == wx.WXK_F8', 
-                    wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10', 
-                    wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'}
-
-        bin_event = event.GetKeyCode()
-        if recycle_bin.has_key(bin_event):
-	    logger.debug(lambda bin_event: recycle_bin[bin_event])
-	    macroText = self.settings.get_setting(recycle_bin[bin_event][29:])
-	    recycle_bin = {}; del bin_event
-
-        # Append to the existing typed text as needed and make sure the status doesn't change back.
-        if len(macroText):
-            self.sendTyping(0)
-            s = macroText
-
-        ## RETURN KEY (and not text in control)
-        if (event.GetKeyCode() == wx.WXK_RETURN and len(s)) or len(macroText):
-            logger.debug("(event.GetKeyCode() == wx.WXK_RETURN and len(s)) or len(macroText)")
-            self.histidx = -1
-            self.temptext = ""
-            self.history = [s] + self.history#prepended instead of appended now, so higher index = greater age
-            if not len(macroText): self.chattxt.SetValue("")
-            # play sound
-            sound_file = self.settings.get_setting("SendSound")
-            if sound_file != '': component.get('sound').play(sound_file)
-            if s[0] != "/": ## it's not a slash command
-                s = self.ParsePost( s, True, True )
-            else: self.chat_cmds.docmd(s) # emote is in chatutils.py
-
-        ## UP KEY
-        elif event.GetKeyCode() == wx.WXK_UP:
-            logger.debug("event.GetKeyCode() == wx.WXK_UP")
-            if self.histidx < len(self.history)-1:
-                #text that's not in history but also hasn't been sent to chat gets stored in self.temptext
-                #this way if someone presses the up key, they don't lose their current message permanently
-                #(unless they also press enter at the time)
-                if self.histidx is -1: self.temptext = self.chattxt.GetValue()
-                self.histidx += 1
-                self.chattxt.SetValue(self.history[self.histidx])
-                self.chattxt.SetInsertionPointEnd()
-            else:
-                self.histidx = len(self.history) -1#in case it got too high somehow, this should fix it
-                #self.InfoPost("**Going up? I don't think so.**")
-            #print self.histidx, "in",self.history
-
-        ## DOWN KEY
-        elif event.GetKeyCode() == wx.WXK_DOWN:
-            logger.debug("event.GetKeyCode() == wx.WXK_DOWN")
-            #histidx of -1 indicates currently viewing text that's not in self.history
-            if self.histidx > -1:
-                self.histidx -= 1
-                if self.histidx is -1: #remember, it just decreased
-                    self.chattxt.SetValue(self.temptext)
-                else: self.chattxt.SetValue(self.history[self.histidx])
-                self.chattxt.SetInsertionPointEnd()
-            else: self.histidx = -1 #just in case it somehow got below -1, this should fix it
-                #self.InfoPost("**Going down? I don't think so.**")
-            #print self.histidx, "in",self.history
-
-        ## TAB KEY
-        elif  event.GetKeyCode() == wx.WXK_TAB:
-            logger.debug("event.GetKeyCode() == wx.WXK_TAB")
-            if s !="":
-                found = 0
-                nicks = []
-                testnick = ""
-                inlength = len(s)
-                for getnames in self.session.players.keys():
-                    striphtmltag = re.compile ('<[^>]+>*')
-                    testnick = striphtmltag.sub ("", self.session.players[getnames][0])
-                    if string.lower(s) == string.lower(testnick[:inlength]):
-                        found = found + 1
-                        nicks[len(nicks):]=[testnick]
-                if found == 0: ## no nick match
-                    self.Post(self.colorize(self.syscolor," ** No match found"))
-                elif found > 1: ## matched more than 1, tell user what matched
-                    nickstring = ""
-                    nicklist = []
-                    for foundnicks in nicks:
-                        nickstring = nickstring + foundnicks + ", "
-                        nicklist.append(foundnicks)
-                    nickstring = nickstring[:-2]
-                    self.Post(self.colorize(self.syscolor, " ** Multiple matches found: " + nickstring))
-                    # set text to the prefix match between first two matches
-                    settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(nicklist[0]))), string.lower(nicklist[1])).group()
-                    # run through the rest of the nicks
-                    for i in nicklist:
-                        settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(i))), string.lower(settext)).group()
-                    if settext:
-                        self.chattxt.SetValue(settext)
-                        self.chattxt.SetInsertionPointEnd()
-                else: ## put the matched name in the chattxt box
-                    settext = nicks[0] + ": "
-                    self.chattxt.SetValue(settext)
-                    self.chattxt.SetInsertionPointEnd()
-            else: ## not online, and no text in chattxt box
-                self.Post(self.colorize(self.syscolor, " ** That's the Tab key, Dave"))
-
-        ## PAGE UP
-        elif event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP):
-            logger.debug("event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP)")
-            self.chatwnd.ScrollPages(-1)
-            if not self.lockscroll: self.lock_scroll(0)
-
-        ## PAGE DOWN
-        elif event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN):
-            logger.debug("event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN)")
-            if not self.lockscroll: self.lock_scroll(0)
-            if ((self.chatwnd.GetScrollRange(1)-self.chatwnd.GetScrollPos(1)-self.chatwnd.GetScrollThumb(1) < 30) and self.lockscroll):
-                self.lock_scroll(0)
-            self.chatwnd.ScrollPages(1)
-
-        ## END
-        elif event.GetKeyCode() == wx.WXK_END:
-            logger.debug("event.GetKeyCode() == wx.WXK_END")
-            if self.lockscroll:
-                self.lock_scroll(0)
-                self.Post()
-            event.Skip()
-
-        ## NOTHING
-        else: event.Skip()
-        logger.debug("Exit chat_panel->OnChar(self, event)")
-    # def OnChar - end
-
-    
-    def onDieRoll(self, evt):
-        """Roll the dice based on the button pressed and the die modifiers entered, if any."""
-        # Get any die modifiers if they have been entered
-        numDie = self.numDieText.GetValue()
-        dieMod = self.dieModText.GetValue()
-        dieText = numDie
-        # Now, apply and roll die mods based on the button that was pressed
-        id = evt.GetId()
-        if self.dieIDs.has_key(id): dieText += self.dieIDs[id]
-        if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod
-        dieText += dieMod
-        dieText = "[" + dieText + "]"
-        self.ParsePost(dieText, 1, 1)
-        self.chattxt.SetFocus()
-
-    # This subroutine saves a chat buffer as html to a file chosen via a
-    # FileDialog.
-    #
-    # !self : instance of self
-    # !evt :
-    
-    def on_chat_save(self, evt):
-        f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE)
-        if f.ShowModal() == wx.ID_OK:
-            file = open(f.GetPath(), "w")
-            file.write(self.ResetPage() + "</body></html>")
-            file.close()
-        f.Destroy()
-        os.chdir(dir_struct["home"])
-    # def on_chat_save - end
-
-    
-    def ResetPage(self):
-        self.set_colors()
-        buffertext = self.chatwnd.Header() + "\n"
-        buffertext += chat_util.strip_body_tags(self.chatwnd.StripHeader()).replace("<br>", 
-                                                                            "<br />").replace('</html>', 
-                                                                            '').replace("<br />", 
-                                                                            "<br />\n").replace("\n\n", '')
-        return buffertext
-
-    # This subroutine sets the color of selected text, or base text color if
-    # nothing is selected
-    
-    def on_text_color(self, event):
-        hexcolor = self.r_h.do_hex_color_dlg(self)
-        if hexcolor != None:
-            (beg,end) = self.chattxt.GetSelection()
-            if beg != end:
-                txt = self.chattxt.GetValue()
-                txt = txt[:beg]+self.colorize(hexcolor,txt[beg:end]) +txt[end:]
-                self.chattxt.SetValue(txt)
-                self.chattxt.SetInsertionPointEnd()
-                self.chattxt.SetFocus()
-            else:
-                self.color_button.SetBackgroundColour(hexcolor)
-                self.mytextcolor = hexcolor
-                self.settings.set_setting('mytextcolor',hexcolor)
-                self.set_colors()
-                self.Post()
-    # def on_text_color - end
-
-    # This subroutine take a color and a text string and formats it into html.
-    #
-    # !self : instance of self
-    # !color : color for the text to be set
-    # !text : text string to be included in the html.
-    
-    def colorize(self, color, text):
-        """Puts font tags of 'color' around 'text' value, and returns the string"""
-        return "<font color='" + color + "'>" + text + "</font>"
-    # def colorize - end
-
-    # This subroutine takes and event and inserts text with the basic format
-    # tags included.
-    #
-    # !self : instance of self
-    # !event :
-    
-    def on_text_format(self, event):
-        id = event.GetId()
-        txt = self.chattxt.GetValue()
-        (beg,end) = self.chattxt.GetSelection()
-        if beg != end: sel_txt = txt[beg:end]
-        else: sel_txt = txt
-        if id == self.boldButton.GetId(): sel_txt = "<b>" + sel_txt + "</b>"
-        elif id == self.italicButton.GetId(): sel_txt = "<i>" + sel_txt + "</i>"
-        elif id == self.underlineButton.GetId(): sel_txt = "<u>" + sel_txt + "</u>"
-        if beg != end: txt = txt[:beg] + sel_txt + txt[end:]
-        else: txt = sel_txt
-        self.chattxt.SetValue(txt)
-        self.chattxt.SetInsertionPointEnd()
-        self.chattxt.SetFocus()
-    # def on_text_format - end
-
-    
-    def lock_scroll(self, event):
-        if self.lockscroll:
-            self.lockscroll = False
-            self.scroll_lock.SetLabel("Scroll ON")
-            if len(self.storedata) != 0:
-                for line in self.storedata: self.chatwnd.AppendToPage(line)
-            self.storedata = []
-            self.scroll_down()
-        else:
-            self.lockscroll = True
-            self.scroll_lock.SetLabel("Scroll OFF")
-
-    # This subroutine will popup a text window with the chatbuffer contents
-    #
-    # !self : instance of self
-    # !event :
-    
-    def pop_textpop(self, event):
-        """searchable popup text view of chatbuffer"""
-        h_buffertext = self.ResetPage()
-        h_dlg = orpgScrolledMessageFrameEditor(self, h_buffertext, "Text View of Chat Window", None, (500,300))
-        h_dlg.Show(True)
-
-    # This subroutine will change the dimension of the window
-    #
-    # !self : instance of self
-    # !event :
-    
-    def OnSize(self, event=None):
-        event.Skip()
-        wx.CallAfter(self.scroll_down)
-    # def OnSize - end
-
-    
-    def scroll_down(self):
-        self.Freeze()
-        self.chatwnd.scroll_down()
-        self.Thaw()
-
-    ###### message helpers ######
-    
-    def PurgeChat(self):
-        self.set_colors()
-        self.chatwnd.SetPage(self.chatwnd.Header())
-
-    
-    def system_message(self, text):
-        self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE)
-        self.SystemPost(text)
-
-    
-    def info_message(self, text):
-        self.send_chat_message(text,chat_msg.INFO_MESSAGE)
-        self.InfoPost(text)
-
-    
-    def get_gms(self):
-        the_gms = []
-        for playerid in self.session.players:
-            if len(self.session.players[playerid])>7:
-                if self.session.players[playerid][7]=="GM" and self.session.group_id != '0': the_gms += [playerid]
-        return the_gms
-
-    
-    def GetName(self):
-        self.AliasLib = component.get('alias')
-        player = self.session.get_my_info()
-        if self.AliasLib != None:
-            self.AliasLib.alias = self.aliasList.GetStringSelection();
-            if self.AliasLib.alias[0] != self.defaultAliasName:
-                logger.debug("Exit chat_panel->GetName(self)")
-                return [self.chat_display_name([self.AliasLib.alias[0], player[1], player[2]]), self.AliasLib.alias[1]]
-        return [self.chat_display_name(player), "Default"]
-
-    
-    def GetFilteredText(self, text):
-        advregex = re.compile('\"(.*?)\"', re.I)
-        self.AliasLib = component.get('alias')
-        if self.AliasLib != None:
-            self.AliasLib.filter = self.filterList.GetSelection()-1;
-            for rule in self.AliasLib.filterRegEx:
-                if not self.advancedFilter: text = re.sub(rule[0], rule[1], text)
-                else:
-                    for m in advregex.finditer(text):
-                        match = m.group(0)
-                        newmatch = re.sub(rule[0], rule[1], match)
-                        text = text.replace(match, newmatch)
-        return text
-
-    
-    def emote_message(self, text):
-        text = self.NormalizeParse(text)
-        text = self.colorize(self.emotecolor, text)
-
-        if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
-        elif self.type == MAIN_TAB and self.sendtarget == "gm":
-            msg_type = chat_msg.WHISPER_EMOTE_MESSAGE
-            the_gms = self.get_gms()
-            for each_gm in the_gms: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(each_gm))
-        elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
-            for pid in WG_LIST[self.sendtarget]:
-                self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(pid))
-        elif self.type == WHISPER_TAB: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(self.sendtarget))
-        elif self.type == NULL_TAB: pass
-        name = self.GetName()[0]
-        text = "** " + name + " " + text + " **"
-        self.EmotePost(text)
-
-    
-    def whisper_to_players(self, text, player_ids):
-        tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
-        # Heroman - apply any filtering selected
-        text = self.NormalizeParse(text)
-        player_names = ""
-        # post to our chat before we colorize
-        for m in player_ids:
-            id = m.strip()
-            if self.session.is_valid_id(id):
-                returned_name = self.session.get_player_by_player_id(id)[0]
-                player_names += returned_name
-                player_names += ", "
-            else:
-                player_names += " Unknown!"
-                player_names += ", "
-        comma = ","
-        comma.join(player_ids)
-        if (self.sendtarget == "all"):
-            self.InfoPost("<i>whispering to "+ player_names + " " + text + "</i> ")
-        # colorize and loop, sending whisper messages to all valid clients
-        text = self.colorize(self.mytextcolor, text)
-        for id in player_ids:
-            id = id.strip()
-            if self.session.is_valid_id(id): self.send_chat_message(text,chat_msg.WHISPER_MESSAGE,id)
-            else: self.InfoPost(id + " Unknown!")
-
-    
-    def send_chat_message(self, text, type=chat_msg.CHAT_MESSAGE, player_id="all"):
-        #########send_msg()#############
-        send = 1
-        for plugin_fname in self.activeplugins.keys():
-            plugin = self.activeplugins[plugin_fname]
-            try: text, send = plugin.send_msg(text, send)
-            except Exception, e:
-                if str(e) != "'module' object has no attribute 'send_msg'":
-                    logger.general(traceback.format_exc())
-                    logger.general("EXCEPTION: " + str(e))
-        msg = chat_msg.chat_msg()
-        msg.set_text(text)
-        msg.set_type(type)
-        turnedoff = False
-        if self.settings.get_setting("ShowIDInChat") == "1":
-            turnedoff = True
-            self.settings.set_setting("ShowIDInChat", "0")
-        playername = self.GetName()[0]
-
-        if turnedoff: self.settings.set_setting("ShowIDInChat", "1")
-        msg.set_alias(playername)
-        if send: self.session.send(msg.toxml(),player_id)
-        del msg
-
-    #### incoming chat message handler #####
-    
-    def post_incoming_msg(self, msg, player):
-
-        # pull data
-        type = msg.get_type()
-        text = msg.get_text()
-        alias = msg.get_alias()
-        # who sent us the message?
-        if alias: display_name = self.chat_display_name([alias, player[1], player[2]])
-        elif player: display_name = self.chat_display_name(player)
-        else: display_name = "Server Administrator"
-
-        ######### START plugin_incoming_msg() ###########
-        for plugin_fname in self.activeplugins.keys():
-            plugin = self.activeplugins[plugin_fname]
-            try: text, type, name = plugin.plugin_incoming_msg(text, type, display_name, player)
-            except Exception, e:
-                if str(e) != "'module' object has no attribute 'receive_msg'":
-                    logger.general(traceback.format_exc())
-                    logger.general("EXCEPTION: " + str(e))
-        #end mDuo13 added code
-        #image stripping for players' names
-        strip_img = self.settings.get_setting("Show_Images_In_Chat")
-        if (strip_img == "0"): display_name = chat_util.strip_img_tags(display_name)
-        #end image stripping. --mDuo13, July 11th, 2005
-        # default sound
-        recvSound = "RecvSound"
-        # act on the type of messsage
-        if (type == chat_msg.CHAT_MESSAGE):
-            text = "<b>" + display_name + "</b>: " + text
-            self.Post(text)
-            self.parent.newMsg(0)
-        elif type == chat_msg.WHISPER_MESSAGE or type == chat_msg.WHISPER_EMOTE_MESSAGE:
-            tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
-            displaypanel = self
-            whisperingstring = " (whispering): "
-            panelexists = 0
-            GMWhisperTab = self.settings.get_setting("GMWhisperTab")
-            GroupWhisperTab = self.settings.get_setting("GroupWhisperTab")
-            name = '<i><b>' + display_name + '</b>: '
-            text += '</i>'
-            panelexists = 0
-            created = 0
-            try:
-                if GMWhisperTab == '1':
-                    the_gms = self.get_gms()
-                    #Check if whisper if from a GM
-                    if player[2] in the_gms:
-                        msg = name + ' (GM Whisper:) ' + text
-                        if type == chat_msg.WHISPER_MESSAGE: self.parent.GMChatPanel.Post(msg)
-                        else: self.parent.GMChatPanel.EmotePost("**" + msg + "**")
-                        idx = self.parent.get_tab_index(self.parent.GMChatPanel)
-                        self.parent.newMsg(idx)
-                        panelexists = 1
-                #See if message if from someone in our groups or for a whisper tab we already have
-                if not panelexists and GroupWhisperTab == "1":
-                    for panel in self.parent.group_tabs:
-                        if WG_LIST.has_key(panel.sendtarget) and WG_LIST[panel.sendtarget].has_key(int(player[2])):
-                            msg = name + text
-                            if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
-                            else: panel.EmotePost("**" + msg + "**")
-                            idx = self.parent.get_tab_index(panel)
-                            self.parent.newMsg(idx)
-                            panelexists = 1
-                            break
-                if not panelexists and tabbed_whispers_p == "1":
-                    for panel in self.parent.whisper_tabs:
-                        #check for whisper tabs as well, to save the number of loops
-                        if panel.sendtarget == player[2]:
-                            msg = name + whisperingstring + text
-                            if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
-                            else: panel.EmotePost("**" + msg + "**")
-                            idx = self.parent.get_tab_index(panel)
-                            self.parent.newMsg(idx)
-                            panelexists = 1
-                            break
-                #We did not fint the tab
-                if not panelexists:
-                    #If we get here the tab was not found
-                    if GroupWhisperTab == "1":
-                        for group in WG_LIST.keys():
-                            #Check if this group has the player in it
-                            if WG_LIST[group].has_key(int(player[2])):
-                                #Yup, post message. Player may be in more then 1 group so continue as well
-                                panel = self.parent.create_group_tab(group)
-                                msg = name + text
-                                if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
-                                else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
-                                created = 1
-                    #Check to see if we should create a whisper tab
-                    if not created and tabbed_whispers_p == "1":
-                        panel = self.parent.create_whisper_tab(player[2])
-                        msg = name + whisperingstring + text
-                        if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
-                        else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
-                        created = 1
-                    #Final check
-                    if not created:
-                        #No tabs to create, just send the message to the main chat tab
-                        msg = name + whisperingstring + text
-                        if type == chat_msg.WHISPER_MESSAGE: self.parent.MainChatPanel.Post(msg)
-                        else: self.parent.MainChatPanel.EmotePost("**" + msg + "**")
-                        self.parent.newMsg(0)
-            except Exception, e:
-                logger.general(traceback.format_exc())
-                logger.general("EXCEPTION: 'Error in posting whisper message': " + str(e))
-        elif (type == chat_msg.EMOTE_MESSAGE):
-            text = "** " + display_name + " " + text + " **"
-            self.EmotePost(text)
-            self.parent.newMsg(0)
-        elif (type == chat_msg.INFO_MESSAGE):
-            text = "<b>" + display_name + "</b>: " + text
-            self.InfoPost(text)
-            self.parent.newMsg(0)
-        elif (type == chat_msg.SYSTEM_MESSAGE):
-            text = "<b>" + display_name + "</b>: " + text
-            self.SystemPost(text)
-            self.parent.newMsg(0)
-        # playe sound
-        sound_file = self.settings.get_setting(recvSound)
-        if sound_file != '':
-            component.get('sound').play(sound_file)
-    #### Posting helpers #####
-
-    
-    def InfoPost(self, s):
-        self.Post(self.colorize(self.infocolor, s), c='info')
-
-    
-    def SystemPost(self, s):
-        self.Post(self.colorize(self.syscolor, s), c='system')
-
-    
-    def EmotePost(self, s):
-        self.Post(self.colorize(self.emotecolor, s), c='emote')
-
-    #### Standard Post method #####
-    
-    def Post(self, s="", send=False, myself=False, c='post'):
-        strip_p = self.settings.get_setting("striphtml")
-        strip_img = self.settings.get_setting("Show_Images_In_Chat")#moved back 7-11-05. --mDuo13
-        if (strip_p == "1"): s = strip_html(s)
-        if (strip_img == "0"): s = chat_util.strip_img_tags(s)
-        s = chat_util.simple_html_repair(s)
-        s = chat_util.strip_script_tags(s)
-        s = chat_util.strip_li_tags(s)
-        s = chat_util.strip_body_tags(s) #7-27-05 mDuo13
-        s = chat_util.strip_misalignment_tags(s) #7-27-05 mDuo13
-        aliasInfo = self.GetName()
-        display_name = aliasInfo[0]
-        if aliasInfo[1] != 'Default':
-            defaultcolor = self.settings.get_setting("mytextcolor")
-            self.settings.set_setting("mytextcolor", aliasInfo[1])
-            self.set_colors()
-        newline = ''
-        #following added by mDuo13
-        #########post_msg() - other##########
-        if not myself and not send:
-            for plugin_fname in self.activeplugins.keys():
-                plugin = self.activeplugins[plugin_fname]
-                try: s = plugin.post_msg(s, myself)
-                except Exception, e:
-                    if str(e) != "'module' object has no attribute 'post_msg'":
-                        logger.general(traceback.format_exc())
-                        logger.general("EXCEPTION: " + str(e))
-        #end mDuo13 added code
-        if myself:
-            name = "<b>" + display_name + "</b>: "
-            s = self.colorize(self.mytextcolor, s)
-        else: name = ""
-        if aliasInfo[1] != 'Default':
-            self.settings.set_setting("mytextcolor", defaultcolor)
-            self.set_colors()
-        #following line based on sourceforge patch #880403 from mDuo
-        # EDIT: Had to rework blank line check to handle malformed HTML throwing error.
-        #       this limits the effectiveness of this check -SD
-        lineHasText = 1
-        try: lineHasText = strip_html(s).replace("&nbsp;","").replace(" ","").strip()!=""
-        except:
-            # HTML parser has errored out (most likely). Being as all we are doing is
-            # scanning for empty/blank lines anyway there is no harm in letting a
+        self.toolbar_sizer.Layout()
+
+    # Heroman - Ideally, we would use static labels...
+    
+    def build_colorbutton(self):
+        self.color_button = createMaskedButton(self, dir_struct["icon"]+'textcolor.gif', 
+                                                    'Text Color', wx.ID_ANY, '#bdbdbd', 
+                                                    wx.BITMAP_TYPE_GIF)
+
+        self.saveButton = createMaskedButton(self, dir_struct["icon"]+'save.bmp', 
+                                                    'Save the chatbuffer', wx.ID_ANY, 
+                                                    '#c0c0c0', wx.BITMAP_TYPE_BMP )
+        self.color_button.SetBackgroundColour(self.settings.get_setting('mytextcolor'))
+        self.toolbar_sizer.Add(self.color_button, 0, wx.EXPAND)
+        self.toolbar_sizer.Add(self.saveButton, 0, wx.EXPAND)
+
+    
+    def OnMotion(self, evt):
+        contain = self.chatwnd.GetInternalRepresentation()
+        if contain:
+            sx = sy = 0
+            x = y = 0
+            (sx,sy) = self.chatwnd.GetViewStart()
+            (sx1,sy1) = self.chatwnd.GetScrollPixelsPerUnit()
+            sx = sx*sx1
+            sy = sy*sy1
+            (x,y) = evt.GetPosition()
+            lnk = contain.GetLink(sx+x,sy+y)
+            if lnk:
+                try:
+                    link = lnk.GetHref()
+                    self.session.set_status_url(link)
+                except: pass
+        else: logger.general("Error, self.chatwnd.GetInternalRepresentation() return None")
+        evt.Skip()
+
+    #  This subroutine is registered with predTextCtrl to be run for every OnChar event
+    #  It checks if we need to send a typing message
+
+    #
+    #  self:  duh
+    #  event:  raw KeyEvent from OnChar()
+    
+    def myKeyHook(self, event):
+        if self.session.get_status() == MPLAY_CONNECTED:   #  only do if we're connected
+            thisPress = time.time()                #  thisPress is local temp variable
+            if (thisPress - self.lastSend) > 4:    #  Check to see if it's been 5 seconds since our last notice
+                                                   #    If we're not already typing, then self.lastSend will be 0
+                self.sendTyping(1)                 #  send a not typing event here (1 for True)
+            self.lastPress = thisPress             #  either way, record the time of this keystroke for use in
+                                                   #  self.typingTimerFunc()
+        if self.settings.get_setting('SuppressChatAutoComplete') == '1':
+            logger.debug("Exit chat_panel->myKeyHook(self, event) return 1")
+            return 1
+        else:
+            logger.debug("Exit chat_panel->myKeyHook(self, event) return 0")
+            return 0
+
+    #  This subroutine gets called once a second by the typing Timer
+    #  It checks if we need to send a not_typing message
+    #
+    #  self:  duh
+    
+    def typingTimerFunc(self, event):
+        #following added by mDuo13
+        ##############refresh_counter()##############
+        for plugin_fname in self.activeplugins.keys():
+            plugin = self.activeplugins[plugin_fname]
+            try: plugin.refresh_counter()
+            except Exception, e:
+                if str(e) != "'module' object has no attribute 'refresh_counter'":
+                    logger.general(traceback.format_exc())
+                    logger.general("EXCEPTION: " + str(e))
+        #end mDuo13 added code
+        if self.lastSend:                          #  This will be zero when not typing, so equiv to if is_typing
+            thisTime = time.time()                 #  thisTime is a local temp variable
+            if (thisTime - self.lastPress) > 4:    #  Check to see if it's been 5 seconds since our last keystroke
+                                                   #  If we're not already typing, then self.lastSend will be 0
+
+                self.sendTyping(0)                 #  send a typing event here (0 for False)
+    #  This subroutine actually takes care of sending the messages for typing/not_typing events
+    #
+    #  self:  duh
+    #  typing:  boolean
+
+    
+    def sendTyping(self, typing):
+        if typing:
+            self.lastSend = time.time()  #  remember our send time for use in myKeyHook()
+            #I think this is cleaner
+            status_text = self.settings.get_setting('TypingStatusAlias')
+            if status_text == "" or status_text == None: status_text = "Typing"
+            self.session.set_text_status(status_text)
+        else:
+            self.lastSend = 0                            #  set lastSend to zero to indicate we're not typing
+            #I think this is cleaner
+            status_text = self.settings.get_setting('IdleStatusAlias')
+            if status_text == "" or status_text == None: status_text = "Idle"
+            self.session.set_text_status(status_text)
+
+    # This subroutine sets the colors of the chat based on the settings in the
+    # self instance.
+    #
+    # !self : instance of self
+    
+    def set_colors(self):
+        # chat window backround color
+        self.bgcolor = self.settings.get_setting('bgcolor')
+        # chat window normal text color
+        self.textcolor = self.settings.get_setting('textcolor')
+        # color of text player types
+        self.mytextcolor = self.settings.get_setting('mytextcolor')
+        # color of system warnings
+        self.syscolor = self.settings.get_setting('syscolor')
+        # color of system info messages
+        self.infocolor = self.settings.get_setting('infocolor')
+        # color of emotes
+        self.emotecolor = self.settings.get_setting('emotecolor')
+        # color of whispers
+        self.whispercolor = self.settings.get_setting('whispercolor')
+    # def set_colors - end
+
+    # This subroutine will insert text into the chat window
+    #
+    # !self : instance of self
+    # !txt : text to be inserted into the chat window
+    
+    def set_chat_text(self, txt):
+        self.chattxt.SetValue(txt)
+        self.chattxt.SetFocus()
+        self.chattxt.SetInsertionPointEnd()
+    # def set_chat_text - end
+
+    
+    def get_chat_text(self):
+        return self.chattxt.GetValue()
+
+    # This subroutine sets the focus to the chat window
+    
+    def set_chat_text_focus(self, event):
+        wx.CallAfter(self.chattxt.SetFocus)
+    # def set_chat_text_focus - end
+
+    # This subrtouine grabs the user input and make the special keys and
+    # modifiers work.
+    #
+    # !self : instance of self
+    # !event :
+    #
+    # Note:  self.chattxt now handles it's own Key events.  It does, however still
+    #        call it's parent's (self) OnChar to handle "default" behavior.
+    
+    def OnChar(self, event):
+        s = self.chattxt.GetValue()
+        #self.histlen = len(self.history) - 1
+
+        ## RETURN KEY (no matter if there is text in chattxt)
+        #  This section is run even if there is nothing in the chattxt (as opposed to the next wx.WXK_RETURN handler
+        if event.GetKeyCode() == wx.WXK_RETURN:
+            logger.debug("event.GetKeyCode() == wx.WXK_RETURN")
+            self.set_colors()
+            if self.session.get_status() == MPLAY_CONNECTED:          #  only do if we're connected
+                self.sendTyping(0)                                    #  Send a "not_typing" event on enter key press
+        macroText=""
+        recycle_bin = {wx.WXK_F1: 'event.GetKeyCode() == wx.WXK_F1', wx.WXK_F2: 'event.GetKeyCode() == wx.WXK_F2', 
+                    wx.WXK_F3: 'event.GetKeyCode() == wx.WXK_F3', wx.WXK_F4: 'event.GetKeyCode() == wx.WXK_F4', 
+                    wx.WXK_F5: 'event.GetKeyCode() == wx.WXK_F5', wx.WXK_F6: 'event.GetKeyCode() == wx.WXK_F6', 
+                    wx.WXK_F7: 'event.GetKeyCode() == wx.WXK_F7', wx.WXK_F8: 'event.GetKeyCode() == wx.WXK_F8', 
+                    wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10', 
+                    wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'}
+
+        bin_event = event.GetKeyCode()
+        if recycle_bin.has_key(bin_event):
+	    logger.debug(lambda bin_event: recycle_bin[bin_event])
+	    macroText = self.settings.get_setting(recycle_bin[bin_event][29:])
+	    recycle_bin = {}; del bin_event
+
+        # Append to the existing typed text as needed and make sure the status doesn't change back.
+        if len(macroText):
+            self.sendTyping(0)
+            s = macroText
+
+        ## RETURN KEY (and not text in control)
+        if (event.GetKeyCode() == wx.WXK_RETURN and len(s)) or len(macroText):
+            logger.debug("(event.GetKeyCode() == wx.WXK_RETURN and len(s)) or len(macroText)")
+            self.histidx = -1
+            self.temptext = ""
+            self.history = [s] + self.history#prepended instead of appended now, so higher index = greater age
+            if not len(macroText): self.chattxt.SetValue("")
+            # play sound
+            sound_file = self.settings.get_setting("SendSound")
+            if sound_file != '': component.get('sound').play(sound_file)
+            if s[0] != "/": ## it's not a slash command
+                s = self.ParsePost( s, True, True )
+            else: self.chat_cmds.docmd(s) # emote is in chatutils.py
+
+        ## UP KEY
+        elif event.GetKeyCode() == wx.WXK_UP:
+            logger.debug("event.GetKeyCode() == wx.WXK_UP")
+            if self.histidx < len(self.history)-1:
+                #text that's not in history but also hasn't been sent to chat gets stored in self.temptext
+                #this way if someone presses the up key, they don't lose their current message permanently
+                #(unless they also press enter at the time)
+                if self.histidx is -1: self.temptext = self.chattxt.GetValue()
+                self.histidx += 1
+                self.chattxt.SetValue(self.history[self.histidx])
+                self.chattxt.SetInsertionPointEnd()
+            else:
+                self.histidx = len(self.history) -1#in case it got too high somehow, this should fix it
+                #self.InfoPost("**Going up? I don't think so.**")
+            #print self.histidx, "in",self.history
+
+        ## DOWN KEY
+        elif event.GetKeyCode() == wx.WXK_DOWN:
+            logger.debug("event.GetKeyCode() == wx.WXK_DOWN")
+            #histidx of -1 indicates currently viewing text that's not in self.history
+            if self.histidx > -1:
+                self.histidx -= 1
+                if self.histidx is -1: #remember, it just decreased
+                    self.chattxt.SetValue(self.temptext)
+                else: self.chattxt.SetValue(self.history[self.histidx])
+                self.chattxt.SetInsertionPointEnd()
+            else: self.histidx = -1 #just in case it somehow got below -1, this should fix it
+                #self.InfoPost("**Going down? I don't think so.**")
+            #print self.histidx, "in",self.history
+
+        ## TAB KEY
+        elif  event.GetKeyCode() == wx.WXK_TAB:
+            logger.debug("event.GetKeyCode() == wx.WXK_TAB")
+            if s !="":
+                found = 0
+                nicks = []
+                testnick = ""
+                inlength = len(s)
+                for getnames in self.session.players.keys():
+                    striphtmltag = re.compile ('<[^>]+>*')
+                    testnick = striphtmltag.sub ("", self.session.players[getnames][0])
+                    if string.lower(s) == string.lower(testnick[:inlength]):
+                        found = found + 1
+                        nicks[len(nicks):]=[testnick]
+                if found == 0: ## no nick match
+                    self.Post(self.colorize(self.syscolor," ** No match found"))
+                elif found > 1: ## matched more than 1, tell user what matched
+                    nickstring = ""
+                    nicklist = []
+                    for foundnicks in nicks:
+                        nickstring = nickstring + foundnicks + ", "
+                        nicklist.append(foundnicks)
+                    nickstring = nickstring[:-2]
+                    self.Post(self.colorize(self.syscolor, " ** Multiple matches found: " + nickstring))
+                    # set text to the prefix match between first two matches
+                    settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(nicklist[0]))), string.lower(nicklist[1])).group()
+                    # run through the rest of the nicks
+                    for i in nicklist:
+                        settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(i))), string.lower(settext)).group()
+                    if settext:
+                        self.chattxt.SetValue(settext)
+                        self.chattxt.SetInsertionPointEnd()
+                else: ## put the matched name in the chattxt box
+                    settext = nicks[0] + ": "
+                    self.chattxt.SetValue(settext)
+                    self.chattxt.SetInsertionPointEnd()
+            else: ## not online, and no text in chattxt box
+                self.Post(self.colorize(self.syscolor, " ** That's the Tab key, Dave"))
+
+        ## PAGE UP
+        elif event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP):
+            logger.debug("event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP)")
+            self.chatwnd.ScrollPages(-1)
+            if not self.lockscroll: self.lock_scroll(0)
+
+        ## PAGE DOWN
+        elif event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN):
+            logger.debug("event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN)")
+            if not self.lockscroll: self.lock_scroll(0)
+            if ((self.chatwnd.GetScrollRange(1)-self.chatwnd.GetScrollPos(1)-self.chatwnd.GetScrollThumb(1) < 30) and self.lockscroll):
+                self.lock_scroll(0)
+            self.chatwnd.ScrollPages(1)
+
+        ## END
+        elif event.GetKeyCode() == wx.WXK_END:
+            logger.debug("event.GetKeyCode() == wx.WXK_END")
+            if self.lockscroll:
+                self.lock_scroll(0)
+                self.Post()
+            event.Skip()
+
+        ## NOTHING
+        else: event.Skip()
+        logger.debug("Exit chat_panel->OnChar(self, event)")
+    # def OnChar - end
+
+    
+    def onDieRoll(self, evt):
+        """Roll the dice based on the button pressed and the die modifiers entered, if any."""
+        # Get any die modifiers if they have been entered
+        numDie = self.numDieText.GetValue()
+        dieMod = self.dieModText.GetValue()
+        dieText = numDie
+        # Now, apply and roll die mods based on the button that was pressed
+        id = evt.GetId()
+        if self.dieIDs.has_key(id): dieText += self.dieIDs[id]
+        if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod
+        dieText += dieMod
+        dieText = "[" + dieText + "]"
+        self.ParsePost(dieText, 1, 1)
+        self.chattxt.SetFocus()
+
+    # This subroutine saves a chat buffer as html to a file chosen via a
+    # FileDialog.
+    #
+    # !self : instance of self
+    # !evt :
+    
+    def on_chat_save(self, evt):
+        f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE)
+        if f.ShowModal() == wx.ID_OK:
+            file = open(f.GetPath(), "w")
+            file.write(self.ResetPage() + "</body></html>")
+            file.close()
+        f.Destroy()
+        os.chdir(dir_struct["home"])
+    # def on_chat_save - end
+
+    
+    def ResetPage(self):
+        self.set_colors()
+        buffertext = self.chatwnd.Header() + "\n"
+        buffertext += chat_util.strip_body_tags(self.chatwnd.StripHeader()).replace("<br>", 
+                                                                            "<br />").replace('</html>', 
+                                                                            '').replace("<br />", 
+                                                                            "<br />\n").replace("\n\n", '')
+        return buffertext
+
+    # This subroutine sets the color of selected text, or base text color if
+    # nothing is selected
+    
+    def on_text_color(self, event):
+        hexcolor = self.r_h.do_hex_color_dlg(self)
+        if hexcolor != None:
+            (beg,end) = self.chattxt.GetSelection()
+            if beg != end:
+                txt = self.chattxt.GetValue()
+                txt = txt[:beg]+self.colorize(hexcolor,txt[beg:end]) +txt[end:]
+                self.chattxt.SetValue(txt)
+                self.chattxt.SetInsertionPointEnd()
+                self.chattxt.SetFocus()
+            else:
+                self.color_button.SetBackgroundColour(hexcolor)
+                self.mytextcolor = hexcolor
+                self.settings.set_setting('mytextcolor',hexcolor)
+                self.set_colors()
+                self.Post()
+    # def on_text_color - end
+
+    # This subroutine take a color and a text string and formats it into html.
+    #
+    # !self : instance of self
+    # !color : color for the text to be set
+    # !text : text string to be included in the html.
+    
+    def colorize(self, color, text):
+        """Puts font tags of 'color' around 'text' value, and returns the string"""
+        return "<font color='" + color + "'>" + text + "</font>"
+    # def colorize - end
+
+    # This subroutine takes and event and inserts text with the basic format
+    # tags included.
+    #
+    # !self : instance of self
+    # !event :
+    
+    def on_text_format(self, event):
+        id = event.GetId()
+        txt = self.chattxt.GetValue()
+        (beg,end) = self.chattxt.GetSelection()
+        if beg != end: sel_txt = txt[beg:end]
+        else: sel_txt = txt
+        if id == self.boldButton.GetId(): sel_txt = "<b>" + sel_txt + "</b>"
+        elif id == self.italicButton.GetId(): sel_txt = "<i>" + sel_txt + "</i>"
+        elif id == self.underlineButton.GetId(): sel_txt = "<u>" + sel_txt + "</u>"
+        if beg != end: txt = txt[:beg] + sel_txt + txt[end:]
+        else: txt = sel_txt
+        self.chattxt.SetValue(txt)
+        self.chattxt.SetInsertionPointEnd()
+        self.chattxt.SetFocus()
+    # def on_text_format - end
+
+    
+    def lock_scroll(self, event):
+        if self.lockscroll:
+            self.lockscroll = False
+            self.scroll_lock.SetLabel("Scroll ON")
+            if len(self.storedata) != 0:
+                for line in self.storedata: self.chatwnd.AppendToPage(line)
+            self.storedata = []
+            self.scroll_down()
+        else:
+            self.lockscroll = True
+            self.scroll_lock.SetLabel("Scroll OFF")
+
+    # This subroutine will popup a text window with the chatbuffer contents
+    #
+    # !self : instance of self
+    # !event :
+    
+    def pop_textpop(self, event):
+        """searchable popup text view of chatbuffer"""
+        h_buffertext = self.ResetPage()
+        h_dlg = orpgScrolledMessageFrameEditor(self, h_buffertext, "Text View of Chat Window", None, (500,300))
+        h_dlg.Show(True)
+
+    # This subroutine will change the dimension of the window
+    #
+    # !self : instance of self
+    # !event :
+    
+    def OnSize(self, event=None):
+        event.Skip()
+        wx.CallAfter(self.scroll_down)
+    # def OnSize - end
+
+    
+    def scroll_down(self):
+        self.Freeze()
+        self.chatwnd.scroll_down()
+        self.Thaw()
+
+    ###### message helpers ######
+    
+    def PurgeChat(self):
+        self.set_colors()
+        self.chatwnd.SetPage(self.chatwnd.Header())
+
+    
+    def system_message(self, text):
+        self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE)
+        self.SystemPost(text)
+
+    
+    def info_message(self, text):
+        self.send_chat_message(text,chat_msg.INFO_MESSAGE)
+        self.InfoPost(text)
+
+    
+    def get_gms(self):
+        the_gms = []
+        for playerid in self.session.players:
+            if len(self.session.players[playerid])>7:
+                if self.session.players[playerid][7]=="GM" and self.session.group_id != '0': the_gms += [playerid]
+        return the_gms
+
+    
+    def GetName(self):
+        self.AliasLib = component.get('alias')
+        player = self.session.get_my_info()
+        if self.AliasLib != None:
+            self.AliasLib.alias = self.aliasList.GetStringSelection();
+            if self.AliasLib.alias[0] != self.defaultAliasName:
+                logger.debug("Exit chat_panel->GetName(self)")
+                return [self.chat_display_name([self.AliasLib.alias[0], player[1], player[2]]), self.AliasLib.alias[1]]
+        return [self.chat_display_name(player), "Default"]
+
+    
+    def GetFilteredText(self, text):
+        advregex = re.compile('\"(.*?)\"', re.I)
+        self.AliasLib = component.get('alias')
+        if self.AliasLib != None:
+            self.AliasLib.filter = self.filterList.GetSelection()-1;
+            for rule in self.AliasLib.filterRegEx:
+                if not self.advancedFilter: text = re.sub(rule[0], rule[1], text)
+                else:
+                    for m in advregex.finditer(text):
+                        match = m.group(0)
+                        newmatch = re.sub(rule[0], rule[1], match)
+                        text = text.replace(match, newmatch)
+        return text
+
+    
+    def emote_message(self, text):
+        text = self.NormalizeParse(text)
+        text = self.colorize(self.emotecolor, text)
+
+        if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
+        elif self.type == MAIN_TAB and self.sendtarget == "gm":
+            msg_type = chat_msg.WHISPER_EMOTE_MESSAGE
+            the_gms = self.get_gms()
+            for each_gm in the_gms: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(each_gm))
+        elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
+            for pid in WG_LIST[self.sendtarget]:
+                self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(pid))
+        elif self.type == WHISPER_TAB: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(self.sendtarget))
+        elif self.type == NULL_TAB: pass
+        name = self.GetName()[0]
+        text = "** " + name + " " + text + " **"
+        self.EmotePost(text)
+
+    
+    def whisper_to_players(self, text, player_ids):
+        tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
+        # Heroman - apply any filtering selected
+        text = self.NormalizeParse(text)
+        player_names = ""
+        # post to our chat before we colorize
+        for m in player_ids:
+            id = m.strip()
+            if self.session.is_valid_id(id):
+                returned_name = self.session.get_player_by_player_id(id)[0]
+                player_names += returned_name
+                player_names += ", "
+            else:
+                player_names += " Unknown!"
+                player_names += ", "
+        comma = ","
+        comma.join(player_ids)
+        if (self.sendtarget == "all"):
+            self.InfoPost("<i>whispering to "+ player_names + " " + text + "</i> ")
+        # colorize and loop, sending whisper messages to all valid clients
+        text = self.colorize(self.mytextcolor, text)
+        for id in player_ids:
+            id = id.strip()
+            if self.session.is_valid_id(id): self.send_chat_message(text,chat_msg.WHISPER_MESSAGE,id)
+            else: self.InfoPost(id + " Unknown!")
+
+    
+    def send_chat_message(self, text, type=chat_msg.CHAT_MESSAGE, player_id="all"):
+        #########send_msg()#############
+        send = 1
+        for plugin_fname in self.activeplugins.keys():
+            plugin = self.activeplugins[plugin_fname]
+            try: text, send = plugin.send_msg(text, send)
+            except Exception, e:
+                if str(e) != "'module' object has no attribute 'send_msg'":
+                    logger.general(traceback.format_exc())
+                    logger.general("EXCEPTION: " + str(e))
+        msg = chat_msg.chat_msg()
+        msg.set_text(text)
+        msg.set_type(type)
+        turnedoff = False
+        if self.settings.get_setting("ShowIDInChat") == "1":
+            turnedoff = True
+            self.settings.set_setting("ShowIDInChat", "0")
+        playername = self.GetName()[0]
+
+        if turnedoff: self.settings.set_setting("ShowIDInChat", "1")
+        msg.set_alias(playername)
+        if send: self.session.send(msg.toxml(),player_id)
+        del msg
+
+    #### incoming chat message handler #####
+    
+    def post_incoming_msg(self, msg, player):
+
+        # pull data
+        type = msg.get_type()
+        text = msg.get_text()
+        alias = msg.get_alias()
+        # who sent us the message?
+        if alias: display_name = self.chat_display_name([alias, player[1], player[2]])
+        elif player: display_name = self.chat_display_name(player)
+        else: display_name = "Server Administrator"
+
+        ######### START plugin_incoming_msg() ###########
+        for plugin_fname in self.activeplugins.keys():
+            plugin = self.activeplugins[plugin_fname]
+            try: text, type, name = plugin.plugin_incoming_msg(text, type, display_name, player)
+            except Exception, e:
+                if str(e) != "'module' object has no attribute 'receive_msg'":
+                    logger.general(traceback.format_exc())
+                    logger.general("EXCEPTION: " + str(e))
+        #end mDuo13 added code
+        #image stripping for players' names
+        strip_img = self.settings.get_setting("Show_Images_In_Chat")
+        if (strip_img == "0"): display_name = chat_util.strip_img_tags(display_name)
+        #end image stripping. --mDuo13, July 11th, 2005
+        # default sound
+        recvSound = "RecvSound"
+        # act on the type of messsage
+        if (type == chat_msg.CHAT_MESSAGE):
+            text = "<b>" + display_name + "</b>: " + text
+            self.Post(text)
+            self.parent.newMsg(0)
+        elif type == chat_msg.WHISPER_MESSAGE or type == chat_msg.WHISPER_EMOTE_MESSAGE:
+            tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
+            displaypanel = self
+            whisperingstring = " (whispering): "
+            panelexists = 0
+            GMWhisperTab = self.settings.get_setting("GMWhisperTab")
+            GroupWhisperTab = self.settings.get_setting("GroupWhisperTab")
+            name = '<i><b>' + display_name + '</b>: '
+            text += '</i>'
+            panelexists = 0
+            created = 0
+            try:
+                if GMWhisperTab == '1':
+                    the_gms = self.get_gms()
+                    #Check if whisper if from a GM
+                    if player[2] in the_gms:
+                        msg = name + ' (GM Whisper:) ' + text
+                        if type == chat_msg.WHISPER_MESSAGE: self.parent.GMChatPanel.Post(msg)
+                        else: self.parent.GMChatPanel.EmotePost("**" + msg + "**")
+                        idx = self.parent.get_tab_index(self.parent.GMChatPanel)
+                        self.parent.newMsg(idx)
+                        panelexists = 1
+                #See if message if from someone in our groups or for a whisper tab we already have
+                if not panelexists and GroupWhisperTab == "1":
+                    for panel in self.parent.group_tabs:
+                        if WG_LIST.has_key(panel.sendtarget) and WG_LIST[panel.sendtarget].has_key(int(player[2])):
+                            msg = name + text
+                            if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
+                            else: panel.EmotePost("**" + msg + "**")
+                            idx = self.parent.get_tab_index(panel)
+                            self.parent.newMsg(idx)
+                            panelexists = 1
+                            break
+                if not panelexists and tabbed_whispers_p == "1":
+                    for panel in self.parent.whisper_tabs:
+                        #check for whisper tabs as well, to save the number of loops
+                        if panel.sendtarget == player[2]:
+                            msg = name + whisperingstring + text
+                            if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
+                            else: panel.EmotePost("**" + msg + "**")
+                            idx = self.parent.get_tab_index(panel)
+                            self.parent.newMsg(idx)
+                            panelexists = 1
+                            break
+                #We did not fint the tab
+                if not panelexists:
+                    #If we get here the tab was not found
+                    if GroupWhisperTab == "1":
+                        for group in WG_LIST.keys():
+                            #Check if this group has the player in it
+                            if WG_LIST[group].has_key(int(player[2])):
+                                #Yup, post message. Player may be in more then 1 group so continue as well
+                                panel = self.parent.create_group_tab(group)
+                                msg = name + text
+                                if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
+                                else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
+                                created = 1
+                    #Check to see if we should create a whisper tab
+                    if not created and tabbed_whispers_p == "1":
+                        panel = self.parent.create_whisper_tab(player[2])
+                        msg = name + whisperingstring + text
+                        if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
+                        else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
+                        created = 1
+                    #Final check
+                    if not created:
+                        #No tabs to create, just send the message to the main chat tab
+                        msg = name + whisperingstring + text
+                        if type == chat_msg.WHISPER_MESSAGE: self.parent.MainChatPanel.Post(msg)
+                        else: self.parent.MainChatPanel.EmotePost("**" + msg + "**")
+                        self.parent.newMsg(0)
+            except Exception, e:
+                logger.general(traceback.format_exc())
+                logger.general("EXCEPTION: 'Error in posting whisper message': " + str(e))
+        elif (type == chat_msg.EMOTE_MESSAGE):
+            text = "** " + display_name + " " + text + " **"
+            self.EmotePost(text)
+            self.parent.newMsg(0)
+        elif (type == chat_msg.INFO_MESSAGE):
+            text = "<b>" + display_name + "</b>: " + text
+            self.InfoPost(text)
+            self.parent.newMsg(0)
+        elif (type == chat_msg.SYSTEM_MESSAGE):
+            text = "<b>" + display_name + "</b>: " + text
+            self.SystemPost(text)
+            self.parent.newMsg(0)
+        # playe sound
+        sound_file = self.settings.get_setting(recvSound)
+        if sound_file != '':
+            component.get('sound').play(sound_file)
+    #### Posting helpers #####
+
+    
+    def InfoPost(self, s):
+        self.Post(self.colorize(self.infocolor, s), c='info')
+
+    
+    def SystemPost(self, s):
+        self.Post(self.colorize(self.syscolor, s), c='system')
+
+    
+    def EmotePost(self, s):
+        self.Post(self.colorize(self.emotecolor, s), c='emote')
+
+    #### Standard Post method #####
+    
+    def Post(self, s="", send=False, myself=False, c='post'):
+        strip_p = self.settings.get_setting("striphtml")
+        strip_img = self.settings.get_setting("Show_Images_In_Chat")#moved back 7-11-05. --mDuo13
+        if (strip_p == "1"): s = strip_html(s)
+        if (strip_img == "0"): s = chat_util.strip_img_tags(s)
+        s = chat_util.simple_html_repair(s)
+        s = chat_util.strip_script_tags(s)
+        s = chat_util.strip_li_tags(s)
+        s = chat_util.strip_body_tags(s) #7-27-05 mDuo13
+        s = chat_util.strip_misalignment_tags(s) #7-27-05 mDuo13
+        aliasInfo = self.GetName()
+        display_name = aliasInfo[0]
+        if aliasInfo[1] != 'Default':
+            defaultcolor = self.settings.get_setting("mytextcolor")
+            self.settings.set_setting("mytextcolor", aliasInfo[1])
+            self.set_colors()
+        newline = ''
+        #following added by mDuo13
+        #########post_msg() - other##########
+        if not myself and not send:
+            for plugin_fname in self.activeplugins.keys():
+                plugin = self.activeplugins[plugin_fname]
+                try: s = plugin.post_msg(s, myself)
+                except Exception, e:
+                    if str(e) != "'module' object has no attribute 'post_msg'":
+                        logger.general(traceback.format_exc())
+                        logger.general("EXCEPTION: " + str(e))
+        #end mDuo13 added code
+        if myself:
+            name = "<b>" + display_name + "</b>: "
+            s = self.colorize(self.mytextcolor, s)
+        else: name = ""
+        if aliasInfo[1] != 'Default':
+            self.settings.set_setting("mytextcolor", defaultcolor)
+            self.set_colors()
+        #following line based on sourceforge patch #880403 from mDuo
+        # EDIT: Had to rework blank line check to handle malformed HTML throwing error.
+        #       this limits the effectiveness of this check -SD
+        lineHasText = 1
+        try: lineHasText = strip_html(s).replace("&nbsp;","").replace(" ","").strip()!=""
+        except:
+            # HTML parser has errored out (most likely). Being as all we are doing is
+            # scanning for empty/blank lines anyway there is no harm in letting a
             # troublesome message though. Worst case is a blank line to chat.
-            lineHasText = 1
+            lineHasText = 1
         if lineHasText:
-            #following added by mDuo13
-            if myself:
-                s2 = s
-                ########post_msg() - self #######
-                for plugin_fname in self.activeplugins.keys():
-                    plugin = self.activeplugins[plugin_fname]
-                    try:
-                        s2 = plugin.post_msg(s2, myself)
-                    except Exception, e:
-                        if str(e) != "'module' object has no attribute 'post_msg'":
-                            logger.general(traceback.format_exc())
-                            logger.general("EXCEPTION: " + str(e))
-                if s2 != "":
-                    #Italici the messages from tabbed whispers
-                    if self.type == WHISPER_TAB or self.type == GROUP_TAB or self.sendtarget == 'gm':
-                        s2 = s2 + '</i>'
-                        name = '<i>' + name
-                        if self.type == WHISPER_TAB: name += " (whispering): "
-                        elif self.type == GROUP_TAB: name += self.settings.get_setting("gwtext") + ' '
-                        elif self.sendtarget == 'gm': name += " (whispering to GM) "
-                    newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s2 + "</div>"
-                    log( self.settings, c, name+s2 )
-            else:
-                newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s + "</div>"
-                log( self.settings, c, name+s )
-        else: send = False
-        newline = chat_util.strip_unicode(newline)
+            #following added by mDuo13
+            if myself:
+                s2 = s
+                ########post_msg() - self #######
+                for plugin_fname in self.activeplugins.keys():
+                    plugin = self.activeplugins[plugin_fname]
+                    try:
+                        s2 = plugin.post_msg(s2, myself)
+                    except Exception, e:
+                        if str(e) != "'module' object has no attribute 'post_msg'":
+                            logger.general(traceback.format_exc())
+                            logger.general("EXCEPTION: " + str(e))
+                if s2 != "":
+                    #Italici the messages from tabbed whispers
+                    if self.type == WHISPER_TAB or self.type == GROUP_TAB or self.sendtarget == 'gm':
+                        s2 = s2 + '</i>'
+                        name = '<i>' + name
+                        if self.type == WHISPER_TAB: name += " (whispering): "
+                        elif self.type == GROUP_TAB: name += self.settings.get_setting("gwtext") + ' '
+                        elif self.sendtarget == 'gm': name += " (whispering to GM) "
+                    newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s2 + "</div>"
+                    log( self.settings, c, name+s2 )
+            else:
+                newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s + "</div>"
+                log( self.settings, c, name+s )
+        else: send = False
+        newline = chat_util.strip_unicode(newline)
         if self.lockscroll == 0:
-            self.chatwnd.AppendToPage(newline)
-            self.scroll_down()
-        else: self.storedata.append(newline)
+            self.chatwnd.AppendToPage(newline)
+            self.scroll_down()
+        else: self.storedata.append(newline)
         if send:
-            if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(s)
-            elif self.type == MAIN_TAB and self.sendtarget == "gm":
-                the_gms = self.get_gms()
-                self.whisper_to_players(s, the_gms)
-            elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
-                members = []
-                for pid in WG_LIST[self.sendtarget]: members.append(str(WG_LIST[self.sendtarget][pid]))
-                self.whisper_to_players(self.settings.get_setting("gwtext") + s, members)
-            elif self.type == WHISPER_TAB: self.whisper_to_players(s, [self.sendtarget])
-            elif self.type == NULL_TAB: pass
-            else: self.InfoPost("Failed to send message, unknown send type for this tab")
-        self.parsed=0
-
-    #
-    # TimeIndexString()
-    #
-    # time indexing for chat display only (don't log time indexing)
-    # added by Snowdog 4/04
-    
-    def TimeIndexString(self):
-        try:
-            mtime = ""
-            if self.settings.get_setting('Chat_Time_Indexing') == "0": pass
-            elif self.settings.get_setting('Chat_Time_Indexing') == "1":
-                mtime = time.strftime("[%I:%M:%S] ", time.localtime())
-            return mtime
-        except Exception, e:
-            logger.general(traceback.format_exc())
-            logger.general("EXCEPTION: " + str(e))
-            return "[ERROR]"
-
-    ####  Post with parsing dice ####
-    
-    def ParsePost(self, s, send=False, myself=False):
-        s = self.NormalizeParse(s)
-        self.set_colors()
-        self.Post(s,send,myself)
-    
-    def NormalizeParse(self, s):
-        for plugin_fname in self.activeplugins.keys():
-            plugin = self.activeplugins[plugin_fname]
-            try: s = plugin.pre_parse(s)
-            except Exception, e:
-                if str(e) != "'module' object has no attribute 'post_msg'":
-                    logger.general(traceback.format_exc())
-                    logger.general("EXCEPTION: " + str(e))
-        if self.parsed == 0:
-            s = self.ParseNode(s)
-            s = self.ParseDice(s)
-            s = self.ParseFilter(s)
-            self.parsed = 1
-        return s
-    
-    def ParseFilter(self, s):
-        s = self.GetFilteredText(s)
-        return s
-    
-    def ParseNode(self, s):
-        """Parses player input for embedded nodes rolls"""
-        cur_loc = 0
-        #[a-zA-Z0-9 _\-\.]
-        reg = re.compile("(!@(.*?)@!)")
-        matches = reg.findall(s)
-        for i in xrange(0,len(matches)):
-            newstr = self.ParseNode(self.resolve_nodes(matches[i][1]))
-            s = s.replace(matches[i][0], newstr, 1)
-        return s
-    
-    def ParseDice(self, s):
-        """Parses player input for embedded dice rolls"""
-        reg = re.compile("\[([^]]*?)\]")
-        matches = reg.findall(s)
-        for i in xrange(0,len(matches)):
-            newstr = self.PraseUnknowns(matches[i])
-            qmode = 0
-            newstr1 = newstr
-            if newstr[0].lower() == 'q':
-                newstr = newstr[1:]
+            if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(s)
+            elif self.type == MAIN_TAB and self.sendtarget == "gm":
+                the_gms = self.get_gms()
+                self.whisper_to_players(s, the_gms)
+            elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
+                members = []
+                for pid in WG_LIST[self.sendtarget]: members.append(str(WG_LIST[self.sendtarget][pid]))
+                self.whisper_to_players(self.settings.get_setting("gwtext") + s, members)
+            elif self.type == WHISPER_TAB: self.whisper_to_players(s, [self.sendtarget])
+            elif self.type == NULL_TAB: pass
+            else: self.InfoPost("Failed to send message, unknown send type for this tab")
+        self.parsed=0
+
+    #
+    # TimeIndexString()
+    #
+    # time indexing for chat display only (don't log time indexing)
+    # added by Snowdog 4/04
+    
+    def TimeIndexString(self):
+        try:
+            mtime = ""
+            if self.settings.get_setting('Chat_Time_Indexing') == "0": pass
+            elif self.settings.get_setting('Chat_Time_Indexing') == "1":
+                mtime = time.strftime("[%I:%M:%S] ", time.localtime())
+            return mtime
+        except Exception, e:
+            logger.general(traceback.format_exc())
+            logger.general("EXCEPTION: " + str(e))
+            return "[ERROR]"
+
+    ####  Post with parsing dice ####
+    
+    def ParsePost(self, s, send=False, myself=False):
+        s = self.NormalizeParse(s)
+        self.set_colors()
+        self.Post(s,send,myself)
+    
+    def NormalizeParse(self, s):
+        for plugin_fname in self.activeplugins.keys():
+            plugin = self.activeplugins[plugin_fname]
+            try: s = plugin.pre_parse(s)
+            except Exception, e:
+                if str(e) != "'module' object has no attribute 'post_msg'":
+                    logger.general(traceback.format_exc())
+                    logger.general("EXCEPTION: " + str(e))
+        if self.parsed == 0:
+            s = self.ParseNode(s)
+            s = self.ParseDice(s)
+            s = self.ParseFilter(s)
+            self.parsed = 1
+        return s
+    
+    def ParseFilter(self, s):
+        s = self.GetFilteredText(s)
+        return s
+    
+    def ParseNode(self, s):
+        """Parses player input for embedded nodes rolls"""
+        cur_loc = 0
+        #[a-zA-Z0-9 _\-\.]
+        reg = re.compile("(!@(.*?)@!)")
+        matches = reg.findall(s)
+        for i in xrange(0,len(matches)):
+            newstr = self.ParseNode(self.resolve_nodes(matches[i][1]))
+            s = s.replace(matches[i][0], newstr, 1)
+        return s
+    
+    def ParseDice(self, s):
+        """Parses player input for embedded dice rolls"""
+        reg = re.compile("\[([^]]*?)\]")
+        matches = reg.findall(s)
+        for i in xrange(0,len(matches)):
+            newstr = self.PraseUnknowns(matches[i])
+            qmode = 0
+            newstr1 = newstr
+            if newstr[0].lower() == 'q':
+                newstr = newstr[1:]
                 qmode = 1
-            if newstr[0].lower() == '#':
-                newstr = newstr[1:]
-                qmode = 2
-            try: newstr = component.get('DiceManager').proccessRoll(newstr)
-            except: pass
-            if qmode == 1:
+            if newstr[0].lower() == '#':
+                newstr = newstr[1:]
+                qmode = 2
+            try: newstr = component.get('DiceManager').proccessRoll(newstr)
+            except: pass
+            if qmode == 1:
                 s = s.replace("[" + matches[i] + "]", "<!-- Official Roll [" + newstr1 + "] => " + newstr + "-->" + newstr, 1)
             elif qmode == 2:
-                s = s.replace("[" + matches[i] + "]", newstr[len(newstr)-2:-1], 1)
-            else: s = s.replace("[" + matches[i] + "]", "[" + newstr1 + "<!-- Official Roll -->] => " + newstr, 1)
-        return s
-    
-    def PraseUnknowns(self, s):
-	# Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY
-        newstr = "0"
-        reg = re.compile("(\?\{*)([a-zA-Z ]*)(\}*)")
-        matches = reg.findall(s)
-        for i in xrange(0,len(matches)):
-            lb = "Replace '?' with: "
-            if len(matches[i][0]):
-                lb = matches[i][1] + "?: "
-            dlg = wx.TextEntryDialog(self, lb, "Missing Value?")
-            dlg.SetValue('')
-            if matches[i][0] != '':
-                dlg.SetTitle("Enter Value for " + matches[i][1])
-            if dlg.ShowModal() == wx.ID_OK: newstr = dlg.GetValue()
-            if newstr == '': newstr = '0'
-            s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1)
-            dlg.Destroy()
-        return s
-
-    # This subroutine builds a chat display name.
-    #
-    def chat_display_name(self, player):
-        if self.settings.get_setting("ShowIDInChat") == "0":
-            display_name = player[0]
-        else:
-            display_name = "("+player[2]+") " + player[0]
-        return display_name
-
-    # This subroutine will get a hex color and return it, or return nothing
-    #
-    def get_color(self):
-        data = wx.ColourData()
-        data.SetChooseFull(True)
-        dlg = wx.ColourDialog(self, data)
-        if dlg.ShowModal() == wx.ID_OK:
-            data = dlg.GetColourData()
-            (red,green,blue) = data.GetColour().Get()
-            hexcolor = self.r_h.hexstring(red, green, blue)
-            dlg.Destroy()
-            return hexcolor
-        else:
-            dlg.Destroy()
-            return None
-    # def get_color - end
-
-    def replace_quotes(self, s):
-        in_tag = 0
-        i = 0
-        rs = s[:]
-        for c in s:
-            if c == "<":
-                in_tag += 1
-            elif c == ">":
-                if in_tag:
-                    in_tag -= 1
-            elif c == '"':
-                if in_tag:
-                    rs = rs[:i] + "'" + rs[i+1:]
-            i += 1
-        return rs
-
-    def resolve_loop(self, node, path, step, depth):
-        if step == depth:
-            self.resolution(node)
-        else:
-            child_list = node.findall('nodehandler')
-            for child in child_list:
-                if step == depth: break
-                if child.get('name') == path[step]:
+                s = s.replace("[" + matches[i] + "]", newstr[len(newstr)-2:-1], 1)
+            else: s = s.replace("[" + matches[i] + "]", "[" + newstr1 + "<!-- Official Roll -->] => " + newstr, 1)
+        return s
+    
+    def PraseUnknowns(self, s):
+	# Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY
+        newstr = "0"
+        reg = re.compile("(\?\{*)([a-zA-Z ]*)(\}*)")
+        matches = reg.findall(s)
+        for i in xrange(0,len(matches)):
+            lb = "Replace '?' with: "
+            if len(matches[i][0]):
+                lb = matches[i][1] + "?: "
+            dlg = wx.TextEntryDialog(self, lb, "Missing Value?")
+            dlg.SetValue('')
+            if matches[i][0] != '':
+                dlg.SetTitle("Enter Value for " + matches[i][1])
+            if dlg.ShowModal() == wx.ID_OK: newstr = dlg.GetValue()
+            if newstr == '': newstr = '0'
+            s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1)
+            dlg.Destroy()
+        return s
+
+    # This subroutine builds a chat display name.
+    #
+    def chat_display_name(self, player):
+        if self.settings.get_setting("ShowIDInChat") == "0":
+            display_name = player[0]
+        else:
+            display_name = "("+player[2]+") " + player[0]
+        return display_name
+
+    # This subroutine will get a hex color and return it, or return nothing
+    #
+    def get_color(self):
+        data = wx.ColourData()
+        data.SetChooseFull(True)
+        dlg = wx.ColourDialog(self, data)
+        if dlg.ShowModal() == wx.ID_OK:
+            data = dlg.GetColourData()
+            (red,green,blue) = data.GetColour().Get()
+            hexcolor = self.r_h.hexstring(red, green, blue)
+            dlg.Destroy()
+            return hexcolor
+        else:
+            dlg.Destroy()
+            return None
+    # def get_color - end
+
+    def replace_quotes(self, s):
+        in_tag = 0
+        i = 0
+        rs = s[:]
+        for c in s:
+            if c == "<":
+                in_tag += 1
+            elif c == ">":
+                if in_tag:
+                    in_tag -= 1
+            elif c == '"':
+                if in_tag:
+                    rs = rs[:i] + "'" + rs[i+1:]
+            i += 1
+        return rs
+
+    def resolve_loop(self, node, path, step, depth):
+        if step == depth:
+            self.resolution(node)
+        else:
+            child_list = node.findall('nodehandler')
+            for child in child_list:
+                if step == depth: break
+                if child.get('name') == path[step]:
                     node = child
                     step += 1
                     if node.get('class') in ('dnd35char_handler', "SWd20char_handler", "d20char_handler", "dnd3echar_handler"): self.resolve_cust_loop(node, path, step, depth)
-                    elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, step, depth)
+                    elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, step, depth)
                     else: self.resolve_loop(node, path, step, depth)
 
 
@@ -1964,38 +1964,38 @@
             elif path[step+1].title() == 'Mod': self.data = pc_stats[path[step].title()][1]
             elif path[step+1].title() == 'Check': self.data = '<b>'+path[step].title()+' Check:</b> [1d20+'+str(pc_stats[path[step].title()][1])+']'
             return
-
-    def resolution(self, node):
-        if self.passed == False:
-            self.passed = True
-            if node.get('class') == 'textctrl_handler': self.data = str(node.find('text').text)
-            else: self.data = 'Nodehandler for '+ node.get('class') + ' not done!' or 'Invalid Reference!'
-        else:
-            self.data = ''
+
+    def resolution(self, node):
+        if self.passed == False:
+            self.passed = True
+            if node.get('class') == 'textctrl_handler': self.data = str(node.find('text').text)
+            else: self.data = 'Nodehandler for '+ node.get('class') + ' not done!' or 'Invalid Reference!'
+        else:
+            self.data = ''
             pass
         self.data = self.ParseMap(self.data, node)
 
-    def ParseMap(self, s, node):
-        """Parses player input for embedded nodes rolls"""
-        cur_loc = 0
-        reg = re.compile("(!!(.*?)!!)")
+    def ParseMap(self, s, node):
+        """Parses player input for embedded nodes rolls"""
+        cur_loc = 0
+        reg = re.compile("(!!(.*?)!!)")
         matches = reg.findall(s)
-        for i in xrange(0,len(matches)):
-            newstr = txt = '!@' + node.get('map') + '::' + matches[i][1] + '@!'
+        for i in xrange(0,len(matches)):
+            newstr = txt = '!@' + node.get('map') + '::' + matches[i][1] + '@!'
             s = s.replace(matches[i][0], newstr, 1)
-            s = self.ParseNode(s)
-        return s
-
-    def resolve_nodes(self, s):
-        self.passed = False
-        self.data = 'Invalid Reference!'
-        value = ""
-        path = s.split('::')
-        depth = len(path)
+            s = self.ParseNode(s)
+        return s
+
+    def resolve_nodes(self, s):
+        self.passed = False
+        self.data = 'Invalid Reference!'
+        value = ""
+        path = s.split('::')
+        depth = len(path)
         self.gametree = component.get('tree')
         try: node = self.gametree.tree_map[path[0]]['node']
         except: return self.data
         if node.get('class') in ('dnd35char_handler', "SWd20char_handler", "d20char_handler", "dnd3echar_handler"): self.resolve_cust_loop(node, path, 1, depth)
-        elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, 1, depth)
+        elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, 1, depth)
         else: self.resolve_loop(node, path, 1, depth)
-        return self.data
+        return self.data