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