File: chatwnd.py

package info (click to toggle)
openrpg 1.6.1-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 3,564 kB
  • ctags: 3,390
  • sloc: python: 25,165; xml: 11,229; makefile: 34; sh: 34
file content (1744 lines) | stat: -rw-r--r-- 67,385 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
# 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.62 2003/12/11 15:17:14 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
#

__version__ = "$Id: chatwnd.py,v 1.62 2003/12/11 15:17:14 digitalxero Exp $"


##
## Module Loading
##
from orpg.orpg_windows import *
import orpg.dirpath
import orpg.tools.rgbhex
import orpg.tools.inputValidator
import orpg.tools.orpg_sound
import webbrowser
from string import *
from orpg.orpg_version import VERSION
import orpg.tools.orpg_update
import commands
import chat_msg
import time   #  needed for detecting when the user stops typing.
#  imports the predicting text control to use in place of wxTextCtrl in chattxt
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


# 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 = ""
  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_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 = ''
        f = open( orpg.dirpath.dir_struct["user"]+filename, 'a' )
        f.write( '%s%s<br>\n' % ( header, text ) )
        f.close()

########################
## timer
##
##  Stolen from main.py to use for typing pause detection.
########################

class mytimer(wxTimer):
    def __init__(self,func):
        self.func = func
        wxTimer.__init__(self)

    def Notify(self):
        self.func()

# 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(wxHtmlWindow):
    """ a wxHTMLwindow that will load links  """

    # initialization subroutine
    #
    # !self : instance of self
    # !parent :
    # !id :
    def __init__(self, parent, id):
        wxHtmlWindow.__init__(self, parent, id, wxDefaultPosition,wxDefaultSize,
								wxSUNKEN_BORDER | wxHW_SCROLLBAR_AUTO)

        self.parent = parent
        # Hook up event handler for right-mouse-click
        EVT_RIGHT_UP(self, self.on_rclick)

    # def __init__ - end

    def on_rclick(self, event):
      "Create a popup menu when right-clicked."

      if not hasattr(self, "popup_close"):
        self.popup_close = wxNewId()
        EVT_MENU(self, self.popup_close, self.OnPopupClose)

      popup_menu = wxMenu()
      item = wxMenuItem(popup_menu, self.popup_close, "Close window")
      popup_menu.AppendItem(item)
      self.PopupMenu(popup_menu, event.GetPosition())
      popup_menu.Destroy()

    def OnPopupClose(self, event):
      self.parent.parent.destroy_private_tab(self.parent)



    # 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()
        webbrowser.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(fontsize))
        return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())


# class chat_html_window - end



#########################
#chat frame window
#########################
# These are kinda global...and static..and should be located somewhere else
# then the middle of a file between two classes.
IDC_TREE = wxNewId()
IDC_SEND = wxNewId()
IDC_BOLD = wxNewId()
IDC_ITALIC = wxNewId()
IDC_UNDER = wxNewId()
IDC_TEXT_COLOR = wxNewId()
CHAT_SAVE = wxNewId()
IDC_D4 = wxNewId()
IDC_D6 = wxNewId()
IDC_D8 = wxNewId()
IDC_D10 = wxNewId()
IDC_D12 = wxNewId()
IDC_D20 = wxNewId()
IDC_D100 = wxNewId()
IDC_NUMDICE = wxNewId()
IDC_MODS = wxNewId()
TOGM = wxNewId()
SLOCK = wxNewId()
TEXTPOP = wxNewId()
SCROL_TOOL = wxNewId()
SAVE_LOG = wxNewId()
TEXTPOP_MENU = wxNewId()

# Heroman - Alias & Filter toolbar buttons
IDC_ALIAS = wxNewId()
IDC_ALIAS_REFRESH=wxNewId()
IDC_FILTER = wxNewId()
IDC_FILTER_REFRESH=wxNewId()
DEFAULT_ALIAS_NAME='Use Real Name'
DEFAULT_FILTER_NAME='No Filter'

BACKGROUND_COLOR = 1050
TEXT_COLOR = 1060
SYSTEM_COLOR = 1070
INFO_COLOR = 1080
EMOTE_COLOR = 1090

BUF_SIZE = wxNewId()

#DUMMY MENUS
SET_CHAT_FOCUS = wxNewId()


# 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(wxNotebook):

  def __init__(self, parent, id, openrpg):
    wxNotebook.__init__(self, parent, id)
    self.openrpg = openrpg
    self.panel_list = []  # list of 'private' chatpanels holding whispered chats

    # build list of images to put in chatpanel tabs
    imagelist = wxImageList(16, 16)
    self.chatNormalIdx = imagelist.Add(getNormalBitmap())
    self.chatAttentionIdx = imagelist.Add(getAttentionBitmap())
    self.AssignImageList(imagelist)

    # Create "main" chatpanel tab, undeletable, connected to 'public' room.
    self.MainChatPanel = chat_panel(self, -1, openrpg, "all")
    self.AddPage(self.MainChatPanel, "Main Room")

    # Hook up event handler for flipping tabs
    EVT_NOTEBOOK_PAGE_CHANGED(self, self.GetId(), self.OnPageChanged)

    # html font/fontsize is global to all the notebook tabs.
    self.font = self.MainChatPanel.chatwnd.GetFont().GetFaceName()
    self.fontsize = self.MainChatPanel.chatwnd.GetFont().GetPointSize()


  def get_tab_index(self, chatpanel):
    "Return the index of a chatpanel in the wxNotebook."

    for i in range(self.GetPageCount()):
      if (self.GetPage(i) == chatpanel):
        return i


  def create_private_tab(self, playerid):
    "Add a new chatpanel directly connected to integer 'playerid' via whispering."

    private_tab = chat_panel(self, -1, self.openrpg, playerid)
    playername = strip_html(self.MainChatPanel.session.get_player_by_player_id(playerid)[0])
    self.AddPage(private_tab, playername)
    private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)

    self.panel_list.append(private_tab)

    idx = self.get_tab_index(private_tab)
    self.SetPageImage(idx, self.chatAttentionIdx)

    return private_tab


  def destroy_private_tab(self, chatpanel):
    "Destroy a chatpanel tab attached to notebook."

    if (chatpanel != self.MainChatPanel):
      self.panel_list.remove(chatpanel)
      chatpanel.chat_timer.Stop()
      self.DeletePage(self.get_tab_index(chatpanel))


  def OnPageChanged(self, event):
    "When private chattabs are selected, set the bitmap back to 'normal'."

    selected_idx = event.GetSelection()
    selected_chatpanel = self.GetPage(selected_idx)
    if (selected_chatpanel.sendtarget != "all"):
      self.SetPageImage(selected_idx, self.chatNormalIdx)
    event.Skip()



# This class defines and builds the Chat Frame for OpenRPG
#
# Inherits: wxPanel
#
# Defines:
#   __init__((self, parent, id, openrpg, sendtarget)
#   build_ctrls(self)
#   on_buffer_size(self,evt)
#   set_colors(self)
#   set_buffersize(self)
#   set_chat_text(self,txt)
#   OnChar(self,event)
#   on_chat_save(self,evt)
#   on_text_color(self,event)
#   colorize(self, color, text)
#   on_text_format(self,event)
#   change_color(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(wxPanel):

	# 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, openrpg, sendtarget):
        wxPanel.__init__(self, parent, id)
        self.session = openrpg.get_component('session')
        self.settings = openrpg.get_component('settings')
        self.myopenrpg = openrpg
        self.parent = parent
        # who receives outbound messages, either "all" or "playerid" string
        self.sendtarget = sendtarget

        # create die roller manager
        self.chatbuffer = []
        self.roller_manager = openrpg.get_component('roller_manager')
        # create rpghex tool
        self.r_h = orpg.tools.rgbhex.RGBHex()
        self.set_colors()
        self.version = VERSION
        self.histidx = 0
        self.history = []
        self.lasthistevt = None
        self.buffersize = int(self.settings.get_setting('buffersize'))
        self.bufferpointer = self.lowbuffer = self.hibuffer = 0
        #chat commands
        self.lockscroll = 0      # set the default to scrolling on.
        self.chat_cmds = commands.chat_commands(self)

        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.chat_timer = mytimer(self.typingTimerFunc)
        self.chat_timer.Start(1000)
        EVT_SIZE(self, 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.
	self.font = self.chatwnd.GetFont().GetFaceName()
        self.fontsize = self.chatwnd.GetFont().GetPointSize()
        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

    def set_default_font(self, fontname=None, fontsize=None):
	    """Set all chatpanels to new default fontname/fontsize.
	    Returns current font settings in a (fontname, fontsize) tuple."""

	    if (fontname is not None):
	      newfont = fontname
	    else:
	      newfont = self.font

	    if (fontsize is not None):
	      newfontsize = fontsize
	    else:
	      newfontsize = self.fontsize

	    self.chatwnd.SetDefaultFontAndSize(newfont, newfontsize)
	    self.InfoPost("Font is now " + newfont + " point size " + `newfontsize`)

	    self.font = newfont
	    self.fontsize = newfontsize
	    return (self.font, self.fontsize)



    def build_menu(self):
        top_frame = self.myopenrpg.get_component('frame')

        menu = wxMenu()
        menu.Append(BACKGROUND_COLOR,"&Background color")
        menu.Append(TEXT_COLOR,"&Text color")
        menu.AppendSeparator()
        menu.Append(SET_CHAT_FOCUS,"&Chat Focus\tCtrl-H")
        menu.AppendSeparator()
        menu.Append( SCROL_TOOL, "Toggle &Scroll Lock")
        menu.Append( SAVE_LOG, "Save Chat &Log")
        menu.Append( TEXTPOP_MENU, "Text &View")

        top_frame.mainmenu.Insert(2, menu, '&Chat')

        EVT_MENU(top_frame, TEXTPOP_MENU, self.pop_textpop)
        EVT_MENU(top_frame, SAVE_LOG, self.on_chat_save)
        EVT_MENU(top_frame, SCROL_TOOL, self.togglescroll)

        EVT_MENU(top_frame, BACKGROUND_COLOR, self.change_color)
        EVT_MENU(top_frame, TEXT_COLOR, self.change_color)
        EVT_MENU(top_frame, SET_CHAT_FOCUS, self.set_chat_text_focus)


    def get_hot_keys(self):
        # dummy menus for hotkeys
        #top_frame = self.myopenrpg.get_component('frame')
        #EVT_MENU(top_frame, SET_CHAT_FOCUS, self.set_chat_text_focus)
        self.build_menu()
        entries = []
        entries.append((wxACCEL_CTRL,ord('H'),SET_CHAT_FOCUS))
        return entries

    # This subroutine builds the controls for the chat frame
    #
    # !self : instance of self
    def build_ctrls(self):
        #chat log
        self.chatwnd = chat_html_window(self,-1)
        if (self.sendtarget == "all"):
            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.toolbar_sizer = wxBoxSizer(wxHORIZONTAL)
        self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "",
            style=wxTE_PROCESS_ENTER|wxTE_PROCESS_TAB,keyHook = self.myKeyHook, validator=None )
        self.build_bar()
        self.basesizer = wxBoxSizer(wxVERTICAL)
        self.basesizer.Add( self.chatwnd,1,wxEXPAND )
        self.basesizer.Add( self.toolbar_sizer, 0, wxEXPAND )
        self.basesizer.Add( self.chattxt, 0, wxEXPAND )
        self.SetSizer(self.basesizer)
        #events
        EVT_TEXT(self, BUF_SIZE, self.on_buffer_size)
        EVT_BUTTON(self, IDC_BOLD, self.on_text_format)
        EVT_BUTTON(self, IDC_ITALIC, self.on_text_format)
        EVT_BUTTON(self, IDC_UNDER, self.on_text_format)
        EVT_BUTTON(self, IDC_TEXT_COLOR, self.on_text_color)
        EVT_BUTTON(self, CHAT_SAVE, self.on_chat_save)

        EVT_BUTTON( self, IDC_D4, self.onDieRoll )
        EVT_BUTTON( self, IDC_D6, self.onDieRoll )
        EVT_BUTTON( self, IDC_D8, self.onDieRoll )
        EVT_BUTTON( self, IDC_D10, self.onDieRoll )
        EVT_BUTTON( self, IDC_D12, self.onDieRoll )
        EVT_BUTTON( self, IDC_D20, self.onDieRoll )
        EVT_BUTTON( self, IDC_D100, self.onDieRoll )
        EVT_BUTTON(self, TEXTPOP, self.pop_textpop)
        try:
            EVT_TOGGLEBUTTON(self, SLOCK,self.lock_scroll)
        except:
            EVT_BUTTON(self, SLOCK,self.lock_scroll)

        EVT_RIGHT_DCLICK(self.chatwnd,self.set_chat_text_focus)
        EVT_MOTION(self.chatwnd,self.OnMotion)
        EVT_LEFT_UP(self.chatwnd,self.set_chat_text_focus)
        # Maybe nuke this for the prefs dialog instead


#        if self.settings.get_setting('SuppressChatAutoComplete') == '1':
#            EVT_CHAR(self.chattxt, self.OnChar)
#        else:
        EVT_CHAR(self.chattxt, self.chattxt.OnChar)

        #EVT_TEXT_ENTER(self.chattxt,1, self.OnChar)
    # def build_ctrls - end

    def set_sizer(self):
        self.SetSizer(self.basesizer)

    def build_bar(self):
        self.toolbar_sizer = wxBoxSizer(wxHORIZONTAL)
        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_gm()
            self.build_scroll()
            self.build_text()
            self.toolbar_sizer.Add( self.textpop_lock, 0, wxEXPAND )
            self.toolbar_sizer.Add(self.scroll_lock,0,wxEXPAND)
            self.build_formating()
            self.build_colorbutton()


    def build_scroll(self):
        self.scroll_lock = None
        if sys.platform != "darwin":
            self.scroll_lock = wxToggleButton( self, SLOCK, "Scroll ON",size=wxSize(70,25))
        else:
            self.scroll_lock = wxButton( self, SLOCK, "*Disabled*",size=wxSize(80,25))

    def build_alias(self):
        sampleList=[DEFAULT_ALIAS_NAME,'','','','','']
        self.aliasList=wxChoice(self,IDC_ALIAS,size=wxSize(100, 25),choices=sampleList)
        sampleList=[DEFAULT_FILTER_NAME,'','','','','']
        self.filterList=wxChoice(self,IDC_FILTER,size=wxSize(100, 25),choices=sampleList)
        if self.settings.get_setting('AliasTool_On') == "1":
            self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'player.gif', 'Refresh list of aliases from Game Tree', IDC_ALIAS_REFRESH, '#bdbdbd' ), 0, wxEXPAND )
            self.toolbar_sizer.Add( self.aliasList,0,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'add_filter.gif', 'Refresh list of filters from Game Tree', IDC_FILTER_REFRESH, '#bdbdbd' ), 0, wxEXPAND )
            self.toolbar_sizer.Add( self.filterList,0,wxEXPAND)
          # Heroman - Refresh alias, filter lists
            EVT_BUTTON(self,IDC_ALIAS_REFRESH,self.onRefreshAliasList)
            EVT_BUTTON(self,IDC_FILTER_REFRESH,self.onRefreshFilterList)
        else:
            self.aliasList.Show(0)
            self.filterList.Show(0)

    def build_gm(self):
        if self.settings.get_setting('ToGMsButton_On') == "1":
           if sys.platform != "darwin":
               self.to_gm = wxToggleButton( self, TOGM, "To GM(s)", size= wxSize(70,25))
           else:
               self.to_gm = wxButton( self, TOGM, "*Disabled*", size=wxSize(80,25))
           self.toolbar_sizer.Add(self.to_gm,0,wxEXPAND)
        else:
           self.to_gm = None

    def build_text(self):
        self.textpop_lock = createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', TEXTPOP, '#bdbdbd')

    def build_dice(self):
        if self.settings.get_setting('DiceButtons_On') == "1":
            self.numDieText = wxTextCtrl( self, IDC_NUMDICE, "1", size=wxSize(25, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
            self.dieModText = wxTextCtrl( self, IDC_MODS, "", size=wxSize(50, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
            self.toolbar_sizer.Add(10,10)
            self.toolbar_sizer.Add( self.numDieText, 0, wxALIGN_CENTER | wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d4.gif', 'Roll d4', IDC_D4, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d6.gif', 'Roll d6', IDC_D6, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d8.gif', 'Roll d8', IDC_D8, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d10.gif', 'Roll d10', IDC_D10, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d12.gif', 'Roll d12', IDC_D12, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d20.gif', 'Roll d20', IDC_D20, '#bdbdbd'), 0 ,wxEXPAND)
            self.toolbar_sizer.Add( createMaskedButton(self, orpg.dirpath.dir_struct["icon"]+'b_d100.gif', 'Roll d100', IDC_D100, '#bdbdbd'), 0 ,wxEXPAND)
        #self.toolbar_sizer.Add( wxStaticText( self, -1, "Die Modifier(s)" ), 0, wxALL | wxALIGN_CENTER, 10 )
            self.toolbar_sizer.Add( self.dieModText, 0, wxALIGN_CENTER, 5 )
            self.toolbar_sizer.Add(10,10,1)

    def build_formating(self):
        if self.settings.get_setting('FormattingButtons_On') != "1":
            return
        self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'bold.gif', 'Make the selected text Bold', IDC_BOLD, '#bdbdbd'), 0, wxEXPAND )
        self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'italic.gif', 'Italicize the selected text', IDC_ITALIC, '#bdbdbd' ), 0, wxEXPAND )
        self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'underlined.gif', 'Underline the selected text', IDC_UNDER, '#bdbdbd' ), 0, wxEXPAND )
        self.toolbar_sizer.Add(10,10)

# Heroman - Ideally, we would use static labels...
    def build_colorbutton(self):
        self.color_button = wxButton(self, IDC_TEXT_COLOR, "C",wxPoint(0,0), wxSize(22,0))
        self.color_button.SetBackgroundColour(wxBLACK)
        self.color_button.SetForegroundColour(wxWHITE)
        self.toolbar_sizer.Add(self.color_button, 0, wxEXPAND)
        self.toolbar_sizer.Add( createMaskedButton( self, orpg.dirpath.dir_struct["icon"]+'save.bmp', 'Save the chatbuffer', CHAT_SAVE, '#c0c0c0', wxBITMAP_TYPE_BMP ), 0, wxEXPAND )

    def AddAliasHandle(self,dom,names):
        for n in dom._get_childNodes():
            try:
                if n.getAttribute('class') == "voxchat_handler":
                    list = n.getElementsByTagName( 'voxchat.alias' )
                    for entry in list:
                        name = entry.getAttribute( 'name' )
                        names.append( name )
                self.AddAliasHandle(n,names)
            except:
                continue


    def AddFilterHandle(self,dom,names):
        for n in dom._get_childNodes():
            try:
                if n.getAttribute('class') == "voxchat_handler":
                    list = n.getElementsByTagName( 'voxchat.filter' )
                    for entry in list:
                        name = entry.getAttribute( 'name' )
                        names.append( name )
                self.AddFilterHandle(n,names)
            except:
                continue

    def RefreshAliasList(self):
        self.aliasList.Clear()
        self.aliasList.Append(DEFAULT_ALIAS_NAME)
        gametree=self.myopenrpg.get_component('tree')
        names = []
        self.AddAliasHandle(gametree.master_dom,names)
        names.sort()
        if names:
            last = names[-1]
            for i in range(len(names)-2, -1, -1):
                if last==names[i]: del names[i]
                else: last=names[i]

        for sortName in names:
            self.aliasList.Append(sortName)
#        self.InfoPost("Found " + str(aliasCount) + " aliases from top level game tree.")

    def RefreshFilterList(self):
        self.filterList.Clear()
        self.filterList.Append(DEFAULT_FILTER_NAME)
        gametree=self.myopenrpg.get_component('tree')
        aliasFilters = []
        self.AddFilterHandle(gametree.master_dom,aliasFilters)
        aliasFilters.sort()
        if aliasFilters:
            last = aliasFilters[-1]
            for i in range(len(aliasFilters)-2, -1, -1):
                if last==aliasFilters[i]: del aliasFilters[i]
                else: last=aliasFilters[i]
        for sortName in aliasFilters:
            self.filterList.Append(sortName)


    def applyFilter(self,text,filterName):
        gametree=self.myopenrpg.get_component('tree')
        list=gametree.master_dom.getElementsByTagName( 'voxchat.filter' )
        for node in list:
            if (node.getAttribute('name')==filterName):
                list = node.getElementsByTagName( 'rule' )
                for rule in list:
                    match = rule.getAttribute( 'match' )
                    sub = rule.getAttribute( 'sub' )
                    text = re.sub( match, sub, text )
                return text
        return text

    def onRefreshFilterList(self,evt):
        self.RefreshFilterList()

    def onRefreshAliasList(self,evt):
        self.RefreshAliasList()

    def OnMotion(self,evt):
        contain = self.chatwnd.GetInternalRepresentation()
        if contain:
            sx = sy = 0
            x = y = 0
            (sx,sy) = self.chatwnd.GetViewStart()
            (sx1,sy1) = self.chatwnd.GetScrollPixelsPerUnit()
            sx = sx*sx1
            sy = sy*sy1
            (x,y) = evt.GetPosition()
            lnk = contain.GetLink(sx+x,sy+y)

            if lnk:
                try:
                    link = lnk.GetHref()
                    self.session.set_status_url(link)
                except:
                    pass

        else:
            print "Error, self.chatwnd.GetInternalRepresentation() return None"

        evt.Skip()


    #  This subroutine is registered with predTextCtrl to be run for every OnChar event
    #  It checks if we need to send a typing message

    #
    #  self:  duh
    #  event:  raw KeyEvent from OnChar()
    def myKeyHook(self,event):

        if self.session.get_status() == MPLAY_CONNECTED:   #  only do if we're connected
            thisPress = time.time()                #  thisPress is local temp variable

            if (thisPress - self.lastSend) > 4:    #  Check to see if it's been 5 seconds since our last notice
                                                   #    If we're not already typing, then self.lastSend will be 0
                self.sendTyping(1)                 #  send a not typing event here (1 for true)

            self.lastPress = thisPress             #  either way, record the time of this keystroke for use in
                                                   #  self.typingTimerFunc()

        if self.settings.get_setting('SuppressChatAutoComplete') == '1':
            return 1
        else:
            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):

        if self.lastSend:                          #  This will be zero when not typing, so equiv to if is_typing
            thisTime = time.time()                 #  thisTime is a local temp variable
            if (thisTime - self.lastPress) > 4:    #  Check to see if it's been 5 seconds since our last keystroke
                                               #    If we're not already typing, then self.lastSend will be 0

                self.sendTyping(0)                 #  send a typing event here (0 for false)


    #  This subroutine actually takes care of sending the messages for typing/not_typing events
    #
    #  self:  duh
    #  typing:  boolean
    def sendTyping(self,typing):

        if typing:
            self.lastSend = time.time()  #  remember our send time for use in myKeyHook()
            #I think this is cleaner
            status_text = self.settings.get_setting('TypingStatusAlias')
            if status_text == "" or status_text == None:
                status_text = "Typing"
            self.session.set_text_status(status_text)

            #theMsg = self.session.toxml("typing")        #  build the xml message to send
            #self.session.pretranslate(theMsg)            #  since the server won't return our message to us,
                                                         #    pretend it does by calling the pretranlation routine
                                                         #    to simulate this behavior locally.

            #self.session.outbox.put(theMsg)              #  code to send xml

        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)
            #theMsg = self.session.toxml("not_typing")    #  build the xml message to send
            #self.session.pretranslate(theMsg)            #  since the server won't return our message to us,
                                                         #    to simulate this behavior locally.

            #self.session.outbox.put(theMsg)              #  code to send xml



    # This subroutine checks the buffersize of the chat window to make sure it
    # is good.
    #
    # !self : instance of self
    # !evt :
    def on_buffer_size(self,evt):
#        txt = self.buftxt.GetValue()
        try:
            num = int(evt)
            if num > 0:
                self.buffersize = num
#                self.settings.set_setting('buffersize',txt)
        except:
            print "Invalid chat buffer size!"
    # def in_buffer_size - end


    # This subroutine sets the colors of the chat based on the settings in the
    # self instance.
    #
    # !self : instance of self
    def set_colors(self):
        # chat window backround color
        self.bgcolor = self.settings.get_setting('bgcolor')
         # chat window normal text color
        self.textcolor = self.settings.get_setting('textcolor')
        # color of text player types
        self.mytextcolor = self.settings.get_setting('mytextcolor')
        # color of system warnings
        self.syscolor = self.settings.get_setting('syscolor')
        # color of system info messages
        self.infocolor = self.settings.get_setting('infocolor')
        # color of emotes
        self.emotecolor = self.settings.get_setting('emotecolor')
        # color of whispers
        self.whispercolor = self.settings.get_setting('whispercolor')
    # def set_colors - end


    # This subroutine will set the size of the buffer on the chat window
    #
    # !self : instance of self
    def set_buffersize(self):
        self.buffersize = int(self.settings.get_setting('buffersize'))
#        self.buftxt.SetValue(self.settings.get_setting('buffersize'))
    # def set_buffersize - end


    # This subroutine will insert text into the chat window
    #
    # !self : instance of self
    # !txt : text to be inserted into the chat window
    def set_chat_text(self,txt):
        self.chattxt.SetValue(txt)
        self.chattxt.SetFocus()
        self.chattxt.SetInsertionPointEnd()
    # def set_chat_text - end

    def get_chat_text(self):
        return self.chattxt.GetValue()

    # This subroutine sets the focus to the chat window
    def set_chat_text_focus(self,event):
        self.chattxt.SetFocus()
    # def set_chat_text_focus - end

    # This subrtouine grabs the user input and make the special keys and
    # modifiers work.
    #
    # !self : instance of self
    # !event :
    #
    # Note:  self.chattxt now handles it's own Key events.  It does, however still
    #        call it's parent's (self) OnChar to handle "default" behavior.
    def OnChar(self,event):
        s = self.chattxt.GetValue()
        self.histlen = len(self.history) - 1

        ## RETURN KEY (no matter if there is text in chattxt)
        #  This section is run even if there is nothing in the chattxt (as opposed to the next WXK_RETURN handler
        if event.KeyCode() == WXK_RETURN:
            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=""
        if event.KeyCode() == WXK_F1:
            macroText=self.settings.get_setting('F1')
        elif event.KeyCode() == WXK_F2:
            macroText=self.settings.get_setting('F2')
        elif event.KeyCode() == WXK_F3:
            macroText=self.settings.get_setting('F3')
        elif event.KeyCode() == WXK_F4:
            macroText=self.settings.get_setting('F4')
        elif event.KeyCode() == WXK_F5:
            macroText=self.settings.get_setting('F5')
        elif event.KeyCode() == WXK_F6:
            macroText=self.settings.get_setting('F6')
        elif event.KeyCode() == WXK_F7:
            macroText=self.settings.get_setting('F7')
        elif event.KeyCode() == WXK_F8:
            macroText=self.settings.get_setting('F8')
        elif event.KeyCode() == WXK_F9:
            macroText=self.settings.get_setting('F9')
        elif event.KeyCode() == WXK_F10:
            macroText=self.settings.get_setting('F10')
        elif event.KeyCode() == WXK_F11:
            macroText=self.settings.get_setting('F11')
        elif event.KeyCode() == WXK_F12:
            macroText=self.settings.get_setting('F12')

        # 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.KeyCode() == WXK_RETURN and len(s)) or len(macroText):
#            if self.settings.get_setting( "SuppressChatAutoComplete" ) != "0":
            self.histidx=0
            self.history.append(s)
            self.lasthistevt=None

            try:
                if self.to_gm.GetValue()==1 and s[0]!="/":
                    the_gms = []
                    for playerid in self.session.players:
                        if len(self.session.players[playerid])>7:
                            print "Role of ID#"+playerid+" is "+self.session.players[playerid][7]
                            if self.session.players[playerid][7]=="GM":
                                the_gms += [playerid]
                    if len(the_gms):
                        gmstring = ""
                        for each_gm in the_gms:
                            gmstring +=each_gm+","
                        s = "/w "+gmstring[:-1]+"="+s
            except:
                pass


            if s[0] != "/": ## it's not a slash command
                s = self.ParsePost( s, true, true )
            else:
                #s = self.ParseDice(s)
                self.chat_cmds.docmd(s) # emote is in chatutils.py

            if not len(macroText):
                self.chattxt.SetValue("")

            # play sound
            sound_file = self.settings.get_setting("SendSound")
            sound_player = orpg.tools.orpg_sound.orpg_sound(self.settings.get_setting("UnixSoundPlayer"));
            sound_player.play(sound_file)


        ## UP KEY
        elif event.KeyCode() == WXK_UP and self.histlen >= 0:
            if self.lasthistevt == 'dn' and self.histlen != self.histidx:
                self.histidx = self.histidx + 1
            try:
                histdata = self.history[self.histlen - self.histidx]
                self.chattxt.SetValue(histdata)
                self.chattxt.SetInsertionPointEnd()
                self.lasthistevt='up'
            except:
                print "bad value in the up key: histidx = %s" % self.histidx
            if self.histidx < self.histlen:
                self.histidx = self.histidx + 1
            else:
                self.lasthistevt=None

        ## DOWN KEY
        elif event.KeyCode() == WXK_DOWN and self.histlen >= 0:
            if self.histidx > 0:
                if self.lasthistevt == 'up' and self.histidx != 0:
                    self.histidx = self.histidx - 1
                self.histidx = self.histidx - 1
                try:
                    histdata = self.history[self.histlen - self.histidx]
                    self.chattxt.SetValue(histdata)
                    self.chattxt.SetInsertionPointEnd()
                    self.lasthistevt='dn'
                except:
                    print "bad value in the down key: histidx = %s" % self.histidx
            elif self.histidx == 0:
                self.chattxt.SetValue("")
                self.lasthistevt=None

        ## TAB KEY
        elif  event.KeyCode() == WXK_TAB:

            if s !="":
                found = 0
                nicks = []
                testnick = ""
                inlength = len(s)
                for getnames in self.session.players.keys():
                    striphtmltag = re.compile ('<[^>]+>*')
                    testnick = striphtmltag.sub ("", self.session.players[getnames][0])
                    if string.lower(s) == string.lower(testnick[:inlength]):
                        found = found + 1
                        nicks[len(nicks):]=[testnick]
                if found == 0: ## no nick match
                    self.Post(self.colorize(self.syscolor," ** No match found"))
                elif found > 1: ## matched more than 1, tell user what matched
                    nickstring = ""
                    nicklist = []
                    for foundnicks in nicks:
                        nickstring = nickstring + foundnicks + ", "
                        nicklist.append(foundnicks)
                    nickstring = nickstring[:-2]
                    self.Post(self.colorize(self.syscolor, " ** Multiple matches found: " + nickstring))
                    # set text to the prefix match between first two matches
                    settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(nicklist[0]))), string.lower(nicklist[1])).group()
                    # run through the rest of the nicks
                    for i in nicklist:
                        settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(i))), string.lower(settext)).group()
                    if settext:
                        self.chattxt.SetValue(settext)
                        self.chattxt.SetInsertionPointEnd()
                else: ## put the matched name in the chattxt box
                    settext = nicks[0] + ": "
                    self.chattxt.SetValue(settext)
                    self.chattxt.SetInsertionPointEnd()
            else: ## not online, and no text in chattxt box
                self.Post(self.colorize(self.syscolor, " ** That's the Tab key, Dave"))
        ## PAGE UP
        elif event.KeyCode() == WXK_PRIOR:
            if self.bufferpointer < len(self.chatbuffer)-self.buffersize and len(self.chatbuffer) > self.buffersize:
                self.bufferpointer = self.bufferpointer + self.buffersize
                self.Post()
            else:
                event.Skip()
        ## PAGE DOWN
        elif event.KeyCode() == WXK_NEXT:
            if self.bufferpointer > 0:
                self.bufferpointer = self.bufferpointer - self.buffersize
                self.Post()
            else:
                event.Skip()

        ## END
        elif event.KeyCode() == WXK_END:
            self.bufferpointer = 0
            self.Post()
            event.Skip()

        ## NOTHING
        else:
            event.Skip()
    # def OnChar - end




    def onDieRoll( self, evt ):
        """Roll the dice based on the button pressed and the die modifiers entered, if any."""

        # Get any die modifiers if they have been entered
        numDie = self.numDieText.GetValue()
        dieMod = self.dieModText.GetValue()

        dieText = numDie

        # Now, apply and roll die mods based on the button that was pressed
        id = evt.GetId()
        if id == IDC_D4:
            dieText += "d4"
        elif id == IDC_D6:
            dieText += "d6"
        elif id == IDC_D8:
            dieText += "d8"
        elif id == IDC_D10:
            dieText += "d10"
        elif id == IDC_D12:
            dieText += "d12"
        elif id == IDC_D20:
            dieText += "d20"
        elif id == IDC_D100:
            dieText += "d100"

        if len(dieMod) and dieMod[0] not in "*/-+":
            dieMod = "+" + dieMod

        dieText += dieMod
        dieText = "["+dieText+"]"
        self.ParsePost(dieText, 1, 1)
        self.chattxt.SetFocus()


    # This subroutine saves a chat buffer as html to a file chosen via a
    # FileDialog.
    #
    # !self : instance of self
    # !evt :
    def on_chat_save(self,evt):
        f =wxFileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wxSAVE)
        if f.ShowModal() == wxID_OK:
            file = open(f.GetPath(),"w")
            buffertext = "<html><body bgcolor='"+self.bgcolor+"' text='"+self.textcolor+"'>"
            for bufferslice in self.chatbuffer:
                buffertext = buffertext + bufferslice
            file.write(buffertext+"</body></html>")
            file.close()
        f.Destroy()
        os.chdir(self.root_dir)
        auto_purge_after_save = (self.settings.get_setting('AutoPurgeAfterSave')).lower()
        if auto_purge_after_save == "auto":
           self.Purge_buffer()
        elif auto_purge_after_save == "ask":
            f = wxMessageDialog(self,"Purge Buffer","",wxYES_NO)
            if f.ShowModal() == wxID_OK:
                self.Purge_buffer()
            f.Destroy()
    # def on_chat_save - end


    # This subroutine sets the color of selected text, or base text color if
    # nothing is selected
    def on_text_color(self,event):
        hexcolor = self.r_h.do_hex_color_dlg(self)
        if hexcolor != None:
            (beg,end) = self.chattxt.GetSelection()
            if beg != end:
                txt = self.chattxt.GetValue()
                txt = txt[:beg]+self.colorize(hexcolor,txt[beg:end]) +txt[end:]
                self.chattxt.SetValue(txt)
                self.chattxt.SetInsertionPointEnd()
                self.chattxt.SetFocus()
            else:
                self.color_button.SetBackgroundColour(hexcolor)
                self.mytextcolor = hexcolor
                self.settings.set_setting('mytextcolor',hexcolor)
                self.Post()
    # def on_text_color - end


    # This subroutine take a color and a text string and formats it into html.
    #
    # !self : instance of self
    # !color : color for the text to be set
    # !text : text string to be included in the html.
    def colorize(self, color, text):
        "Puts font tags of 'color' around 'text' value, and returns the string"
        return "<font color='" + color + "'>" + text + "</font>"
    # def colorize - end


    # This subroutine takes and event and inserts text with the basic format
    # tags included.
    #
    # !self : instance of self
    # !event :
    def on_text_format(self,event):
        id = event.GetId()
        txt = self.chattxt.GetValue()
        (beg,end) = self.chattxt.GetSelection()
        if beg != end:
            sel_txt = txt[beg:end]
        else:
            sel_txt = txt

        if id == IDC_BOLD:
            sel_txt = "<b>" + sel_txt + "</b>"
        elif id == IDC_ITALIC:
            sel_txt = "<i>" + sel_txt + "</i>"
        elif id == IDC_UNDER:
            sel_txt = "<u>" + sel_txt + "</u>"

        if beg != end:
            txt = txt[:beg] + sel_txt + txt[end:]
        else:
            txt = sel_txt

        self.chattxt.SetValue(txt)
        self.chattxt.SetInsertionPointEnd()
        self.chattxt.SetFocus()
    # def on_text_format - end


    # This subroutine will change the color of the text and the background
    # color.
    #
    # !self : instance of self
    # !event :
    def change_color(self,event):
        event_id = event.GetId()
        hexcolor = self.get_color()
        if hexcolor != None:
            if event_id == TEXT_COLOR:
                self.textcolor = hexcolor
                self.settings.set_setting('textcolor',hexcolor)
            elif event_id == BACKGROUND_COLOR:
                self.bgcolor = hexcolor
                self.settings.set_setting('bgcolor',hexcolor)
            self.Post()
    # def change_color - end

    def togglescroll(self, event):
        if self.lockscroll == 1:
            self.lockscroll = 0
            onoff = "On"
        elif self.lockscroll == 0:
            self.lockscroll = 1
            onoff = "Off"
        if self.settings.get_setting('Toolbar_On') == "1":
            try:
                self.scroll_lock.SetValue(self.lockscroll)
            except:
                pass #if the Toolbar was turned on this session there will be no instance of scroll_lock
        self.Post("Set scrolling to <b>"+onoff+"</b>")
        self.lock_scroll(1)

    def lock_scroll(self,event):
        if self.scroll_lock == None:
            return
        self.lockscroll = self.scroll_lock.GetValue()
        if self.lockscroll == 0:
            self.scroll_lock.SetLabel("Scroll ON")
            if (self.storedata <> 0):
                self.chatwnd.SetPage(self.storedata)
            self.scroll_down()
        else:
            self.scroll_lock.SetLabel("Scroll OFF")

    # This subroutine will popup a text window with the chatbuffer contents
    #
    # !self : instance of self
    # !event :
    def pop_textpop(self, event):
        "searchable popup text view of chatbuffer"
        h_buffertext = ""
        for h_bufferslice in self.chatbuffer:
            h_buffertext = h_buffertext + string.replace(h_bufferslice, "<br>", "<br>\n")
        h_dlg = wxScrolledMessageFrameEditor(self, h_buffertext, "Text View of Chat Window", None, (500,300))
        h_dlg.Show(true)

    # This subroutine will change the dimension of the window
    #
    # !self : instance of self
    # !event :
    def OnSize(self, event = None):
        s = self.GetClientSizeTuple()
        if self.settings.get_setting('Toolbar_On') == "1":
            self.chatwnd.SetDimensions(0,0,s[0],s[1]-50)
            self.chattxt.SetDimensions(0,s[1]-25,s[0],25)
            self.toolbar_sizer.SetDimension(0,s[1]-50,s[0],25)
        else:
            self.chatwnd.SetDimensions(0,0,s[0],s[1]-25)
            self.chattxt.SetDimensions(0,s[1]-25,s[0],25)
        self.scroll_down()
    # def OnSize - end


    # This subroutine provides the ability to scroll up and down in the chat
    # window.
    #
    # !self : instance of self
##    def scroll_window(self,scroll_function = SCROLL_DOWN_ONE_LINE):
##        sy = self.chattextpointer
##        print "scroll_window:  sy = ", sy
##        (dummy,vh) = self.chatwnd.GetVirtualSize()
##        print "virt size = ", dummy, vh
##        (pw,ph) = self.chatwnd.GetScrollPixelsPerUnit()
##        print "scroll pixel units = ", pw, ph
##        (dummy,h) = self.chatwnd.GetClientSizeTuple()
##        print "client size = ", dummy, h


    def scroll_down(self):
        (vw,vh) = self.chatwnd.GetVirtualSize()
        (w,h) = self.chatwnd.GetClientSizeTuple()
        h_dif = vh-(h)
        (pw,ph) = self.chatwnd.GetScrollPixelsPerUnit()
        self.chatwnd.Scroll(-1,h_dif/ph)


    ###### message helpers ######

    def system_message(self,text):
        self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE)
        self.SystemPost(text)

    def info_message(self,text):
        self.send_chat_message(text,chat_msg.INFO_MESSAGE)
        self.InfoPost(text)

    def emote_message(self,text):
        text = self.ParseDice(text)
        # Heroman - apply any filtering selected
        text=self.ParseFilter(text)
        try:
            if self.to_gm.GetValue():
                prfx = "Sending emote to GM(s): "
                the_gms = []
                for playerid in self.session.players:
                    if len(self.session.players[playerid])>7:
                       #print "Role of ID#"+playerid+" is "+self.session.players[playerid][7]
                        if self.session.players[playerid][7]=="GM":
                              the_gms += [playerid]
                for each_gm in the_gms:
                    prfx += self.session.players[each_gm][0]+" "
                    self.send_chat_message(text,chat_msg.EMOTE_MESSAGE, each_gm)
            else:
                self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
        except:
            self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
        if self.settings.get_setting('Toolbar_On') == "1":
            try:
                alias=self.aliasList.GetStringSelection();
            except:
                alias=""
            if alias!="" and alias!=DEFAULT_ALIAS_NAME:
                name = alias
            else:
                name = self.chat_display_name(self.session.get_my_info())
        else:
            name = self.chat_display_name(self.session.get_my_info())
        text = "** " + name + " " + text + " **"
        try:
            if self.to_gm.GetValue():
                 #text = prfx+text
                pass
        except:
            pass
        self.EmotePost(text)


    def whisper_to_players(self,text,player_ids):
        tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
        try:
            activeidx = self.parent.GetSelection()
        except:
            activeidx = 0
        if activeidx != 0:
            pass
        else:
            text = self.ParseDice(text)

        # Heroman - apply any filtering selected
        text=self.ParseFilter(text)
        player_names = ""
        # post to our chat before we colorize
        for m in player_ids:
            id = m.strip()
            if self.session.is_valid_id(id):
                returned_name = self.session.get_player_by_player_id(id)[0]
                player_names += returned_name
                player_names += ", "
            else:
                player_names += " Unknown!"
                player_names += ", "
        comma = ","
        comma.join(player_ids)
        if (self.sendtarget == "all"):
          self.InfoPost("<i>whispering to "+ player_names + " " + text + "</i> ")
        # colorize and loop, sending whisper messages to all valid clients
        text = self.colorize(self.mytextcolor, text)
        for id in player_ids:
            id = id.strip()
            if self.session.is_valid_id(id):
                self.send_chat_message(text,chat_msg.WHISPER_MESSAGE,id)
            else:
                self.InfoPost(id + " Unknown!")

    def send_chat_message(self,text,type=chat_msg.CHAT_MESSAGE,player_id="all"):
        msg = chat_msg.chat_msg()
        msg.set_text(text)
        msg.set_type(type)
	if self.settings.get_setting('Toolbar_On') == "1":
            try:
                alias=self.aliasList.GetStringSelection()
            except:
                alias=""
        else:
            alias = ""
        if alias!="" and alias!=DEFAULT_ALIAS_NAME:
            playername = self.settings.get_setting( 'player' )
            self.chat_cmds.docmd( "/name "+ alias)
            self.session.send(msg.toxml(),player_id)
            self.chat_cmds.docmd( "/name "+ playername)
        else:
            self.session.send(msg.toxml(),player_id)
        del msg

    #### incoming chat message handler #####

    def post_incoming_msg(self,msg,player):
        # pull data
        type = msg.get_type()
        text = msg.get_text()

        # who sent us the message?
        if player:
            display_name = self.chat_display_name(player)
        else:
            display_name = "Server Administrator"

        # 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)

        elif type == chat_msg.WHISPER_MESSAGE:

            tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
            displaypanel = self
            whisperingstring = " (whispering): "
            panelexists = 0
            if (tabbed_whispers_p == "1"):
                try:
                  # Check to see if parent notebook already has a private tab for player
                    for panel in self.parent.panel_list:
                       if (panel.sendtarget == player[2]):
                            displaypanel = panel
                            idx = self.parent.get_tab_index(displaypanel)
                            activeidx = self.parent.GetSelection()
                            if(activeidx != idx):
                                self.parent.SetPageImage(idx, self.parent.chatAttentionIdx)
		            panelexists = 1
                            break
              	       # if not, have parent notebook create a new tab
                    if (panelexists == 0):
                        displaypanel = self.parent.create_private_tab(player[2])
                        cidx = self.parent.GetSelection()
                        nidx = self.parent.get_tab_index(displaypanel)
                        self.parent.SetSelection(nidx)
                        self.parent.SetSelection(cidx)
                        self.parent.SetPageImage(nidx, self.parent.chatAttentionIdx)

                    if (len(self.parent.panel_list)>0):
                        text = "<B>"+display_name+"</B>: " + text
                        displaypanel.Post(text)
                        recvSound = "WhisperSound"
                except:
                    # no tabbed whispering feature
                    text = "<i><B>" + display_name + "</B>" + whisperingstring + text + "</i>"
                    self.Post(text)
                    recvSound = "WhisperSound"
            else:
                # no tabbed whispering feature
	        text = "<i><B>" + display_name + "</B>" + whisperingstring + text + "</i>"
	        self.Post(text)
                recvSound = "WhisperSound"

        elif type == chat_msg.EMOTE_MESSAGE:
            text = "** " + display_name + " " + text + " **"
            self.EmotePost(text)
        elif type == chat_msg.INFO_MESSAGE:
            text = "<B>"+display_name+"</B>: " + text
            self.InfoPost(text)
        elif type == chat_msg.SYSTEM_MESSAGE:
            text = "<B>"+display_name+"</B>: " + text
            self.SystemPost(text)

        # playe sound
        sound_file = self.settings.get_setting(recvSound)
        sound_player = orpg.tools.orpg_sound.orpg_sound(self.settings.get_setting("UnixSoundPlayer"));
        sound_player.play(sound_file)

    #### Posting helpers #####

    def InfoPost(self,s):
        self.Post(self.colorize(self.infocolor, s))

    def SystemPost(self,s):
        self.Post(self.colorize(self.syscolor, s))

    def EmotePost(self,s):
        self.Post(self.colorize(self.emotecolor, s))

    def Purge_buffer(self):
        x = self.chatbuffer[len(self.chatbuffer)-self.buffersize:]
        self.chatbuffer=x
        self.InfoPost("Buffer purged")

    #### Standard Post method #####

    def Post(self,s="",send=false,myself=false):
        strip_p = self.settings.get_setting("striphtml")
        if (strip_p == "1"):
          s = strip_html(s)
        self.storedata = 0
        if myself:
            name = ""
            if self.settings.get_setting('Toolbar_On') == "1":
                try:
                    alias=self.aliasList.GetStringSelection();
                except:
                    alias="" # toolbar status could have changed this session
                if alias!="" and alias!=DEFAULT_ALIAS_NAME:
                    alt=self.session.get_my_info()
                    name = "<B>"+self.chat_display_name([alias,alt[1],alt[2]])+"</B>: "
                else:
                    name = "<B>"+self.chat_display_name(self.session.get_my_info())+"</B>: "
	    else:
                name = "<B>"+self.chat_display_name(self.session.get_my_info())+"</B>: "
            s = self.colorize(self.mytextcolor, s)
        else:
            name = ""
        if s!="":
            self.chatbuffer.append(name +  s + "<br>")
            log( self.settings, name + s )

        purge_at = (self.settings.get_setting('PurgeAtBuffersizeTimesLines'))
        if purge_at == 0:
            purge_at = 10000
            if (len(self.chatbuffer) > self.buffersize*purge_at):
                self.Purge_buffer()

        buffertext = "<body bgcolor='" + self.bgcolor + "'text='" + self.textcolor + "'>"

        if self.bufferpointer == 0: ## we're not in the chat history
            hipointer = self.hibuffer = len(self.chatbuffer)
            lowpointer = self.lowbuffer = self.hibuffer - self.buffersize
            if self.lowbuffer < 0:
                lowpointer = self.lowbuffer = 0
        else: ## we are in the chat history
            hipointer = self.hibuffer - self.bufferpointer
            lowpointer = hipointer - self.buffersize
            if lowpointer < 0:
                lowpointer = 0
            buffertext = buffertext + self.colorize(self.infocolor, " ** You are viewing lines " + str(lowpointer) + "-" + str(hipointer) + " of your chat buffer.<BR>")
        for bufferslice in self.chatbuffer[lowpointer:hipointer]:
            buffertext = buffertext + bufferslice + "\n"

        #buffertext = string.replace(buffertext,'"',"'")
        buffertext = self.replace_quotes(buffertext)
        #print buffertext
##        self.chatwnd.SetPage(buffertext)

        if self.lockscroll == 0:
             self.chatwnd.SetPage(buffertext)
             self.scroll_down()
        else:
             self.storedata = buffertext
        if send:
             #self.session.send(s)
             if (self.sendtarget == "all"):
               self.send_chat_message(s)
             else:
               self.whisper_to_players(s, [self.sendtarget])


    ####  Post with parsing dice ####

    def ParsePost(self,s,send=false,myself=false):
        s = self.NormalizeParse(s)
        self.Post(s,send,myself)

    def NormalizeParse(self,s):
        s = self.ParseNode(s)
        s = self.ParseDice(s)
        s = self.ParseFilter(s)
        return s

    def ParseFilter(self,s):
        if self.settings.get_setting('Toolbar_On') == "1":
            try:
                filter=self.filterList.GetStringSelection();
                if filter!="" and filter!=DEFAULT_FILTER_NAME:
                    s=self.applyFilter(s,filter)
            except:
                pass  # this try except catches if the user hasn't rebooted openrpg since they turned on the Toolbar.
        return s

    def ParseNode(self,s):
        cur_loc = 0
        "Parses player input for embedded nodes rolls"
	while 1:
            loc = s.find("!@",cur_loc)
            if loc == -1: break
            cur_loc = loc+1
            nex = s.find("!@",cur_loc)
            myend = s.find("@!",cur_loc)
            if (nex > myend) or (nex == -1):
                old = s[loc:myend+2]
                if len(old) >0:
                    newstr = self.resolve_nodes(old)
                    s = s.replace(old,newstr,1)
                    cur_loc = 0
            else:
                cur_loc = loc+1
        return s


    def ParseDice(self,s):

        "Parses player input for embedded dice rolls"
        cur_pos = 0
        while 1:
            start = s.find('[',cur_pos)
            end = s.find(']',cur_pos)
            #break if no []
            if not start >= 0 or not end >= 0 :
                break
            old = s[start:end+1]

            try:
                #newstr = self.dice.roll(s[start+1:end])

                newstr = self.PraseUnknowns(s[start+1:end])

                if newstr[0].upper() == 'Q':
                    quiet = 1
                    newstr= newstr[1:]
                else: quiet = 0

                if self.settings.get_setting("dievars") == "1":
                    roll = "[" + newstr + "]"
                else:
                    roll = old

                newstr = self.roller_manager.resolveDieStr(newstr)
                if not quiet:
                    newstr = roll + " -> " + newstr

            except Exception, e:
                print e
                newstr = "[Bad dice format] - [" + newstr +"]"

            s = s[:start] + s[start:].replace(old,newstr,1)
            cur_pos = start + len(newstr)
        return s


    def PraseUnknowns(self,s):
        newstr = "0"
        dlg = wxTextEntryDialog(self,"Missing Value?",newstr)
        while 1:
            pos = s.find('?')
            #break if no []
            if pos < 0:
                break
            oldstr = s[pos:pos+1]

            if dlg.ShowModal() == wxID_OK:
            	newstr = dlg.GetValue()
            s = s.replace(oldstr,newstr,1)
        dlg.Destroy()
        return s



	# This subroutine builds a chat display name.
    #

    def chat_display_name(self,player):
        if self.settings.get_setting("ShowIDInChat") == "0":
            display_name = player[0]
        else:
            display_name = "("+player[2]+") "+player[0]
        return display_name


    # This subroutine will get a hex color and return it, or return nothing
    #
    def get_color(self):
        data = wxColourData()
        data.SetChooseFull(true)
        dlg = wxColourDialog(self, data)
        if dlg.ShowModal() == wxID_OK:
            data = dlg.GetColourData()
            (red,green,blue) = data.GetColour().Get()
            hexcolor = self.r_h.hexstring(red, green, blue)
            dlg.Destroy()
            return hexcolor
        else:
            dlg.Destroy()
            return None
    # def get_color - end


    def replace_quotes(self,s):

        in_tag = 0
        i = 0
        rs = s[:]

        for c in s:
            if c == "<":
                in_tag += 1
            elif c == ">":
                if in_tag:
                    in_tag -= 1
            elif c == '"':
                if in_tag:
                    rs = rs[:i] + "'" + rs[i+1:]
            i += 1

        return rs

    def resolve_nodes(self,s):
        value = ""
        s = s[2:len(s)-2]
        node_path_list = s.split("::")
        node = None
        gametree=self.myopenrpg.get_component('tree')
        nodes = gametree.master_dom.getElementsByTagName('nodehandler')
        for nnodes in node_path_list:
            for entry in nodes:
                t_node = entry
                atts = t_node.getAttributeKeys()
                for attributess in range(0,len(atts)):
                    Value = t_node.getAttribute('name')
                    if Value == nnodes:
                        break
                if Value == nnodes:
                    break
            nodes = entry.getElementsByTagName('nodehandler')
        if Value != nnodes:
            return s
        nodetype = entry.getAttribute('class')
        if nodetype == "textctrl_handler":
            x = t_node.getElementsByTagName('text')
            t_node = x[0]._get_firstChild()
            value = t_node._get_nodeValue()
#        elif nodetype == "rpg_grid_handler":
#            x = t_node.getElementsByTagName('grid')
#            for grids in x:
#                t_node = x[0]._get_firstChild()
#                gn =x.getAttribute('name')
#		if gn == s1[1]:
#                    value = t_
#        print entry.getAttribute('class')
        if value =="":
            value = s
        return value


# ---------------------------------------------------------------------
# Some inline image data for the chat-tabs.
# This data was produced by running:  print repr(file('image.png').read())

def getAttentionData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x02\x00\x00\x00\x90\x91h6\x00\x00\x00\tpHYs\x00\x00\x0b\x12\x00\x00\x0b\x12\x01\xd2\xdd~\xfc\x00\x00\x01\x9dIDATx\x9c}\x8f]K\x94a\x14E\xd7>\xef\xe3h4\x83\x13\x89\x8a\x91\x1fEbQ]\xd4]\x7f\xbd_\x10\x84]\x14dZ\x84F9:\x96\xa3\x0ej\xd3<gwaB\x90v\xae\xd7\xda{\x1f\xd9il\x8f\xe4c\xb3g\xef;{\xc9\t\xd8L6tC\xf3\xd2\x82\xa3+uD\x14\xa80\xc6\xfb\x99\x9f2\xdf9\xb7\x93\x1d\xfbP\xb6\xe3\x06\xccT-D<\x14\x8f"\xeeI\xedb\xdb\xeeg\xbe\xadu=\xfd*s+\xf9.*8\xab\xc5\xcd\x88\xb9\xc2\xae8\x12#\xc7j1G\xce\x8f5\xd7\xc7\xf9\xd2\xde\xc2\xa7\x92A\xe0P\xc0\x89=\xfcU\x07\xc5\xe7Ac\xda\xc5\xd9\xaf~\x9f\xf9\xda\xfe\x00C\xd4\x82\x04.\x1c\x08H|0\xae\xeb\xa8[X\x08\xb3g\x7f\xae\xde\x80S(\x97\xf4\xdf\')P/s#\xd9\t\xbb\x8f{p \xf4\x0f\xfaG0\xd8i\xef\xa6\xbf\x06\xfai\x0f\xc9\x91\xaf\xc1/[\xaa\x19\xc3(.3\nn\xfek\\h\x84\xdc\x11]4\x85\xea\xb5\xb0!\x1b\xd1\x11\xd3!\xcd\x87V\x1a\xe6q\x00W\x0f\x93\xa0\x15Z\x0c-F0\xa7\xb8\x1f\xcd\x13i\xda\xaeW=\x9e\xd6\xd8\xb1\xa8X\x13+\x85\xe86^s\xf4\x93\xa1\xea\x1bq\x88m]\x94\t,Z\xf6\xf2DyQ\xf4,b\xa9\xa06\xb1\x14>+\x9e\xa8\xcd-{\xb3\xd6/\xf2\xb9e4\x15t\xa2\xb9+=mx\x1e\xf1\x18\xcd\xcaNS\xc9\x83\xcc^\xf5\xa6\xd9\xc6\xdf\x9c\xc7\xc6\x8a\x89\xf0,\xcd\x9d\xd0\x83\xd0\xb24\'M\xca6`\x8f\xf1\x19\x0c\xec\x1f\xf6\xc0\x9c\xd8\x19\x9a\x14m\xe26\x9a\t\xb5MK\xe87\xb49\xfd!Vt\xf5f\x00\x00\x00\x00IEND\xaeB`\x82'

def getAttentionBitmap():
    return wxBitmapFromImage(getAttentionImage())

def getAttentionImage():
    stream = cStringIO.StringIO(getAttentionData())
    return wxImageFromStream(stream)


def getNormalData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x02\x00\x00\x00\x90\x91h6\x00\x00\x00\tpHYs\x00\x00\x0b\x12\x00\x00\x0b\x12\x01\xd2\xdd~\xfc\x00\x00\x01\x9eIDATx\x9c\x8d\xcb\xcfK\xd3q\x1c\xc7\xf1\xd7\xeb\xfd\xf9\xda\x886\x1c$\xb9\x8c\x99V\xb3\x15\x16\xe8\x8eI\x87\x0e\xb1\xf5\x0fx\xebo\x88N\xfd\x03\xd1\xbf\xe1\xcds\x04\x99\x04\x15\x11A\x87\x141b?\x88pPN\x17\xe5\\\x10\xad\xef\xe7\xfd\xea\xd0\xa5\xc3\x98>\xcf\x8f\'%\x17$\r\xa8CaO\xda\x97w\x1c}@B& o,\x90S\xb2<\x99#,\x01"\x90B\xfb\xee\x9f\xdc?\xc8w\x1cm\xe9\x07%\xd9I`"r\xca\xec\nq\xd5\xec\x02\x99M$I]\xf7\xed\x187\\\xef\xdc[\xd5\xb9\x06\xfe\xebYk!\xc1.\xd1#\x06\xb29F\xef*n\xa7\xfe&\x8d\xcf\xab\xa5\xd7\x18Vmy\xf5\xfe\xc3\'I\xb8i\xb6\xc44\xfd\x18\xfde\x8c\xeb\xb7/=\x1e\xaa\xff\xb5\xd6(\'\xc9\xad\xc4\xaa&\xecI\x9f\xa3\xea#4\x80\xda\xe5\xba{\xdd\xd16\xa9\x0buj\xa5\xc6\xe8\x01\x80\xb4\xeb\xfab\xe0o\xe9\xe7\x91\x1a@\xbfw\n\x18\x18\x00\x80\xc7\x19\x00\x900*G\xe4\x8f\xa3s\xe3N\x8c\x1bY0\xce\xae7\x17\x8e\x1c\x8c\xd3\xc6i3L\xd2.Z\xb8v\xb6\xd8\x1e\xa1\x9f\xb6\x16ieb\xd6`\xf9\xc0r\xb0\xca\xca\xab{\xb5\xe5\xd5\xa1z\xadY\x19\x0b7\x12[4;O\xd7\x1fx\xcfU\x8fq+\xea\xbd\xd4|p\xf7\xd1\xe6\xdb%\x00\x85b{\xe5\xc5\x1d\x0bE\xf2z@%\xd8<m\x86\x92\x0b\x11\xfe\xcd\xbd\x13\xd5\x14v\xa0\xaf\xf2CA\xb41\xd3\x19\x84s\xc6\x92q\x86\x9c$3\x94\x04@J\xa1_\xc0\x81\xf4]:\x10\xfa\x92\x1b3D\x16v\x1a\x9c0f\x85\x13\x04\xff\x02\xf1\xb8\xd1\xec\x08\xfa\x9c\x89\x00\x00\x00\x00IEND\xaeB`\x82'


def getNormalBitmap():
    return wxBitmapFromImage(getNormalImage())

def getNormalImage():
    stream = cStringIO.StringIO(getNormalData())
    return wxImageFromStream(stream)