diff orpg/chat/chatwnd.py @ 0:4385a7d0efd1 grumpy-goblin

Deleted and repushed it with the 'grumpy-goblin' branch. I forgot a y
author sirebral
date Tue, 14 Jul 2009 16:41:58 -0500
parents
children 5aff3ef1ae46
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/orpg/chat/chatwnd.py	Tue Jul 14 16:41:58 2009 -0500
@@ -0,0 +1,2140 @@
+# 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
+##
+from orpg.orpg_windows import *
+from orpg.player_list import WG_LIST
+import orpg.dirpath
+import orpg.tools.rgbhex
+import orpg.tools.inputValidator
+from orpg.tools.metamenus import MenuEx
+from orpg.orpgCore import open_rpg
+import webbrowser
+from string import *
+from orpg.orpg_version import VERSION
+import commands
+import chat_msg
+import time
+import orpg.tools.predTextCtrl
+from orpg.networking.mplay_client import MPLAY_CONNECTED  # needed to only send typing/not_typing messages while connected
+import os
+import time
+import re
+import sys
+import cStringIO # for reading inline imagedata as a stream
+from HTMLParser import HTMLParser
+import chat_util
+import traceback
+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, 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( orpg.dirpath.dir_struct["user"] + filename, 'a' )
+            f.write( '%s%s<br />\n' % ( header, text ) )
+            f.close()
+        except:
+            print "could not open " + orpg.dirpath.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.Open()
+        wx.TheClipboard.Clear()
+        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)
+
+        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):
+            self.Copy()
+
+        #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):
+        self.log = open_rpg.get_component("log")
+        self.log.log("Enter chat_notebook", ORPG_DEBUG)
+        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 = open_rpg.get_component("settings")
+        self.whisper_tabs = []
+        self.group_tabs = []
+        self.null_tabs = []
+        self.il = wx.ImageList(16, 16)
+        bmp = wx.Bitmap(orpg.dirpath.dir_struct["icon"]+'player.gif')
+        self.il.Add(bmp)
+        bmp = wx.Bitmap(orpg.dirpath.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)
+        self.log.log("Exit chat_notebook", ORPG_DEBUG)
+
+    def get_tab_index(self, chatpanel):
+        "Return the index of a chatpanel in the wxNotebook."
+        self.log.log("Enter chat_notebook->get_tab_index(self, chatpanel)", ORPG_DEBUG)
+
+        for i in xrange(self.GetPageCount()):
+            if (self.GetPage(i) == chatpanel):
+                self.log.log("Exit chat_notebook->get_tab_index(self, chatpanel)", ORPG_DEBUG)
+                return i
+
+    def create_gm_tab(self):
+        self.log.log("Enter chat_notebook->create_gm_tab(self)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_notebook->create_gm_tab(self)", ORPG_DEBUG)
+
+    def create_whisper_tab(self, playerid):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        self.log.log("Enter chat_notebook->create_whisper_tab(self," + str(playerid) +")", ORPG_DEBUG)
+        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 = open_rpg.get_component('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        self.log.log("Exit chat_notebook->create_whisper_tab(self, playerid)", ORPG_DEBUG)
+        return private_tab
+
+    def create_group_tab(self, group_name):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        self.log.log("Enter chat_notebook->create_group_tab(self, group_name)", ORPG_DEBUG)
+        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 = open_rpg.get_component('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        self.log.log("Exit chat_notebook->create_group_tab(self, group_name)", ORPG_DEBUG)
+        return private_tab
+
+    def create_null_tab(self, tab_name):
+        "Add a new chatpanel directly connected to integer 'playerid' via whispering."
+        self.log.log("Enter chat_notebook->create_null_tab(self, tab_name)", ORPG_DEBUG)
+        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 = open_rpg.get_component('alias')
+        wx.CallAfter(self.AliasLib.RefreshAliases)
+        self.log.log("Exit chat_notebook->create_null_tab(self, tab_name)", ORPG_DEBUG)
+        return private_tab
+
+    def onCloseTab(self, evt):
+        self.log.log("Enter chat_notebook->onCloseTab(self, evt)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_notebook->onCloseTab(self, evt)", ORPG_DEBUG)
+
+    def newMsg(self, tabid):
+        self.log.log("Enter chat_notebook->newMsg(self, tabid)", ORPG_DEBUG)
+        if tabid != self.GetSelection():
+            self.SetPageImage(tabid, 0)
+        self.log.log("Exit chat_notebook->newMsg(self, tabid)", ORPG_DEBUG)
+
+    def onPageChanging(self, event):
+        """When private chattabs are selected, set the bitmap back to 'normal'."""
+        self.log.log("Enter chat_notebook->onPageChanging(self, event)", ORPG_DEBUG)
+        event.Skip()
+        self.log.log("Exit chat_notebook->onPageChanging(self, event)", ORPG_DEBUG)
+
+    def onPageChanged(self, event):
+        """When private chattabs are selected, set the bitmap back to 'normal'."""
+        self.log.log("Enter chat_notebook->onPageChanged(self, event)", ORPG_DEBUG)
+        selected_idx = event.GetSelection()
+        self.SetPageImage(selected_idx, 1)
+        page = self.GetPage(selected_idx)
+        #wx.CallAfter(page.set_chat_text_focus, 0)
+        event.Skip()
+        self.log.log("Exit chat_notebook->onPageChanged(self, event)", ORPG_DEBUG)
+
+# 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):
+        self.log = open_rpg.get_component("log")
+        self.log.log("Enter chat_panel", ORPG_DEBUG)
+        wx.Panel.__init__(self, parent, id)
+        self.session = open_rpg.get_component('session')
+        self.settings = open_rpg.get_component('settings')
+        self.activeplugins = open_rpg.get_component('plugins')
+        self.parent = parent
+        # who receives outbound messages, either "all" or "playerid" string
+        self.sendtarget = sendtarget
+        self.type = tab_type
+        self.sound_player = open_rpg.get_component('sound')
+        # create die roller manager
+        self.DiceManager = open_rpg.get_component('DiceManager')
+        # 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.build_ctrls()
+        #openrpg dir
+        self.root_dir = orpg.dirpath.dir_struct["home"]
+        # html font/fontsize is global to all the notebook tabs.
+        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()
+        self.log.log("Exit chat_panel", ORPG_DEBUG)
+
+    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."""
+        self.log.log("Enter chat_panel->set_default_font(self, fontname=None, fontsize=None)", ORPG_DEBUG)
+        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
+        self.log.log("Exit chat_panel->set_default_font(self, fontname=None, fontsize=None)", ORPG_DEBUG)
+        return (self.font, self.fontsize)
+
+    def build_menu(self):
+        self.log.log("Enter chat_panel->build_menu(self)", ORPG_DEBUG)
+        top_frame = open_rpg.get_component('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')
+        self.log.log("Exit chat_panel->build_menu(self)", ORPG_DEBUG)
+
+    ## Settings Menu Events
+    def OnMB_ShowImages(self, event):
+        self.log.log("Enter chat_panel->OnMB_ShowImages(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("Show_Images_In_Chat", '1')
+        else:
+            self.settings.set_setting("Show_Images_In_Chat", '0')
+        self.log.log("Exit chat_panel->OnMB_ShowImages(self, event)", ORPG_DEBUG)
+
+    def OnMB_StripHTML(self, event):
+        self.log.log("Enter chat_panel->OnMB_StripHTML(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("Sstriphtml", '1')
+        else:
+            self.settings.set_setting("striphtml", '0')
+        self.log.log("Exit chat_panel->OnMB_StripHTML(self, event)", ORPG_DEBUG)
+
+    def OnMB_ChatTimeIndex(self, event):
+        self.log.log("Enter chat_panel->OnMB_ChatTimeIndex(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("Chat_Time_Indexing", '1')
+        else:
+            self.settings.set_setting("Chat_Time_Indexing", '0')
+        self.log.log("Exit chat_panel->OnMB_ChatTimeIndex(self, event)", ORPG_DEBUG)
+
+    def OnMB_ChatAutoComplete(self, event):
+        self.log.log("Enter chat_panel->OnMB_ChatAutoComplete(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("SuppressChatAutoComplete", '0')
+        else:
+            self.settings.set_setting("SuppressChatAutoComplete", '1')
+        self.log.log("Exit chat_panel->OnMB_ChatAutoComplete(self, event)", ORPG_DEBUG)
+
+    def OnMB_ShowIDinChat(self, event):
+        self.log.log("Enter chat_panel->OnMB_ShowIDinChat(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("ShowIDInChat", '1')
+        else:
+            self.settings.set_setting("ShowIDInChat", '0')
+        self.log.log("Exit chat_panel->OnMB_ShowIDinChat(self, event)", ORPG_DEBUG)
+
+    def OnMB_LogTimeIndex(self, event):
+        self.log.log("Enter chat_panel->OnMB_LogTimeIndex(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("TimeStampGameLog", '1')
+        else:
+            self.settings.set_setting("TimeStampGameLog", '0')
+        self.log.log("Exit chat_panel->OnMB_LogTimeIndex(self, event)", ORPG_DEBUG)
+
+    def OnMB_TabbedWhispers(self, event):
+        self.log.log("Enter chat_panel->OnMB_TabbedWhispers(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("tabbedwhispers", '1')
+        else:
+            self.settings.set_setting("tabbedwhispers", '0')
+        self.log.log("Exit chat_panel->OnMB_TabbedWhispers(self, event)", ORPG_DEBUG)
+
+    def OnMB_GMTab(self, event):
+        self.log.log("Enter chat_panel->OnMB_GMTab(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("GMWhisperTab", '1')
+            self.parent.create_gm_tab()
+        else:
+            self.settings.set_setting("GMWhisperTab", '0')
+        self.log.log("Exit chat_panel->OnMB_GMTab(self, event)", ORPG_DEBUG)
+
+    def OnMB_GroupWhisperTabs(self, event):
+        self.log.log("Enter chat_panel->OnMB_GroupWhisperTabs(self, event)", ORPG_DEBUG)
+        if event.IsChecked():
+            self.settings.set_setting("GroupWhisperTab", '1')
+        else:
+            self.settings.set_setting("GroupWhisperTab", '0')
+        self.log.log("Exit chat_panel->OnMB_GroupWhisperTabs(self, event)", ORPG_DEBUG)
+
+
+    def OnMB_DiceBar(self, event):
+        self.log.log("Enter chat_panel->OnMB_DiceBar(self, event)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_panel->OnMB_DiceBar(self, event)", ORPG_DEBUG)
+
+    def OnMB_FormatButtons(self, event):
+        self.log.log("Enter chat_panel->OnMB_FormatButtons(self, event)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_panel->OnMB_FormatButtons(self, event)", ORPG_DEBUG)
+
+    def OnMB_AliasTool(self, event):
+        self.log.log("Enter chat_panel->OnMB_AliasTool(self, event)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_panel->OnMB_AliasTool(self, event)", ORPG_DEBUG)
+
+    def OnMB_BackgroundColor(self, event):
+        self.log.log("Enter chat_panel->OnMB_BackgroundColor(self, event)", ORPG_DEBUG)
+        top_frame = open_rpg.get_component('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()
+        self.log.log("Exit chat_panel->OnMB_BackgroundColor(self, event)", ORPG_DEBUG)
+
+    def OnMB_TextColor(self, event):
+        self.log.log("Enter chat_panel->OnMB_TextColor(self, event)", ORPG_DEBUG)
+        top_frame = open_rpg.get_component('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()
+        self.log.log("Exit chat_panel->OnMB_TextColor(self, event)", ORPG_DEBUG)
+
+
+    def get_hot_keys(self):
+        self.log.log("Enter chat_panel->get_hot_keys(self)", ORPG_DEBUG)
+        # 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))
+        self.log.log("Enter chat_panel->get_hot_keys(self)", ORPG_DEBUG)
+        return entries
+
+    def forward_tabs(self, evt):
+        self.log.log("Enter chat_panel->swap_tabs(self, evt)", ORPG_DEBUG)
+        self.parent.AdvanceSelection()
+        self.log.log("Exit chat_panel->swap_tabs(self, evt)", ORPG_DEBUG)
+
+    def back_tabs(self, evt):
+        self.log.log("Enter chat_panel->swap_tabs(self, evt)", ORPG_DEBUG)
+        self.parent.AdvanceSelection(False)
+        self.log.log("Exit chat_panel->swap_tabs(self, evt)", ORPG_DEBUG)
+
+    # This subroutine builds the controls for the chat frame
+    #
+    # !self : instance of self
+    def build_ctrls(self):
+        self.log.log("Enter chat_panel->build_ctrls(self)", ORPG_DEBUG)
+        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,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.textCopy)
+        self.log.log("Exit chat_panel->build_ctrls(self)", ORPG_DEBUG)
+    # def build_ctrls - end
+
+    def textCopy(self, event):
+        if self.chattxt.GetStringSelection() == '':
+            self.chatwnd.OnM_EditCopy(None)
+        else:
+            self.chatwnd.Copy()
+
+    def build_bar(self):
+        self.log.log("Enter chat_panel->build_bar(self)", ORPG_DEBUG)
+        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()
+        self.log.log("Exit chat_panel->build_bar(self)", ORPG_DEBUG)
+
+    def build_scroll(self):
+        self.log.log("Enter chat_panel->build_scroll(self)", ORPG_DEBUG)
+        self.scroll_lock = wx.Button( self, wx.ID_ANY, "Scroll ON",size= wx.Size(80,25))
+        self.log.log("Exit chat_panel->build_scroll(self)", ORPG_DEBUG)
+
+    def build_alias(self):
+        self.log.log("Enter chat_panel->build_alias(self)", ORPG_DEBUG)
+        self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName])
+        self.aliasButton = createMaskedButton( self, orpg.dirpath.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, orpg.dirpath.dir_struct["icon"] + 'add_filter.gif', 'Refresh list of filters from Game Tree', wx.ID_ANY, '#bdbdbd' )
+        self.filterList.SetSelection(0)
+        self.toolbar_sizer.Add( self.aliasButton, 0, wx.EXPAND )
+        self.toolbar_sizer.Add( self.aliasList,0,wx.EXPAND)
+        self.toolbar_sizer.Add( self.filterButton, 0, wx.EXPAND )
+        self.toolbar_sizer.Add( self.filterList,0,wx.EXPAND)
+        if self.settings.get_setting('AliasTool_On') == '0':
+            self.toggle_alias('0')
+        else:
+            self.toggle_alias('1')
+        self.log.log("Exit chat_panel->build_alias(self)", ORPG_DEBUG)
+
+    def toggle_alias(self, act):
+        self.log.log("Enter chat_panel->toggle_alias(self, " + str(act) + ")", ORPG_DEBUG)
+        if act == '0':
+            self.toolbar_sizer.Show(self.aliasList, False)
+            self.toolbar_sizer.Show(self.filterList, False)
+            self.toolbar_sizer.Show(self.aliasButton, False)
+            self.toolbar_sizer.Show(self.filterButton, False)
+            self.toolbar_sizer.Layout()
+        else:
+            self.toolbar_sizer.Show(self.aliasList, True)
+            self.toolbar_sizer.Show(self.filterList, True)
+            self.toolbar_sizer.Show(self.aliasButton, True)
+            self.toolbar_sizer.Show(self.filterButton, True)
+            self.toolbar_sizer.Layout()
+        self.log.log("Exit chat_panel->toggle_alias(self, act)", ORPG_DEBUG)
+
+    def build_text(self):
+        self.log.log("Enter chat_panel->build_text(self)", ORPG_DEBUG)
+        self.textpop_lock = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', wx.ID_ANY, '#bdbdbd')
+        self.log.log("Exit chat_panel->build_text(self)", ORPG_DEBUG)
+
+    def build_dice(self):
+        self.log.log("Enter chat_panel->build_dice(self)", ORPG_DEBUG)
+        self.numDieText = wx.TextCtrl( self, wx.ID_ANY, "1", 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, orpg.dirpath.dir_struct["icon"]+'b_d4.gif', 'Roll d4', wx.ID_ANY, '#bdbdbd')
+        self.d6Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d6.gif', 'Roll d6', wx.ID_ANY, '#bdbdbd')
+        self.d8Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d8.gif', 'Roll d8', wx.ID_ANY, '#bdbdbd')
+        self.d10Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d10.gif', 'Roll d10', wx.ID_ANY, '#bdbdbd')
+        self.d12Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d12.gif', 'Roll d12', wx.ID_ANY, '#bdbdbd')
+        self.d20Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d20.gif', 'Roll d20', wx.ID_ANY, '#bdbdbd')
+        self.d100Button = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d100.gif', 'Roll d100', wx.ID_ANY, '#bdbdbd')
+        self.toolbar_sizer.Add( self.numDieText, 0, wx.ALIGN_CENTER | wx.EXPAND)
+        self.toolbar_sizer.Add( self.d4Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d6Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d8Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d10Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d12Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d20Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.d100Button, 0 ,wx.EXPAND)
+        self.toolbar_sizer.Add( self.dieModText, 0, wx.ALIGN_CENTER, 5 )
+        if self.settings.get_setting('DiceButtons_On') == '0':
+            self.toggle_dice('0')
+        else:
+            self.toggle_dice('1')
+        self.log.log("Exit chat_panel->build_dice(self)", ORPG_DEBUG)
+
+    def toggle_dice(self, act):
+        self.log.log("Enter chat_panel->toggle_dice(self, "+ str(act) + ")", ORPG_DEBUG)
+        if act == '0':
+            self.toolbar_sizer.Show(self.numDieText, False)
+            self.toolbar_sizer.Show(self.d4Button, False)
+            self.toolbar_sizer.Show(self.d6Button, False)
+            self.toolbar_sizer.Show(self.d8Button, False)
+            self.toolbar_sizer.Show(self.d10Button, False)
+            self.toolbar_sizer.Show(self.d12Button, False)
+            self.toolbar_sizer.Show(self.d20Button, False)
+            self.toolbar_sizer.Show(self.d100Button, False)
+            self.toolbar_sizer.Show(self.dieModText, False)
+            self.toolbar_sizer.Layout()
+        else:
+            self.toolbar_sizer.Show(self.numDieText, True)
+            self.toolbar_sizer.Show(self.d4Button, True)
+            self.toolbar_sizer.Show(self.d6Button, True)
+            self.toolbar_sizer.Show(self.d8Button, True)
+            self.toolbar_sizer.Show(self.d10Button, True)
+            self.toolbar_sizer.Show(self.d12Button, True)
+            self.toolbar_sizer.Show(self.d20Button, True)
+            self.toolbar_sizer.Show(self.d100Button, True)
+            self.toolbar_sizer.Show(self.dieModText, True)
+            self.toolbar_sizer.Layout()
+        self.log.log("Exit chat_panel->toggle_dice(self, "+ str(act) + ")", ORPG_DEBUG)
+
+    def build_formating(self):
+        self.log.log("Enter chat_panel->build_formating(self)", ORPG_DEBUG)
+        self.boldButton = createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'bold.gif', 'Make the selected text Bold', wx.ID_ANY, '#bdbdbd')
+        self.italicButton = createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'italic.gif', 'Italicize the selected text', wx.ID_ANY, '#bdbdbd' )
+        self.underlineButton = createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'underlined.gif', 'Underline the selected text', wx.ID_ANY, '#bdbdbd' )
+        self.toolbar_sizer.Add( self.boldButton, 0, wx.EXPAND )
+        self.toolbar_sizer.Add( self.italicButton, 0, wx.EXPAND )
+        self.toolbar_sizer.Add( self.underlineButton, 0, wx.EXPAND )
+        if self.settings.get_setting('FormattingButtons_On') == '0':
+            self.toggle_formating('0')
+        else:
+            self.toggle_formating('1')
+        self.log.log("Exit chat_panel->build_formating(self)", ORPG_DEBUG)
+
+    def toggle_formating(self, act):
+        self.log.log("Enter chat_panel->toggle_formating(self, " + str(act) + ")", ORPG_DEBUG)
+        if act == '0':
+            self.toolbar_sizer.Show(self.boldButton, False)
+            self.toolbar_sizer.Show(self.italicButton, False)
+            self.toolbar_sizer.Show(self.underlineButton, False)
+            self.toolbar_sizer.Layout()
+        else:
+            self.toolbar_sizer.Show(self.boldButton, True)
+            self.toolbar_sizer.Show(self.italicButton, True)
+            self.toolbar_sizer.Show(self.underlineButton, True)
+            self.toolbar_sizer.Layout()
+        self.log.log("Exit chat_panel->toggle_formating(self, " + str(act) + ")", ORPG_DEBUG)
+
+    # Heroman - Ideally, we would use static labels...
+    def build_colorbutton(self):
+        self.log.log("Enter chat_panel->build_colorbutton(self)", ORPG_DEBUG)
+        self.color_button = wx.Button(self, wx.ID_ANY, "C",wx.Point(0,0), wx.Size(22,0))
+        self.saveButton = createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'save.bmp', 'Save the chatbuffer', wx.ID_ANY, '#c0c0c0', wx.BITMAP_TYPE_BMP )
+        self.color_button.SetBackgroundColour(wx.BLACK)
+        self.color_button.SetForegroundColour(wx.WHITE)
+        self.toolbar_sizer.Add(self.color_button, 0, wx.EXPAND)
+        self.toolbar_sizer.Add( self.saveButton, 0, wx.EXPAND )
+        self.log.log("Exit chat_panel->build_colorbutton(self)", ORPG_DEBUG)
+
+    def OnMotion(self, evt):
+        self.log.log("Enter chat_panel->OnMotion(self, evt)", ORPG_DEBUG)
+        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:
+            self.log.log("Error, self.chatwnd.GetInternalRepresentation() return None", ORPG_GENERAL)
+        evt.Skip()
+        self.log.log("Exit chat_panel->OnMotion(self, evt)", ORPG_DEBUG)
+
+    #  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):
+        self.log.log("Enter chat_panel->myKeyHook(self, event)", ORPG_DEBUG)
+        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':
+            self.log.log("Exit chat_panel->myKeyHook(self, event) return 1", ORPG_DEBUG)
+            return 1
+        else:
+            self.log.log("Exit chat_panel->myKeyHook(self, event) return 0", ORPG_DEBUG)
+            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'":
+                    self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                    self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+        #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):
+        self.log.log("Enter chat_panel->sendTyping(self, typing)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_panel->sendTyping(self, typing)", ORPG_DEBUG)
+
+    # This subroutine sets the colors of the chat based on the settings in the
+    # self instance.
+    #
+    # !self : instance of self
+    def set_colors(self):
+        self.log.log("Enter chat_panel->set_colors(self)", ORPG_DEBUG)
+        # 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')
+        self.log.log("Exit chat_panel->set_colors(self)", ORPG_DEBUG)
+    # 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.log.log("Enter chat_panel->set_chat_text(self, txt)", ORPG_DEBUG)
+        self.chattxt.SetValue(txt)
+        self.chattxt.SetFocus()
+        self.chattxt.SetInsertionPointEnd()
+        self.log.log("Exit chat_panel->set_chat_text(self, txt)", ORPG_DEBUG)
+    # def set_chat_text - end
+
+    def get_chat_text(self):
+        self.log.log("Enter chat_panel->get_chat_text(self)", ORPG_DEBUG)
+        self.log.log("Enter chat_panel->get_chat_text(self)", ORPG_DEBUG)
+        return self.chattxt.GetValue()
+
+    # This subroutine sets the focus to the chat window
+    def set_chat_text_focus(self, event):
+        self.log.log("Enter chat_panel->set_chat_text_focus(self, event)", ORPG_DEBUG)
+        wx.CallAfter(self.chattxt.SetFocus)
+        self.log.log("Exit chat_panel->set_chat_text_focus(self, event)", ORPG_DEBUG)
+    # 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):
+        self.log.log("Enter chat_panel->OnChar(self, event)", ORPG_DEBUG)
+        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:
+            self.log.log("event.GetKeyCode() == wx.WXK_RETURN", ORPG_DEBUG)
+            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'}
+# Recycle Bin and Lambda should reduce this whole IF ELIF statement block.
+        bin_event = event.GetKeyCode()
+	if recycle_bin.has_key(bin_event):
+	    self.log.log(lambda bin_event: recycle_bin[bin_event], ORPG_DEBUG)
+	    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):
+            self.log.log("(event.GetKeyCode() == wx.WXK_RETURN and len(s)) or len(macroText)", ORPG_DEBUG)
+            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 != '':
+                self.sound_player.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:
+            self.log.log("event.GetKeyCode() == wx.WXK_UP", ORPG_DEBUG)
+            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:
+            self.log.log("event.GetKeyCode() == wx.WXK_DOWN", ORPG_DEBUG)
+            #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:
+            self.log.log("event.GetKeyCode() == wx.WXK_TAB", ORPG_DEBUG)
+            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):
+            self.log.log("event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP)", ORPG_DEBUG)
+            self.chatwnd.ScrollPages(-1)
+            if not self.lockscroll:
+                self.lock_scroll(0)
+
+        ## PAGE DOWN
+        elif event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN):
+            self.log.log("event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN)", ORPG_DEBUG)
+            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:
+            self.log.log("event.GetKeyCode() == wx.WXK_END", ORPG_DEBUG)
+            if self.lockscroll:
+                self.lock_scroll(0)
+                self.Post()
+            event.Skip()
+
+        ## NOTHING
+        else:
+            event.Skip()
+        self.log.log("Exit chat_panel->OnChar(self, event)", ORPG_DEBUG)
+    # def OnChar - end
+
+    def onDieRoll(self, evt):
+        """Roll the dice based on the button pressed and the die modifiers entered, if any."""
+        self.log.log("Enter chat_panel->onDieRoll(self, evt)", ORPG_DEBUG)
+        # 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()
+        self.log.log("Exit chat_panel->onDieRoll(self, evt)", ORPG_DEBUG)
+
+    # 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):
+        self.log.log("Enter chat_panel->on_chat_save(self, evt)", ORPG_DEBUG)
+        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(self.root_dir)
+        self.log.log("Exit chat_panel->on_chat_save(self, evt)", ORPG_DEBUG)
+    # 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):
+        self.log.log("Enter chat_panel->on_text_color(self, event)", ORPG_DEBUG)
+        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()
+        self.log.log("Exit chat_panel->on_text_color(self, event)", ORPG_DEBUG)
+    # 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"""
+        self.log.log("Enter chat_panel->colorize(self, color, text)", ORPG_DEBUG)
+        self.log.log("Exit chat_panel->colorize(self, color, text)", ORPG_DEBUG)
+        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):
+        self.log.log("Enter chat_panel->on_text_format(self, event)", ORPG_DEBUG)
+        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()
+        self.log.log("Exit chat_panel->on_text_format(self, event)", ORPG_DEBUG)
+    # def on_text_format - end
+
+    def lock_scroll(self, event):
+        self.log.log("Enter chat_panel->lock_scroll(self, event)", ORPG_DEBUG)
+        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")
+        self.log.log("Exit chat_panel->lock_scroll(self, event)", ORPG_DEBUG)
+
+    # 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"""
+        self.log.log("Enter chat_panel->pop_textpop(self, event)", ORPG_DEBUG)
+        h_buffertext = self.ResetPage()
+        h_dlg = orpgScrolledMessageFrameEditor(self, h_buffertext, "Text View of Chat Window", None, (500,300))
+        h_dlg.Show(True)
+        self.log.log("Exit chat_panel->pop_textpop(self, event)", ORPG_DEBUG)
+
+    # This subroutine will change the dimension of the window
+    #
+    # !self : instance of self
+    # !event :
+    def OnSize(self, event=None):
+        self.log.log("Enter chat_panel->OnSize(self, event=None)", ORPG_DEBUG)
+        event.Skip()
+        wx.CallAfter(self.scroll_down)
+        self.log.log("Exit chat_panel->OnSize(self, event=None)", ORPG_DEBUG)
+    # 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.log.log("Enter chat_panel->system_message(self, text)", ORPG_DEBUG)
+        self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE)
+        self.SystemPost(text)
+        self.log.log("Exit chat_panel->system_message(self, text)", ORPG_DEBUG)
+
+    def info_message(self, text):
+        self.log.log("Enter chat_panel->info_message(self, text)", ORPG_DEBUG)
+        self.send_chat_message(text,chat_msg.INFO_MESSAGE)
+        self.InfoPost(text)
+        self.log.log("Exit chat_panel->info_message(self, text)", ORPG_DEBUG)
+
+    def get_gms(self):
+        self.log.log("Enter chat_panel->get_gms(self)", ORPG_DEBUG)
+        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]
+        self.log.log("Exit chat_panel->get_gms(self)", ORPG_DEBUG)
+        return the_gms
+
+    def GetName(self):
+        self.log.log("Enter chat_panel->GetName(self)", ORPG_DEBUG)
+        self.AliasLib = open_rpg.get_component('alias')
+        player = self.session.get_my_info()
+        if self.AliasLib != None:
+            self.AliasLib.alias = self.aliasList.GetStringSelection();
+            if self.AliasLib.alias[0] != self.defaultAliasName:
+                self.log.log("Exit chat_panel->GetName(self)", ORPG_DEBUG)
+                return [self.chat_display_name([self.AliasLib.alias[0], player[1], player[2]]), self.AliasLib.alias[1]]
+        self.log.log("Exit chat_panel->GetName(self)", ORPG_DEBUG)
+        return [self.chat_display_name(player), "Default"]
+
+    def GetFilteredText(self, text):
+        self.log.log("Enter chat_panel->GetFilteredText(self, text)", ORPG_DEBUG)
+        advregex = re.compile('\"(.*?)\"', re.I)
+        self.AliasLib = open_rpg.get_component('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)
+        self.log.log("Exit chat_panel->GetFilteredText(self, text)", ORPG_DEBUG)
+        return text
+
+    def emote_message(self, text):
+        self.log.log("Enter chat_panel->emote_message(self, text)", ORPG_DEBUG)
+        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)
+        self.log.log("Exit chat_panel->emote_message(self, text)", ORPG_DEBUG)
+
+    def whisper_to_players(self, text, player_ids):
+        self.log.log("Enter chat_panel->whisper_to_players(self, text, player_ids)", ORPG_DEBUG)
+        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!")
+        self.log.log("Exit chat_panel->whisper_to_players(self, text, player_ids)", ORPG_DEBUG)
+
+    def send_chat_message(self, text, type=chat_msg.CHAT_MESSAGE, player_id="all"):
+        self.log.log("Enter chat_panel->send_chat_message(self, text, type, player_id)", ORPG_DEBUG)
+        #########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'":
+                    self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                    self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+        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
+        self.log.log("Exit chat_panel->send_chat_message(self, text, type, player_id)", ORPG_DEBUG)
+
+    #### incoming chat message handler #####
+    def post_incoming_msg(self, msg, player):
+        self.log.log("Enter chat_panel->post_incoming_msg(self, msg, player)", ORPG_DEBUG)
+
+        # 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'":
+                    self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                    self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+        #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:
+                self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                self.log.log("EXCEPTION: 'Error in posting whisper message': " + str(e), ORPG_GENERAL)
+        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 != '':
+            self.sound_player.play(sound_file)
+        self.log.log("Exit chat_panel->post_incoming_msg(self, msg, player)", ORPG_DEBUG)
+    #### Posting helpers #####
+
+    def InfoPost(self, s):
+        self.log.log("Enter chat_panel->InfoPost(self, s)", ORPG_DEBUG)
+        self.Post(self.colorize(self.infocolor, s))
+        self.log.log("Exit chat_panel->InfoPost(self, s)", ORPG_DEBUG)
+
+    def SystemPost(self, s):
+        self.log.log("Enter chat_panel->SystemPost(self, s)", ORPG_DEBUG)
+        self.Post(self.colorize(self.syscolor, s))
+        self.log.log("Exit chat_panel->SystemPost(self, s)", ORPG_DEBUG)
+
+    def EmotePost(self, s):
+        self.log.log("Enter chat_panel->EmotePost(self, s)", ORPG_DEBUG)
+        self.Post(self.colorize(self.emotecolor, s))
+        self.log.log("Exit chat_panel->EmotePost(self, s)", ORPG_DEBUG)
+
+    #### Standard Post method #####
+    def Post(self, s="", send=False, myself=False):
+        self.log.log("Enter chat_panel->Post(self, s, send, myself)", ORPG_DEBUG)
+        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'":
+                        self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                        self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+        #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
+        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'":
+                            self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                            self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+                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 = self.TimeIndexString() + name +  s2 + "<br />"
+                    log( self.settings, name + s2 )
+            else:
+                newline = self.TimeIndexString() + name +  s + "<br />"
+                log( self.settings, 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)
+        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
+        self.log.log("Exit chat_panel->Post(self, s, send, myself)", ORPG_DEBUG)
+
+    #
+    # TimeIndexString()
+    #
+    # time indexing for chat display only (don't log time indexing)
+    # added by Snowdog 4/04
+    def TimeIndexString(self):
+        self.log.log("Enter chat_panel->TimeIndexString(self)", ORPG_DEBUG)
+        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())
+            self.log.log("Exit chat_panel->TimeIndexString(self)", ORPG_DEBUG)
+            return mtime
+        except Exception, e:
+            self.log.log(traceback.format_exc(), ORPG_GENERAL)
+            self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+            return "[ERROR]"
+
+    ####  Post with parsing dice ####
+    def ParsePost(self, s, send=False, myself=False):
+        self.log.log("Enter chat_panel->ParsePost(self, s, send, myself)", ORPG_DEBUG)
+        s = self.NormalizeParse(s)
+        self.set_colors()
+        self.Post(s,send,myself)
+        self.log.log("Exit chat_panel->ParsePost(self, s, send, myself)", ORPG_DEBUG)
+
+    def NormalizeParse(self, s):
+        self.log.log("Enter chat_panel->NormalizeParse(self, s)", ORPG_DEBUG)
+        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'":
+                    self.log.log(traceback.format_exc(), ORPG_GENERAL)
+                    self.log.log("EXCEPTION: " + str(e), ORPG_GENERAL)
+        if self.parsed == 0:
+            s = self.ParseNode(s)
+            s = self.ParseDice(s)
+            s = self.ParseFilter(s)
+            self.parsed = 1
+        self.log.log("Exit chat_panel->NormalizeParse(self, s)", ORPG_DEBUG)
+        return s
+
+    def ParseFilter(self, s):
+        self.log.log("Enter chat_panel->ParseFilter(self, s)", ORPG_DEBUG)
+        s = self.GetFilteredText(s)
+        self.log.log("Exit chat_panel->ParseFilter(self, s)", ORPG_DEBUG)
+        return s
+
+    def ParseNode(self, s):
+        """Parses player input for embedded nodes rolls"""
+        self.log.log("Enter chat_panel->ParseNode(self, s)", ORPG_DEBUG)
+        cur_loc = 0
+        #[a-zA-Z0-9 _\-\.]
+        reg = re.compile("(!@([a-zA-Z0-9 _\-\./]+(::[a-zA-Z0-9 _\-\./]+)*)@!)")
+        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)
+        self.log.log("Exit chat_panel->ParseNode(self, s)", ORPG_DEBUG)
+        return s
+
+    def ParseDice(self, s):
+        """Parses player input for embedded dice rolls"""
+        self.log.log("Enter chat_panel->ParseDice(self, s)", ORPG_DEBUG)
+        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
+            try:
+                newstr = self.DiceManager.proccessRoll(newstr)
+            except:
+                pass
+            if qmode == 1:
+                s = s.replace("[" + matches[i] + "]", "<!-- Official Roll [" + newstr1 + "] => " + newstr + "-->" + newstr, 1)
+            else:
+                s = s.replace("[" + matches[i] + "]", "[" + newstr1 + "<!-- Official Roll -->] => " + newstr, 1)
+        self.log.log("Exit chat_panel->ParseDice(self, s)", ORPG_DEBUG)
+        return s
+
+    def PraseUnknowns(self, s):
+	# Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY
+        self.log.log("Enter chat_panel->PraseUnknowns(self, s)", ORPG_DEBUG)
+        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()
+        self.log.log("Exit chat_panel->PraseUnknowns(self, s)", ORPG_DEBUG)
+        return s
+
+    # This subroutine builds a chat display name.
+    #
+    def chat_display_name(self, player):
+        self.log.log("Enter chat_panel->chat_display_name(self, player)", ORPG_DEBUG)
+        if self.settings.get_setting("ShowIDInChat") == "0":
+            display_name = player[0]
+        else:
+            display_name = "("+player[2]+") " + player[0]
+        self.log.log("Exit chat_panel->chat_display_name(self, player)", ORPG_DEBUG)
+        return display_name
+
+    # This subroutine will get a hex color and return it, or return nothing
+    #
+    def get_color(self):
+        self.log.log("Enter chat_panel->get_color(self)", ORPG_DEBUG)
+        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()
+            self.log.log("Exit chat_panel->get_color(self) return hexcolor", ORPG_DEBUG)
+            return hexcolor
+        else:
+            dlg.Destroy()
+            self.log.log("Exit chat_panel->get_color(self) return None", ORPG_DEBUG)
+            return None
+    # def get_color - end
+
+    def replace_quotes(self, s):
+        self.log.log("Enter chat_panel->replace_quotes(self, s)", ORPG_DEBUG)
+        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
+        self.log.log("Exit chat_panel->replace_quotes(self, s)", ORPG_DEBUG)
+        return rs
+
+    def resolve_loop(self, dom, nodeName, doLoop = False):
+        self.log.log("Enter chat_panel->resolve_loop(self, dom, nodeName)", ORPG_DEBUG)
+        for node in dom:
+            if node._get_tagName() != 'nodehandler':
+                continue
+            if doLoop and node.getAttribute('class') != 'textctrl_handler' and node.hasChildNodes():
+                (found, node) = self.resolve_loop(node.getChildren(), nodeName, doLoop)
+                if not found:
+                    continue
+            if node.getAttribute('name') != nodeName:
+                    continue
+            foundNode = node
+            self.log.log("Exit chat_panel->resolve_loop(self, dom, path) return (True, value)", ORPG_DEBUG)
+            return (True, foundNode)
+        self.log.log("Exit chat_panel->resolve_loop(self, dom, path) return (False, '')", ORPG_DEBUG)
+        return (False, '')
+
+    def resolve_nodes(self, s):
+        self.log.log("Enter chat_panel->resolve_nodes(self, s)", ORPG_DEBUG)
+        value = ""
+        node_path_list = s.split("::")
+        gametree = open_rpg.get_component('tree')
+        dom = gametree.master_dom.getChildren()
+        for nodeName in node_path_list:
+            (found, node) = self.resolve_loop(dom, nodeName)
+            if not found:
+                break
+            dom = node.getChildren()
+        if not found:
+            dom = gametree.master_dom.getChildren()
+            loop = False
+            if len(node_path_list) == 1:
+                loop = True
+            for nodeName in node_path_list:
+                (found, node) = self.resolve_loop(dom, nodeName, loop)
+                if not found:
+                    break
+                dom = node.getChildren()
+                loop = True
+        if found:
+            text = node.getElementsByTagName('text')
+            node = text[0]._get_firstChild()
+            value = node._get_nodeValue()
+        else:
+            value = s
+        self.log.log("Exit chat_panel->resolve_nodes(self, s)", ORPG_DEBUG)
+        return value