155
|
1 # Copyright (C) 2000-2001 The OpenRPG Project
|
|
2 #
|
|
3 # openrpg-dev@lists.sourceforge.net
|
|
4 #
|
|
5 # This program is free software; you can redistribute it and/or modify
|
|
6 # it under the terms of the GNU General Public License as published by
|
|
7 # the Free Software Foundation; either version 2 of the License, or
|
|
8 # (at your option) any later version.
|
|
9 #
|
|
10 # This program is distributed in the hope that it will be useful,
|
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13 # GNU General Public License for more details.
|
|
14 #
|
|
15 # You should have received a copy of the GNU General Public License
|
|
16 # along with this program; if not, write to the Free Software
|
|
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
18 # --
|
|
19 #
|
|
20 # File: chatutils.py
|
|
21 # Author: Chris Davis
|
|
22 # Maintainer:
|
|
23 # Version:
|
|
24 # $Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $
|
|
25 #
|
|
26 # Description: This file contains some of the basic definitions for the chat
|
|
27 # utilities in the orpg project.
|
|
28 #
|
|
29 # History
|
|
30 # 2002-01-20 HeroMan
|
|
31 # + Added 4 dialog items on toolbar in support of Alias Library Functionallity
|
|
32 # + Shrunk the text view button to an image
|
|
33 # 2005-04-25 Snowdog
|
|
34 # + Added simple_html_repair() to post() to fix malformed html in the chat window
|
|
35 # + Added strip_script_tags() to post() to remove crash point. See chat_util.py
|
|
36 # 2005-04-25 Snowdog
|
|
37 # + Added simple_html_repair() to post() to fix malformed html in the chat window
|
|
38 #
|
|
39
|
|
40 __version__ = "$Id: chatwnd.py,v 1.177 2007/12/07 20:39:48 digitalxero Exp $"
|
|
41
|
|
42
|
|
43 ##
|
|
44 ## Module Loading
|
133
|
45 ##
|
|
46 import os, time, re, sys, traceback, webbrowser, commands, chat_msg, chat_util
|
|
47
|
155
|
48 from orpg.orpg_version import VERSION
|
|
49 from orpg.orpg_windows import *
|
|
50 from orpg.player_list import WG_LIST
|
|
51 from orpg.dirpath import dir_struct
|
|
52 #from orpg.tools.metamenus import MenuEx #Needed?
|
|
53 from string import *
|
133
|
54
|
155
|
55 import cStringIO # for reading inline imagedata as a stream
|
|
56 from HTMLParser import HTMLParser
|
|
57 from wx.lib.expando import EVT_ETC_LAYOUT_NEEDED
|
133
|
58
|
155
|
59 import orpg.tools.rgbhex
|
|
60 import orpg.tools.inputValidator
|
|
61 from orpg.tools.validate import validate
|
133
|
62 from orpg.tools.orpg_settings import settings
|
155
|
63 import orpg.tools.predTextCtrl
|
133
|
64 from orpg.tools.orpg_log import logger, debug
|
|
65 from orpg.orpgCore import component
|
|
66 from xml.etree.ElementTree import tostring
|
|
67
|
155
|
68 from orpg.networking.mplay_client import MPLAY_CONNECTED # needed to only send typing/not_typing messages while connected
|
|
69
|
|
70 NEWCHAT = False
|
|
71 try:
|
|
72 import wx.webview
|
|
73 NEWCHAT = True
|
|
74 except: pass
|
|
75 NEWCHAT = False
|
|
76
|
|
77 # Global parser for stripping HTML tags:
|
|
78 # The 'tag stripping' is implicit, because this parser echoes every
|
|
79 # type of html data *except* the tags.
|
|
80 class HTMLStripper(HTMLParser):
|
|
81
|
|
82 def __init__(self):
|
|
83 self.accum = ""
|
|
84 self.special_tags = ['hr', 'br', 'img']
|
|
85
|
|
86 def handle_data(self, data): # quote cdata literally
|
|
87 self.accum += data
|
|
88
|
|
89 def handle_entityref(self, name): # entities must be preserved exactly
|
|
90 self.accum += "&" + name + ";"
|
|
91
|
|
92 def handle_starttag(self, tag, attrs):
|
|
93 if tag in self.special_tags:
|
|
94 self.accum += '<' + tag
|
|
95 for attrib in attrs: self.accum += ' ' + attrib[0] + '="' + attrib[1] + '"'
|
|
96 self.accum += '>'
|
|
97
|
|
98 def handle_charref(self, name): # charrefs too
|
|
99 self.accum += "&#" + name + ";"
|
|
100 htmlstripper = HTMLStripper()
|
|
101
|
|
102 # utility function; see Post().
|
|
103
|
|
104 def strip_html(string):
|
|
105 "Return string tripped of html tags."
|
|
106 htmlstripper.reset()
|
|
107 htmlstripper.accum = ""
|
|
108 htmlstripper.feed(string)
|
|
109 htmlstripper.close()
|
|
110 return htmlstripper.accum
|
|
111
|
|
112
|
|
113 def log( settings, c, text ):
|
|
114 filename = settings.get_setting('GameLogPrefix')
|
|
115 if filename > '' and filename[0] != commands.ANTI_LOG_CHAR:
|
|
116 filename = filename + time.strftime( '-%Y-%m-%d.html', time.localtime( time.time() ) )
|
|
117 #filename = time.strftime( filename, time.localtime( time.time() ) )
|
|
118 timestamp = time.ctime(time.time())
|
|
119 header = '[%s] : ' % ( timestamp );
|
|
120 if settings.get_setting('TimeStampGameLog') != '1': header = ''
|
|
121 try:
|
|
122 f = open( dir_struct["user"] + filename, 'a' )
|
|
123 f.write( '<div class="'+c+'">%s%s</div>\n' % ( header, text ) )
|
|
124 f.close()
|
|
125 except:
|
|
126 print "could not open " + dir_struct["user"] + filename + ", ignoring..."
|
|
127 pass
|
|
128
|
|
129 # This class displayes the chat information in html?
|
|
130 #
|
|
131 # Defines:
|
|
132 # __init__(self, parent, id)
|
|
133 # OnLinkClicked(self, linkinfo)
|
|
134 # CalculateAllFonts(self, defaultsize)
|
|
135 # SetDefaultFontAndSize(self, fontname)
|
|
136 #
|
|
137 class chat_html_window(wx.html.HtmlWindow):
|
|
138 """ a wxHTMLwindow that will load links """
|
|
139 # initialization subroutine
|
|
140 #
|
|
141 # !self : instance of self
|
|
142 # !parent :
|
|
143 # !id :
|
|
144
|
|
145 def __init__(self, parent, id):
|
|
146 wx.html.HtmlWindow.__init__(self, parent, id,
|
|
147 style=wx.SUNKEN_BORDER|wx.html.HW_SCROLLBAR_AUTO|wx.NO_FULL_REPAINT_ON_RESIZE)
|
|
148 self.parent = parent
|
|
149 self.build_menu()
|
|
150 self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
|
|
151 self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
|
|
152 if "gtk2" in wx.PlatformInfo: self.SetStandardFonts()
|
|
153 # def __init__ - end
|
|
154
|
|
155
|
|
156 def onPopup(self, evt):
|
|
157 self.PopupMenu(self.menu)
|
|
158
|
|
159
|
|
160 def LeftUp(self, event):
|
|
161 event.Skip()
|
|
162 wx.CallAfter(self.parent.set_chat_text_focus, None)
|
|
163
|
|
164
|
|
165 def build_menu(self):
|
|
166 self.menu = wx.Menu()
|
|
167 item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
|
|
168 self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
|
|
169 self.menu.AppendItem(item)
|
|
170
|
|
171 def OnM_EditCopy(self, evt):
|
|
172 wx.TheClipboard.UsePrimarySelection(False)
|
|
173 wx.TheClipboard.Open()
|
|
174 wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
|
|
175 wx.TheClipboard.Close()
|
|
176
|
|
177
|
|
178 def scroll_down(self):
|
|
179 maxrange = self.GetScrollRange(wx.VERTICAL)
|
|
180 pagesize = self.GetScrollPageSize(wx.VERTICAL)
|
|
181 self.Scroll(-1, maxrange-pagesize)
|
|
182
|
|
183
|
|
184 def mouse_wheel(self, event):
|
|
185 amt = event.GetWheelRotation()
|
|
186 units = amt/(-(event.GetWheelDelta()))
|
|
187 self.ScrollLines(units*3)
|
|
188
|
|
189
|
|
190 def Header(self):
|
|
191 return '<html><body bgcolor="' + self.parent.bgcolor + '" text="' + self.parent.textcolor + '">'
|
|
192
|
|
193
|
|
194 def StripHeader(self):
|
|
195 return self.GetPageSource().replace(self.Header(), '')
|
|
196
|
|
197
|
|
198 def GetPageSource(self):
|
|
199 return self.GetParser().GetSource()
|
|
200
|
|
201 # This subroutine fires up the webbrowser when a link is clicked.
|
|
202 #
|
|
203 # !self : instance of self
|
|
204 # !linkinfo : instance of a class that contains the link information
|
|
205
|
|
206 def OnLinkClicked(self, linkinfo):
|
|
207 href = linkinfo.GetHref()
|
|
208 wb = webbrowser.get()
|
|
209 wb.open(href)
|
|
210 # def OnLinkClicked - end
|
|
211
|
|
212
|
|
213 def CalculateAllFonts(self, defaultsize):
|
|
214 return [int(defaultsize * 0.4),
|
|
215 int(defaultsize * 0.7),
|
|
216 int(defaultsize),
|
|
217 int(defaultsize * 1.3),
|
|
218 int(defaultsize * 1.7),
|
|
219 int(defaultsize * 2),
|
|
220 int(defaultsize * 2.5)]
|
|
221
|
|
222
|
|
223 def SetDefaultFontAndSize(self, fontname, fontsize):
|
|
224 """Set 'fontname' to the default chat font.
|
|
225 Returns current font settings in a (fontname, fontsize) tuple."""
|
|
226 self.SetFonts(fontname, "", self.CalculateAllFonts(int(fontsize)))
|
|
227 return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
|
|
228
|
|
229 # class chat_html_window - end
|
138
|
230 if NEWCHAT:
|
155
|
231 class ChatHtmlWindow(wx.webview.WebView):
|
|
232
|
|
233 def __init__(self, parent, id):
|
|
234 wx.webview.WebView.__init__(self, parent, id)
|
|
235 self.parent = parent
|
|
236 self.__font = wx.Font(10, wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, faceName='Ariel')
|
|
237 self.build_menu()
|
|
238 self.Bind(wx.EVT_LEFT_UP, self.LeftUp)
|
|
239 self.Bind(wx.EVT_RIGHT_DOWN, self.onPopup)
|
|
240 self.Bind(wx.webview.EVT_WEBVIEW_BEFORE_LOAD, self.OnLinkClicked)
|
|
241
|
|
242 #Wrapers so I dont have to add special Code
|
|
243 def SetPage(self, htmlstring):
|
|
244 self.SetPageSource(htmlstring)
|
|
245
|
138
|
246 def AppendToPage(self, htmlstring):
|
155
|
247 self.SetPageSource(self.GetPageSource() + htmlstring)
|
|
248
|
|
249 def GetFont(self):
|
|
250 return self.__font
|
|
251
|
|
252 def CalculateAllFonts(self, defaultsize):
|
|
253 return
|
|
254
|
|
255 def SetDefaultFontAndSize(self, fontname, fontsize):
|
|
256 self.__font = wx.Font(int(fontsize),
|
|
257 wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL,
|
|
258 wx.FONTWEIGHT_NORMAL, faceName=fontname)
|
|
259 try: self.SetPageSource(self.Header() + self.StripHeader())
|
|
260 except Exception, e: print e
|
|
261 return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize())
|
|
262
|
|
263 #Events
|
|
264 def OnLinkClicked(self, linkinfo):
|
|
265 href = linkinfo.GetHref()
|
|
266 wb = webbrowser.get()
|
|
267 wb.open(href)
|
|
268
|
|
269 def onPopup(self, evt):
|
|
270 self.PopupMenu(self.menu)
|
|
271
|
|
272 def LeftUp(self, event):
|
|
273 event.Skip()
|
|
274 wx.CallAfter(self.parent.set_chat_text_focus, None)
|
|
275
|
|
276 def OnM_EditCopy(self, evt):
|
|
277 wx.TheClipboard.UsePrimarySelection(False)
|
|
278 wx.TheClipboard.Open()
|
|
279 wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText()))
|
|
280 wx.TheClipboard.Close()
|
|
281
|
|
282 #Cutom Methods
|
|
283 def Header(self):
|
|
284 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>"
|
|
285
|
|
286 def StripHeader(self):
|
|
287 tmp = self.GetPageSource().split('<BODY>')
|
|
288 if tmp[-1].find('<body>') > -1: tmp = tmp[-1].split('<body>')
|
|
289 return tmp[-1]
|
|
290
|
|
291 def build_menu(self):
|
|
292 self.menu = wx.Menu()
|
|
293 item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy")
|
|
294 self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item)
|
|
295 self.menu.AppendItem(item)
|
|
296
|
|
297 def scroll_down(self):
|
|
298 maxrange = self.GetScrollRange(wx.VERTICAL)
|
|
299 pagesize = self.GetScrollPageSize(wx.VERTICAL)
|
|
300 self.Scroll(-1, maxrange-pagesize)
|
|
301
|
|
302 def mouse_wheel(self, event):
|
|
303 amt = event.GetWheelRotation()
|
|
304 units = amt/(-(event.GetWheelDelta()))
|
|
305 self.ScrollLines(units*3)
|
|
306 chat_html_window = ChatHtmlWindow
|
|
307
|
|
308 #########################
|
|
309 #chat frame window
|
|
310 #########################
|
|
311 # These are kinda global...and static..and should be located somewhere else
|
|
312 # then the middle of a file between two classes.
|
|
313
|
|
314 ###################
|
|
315 # Tab Types
|
|
316 ###################
|
|
317 MAIN_TAB = wx.NewId()
|
|
318 WHISPER_TAB = wx.NewId()
|
|
319 GROUP_TAB = wx.NewId()
|
|
320 NULL_TAB = wx.NewId()
|
|
321
|
|
322 # This class defines the tabbed 'notebook' that holds multiple chatpanels.
|
|
323 # It's the widget attached to the main application frame.
|
|
324 #
|
|
325 # Inherits: wxNotebook
|
|
326 #
|
|
327 # Defines:
|
|
328 # create_private_tab(self, playerid)
|
|
329 # get_tab_index(self, chatpanel)
|
|
330 # destroy_private_tab(self, chatpanel)
|
|
331 # OnPageChanged(self, event)
|
|
332 # set_default_font(self, font, fontsize)
|
|
333
|
|
334 class chat_notebook(orpgTabberWnd):
|
|
335
|
|
336 def __init__(self, parent, size):
|
|
337 orpgTabberWnd.__init__(self, parent, True, size=size,
|
|
338 style=FNB.FNB_DROPDOWN_TABS_LIST|FNB.FNB_NO_NAV_BUTTONS|FNB.FNB_MOUSE_MIDDLE_CLOSES_TABS)
|
|
339 self.settings = component.get("settings")
|
|
340 self.whisper_tabs = []
|
|
341 self.group_tabs = []
|
|
342 self.null_tabs = []
|
|
343 self.il = wx.ImageList(16, 16)
|
|
344 bmp = wx.Bitmap(dir_struct["icon"]+'player.gif')
|
|
345 self.il.Add(bmp)
|
|
346 bmp = wx.Bitmap(dir_struct["icon"]+'clear.gif')
|
|
347 self.il.Add(bmp)
|
|
348 self.SetImageList(self.il)
|
|
349 # Create "main" chatpanel tab, undeletable, connected to 'public' room.
|
|
350 self.MainChatPanel = chat_panel(self, -1, MAIN_TAB, 'all')
|
|
351 self.AddPage(self.MainChatPanel, "Main Room")
|
|
352 self.SetPageImage(0, 1)
|
|
353 self.chat_timer = wx.Timer(self, wx.NewId())
|
|
354 self.Bind(wx.EVT_TIMER, self.MainChatPanel.typingTimerFunc)
|
|
355 self.chat_timer.Start(1000)
|
|
356 # Hook up event handler for flipping tabs
|
|
357 self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.onPageChanged)
|
|
358 self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.onPageChanging)
|
|
359 self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.onCloseTab)
|
|
360 # html font/fontsize is global to all the notebook tabs.
|
|
361 self.font, self.fontsize = self.MainChatPanel.chatwnd.SetDefaultFontAndSize(self.settings.get_setting('defaultfont'), self.settings.get_setting('defaultfontsize'))
|
|
362 self.GMChatPanel = None
|
|
363 if self.settings.get_setting("GMWhisperTab") == '1':
|
|
364 self.create_gm_tab()
|
|
365 self.SetSelection(0)
|
|
366
|
|
367 def get_tab_index(self, chatpanel):
|
|
368 "Return the index of a chatpanel in the wxNotebook."
|
|
369
|
|
370 for i in xrange(self.GetPageCount()):
|
|
371 if (self.GetPage(i) == chatpanel):
|
|
372 return i
|
|
373
|
|
374 def create_gm_tab(self):
|
|
375 if self.GMChatPanel == None:
|
|
376 self.GMChatPanel = chat_panel(self, -1, MAIN_TAB, 'gm')
|
|
377 self.AddPage(self.GMChatPanel, "GM", False)
|
|
378 self.SetPageImage(self.GetPageCount()-1, 1)
|
|
379 self.GMChatPanel.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
|
|
380
|
|
381 def create_whisper_tab(self, playerid):
|
|
382 "Add a new chatpanel directly connected to integer 'playerid' via whispering."
|
|
383 private_tab = chat_panel(self, -1, WHISPER_TAB, playerid)
|
|
384 playername = strip_html(self.MainChatPanel.session.get_player_by_player_id(playerid)[0])
|
|
385 self.AddPage(private_tab, playername, False)
|
|
386 private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
|
|
387 self.whisper_tabs.append(private_tab)
|
|
388 self.newMsg(self.GetPageCount()-1)
|
|
389 self.AliasLib = component.get('alias')
|
|
390 wx.CallAfter(self.AliasLib.RefreshAliases)
|
|
391 return private_tab
|
|
392
|
|
393 def create_group_tab(self, group_name):
|
|
394 "Add a new chatpanel directly connected to integer 'playerid' via whispering."
|
|
395 private_tab = chat_panel(self, -1, GROUP_TAB, group_name)
|
|
396 self.AddPage(private_tab, group_name, False)
|
|
397 private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
|
|
398 self.group_tabs.append(private_tab)
|
|
399 self.newMsg(self.GetPageCount()-1)
|
|
400 self.AliasLib = component.get('alias')
|
|
401 wx.CallAfter(self.AliasLib.RefreshAliases)
|
|
402 return private_tab
|
|
403
|
|
404 def create_null_tab(self, tab_name):
|
|
405 "Add a new chatpanel directly connected to integer 'playerid' via whispering."
|
|
406 private_tab = chat_panel(self, -1, NULL_TAB, tab_name)
|
|
407 self.AddPage(private_tab, tab_name, False)
|
|
408 private_tab.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize)
|
|
409 self.null_tabs.append(private_tab)
|
|
410 self.newMsg(self.GetPageCount()-1)
|
|
411 self.AliasLib = component.get('alias')
|
|
412 wx.CallAfter(self.AliasLib.RefreshAliases)
|
|
413 return private_tab
|
|
414
|
|
415 def onCloseTab(self, evt):
|
|
416 try: tabid = evt.GetSelection()
|
|
417 except: tabid = self.GetSelection()
|
|
418 if self.GetPageText(tabid) == 'Main Room':
|
|
419 #send no close error to chat
|
|
420 evt.Veto()
|
|
421 return
|
|
422 if self.GetPageText(tabid) == 'GM':
|
|
423 msg = "Are You Sure You Want To Close This Page?"
|
|
424 dlg = wx.MessageDialog(self, msg, "NotebookCtrl Question",
|
|
425 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
|
|
426 if wx.Platform != '__WXMAC__':
|
|
427 dlg.SetFont(wx.Font(8, wx.NORMAL, wx.NORMAL, wx.NORMAL, False))
|
|
428
|
|
429 if dlg.ShowModal() in [wx.ID_NO]:
|
|
430 dlg.Destroy()
|
|
431 evt.Veto()
|
|
432 return
|
|
433 dlg.Destroy()
|
|
434 self.GMChatPanel = None
|
|
435 self.settings.set_setting("GMWhisperTab", "0")
|
|
436 panel = self.GetPage(tabid)
|
|
437 if panel in self.whisper_tabs: self.whisper_tabs.remove(panel)
|
|
438 elif panel in self.group_tabs: self.group_tabs.remove(panel)
|
|
439 elif panel in self.null_tabs: self.null_tabs.remove(panel)
|
|
440
|
|
441 def newMsg(self, tabid):
|
|
442 if tabid != self.GetSelection(): self.SetPageImage(tabid, 0)
|
|
443
|
|
444 def onPageChanging(self, event):
|
|
445 """When private chattabs are selected, set the bitmap back to 'normal'."""
|
|
446 event.Skip()
|
|
447
|
|
448 def onPageChanged(self, event):
|
|
449 """When private chattabs are selected, set the bitmap back to 'normal'."""
|
|
450 selected_idx = event.GetSelection()
|
|
451 self.SetPageImage(selected_idx, 1)
|
|
452 page = self.GetPage(selected_idx)
|
|
453 #wx.CallAfter(page.set_chat_text_focus, 0)
|
|
454 event.Skip()
|
|
455
|
|
456 """
|
|
457 This class defines and builds the Chat Frame for OpenRPG
|
|
458
|
|
459 Inherits: wxPanel
|
|
460
|
|
461 Defines:
|
|
462 __init__((self, parent, id, openrpg, sendtarget)
|
|
463 build_ctrls(self)
|
|
464 on_buffer_size(self,evt)
|
|
465 set_colors(self)
|
|
466 set_buffersize(self)
|
|
467 set_chat_text(self,txt)
|
|
468 OnChar(self,event)
|
|
469 on_chat_save(self,evt)
|
|
470 on_text_color(self,event)
|
|
471 colorize(self, color, text)
|
|
472 on_text_format(self,event)
|
|
473 OnSize(self,event)
|
|
474 scroll_down(self)
|
|
475 InfoPost(self,s)
|
|
476 Post(self,s="",send=False,myself=False)
|
|
477 ParsePost(self,s,send=False,myself=False)
|
|
478 ParseDice(self,s)
|
|
479 ParseNodes(self,s)
|
|
480 get_sha_checksum(self)
|
|
481 get_color(self)
|
|
482
|
|
483 """
|
|
484
|
|
485 class chat_panel(wx.Panel):
|
|
486
|
|
487 """
|
|
488 This is the initialization subroutine
|
|
489
|
|
490 !self : instance of self
|
|
491 !parent : parent that defines the chatframe
|
|
492 !id :
|
|
493 !openrpg :
|
|
494 !sendtarget: who gets outbound messages: either 'all' or a playerid
|
|
495 """
|
|
496
|
|
497
|
|
498 def __init__(self, parent, id, tab_type, sendtarget):
|
|
499 wx.Panel.__init__(self, parent, id)
|
|
500 logger._set_log_to_console(False)
|
|
501 self.session = component.get('session')
|
|
502 self.settings = component.get('settings')
|
|
503 self.activeplugins = component.get('plugins')
|
|
504 self.parent = parent
|
|
505 # who receives outbound messages, either "all" or "playerid" string
|
|
506 self.sendtarget = sendtarget
|
|
507 self.type = tab_type
|
|
508 #self.sound_player = component.get('sound') #Removing!
|
|
509 # create die roller manager
|
|
510 #self.DiceManager = component.get('DiceManager') #Removing!
|
|
511 # create rpghex tool
|
|
512 self.r_h = orpg.tools.rgbhex.RGBHex()
|
|
513 self.h = 0
|
|
514 self.set_colors()
|
|
515 self.version = VERSION
|
|
516 self.histidx = -1
|
|
517 self.temptext = ""
|
|
518 self.history = []
|
|
519 self.storedata = []
|
|
520 #self.lasthistevt = None
|
|
521 self.parsed=0
|
|
522 #chat commands
|
|
523 self.lockscroll = False # set the default to scrolling on.
|
|
524 self.chat_cmds = commands.chat_commands(self)
|
|
525 self.html_strip = strip_html
|
178
|
526 self.f_keys = {wx.WXK_F1: 'event.GetKeyCode() == wx.WXK_F1', wx.WXK_F2: 'event.GetKeyCode() == wx.WXK_F2',
|
|
527 wx.WXK_F3: 'event.GetKeyCode() == wx.WXK_F3', wx.WXK_F4: 'event.GetKeyCode() == wx.WXK_F4',
|
|
528 wx.WXK_F5: 'event.GetKeyCode() == wx.WXK_F5', wx.WXK_F6: 'event.GetKeyCode() == wx.WXK_F6',
|
|
529 wx.WXK_F7: 'event.GetKeyCode() == wx.WXK_F7', wx.WXK_F8: 'event.GetKeyCode() == wx.WXK_F8',
|
|
530 wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10',
|
|
531 wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'}
|
155
|
532 #Alias Lib stuff
|
|
533 self.defaultAliasName = 'Use Real Name'
|
|
534 self.defaultFilterName = 'No Filter'
|
|
535 self.advancedFilter = False
|
|
536 self.lastSend = 0 # this is used to help implement the player typing indicator
|
|
537 self.lastPress = 0 # this is used to help implement the player typing indicator
|
|
538 self.Bind(wx.EVT_SIZE, self.OnSize)
|
|
539 self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnSize) #require to keep text at bottom of chat when text entry expands --SD
|
|
540 self.build_ctrls()
|
|
541 StartupFont = self.settings.get_setting("defaultfont")
|
|
542 StartupFontSize = self.settings.get_setting("defaultfontsize")
|
|
543 if(StartupFont != "") and (StartupFontSize != ""):
|
|
544 try: self.set_default_font(StartupFont, int(StartupFontSize))
|
|
545 except: pass
|
|
546 self.font = self.chatwnd.GetFont().GetFaceName()
|
|
547 self.fontsize = self.chatwnd.GetFont().GetPointSize()
|
|
548 self.scroll_down()
|
|
549
|
|
550 def set_default_font(self, fontname=None, fontsize=None):
|
|
551 """Set all chatpanels to new default fontname/fontsize.
|
|
552 Returns current font settings in a (fontname, fontsize) tuple."""
|
|
553 if (fontname is not None): newfont = fontname
|
|
554 else: newfont = self.font
|
|
555 if (fontsize is not None): newfontsize = int(fontsize)
|
|
556 else: newfontsize = int(self.fontsize)
|
|
557 self.chatwnd.SetDefaultFontAndSize(newfont, newfontsize)
|
|
558 self.InfoPost("Font is now " + newfont + " point size " + `newfontsize`)
|
|
559 self.font = newfont
|
|
560 self.fontsize = newfontsize
|
|
561 return (self.font, self.fontsize)
|
|
562
|
|
563 def build_menu(self):
|
|
564 top_frame = component.get('frame')
|
|
565 menu = wx.Menu()
|
|
566 item = wx.MenuItem(menu, wx.ID_ANY, "&Background color", "Background color")
|
|
567 top_frame.Bind(wx.EVT_MENU, self.OnMB_BackgroundColor, item)
|
|
568 menu.AppendItem(item)
|
|
569 item = wx.MenuItem(menu, wx.ID_ANY, "&Text color", "Text color")
|
|
570 top_frame.Bind(wx.EVT_MENU, self.OnMB_TextColor, item)
|
|
571 menu.AppendItem(item)
|
|
572 menu.AppendSeparator()
|
|
573 item = wx.MenuItem(menu, wx.ID_ANY, "&Chat Focus\tCtrl-H", "Chat Focus")
|
|
574 self.setChatFocusMenu = item
|
|
575 top_frame.Bind(wx.EVT_MENU, self.set_chat_text_focus, item)
|
|
576 menu.AppendItem(item)
|
|
577 menu.AppendSeparator()
|
|
578 item = wx.MenuItem(menu, wx.ID_ANY, "Toggle &Scroll Lock", "Toggle Scroll Lock")
|
|
579 top_frame.Bind(wx.EVT_MENU, self.lock_scroll, item)
|
|
580 menu.AppendItem(item)
|
|
581 item = wx.MenuItem(menu, wx.ID_ANY, "Save Chat &Log", "Save Chat Log")
|
|
582 top_frame.Bind(wx.EVT_MENU, self.on_chat_save, item)
|
|
583 menu.AppendItem(item)
|
|
584 item = wx.MenuItem(menu, wx.ID_ANY, "Text &View", "Text View")
|
|
585 top_frame.Bind(wx.EVT_MENU, self.pop_textpop, item)
|
|
586 menu.AppendItem(item)
|
|
587 item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Tab", "Swap Tabs")
|
|
588 top_frame.Bind(wx.EVT_MENU, self.forward_tabs, item)
|
|
589 menu.AppendItem(item)
|
|
590 item = wx.MenuItem(menu, wx.ID_ANY, "Forward Tab\tCtrl+Shift+Tab", "Swap Tabs")
|
|
591 top_frame.Bind(wx.EVT_MENU, self.back_tabs, item)
|
|
592 menu.AppendItem(item)
|
|
593 menu.AppendSeparator()
|
|
594 settingmenu = wx.Menu()
|
|
595 wndmenu = wx.Menu()
|
|
596 tabmenu = wx.Menu()
|
|
597 toolmenu = wx.Menu()
|
|
598 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show Images", "Show Images", wx.ITEM_CHECK)
|
|
599 top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowImages, item)
|
|
600
|
|
601 wndmenu.AppendItem(item)
|
|
602 if self.settings.get_setting("Show_Images_In_Chat") == '1': item.Check(True)
|
|
603 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Strip HTML", "Strip HTML", wx.ITEM_CHECK)
|
|
604 top_frame.Bind(wx.EVT_MENU, self.OnMB_StripHTML, item)
|
|
605 wndmenu.AppendItem(item)
|
|
606 if self.settings.get_setting("striphtml") == '1': item.Check(True)
|
|
607 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Time Index", "Chat Time Index", wx.ITEM_CHECK)
|
|
608 top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatTimeIndex, item)
|
|
609 wndmenu.AppendItem(item)
|
|
610 if self.settings.get_setting("Chat_Time_Indexing") == '1': item.Check(True)
|
|
611 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Chat Auto Complete", "Chat Auto Complete", wx.ITEM_CHECK)
|
|
612 top_frame.Bind(wx.EVT_MENU, self.OnMB_ChatAutoComplete, item)
|
|
613 wndmenu.AppendItem(item)
|
|
614 if self.settings.get_setting("SuppressChatAutoComplete") == '0': item.Check(True)
|
|
615 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show ID in Chat", "Show ID in Chat", wx.ITEM_CHECK)
|
|
616 top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowIDinChat, item)
|
|
617 wndmenu.AppendItem(item)
|
|
618 if self.settings.get_setting("ShowIDInChat") == '1': item.Check(True)
|
|
619 item = wx.MenuItem(wndmenu, wx.ID_ANY, "Log Time Index", "Log Time Index", wx.ITEM_CHECK)
|
|
620 top_frame.Bind(wx.EVT_MENU, self.OnMB_LogTimeIndex, item)
|
|
621 wndmenu.AppendItem(item)
|
|
622 if self.settings.get_setting("TimeStampGameLog") == '1': item.Check(True)
|
|
623 settingmenu.AppendMenu(wx.ID_ANY, 'Chat Window', wndmenu )
|
|
624 item = wx.MenuItem(tabmenu, wx.ID_ANY, "Tabbed Whispers", "Tabbed Whispers", wx.ITEM_CHECK)
|
|
625 top_frame.Bind(wx.EVT_MENU, self.OnMB_TabbedWhispers, item)
|
|
626 tabmenu.AppendItem(item)
|
|
627 if self.settings.get_setting("tabbedwhispers") == '1': item.Check(True)
|
|
628 item = wx.MenuItem(tabmenu, wx.ID_ANY, "GM Tab", "GM Tab", wx.ITEM_CHECK)
|
|
629 top_frame.Bind(wx.EVT_MENU, self.OnMB_GMTab, item)
|
|
630 tabmenu.AppendItem(item)
|
|
631 if self.settings.get_setting("GMWhisperTab") == '1':item.Check(True)
|
|
632 item = wx.MenuItem(tabmenu, wx.ID_ANY, "Group Whisper Tabs", "Group Whisper Tabs", wx.ITEM_CHECK)
|
|
633 top_frame.Bind(wx.EVT_MENU, self.OnMB_GroupWhisperTabs, item)
|
|
634 tabmenu.AppendItem(item)
|
|
635 if self.settings.get_setting("GroupWhisperTab") == '1': item.Check(True)
|
|
636 settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tabs', tabmenu)
|
|
637 item = wx.MenuItem(toolmenu, wx.ID_ANY, "Dice Bar", "Dice Bar", wx.ITEM_CHECK)
|
|
638 top_frame.Bind(wx.EVT_MENU, self.OnMB_DiceBar, item)
|
|
639 toolmenu.AppendItem(item)
|
|
640 if self.settings.get_setting("DiceButtons_On") == '1': item.Check(True)
|
|
641 item = wx.MenuItem(toolmenu, wx.ID_ANY, "Format Buttons", "Format Buttons", wx.ITEM_CHECK)
|
|
642 top_frame.Bind(wx.EVT_MENU, self.OnMB_FormatButtons, item)
|
|
643 toolmenu.AppendItem(item)
|
|
644 if self.settings.get_setting("FormattingButtons_On") == '1': item.Check(True)
|
|
645 item = wx.MenuItem(toolmenu, wx.ID_ANY, "Alias Tool", "Alias Tool", wx.ITEM_CHECK)
|
|
646 top_frame.Bind(wx.EVT_MENU, self.OnMB_AliasTool, item)
|
|
647 toolmenu.AppendItem(item)
|
|
648 if self.settings.get_setting("AliasTool_On") == '1': item.Check(True)
|
|
649 settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tool Bars', toolmenu)
|
|
650 menu.AppendMenu(wx.ID_ANY, 'Chat Settings', settingmenu)
|
|
651 top_frame.mainmenu.Insert(2, menu, '&Chat')
|
|
652
|
|
653 ## Settings Menu Events
|
|
654 def OnMB_ShowImages(self, event):
|
|
655 if event.IsChecked(): self.settings.set_setting("Show_Images_In_Chat", '1')
|
|
656 else: self.settings.set_setting("Show_Images_In_Chat", '0')
|
|
657
|
|
658 def OnMB_StripHTML(self, event):
|
|
659 if event.IsChecked(): self.settings.set_setting("striphtml", '1')
|
|
660 else: self.settings.set_setting("striphtml", '0')
|
|
661
|
|
662 def OnMB_ChatTimeIndex(self, event):
|
|
663 if event.IsChecked(): self.settings.set_setting("Chat_Time_Indexing", '1')
|
|
664 else: self.settings.set_setting("Chat_Time_Indexing", '0')
|
|
665
|
|
666 def OnMB_ChatAutoComplete(self, event):
|
|
667 if event.IsChecked(): self.settings.set_setting("SuppressChatAutoComplete", '0')
|
|
668 else: self.settings.set_setting("SuppressChatAutoComplete", '1')
|
|
669
|
|
670 def OnMB_ShowIDinChat(self, event):
|
|
671 if event.IsChecked(): self.settings.set_setting("ShowIDInChat", '1')
|
|
672 else: self.settings.set_setting("ShowIDInChat", '0')
|
|
673
|
|
674 def OnMB_LogTimeIndex(self, event):
|
|
675 if event.IsChecked(): self.settings.set_setting("TimeStampGameLog", '1')
|
|
676 else: self.settings.set_setting("TimeStampGameLog", '0')
|
|
677
|
|
678 def OnMB_TabbedWhispers(self, event):
|
|
679 if event.IsChecked(): self.settings.set_setting("tabbedwhispers", '1')
|
|
680 else: self.settings.set_setting("tabbedwhispers", '0')
|
|
681
|
|
682 def OnMB_GMTab(self, event):
|
|
683 if event.IsChecked():
|
|
684 self.settings.set_setting("GMWhisperTab", '1')
|
|
685 self.parent.create_gm_tab()
|
|
686 else: self.settings.set_setting("GMWhisperTab", '0')
|
|
687
|
|
688 def OnMB_GroupWhisperTabs(self, event):
|
|
689 if event.IsChecked(): self.settings.set_setting("GroupWhisperTab", '1')
|
|
690 else: self.settings.set_setting("GroupWhisperTab", '0')
|
|
691
|
|
692 def OnMB_DiceBar(self, event):
|
|
693 act = '0'
|
|
694 if event.IsChecked():
|
|
695 self.settings.set_setting("DiceButtons_On", '1')
|
|
696 act = '1'
|
|
697 else: self.settings.set_setting("DiceButtons_On", '0')
|
|
698 self.toggle_dice(act)
|
|
699 try: self.parent.GMChatPanel.toggle_dice(act)
|
|
700 except: pass
|
|
701 for panel in self.parent.whisper_tabs: panel.toggle_dice(act)
|
|
702 for panel in self.parent.group_tabs: panel.toggle_dice(act)
|
|
703 for panel in self.parent.null_tabs: panel.toggle_dice(act)
|
|
704
|
|
705 def OnMB_FormatButtons(self, event):
|
|
706 act = '0'
|
|
707 if event.IsChecked():
|
|
708 self.settings.set_setting("FormattingButtons_On", '1')
|
|
709 act = '1'
|
|
710 else:
|
|
711 self.settings.set_setting("FormattingButtons_On", '0')
|
|
712 self.toggle_formating(act)
|
|
713 try: self.parent.GMChatPanel.toggle_formating(act)
|
|
714 except: pass
|
|
715 for panel in self.parent.whisper_tabs: panel.toggle_formating(act)
|
|
716 for panel in self.parent.group_tabs: panel.toggle_formating(act)
|
|
717 for panel in self.parent.null_tabs: panel.toggle_formating(act)
|
|
718
|
|
719 def OnMB_AliasTool(self, event):
|
|
720 act = '0'
|
|
721 if event.IsChecked():
|
|
722 self.settings.set_setting("AliasTool_On", '1')
|
|
723 act = '1'
|
|
724 else: self.settings.set_setting("AliasTool_On", '0')
|
|
725 self.toggle_alias(act)
|
|
726 try: self.parent.GMChatPanel.toggle_alias(act)
|
|
727 except: pass
|
|
728 for panel in self.parent.whisper_tabs: panel.toggle_alias(act)
|
|
729 for panel in self.parent.group_tabs: panel.toggle_alias(act)
|
|
730 for panel in self.parent.null_tabs:panel.toggle_alias(act)
|
|
731
|
|
732 def OnMB_BackgroundColor(self, event):
|
|
733 top_frame = component.get('frame')
|
|
734 hexcolor = self.get_color()
|
|
735 if hexcolor != None:
|
|
736 self.bgcolor = hexcolor
|
|
737 self.settings.set_setting('bgcolor', hexcolor)
|
|
738 self.chatwnd.SetPage(self.ResetPage())
|
|
739 if self.settings.get_setting('ColorTree') == '1':
|
|
740 top_frame.tree.SetBackgroundColour(self.settings.get_setting('bgcolor'))
|
|
741 top_frame.tree.Refresh()
|
|
742 top_frame.players.SetBackgroundColour(self.settings.get_setting('bgcolor'))
|
|
743 top_frame.players.Refresh()
|
|
744 else:
|
|
745 top_frame.tree.SetBackgroundColour('white')
|
|
746 top_frame.tree.SetForegroundColour('black')
|
|
747 top_frame.tree.Refresh()
|
|
748 top_frame.players.SetBackgroundColour('white')
|
|
749 top_frame.players.SetForegroundColour('black')
|
|
750 top_frame.players.Refresh()
|
|
751 self.chatwnd.scroll_down()
|
|
752
|
|
753
|
|
754 def OnMB_TextColor(self, event):
|
|
755 top_frame = component.get('frame')
|
|
756 hexcolor = self.get_color()
|
|
757 if hexcolor != None:
|
|
758 self.textcolor = hexcolor
|
|
759 self.settings.set_setting('textcolor', hexcolor)
|
|
760 self.chatwnd.SetPage(self.ResetPage())
|
|
761 if self.settings.get_setting('ColorTree') == '1':
|
|
762 top_frame.tree.SetForegroundColour(self.settings.get_setting('textcolor'))
|
|
763 top_frame.tree.Refresh()
|
|
764 top_frame.players.SetForegroundColour(self.settings.get_setting('textcolor'))
|
|
765 top_frame.players.Refresh()
|
|
766 else:
|
|
767 top_frame.tree.SetBackgroundColour('white')
|
|
768 top_frame.tree.SetForegroundColour('black')
|
|
769 top_frame.tree.Refresh()
|
|
770 top_frame.players.SetBackgroundColour('white')
|
|
771 top_frame.players.SetForegroundColour('black')
|
|
772 top_frame.players.Refresh()
|
|
773 self.chatwnd.scroll_down()
|
|
774
|
|
775
|
|
776 def get_hot_keys(self):
|
|
777 # dummy menus for hotkeys
|
|
778 self.build_menu()
|
|
779 entries = []
|
|
780 entries.append((wx.ACCEL_CTRL, ord('H'), self.setChatFocusMenu.GetId()))
|
|
781 #entries.append((wx.ACCEL_CTRL, wx.WXK_TAB, SWAP_TABS))
|
|
782 return entries
|
|
783
|
|
784
|
|
785 def forward_tabs(self, evt):
|
|
786 self.parent.AdvanceSelection()
|
|
787
|
|
788 def back_tabs(self, evt):
|
|
789 self.parent.AdvanceSelection(False)
|
|
790
|
|
791 # This subroutine builds the controls for the chat frame
|
|
792 #
|
|
793 # !self : instance of self
|
|
794
|
|
795 def build_ctrls(self):
|
|
796 self.chatwnd = chat_html_window(self,-1)
|
|
797 self.set_colors()
|
|
798 wx.CallAfter(self.chatwnd.SetPage, self.chatwnd.Header())
|
|
799 if (self.sendtarget == "all"):
|
|
800 wx.CallAfter(self.Post, self.colorize(self.syscolor,
|
|
801 "<b>Welcome to <a href='http://www.openrpg.com'>OpenRPG</a> version " + self.version + "... </b>"))
|
|
802 #self.chat_cmds.on_help()
|
|
803 self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "",
|
|
804 style=wx.TE_PROCESS_ENTER |wx.TE_PROCESS_TAB|wx.TE_LINEWRAP,
|
|
805 keyHook = self.myKeyHook, validator=None )
|
|
806 self.build_bar()
|
|
807 self.basesizer = wx.BoxSizer(wx.VERTICAL)
|
|
808 self.basesizer.Add( self.chatwnd, 1, wx.EXPAND )
|
|
809 self.basesizer.Add( self.toolbar_sizer, 0, wx.EXPAND )
|
138
|
810 self.basesizer.Add( self.chattxt, 0, wx.EXPAND )
|
155
|
811 self.SetSizer(self.basesizer)
|
|
812 self.SetAutoLayout(True)
|
|
813 self.Fit()
|
|
814 #events
|
|
815 self.Bind(wx.EVT_BUTTON, self.on_text_format, self.boldButton)
|
|
816 self.Bind(wx.EVT_BUTTON, self.on_text_format, self.italicButton)
|
|
817 self.Bind(wx.EVT_BUTTON, self.on_text_format, self.underlineButton)
|
|
818 self.Bind(wx.EVT_BUTTON, self.on_text_color, self.color_button)
|
|
819 self.Bind(wx.EVT_BUTTON, self.on_chat_save, self.saveButton)
|
|
820 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d4Button)
|
|
821 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d6Button)
|
|
822 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d8Button)
|
|
823 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d10Button)
|
|
824 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d12Button)
|
|
825 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d20Button)
|
|
826 self.Bind(wx.EVT_BUTTON, self.onDieRoll, self.d100Button)
|
|
827 self.dieIDs = {}
|
|
828 self.dieIDs[self.d4Button.GetId()] = 'd4'
|
|
829 self.dieIDs[self.d6Button.GetId()] = 'd6'
|
|
830 self.dieIDs[self.d8Button.GetId()] = 'd8'
|
|
831 self.dieIDs[self.d10Button.GetId()] = 'd10'
|
|
832 self.dieIDs[self.d12Button.GetId()] = 'd12'
|
|
833 self.dieIDs[self.d20Button.GetId()] = 'd20'
|
|
834 self.dieIDs[self.d100Button.GetId()] = 'd100'
|
|
835 self.Bind(wx.EVT_BUTTON, self.pop_textpop, self.textpop_lock)
|
|
836 self.Bind(wx.EVT_BUTTON, self.lock_scroll, self.scroll_lock)
|
|
837 self.chattxt.Bind(wx.EVT_MOUSEWHEEL, self.chatwnd.mouse_wheel)
|
|
838 self.chattxt.Bind(wx.EVT_CHAR, self.chattxt.OnChar)
|
178
|
839 self.chattxt.Bind(wx.EVT_KEY_DOWN, self.on_chat_key_down)
|
155
|
840 self.chattxt.Bind(wx.EVT_TEXT_COPY, self.chatwnd.OnM_EditCopy)
|
|
841 # def build_ctrls - end
|
|
842
|
|
843 def build_bar(self):
|
|
844 self.toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
845 self.scroll_lock = None
|
|
846 self.numDieText = None
|
|
847 self.dieModText = None
|
|
848 if self.settings.get_setting('Toolbar_On') == "1":
|
|
849 self.build_alias()
|
|
850 self.build_dice()
|
|
851 self.build_scroll()
|
|
852 self.build_text()
|
|
853 self.toolbar_sizer.Add(self.textpop_lock, 0, wx.EXPAND)
|
|
854 self.toolbar_sizer.Add(self.scroll_lock, 0, wx.EXPAND)
|
|
855 self.build_formating()
|
|
856 self.build_colorbutton()
|
|
857
|
|
858
|
|
859 def build_scroll(self):
|
|
860 self.scroll_lock = wx.Button( self, wx.ID_ANY, "Scroll ON",size= wx.Size(80,25))
|
|
861
|
|
862
|
133
|
863 def build_alias(self):
|
155
|
864 self.aliasSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
865 self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName])
|
|
866 self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif',
|
138
|
867 'Refresh list of aliases from Game Tree',
|
155
|
868 wx.ID_ANY, '#bdbdbd' )
|
|
869 self.aliasList.SetSelection(0)
|
|
870 self.filterList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultFilterName])
|
|
871 self.filterButton = createMaskedButton( self, dir_struct["icon"] + 'add_filter.gif',
|
138
|
872 'Refresh list of filters from Game Tree',
|
155
|
873 wx.ID_ANY, '#bdbdbd' )
|
133
|
874 self.filterList.SetSelection(0)
|
|
875
|
155
|
876 self.aliasSizer.Add( self.aliasButton, 0, wx.EXPAND )
|
|
877 self.aliasSizer.Add( self.aliasList,0,wx.EXPAND)
|
|
878 self.aliasSizer.Add( self.filterButton, 0, wx.EXPAND )
|
133
|
879 self.aliasSizer.Add( self.filterList,0,wx.EXPAND)
|
|
880
|
|
881 self.toolbar_sizer.Add(self.aliasSizer, 0, wx.EXPAND)
|
|
882
|
155
|
883 if self.settings.get_setting('AliasTool_On') == '0': self.toggle_alias('0')
|
|
884 else: self.toggle_alias('1')
|
|
885
|
133
|
886 def toggle_alias(self, act):
|
|
887 if act == '0': self.toolbar_sizer.Show(self.aliasSizer, False)
|
|
888 else: self.toolbar_sizer.Show(self.aliasSizer, True)
|
155
|
889 self.toolbar_sizer.Layout()
|
|
890
|
|
891 def build_text(self):
|
|
892 self.textpop_lock = createMaskedButton(self, dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', wx.ID_ANY, '#bdbdbd')
|
|
893
|
|
894
|
136
|
895 def build_dice(self):
|
155
|
896 self.diceSizer = wx.BoxSizer(wx.HORIZONTAL)
|
136
|
897 self.numDieText = wx.TextCtrl( self, wx.ID_ANY, "1",
|
155
|
898 size= wx.Size(25, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
|
136
|
899 self.dieModText = wx.TextCtrl( self, wx.ID_ANY, "",
|
155
|
900 size= wx.Size(50, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() )
|
|
901 self.d4Button = createMaskedButton(self, dir_struct["icon"]+'b_d4.gif', 'Roll d4', wx.ID_ANY)
|
|
902 self.d6Button = createMaskedButton(self, dir_struct["icon"]+'b_d6.gif', 'Roll d6', wx.ID_ANY)
|
|
903 self.d8Button = createMaskedButton(self, dir_struct["icon"]+'b_d8.gif', 'Roll d8', wx.ID_ANY)
|
|
904 self.d10Button = createMaskedButton(self, dir_struct["icon"]+'b_d10.gif', 'Roll d10', wx.ID_ANY)
|
|
905 self.d12Button = createMaskedButton(self, dir_struct["icon"]+'b_d12.gif', 'Roll d12', wx.ID_ANY)
|
|
906 self.d20Button = createMaskedButton(self, dir_struct["icon"]+'b_d20.gif', 'Roll d20', wx.ID_ANY)
|
136
|
907 self.d100Button = createMaskedButton(self, dir_struct["icon"]+'b_d100.gif', 'Roll d100', wx.ID_ANY)
|
155
|
908
|
|
909 self.diceSizer.Add( self.numDieText, 0, wx.ALIGN_CENTER | wx.EXPAND)
|
|
910 self.diceSizer.Add( self.d4Button, 0 ,wx.EXPAND)
|
|
911 self.diceSizer.Add( self.d6Button, 0 ,wx.EXPAND)
|
|
912 self.diceSizer.Add( self.d8Button, 0 ,wx.EXPAND)
|
|
913 self.diceSizer.Add( self.d10Button, 0 ,wx.EXPAND)
|
|
914 self.diceSizer.Add( self.d12Button, 0 ,wx.EXPAND)
|
|
915 self.diceSizer.Add( self.d20Button, 0 ,wx.EXPAND)
|
|
916 self.diceSizer.Add( self.d100Button, 0 ,wx.EXPAND)
|
136
|
917 self.diceSizer.Add( self.dieModText, 0, wx.ALIGN_CENTER, 5 )
|
|
918
|
155
|
919 self.toolbar_sizer.Add( self.diceSizer, 0, wx.EXPAND)
|
|
920 if self.settings.get_setting('DiceButtons_On') == '0': self.toggle_dice('0')
|
|
921 else: self.toggle_dice('1')
|
|
922
|
|
923
|
|
924 def toggle_dice(self, act):
|
136
|
925 if act == '0': self.toolbar_sizer.Show(self.diceSizer, False)
|
|
926 else: self.toolbar_sizer.Show(self.diceSizer, True)
|
155
|
927 self.toolbar_sizer.Layout()
|
|
928
|
|
929
|
136
|
930 def build_formating(self):
|
155
|
931 self.formatSizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
932 self.boldButton = createMaskedButton( self, dir_struct["icon"]+'bold.gif',
|
|
933 'Make the selected text Bold', wx.ID_ANY, '#bdbdbd')
|
|
934 self.italicButton = createMaskedButton( self, dir_struct["icon"]+'italic.gif',
|
|
935 'Italicize the selected text', wx.ID_ANY, '#bdbdbd' )
|
|
936 self.underlineButton = createMaskedButton( self, dir_struct["icon"]+'underlined.gif',
|
136
|
937 'Underline the selected text', wx.ID_ANY, '#bdbdbd' )
|
155
|
938
|
|
939 self.formatSizer.Add( self.boldButton, 0, wx.EXPAND )
|
|
940 self.formatSizer.Add( self.italicButton, 0, wx.EXPAND )
|
136
|
941 self.formatSizer.Add( self.underlineButton, 0, wx.EXPAND )
|
155
|
942 self.toolbar_sizer.Add( self.formatSizer, 0, wx.EXPAND )
|
|
943 if self.settings.get_setting('FormattingButtons_On') == '0': self.toggle_formating('0')
|
|
944 else: self.toggle_formating('1')
|
|
945
|
|
946
|
|
947 def toggle_formating(self, act):
|
136
|
948 if act == '0': self.toolbar_sizer.Show(self.formatSizer, False)
|
|
949 else: self.toolbar_sizer.Show(self.formatSizer, True)
|
155
|
950 self.toolbar_sizer.Layout()
|
|
951
|
|
952 # Heroman - Ideally, we would use static labels...
|
|
953
|
|
954 def build_colorbutton(self):
|
|
955 self.color_button = createMaskedButton(self, dir_struct["icon"]+'textcolor.gif',
|
|
956 'Text Color', wx.ID_ANY, '#bdbdbd',
|
|
957 wx.BITMAP_TYPE_GIF)
|
|
958
|
|
959 self.saveButton = createMaskedButton(self, dir_struct["icon"]+'save.bmp',
|
|
960 'Save the chatbuffer', wx.ID_ANY,
|
|
961 '#c0c0c0', wx.BITMAP_TYPE_BMP )
|
|
962 self.color_button.SetBackgroundColour(self.settings.get_setting('mytextcolor'))
|
|
963 self.toolbar_sizer.Add(self.color_button, 0, wx.EXPAND)
|
|
964 self.toolbar_sizer.Add(self.saveButton, 0, wx.EXPAND)
|
|
965
|
|
966
|
|
967 def OnMotion(self, evt):
|
|
968 contain = self.chatwnd.GetInternalRepresentation()
|
|
969 if contain:
|
|
970 sx = sy = 0
|
|
971 x = y = 0
|
|
972 (sx,sy) = self.chatwnd.GetViewStart()
|
|
973 (sx1,sy1) = self.chatwnd.GetScrollPixelsPerUnit()
|
|
974 sx = sx*sx1
|
|
975 sy = sy*sy1
|
|
976 (x,y) = evt.GetPosition()
|
|
977 lnk = contain.GetLink(sx+x,sy+y)
|
|
978 if lnk:
|
|
979 try:
|
|
980 link = lnk.GetHref()
|
|
981 self.session.set_status_url(link)
|
|
982 except: pass
|
|
983 else: logger.general("Error, self.chatwnd.GetInternalRepresentation() return None")
|
|
984 evt.Skip()
|
|
985
|
|
986 # This subroutine is registered with predTextCtrl to be run for every OnChar event
|
|
987 # It checks if we need to send a typing message
|
|
988
|
|
989 #
|
|
990 # self: duh
|
|
991 # event: raw KeyEvent from OnChar()
|
|
992
|
|
993 def myKeyHook(self, event):
|
|
994 if self.session.get_status() == MPLAY_CONNECTED: # only do if we're connected
|
|
995 thisPress = time.time() # thisPress is local temp variable
|
|
996 if (thisPress - self.lastSend) > 4: # Check to see if it's been 5 seconds since our last notice
|
|
997 # If we're not already typing, then self.lastSend will be 0
|
|
998 self.sendTyping(1) # send a not typing event here (1 for True)
|
|
999 self.lastPress = thisPress # either way, record the time of this keystroke for use in
|
|
1000 # self.typingTimerFunc()
|
|
1001 if self.settings.get_setting('SuppressChatAutoComplete') == '1':
|
|
1002 logger.debug("Exit chat_panel->myKeyHook(self, event) return 1")
|
|
1003 return 1
|
|
1004 else:
|
|
1005 logger.debug("Exit chat_panel->myKeyHook(self, event) return 0")
|
|
1006 return 0
|
|
1007
|
|
1008 # This subroutine gets called once a second by the typing Timer
|
|
1009 # It checks if we need to send a not_typing message
|
|
1010 #
|
|
1011 # self: duh
|
|
1012
|
|
1013 def typingTimerFunc(self, event):
|
|
1014 #following added by mDuo13
|
|
1015 ##############refresh_counter()##############
|
|
1016 for plugin_fname in self.activeplugins.keys():
|
|
1017 plugin = self.activeplugins[plugin_fname]
|
|
1018 try: plugin.refresh_counter()
|
|
1019 except Exception, e:
|
|
1020 if str(e) != "'module' object has no attribute 'refresh_counter'":
|
|
1021 logger.general(traceback.format_exc())
|
|
1022 logger.general("EXCEPTION: " + str(e))
|
|
1023 #end mDuo13 added code
|
|
1024 if self.lastSend: # This will be zero when not typing, so equiv to if is_typing
|
|
1025 thisTime = time.time() # thisTime is a local temp variable
|
|
1026 if (thisTime - self.lastPress) > 4: # Check to see if it's been 5 seconds since our last keystroke
|
|
1027 # If we're not already typing, then self.lastSend will be 0
|
|
1028
|
|
1029 self.sendTyping(0) # send a typing event here (0 for False)
|
|
1030 # This subroutine actually takes care of sending the messages for typing/not_typing events
|
|
1031 #
|
|
1032 # self: duh
|
|
1033 # typing: boolean
|
|
1034
|
|
1035
|
|
1036 def sendTyping(self, typing):
|
|
1037 if typing:
|
|
1038 self.lastSend = time.time() # remember our send time for use in myKeyHook()
|
|
1039 #I think this is cleaner
|
|
1040 status_text = self.settings.get_setting('TypingStatusAlias')
|
|
1041 if status_text == "" or status_text == None: status_text = "Typing"
|
|
1042 self.session.set_text_status(status_text)
|
|
1043 else:
|
|
1044 self.lastSend = 0 # set lastSend to zero to indicate we're not typing
|
|
1045 #I think this is cleaner
|
|
1046 status_text = self.settings.get_setting('IdleStatusAlias')
|
|
1047 if status_text == "" or status_text == None: status_text = "Idle"
|
|
1048 self.session.set_text_status(status_text)
|
|
1049
|
|
1050 # This subroutine sets the colors of the chat based on the settings in the
|
|
1051 # self instance.
|
|
1052 #
|
|
1053 # !self : instance of self
|
|
1054
|
|
1055 def set_colors(self):
|
|
1056 # chat window backround color
|
|
1057 self.bgcolor = self.settings.get_setting('bgcolor')
|
|
1058 # chat window normal text color
|
|
1059 self.textcolor = self.settings.get_setting('textcolor')
|
|
1060 # color of text player types
|
|
1061 self.mytextcolor = self.settings.get_setting('mytextcolor')
|
|
1062 # color of system warnings
|
|
1063 self.syscolor = self.settings.get_setting('syscolor')
|
|
1064 # color of system info messages
|
|
1065 self.infocolor = self.settings.get_setting('infocolor')
|
|
1066 # color of emotes
|
|
1067 self.emotecolor = self.settings.get_setting('emotecolor')
|
|
1068 # color of whispers
|
|
1069 self.whispercolor = self.settings.get_setting('whispercolor')
|
|
1070 # def set_colors - end
|
|
1071
|
|
1072 # This subroutine will insert text into the chat window
|
|
1073 #
|
|
1074 # !self : instance of self
|
|
1075 # !txt : text to be inserted into the chat window
|
|
1076
|
|
1077 def set_chat_text(self, txt):
|
|
1078 self.chattxt.SetValue(txt)
|
|
1079 self.chattxt.SetFocus()
|
|
1080 self.chattxt.SetInsertionPointEnd()
|
|
1081 # def set_chat_text - end
|
|
1082
|
|
1083
|
|
1084 def get_chat_text(self):
|
|
1085 return self.chattxt.GetValue()
|
|
1086
|
|
1087 # This subroutine sets the focus to the chat window
|
|
1088
|
|
1089 def set_chat_text_focus(self, event):
|
|
1090 wx.CallAfter(self.chattxt.SetFocus)
|
|
1091 # def set_chat_text_focus - end
|
|
1092
|
|
1093 # This subrtouine grabs the user input and make the special keys and
|
|
1094 # modifiers work.
|
|
1095 #
|
|
1096 # !self : instance of self
|
|
1097 # !event :
|
|
1098 #
|
|
1099 # Note: self.chattxt now handles it's own Key events. It does, however still
|
|
1100 # call it's parent's (self) OnChar to handle "default" behavior.
|
178
|
1101
|
|
1102 def submit_chat_text(self, s):
|
|
1103 self.histidx = -1
|
|
1104 self.temptext = ""
|
|
1105 self.history = [s] + self.history
|
|
1106 #if not len(macroText): self.chattxt.SetValue("")
|
|
1107
|
|
1108 # play sound
|
|
1109 sound_file = self.settings.get_setting("SendSound")
|
|
1110 if sound_file != '': component.get('sound').play(sound_file)
|
|
1111 if s[0] != "/": ## it's not a slash command
|
|
1112 s = self.ParsePost( s, True, True )
|
|
1113 else: self.chat_cmds.docmd(s) # emote is in chatutils.py
|
|
1114
|
|
1115 def on_chat_key_down(self, event):
|
|
1116 s = self.chattxt.GetValue()
|
|
1117 if event.GetKeyCode() == wx.WXK_RETURN and not event.ShiftDown():
|
|
1118 logger.debug("event.GetKeyCode() == wx.WXK_RETURN")
|
|
1119 self.set_colors()
|
|
1120 if self.session.get_status() == MPLAY_CONNECTED:
|
|
1121 self.sendTyping(0)
|
|
1122 if len(s):
|
|
1123 self.chattxt.SetValue('')
|
|
1124 s = s.replace('\n', '<br />')
|
|
1125 self.submit_chat_text(s)
|
|
1126 return
|
|
1127 event.Skip()
|
155
|
1128
|
|
1129 def OnChar(self, event):
|
|
1130 s = self.chattxt.GetValue()
|
|
1131
|
178
|
1132 macroText = ""
|
|
1133 s_key = False
|
|
1134 if self.f_keys.has_key(event.GetKeyCode()): s_key = self.f_keys[event.GetKeyCode()]
|
155
|
1135
|
178
|
1136 if s_key: macroText = settings.get(s_key[29:])
|
155
|
1137
|
|
1138 # Append to the existing typed text as needed and make sure the status doesn't change back.
|
|
1139 if len(macroText):
|
|
1140 self.sendTyping(0)
|
178
|
1141 self.submit_chat_text(macroText)
|
155
|
1142
|
|
1143 ## UP KEY
|
|
1144 elif event.GetKeyCode() == wx.WXK_UP:
|
|
1145 logger.debug("event.GetKeyCode() == wx.WXK_UP")
|
|
1146 if self.histidx < len(self.history)-1:
|
|
1147 #text that's not in history but also hasn't been sent to chat gets stored in self.temptext
|
|
1148 #this way if someone presses the up key, they don't lose their current message permanently
|
|
1149 #(unless they also press enter at the time)
|
|
1150 if self.histidx is -1: self.temptext = self.chattxt.GetValue()
|
|
1151 self.histidx += 1
|
|
1152 self.chattxt.SetValue(self.history[self.histidx])
|
|
1153 self.chattxt.SetInsertionPointEnd()
|
|
1154 else:
|
|
1155 self.histidx = len(self.history) -1#in case it got too high somehow, this should fix it
|
|
1156 #self.InfoPost("**Going up? I don't think so.**")
|
|
1157 #print self.histidx, "in",self.history
|
|
1158
|
|
1159 ## DOWN KEY
|
|
1160 elif event.GetKeyCode() == wx.WXK_DOWN:
|
|
1161 logger.debug("event.GetKeyCode() == wx.WXK_DOWN")
|
|
1162 #histidx of -1 indicates currently viewing text that's not in self.history
|
|
1163 if self.histidx > -1:
|
|
1164 self.histidx -= 1
|
|
1165 if self.histidx is -1: #remember, it just decreased
|
|
1166 self.chattxt.SetValue(self.temptext)
|
|
1167 else: self.chattxt.SetValue(self.history[self.histidx])
|
|
1168 self.chattxt.SetInsertionPointEnd()
|
|
1169 else: self.histidx = -1 #just in case it somehow got below -1, this should fix it
|
|
1170 #self.InfoPost("**Going down? I don't think so.**")
|
|
1171 #print self.histidx, "in",self.history
|
|
1172
|
|
1173 ## TAB KEY
|
|
1174 elif event.GetKeyCode() == wx.WXK_TAB:
|
|
1175 logger.debug("event.GetKeyCode() == wx.WXK_TAB")
|
|
1176 if s !="":
|
|
1177 found = 0
|
|
1178 nicks = []
|
|
1179 testnick = ""
|
|
1180 inlength = len(s)
|
|
1181 for getnames in self.session.players.keys():
|
|
1182 striphtmltag = re.compile ('<[^>]+>*')
|
|
1183 testnick = striphtmltag.sub ("", self.session.players[getnames][0])
|
|
1184 if string.lower(s) == string.lower(testnick[:inlength]):
|
|
1185 found = found + 1
|
|
1186 nicks[len(nicks):]=[testnick]
|
|
1187 if found == 0: ## no nick match
|
|
1188 self.Post(self.colorize(self.syscolor," ** No match found"))
|
|
1189 elif found > 1: ## matched more than 1, tell user what matched
|
|
1190 nickstring = ""
|
|
1191 nicklist = []
|
|
1192 for foundnicks in nicks:
|
|
1193 nickstring = nickstring + foundnicks + ", "
|
|
1194 nicklist.append(foundnicks)
|
|
1195 nickstring = nickstring[:-2]
|
|
1196 self.Post(self.colorize(self.syscolor, " ** Multiple matches found: " + nickstring))
|
|
1197 # set text to the prefix match between first two matches
|
|
1198 settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(nicklist[0]))), string.lower(nicklist[1])).group()
|
|
1199 # run through the rest of the nicks
|
|
1200 for i in nicklist:
|
|
1201 settext = re.match(''.join(map(lambda x: '(%s?)' % x, string.lower(i))), string.lower(settext)).group()
|
|
1202 if settext:
|
|
1203 self.chattxt.SetValue(settext)
|
|
1204 self.chattxt.SetInsertionPointEnd()
|
|
1205 else: ## put the matched name in the chattxt box
|
|
1206 settext = nicks[0] + ": "
|
|
1207 self.chattxt.SetValue(settext)
|
|
1208 self.chattxt.SetInsertionPointEnd()
|
|
1209 else: ## not online, and no text in chattxt box
|
|
1210 self.Post(self.colorize(self.syscolor, " ** That's the Tab key, Dave"))
|
|
1211
|
|
1212 ## PAGE UP
|
|
1213 elif event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP):
|
|
1214 logger.debug("event.GetKeyCode() in (wx.WXK_PRIOR, wx.WXK_PAGEUP)")
|
|
1215 self.chatwnd.ScrollPages(-1)
|
|
1216 if not self.lockscroll: self.lock_scroll(0)
|
|
1217
|
|
1218 ## PAGE DOWN
|
|
1219 elif event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN):
|
|
1220 logger.debug("event.GetKeyCode() in (wx.WXK_NEXT, wx.WXK_PAGEDOWN)")
|
|
1221 if not self.lockscroll: self.lock_scroll(0)
|
|
1222 if ((self.chatwnd.GetScrollRange(1)-self.chatwnd.GetScrollPos(1)-self.chatwnd.GetScrollThumb(1) < 30) and self.lockscroll):
|
|
1223 self.lock_scroll(0)
|
|
1224 self.chatwnd.ScrollPages(1)
|
|
1225
|
|
1226 ## END
|
|
1227 elif event.GetKeyCode() == wx.WXK_END:
|
|
1228 logger.debug("event.GetKeyCode() == wx.WXK_END")
|
|
1229 if self.lockscroll:
|
|
1230 self.lock_scroll(0)
|
|
1231 self.Post()
|
|
1232 event.Skip()
|
|
1233
|
178
|
1234 elif event.GetKeyCode() == wx.WXK_RETURN and event.ShiftDown():
|
|
1235 st = self.chattxt.GetValue().split('\x0b')
|
|
1236 st += '\n'
|
|
1237 i = self.chattxt.GetInsertionPoint()
|
|
1238 self.chattxt.SetValue(''.join(st))
|
|
1239 self.chattxt.SetInsertionPoint(i+1)
|
|
1240 return
|
|
1241
|
155
|
1242 ## NOTHING
|
|
1243 else: event.Skip()
|
|
1244 logger.debug("Exit chat_panel->OnChar(self, event)")
|
|
1245 # def OnChar - end
|
|
1246
|
|
1247
|
|
1248 def onDieRoll(self, evt):
|
|
1249 """Roll the dice based on the button pressed and the die modifiers entered, if any."""
|
|
1250 # Get any die modifiers if they have been entered
|
|
1251 numDie = self.numDieText.GetValue()
|
|
1252 dieMod = self.dieModText.GetValue()
|
|
1253 dieText = numDie
|
|
1254 # Now, apply and roll die mods based on the button that was pressed
|
|
1255 id = evt.GetId()
|
|
1256 if self.dieIDs.has_key(id): dieText += self.dieIDs[id]
|
|
1257 if len(dieMod) and dieMod[0] not in "*/-+": dieMod = "+" + dieMod
|
|
1258 dieText += dieMod
|
|
1259 dieText = "[" + dieText + "]"
|
|
1260 self.ParsePost(dieText, 1, 1)
|
|
1261 self.chattxt.SetFocus()
|
|
1262
|
|
1263 # This subroutine saves a chat buffer as html to a file chosen via a
|
|
1264 # FileDialog.
|
|
1265 #
|
|
1266 # !self : instance of self
|
|
1267 # !evt :
|
|
1268
|
|
1269 def on_chat_save(self, evt):
|
|
1270 f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE)
|
|
1271 if f.ShowModal() == wx.ID_OK:
|
|
1272 file = open(f.GetPath(), "w")
|
|
1273 file.write(self.ResetPage() + "</body></html>")
|
|
1274 file.close()
|
|
1275 f.Destroy()
|
|
1276 os.chdir(dir_struct["home"])
|
|
1277 # def on_chat_save - end
|
|
1278
|
|
1279
|
|
1280 def ResetPage(self):
|
|
1281 self.set_colors()
|
|
1282 buffertext = self.chatwnd.Header() + "\n"
|
|
1283 buffertext += chat_util.strip_body_tags(self.chatwnd.StripHeader()).replace("<br>",
|
|
1284 "<br />").replace('</html>',
|
|
1285 '').replace("<br />",
|
|
1286 "<br />\n").replace("\n\n", '')
|
|
1287 return buffertext
|
|
1288
|
|
1289 # This subroutine sets the color of selected text, or base text color if
|
|
1290 # nothing is selected
|
|
1291
|
|
1292 def on_text_color(self, event):
|
|
1293 hexcolor = self.r_h.do_hex_color_dlg(self)
|
|
1294 if hexcolor != None:
|
|
1295 (beg,end) = self.chattxt.GetSelection()
|
|
1296 if beg != end:
|
|
1297 txt = self.chattxt.GetValue()
|
|
1298 txt = txt[:beg]+self.colorize(hexcolor,txt[beg:end]) +txt[end:]
|
|
1299 self.chattxt.SetValue(txt)
|
|
1300 self.chattxt.SetInsertionPointEnd()
|
|
1301 self.chattxt.SetFocus()
|
|
1302 else:
|
|
1303 self.color_button.SetBackgroundColour(hexcolor)
|
|
1304 self.mytextcolor = hexcolor
|
|
1305 self.settings.set_setting('mytextcolor',hexcolor)
|
|
1306 self.set_colors()
|
|
1307 self.Post()
|
|
1308 # def on_text_color - end
|
|
1309
|
|
1310 # This subroutine take a color and a text string and formats it into html.
|
|
1311 #
|
|
1312 # !self : instance of self
|
|
1313 # !color : color for the text to be set
|
|
1314 # !text : text string to be included in the html.
|
|
1315
|
|
1316 def colorize(self, color, text):
|
|
1317 """Puts font tags of 'color' around 'text' value, and returns the string"""
|
|
1318 return "<font color='" + color + "'>" + text + "</font>"
|
|
1319 # def colorize - end
|
|
1320
|
|
1321 # This subroutine takes and event and inserts text with the basic format
|
|
1322 # tags included.
|
|
1323 #
|
|
1324 # !self : instance of self
|
|
1325 # !event :
|
|
1326
|
|
1327 def on_text_format(self, event):
|
|
1328 id = event.GetId()
|
|
1329 txt = self.chattxt.GetValue()
|
|
1330 (beg,end) = self.chattxt.GetSelection()
|
|
1331 if beg != end: sel_txt = txt[beg:end]
|
|
1332 else: sel_txt = txt
|
|
1333 if id == self.boldButton.GetId(): sel_txt = "<b>" + sel_txt + "</b>"
|
|
1334 elif id == self.italicButton.GetId(): sel_txt = "<i>" + sel_txt + "</i>"
|
|
1335 elif id == self.underlineButton.GetId(): sel_txt = "<u>" + sel_txt + "</u>"
|
|
1336 if beg != end: txt = txt[:beg] + sel_txt + txt[end:]
|
|
1337 else: txt = sel_txt
|
|
1338 self.chattxt.SetValue(txt)
|
|
1339 self.chattxt.SetInsertionPointEnd()
|
|
1340 self.chattxt.SetFocus()
|
|
1341 # def on_text_format - end
|
|
1342
|
|
1343
|
|
1344 def lock_scroll(self, event):
|
|
1345 if self.lockscroll:
|
|
1346 self.lockscroll = False
|
|
1347 self.scroll_lock.SetLabel("Scroll ON")
|
|
1348 if len(self.storedata) != 0:
|
|
1349 for line in self.storedata: self.chatwnd.AppendToPage(line)
|
|
1350 self.storedata = []
|
|
1351 self.scroll_down()
|
|
1352 else:
|
|
1353 self.lockscroll = True
|
|
1354 self.scroll_lock.SetLabel("Scroll OFF")
|
|
1355
|
|
1356 # This subroutine will popup a text window with the chatbuffer contents
|
|
1357 #
|
|
1358 # !self : instance of self
|
|
1359 # !event :
|
|
1360
|
|
1361 def pop_textpop(self, event):
|
|
1362 """searchable popup text view of chatbuffer"""
|
|
1363 h_buffertext = self.ResetPage()
|
|
1364 h_dlg = orpgScrolledMessageFrameEditor(self, h_buffertext, "Text View of Chat Window", None, (500,300))
|
|
1365 h_dlg.Show(True)
|
|
1366
|
|
1367 # This subroutine will change the dimension of the window
|
|
1368 #
|
|
1369 # !self : instance of self
|
|
1370 # !event :
|
|
1371
|
|
1372 def OnSize(self, event=None):
|
|
1373 event.Skip()
|
|
1374 wx.CallAfter(self.scroll_down)
|
|
1375 # def OnSize - end
|
|
1376
|
|
1377
|
|
1378 def scroll_down(self):
|
|
1379 self.Freeze()
|
|
1380 self.chatwnd.scroll_down()
|
|
1381 self.Thaw()
|
|
1382
|
|
1383 ###### message helpers ######
|
|
1384
|
|
1385 def PurgeChat(self):
|
|
1386 self.set_colors()
|
|
1387 self.chatwnd.SetPage(self.chatwnd.Header())
|
|
1388
|
|
1389
|
|
1390 def system_message(self, text):
|
|
1391 self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE)
|
|
1392 self.SystemPost(text)
|
|
1393
|
|
1394
|
|
1395 def info_message(self, text):
|
|
1396 self.send_chat_message(text,chat_msg.INFO_MESSAGE)
|
|
1397 self.InfoPost(text)
|
|
1398
|
|
1399
|
|
1400 def get_gms(self):
|
|
1401 the_gms = []
|
|
1402 for playerid in self.session.players:
|
|
1403 if len(self.session.players[playerid])>7:
|
|
1404 if self.session.players[playerid][7]=="GM" and self.session.group_id != '0': the_gms += [playerid]
|
|
1405 return the_gms
|
|
1406
|
|
1407
|
|
1408 def GetName(self):
|
|
1409 self.AliasLib = component.get('alias')
|
|
1410 player = self.session.get_my_info()
|
|
1411 if self.AliasLib != None:
|
|
1412 self.AliasLib.alias = self.aliasList.GetStringSelection();
|
|
1413 if self.AliasLib.alias[0] != self.defaultAliasName:
|
|
1414 logger.debug("Exit chat_panel->GetName(self)")
|
|
1415 return [self.chat_display_name([self.AliasLib.alias[0], player[1], player[2]]), self.AliasLib.alias[1]]
|
|
1416 return [self.chat_display_name(player), "Default"]
|
|
1417
|
|
1418
|
|
1419 def GetFilteredText(self, text):
|
|
1420 advregex = re.compile('\"(.*?)\"', re.I)
|
|
1421 self.AliasLib = component.get('alias')
|
|
1422 if self.AliasLib != None:
|
|
1423 self.AliasLib.filter = self.filterList.GetSelection()-1;
|
|
1424 for rule in self.AliasLib.filterRegEx:
|
|
1425 if not self.advancedFilter: text = re.sub(rule[0], rule[1], text)
|
|
1426 else:
|
|
1427 for m in advregex.finditer(text):
|
|
1428 match = m.group(0)
|
|
1429 newmatch = re.sub(rule[0], rule[1], match)
|
|
1430 text = text.replace(match, newmatch)
|
|
1431 return text
|
|
1432
|
|
1433
|
|
1434 def emote_message(self, text):
|
|
1435 text = self.NormalizeParse(text)
|
|
1436 text = self.colorize(self.emotecolor, text)
|
|
1437
|
|
1438 if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE)
|
|
1439 elif self.type == MAIN_TAB and self.sendtarget == "gm":
|
|
1440 msg_type = chat_msg.WHISPER_EMOTE_MESSAGE
|
|
1441 the_gms = self.get_gms()
|
|
1442 for each_gm in the_gms: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(each_gm))
|
|
1443 elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
|
|
1444 for pid in WG_LIST[self.sendtarget]:
|
|
1445 self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(pid))
|
|
1446 elif self.type == WHISPER_TAB: self.send_chat_message(text,chat_msg.WHISPER_EMOTE_MESSAGE, str(self.sendtarget))
|
|
1447 elif self.type == NULL_TAB: pass
|
|
1448 name = self.GetName()[0]
|
|
1449 text = "** " + name + " " + text + " **"
|
|
1450 self.EmotePost(text)
|
|
1451
|
|
1452
|
|
1453 def whisper_to_players(self, text, player_ids):
|
|
1454 tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
|
|
1455 # Heroman - apply any filtering selected
|
|
1456 text = self.NormalizeParse(text)
|
|
1457 player_names = ""
|
|
1458 # post to our chat before we colorize
|
|
1459 for m in player_ids:
|
|
1460 id = m.strip()
|
|
1461 if self.session.is_valid_id(id):
|
|
1462 returned_name = self.session.get_player_by_player_id(id)[0]
|
|
1463 player_names += returned_name
|
|
1464 player_names += ", "
|
|
1465 else:
|
|
1466 player_names += " Unknown!"
|
|
1467 player_names += ", "
|
|
1468 comma = ","
|
|
1469 comma.join(player_ids)
|
|
1470 if (self.sendtarget == "all"):
|
|
1471 self.InfoPost("<i>whispering to "+ player_names + " " + text + "</i> ")
|
|
1472 # colorize and loop, sending whisper messages to all valid clients
|
|
1473 text = self.colorize(self.mytextcolor, text)
|
|
1474 for id in player_ids:
|
|
1475 id = id.strip()
|
|
1476 if self.session.is_valid_id(id): self.send_chat_message(text,chat_msg.WHISPER_MESSAGE,id)
|
|
1477 else: self.InfoPost(id + " Unknown!")
|
|
1478
|
|
1479
|
|
1480 def send_chat_message(self, text, type=chat_msg.CHAT_MESSAGE, player_id="all"):
|
|
1481 #########send_msg()#############
|
|
1482 send = 1
|
|
1483 for plugin_fname in self.activeplugins.keys():
|
|
1484 plugin = self.activeplugins[plugin_fname]
|
|
1485 try: text, send = plugin.send_msg(text, send)
|
|
1486 except Exception, e:
|
|
1487 if str(e) != "'module' object has no attribute 'send_msg'":
|
|
1488 logger.general(traceback.format_exc())
|
|
1489 logger.general("EXCEPTION: " + str(e))
|
|
1490 msg = chat_msg.chat_msg()
|
|
1491 msg.set_text(text)
|
|
1492 msg.set_type(type)
|
|
1493 turnedoff = False
|
|
1494 if self.settings.get_setting("ShowIDInChat") == "1":
|
|
1495 turnedoff = True
|
|
1496 self.settings.set_setting("ShowIDInChat", "0")
|
|
1497 playername = self.GetName()[0]
|
|
1498
|
|
1499 if turnedoff: self.settings.set_setting("ShowIDInChat", "1")
|
|
1500 msg.set_alias(playername)
|
|
1501 if send: self.session.send(msg.toxml(),player_id)
|
|
1502 del msg
|
|
1503
|
|
1504 #### incoming chat message handler #####
|
|
1505
|
|
1506 def post_incoming_msg(self, msg, player):
|
|
1507
|
|
1508 # pull data
|
|
1509 type = msg.get_type()
|
|
1510 text = msg.get_text()
|
|
1511 alias = msg.get_alias()
|
|
1512 # who sent us the message?
|
|
1513 if alias: display_name = self.chat_display_name([alias, player[1], player[2]])
|
|
1514 elif player: display_name = self.chat_display_name(player)
|
|
1515 else: display_name = "Server Administrator"
|
|
1516
|
|
1517 ######### START plugin_incoming_msg() ###########
|
|
1518 for plugin_fname in self.activeplugins.keys():
|
|
1519 plugin = self.activeplugins[plugin_fname]
|
|
1520 try: text, type, name = plugin.plugin_incoming_msg(text, type, display_name, player)
|
|
1521 except Exception, e:
|
|
1522 if str(e) != "'module' object has no attribute 'receive_msg'":
|
|
1523 logger.general(traceback.format_exc())
|
|
1524 logger.general("EXCEPTION: " + str(e))
|
|
1525 #end mDuo13 added code
|
|
1526 #image stripping for players' names
|
|
1527 strip_img = self.settings.get_setting("Show_Images_In_Chat")
|
|
1528 if (strip_img == "0"): display_name = chat_util.strip_img_tags(display_name)
|
|
1529 #end image stripping. --mDuo13, July 11th, 2005
|
|
1530 # default sound
|
|
1531 recvSound = "RecvSound"
|
|
1532 # act on the type of messsage
|
|
1533 if (type == chat_msg.CHAT_MESSAGE):
|
|
1534 text = "<b>" + display_name + "</b>: " + text
|
|
1535 self.Post(text)
|
|
1536 self.parent.newMsg(0)
|
|
1537 elif type == chat_msg.WHISPER_MESSAGE or type == chat_msg.WHISPER_EMOTE_MESSAGE:
|
|
1538 tabbed_whispers_p = self.settings.get_setting("tabbedwhispers")
|
|
1539 displaypanel = self
|
|
1540 whisperingstring = " (whispering): "
|
|
1541 panelexists = 0
|
|
1542 GMWhisperTab = self.settings.get_setting("GMWhisperTab")
|
|
1543 GroupWhisperTab = self.settings.get_setting("GroupWhisperTab")
|
|
1544 name = '<i><b>' + display_name + '</b>: '
|
|
1545 text += '</i>'
|
|
1546 panelexists = 0
|
|
1547 created = 0
|
|
1548 try:
|
|
1549 if GMWhisperTab == '1':
|
|
1550 the_gms = self.get_gms()
|
|
1551 #Check if whisper if from a GM
|
|
1552 if player[2] in the_gms:
|
|
1553 msg = name + ' (GM Whisper:) ' + text
|
|
1554 if type == chat_msg.WHISPER_MESSAGE: self.parent.GMChatPanel.Post(msg)
|
|
1555 else: self.parent.GMChatPanel.EmotePost("**" + msg + "**")
|
|
1556 idx = self.parent.get_tab_index(self.parent.GMChatPanel)
|
|
1557 self.parent.newMsg(idx)
|
|
1558 panelexists = 1
|
|
1559 #See if message if from someone in our groups or for a whisper tab we already have
|
|
1560 if not panelexists and GroupWhisperTab == "1":
|
|
1561 for panel in self.parent.group_tabs:
|
|
1562 if WG_LIST.has_key(panel.sendtarget) and WG_LIST[panel.sendtarget].has_key(int(player[2])):
|
|
1563 msg = name + text
|
|
1564 if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
|
|
1565 else: panel.EmotePost("**" + msg + "**")
|
|
1566 idx = self.parent.get_tab_index(panel)
|
|
1567 self.parent.newMsg(idx)
|
|
1568 panelexists = 1
|
|
1569 break
|
|
1570 if not panelexists and tabbed_whispers_p == "1":
|
|
1571 for panel in self.parent.whisper_tabs:
|
|
1572 #check for whisper tabs as well, to save the number of loops
|
|
1573 if panel.sendtarget == player[2]:
|
|
1574 msg = name + whisperingstring + text
|
|
1575 if type == chat_msg.WHISPER_MESSAGE: panel.Post(msg)
|
|
1576 else: panel.EmotePost("**" + msg + "**")
|
|
1577 idx = self.parent.get_tab_index(panel)
|
|
1578 self.parent.newMsg(idx)
|
|
1579 panelexists = 1
|
|
1580 break
|
|
1581 #We did not fint the tab
|
|
1582 if not panelexists:
|
|
1583 #If we get here the tab was not found
|
|
1584 if GroupWhisperTab == "1":
|
|
1585 for group in WG_LIST.keys():
|
|
1586 #Check if this group has the player in it
|
|
1587 if WG_LIST[group].has_key(int(player[2])):
|
|
1588 #Yup, post message. Player may be in more then 1 group so continue as well
|
|
1589 panel = self.parent.create_group_tab(group)
|
|
1590 msg = name + text
|
|
1591 if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
|
|
1592 else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
|
|
1593 created = 1
|
|
1594 #Check to see if we should create a whisper tab
|
|
1595 if not created and tabbed_whispers_p == "1":
|
|
1596 panel = self.parent.create_whisper_tab(player[2])
|
|
1597 msg = name + whisperingstring + text
|
|
1598 if type == chat_msg.WHISPER_MESSAGE: wx.CallAfter(panel.Post, msg)
|
|
1599 else: wx.CallAfter(panel.EmotePost, "**" + msg + "**")
|
|
1600 created = 1
|
|
1601 #Final check
|
|
1602 if not created:
|
|
1603 #No tabs to create, just send the message to the main chat tab
|
|
1604 msg = name + whisperingstring + text
|
|
1605 if type == chat_msg.WHISPER_MESSAGE: self.parent.MainChatPanel.Post(msg)
|
|
1606 else: self.parent.MainChatPanel.EmotePost("**" + msg + "**")
|
|
1607 self.parent.newMsg(0)
|
|
1608 except Exception, e:
|
|
1609 logger.general(traceback.format_exc())
|
|
1610 logger.general("EXCEPTION: 'Error in posting whisper message': " + str(e))
|
|
1611 elif (type == chat_msg.EMOTE_MESSAGE):
|
|
1612 text = "** " + display_name + " " + text + " **"
|
|
1613 self.EmotePost(text)
|
|
1614 self.parent.newMsg(0)
|
|
1615 elif (type == chat_msg.INFO_MESSAGE):
|
|
1616 text = "<b>" + display_name + "</b>: " + text
|
|
1617 self.InfoPost(text)
|
|
1618 self.parent.newMsg(0)
|
|
1619 elif (type == chat_msg.SYSTEM_MESSAGE):
|
|
1620 text = "<b>" + display_name + "</b>: " + text
|
|
1621 self.SystemPost(text)
|
|
1622 self.parent.newMsg(0)
|
|
1623 # playe sound
|
|
1624 sound_file = self.settings.get_setting(recvSound)
|
|
1625 if sound_file != '':
|
|
1626 component.get('sound').play(sound_file)
|
|
1627 #### Posting helpers #####
|
|
1628
|
|
1629
|
|
1630 def InfoPost(self, s):
|
|
1631 self.Post(self.colorize(self.infocolor, s), c='info')
|
|
1632
|
|
1633
|
|
1634 def SystemPost(self, s):
|
|
1635 self.Post(self.colorize(self.syscolor, s), c='system')
|
|
1636
|
|
1637
|
|
1638 def EmotePost(self, s):
|
|
1639 self.Post(self.colorize(self.emotecolor, s), c='emote')
|
|
1640
|
|
1641 #### Standard Post method #####
|
|
1642
|
|
1643 def Post(self, s="", send=False, myself=False, c='post'):
|
|
1644 strip_p = self.settings.get_setting("striphtml")
|
|
1645 strip_img = self.settings.get_setting("Show_Images_In_Chat")#moved back 7-11-05. --mDuo13
|
|
1646 if (strip_p == "1"): s = strip_html(s)
|
|
1647 if (strip_img == "0"): s = chat_util.strip_img_tags(s)
|
|
1648 s = chat_util.simple_html_repair(s)
|
|
1649 s = chat_util.strip_script_tags(s)
|
|
1650 s = chat_util.strip_li_tags(s)
|
|
1651 s = chat_util.strip_body_tags(s) #7-27-05 mDuo13
|
|
1652 s = chat_util.strip_misalignment_tags(s) #7-27-05 mDuo13
|
|
1653 aliasInfo = self.GetName()
|
|
1654 display_name = aliasInfo[0]
|
|
1655 if aliasInfo[1] != 'Default':
|
|
1656 defaultcolor = self.settings.get_setting("mytextcolor")
|
|
1657 self.settings.set_setting("mytextcolor", aliasInfo[1])
|
|
1658 self.set_colors()
|
|
1659 newline = ''
|
|
1660 #following added by mDuo13
|
|
1661 #########post_msg() - other##########
|
|
1662 if not myself and not send:
|
|
1663 for plugin_fname in self.activeplugins.keys():
|
|
1664 plugin = self.activeplugins[plugin_fname]
|
|
1665 try: s = plugin.post_msg(s, myself)
|
|
1666 except Exception, e:
|
|
1667 if str(e) != "'module' object has no attribute 'post_msg'":
|
|
1668 logger.general(traceback.format_exc())
|
|
1669 logger.general("EXCEPTION: " + str(e))
|
|
1670 #end mDuo13 added code
|
|
1671 if myself:
|
|
1672 name = "<b>" + display_name + "</b>: "
|
|
1673 s = self.colorize(self.mytextcolor, s)
|
|
1674 else: name = ""
|
|
1675 if aliasInfo[1] != 'Default':
|
|
1676 self.settings.set_setting("mytextcolor", defaultcolor)
|
|
1677 self.set_colors()
|
|
1678 #following line based on sourceforge patch #880403 from mDuo
|
|
1679 # EDIT: Had to rework blank line check to handle malformed HTML throwing error.
|
|
1680 # this limits the effectiveness of this check -SD
|
|
1681 lineHasText = 1
|
|
1682 try: lineHasText = strip_html(s).replace(" ","").replace(" ","").strip()!=""
|
|
1683 except:
|
|
1684 # HTML parser has errored out (most likely). Being as all we are doing is
|
|
1685 # scanning for empty/blank lines anyway there is no harm in letting a
|
138
|
1686 # troublesome message though. Worst case is a blank line to chat.
|
155
|
1687 lineHasText = 1
|
138
|
1688 if lineHasText:
|
155
|
1689 #following added by mDuo13
|
|
1690 if myself:
|
|
1691 s2 = s
|
|
1692 ########post_msg() - self #######
|
|
1693 for plugin_fname in self.activeplugins.keys():
|
|
1694 plugin = self.activeplugins[plugin_fname]
|
|
1695 try:
|
|
1696 s2 = plugin.post_msg(s2, myself)
|
|
1697 except Exception, e:
|
|
1698 if str(e) != "'module' object has no attribute 'post_msg'":
|
|
1699 logger.general(traceback.format_exc())
|
|
1700 logger.general("EXCEPTION: " + str(e))
|
|
1701 if s2 != "":
|
|
1702 #Italici the messages from tabbed whispers
|
|
1703 if self.type == WHISPER_TAB or self.type == GROUP_TAB or self.sendtarget == 'gm':
|
|
1704 s2 = s2 + '</i>'
|
|
1705 name = '<i>' + name
|
|
1706 if self.type == WHISPER_TAB: name += " (whispering): "
|
|
1707 elif self.type == GROUP_TAB: name += self.settings.get_setting("gwtext") + ' '
|
|
1708 elif self.sendtarget == 'gm': name += " (whispering to GM) "
|
|
1709 newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s2 + "</div>"
|
|
1710 log( self.settings, c, name+s2 )
|
|
1711 else:
|
|
1712 newline = "<div class='"+c+"'> " + self.TimeIndexString() + name + s + "</div>"
|
|
1713 log( self.settings, c, name+s )
|
|
1714 else: send = False
|
|
1715 newline = chat_util.strip_unicode(newline)
|
138
|
1716 if self.lockscroll == 0:
|
155
|
1717 self.chatwnd.AppendToPage(newline)
|
|
1718 self.scroll_down()
|
|
1719 else: self.storedata.append(newline)
|
133
|
1720 if send:
|
155
|
1721 if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(s)
|
|
1722 elif self.type == MAIN_TAB and self.sendtarget == "gm":
|
|
1723 the_gms = self.get_gms()
|
|
1724 self.whisper_to_players(s, the_gms)
|
|
1725 elif self.type == GROUP_TAB and WG_LIST.has_key(self.sendtarget):
|
|
1726 members = []
|
|
1727 for pid in WG_LIST[self.sendtarget]: members.append(str(WG_LIST[self.sendtarget][pid]))
|
|
1728 self.whisper_to_players(self.settings.get_setting("gwtext") + s, members)
|
|
1729 elif self.type == WHISPER_TAB: self.whisper_to_players(s, [self.sendtarget])
|
|
1730 elif self.type == NULL_TAB: pass
|
|
1731 else: self.InfoPost("Failed to send message, unknown send type for this tab")
|
|
1732 self.parsed=0
|
|
1733
|
|
1734 #
|
|
1735 # TimeIndexString()
|
|
1736 #
|
|
1737 # time indexing for chat display only (don't log time indexing)
|
|
1738 # added by Snowdog 4/04
|
|
1739
|
|
1740 def TimeIndexString(self):
|
|
1741 try:
|
|
1742 mtime = ""
|
|
1743 if self.settings.get_setting('Chat_Time_Indexing') == "0": pass
|
|
1744 elif self.settings.get_setting('Chat_Time_Indexing') == "1":
|
|
1745 mtime = time.strftime("[%I:%M:%S] ", time.localtime())
|
|
1746 return mtime
|
|
1747 except Exception, e:
|
|
1748 logger.general(traceback.format_exc())
|
|
1749 logger.general("EXCEPTION: " + str(e))
|
|
1750 return "[ERROR]"
|
|
1751
|
|
1752 #### Post with parsing dice ####
|
|
1753
|
|
1754 def ParsePost(self, s, send=False, myself=False):
|
|
1755 s = self.NormalizeParse(s)
|
|
1756 self.set_colors()
|
|
1757 self.Post(s,send,myself)
|
|
1758
|
|
1759 def NormalizeParse(self, s):
|
|
1760 for plugin_fname in self.activeplugins.keys():
|
|
1761 plugin = self.activeplugins[plugin_fname]
|
|
1762 try: s = plugin.pre_parse(s)
|
|
1763 except Exception, e:
|
|
1764 if str(e) != "'module' object has no attribute 'post_msg'":
|
|
1765 logger.general(traceback.format_exc())
|
|
1766 logger.general("EXCEPTION: " + str(e))
|
|
1767 if self.parsed == 0:
|
|
1768 s = self.ParseNode(s)
|
|
1769 s = self.ParseDice(s)
|
|
1770 s = self.ParseFilter(s)
|
|
1771 self.parsed = 1
|
|
1772 return s
|
|
1773
|
|
1774 def ParseFilter(self, s):
|
|
1775 s = self.GetFilteredText(s)
|
|
1776 return s
|
|
1777
|
|
1778 def ParseNode(self, s):
|
|
1779 """Parses player input for embedded nodes rolls"""
|
|
1780 cur_loc = 0
|
|
1781 #[a-zA-Z0-9 _\-\.]
|
|
1782 reg = re.compile("(!@(.*?)@!)")
|
|
1783 matches = reg.findall(s)
|
|
1784 for i in xrange(0,len(matches)):
|
|
1785 newstr = self.ParseNode(self.resolve_nodes(matches[i][1]))
|
|
1786 s = s.replace(matches[i][0], newstr, 1)
|
|
1787 return s
|
|
1788
|
|
1789 def ParseDice(self, s):
|
|
1790 """Parses player input for embedded dice rolls"""
|
|
1791 reg = re.compile("\[([^]]*?)\]")
|
|
1792 matches = reg.findall(s)
|
|
1793 for i in xrange(0,len(matches)):
|
|
1794 newstr = self.PraseUnknowns(matches[i])
|
|
1795 qmode = 0
|
|
1796 newstr1 = newstr
|
|
1797 if newstr[0].lower() == 'q':
|
|
1798 newstr = newstr[1:]
|
143
|
1799 qmode = 1
|
155
|
1800 if newstr[0].lower() == '#':
|
|
1801 newstr = newstr[1:]
|
|
1802 qmode = 2
|
|
1803 try: newstr = component.get('DiceManager').proccessRoll(newstr)
|
|
1804 except: pass
|
|
1805 if qmode == 1:
|
167
|
1806 s = s.replace("[" + matches[i] + "]",
|
|
1807 "<!-- Official Roll [" + newstr1 + "] => " + newstr + "-->" + newstr, 1)
|
143
|
1808 elif qmode == 2:
|
155
|
1809 s = s.replace("[" + matches[i] + "]", newstr[len(newstr)-2:-1], 1)
|
167
|
1810 else: s = s.replace("[" + matches[i] + "]",
|
|
1811 "[" + newstr1 + "<!-- Official Roll -->] => " + newstr, 1)
|
155
|
1812 return s
|
|
1813
|
|
1814 def PraseUnknowns(self, s):
|
|
1815 # Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY
|
|
1816 newstr = "0"
|
|
1817 reg = re.compile("(\?\{*)([a-zA-Z ]*)(\}*)")
|
|
1818 matches = reg.findall(s)
|
|
1819 for i in xrange(0,len(matches)):
|
|
1820 lb = "Replace '?' with: "
|
|
1821 if len(matches[i][0]):
|
|
1822 lb = matches[i][1] + "?: "
|
|
1823 dlg = wx.TextEntryDialog(self, lb, "Missing Value?")
|
|
1824 dlg.SetValue('')
|
|
1825 if matches[i][0] != '':
|
|
1826 dlg.SetTitle("Enter Value for " + matches[i][1])
|
|
1827 if dlg.ShowModal() == wx.ID_OK: newstr = dlg.GetValue()
|
|
1828 if newstr == '': newstr = '0'
|
|
1829 s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1)
|
|
1830 dlg.Destroy()
|
|
1831 return s
|
|
1832
|
|
1833 # This subroutine builds a chat display name.
|
|
1834 #
|
|
1835 def chat_display_name(self, player):
|
|
1836 if self.settings.get_setting("ShowIDInChat") == "0":
|
|
1837 display_name = player[0]
|
|
1838 else:
|
|
1839 display_name = "("+player[2]+") " + player[0]
|
|
1840 return display_name
|
|
1841
|
|
1842 # This subroutine will get a hex color and return it, or return nothing
|
|
1843 #
|
|
1844 def get_color(self):
|
|
1845 data = wx.ColourData()
|
|
1846 data.SetChooseFull(True)
|
|
1847 dlg = wx.ColourDialog(self, data)
|
|
1848 if dlg.ShowModal() == wx.ID_OK:
|
|
1849 data = dlg.GetColourData()
|
|
1850 (red,green,blue) = data.GetColour().Get()
|
|
1851 hexcolor = self.r_h.hexstring(red, green, blue)
|
|
1852 dlg.Destroy()
|
|
1853 return hexcolor
|
|
1854 else:
|
|
1855 dlg.Destroy()
|
|
1856 return None
|
|
1857 # def get_color - end
|
|
1858
|
|
1859 def replace_quotes(self, s):
|
|
1860 in_tag = 0
|
|
1861 i = 0
|
|
1862 rs = s[:]
|
|
1863 for c in s:
|
167
|
1864 if c == "<": in_tag += 1
|
155
|
1865 elif c == ">":
|
167
|
1866 if in_tag: in_tag -= 1
|
155
|
1867 elif c == '"':
|
167
|
1868 if in_tag: rs = rs[:i] + "'" + rs[i+1:]
|
155
|
1869 i += 1
|
|
1870 return rs
|
|
1871
|
|
1872 def resolve_loop(self, node, path, step, depth):
|
|
1873 if step == depth:
|
176
|
1874 return self.resolution(node)
|
155
|
1875 else:
|
|
1876 child_list = node.findall('nodehandler')
|
|
1877 for child in child_list:
|
|
1878 if step == depth: break
|
|
1879 if child.get('name') == path[step]:
|
136
|
1880 node = child
|
|
1881 step += 1
|
176
|
1882 if node.get('class') in ('dnd35char_handler',
|
|
1883 "SWd20char_handler",
|
|
1884 "d20char_handler",
|
|
1885 "dnd3echar_handler"): self.resolve_cust_loop(node, path, step, depth)
|
155
|
1886 elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, step, depth)
|
136
|
1887 else: self.resolve_loop(node, path, step, depth)
|
|
1888
|
138
|
1889 def resolve_grid(self, node, path, step, depth):
|
|
1890 if step == depth:
|
|
1891 self.data = 'Invalid Grid Reference!'
|
|
1892 return
|
|
1893 cell = tuple(path[step].strip('(').strip(')').split(','))
|
|
1894 grid = node.find('grid')
|
|
1895 rows = grid.findall('row')
|
161
|
1896 col = rows[int(self.ParseDice(cell[0]))-1].findall('cell')
|
|
1897 try: self.data = self.ParseMap(col[int(self.ParseDice(cell[1]))-1].text, node) or 'No Cell Data'
|
143
|
1898 except: self.data = 'Invalid Grid Reference!'
|
138
|
1899 return
|
|
1900
|
176
|
1901 def resolution(self, node):
|
|
1902 if self.passed == False:
|
|
1903 self.passed = True
|
|
1904 if node.get('class') == 'textctrl_handler':
|
|
1905 s = str(node.find('text').text)
|
|
1906 else: s = 'Nodehandler for '+ node.get('class') + ' not done!' or 'Invalid Reference!'
|
|
1907 else:
|
|
1908 s = ''
|
182
|
1909 s = self.ParseMap(s, node)
|
|
1910 s = self.ParseParent(s, node.get('map'))
|
|
1911 self.data = s
|
176
|
1912
|
|
1913 def ParseMap(self, s, node):
|
|
1914 """Parses player input for embedded nodes rolls"""
|
|
1915 cur_loc = 0
|
|
1916 reg = re.compile("(!!(.*?)!!)")
|
|
1917 matches = reg.findall(s)
|
|
1918 for i in xrange(0,len(matches)):
|
|
1919 tree_map = node.get('map')
|
|
1920 tree_map = tree_map + '::' + matches[i][1]
|
|
1921 newstr = '!@'+ tree_map +'@!'
|
|
1922 s = s.replace(matches[i][0], newstr, 1)
|
|
1923 s = self.ParseNode(s)
|
|
1924 s = self.ParseParent(s, tree_map)
|
|
1925 return s
|
|
1926
|
|
1927 def ParseParent(self, s, tree_map):
|
|
1928 """Parses player input for embedded nodes rolls"""
|
|
1929 cur_loc = 0
|
|
1930 reg = re.compile("(!#(.*?)#!)")
|
|
1931 matches = reg.findall(s)
|
|
1932 for i in xrange(0,len(matches)):
|
|
1933 ## Build the new tree_map
|
|
1934 new_map = tree_map.split('::')
|
|
1935 del new_map[len(new_map)-1]
|
|
1936 parent_map = matches[i][1].split('::')
|
|
1937 ## Find an index or use 1 for ease of use.
|
|
1938 try: index = new_map.index(parent_map[0])
|
|
1939 except: index = 1
|
|
1940 ## Just replace the old tree_map from the index.
|
|
1941 new_map[index:len(new_map)] = parent_map
|
|
1942 newstr = '::'.join(new_map)
|
|
1943 newstr = '!@'+ newstr +'@!'
|
|
1944 s = s.replace(matches[i][0], newstr, 1)
|
|
1945 #s = self.ParseMap(s, node) ## Needs to be added
|
|
1946 s = self.ParseNode(s)
|
|
1947 return s
|
|
1948
|
|
1949 def resolve_nodes(self, s):
|
|
1950 self.passed = False
|
|
1951 self.data = 'Invalid Reference!'
|
|
1952 value = ""
|
|
1953 path = s.split('::')
|
|
1954 depth = len(path)
|
182
|
1955 self.gametree = component.get('tree')
|
176
|
1956 try: node = self.gametree.tree_map[path[0]]['node']
|
|
1957 except Exception, e: return self.data
|
|
1958 if node.get('class') in ('dnd35char_handler',
|
|
1959 "SWd20char_handler",
|
|
1960 "d20char_handler",
|
|
1961 "dnd3echar_handler"): self.resolve_cust_loop(node, path, 1, depth)
|
|
1962 elif node.get('class') == 'rpg_grid_handler': self.resolve_grid(node, path, 1, depth)
|
|
1963 else: self.resolve_loop(node, path, 1, depth)
|
|
1964 return self.data
|
|
1965
|
136
|
1966 def resolve_cust_loop(self, node, path, step, depth):
|
|
1967 node_class = node.get('class')
|
141
|
1968 ## Code needs clean up. Either choose .lower() or .title(), then reset the path list's content ##
|
|
1969 if step == depth: self.resolution(node)
|
136
|
1970 ##Build Abilities dictionary##
|
|
1971 if node_class not in ('d20char_handler', "SWd20char_handler"): ab = node.find('character').find('abilities')
|
|
1972 else: ab = node.find('abilities')
|
|
1973 ab_list = ab.findall('stat'); pc_stats = {}
|
|
1974
|
|
1975 for ability in ab_list:
|
167
|
1976 pc_stats[ability.get('name')] = (
|
|
1977 str(ability.get('base')),
|
|
1978 str((int(ability.get('base'))-10)/2) )
|
|
1979 pc_stats[ability.get('abbr')] = (
|
|
1980 str(ability.get('base')),
|
|
1981 str((int(ability.get('base'))-10)/2) )
|
136
|
1982
|
|
1983 if node_class not in ('d20char_handler', "SWd20char_handler"): ab = node.find('character').find('saves')
|
|
1984 else: ab = node.find('saves')
|
|
1985 ab_list = ab.findall('save')
|
|
1986 for save in ab_list:
|
176
|
1987 pc_stats[save.get('name')] = (str(save.get('base')), str(int(save.get('magmod')) + int(save.get('miscmod')) + int(pc_stats[save.get('stat')][1]) ) )
|
136
|
1988 if save.get('name') == 'Fortitude': abbr = 'Fort'
|
|
1989 if save.get('name') == 'Reflex': abbr = 'Ref'
|
|
1990 if save.get('name') == 'Will': abbr = 'Will'
|
|
1991 pc_stats[abbr] = ( str(save.get('base')), str(int(save.get('magmod')) + int(save.get('miscmod')) + int(pc_stats[save.get('stat')][1]) ) )
|
|
1992
|
138
|
1993 if path[step].lower() == 'skill':
|
136
|
1994 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snf')
|
|
1995 node = node.find('skills')
|
|
1996 child_list = node.findall('skill')
|
|
1997 for child in child_list:
|
138
|
1998 if path[step+1].lower() == child.get('name').lower():
|
136
|
1999 if step+2 == depth: self.data = child.get('rank')
|
138
|
2000 elif path[step+2].lower() == 'check':
|
|
2001 self.data = '<b>Skill Check:</b> ' + child.get('name') + ' [1d20+'+str( int(child.get('rank')) + int(pc_stats[child.get('stat')][1]) )+']'
|
136
|
2002 return
|
|
2003
|
138
|
2004 if path[step].lower() == 'feat':
|
136
|
2005 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snf')
|
|
2006 node = node.find('feats')
|
|
2007 child_list = node.findall('feat')
|
|
2008 for child in child_list:
|
138
|
2009 if path[step+1].lower() == child.get('name').lower():
|
|
2010 if step+2 == depth: self.data = '<b>'+child.get('name')+'</b>'+': '+child.get('desc')
|
136
|
2011 return
|
138
|
2012 if path[step].lower() == 'cast':
|
136
|
2013 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('snp')
|
|
2014 node = node.find('spells')
|
|
2015 child_list = node.findall('spell')
|
|
2016 for child in child_list:
|
138
|
2017 if path[step+1].lower() == child.get('name').lower():
|
|
2018 if step+2 == depth: self.data = '<b>'+child.get('name')+'</b>'+': '+child.get('desc')
|
136
|
2019 return
|
138
|
2020 if path[step].lower() == 'attack':
|
136
|
2021 if node_class not in ('d20char_handler', "SWd20char_handler"): node = node.find('combat')
|
138
|
2022 if path[step+1].lower() == 'melee' or path[step+1].lower() == 'm':
|
|
2023 bonus_text = '(Melee)'
|
136
|
2024 bonus = node.find('attacks')
|
|
2025 bonus = bonus.find('melee')
|
141
|
2026 bonus = bonus.attrib; d = int(pc_stats['Str'][1])
|
138
|
2027 elif path[step+1].lower() == 'ranged' or path[step+1].lower() == 'r':
|
|
2028 bonus_text = '(Ranged)'
|
136
|
2029 bonus = node.find('attacks')
|
|
2030 bonus = bonus.find('ranged')
|
141
|
2031 bonus = bonus.attrib; d = int(pc_stats['Dex'][1])
|
136
|
2032 for b in bonus:
|
|
2033 d += int(bonus[b])
|
|
2034 bonus = str(d)
|
138
|
2035 if path[step+2] == None: self.data = bonus
|
136
|
2036 else:
|
|
2037 weapons = node.find('attacks')
|
|
2038 weapons = weapons.findall('weapon')
|
|
2039 for child in weapons:
|
138
|
2040 if path[step+2].lower() == child.get('name').lower():
|
|
2041 self.data = '<b>Attack: '+bonus_text+'</b> '+child.get('name')+' [1d20+'+bonus+'] ' + 'Damage: ['+child.get('damage')+']'
|
136
|
2042 return
|
141
|
2043 elif pc_stats.has_key(path[step].title()):
|
|
2044 if step+1 == depth: self.data = pc_stats[path[step].title()][0] + ' +('+pc_stats[path[step].title()][1]+')'
|
|
2045 elif path[step+1].title() == 'Mod': self.data = pc_stats[path[step].title()][1]
|
|
2046 elif path[step+1].title() == 'Check': self.data = '<b>'+path[step].title()+' Check:</b> [1d20+'+str(pc_stats[path[step].title()][1])+']'
|
136
|
2047 return
|