# HG changeset patch # User sirebral # Date 1257883888 21600 # Node ID dcf4fbe09b70419146707ce9f6193c06a281ecde # Parent 394ebb3b6a0f948cf0e2979811ace2898b0b3ae6 Traipse Beta 'OpenRPG' {091010-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) Added Bookmarks Fix to Remote Admin Commands Minor fix to text based Server Fix to Pretty Print, from Core Fix to Splitter Nodes not being created Fix to massive amounts of images loading, from Core Added 'boot' command to remote admin Added confirmation window for sent nodes Minor changes to allow for portability to an OpenSUSE linux OS Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Zoom Mouse plugin added Images added to Plugin UI Switching to Element Tree Map efficiency, from FlexiRPG Added Status Bar to Update Manager default_manifest.xml renamed to default_upmana.xml Cleaner clode for saved repositories New TrueDebug Class in orpg_log (See documentation for usage) Mercurial's hgweb folder is ported to upmana **Pretty important update that can help remove thousands of dead children from your gametree. **Children, , , , , , , etc... are all tags now. Check your gametree and look for dead children!! **New Gamtree Recusion method, mapping, and context sensitivity. !!Alpha - Watch out for infinite loops!! diff -r 394ebb3b6a0f -r dcf4fbe09b70 images/add.png Binary file images/add.png has changed diff -r 394ebb3b6a0f -r dcf4fbe09b70 images/add_button.png Binary file images/add_button.png has changed diff -r 394ebb3b6a0f -r dcf4fbe09b70 images/check_button.png Binary file images/check_button.png has changed diff -r 394ebb3b6a0f -r dcf4fbe09b70 images/star.png Binary file images/star.png has changed diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/chat/chat_msg.py --- a/orpg/chat/chat_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/chat/chat_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -31,8 +31,8 @@ from orpg.orpgCore import * from chat_version import CHAT_VERSION -from orpg.tools.orpg_log import logger -from orpg.tools.decorators import debugging +from orpg.tools.orpg_log import logger, debug +from xml.etree.ElementTree import tostring, fromstring CHAT_MESSAGE = 1 WHISPER_MESSAGE = 2 @@ -42,64 +42,43 @@ WHISPER_EMOTE_MESSAGE = 6 class chat_msg: - @debugging - def __init__(self,xml_text=""): - self.chat_dom = None + + def __init__(self, xml_text=""): + self.chat_dom = None self.takexml(xml_text) - @debugging def __del__(self): if self.chat_dom: self.chat_dom.unlink() - @debugging - def toxml(self): - return component.get('xml').toxml(self.chat_dom) + def toxml(self): + return tostring(self.chat_dom) - @debugging def takexml(self,xml_text): - xml_dom = component.get('xml').parseXml(xml_text) - node_list = xml_dom.getElementsByTagName("chat") - if len(node_list) < 1: - print "Warning: no elements found in DOM." - else: - if len(node_list) > 1: - print "Found more than one instance of <" + self.tagname + "/>. Taking first one" - self.takedom(node_list[0]) - - @debugging - def takedom(self,xml_dom): - if self.chat_dom: - self.text_node = None - self.chat_dom.unlink() - self.chat_dom = xml_dom - self.text_node = component.get('xml').safe_get_text_node(self.chat_dom) + xml_dom = fromstring(xml_text) + self.takedom(xml_dom) - @debugging - def set_text(self,text): - text = component.get('xml').strip_text(text) - self.text_node._set_nodeValue(text) - - @debugging + def takedom(self, xml_dom): + self.chat_dom = xml_dom + self.text_node = xml_dom.text + + def set_text(self, text): + self.chat_dom.text = text + def set_type(self,type): - self.chat_dom.setAttribute("type",str(type)) - - @debugging + self.chat_dom.set("type", str(type)) + def get_type(self): - return int(self.chat_dom.getAttribute("type")) + return int(self.chat_dom.get("type")) - @debugging def set_alias(self,alias): - self.chat_dom.setAttribute("alias",alias) + self.chat_dom.set("alias",alias) - @debugging def get_alias(self): - return self.chat_dom.getAttribute("alias") + return self.chat_dom.get("alias") - @debugging def get_text(self): - return self.text_node._get_nodeValue() + return self.text_node - @debugging def get_version(self): - return self.chat_dom.getAttribute("version") + return self.chat_dom.get("version") diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/chat/chatwnd.py --- a/orpg/chat/chatwnd.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/chat/chatwnd.py Tue Nov 10 14:11:28 2009 -0600 @@ -42,37 +42,30 @@ ## ## Module Loading -## +## +import os, time, re, sys, traceback, webbrowser, commands, chat_msg, chat_util + +from orpg.orpg_version import VERSION from orpg.orpg_windows import * from orpg.player_list import WG_LIST from orpg.dirpath import dir_struct +#from orpg.tools.metamenus import MenuEx #Needed? +from string import * + +import cStringIO # for reading inline imagedata as a stream +from HTMLParser import HTMLParser +from wx.lib.expando import EVT_ETC_LAYOUT_NEEDED + import orpg.tools.rgbhex import orpg.tools.inputValidator -#from orpg.tools.metamenus import MenuEx #Needed? - -import webbrowser -from string import * -from orpg.orpg_version import VERSION -import commands -import chat_msg -import time +from orpg.tools.validate import validate +from orpg.tools.orpg_settings import settings import orpg.tools.predTextCtrl +from orpg.tools.orpg_log import logger, debug +from orpg.orpgCore import component +from xml.etree.ElementTree import tostring + from orpg.networking.mplay_client import MPLAY_CONNECTED # needed to only send typing/not_typing messages while connected -import os -import time -import re -import sys -import cStringIO # for reading inline imagedata as a stream -from HTMLParser import HTMLParser -import chat_util -import traceback -from wx.lib.expando import EVT_ETC_LAYOUT_NEEDED - -from orpg.tools.validate import validate -from orpg.tools.orpg_settings import settings -from orpg.orpgCore import component -from orpg.tools.orpg_log import logger -from orpg.tools.decorators import debugging NEWCHAT = False try: @@ -84,39 +77,39 @@ # Global parser for stripping HTML tags: # The 'tag stripping' is implicit, because this parser echoes every # type of html data *except* the tags. -class HTMLStripper(HTMLParser): - @debugging +class HTMLStripper(HTMLParser): + def __init__(self): self.accum = "" - self.special_tags = ['hr', 'br', 'img'] - @debugging + self.special_tags = ['hr', 'br', 'img'] + def handle_data(self, data): # quote cdata literally - self.accum += data - @debugging + self.accum += data + def handle_entityref(self, name): # entities must be preserved exactly - self.accum += "&" + name + ";" - @debugging + self.accum += "&" + name + ";" + def handle_starttag(self, tag, attrs): if tag in self.special_tags: self.accum += '<' + tag for attrib in attrs: self.accum += ' ' + attrib[0] + '="' + attrib[1] + '"' - self.accum += '>' - @debugging + self.accum += '>' + def handle_charref(self, name): # charrefs too self.accum += "&#" + name + ";" htmlstripper = HTMLStripper() -# utility function; see Post(). -@debugging +# utility function; see Post(). + def strip_html(string): "Return string tripped of html tags." htmlstripper.reset() htmlstripper.accum = "" htmlstripper.feed(string) htmlstripper.close() - return htmlstripper.accum + return htmlstripper.accum -@debugging + def log( settings, c, text ): filename = settings.get_setting('GameLogPrefix') if filename > '' and filename[0] != commands.ANTI_LOG_CHAR: @@ -148,9 +141,9 @@ # !self : instance of self # !parent : # !id : - @debugging + def __init__(self, parent, id): - wx.html.HtmlWindow.__init__(self, parent, id, + wx.html.HtmlWindow.__init__(self, parent, id, style=wx.SUNKEN_BORDER|wx.html.HW_SCROLLBAR_AUTO|wx.NO_FULL_REPAINT_ON_RESIZE) self.parent = parent self.build_menu() @@ -159,50 +152,50 @@ if "gtk2" in wx.PlatformInfo: self.SetStandardFonts() # def __init__ - end - @debugging + def onPopup(self, evt): self.PopupMenu(self.menu) - @debugging + def LeftUp(self, event): event.Skip() wx.CallAfter(self.parent.set_chat_text_focus, None) - @debugging + def build_menu(self): self.menu = wx.Menu() item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy") self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item) self.menu.AppendItem(item) - @debugging - def OnM_EditCopy(self, evt): - wx.TheClipboard.UsePrimarySelection(False) - wx.TheClipboard.Open() - wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText())) - wx.TheClipboard.Close() + + def OnM_EditCopy(self, evt): + wx.TheClipboard.UsePrimarySelection(False) + wx.TheClipboard.Open() + wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText())) + wx.TheClipboard.Close() - @debugging + def scroll_down(self): maxrange = self.GetScrollRange(wx.VERTICAL) pagesize = self.GetScrollPageSize(wx.VERTICAL) self.Scroll(-1, maxrange-pagesize) - @debugging + def mouse_wheel(self, event): amt = event.GetWheelRotation() units = amt/(-(event.GetWheelDelta())) self.ScrollLines(units*3) - @debugging + def Header(self): return '' - @debugging + def StripHeader(self): return self.GetPageSource().replace(self.Header(), '') - @debugging + def GetPageSource(self): return self.GetParser().GetSource() @@ -210,14 +203,14 @@ # # !self : instance of self # !linkinfo : instance of a class that contains the link information - @debugging + def OnLinkClicked(self, linkinfo): href = linkinfo.GetHref() wb = webbrowser.get() wb.open(href) # def OnLinkClicked - end - @debugging + def CalculateAllFonts(self, defaultsize): return [int(defaultsize * 0.4), int(defaultsize * 0.7), @@ -227,17 +220,17 @@ int(defaultsize * 2), int(defaultsize * 2.5)] - @debugging + def SetDefaultFontAndSize(self, fontname, fontsize): """Set 'fontname' to the default chat font. Returns current font settings in a (fontname, fontsize) tuple.""" self.SetFonts(fontname, "", self.CalculateAllFonts(int(fontsize))) return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize()) - + # class chat_html_window - end if NEWCHAT: class ChatHtmlWindow(wx.webview.WebView): - @debugging + def __init__(self, parent, id): wx.webview.WebView.__init__(self, parent, id) self.parent = parent @@ -248,79 +241,79 @@ self.Bind(wx.webview.EVT_WEBVIEW_BEFORE_LOAD, self.OnLinkClicked) #Wrapers so I dont have to add special Code - @debugging + def SetPage(self, htmlstring): self.SetPageSource(htmlstring) - @debugging + def AppendToPage(self, htmlstring): self.SetPageSource(self.GetPageSource() + htmlstring) - @debugging + def GetFont(self): return self.__font - @debugging + def CalculateAllFonts(self, defaultsize): return - @debugging + def SetDefaultFontAndSize(self, fontname, fontsize): - self.__font = wx.Font(int(fontsize), - wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, + self.__font = wx.Font(int(fontsize), + wx.FONTFAMILY_ROMAN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, faceName=fontname) try: self.SetPageSource(self.Header() + self.StripHeader()) except Exception, e: print e return (self.GetFont().GetFaceName(), self.GetFont().GetPointSize()) #Events - @debugging + def OnLinkClicked(self, linkinfo): href = linkinfo.GetHref() wb = webbrowser.get() wb.open(href) - @debugging + def onPopup(self, evt): self.PopupMenu(self.menu) - @debugging + def LeftUp(self, event): event.Skip() wx.CallAfter(self.parent.set_chat_text_focus, None) - @debugging - def OnM_EditCopy(self, evt): - wx.TheClipboard.UsePrimarySelection(False) - wx.TheClipboard.Open() - wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText())) + + def OnM_EditCopy(self, evt): + wx.TheClipboard.UsePrimarySelection(False) + wx.TheClipboard.Open() + wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText())) wx.TheClipboard.Close() #Cutom Methods - @debugging + def Header(self): return "" - @debugging + def StripHeader(self): tmp = self.GetPageSource().split('') if tmp[-1].find('') > -1: tmp = tmp[-1].split('') return tmp[-1] - @debugging + def build_menu(self): self.menu = wx.Menu() item = wx.MenuItem(self.menu, wx.ID_ANY, "Copy", "Copy") self.Bind(wx.EVT_MENU, self.OnM_EditCopy, item) self.menu.AppendItem(item) - @debugging + def scroll_down(self): maxrange = self.GetScrollRange(wx.VERTICAL) pagesize = self.GetScrollPageSize(wx.VERTICAL) self.Scroll(-1, maxrange-pagesize) - @debugging + def mouse_wheel(self, event): amt = event.GetWheelRotation() units = amt/(-(event.GetWheelDelta())) @@ -353,10 +346,10 @@ # OnPageChanged(self, event) # set_default_font(self, font, fontsize) -class chat_notebook(orpgTabberWnd): - @debugging +class chat_notebook(orpgTabberWnd): + def __init__(self, parent, size): - orpgTabberWnd.__init__(self, parent, True, size=size, + orpgTabberWnd.__init__(self, parent, True, size=size, style=FNB.FNB_DROPDOWN_TABS_LIST|FNB.FNB_NO_NAV_BUTTONS|FNB.FNB_MOUSE_MIDDLE_CLOSES_TABS) self.settings = component.get("settings") self.whisper_tabs = [] @@ -385,24 +378,24 @@ if self.settings.get_setting("GMWhisperTab") == '1': self.create_gm_tab() self.SetSelection(0) - - @debugging + + def get_tab_index(self, chatpanel): "Return the index of a chatpanel in the wxNotebook." for i in xrange(self.GetPageCount()): if (self.GetPage(i) == chatpanel): return i - - @debugging + + def create_gm_tab(self): if self.GMChatPanel == None: self.GMChatPanel = chat_panel(self, -1, MAIN_TAB, 'gm') self.AddPage(self.GMChatPanel, "GM", False) self.SetPageImage(self.GetPageCount()-1, 1) self.GMChatPanel.chatwnd.SetDefaultFontAndSize(self.font, self.fontsize) - - @debugging + + def create_whisper_tab(self, playerid): "Add a new chatpanel directly connected to integer 'playerid' via whispering." private_tab = chat_panel(self, -1, WHISPER_TAB, playerid) @@ -414,8 +407,8 @@ self.AliasLib = component.get('alias') wx.CallAfter(self.AliasLib.RefreshAliases) return private_tab - - @debugging + + def create_group_tab(self, group_name): "Add a new chatpanel directly connected to integer 'playerid' via whispering." private_tab = chat_panel(self, -1, GROUP_TAB, group_name) @@ -426,8 +419,8 @@ self.AliasLib = component.get('alias') wx.CallAfter(self.AliasLib.RefreshAliases) return private_tab - - @debugging + + def create_null_tab(self, tab_name): "Add a new chatpanel directly connected to integer 'playerid' via whispering." private_tab = chat_panel(self, -1, NULL_TAB, tab_name) @@ -438,8 +431,8 @@ self.AliasLib = component.get('alias') wx.CallAfter(self.AliasLib.RefreshAliases) return private_tab - - @debugging + + def onCloseTab(self, evt): try: tabid = evt.GetSelection() except: tabid = self.GetSelection() @@ -467,17 +460,17 @@ if panel in self.whisper_tabs: self.whisper_tabs.remove(panel) elif panel in self.group_tabs: self.group_tabs.remove(panel) elif panel in self.null_tabs: self.null_tabs.remove(panel) - - @debugging + + def newMsg(self, tabid): if tabid != self.GetSelection(): self.SetPageImage(tabid, 0) - - @debugging + + def onPageChanging(self, event): """When private chattabs are selected, set the bitmap back to 'normal'.""" event.Skip() - - @debugging + + def onPageChanged(self, event): """When private chattabs are selected, set the bitmap back to 'normal'.""" selected_idx = event.GetSelection() @@ -485,7 +478,7 @@ page = self.GetPage(selected_idx) #wx.CallAfter(page.set_chat_text_focus, 0) event.Skip() - + """ This class defines and builds the Chat Frame for OpenRPG @@ -512,11 +505,11 @@ ParseNodes(self,s) get_sha_checksum(self) get_color(self) - + """ class chat_panel(wx.Panel): - + """ This is the initialization subroutine @@ -524,13 +517,13 @@ !parent : parent that defines the chatframe !id : !openrpg : - !sendtarget: who gets outbound messages: either 'all' or a playerid - """ - - @debugging + !sendtarget: who gets outbound messages: either 'all' or a playerid + """ + + def __init__(self, parent, id, tab_type, sendtarget): - wx.Panel.__init__(self, parent, id) - logger._set_log_to_console(False) + wx.Panel.__init__(self, parent, id) + logger._set_log_to_console(False) self.session = component.get('session') self.settings = component.get('settings') self.activeplugins = component.get('plugins') @@ -562,8 +555,8 @@ self.advancedFilter = False self.lastSend = 0 # this is used to help implement the player typing indicator self.lastPress = 0 # this is used to help implement the player typing indicator - self.Bind(wx.EVT_SIZE, self.OnSize) - self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnSize) #require to keep text at bottom of chat when text entry expands --SD + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(EVT_ETC_LAYOUT_NEEDED, self.OnSize) #require to keep text at bottom of chat when text entry expands --SD self.build_ctrls() StartupFont = self.settings.get_setting("defaultfont") StartupFontSize = self.settings.get_setting("defaultfontsize") @@ -572,11 +565,11 @@ except: pass self.font = self.chatwnd.GetFont().GetFaceName() self.fontsize = self.chatwnd.GetFont().GetPointSize() - self.scroll_down() - - @debugging + self.scroll_down() + + def set_default_font(self, fontname=None, fontsize=None): - """Set all chatpanels to new default fontname/fontsize. + """Set all chatpanels to new default fontname/fontsize. Returns current font settings in a (fontname, fontsize) tuple.""" if (fontname is not None): newfont = fontname else: newfont = self.font @@ -587,10 +580,10 @@ self.font = newfont self.fontsize = newfontsize return (self.font, self.fontsize) - - @debugging + + def build_menu(self): - top_frame = component.get('frame') + top_frame = component.get('frame') menu = wx.Menu() item = wx.MenuItem(menu, wx.ID_ANY, "&Background color", "Background color") top_frame.Bind(wx.EVT_MENU, self.OnMB_BackgroundColor, item) @@ -625,7 +618,7 @@ tabmenu = wx.Menu() toolmenu = wx.Menu() item = wx.MenuItem(wndmenu, wx.ID_ANY, "Show Images", "Show Images", wx.ITEM_CHECK) - top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowImages, item) + top_frame.Bind(wx.EVT_MENU, self.OnMB_ShowImages, item) wndmenu.AppendItem(item) if self.settings.get_setting("Show_Images_In_Chat") == '1': item.Check(True) @@ -676,58 +669,58 @@ toolmenu.AppendItem(item) if self.settings.get_setting("AliasTool_On") == '1': item.Check(True) settingmenu.AppendMenu(wx.ID_ANY, 'Chat Tool Bars', toolmenu) - menu.AppendMenu(wx.ID_ANY, 'Chat Settings', settingmenu) + menu.AppendMenu(wx.ID_ANY, 'Chat Settings', settingmenu) top_frame.mainmenu.Insert(2, menu, '&Chat') - ## Settings Menu Events - @debugging + ## Settings Menu Events + def OnMB_ShowImages(self, event): if event.IsChecked(): self.settings.set_setting("Show_Images_In_Chat", '1') else: self.settings.set_setting("Show_Images_In_Chat", '0') - - @debugging + + def OnMB_StripHTML(self, event): if event.IsChecked(): self.settings.set_setting("Sstriphtml", '1') else: self.settings.set_setting("striphtml", '0') - - @debugging + + def OnMB_ChatTimeIndex(self, event): if event.IsChecked(): self.settings.set_setting("Chat_Time_Indexing", '1') else: self.settings.set_setting("Chat_Time_Indexing", '0') - - @debugging + + def OnMB_ChatAutoComplete(self, event): if event.IsChecked(): self.settings.set_setting("SuppressChatAutoComplete", '0') else: self.settings.set_setting("SuppressChatAutoComplete", '1') - - @debugging + + def OnMB_ShowIDinChat(self, event): if event.IsChecked(): self.settings.set_setting("ShowIDInChat", '1') else: self.settings.set_setting("ShowIDInChat", '0') - - @debugging + + def OnMB_LogTimeIndex(self, event): if event.IsChecked(): self.settings.set_setting("TimeStampGameLog", '1') else: self.settings.set_setting("TimeStampGameLog", '0') - - @debugging + + def OnMB_TabbedWhispers(self, event): if event.IsChecked(): self.settings.set_setting("tabbedwhispers", '1') else: self.settings.set_setting("tabbedwhispers", '0') - - @debugging + + def OnMB_GMTab(self, event): if event.IsChecked(): self.settings.set_setting("GMWhisperTab", '1') self.parent.create_gm_tab() else: self.settings.set_setting("GMWhisperTab", '0') - - @debugging + + def OnMB_GroupWhisperTabs(self, event): if event.IsChecked(): self.settings.set_setting("GroupWhisperTab", '1') else: self.settings.set_setting("GroupWhisperTab", '0') - - @debugging + + def OnMB_DiceBar(self, event): act = '0' if event.IsChecked(): @@ -740,8 +733,8 @@ for panel in self.parent.whisper_tabs: panel.toggle_dice(act) for panel in self.parent.group_tabs: panel.toggle_dice(act) for panel in self.parent.null_tabs: panel.toggle_dice(act) - - @debugging + + def OnMB_FormatButtons(self, event): act = '0' if event.IsChecked(): @@ -755,8 +748,8 @@ for panel in self.parent.whisper_tabs: panel.toggle_formating(act) for panel in self.parent.group_tabs: panel.toggle_formating(act) for panel in self.parent.null_tabs: panel.toggle_formating(act) - - @debugging + + def OnMB_AliasTool(self, event): act = '0' if event.IsChecked(): @@ -769,8 +762,8 @@ for panel in self.parent.whisper_tabs: panel.toggle_alias(act) for panel in self.parent.group_tabs: panel.toggle_alias(act) for panel in self.parent.null_tabs:panel.toggle_alias(act) - - @debugging + + def OnMB_BackgroundColor(self, event): top_frame = component.get('frame') hexcolor = self.get_color() @@ -791,8 +784,8 @@ top_frame.players.SetForegroundColour('black') top_frame.players.Refresh() self.chatwnd.scroll_down() - - @debugging + + def OnMB_TextColor(self, event): top_frame = component.get('frame') hexcolor = self.get_color() @@ -813,8 +806,8 @@ top_frame.players.SetForegroundColour('black') top_frame.players.Refresh() self.chatwnd.scroll_down() - - @debugging + + def get_hot_keys(self): # dummy menus for hotkeys self.build_menu() @@ -822,8 +815,8 @@ entries.append((wx.ACCEL_CTRL, ord('H'), self.setChatFocusMenu.GetId())) #entries.append((wx.ACCEL_CTRL, wx.WXK_TAB, SWAP_TABS)) return entries - - @debugging + + def forward_tabs(self, evt): self.parent.AdvanceSelection() @@ -832,18 +825,18 @@ # This subroutine builds the controls for the chat frame # - # !self : instance of self - @debugging + # !self : instance of self + def build_ctrls(self): self.chatwnd = chat_html_window(self,-1) self.set_colors() wx.CallAfter(self.chatwnd.SetPage, self.chatwnd.Header()) if (self.sendtarget == "all"): - wx.CallAfter(self.Post, self.colorize(self.syscolor, + wx.CallAfter(self.Post, self.colorize(self.syscolor, "Welcome to OpenRPG version " + self.version + "... ")) #self.chat_cmds.on_help() - self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "", - style=wx.TE_PROCESS_ENTER |wx.TE_PROCESS_TAB|wx.TE_LINEWRAP, + self.chattxt = orpg.tools.predTextCtrl.predTextCtrl(self, -1, "", + style=wx.TE_PROCESS_ENTER |wx.TE_PROCESS_TAB|wx.TE_LINEWRAP, keyHook = self.myKeyHook, validator=None ) self.build_bar() self.basesizer = wx.BoxSizer(wx.VERTICAL) @@ -880,8 +873,8 @@ self.chattxt.Bind(wx.EVT_CHAR, self.chattxt.OnChar) self.chattxt.Bind(wx.EVT_TEXT_COPY, self.chatwnd.OnM_EditCopy) # def build_ctrls - end - - @debugging + + def build_bar(self): self.toolbar_sizer = wx.BoxSizer(wx.HORIZONTAL) self.scroll_lock = None @@ -892,52 +885,47 @@ self.build_dice() self.build_scroll() self.build_text() - self.toolbar_sizer.Add( self.textpop_lock, 0, wx.EXPAND ) - self.toolbar_sizer.Add(self.scroll_lock,0,wx.EXPAND) + self.toolbar_sizer.Add(self.textpop_lock, 0, wx.EXPAND) + self.toolbar_sizer.Add(self.scroll_lock, 0, wx.EXPAND) self.build_formating() self.build_colorbutton() - - @debugging + + def build_scroll(self): self.scroll_lock = wx.Button( self, wx.ID_ANY, "Scroll ON",size= wx.Size(80,25)) - - @debugging - def build_alias(self): + + + def build_alias(self): + self.aliasSizer = wx.BoxSizer(wx.HORIZONTAL) ## Future ## Add these to a sizer, then turn the toolbar_sizer into a grid so these can adjust to the frame. self.aliasList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultAliasName]) - self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif', + self.aliasButton = createMaskedButton( self, dir_struct["icon"] + 'player.gif', 'Refresh list of aliases from Game Tree', wx.ID_ANY, '#bdbdbd' ) self.aliasList.SetSelection(0) self.filterList = wx.Choice(self, wx.ID_ANY, size=(100, 25), choices=[self.defaultFilterName]) - self.filterButton = createMaskedButton( self, dir_struct["icon"] + 'add_filter.gif', + self.filterButton = createMaskedButton( self, dir_struct["icon"] + 'add_filter.gif', 'Refresh list of filters from Game Tree', wx.ID_ANY, '#bdbdbd' ) - self.filterList.SetSelection(0) - self.toolbar_sizer.Add( self.aliasButton, 0, wx.EXPAND ) - self.toolbar_sizer.Add( self.aliasList,0,wx.EXPAND) - self.toolbar_sizer.Add( self.filterButton, 0, wx.EXPAND ) - self.toolbar_sizer.Add( self.filterList,0,wx.EXPAND) + self.filterList.SetSelection(0) + + self.aliasSizer.Add( self.aliasButton, 0, wx.EXPAND ) + self.aliasSizer.Add( self.aliasList,0,wx.EXPAND) + self.aliasSizer.Add( self.filterButton, 0, wx.EXPAND ) + self.aliasSizer.Add( self.filterList,0,wx.EXPAND) + + self.toolbar_sizer.Add(self.aliasSizer, 0, wx.EXPAND) + if self.settings.get_setting('AliasTool_On') == '0': self.toggle_alias('0') else: self.toggle_alias('1') - - @debugging - def toggle_alias(self, act): - if act == '0': - self.toolbar_sizer.Show(self.aliasList, False) - self.toolbar_sizer.Show(self.filterList, False) - self.toolbar_sizer.Show(self.aliasButton, False) - self.toolbar_sizer.Show(self.filterButton, False) - self.toolbar_sizer.Layout() - else: - self.toolbar_sizer.Show(self.aliasList, True) - self.toolbar_sizer.Show(self.filterList, True) - self.toolbar_sizer.Show(self.aliasButton, True) - self.toolbar_sizer.Show(self.filterButton, True) - self.toolbar_sizer.Layout() - - @debugging + + + def toggle_alias(self, act): + if act == '0': self.toolbar_sizer.Show(self.aliasSizer, False) + else: self.toolbar_sizer.Show(self.aliasSizer, True) + self.toolbar_sizer.Layout() + def build_text(self): self.textpop_lock = createMaskedButton(self, dir_struct["icon"]+'note.gif', 'Open Text View Of Chat Session', wx.ID_ANY, '#bdbdbd') - - @debugging + + def build_dice(self): self.numDieText = wx.TextCtrl( self, wx.ID_ANY, "1", size= wx.Size(25, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() ) self.dieModText = wx.TextCtrl( self, wx.ID_ANY, "", size= wx.Size(50, 25), validator=orpg.tools.inputValidator.MathOnlyValidator() ) @@ -959,8 +947,8 @@ self.toolbar_sizer.Add( self.dieModText, 0, wx.ALIGN_CENTER, 5 ) if self.settings.get_setting('DiceButtons_On') == '0': self.toggle_dice('0') else: self.toggle_dice('1') - - @debugging + + def toggle_dice(self, act): if act == '0': self.toolbar_sizer.Show(self.numDieText, False) @@ -984,22 +972,22 @@ self.toolbar_sizer.Show(self.d100Button, True) self.toolbar_sizer.Show(self.dieModText, True) self.toolbar_sizer.Layout() - - @debugging + + def build_formating(self): - self.boldButton = createMaskedButton( self, dir_struct["icon"]+'bold.gif', + self.boldButton = createMaskedButton( self, dir_struct["icon"]+'bold.gif', 'Make the selected text Bold', wx.ID_ANY, '#bdbdbd') - self.italicButton = createMaskedButton( self, dir_struct["icon"]+'italic.gif', + self.italicButton = createMaskedButton( self, dir_struct["icon"]+'italic.gif', 'Italicize the selected text', wx.ID_ANY, '#bdbdbd' ) - self.underlineButton = createMaskedButton( self, dir_struct["icon"]+'underlined.gif', + self.underlineButton = createMaskedButton( self, dir_struct["icon"]+'underlined.gif', 'Underline the selected text', wx.ID_ANY, '#bdbdbd' ) self.toolbar_sizer.Add( self.boldButton, 0, wx.EXPAND ) self.toolbar_sizer.Add( self.italicButton, 0, wx.EXPAND ) self.toolbar_sizer.Add( self.underlineButton, 0, wx.EXPAND ) if self.settings.get_setting('FormattingButtons_On') == '0': self.toggle_formating('0') else: self.toggle_formating('1') - - @debugging + + def toggle_formating(self, act): if act == '0': self.toolbar_sizer.Show(self.boldButton, False) @@ -1012,21 +1000,21 @@ self.toolbar_sizer.Show(self.underlineButton, True) self.toolbar_sizer.Layout() - # Heroman - Ideally, we would use static labels... - @debugging + # Heroman - Ideally, we would use static labels... + def build_colorbutton(self): - self.color_button = createMaskedButton(self, dir_struct["icon"]+'textcolor.gif', - 'Text Color', wx.ID_ANY, '#bdbdbd', - wx.BITMAP_TYPE_GIF) + self.color_button = createMaskedButton(self, dir_struct["icon"]+'textcolor.gif', + 'Text Color', wx.ID_ANY, '#bdbdbd', + wx.BITMAP_TYPE_GIF) - self.saveButton = createMaskedButton(self, dir_struct["icon"]+'save.bmp', - 'Save the chatbuffer', wx.ID_ANY, - '#c0c0c0', wx.BITMAP_TYPE_BMP ) + self.saveButton = createMaskedButton(self, dir_struct["icon"]+'save.bmp', + 'Save the chatbuffer', wx.ID_ANY, + '#c0c0c0', wx.BITMAP_TYPE_BMP ) self.color_button.SetBackgroundColour(self.settings.get_setting('mytextcolor')) self.toolbar_sizer.Add(self.color_button, 0, wx.EXPAND) - self.toolbar_sizer.Add(self.saveButton, 0, wx.EXPAND) - - @debugging + self.toolbar_sizer.Add(self.saveButton, 0, wx.EXPAND) + + def OnMotion(self, evt): contain = self.chatwnd.GetInternalRepresentation() if contain: @@ -1051,8 +1039,8 @@ # # self: duh - # event: raw KeyEvent from OnChar() - @debugging + # event: raw KeyEvent from OnChar() + def myKeyHook(self, event): if self.session.get_status() == MPLAY_CONNECTED: # only do if we're connected thisPress = time.time() # thisPress is local temp variable @@ -1071,8 +1059,8 @@ # This subroutine gets called once a second by the typing Timer # It checks if we need to send a not_typing message # - # self: duh - @debugging + # self: duh + def typingTimerFunc(self, event): #following added by mDuo13 ##############refresh_counter()############## @@ -1093,9 +1081,9 @@ # This subroutine actually takes care of sending the messages for typing/not_typing events # # self: duh - # typing: boolean - - @debugging + # typing: boolean + + def sendTyping(self, typing): if typing: self.lastSend = time.time() # remember our send time for use in myKeyHook() @@ -1113,8 +1101,8 @@ # This subroutine sets the colors of the chat based on the settings in the # self instance. # - # !self : instance of self - @debugging + # !self : instance of self + def set_colors(self): # chat window backround color self.bgcolor = self.settings.get_setting('bgcolor') @@ -1135,20 +1123,20 @@ # This subroutine will insert text into the chat window # # !self : instance of self - # !txt : text to be inserted into the chat window - @debugging + # !txt : text to be inserted into the chat window + def set_chat_text(self, txt): self.chattxt.SetValue(txt) self.chattxt.SetFocus() self.chattxt.SetInsertionPointEnd() # def set_chat_text - end - - @debugging + + def get_chat_text(self): return self.chattxt.GetValue() - # This subroutine sets the focus to the chat window - @debugging + # This subroutine sets the focus to the chat window + def set_chat_text_focus(self, event): wx.CallAfter(self.chattxt.SetFocus) # def set_chat_text_focus - end @@ -1160,8 +1148,8 @@ # !event : # # Note: self.chattxt now handles it's own Key events. It does, however still - # call it's parent's (self) OnChar to handle "default" behavior. - @debugging + # call it's parent's (self) OnChar to handle "default" behavior. + def OnChar(self, event): s = self.chattxt.GetValue() #self.histlen = len(self.history) - 1 @@ -1174,18 +1162,18 @@ if self.session.get_status() == MPLAY_CONNECTED: # only do if we're connected self.sendTyping(0) # Send a "not_typing" event on enter key press macroText="" - recycle_bin = {wx.WXK_F1: 'event.GetKeyCode() == wx.WXK_F1', wx.WXK_F2: 'event.GetKeyCode() == wx.WXK_F2', - wx.WXK_F3: 'event.GetKeyCode() == wx.WXK_F3', wx.WXK_F4: 'event.GetKeyCode() == wx.WXK_F4', - wx.WXK_F5: 'event.GetKeyCode() == wx.WXK_F5', wx.WXK_F6: 'event.GetKeyCode() == wx.WXK_F6', - wx.WXK_F7: 'event.GetKeyCode() == wx.WXK_F7', wx.WXK_F8: 'event.GetKeyCode() == wx.WXK_F8', - wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10', - wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'} - - bin_event = event.GetKeyCode() - if recycle_bin.has_key(bin_event): - logger.debug(lambda bin_event: recycle_bin[bin_event]) - macroText = self.settings.get_setting(recycle_bin[bin_event][29:]) - recycle_bin = {}; del bin_event + recycle_bin = {wx.WXK_F1: 'event.GetKeyCode() == wx.WXK_F1', wx.WXK_F2: 'event.GetKeyCode() == wx.WXK_F2', + wx.WXK_F3: 'event.GetKeyCode() == wx.WXK_F3', wx.WXK_F4: 'event.GetKeyCode() == wx.WXK_F4', + wx.WXK_F5: 'event.GetKeyCode() == wx.WXK_F5', wx.WXK_F6: 'event.GetKeyCode() == wx.WXK_F6', + wx.WXK_F7: 'event.GetKeyCode() == wx.WXK_F7', wx.WXK_F8: 'event.GetKeyCode() == wx.WXK_F8', + wx.WXK_F9: 'event.GetKeyCode() == wx.WXK_F9', wx.WXK_F10: 'event.GetKeyCode() == wx.WXK_F10', + wx.WXK_F11: 'event.GetKeyCode() == wx.WXK_F11', wx.WXK_F12: 'event.GetKeyCode() == wx.WXK_F12'} + + bin_event = event.GetKeyCode() + if recycle_bin.has_key(bin_event): + logger.debug(lambda bin_event: recycle_bin[bin_event]) + macroText = self.settings.get_setting(recycle_bin[bin_event][29:]) + recycle_bin = {}; del bin_event # Append to the existing typed text as needed and make sure the status doesn't change back. if len(macroText): @@ -1301,8 +1289,8 @@ else: event.Skip() logger.debug("Exit chat_panel->OnChar(self, event)") # def OnChar - end - - @debugging + + def onDieRoll(self, evt): """Roll the dice based on the button pressed and the die modifiers entered, if any.""" # Get any die modifiers if they have been entered @@ -1322,8 +1310,8 @@ # FileDialog. # # !self : instance of self - # !evt : - @debugging + # !evt : + def on_chat_save(self, evt): f = wx.FileDialog(self,"Save Chat Buffer",".","","HTM* (*.htm*)|*.htm*|HTML (*.html)|*.html|HTM (*.htm)|*.htm",wx.SAVE) if f.ShowModal() == wx.ID_OK: @@ -1333,20 +1321,20 @@ f.Destroy() os.chdir(dir_struct["home"]) # def on_chat_save - end - - @debugging + + def ResetPage(self): self.set_colors() buffertext = self.chatwnd.Header() + "\n" - buffertext += chat_util.strip_body_tags(self.chatwnd.StripHeader()).replace("
", - "
").replace('', - '').replace("
", + buffertext += chat_util.strip_body_tags(self.chatwnd.StripHeader()).replace("
", + "
").replace('', + '').replace("
", "
\n").replace("\n\n", '') return buffertext # This subroutine sets the color of selected text, or base text color if - # nothing is selected - @debugging + # nothing is selected + def on_text_color(self, event): hexcolor = self.r_h.do_hex_color_dlg(self) if hexcolor != None: @@ -1369,8 +1357,8 @@ # # !self : instance of self # !color : color for the text to be set - # !text : text string to be included in the html. - @debugging + # !text : text string to be included in the html. + def colorize(self, color, text): """Puts font tags of 'color' around 'text' value, and returns the string""" return "" + text + "" @@ -1380,8 +1368,8 @@ # tags included. # # !self : instance of self - # !event : - @debugging + # !event : + def on_text_format(self, event): id = event.GetId() txt = self.chattxt.GetValue() @@ -1397,8 +1385,8 @@ self.chattxt.SetInsertionPointEnd() self.chattxt.SetFocus() # def on_text_format - end - - @debugging + + def lock_scroll(self, event): if self.lockscroll: self.lockscroll = False @@ -1414,8 +1402,8 @@ # This subroutine will popup a text window with the chatbuffer contents # # !self : instance of self - # !event : - @debugging + # !event : + def pop_textpop(self, event): """searchable popup text view of chatbuffer""" h_buffertext = self.ResetPage() @@ -1425,44 +1413,44 @@ # This subroutine will change the dimension of the window # # !self : instance of self - # !event : - @debugging + # !event : + def OnSize(self, event=None): event.Skip() wx.CallAfter(self.scroll_down) # def OnSize - end - - @debugging + + def scroll_down(self): self.Freeze() self.chatwnd.scroll_down() self.Thaw() - ###### message helpers ###### - @debugging + ###### message helpers ###### + def PurgeChat(self): self.set_colors() self.chatwnd.SetPage(self.chatwnd.Header()) - - @debugging + + def system_message(self, text): self.send_chat_message(text,chat_msg.SYSTEM_MESSAGE) self.SystemPost(text) - - @debugging + + def info_message(self, text): self.send_chat_message(text,chat_msg.INFO_MESSAGE) self.InfoPost(text) - - @debugging + + def get_gms(self): the_gms = [] for playerid in self.session.players: if len(self.session.players[playerid])>7: if self.session.players[playerid][7]=="GM" and self.session.group_id != '0': the_gms += [playerid] return the_gms - - @debugging + + def GetName(self): self.AliasLib = component.get('alias') player = self.session.get_my_info() @@ -1472,8 +1460,8 @@ logger.debug("Exit chat_panel->GetName(self)") return [self.chat_display_name([self.AliasLib.alias[0], player[1], player[2]]), self.AliasLib.alias[1]] return [self.chat_display_name(player), "Default"] - - @debugging + + def GetFilteredText(self, text): advregex = re.compile('\"(.*?)\"', re.I) self.AliasLib = component.get('alias') @@ -1487,12 +1475,12 @@ newmatch = re.sub(rule[0], rule[1], match) text = text.replace(match, newmatch) return text - - @debugging + + def emote_message(self, text): text = self.NormalizeParse(text) text = self.colorize(self.emotecolor, text) - + if self.type == MAIN_TAB and self.sendtarget == 'all': self.send_chat_message(text,chat_msg.EMOTE_MESSAGE) elif self.type == MAIN_TAB and self.sendtarget == "gm": msg_type = chat_msg.WHISPER_EMOTE_MESSAGE @@ -1506,8 +1494,8 @@ name = self.GetName()[0] text = "** " + name + " " + text + " **" self.EmotePost(text) - - @debugging + + def whisper_to_players(self, text, player_ids): tabbed_whispers_p = self.settings.get_setting("tabbedwhispers") # Heroman - apply any filtering selected @@ -1533,8 +1521,8 @@ id = id.strip() if self.session.is_valid_id(id): self.send_chat_message(text,chat_msg.WHISPER_MESSAGE,id) else: self.InfoPost(id + " Unknown!") - - @debugging + + def send_chat_message(self, text, type=chat_msg.CHAT_MESSAGE, player_id="all"): #########send_msg()############# send = 1 @@ -1558,9 +1546,9 @@ msg.set_alias(playername) if send: self.session.send(msg.toxml(),player_id) del msg - - #### incoming chat message handler ##### - @debugging + + #### incoming chat message handler ##### + def post_incoming_msg(self, msg, player): # pull data @@ -1683,21 +1671,21 @@ if sound_file != '': component.get('sound').play(sound_file) #### Posting helpers ##### - - @debugging + + def InfoPost(self, s): self.Post(self.colorize(self.infocolor, s), c='info') - - @debugging + + def SystemPost(self, s): self.Post(self.colorize(self.syscolor, s), c='system') - - @debugging + + def EmotePost(self, s): self.Post(self.colorize(self.emotecolor, s), c='emote') - #### Standard Post method ##### - @debugging + #### Standard Post method ##### + def Post(self, s="", send=False, myself=False, c='post'): strip_p = self.settings.get_setting("striphtml") strip_img = self.settings.get_setting("Show_Images_In_Chat")#moved back 7-11-05. --mDuo13 @@ -1707,7 +1695,7 @@ s = chat_util.strip_script_tags(s) s = chat_util.strip_li_tags(s) s = chat_util.strip_body_tags(s) #7-27-05 mDuo13 - s = chat_util.strip_misalignment_tags(s) #7-27-05 mDuo13 + s = chat_util.strip_misalignment_tags(s) #7-27-05 mDuo13 aliasInfo = self.GetName() display_name = aliasInfo[0] if aliasInfo[1] != 'Default': @@ -1767,7 +1755,7 @@ newline = "
" + self.TimeIndexString() + name + s2 + "
" log( self.settings, c, name+s2 ) else: - newline = "
" + self.TimeIndexString() + name + s + "
" + newline = "
" + self.TimeIndexString() + name + s + "
" log( self.settings, c, name+s ) else: send = False newline = component.get('xml').strip_unicode(newline) @@ -1793,8 +1781,8 @@ # TimeIndexString() # # time indexing for chat display only (don't log time indexing) - # added by Snowdog 4/04 - @debugging + # added by Snowdog 4/04 + def TimeIndexString(self): try: mtime = "" @@ -1807,15 +1795,15 @@ logger.general("EXCEPTION: " + str(e)) return "[ERROR]" - #### Post with parsing dice #### - @debugging + #### Post with parsing dice #### + def ParsePost(self, s, send=False, myself=False): s = self.NormalizeParse(s) self.set_colors() self.Post(s,send,myself) - - @debugging - def NormalizeParse(self, s): + + + def NormalizeParse(self, s): for plugin_fname in self.activeplugins.keys(): plugin = self.activeplugins[plugin_fname] try: s = plugin.pre_parse(s) @@ -1829,31 +1817,30 @@ s = self.ParseFilter(s) self.parsed = 1 return s - - @debugging - def ParseFilter(self, s): + + + def ParseFilter(self, s): s = self.GetFilteredText(s) return s - - @debugging - def ParseNode(self, s): + + def ParseNode(self, s): """Parses player input for embedded nodes rolls""" cur_loc = 0 #[a-zA-Z0-9 _\-\.] - reg = re.compile("(!@([a-zA-Z0-9 _\-\./]+(::[a-zA-Z0-9 _\-\./]+)*)@!)") + reg = re.compile("(!@(.*?)@!)") matches = reg.findall(s) for i in xrange(0,len(matches)): newstr = self.ParseNode(self.resolve_nodes(matches[i][1])) s = s.replace(matches[i][0], newstr, 1) return s - - @debugging - def ParseDice(self, s): + + + def ParseDice(self, s): """Parses player input for embedded dice rolls""" reg = re.compile("\[([^]]*?)\]") matches = reg.findall(s) for i in xrange(0,len(matches)): - newstr = self.PraseUnknowns(matches[i]) + newstr = self.PraseUnknowns(matches[i]) qmode = 0 newstr1 = newstr if newstr[0].lower() == 'q': @@ -1865,9 +1852,9 @@ s = s.replace("[" + matches[i] + "]", "" + newstr, 1) else: s = s.replace("[" + matches[i] + "]", "[" + newstr1 + "] => " + newstr, 1) return s - - @debugging - def PraseUnknowns(self, s): + + + def PraseUnknowns(self, s): # Uses a tuple. Usage: ?Label}dY. If no Label is assigned then use ?}DY newstr = "0" reg = re.compile("(\?\{*)([a-zA-Z ]*)(\}*)") @@ -1884,22 +1871,22 @@ if newstr == '': newstr = '0' s = s.replace(matches[i][0], newstr, 1).replace(matches[i][1], '', 1).replace(matches[i][2], '', 1) dlg.Destroy() - return s + return s # This subroutine builds a chat display name. - # - @debugging - def chat_display_name(self, player): + # + + def chat_display_name(self, player): if self.settings.get_setting("ShowIDInChat") == "0": display_name = player[0] else: display_name = "("+player[2]+") " + player[0] return display_name - + # This subroutine will get a hex color and return it, or return nothing - # - @debugging - def get_color(self): + # + + def get_color(self): data = wx.ColourData() data.SetChooseFull(True) dlg = wx.ColourDialog(self, data) @@ -1913,8 +1900,8 @@ dlg.Destroy() return None # def get_color - end - - @debugging + + def replace_quotes(self, s): in_tag = 0 i = 0 @@ -1929,49 +1916,51 @@ if in_tag: rs = rs[:i] + "'" + rs[i+1:] i += 1 - return rs + return rs + + def resolve_loop(self, node, path, step, depth): + if step == depth: + self.resolution(node) + else: + child_list = node.findall('nodehandler') + for child in child_list: + if step == depth: break + if child.get('name') == path[step]: + node = child + step += 1 + self.resolve_loop(node, path, step, depth) - @debugging - def resolve_loop(self, dom, nodeName, doLoop = False): - for node in dom: - if node._get_tagName() != 'nodehandler': - continue - if doLoop and node.getAttribute('class') != 'textctrl_handler' and node.hasChildNodes(): - (found, node) = self.resolve_loop(node.getChildren(), nodeName, doLoop) - if not found: - continue - if node.getAttribute('name') != nodeName: - continue - foundNode = node - return (True, foundNode) - return (False, '') + def resolution(self, node): + if self.passed == False: + self.passed = True + if node.get('class') == 'textctrl_handler': self.data = str(node.find('text').text) + else: self.data = 'Nodehandler for '+ node.get('class') + ' not done!' or 'No Data!' + else: + self.data = '' + pass + self.data = self.ParseMap(self.data, node) + + def ParseMap(self, s, node): + """Parses player input for embedded nodes rolls""" + cur_loc = 0 + reg = re.compile("(!!(.*?)!!)") + matches = reg.findall(s) + print matches + for i in xrange(0,len(matches)): + newstr = txt = '!@' + node.get('map') + '::' + matches[i][1] + '@!' + s = s.replace(matches[i][0], newstr, 1) + s = self.ParseNode(s) + return s - @debugging - def resolve_nodes(self, s): + def resolve_nodes(self, s): + self.passed = False + self.data = 'No Data!' value = "" - node_path_list = s.split("::") - gametree = component.get('tree') - dom = gametree.master_dom.getChildren() - for nodeName in node_path_list: - (found, node) = self.resolve_loop(dom, nodeName) - if not found: - break - dom = node.getChildren() - if not found: - dom = gametree.master_dom.getChildren() - loop = False - if len(node_path_list) == 1: - loop = True - for nodeName in node_path_list: - (found, node) = self.resolve_loop(dom, nodeName, loop) - if not found: - break - dom = node.getChildren() - loop = True - if found: - text = node.getElementsByTagName('text') - node = text[0]._get_firstChild() - value = node._get_nodeValue() - else: - value = s - return value + path = s.split('::') + depth = len(path) + self.gametree = component.get('tree') + dom = self.gametree.xml_root.getchildren() + for node in dom: + if node.get('name') == path[0]: + self.resolve_loop(node, path, 1, len(path)) + return self.data diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/chat/commands.py --- a/orpg/chat/commands.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/chat/commands.py Tue Nov 10 14:11:28 2009 -0600 @@ -8,15 +8,13 @@ # -import string -import time +import string, time import orpg.orpg_version import orpg.orpg_windows -import traceback - -from orpg.orpgCore import component -from orpg.tools.orpg_log import logger -from orpg.tools.decorators import debugging +import traceback + +from orpg.orpgCore import component +from orpg.tools.orpg_log import logger ##-------------------------------------------------------------- ## dynamically loading module for extended developer commands @@ -39,8 +37,8 @@ # Initialization subroutine. # # !self : instance of self - # !chat : instance of the chat window to write to - @debugging + # !chat : instance of the chat window to write to + def __init__(self,chat): self.post = chat.Post self.colorize = chat.colorize @@ -60,21 +58,21 @@ # of implemented emotions. # # !self : instance of self - # !text : string of text matching an implemented emotion - @debugging + # !text : string of text matching an implemented emotion + def addcommand(self, cmd, function, helpmsg): if not self.cmdlist.has_key(cmd) and not self.shortcmdlist.has_key(cmd): self.cmdlist[cmd] = {} self.cmdlist[cmd]['function'] = function self.cmdlist[cmd]['help'] = helpmsg #print 'Command Added: ' + cmd - - @debugging + + def addshortcmd(self, shortcmd, longcmd): if not self.shortcmdlist.has_key(shortcmd) and not self.cmdlist.has_key(shortcmd): self.shortcmdlist[shortcmd] = longcmd - - @debugging + + def removecmd(self, cmd): if self.cmdlist.has_key(cmd): del self.cmdlist[cmd] @@ -83,8 +81,8 @@ #print 'Command Removed: ' + cmd - - @debugging + + def defaultcmds(self): self.addcommand('/help', self.on_help, '- Displays this help message') self.addcommand('/version', self.on_version, ' - Displays current version of OpenRPG.') @@ -113,8 +111,8 @@ self.addcommand('/sound', self.on_sound, 'Sound_URL - Plays a sound for all clients in the room.') self.addcommand('/purge', self.on_purge, 'This will clear the entire chat window') self.addcommand('/advfilter', self.on_filter, 'This will toggle the Advanced Filter') - - @debugging + + def defaultcmdalias(self): self.addshortcmd('/?', '/help') self.addshortcmd('/he', '/me') @@ -128,8 +126,8 @@ #This is just an example or a differant way the shorcmd can be used self.addshortcmd('/sleep', '/me falls asleep') - - @debugging + + def docmd(self,text): cmdsearch = string.split(text,None,1) cmd = string.lower(cmdsearch[0]) @@ -144,8 +142,8 @@ else: msg = "Sorry I don't know what %s is!" % (cmd) self.chat.InfoPost(msg) - - @debugging + + def on_filter(self, cmdargs): #print self.chat.advancedFilter test = not self.chat.advancedFilter @@ -169,13 +167,13 @@ self.chat.InfoPost("Advanced Filtering has been turned On") else: self.chat.InfoPost("Advanced Filtering has been turned Off") - - @debugging + + def on_purge(self, cmdargs): self.chat.PurgeChat() self.chat.InfoPost('Chat Buffer has been Purged!') - - @debugging + + def on_sound(self, cmdargs): if len(cmdargs) < 8: self.chat.InfoPost("You must provide a URL for the file name, it does not work for just local sound files") @@ -208,12 +206,12 @@ self.chat.InfoPost("You cannot send sound files to the lobby!") else: self.chat.InfoPost("Something dun fuckered up Frank!") - - @debugging + + def on_version(self, cmdargs=""): self.chat.InfoPost("Version is OpenRPG " + self.chat.version) - - @debugging + + def on_load(self, cmdargs): args = string.split(cmdargs,None,-1) try: @@ -223,15 +221,15 @@ except Exception,e: print e self.chat.InfoPost("ERROR Loading settings") - - @debugging + + def on_font(self, cmdargs): try: fontsettings = self.chat.set_default_font(fontname=cmdargs, fontsize=None) except: self.chat.InfoPost("ERROR setting default font") - - @debugging + + def on_fontsize(self, cmdargs): args = string.split(cmdargs,None,-1) try: @@ -239,8 +237,8 @@ except Exception, e: print e self.chat.InfoPost("ERROR setting default font size") - - @debugging + + def on_close(self, cmdargs): try: chatpanel = self.chat @@ -251,16 +249,16 @@ chatpanel.parent.onCloseTab(0) except: self.chat.InfoPost("Error: cannot close private chat tab.") - - @debugging + + def on_time(self, cmdargs): local_time = time.localtime() gmt_time = time.gmtime() format_string = "%A %b %d, %Y %I:%M:%S%p" self.chat.InfoPost("
Local: " + time.strftime(format_string)+\ "
GMT: "+time.strftime(format_string,gmt_time)) - - @debugging + + def on_dieroller(self, cmdargs): args = string.split(cmdargs,None,-1) rm = component.get('DiceManager') @@ -272,14 +270,14 @@ print e self.chat.InfoPost("Available die rollers: " + str(rm.listRollers())) self.chat.InfoPost("You are using the \"" + rm.getRoller() + "\" die roller.") - - @debugging + + def on_ping(self, cmdargs): ct = time.clock() msg = "" self.session.outbox.put(msg) - - @debugging + + def on_log(self,cmdargs): args = string.split(cmdargs,None,-1) logfile = self.settings.get_setting( 'GameLogPrefix' ) @@ -310,25 +308,22 @@ self.postLoggingState() else: self.chat.InfoPost("Unknown logging command, use 'on' or 'off'" ) - - @debugging + + def postLoggingState( self ): logfile = self.settings.get_setting( 'GameLogPrefix' ) try: - if logfile[0] != ANTI_LOG_CHAR: - comment = 'is' - else: - comment = 'is not' - except: - comment = 'is not' + if logfile[0] != ANTI_LOG_CHAR: comment = 'is' + else: comment = 'is not' + except: comment = 'is not' suffix = time.strftime( '-%d-%m-%y.html', time.localtime( time.time() ) ) self.chat.InfoPost('Log filename is "%s%s", system is %s logging.' % (logfile, suffix, comment) ) # This subroutine will set the players netork status. # #!self : instance of self - - @debugging + + def on_name(self, cmdargs): #only 20 chars no more! :) if cmdargs == "": @@ -339,21 +334,20 @@ self.session.set_name(str(cmdargs)) # def on_status - end - # This subroutine will set the players netork status. # - # !self : instance of self - @debugging + # !self : instance of self + def on_status(self, cmdargs): if cmdargs == "": self.chat.InfoPost("Incorrect synatx for status.") - else: + else: #only 20 chars no more! :) - txt = cmdargs[:20] - self.session.set_text_status(str(txt)) + txt = cmdargs[:20] + self.session.set_text_status(str(txt)) # def on_status - end - - @debugging + + def on_set(self, cmdargs): args = string.split(cmdargs,None,-1) keys = self.settings.get_setting_keys() @@ -387,8 +381,8 @@ # This subroutine will display the correct usage of the different emotions. # #!self : instance of self - - @debugging + + def on_help(self, cmdargs=""): cmds = self.cmdlist.keys() cmds.sort() @@ -404,14 +398,13 @@ if self.shortcmdlist[shortcmd] == cmd: msg += ', %s' % (shortcmd) msg += ' %s' % (self.cmdlist[cmd]['help']) - self.chat.InfoPost(msg) # This subroutine will either show the list of currently ignored users # !self : instance of self # !text : string that is comprised of a list of users to toggle the ignore flag - - @debugging + + def on_ignore(self, cmdargs): args = string.split(cmdargs,None,-1) (ignore_list, ignore_name) = self.session.get_ignore_list() @@ -437,8 +430,8 @@ except: self.chat.InfoPost(m + " was ignored because it is an invalid player ID") traceback.print_exc() - - @debugging + + def on_role(self, cmdargs): if cmdargs == "": self.session.display_roles() @@ -459,8 +452,7 @@ if role_pwd != None: for m in player_ids: self.session.set_role(m.strip(),role,role_pwd) - except: - traceback.print_exc() + except: traceback.print_exc() # return # This subroutine implements the whisper functionality that enables a user @@ -469,33 +461,30 @@ # !self : instance of self # !text : string that is comprised of a list of users and the message to #whisper. - - @debugging + + def on_whisper(self, cmdargs): delim = cmdargs.find("=") if delim < 0: - if self.previous_whisper: - player_ids = self.previous_whisper + if self.previous_whisper: player_ids = self.previous_whisper else: self.chat.InfoPost("**Incorrect syntax for whisper." + str(delim)) return - else: - player_ids = string.split(cmdargs[:delim], ",") + else: player_ids = string.split(cmdargs[:delim], ",") self.previous_whisper = player_ids mesg = string.strip(cmdargs[delim+1:]) self.chat.whisper_to_players(mesg,player_ids) #--------------------------------------------------------- # [START] Digitalxero Multi Whisper Group 1/1/05 -#--------------------------------------------------------- - @debugging +#--------------------------------------------------------- + def on_groupwhisper(self, cmdargs): args = string.split(cmdargs,None,-1) delim = cmdargs.find("=") - if delim > 0: - group_ids = string.split(cmdargs[:delim], ",") + if delim > 0: group_ids = string.split(cmdargs[:delim], ",") elif args[0] == "add": if not orpg.player_list.WG_LIST.has_key(args[2]): orpg.player_list.WG_LIST[args[2]] = {} @@ -540,17 +529,15 @@ for gid in group_ids: idList = "" for n in orpg.player_list.WG_LIST[gid]: - if idList == "": - idList = str(n) - else: - idList = str(n) + ", " + idList + if idList == "": idList = str(n) + else: idList = str(n) + ", " + idList self.on_whisper(idList + "=" + self.settings.get_setting("gwtext") + msg) #--------------------------------------------------------- # [END] Digitalxero Multi Whisper Group 1/1/05 #--------------------------------------------------------- - - @debugging + + def on_gmwhisper(self, cmdargs): if cmdargs == "": self.chat.InfoPost("**Incorrect syntax for GM Whisper.") @@ -559,33 +546,27 @@ if len(the_gms): gmstring = "" for each_gm in the_gms: - if gmstring != "": - gmstring += "," + if gmstring != "": gmstring += "," gmstring += each_gm self.on_whisper(gmstring + "=" + cmdargs) - else: - self.chat.InfoPost("**No GMs to Whisper to.") - - @debugging + else: self.chat.InfoPost("**No GMs to Whisper to.") + + def on_moderate(self, cmdargs): if cmdargs != "": pos = cmdargs.find("=") if (pos < 0): plist = "" - if cmdargs.lower() == "on": - action = "enable" - elif cmdargs.lower() == "off": - action="disable" + if cmdargs.lower() == "on": action = "enable" + elif cmdargs.lower() == "off": action="disable" else: self.chat.InfoPost("Wrong syntax for moderate command!") return else: plist = string.strip(cmdargs[:pos]) tag = string.strip(cmdargs[pos+1:]) - if tag.lower() == "on": - action = "addvoice" - elif tag.lower() == "off": - action = "delvoice" + if tag.lower() == "on": action = "addvoice" + elif tag.lower() == "off": action = "delvoice" else: self.chat.InfoPost("Wrong syntax for moderate command!") return @@ -594,8 +575,7 @@ msg = "" self.session.outbox.put(msg) self.session.update() - - @debugging + + def on_update(self, cmdargs): self.chat.InfoPost("This command is no longer valid") - - @debugging + + def on_description(self, cmdargs): if len(cmdargs) <= 0: self.chat.InfoPost("**No description text to display." + str(delim)) @@ -618,8 +598,8 @@ mesg += "" self.chat.Post(mesg) self.chat.send_chat_message(mesg) - - @debugging + + def invoke_tab(self, cmdargs): ######START mDuo13's Tab Initiator######## try: @@ -630,8 +610,7 @@ if (panel.sendtarget == playerid): self.chat.Post("Cannot invoke tab: Tab already exists.") return - try: - displaypanel = self.chat.parent.create_whisper_tab(playerid) + try: displaypanel = self.chat.parent.create_whisper_tab(playerid) except: self.chat.Post("That ID# is not valid.") return @@ -644,8 +623,9 @@ self.chat.parent.newMsg(nidx) return #######END mDuo13's Tab Initiator######### - - @debugging + + + def on_remote_admin(self, cmdargs): args = string.split(cmdargs,None,-1) #handles remote administration commands @@ -660,8 +640,7 @@ #raw command return state info msg = "
Remote Administrator Config:" if pass_state != 1 : msg += " Password not set. Remote admin functions disabled
" - else: - msg += " Enabled. Using password \""+pwd+"\"
" + else: msg += " Enabled. Using password '"+pwd+"'
" self.chat.SystemPost(msg) return @@ -669,75 +648,54 @@ #no commands under this point will execute unless an admin password has been previously set self.chat.SystemPost("Command ignored. No remote administrator password set!!") return - msgbase = " 1: self.session.orpgFrame_callback.password_manager.server = str( args[1] ) - self.chat.SystemPost( "Remote administration commands using password: \""+str(self.session.orpgFrame_callback.password_manager.GetSilentPassword("server"))+"\"" ) + self.chat.SystemPost( "Remote administration commands using password: "+str(self.session.orpgFrame_callback.password_manager.GetSilentPassword("server"))+"" ) else: pwd = self.session.orpgFrame_callback.password_manager.GetPassword("server") if pwd != None: - self.chat.SystemPost( "Remote administration commands using password: \""+pwd+"\"" ) + self.chat.SystemPost( "Remote administration commands using password: "+pwd+"" ) + elif len(args) == 1: + admin_command = {'banlist': ' cmd="banlist" />', + 'help': " cmd='help' />", + 'roompasswords': " cmd='roompasswords' />", + 'uptime': " cmd='uptime' />", + 'list': " cmd='list' />", + 'killserver': " cmd='killserver' />", + 'savemaps': ' cmd="savemaps" />' + } + if admin_command.has_key(args[0]): + msg = msgbase + admin_command[args[0]] + self.session.outbox.put(msg) - elif args[0] == "ban": - #Ban a player from the server - msg = msgbase + ' cmd="ban" bid="' + str(args[1]) + '" />' - self.session.outbox.put(msg) + elif len(args) == 2: + admin_command = {'ban': ' cmd="ban" bid="' + str(args[1]) + '" />', + 'unban': ' cmd="unban" ip="' + str(args[1]) + '" />', + 'broadcast': " cmd='broadcast' msg='"+ string.join(args[1:])+"' />", + 'killgroup': " cmd='killgroup' gid='"+ str(args[1])+"' />" + } + if admin_command.has_key(args[0]): + msg = msgbase + admin_command[args[0]] + self.session.outbox.put(msg) + + elif len(args) == 3: + admin_command = {'message':" cmd='message' to_id='"+ str(args[1])+"' msg='"+ string.join(args[2:])+"' />", + 'nameroom': " cmd='nameroom' rmid='"+ str(args[1])+"' name='"+ string.join(args[2:])+"' />", + 'passwd': " cmd='passwd' gid='"+str(args[1])+"' pass='"+ str(args[2])+"' />" + } + if admin_command.has_key(args[0]): + msg = msgbase + admin_command[args[0]] + self.session.outbox.put(msg) elif args[0] == "banip": #Ban a player from the server - try: - bname = str(args[2]) - except: - bname = 'Unknown' + try: bname = str(args[2]) + except: bname = 'Unknown' msg = msgbase + ' cmd="banip" bip="' + str(args[1]) + '" bname="' + bname + '"/>' self.session.outbox.put(msg) - elif args[0] == "unban": - #remove a group from the server and drop all players within the group - msg = msgbase + ' cmd="unban" ip="' + str(args[1]) + '" />' - self.session.outbox.put(msg) - - elif args[0] == "banlist": - #remove a group from the server and drop all players within the group - msg = msgbase + ' cmd="banlist" />' - self.session.outbox.put(msg) - - elif args[0] == "help": - #request help from server - msg = msgbase + " cmd=\"help\" />" - self.session.outbox.put(msg) - - elif args[0] == "nameroom": - #reqest room renaming on server - msg = msgbase + " cmd=\"nameroom\" rmid=\""+ str(args[1])+"\" name=\""+ string.join(args[2:])+"\" />" - self.session.outbox.put(msg) - - elif args[0] == "roompasswords": - #reqest room renaming on server - msg = msgbase + " cmd=\"roompasswords\"/>" - self.session.outbox.put(msg) - - elif args[0] == "message": - #send message to a specific player on the server via the system administrator - msg = msgbase + " cmd=\"message\" to_id=\""+ str(args[1])+"\" msg=\""+ string.join(args[2:])+"\" />" - self.session.outbox.put(msg) - - elif args[0] == "broadcast": - #send a message to all players on server from the system administrator - msg = msgbase + " cmd=\"broadcast\" msg=\""+ string.join(args[1:])+"\" />" - self.session.outbox.put(msg) - - elif args[0] == "killgroup": - #remove a group from the server and drop all players within the group - msg = msgbase + " cmd=\"killgroup\" gid=\""+ str(args[1])+"\" />" - self.session.outbox.put(msg) - - elif args[0] == "uptime": - #request uptime report from server - msg = msgbase + " cmd=\"uptime\" />" - self.session.outbox.put(msg) - elif args[0] == "createroom": #request creation of a (temporary) persistant room if len(args) < 2: @@ -747,30 +705,11 @@ self.chat.SystemPost( "You must supply a boot password also.
/admin createroom <name> <boot password> [password]" ) return if len(args) < 4: args.append("") - msg = msgbase + " cmd=\"createroom\" name=\""+str(args[1])+"\" boot=\""+ str(args[2])+"\" pass=\""+ str(args[3])+"\" />" - self.session.outbox.put(msg) - - elif args[0] == "passwd": - #request boot password change on a room - msg = msgbase + " cmd=\"passwd\" gid=\""+str(args[1])+"\" pass=\""+ str(args[2])+"\" />" + msg = msgbase + " cmd='createroom' name='"+str(args[1])+"' boot='"+ str(args[2])+"' pass='"+ str(args[3])+"' />" self.session.outbox.put(msg) - elif args[0] == "list": - #request a list of rooms and players from server - msg = msgbase + " cmd=\"list\" />" - self.session.outbox.put(msg) - - elif args[0] == "killserver": - #remotely kill the server - msg = msgbase + " cmd=\"killserver\" />" - self.session.outbox.put(msg) - - elif args[0] == "savemaps": - msg = msgbase + ' cmd="savemaps" />' - self.session.outbox.put(msg) - - else: - self.chat.InfoPost("Unknown administrator command" ) + else: self.chat.InfoPost("Unknown administrator command" ) + #command_function = {'banip': self.admin.r_admin_banip, 'createroom': self.r_admin_createroom,} except: self.chat.InfoPost("An error has occured while processing a Remote Administrator command!") traceback.print_exc() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/gametree.py --- a/orpg/gametree/gametree.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/gametree.py Tue Nov 10 14:11:28 2009 -0600 @@ -26,28 +26,34 @@ # Description: The file contains code fore the game tree shell # +from __future__ import with_statement + __version__ = "$Id: gametree.py,v 1.68 2007/12/07 20:39:48 digitalxero Exp $" from orpg.orpg_wx import * from orpg.orpg_windows import * from orpg.orpgCore import component from orpg.dirpath import dir_struct -from nodehandlers import core +from nodehandlers import core import string import urllib import time -import os - -from orpg.orpg_xml import xml -from orpg.tools.validate import validate -from orpg.tools.orpg_log import logger -from orpg.tools.decorators import debugging +import os + +from orpg.orpg_xml import xml +from orpg.tools.validate import validate +from orpg.tools.orpg_log import logger, debug +from orpg.tools.orpg_settings import settings from orpg.gametree.nodehandlers import containers, forms, dnd3e, dnd35, chatmacro from orpg.gametree.nodehandlers import map_miniature_nodehandler -from orpg.gametree.nodehandlers import minilib, rpg_grid, d20, StarWarsd20, voxchat - +from orpg.gametree.nodehandlers import minilib, rpg_grid, d20, StarWarsd20, voxchat + from gametree_version import GAMETREE_VERSION +from xml.etree.ElementTree import ElementTree, Element, parse +from xml.etree.ElementTree import fromstring, tostring, XML, iselement +from xml.parsers.expat import ExpatError + STD_MENU_DELETE = wx.NewId() STD_MENU_DESIGN = wx.NewId() STD_MENU_USE = wx.NewId() @@ -76,10 +82,10 @@ TOP_TREE_PROP = wx.NewId() TOP_FEATURES = wx.NewId() -class game_tree(wx.TreeCtrl): - @debugging +class game_tree(wx.TreeCtrl): + def __init__(self, parent, id): - wx.TreeCtrl.__init__(self,parent,id, wx.DefaultPosition, + wx.TreeCtrl.__init__(self,parent,id, wx.DefaultPosition, wx.DefaultSize,style=wx.TR_EDIT_LABELS | wx.TR_HAS_BUTTONS) #self.xml = component.get('xml') # self.settings = component.get('settings') @@ -103,29 +109,28 @@ self.id = 1 self.dragging = False self.last_save_dir = dir_struct["user"] + self.tree_map = [] #Create tree from default if it does not exist validate.config_file("tree.xml","default_tree.xml") component.add("tree", self) + #build tree - self.root = self.AddRoot("Game Tree",self.icons['gear']) + self.root = self.AddRoot("Game Tree", self.icons['gear']) self.was_labeling = 0 self.rename_flag = 0 self.image_cache = {} logger.debug("Exit game_tree") - - @debugging + def add_nodehandler(self, nodehandler, nodeclass): if not self.nodehandlers.has_key(nodehandler): self.nodehandlers[nodehandler] = nodeclass else: logger.debug("Nodehandler for " + nodehandler + " already exists!") - - @debugging + def remove_nodehandler(self, nodehandler): if self.nodehandlers.has_key(nodehandler): del self.nodehandlers[nodehandler] else: logger.debug("No nodehandler for " + nodehandler + " exists!") - - @debugging + def init_nodehandlers(self): self.add_nodehandler('group_handler', containers.group_handler) self.add_nodehandler('tabber_handler', containers.tabber_handler) @@ -151,8 +156,8 @@ self.add_nodehandler('min_map', core.min_map) # event = wxKeyEvent - # set to be called by wxWindows by EVT_CHAR macro in __init__ - @debugging + # set to be called by wxWindows by EVT_CHAR macro in __init__ + def on_key_up(self, evt): key_code = evt.GetKeyCode() if self.dragging and (key_code == wx.WXK_SHIFT): @@ -166,11 +171,10 @@ obj.on_drop(evt) self.drag_obj = None evt.Skip() - - @debugging + def on_char(self, evt): key_code = evt.GetKeyCode() - curSelection = self.GetSelection() # Get the current selection + curSelection = self.GetSelection() # Get the current selection if evt.ShiftDown() and ((key_code == wx.WXK_UP) or (key_code == wx.WXK_DOWN)) and not self.dragging: curSelection = self.GetSelection() obj = self.GetPyData(curSelection) @@ -192,15 +196,16 @@ self.rename_flag = 1 self.EditLabel(curSelection) evt.Skip() - - @debugging - def locate_valid_tree(self, error, msg, dir, filename): ## --Snowdog 3/05 + + def locate_valid_tree(self, error, msg): ## --Snowdog 3/05 """prompts the user to locate a new tree file or create a new one""" response = wx.MessageDialog(self, msg, error, wx.YES|wx.NO|wx.ICON_ERROR) if response == wx.YES: file = None - filetypes = "Gametree (*.xml)|*.xml|All files (*.*)|*.*" - dlg = wx.FileDialog(self, "Locate Gametree file", dir, filename, filetypes,wx.OPEN | wx.CHANGE_DIR) + dlg = wx.FileDialog(self, "Locate Gametree file", dir_struct["user"], + filename[ ((filename.rfind(os.sep))+len(os.sep)):], + "Gametree (*.xml)|*.xml|All files (*.*)|*.*", + wx.OPEN | wx.CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: file = dlg.GetPath() dlg.Destroy() if not file: self.load_tree(error=1) @@ -210,100 +215,84 @@ validate.config_file("tree.xml","default_tree.xml") self.load_tree(error=1) return - - @debugging + + def load_tree(self, filename=dir_struct["user"]+'tree.xml', error=0): - self.settings.set_setting("gametree", filename) - tmp = None - xml_dom = None - xml_doc = None + self.settings.change("gametree", filename) + #check file exists + if not os.path.exists(filename): + emsg = "Gametree Missing!\n"+filename+" cannot be found.\n\n"\ + "Would you like to locate it?\n"\ + "(Selecting 'No' will cause a new default gametree to be generated)" + self.locate_valid_tree("Gametree Error", emsg) + return try: - logger.info("Reading Gametree file: " + filename + "...", True) - tmp = open(filename,"r") - xml_doc = xml.parseXml(tmp.read()) - tmp.close() - if xml_doc == None: pass - else: xml_dom = xml_doc._get_documentElement() - logger.info("done.", True) + f = open(filename, "rb") + tree = parse(f) + self.xml_root = tree.getroot() + except: + f.close() + self.xml_root = None + ### Alpha ### Doing some work on Gametree to add Element Tree, slowly at first. - except IOError: - emsg = "Gametree Missing!\n"+filename+" cannot be found.\n\n"\ - "Would you like to locate it?\n"\ - "(Selecting 'No' will cause a new default gametree to be generated)" - fn = filename[ ((filename.rfind(os.sep))+len(os.sep)):] - self.locate_valid_tree("Gametree Error", emsg, dir_struct["user"], fn) - logger.general(emsg) + if not self.xml_root: + os.rename(filename,filename+".corrupt") + emsg = "Your gametree is being regenerated.\n\n"\ + "To salvage a recent version of your gametree\n"\ + "exit OpenRPG and copy the lastgood.xml file in\n"\ + "your myfiles directory to "+filename+ "\n"\ + "in your myfiles directory.\n\n"\ + "lastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.\n\n"\ + "Would you like to select a different gametree file to use?\n"\ + "(Selecting 'No' will cause a new default gametree to be generated)" + self.locate_valid_tree("Corrupt Gametree!", emsg) return - if not xml_dom: - os.rename(filename,filename+".corrupt") - fn = filename[ ((filename.rfind(os.sep))+len(os.sep)):] - emsg = "Your gametree is being regenerated.\n\n"\ - "To salvage a recent version of your gametree\n"\ - "exit OpenRPG and copy the lastgood.xml file in\n"\ - "your myfiles directory to "+fn+ "\n"\ - "in your myfiles directory.\n\n"\ - "lastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.\n\n"\ - "Would you like to select a different gametree file to use?\n"\ - "(Selecting 'No' will cause a new default gametree to be generated)" - self.locate_valid_tree("Corrupt Gametree!", emsg, dir_struct["user"], fn) - logger.general(emsg) + if self.xml_root.tag != "gametree": + emsg = filename+" does not appear to be a valid gametree file.\n\n"\ + "Would you like to select a different gametree file to use?\n"\ + "(Selecting 'No' will cause a new default gametree to be generated)" + self.locate_valid_tree("Invalid Gametree!", emsg) return - - if xml_dom._get_tagName() != "gametree": - fn = filename[ ((filename.rfind(os.sep))+len(os.sep)):] - emsg = fn+" does not appear to be a valid gametree file.\n\n"\ - "Would you like to select a different gametree file to use?\n"\ - "(Selecting 'No' will cause a new default gametree to be generated)" - self.locate_valid_tree("Invalid Gametree!", emsg, dir_struct["user"], fn) - logger.debug(emsg) - return - - # get gametree version - we could write conversion code here! - self.master_dom = xml_dom - logger.debug("Master Dom Set") - try: - version = self.master_dom.getAttribute("version") + # version = self.xml_root.get("version") # see if we should load the gametree - loadfeatures = int(self.settings.get_setting("LoadGameTreeFeatures")) + loadfeatures = int(settings.get_setting("LoadGameTreeFeatures")) if loadfeatures: - xml_dom = xml.parseXml(open(dir_struct["template"]+"feature.xml","r").read()) - xml_dom = xml_dom._get_documentElement() - xml_dom = self.master_dom.appendChild(xml_dom) - self.settings.set_setting("LoadGameTreeFeatures","0") + features_tree = parse(orpg.dirpath.dir_struct["template"]+"feature.xml") + self.xml_root.append(features_tree.getroot()) + settings.change("LoadGameTreeFeatures","0") ## load tree logger.debug("Features loaded (if required)") self.CollapseAndReset(self.root) - children = self.master_dom._get_childNodes() - logger.info("Parsing Gametree Nodes ", True) - for c in children: - print '.', - self.load_xml(c,self.root) - logger.info("done", True) + logger.note("Parsing Gametree Nodes ", True) + for xml_child in self.xml_root: + logger.note('.', True) + self.load_xml(xml_child,self.root) + logger.note("done", True) + self.Expand(self.root) - self.SetPyData(self.root,self.master_dom) + self.SetPyData(self.root,self.xml_root) if error != 1: - infile = open(filename, "rb") - outfile = open(dir_struct["user"]+"lastgood.xml", "wb") - outfile.write(infile.read()) + with open(filename, "rb") as infile: + with open(dir_struct["user"]+"lastgood.xml", "wb") as outfile: + outfile.write(infile.read()) else: logger.info("Not overwriting lastgood.xml file.", True) except Exception, e: - logger.general(traceback.format_exc()) + logger.exception(traceback.format_exc()) wx.MessageBox("Corrupt Tree!\nYour game tree is being regenerated. To\nsalvage a recent version of your gametree\nexit OpenRPG and copy the lastgood.xml\nfile in your myfiles directory\nto "+filename+ "\nin your myfiles directory.\nlastgood.xml WILL BE OVERWRITTEN NEXT TIME YOU RUN OPENRPG.") os.rename(filename,filename+".corrupt") validate.config_file("tree.xml","default_tree.xml") self.load_tree(error=1) - - @debugging + def build_std_menu(self, obj=None): # build useful menu useful_menu = wx.Menu() useful_menu.Append(STD_MENU_NODE_USEFUL,"Use&ful") useful_menu.Append(STD_MENU_NODE_USELESS,"Use&less") - useful_menu.Append(STD_MENU_NODE_INDIFFERENT,"&Indifferent") + useful_menu.Append(STD_MENU_NODE_INDIFFERENT,"&Indifferent") # build standard menu self.std_menu = wx.Menu() @@ -358,15 +347,15 @@ self.Bind(wx.EVT_MENU, self.on_load_new_tree, id=TOP_NEW_TREE) self.Bind(wx.EVT_MENU, self.on_tree_prop, id=TOP_TREE_PROP) self.Bind(wx.EVT_MENU, self.on_insert_features, id=TOP_FEATURES) - - @debugging + + def do_std_menu(self, evt, obj): try: self.std_menu.Enable(STD_MENU_MAP, obj.checkToMapMenu()) except: self.std_menu.Enable(STD_MENU_MAP, obj.map_aware()) self.std_menu.Enable(STD_MENU_CLONE, obj.can_clone()) self.PopupMenu(self.std_menu) - - @debugging + + def strip_html(self, player): ret_string = "" x = 0 @@ -379,21 +368,16 @@ else: ret_string = ret_string + player[0][x] logger.debug(ret_string) return ret_string - - @debugging - def on_receive_data(self, data, player): - beg = string.find(data,"") - end = string.rfind(data,"") - data = data[6:end] + + + def on_receive_data(self, data): self.insert_xml(data) - - @debugging + def on_send_to_chat(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.on_send_to_chat(evt) - - @debugging + def on_whisper_to(self, evt): players = self.session.get_players() opts = [] @@ -415,48 +399,47 @@ for s in selections: player_ids.append(players[s][2]) self.chat.whisper_to_players(obj.tohtml(),player_ids) - - @debugging + + def on_export_html(self, evt): f = wx.FileDialog(self,"Select a file", self.last_save_dir,"","HTML (*.html)|*.html",wx.SAVE) if f.ShowModal() == wx.ID_OK: item = self.GetSelection() obj = self.GetPyData(item) type = f.GetFilterIndex() - file = open(f.GetPath(),"w") - data = ""+obj.master_dom.getAttribute("name")+"" - data += ""+obj.tohtml()+"" - for tag in ("","","","","",""): - data = data.replace(tag,tag+"\n") - file.write(data) - file.close() + with open(f.GetPath(),"w") as f: + data = ""+obj.xml.get("name")+"" + data += ""+obj.tohtml()+"" + for tag in ("","","","","",""): + data = data.replace(tag,tag+"\n") + f.write(data) self.last_save_dir, throwaway = os.path.split( f.GetPath() ) f.Destroy() os.chdir(dir_struct["home"]) - - @debugging + + def indifferent(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.usefulness("indifferent") - - @debugging + + def useful(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.usefulness("useful") - - @debugging + + def useless(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.usefulness("useless") - - @debugging + + def on_email(self,evt): pass - - @debugging + + def on_send_to(self, evt): players = self.session.get_players() opts = [] @@ -471,14 +454,14 @@ if dlg.ShowModal() == wx.ID_OK: item = self.GetSelection() obj = self.GetPyData(item) - xmldata = "" + xml.toxml(obj) + "" + xmldata = "" + tostring(obj.xml) + "" selections = dlg.get_selections() if len(selections) == len(opts): self.session.send(xmldata) else: for s in selections: self.session.send(xmldata,players[s][2]) dlg.Destroy() - - @debugging + + def on_icon(self, evt): icons = self.icons.keys() icons.sort() @@ -489,20 +472,20 @@ obj = self.GetPyData(item) obj.change_icon(key) dlg.Destroy() - - @debugging + + def on_wizard(self, evt): item = self.GetSelection() obj = self.GetPyData(item) - name = "New " + obj.master_dom.getAttribute("name") - icon = obj.master_dom.getAttribute("icon") + name = "New " + obj.xml_root.get("name") + icon = obj.xml_root.get("icon") xml_data = "" xml_data += xml.toxml(obj) xml_data += "" self.insert_xml(xml_data) logger.debug(xml_data) - - @debugging + + def on_clone(self, evt): item = self.GetSelection() obj = self.GetPyData(item) @@ -510,21 +493,24 @@ parent_node = self.GetItemParent(item) prev_sib = self.GetPrevSibling(item) if not prev_sib.IsOk(): prev_sib = parent_node - xml_dom = xml.parseXml(xml.toxml(obj)) - xml_dom = xml_dom._get_firstChild() - parent = obj.master_dom._get_parentNode() - xml_dom = parent.insertBefore(xml_dom, obj.master_dom) - self.load_xml(xml_dom, parent_node, prev_sib) - - @debugging + clone_xml = XML(tostring(obj.xml)) + if parent_node == self.root: parent_xml = self.GetPyData(parent_node) + else: parent_xml = self.GetPyData(parent_node).xml + for i in range(len(parent_xml)): + if parent_xml[i] is obj.xml: + parent_xml.insert(i, clone_xml) + break + self.load_xml(clone_xml, parent_node, prev_sib) + + def on_save(self, evt): """save node to a xml file""" item = self.GetSelection() obj = self.GetPyData(item) obj.on_save(evt) os.chdir(dir_struct["home"]) - - @debugging + + def on_save_tree_as(self, evt): f = wx.FileDialog(self,"Select a file", self.last_save_dir,"","*.xml",wx.SAVE) if f.ShowModal() == wx.ID_OK: @@ -532,21 +518,19 @@ self.last_save_dir, throwaway = os.path.split( f.GetPath() ) f.Destroy() os.chdir(dir_struct["home"]) - - @debugging + + def on_save_tree(self, evt=None): filename = self.settings.get_setting("gametree") self.save_tree(filename) - - @debugging + + def save_tree(self, filename=dir_struct["user"]+'tree.xml'): - self.master_dom.setAttribute("version",GAMETREE_VERSION) - self.settings.set_setting("gametree",filename) - file = open(filename,"w") - file.write(xml.toxml(self.master_dom,1)) - file.close() - - @debugging + self.xml_root.set("version", GAMETREE_VERSION) + settings.change("gametree", filename) + ElementTree(self.xml_root).write(filename) + + def on_load_new_tree(self, evt): f = wx.FileDialog(self,"Select a file", self.last_save_dir,"","*.xml",wx.OPEN) if f.ShowModal() == wx.ID_OK: @@ -554,8 +538,8 @@ self.last_save_dir, throwaway = os.path.split( f.GetPath() ) f.Destroy() os.chdir(dir_struct["home"]) - - @debugging + + def on_insert_file(self, evt): """loads xml file into the tree""" if self.last_save_dir == ".": @@ -566,8 +550,8 @@ self.last_save_dir, throwaway = os.path.split( f.GetPath() ) f.Destroy() os.chdir(dir_struct["home"]) - - @debugging + + def on_insert_url(self, evt): """loads xml url into the tree""" dlg = wx.TextEntryDialog(self,"URL?","Insert URL", "http://") @@ -576,161 +560,163 @@ file = urllib.urlopen(path) self.insert_xml(file.read()) dlg.Destroy() - - @debugging + + def on_insert_features(self, evt): self.insert_xml(open(dir_struct["template"]+"feature.xml","r").read()) - - @debugging + + def on_tree_prop(self, evt): dlg = gametree_prop_dlg(self, self.settings) if dlg.ShowModal() == wx.ID_OK: pass dlg.Destroy() - - @debugging + + def on_node_design(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.on_design(evt) - - @debugging + + def on_node_use(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.on_use(evt) - - @debugging + + def on_node_pp(self, evt): item = self.GetSelection() obj = self.GetPyData(item) obj.on_html_view(evt) - - @debugging + + def on_del(self, evt): status_value = "none" try: item = self.GetSelection() if item: - obj = self.GetPyData(item) - parent_obj = obj - try: - status_value = parent_obj.master_dom.getAttribute('status') - name = parent_obj.master_dom.getAttribute('name') - except: status_value = "none" - parent_obj = parent_obj.master_dom._get_parentNode() - while status_value!="useful" and status_value!="useless": + handler = self.GetPyData(item) + status_value = handler.xml.get('status') + name = handler.xml.get('name') + parent_item = self.GetItemParent(item) + while parent_item.IsOk() and status_value!="useful" and status_value!="useless": try: - status_value = parent_obj.getAttribute('status') - name = parent_obj.getAttribute('name') - if status_value == "useless": break - elif status_value == "useful": break - except: status_value = "none" - try: parent_obj = parent_obj._get_parentNode() - except: break + parent_handler = self.GetPyData(parent_item) + status_value = parent_handler.get('status') + name = parent_handler.get('name') + if status_value == "useless": + break + elif status_value == "useful": + break + except: + status_value = "none" + parent_item = self.GetItemParent(parent_item) if status_value == "useful": dlg = wx.MessageDialog(self, `name` + " And everything beneath it are considered useful. \n\nAre you sure you want to delete this item?",'Important Item',wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) - if dlg.ShowModal() == wx.ID_YES: obj.delete() - else: obj.delete() + if dlg.ShowModal() == wx.ID_YES: handler.delete() + else: handler.delete() except: if self.GetSelection() == self.GetRootItem(): msg = wx.MessageDialog(None,"You can't delete the root item.","Delete Error",wx.OK) else: msg = wx.MessageDialog(None,"Unknown error deleting node.","Delete Error",wx.OK) msg.ShowModal() msg.Destroy() - - @debugging + + def on_about(self, evt): item = self.GetSelection() obj = self.GetPyData(item) about = MyAboutBox(self,obj.about()) about.ShowModal() about.Destroy() - - @debugging + def on_send_to_map(self, evt): item = self.GetSelection() obj = self.GetPyData(item) if hasattr(obj,"on_send_to_map"): obj.on_send_to_map(evt) - - @debugging + def insert_xml(self, txt): #Updated to allow safe merging of gametree files #without leaving an unusable and undeletable node. # -- Snowdog 8/03 - xml_dom = xml.parseXml(txt) - if xml_dom == None: + if not txt: wx.MessageBox("Import Failed: Invalid or missing node data") - logger.debug("Import Failed: Invalid or missing node data") - logger.debug("Exit game_tree->insert_xml(self, txt)") + logger.general("Import Failed: Invalid or missing node data") return - xml_temp = xml_dom._get_documentElement() - - if not xml_temp: + try: new_xml = XML(txt) + except ExpatError: wx.MessageBox("Error Importing Node or Tree") - logger.debug("Error Importing Node or Tree") - logger.debug("Exit game_tree->insert_xml(self, txt)") + logger.general("Error Importing Node or Tree") + return + + if new_xml.tag == "gametree": + for xml_child in new_xml: + self.load_xml(xml_child, self.root) return - if xml_temp._get_tagName() == "gametree": - children = xml_temp._get_childNodes() - for c in children: self.load_xml(c, self.root) - logger.debug("Exit game_tree->insert_xml(self, txt)") - return + if new_xml.tag == "tree": + self.xml_root.append(new_xml.find('nodehandler')) + for xml_child in new_xml: + self.load_xml(xml_child, self.root) + return - if not xml_dom: - wx.MessageBox("XML Error") - logger.debug("XML Error") - logger.debug("Exit game_tree->insert_xml(self, txt)") - return - - xml_dom = xml_dom._get_firstChild() - child = self.master_dom._get_firstChild() - xml_dom = self.master_dom.insertBefore(xml_dom,child) - self.load_xml(xml_dom,self.root,self.root) - - @debugging + self.xml_root.append(new_xml) + self.load_xml(new_xml, self.root, self.root) + def build_img_list(self): """make image list""" helper = img_helper() self.icons = { } self._imageList= wx.ImageList(16,16,False) - man = open(dir_struct["icon"]+"icons.xml","r") - xml_dom = xml.parseXml(man.read()) - man.close() - xml_dom = xml_dom._get_documentElement() - node_list = xml_dom._get_childNodes() - for n in node_list: - key = n.getAttribute("name") - path = dir_struct["icon"] + n.getAttribute("file") + icons_xml = parse(orpg.dirpath.dir_struct["icon"]+"icons.xml") + for icon in icons_xml.getroot(): + key = icon.get("name") + path = orpg.dirpath.dir_struct["icon"] + icon.get("file") img = helper.load_file(path) self.icons[key] = self._imageList.Add(img) self.SetImageList(self._imageList) - - @debugging - def load_xml(self, xml_dom, parent_node, prev_node=None): + + def get_tree_map(self, parent): + ## Could be a little cleaner ## + family_tree = [] + test = parent + while test != self.root: + parent = self.GetItemText(test) + test = self.GetItemParent(test) + family_tree.append(parent) + return family_tree + + def load_xml(self, xml_element, parent_node, prev_node=None): + if parent_node != self.root: + ## Loading XML seems to lag on Grids and Images need a cache for load speed ## + family_tree = self.get_tree_map(parent_node) + family_tree.reverse() + map_str = '' #'!@' + for member in family_tree: + map_str += member +'::' + map_str = map_str[:len(map_str)-2] #+'@!' + xml_element.set('map', map_str) + #add the first tree node i = 0 - text = xml_dom.getAttribute("name") - icon = xml_dom.getAttribute("icon") + name = xml_element.get("name") + icon = xml_element.get("icon") if self.icons.has_key(icon): i = self.icons[icon] - name = xml_dom._get_nodeName() - logger.debug("Text, icon and name set\n" + text + "\n" + icon + "\n" + name) + if prev_node: - if prev_node == parent_node: new_tree_node = self.PrependItem(parent_node, text, i, i) - else: new_tree_node = self.InsertItem(parent_node,prev_node, text, i, i) - else: new_tree_node = self.AppendItem(parent_node, text, i, i) + if prev_node == parent_node: new_tree_node = self.PrependItem(parent_node, name, i, i) + else: new_tree_node = self.InsertItem(parent_node, prev_node, name, i, i) + else: new_tree_node = self.AppendItem(parent_node, name, i, i) logger.debug("Node Added to tree") #create a nodehandler or continue loading xml into tree - if name == "nodehandler": - #wx.BeginBusyCursor() - logger.debug("We have a Nodehandler") + if xml_element.tag == "nodehandler": try: - py_class = xml_dom.getAttribute("class") + py_class = xml_element.get("class") logger.debug("nodehandler class: " + py_class) if not self.nodehandlers.has_key(py_class): - raise Exception, "Unknown Nodehandler for " + py_class - self.nodes[self.id] = self.nodehandlers[py_class](xml_dom, new_tree_node) + raise Exception("Unknown Nodehandler for " + py_class) + self.nodes[self.id] = self.nodehandlers[py_class](xml_element, new_tree_node) self.SetPyData(new_tree_node, self.nodes[self.id]) logger.debug("Node Data set") bmp = self.nodes[self.id].get_scaled_bitmap(16,16) @@ -738,14 +724,13 @@ logger.debug("Node Icon loaded") self.id = self.id + 1 except Exception, er: - logger.general(traceback.format_exc()) - #logger.debug("Error Info: " + xml_dom.getAttribute("class") + "\n" + str(er), True)?indent? - self.Delete(new_tree_node) - parent = xml_dom._get_parentNode() - parent.removeChild(xml_dom) + logger.exception(traceback.format_exc()) + # was deleted -- should we delete non-nodehandler nodes then? + #self.Delete(new_tree_node) + #parent = xml_dom._get_parentNode() + #parent.removeChild(xml_dom) return new_tree_node - - @debugging + def cached_load_of_image(self, bmp_in, new_tree_node): image_list = self.GetImageList() img = wx.ImageFromBitmap(bmp_in) @@ -755,15 +740,13 @@ if self.image_cache[key] == str(img_data): image_index = key break - if image_index is None: image_index = image_list.Add(bmp_in) self.image_cache[image_index] = img_data self.SetItemImage(new_tree_node,image_index) self.SetItemImage(new_tree_node,image_index, wx.TreeItemIcon_Selected) return image_index - - @debugging + def on_rclick(self, evt): pt = evt.GetPosition() (item, flag) = self.HitTest(pt) @@ -773,8 +756,7 @@ if(isinstance(obj,core.node_handler)): obj.on_rclick(evt) else: self.PopupMenu(self.top_menu) else: self.PopupMenu(self.top_menu,pt) - - @debugging + def on_ldclick(self, evt): self.rename_flag = 0 pt = evt.GetPosition() @@ -789,8 +771,7 @@ elif action == "design": obj.on_design(evt) elif action == "print": obj.on_html_view(evt) elif action == "chat": self.on_send_to_chat(evt) - - @debugging + def on_left_down(self, evt): pt = evt.GetPosition() (item, flag) = self.HitTest(pt) @@ -803,8 +784,7 @@ self.rename_flag = 1 else: self.SelectItem(item) evt.Skip() - - @debugging + def on_left_up(self, evt): if self.dragging: cur = wx.StockCursor(wx.CURSOR_ARROW) @@ -818,8 +798,15 @@ if(isinstance(obj,core.node_handler)): obj.on_drop(evt) self.drag_obj = None - - @debugging + + def traverse(self, root, function, data=None, recurse=True): + child, cookie = self.GetFirstChild(root) + while child.IsOk(): + function(child, data) + if recurse: + self.traverse(child, function, data) + child, cookie = self.GetNextChild(root, cookie) + def on_label_change(self, evt): item = evt.GetItem() txt = evt.GetLabel() @@ -827,10 +814,9 @@ self.rename_flag = 0 if txt != "": obj = self.GetPyData(item) - obj.master_dom.setAttribute('name',txt) + obj.xml_root.setAttribute('name',txt) else: evt.Veto() - - @debugging + def on_label_begin(self, evt): if not self.rename_flag: evt.Veto() else: @@ -838,8 +824,7 @@ item = evt.GetItem() if item == self.GetRootItem(): evt.Veto() - - @debugging + def on_drag(self, evt): self.rename_flag = 0 item = self.GetSelection() @@ -850,8 +835,7 @@ cur = wx.StockCursor(wx.CURSOR_HAND) self.SetCursor(cur) self.drag_obj = obj - - @debugging + def is_parent_node(self, node, compare_node): parent_node = self.GetItemParent(node) if compare_node == parent_node: @@ -870,12 +854,12 @@ CTRL_CHAT = wx.NewId() CTRL_PRINT = wx.NewId() -class gametree_prop_dlg(wx.Dialog): - @debugging +class gametree_prop_dlg(wx.Dialog): + def __init__(self, parent, settings): wx.Dialog.__init__(self, parent, wx.ID_ANY, "Game Tree Properties") self.settings = settings - + #sizers sizers = {} sizers['but'] = wx.BoxSizer(wx.HORIZONTAL) @@ -929,13 +913,13 @@ self.SetAutoLayout(True) self.Fit() self.Bind(wx.EVT_BUTTON, self.on_ok, id=wx.ID_OK) - - @debugging + + def on_ok(self,evt): - self.settings.set_setting("gametree",self.ctrls[CTRL_TREE_FILE].GetValue()) - self.settings.set_setting("SaveGameTreeOnExit",str(self.ctrls[CTRL_YES].GetValue())) - if self.ctrls[CTRL_USE].GetValue(): self.settings.set_setting("treedclick","use") - elif self.ctrls[CTRL_DESIGN].GetValue(): self.settings.set_setting("treedclick","design") - elif self.ctrls[CTRL_PRINT].GetValue(): self.settings.set_setting("treedclick","print") - elif self.ctrls[CTRL_CHAT].GetValue(): self.settings.set_setting("treedclick","chat") + self.settings.change("gametree",self.ctrls[CTRL_TREE_FILE].GetValue()) + self.settings.change("SaveGameTreeOnExit",str(self.ctrls[CTRL_YES].GetValue())) + if self.ctrls[CTRL_USE].GetValue(): self.settings.change("treedclick","use") + elif self.ctrls[CTRL_DESIGN].GetValue(): self.settings.change("treedclick","design") + elif self.ctrls[CTRL_PRINT].GetValue(): self.settings.change("treedclick","print") + elif self.ctrls[CTRL_CHAT].GetValue(): self.settings.change("treedclick","chat") self.EndModal(wx.ID_OK) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/StarWarsd20.py --- a/orpg/gametree/nodehandlers/StarWarsd20.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/StarWarsd20.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,6 +29,8 @@ __version__ = "$Id: StarWarsd20.py,v 1.18 2006/11/15 12:11:23 digitalxero Exp $" from core import * +from orpg.tools.orpg_log import debug +from xml.etree.ElementTree import parse SWD20_EXPORT = wx.NewId() ############################ @@ -44,7 +46,7 @@ self.load_children() def load_children(self): - children = self.master_dom._get_childNodes() + children = self.xml.getchildren() for c in children: self.tree.load_xml(c,self.mytree_node) @@ -58,7 +60,7 @@ opt = wx.MessageBox("Add node as child?","Container Node",wx.YES_NO|wx.CANCEL) if opt == wx.YES: xml_dom = self.tree.drag_obj.delete() - xml_dom = self.master_dom.insertBefore(xml_dom,None) + xml_dom = self.xml.insertBefore(xml_dom,None) self.tree.load_xml(xml_dom, self.mytree_node) self.tree.Expand(self.mytree_node) elif opt == wx.NO: @@ -67,7 +69,7 @@ def tohtml(self): cookie = 0 html_str = "\n" html_str += "" html_str += "
" - html_str += ""+self.master_dom.getAttribute("name") + "" + html_str += ""+self.xml.get("name") + "" html_str += "
" max = tree.GetChildrenCount(handler.mytree_node) @@ -127,38 +129,36 @@ def on_version(self,old_version): node_handler.on_version(self,old_version) if old_version == "": - tmp = open(dir_struct["nodes"]+"StarWars_d20character.xml","r") - xml_dom = parseXml_with_dlg(self.tree,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["nodes"]+"StarWars_d20character.xml") + xml_dom = tree.getroot() ## add new nodes for tag in ("howtouse","inventory","powers","divine","pp"): - node_list = xml_dom.getElementsByTagName(tag) - self.master_dom.appendChild(node_list[0]) + node_list = xml_dom.findall(tag) + self.xml.append(node_list[0]) ## add new atts - melee_attack = self.master_dom.getElementsByTagName('melee')[0] - melee_attack.setAttribute("second","0") - melee_attack.setAttribute("third","0") - melee_attack.setAttribute("forth","0") - melee_attack.setAttribute("fifth","0") - melee_attack.setAttribute("sixth","0") - range_attack = self.master_dom.getElementsByTagName('ranged')[0] - range_attack.setAttribute("second","0") - range_attack.setAttribute("third","0") - range_attack.setAttribute("forth","0") - range_attack.setAttribute("fifth","0") - range_attack.setAttribute("sixth","0") + melee_attack = self.xml.findall('melee')[0] + melee_attack.set("second","0") + melee_attack.set("third","0") + melee_attack.set("forth","0") + melee_attack.set("fifth","0") + melee_attack.set("sixth","0") + range_attack = self.xml.findall('ranged')[0] + range_attack.set("second","0") + range_attack.set("third","0") + range_attack.set("forth","0") + range_attack.set("fifth","0") + range_attack.set("sixth","0") - gen_list = self.master_dom.getElementsByTagName('general')[0] + gen_list = self.xml.findall('general')[0] for tag in ("currentxp","xptolevel"): - node_list = xml_dom.getElementsByTagName(tag) - gen_list.appendChild(node_list[0]) + node_list = xml_dom.findall(tag) + gen_list.append(node_list[0]) ## temp fix - #parent = self.master_dom._get_parentNode() - #old_dom = parent.replaceChild(xml_dom,self.master_dom) - #self.master_dom = xml_dom + #parent = self.xml._get_parentNode() + #old_dom = parent.replaceChild(xml_dom,self.xml) + #self.xml = xml_dom print old_version @@ -176,7 +176,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -257,7 +257,7 @@ panel = obj.get_design_panel(self) else: panel = obj.get_use_panel(self) - name = obj.master_dom.getAttribute("name") + name = obj.xml.get("name") if panel: self.AddPage(panel,name) @@ -288,42 +288,33 @@ created by SWd20char_handler. """ def __init__(self, xml_dom, tree_node, parent): - node_handler.__init__(self,xml_dom, tree_node) + node_handler.__init__(self, xml_dom, tree_node) self.char_hander = parent self.drag = False self.frame = component.get('frame') self.myeditor = None - def on_drop(self,evt): + def on_drop(self, evt): pass - def on_rclick(self,evt): + def on_rclick(self, evt): pass - def on_ldclick(self,evt): #Function needs help. Needs an OnClose I think. - if self.myeditor == None or self.myeditor.destroyed: - title = self.master_dom.getAttribute('name') + " Editor" - # Frame created in correctly. - self.myeditor = wx.Frame(self.frame,title,dir_struct["icon"]+'grid.ico') - wnd = self.get_design_panel(self.myeditor) - self.myeditor.panel = wnd - self.wnd = wnd - self.myeditor.Show(1) - else: - self.myeditor.Raise() + def on_ldclick(self, evt): + return - def on_html(self,evt): + def on_html(self, evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) - def get_design_panel(self,parent): + def get_design_panel(self, parent): pass - def get_use_panel(self,parent): + def get_use_panel(self, parent): return self.get_design_panel(parent) def delete(self): @@ -338,23 +329,23 @@ SWd20_char_child.__init__(self, xml_dom, tree_node, parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') self.skills={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.skills[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) def get_mod(self,name): skill = self.skills[name] - stat = skill.getAttribute('stat') - ac = int(skill.getAttribute('armorcheck')) + stat = skill.get('stat') + ac = int(skill.get('armorcheck')) if ac: ac = self.char_hander.child_handlers['ac'].get_check_pen() stat_mod = self.char_hander.child_handlers['abilities'].get_mod(stat) - rank = int(skill.getAttribute('rank')) - misc = int(skill.getAttribute('misc')) + rank = int(skill.get('rank')) + misc = int(skill.get('misc')) total = stat_mod + rank + misc + ac return total @@ -363,8 +354,8 @@ item = self.tree.GetSelection() name = self.tree.GetItemText(item) skill = self.skills[name] - rank = int(skill.getAttribute('rank')) - untrained = int(skill.getAttribute('untrained')) + rank = int(skill.get('rank')) + untrained = int(skill.get('untrained')) chat = self.chat if item == self.mytree_node: SWd20_char_child.on_ldclick(self,evt) @@ -388,14 +379,14 @@ def tohtml(self): html_str = """""" - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - rank = n.getAttribute('rank') + name = n.get('name') + stat = n.get('stat') + rank = n.get('rank') html_str = html_str + "" stat_mod = str(self.char_hander.child_handlers['abilities'].get_mod(stat)) - misc = n.getAttribute('misc') + misc = n.get('misc') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -413,11 +404,11 @@ def __init__(self, xml_dom, tree_node, parent): SWd20_char_child.__init__(self, xml_dom, tree_node, parent) self.abilities = {} - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') tree = self.tree icons = tree.icons for n in node_list: - name = n.getAttribute('abbr') + name = n.get('abbr') self.abilities[name] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -438,13 +429,13 @@ chat.ParsePost( txt, True, True ) def get_mod(self,abbr): - score = int(self.abilities[abbr].getAttribute('base')) + score = int(self.abilities[abbr].get('base')) mod = (score - 10) / 2 return mod def set_score(self,abbr,score): if score >= 0: - self.abilities[abbr].setAttribute("base",str(score)) + self.abilities[abbr].set("base",str(score)) def get_design_panel(self,parent): wnd = outline_panel(parent,self,abil_grid,"Abilities") @@ -454,11 +445,11 @@ def tohtml(self): html_str = """
SkillKey RankAbilMiscTotal
"+name+""+stat+""+rank+"
""" - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') for n in node_list: - name = n.getAttribute('name') - abbr = n.getAttribute('abbr') - base = n.getAttribute('base') + name = n.get('name') + abbr = n.get('abbr') + base = n.get('base') mod = str(self.get_mod(abbr)) if mod >= 0: mod1 = "+" @@ -476,21 +467,21 @@ SWd20_char_child.__init__(self, xml_dom, tree_node, parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') self.saves={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.saves[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) def get_mod(self,name): save = self.saves[name] - stat = save.getAttribute('stat') + stat = save.get('stat') stat_mod = self.char_hander.child_handlers['abilities'].get_mod(stat) - base = int(save.getAttribute('base')) - miscmod = int(save.getAttribute('miscmod')) -# magmod = int(save.getAttribute('magmod')) + base = int(save.get('base')) + miscmod = int(save.get('miscmod')) +# magmod = int(save.get('magmod')) # total = stat_mod + base + miscmod + magmod total = stat_mod + base + miscmod return total @@ -522,15 +513,15 @@ html_str = """
Ability BaseModifier
""" - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - base = n.getAttribute('base') + name = n.get('name') + stat = n.get('stat') + base = n.get('base') html_str = html_str + "" stat_mod = str(self.char_hander.child_handlers['abilities'].get_mod(stat)) - mag = n.getAttribute('magmod') - misc = n.getAttribute('miscmod') + mag = n.get('magmod') + misc = n.get('miscmod') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -555,12 +546,11 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
Save KeyBaseAbilMagic MiscTotal
"+name+""+stat+""+base+"
General Information
" for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + ", " + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -568,9 +558,8 @@ self.char_hander.rename(name) def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + node = self.xml.findall( 'name' )[0] + return node.text class SWd20classes(SWd20_char_child): @@ -587,17 +576,17 @@ def tohtml(self): html_str = "
Classes
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name') + " ("+n.getAttribute('level')+"), " + html_str += n.get('name') + " ("+n.get('level')+"), " html_str = html_str[:len(html_str)-2] + "
" return html_str def get_char_lvl( self, attr ): - node_list = self.master_dom.getElementsByTagName('class') + node_list = self.xml.findall('class') for n in node_list: - lvl = n.getAttribute('level') - type = n.getAttribute('name') + lvl = n.get('level') + type = n.get('name') if attr == "level": return lvl elif attr == "class": @@ -618,9 +607,9 @@ def tohtml(self): html_str = "
Feats
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name')+ ", " + html_str += n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -649,12 +638,11 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
General Information
" for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + "
" + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + "
" html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -662,9 +650,8 @@ self.char_hander.rename(name) def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + node = self.xml.findall( 'name' )[0] + return node.text class SWd20hp(SWd20_char_child): """ Node Handler for hit points. This handler will be @@ -680,19 +667,19 @@ def tohtml(self): html_str = "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" html_str += "
Wound Points
Max:"+self.master_dom.getAttribute('max')+"Current:"+self.master_dom.getAttribute('current')+"
Max:"+self.xml.get('max')+"Current:"+self.xml.get('current')+"
" return html_str def get_max_hp( self ): try: - return eval( self.master_dom.getAttribute( 'max' ) ) + return eval( self.xml.get( 'max' ) ) except: return 0 def get_current_hp( self ): try: - return eval( self.master_dom.getAttribute( 'current' ) ) + return eval( self.xml.get( 'current' ) ) except: return 0 @@ -710,19 +697,19 @@ def tohtml(self): html_str = "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" html_str += "
Vitality Points
Max:"+self.master_dom.getAttribute('max')+"Current:"+self.master_dom.getAttribute('current')+"
Max:"+self.xml.get('max')+"Current:"+self.xml.get('current')+"
" return html_str def get_max_vp( self ): try: - return eval( self.master_dom.getAttribute( 'max' ) ) + return eval( self.xml.get( 'max' ) ) except: return 0 def get_current_vp( self ): try: - return eval( self.master_dom.getAttribute( 'current' ) ) + return eval( self.xml.get( 'current' ) ) except: return 0 @@ -732,9 +719,9 @@ """ def __init__(self,xml_dom,tree_node,parent): SWd20_char_child.__init__(self,xml_dom,tree_node,parent) - node_list = self.master_dom.getElementsByTagName('melee') + node_list = self.xml.findall('melee') self.melee = node_list[0] - node_list = self.master_dom.getElementsByTagName('ranged') + node_list = self.xml.findall('ranged') self.ranged = node_list[0] self.refresh_weapons() @@ -743,9 +730,9 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('weapon') + node_list = self.xml.findall('weapon') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['sword'],icons['sword']) tree.SetPyData(new_tree_node,self) self.weapons[name]=n @@ -761,13 +748,13 @@ else: stat_mod = self.char_hander.child_handlers['abilities'].get_mod('Dex') temp = self.ranged - base = int(temp.getAttribute('base')) - base2 = int(temp.getAttribute('second')) - base3 = int(temp.getAttribute('third')) - base4 = int(temp.getAttribute('forth')) - base5 = int(temp.getAttribute('fifth')) - base6 = int(temp.getAttribute('sixth')) - misc = int(temp.getAttribute('misc')) + base = int(temp.get('base')) + base2 = int(temp.get('second')) + base3 = int(temp.get('third')) + base4 = int(temp.get('forth')) + base5 = int(temp.get('fifth')) + base6 = int(temp.get('sixth')) + misc = int(temp.get('misc')) return (base, base2, base3, base4, base5, base6, stat_mod ,misc) def on_rclick(self,evt): @@ -778,8 +765,8 @@ SWd20_char_child.on_ldclick(self,evt) #self.frame.add_panel(self.get_design_panel(self.frame.note)) else: - mod = int(self.weapons[name].getAttribute('mod')) - if self.weapons[name].getAttribute('range') == '0': + mod = int(self.weapons[name].get('mod')) + if self.weapons[name].get('range') == '0': mod = mod + self.get_mod('m') if mod >= 0: mod1 = "+" @@ -792,7 +779,7 @@ else: mod1 = "" chat = self.chat - dmg = self.weapons[name].getAttribute('damage') + dmg = self.weapons[name].get('damage') lvl = self.get_char_lvl('level') cname = self.char_hander.get_char_name() txt = '%s %s Attack Roll: [1d20%s%s] ===> DMG: [%s%s%s]' % (cname, name, mod1, mod, dmg, mod1, mod) @@ -825,23 +812,23 @@ html_str += "
"+str(ranged[1])+""+str(ranged[2])+"
" - n_list = self.master_dom.getElementsByTagName('weapon') + n_list = self.xml.findall('weapon') for n in n_list: - mod = n.getAttribute('mod') + mod = n.get('mod') if mod >= 0: mod1 = "+" else: mod1 = "" - ran = n.getAttribute('range') + ran = n.get('range') total = str(int(mod) + self.get_mod(ran)) html_str += """

""" - html_str += "" - html_str += "" + html_str += "" + html_str += "" html_str += """""" - html_str += "" - html_str += "" + html_str += "" + html_str += "" html_str += '
Weapon AttackDamageCritical
"+n.getAttribute('name')+""+total+""+n.getAttribute('damage')+""+n.getAttribute('critical')+"
"+n.get('name')+""+total+""+n.get('damage')+""+n.get('critical')+"
RangeWeight TypeSizeMisc Mod
"+ran+""+n.getAttribute('weight')+""+n.getAttribute('type')+""+n.getAttribute('size')+"
"+ran+""+n.get('weight')+""+n.get('type')+""+n.get('size')+"%s%s
' % (mod1, mod) return html_str @@ -870,19 +857,19 @@ return ac_total def get_max_dex(self): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') dex = 10 for a in armor_list: - temp = int(a.getAttribute("maxdex")) + temp = int(a.get("maxdex")) if temp < dex: dex = temp return dex def get_total(self,attr): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') total = 0 for a in armor_list: - total += int(a.getAttribute(attr)) + total += int(a.get(attr)) return total def get_design_panel(self,parent): @@ -898,19 +885,19 @@ html_str += ""+str(self.get_spell_failure())+"" html_str += ""+str(self.get_max_dex())+"" html_str += ""+str(self.get_total_weight())+"" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: html_str += """

""" - html_str += "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" + html_str += "" html_str += """""" - html_str += "" - html_str += "" - html_str += "" - html_str += "
Armor TypeBonus
"+n.getAttribute('name')+""+n.getAttribute('type')+""+n.getAttribute('bonus')+"
"+n.get('name')+""+n.get('type')+""+n.get('bonus')+"
Check PenaltySpell Failure Max DexSpeedWeight
"+n.getAttribute('checkpenalty')+""+n.getAttribute('maxdex')+""+n.getAttribute('speed')+""+n.getAttribute('weight')+"
" + html_str += ""+n.get('checkpenalty')+"" + html_str += ""+n.get('maxdex')+"" + html_str += ""+n.get('speed')+"" + html_str += ""+n.get('weight')+"" return html_str @@ -943,7 +930,7 @@ class char_panel(wx.ScrolledWindow): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'TWO') + pname = handler.xml.set("name", 'TWO') wx.ScrolledWindow.__init__(self, parent, -1,style=wx.VSCROLL | wx.SUNKEN_BORDER ) self.height = 1200 self.SetScrollbars(10, 10,80, self.height/10) @@ -999,13 +986,12 @@ class howto_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'How To') + pname = handler.xml.set("name", 'How To') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom - n_list = self.master_dom._get_childNodes() + self.xml = handler.xml + n_list = self.xml.getchildren() for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - self.sizer.AddMany([ (wx.StaticText(self, -1, t_node._get_nodeValue()), 0, wx.ALIGN_CENTER_VERTICAL), + self.sizer.AddMany([ (wx.StaticText(self, -1, n.text), 0, wx.ALIGN_CENTER_VERTICAL), ]) self.sizer.AddGrowableCol(1) self.SetSizer(self.sizer) @@ -1017,13 +1003,13 @@ class hp_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'WoundPoints') + pname = handler.xml.set("name", 'WoundPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "WP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, WP_CUR, self.master_dom.getAttribute('current')), 0, wx.EXPAND), + (wx.TextCtrl(self, WP_CUR, self.xml.get('current')), 0, wx.EXPAND), (wx.StaticText(self, -1, "WP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, WP_MAX, self.master_dom.getAttribute('max')), 0, wx.EXPAND), + (wx.TextCtrl(self, WP_MAX, self.xml.get('max')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) self.Bind(wx.EVT_SIZE, self.on_size) @@ -1034,9 +1020,9 @@ def on_text(self,evt): id = evt.GetId() if id == WP_CUR: - self.master_dom.setAttribute('current',evt.GetString()) + self.xml.set('current',evt.GetString()) elif id == WP_MAX: - self.master_dom.setAttribute('max',evt.GetString()) + self.xml.set('max',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1048,13 +1034,13 @@ class vp_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'VitalityPoints') + pname = handler.xml.set("name", 'VitalityPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "VP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, VP_CUR, self.master_dom.getAttribute('current')), 0, wx.EXPAND), + (wx.TextCtrl(self, VP_CUR, self.xml.get('current')), 0, wx.EXPAND), (wx.StaticText(self, -1, "VP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, VP_MAX, self.master_dom.getAttribute('max')), 0, wx.EXPAND), + (wx.TextCtrl(self, VP_MAX, self.xml.get('max')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) self.Bind(wx.EVT_SIZE, self.on_size) @@ -1065,9 +1051,9 @@ def on_text(self,evt): id = evt.GetId() if id == VP_CUR: - self.master_dom.setAttribute('current',evt.GetString()) + self.xml.set('current',evt.GetString()) elif id == VP_MAX: - self.master_dom.setAttribute('max',evt.GetString()) + self.xml.set('max',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1081,18 +1067,18 @@ #class pp_panel(wx.Panel): # def __init__(self, parent, handler): # wx.Panel.__init__(self, parent, -1) -# pname = handler.master_dom.setAttribute("name", 'PowerPoints') +# pname = handler.xml.set("name", 'PowerPoints') # self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap -# self.master_dom = handler.master_dom +# self.xml = handler.xml # # self.sizer.AddMany([ (wx.StaticText(self, -1, "PP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), -# (wx.TextCtrl(self, PP_CUR, self.master_dom.getAttribute('current1')), 0, wx.EXPAND), +# (wx.TextCtrl(self, PP_CUR, self.xml.get('current1')), 0, wx.EXPAND), # (wx.StaticText(self, -1, "PP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), -# (wx.TextCtrl(self, PP_MAX, self.master_dom.getAttribute('max1')), 0, wx.EXPAND), +# (wx.TextCtrl(self, PP_MAX, self.xml.get('max1')), 0, wx.EXPAND), # (wx.StaticText(self, -1, "Current Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), -# (wx.TextCtrl(self, PP_FRE, self.master_dom.getAttribute('free')), 0, wx.EXPAND), +# (wx.TextCtrl(self, PP_FRE, self.xml.get('free')), 0, wx.EXPAND), # (wx.StaticText(self, -1, "Max Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), -# (wx.TextCtrl(self, PP_MFRE, self.master_dom.getAttribute('maxfree')), 0, wx.EXPAND), +# (wx.TextCtrl(self, PP_MFRE, self.xml.get('maxfree')), 0, wx.EXPAND), # ]) # self.sizer.AddGrowableCol(1) # self.Bind(wx.EVT_SIZE, self.on_size) @@ -1104,13 +1090,13 @@ # def on_text(self,evt): # id = evt.GetId() # if id == PP_CUR: -# self.master_dom.setAttribute('current1',evt.GetString()) +# self.xml.set('current1',evt.GetString()) # elif id == PP_MAX: -# self.master_dom.setAttribute('max1',evt.GetString()) +# self.xml.set('max1',evt.GetString()) # elif id == PP_FRE: -# self.master_dom.setAttribute('free',evt.GetString()) +# self.xml.set('free',evt.GetString()) # elif id == PP_MFRE: -# self.master_dom.setAttribute('maxfree',evt.GetString()) +# self.xml.set('maxfree',evt.GetString()) # # def on_size(self,evt): # s = self.GetClientSizeTuple() @@ -1120,12 +1106,12 @@ class gen_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'General') + pname = handler.xml.set("name", 'General') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -1138,15 +1124,13 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + self.n_list[row].text = value if row==0: self.handler.on_name_change(value) def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) + self.SetCellValue(rowi,0,self.n_list[rowi].tag) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + self.SetCellValue(rowi,1,self.n_list[rowi].text) def on_size(self,evt): (w,h) = self.GetClientSizeTuple() @@ -1160,12 +1144,12 @@ class inventory_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Money and Inventory') + pname = handler.xml.set("name", 'Money and Inventory') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -1178,15 +1162,13 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + self.n_list[row].text = value if row==0: self.handler.on_name_change(value) def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) + self.SetCellValue(rowi,0, self.n_list[rowi].tag) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + self.SetCellValue(rowi,1,self.n_list[rowi].text) def on_size(self,evt): (w,h) = self.GetClientSizeTuple() @@ -1200,12 +1182,12 @@ class abil_grid(wx.grid.Grid): """grid for abilities""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Stats') + pname = handler.xml.set("name", 'Stats') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - stats = handler.master_dom.getElementsByTagName('stat') + stats = handler.xml.findall('stat') self.CreateGrid(len(stats),3) self.SetRowLabelSize(0) col_names = ['Ability','Score','Modifier'] @@ -1223,7 +1205,7 @@ value = self.GetCellValue(row,col) try: int(value) - self.stats[row].setAttribute('base',value) + self.stats[row].set('base',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -1232,11 +1214,11 @@ def refresh_row(self,rowi): s = self.stats[rowi] - name = s.getAttribute('name') - abbr = s.getAttribute('abbr') + name = s.get('name') + abbr = s.get('abbr') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,s.getAttribute('base')) + self.SetCellValue(rowi,1,s.get('base')) self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr))) self.SetReadOnly(rowi,2) @@ -1258,12 +1240,12 @@ class save_grid(wx.grid.Grid): """grid for saves""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Saves') + pname = handler.xml.set("name", 'Saves') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - saves = handler.master_dom.getElementsByTagName('save') + saves = handler.xml.findall('save') self.stats = handler.char_hander.child_handlers['abilities'] self.CreateGrid(len(saves),7) self.SetRowLabelSize(0) @@ -1282,28 +1264,28 @@ try: int(value) if col == 2: - self.saves[row].setAttribute('base',value) + self.saves[row].set('base',value) elif col ==4: - self.saves[row].setAttribute('magmod',value) + self.saves[row].set('magmod',value) elif col ==4: - self.saves[row].setAttribute('miscmod',value) + self.saves[row].set('miscmod',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") def refresh_row(self,rowi): s = self.saves[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('base')) + self.SetCellValue(rowi,2,s.get('base')) self.SetCellValue(rowi,3,str(self.stats.get_mod(stat))) self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('magmod')) - self.SetCellValue(rowi,5,s.getAttribute('miscmod')) + self.SetCellValue(rowi,4,s.get('magmod')) + self.SetCellValue(rowi,5,s.get('miscmod')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,6,mod) self.SetReadOnly(rowi,6) @@ -1326,12 +1308,12 @@ class skill_grid(wx.grid.Grid): """ panel for skills """ def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Skills') + pname = handler.xml.set("name", 'Skills') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - skills = handler.master_dom.getElementsByTagName('skill') + skills = handler.xml.findall('skill') self.stats = handler.char_hander.child_handlers['abilities'] self.CreateGrid(len(skills),7) self.SetRowLabelSize(0) @@ -1351,28 +1333,28 @@ try: int(value) if col == 3: - self.skills[row].setAttribute('rank',value) + self.skills[row].set('rank',value) elif col ==5: - self.skills[row].setAttribute('misc',value) + self.skills[row].set('misc',value) elif col == 1: - self.skills[row].setAttribute('untrained',value) + self.skills[row].set('untrained',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") def refresh_row(self,rowi): s = self.skills[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,s.getAttribute('untrained')) - stat = s.getAttribute('stat') + self.SetCellValue(rowi,1,s.get('untrained')) + stat = s.get('stat') self.SetCellValue(rowi,2,stat) self.SetReadOnly(rowi,2) - self.SetCellValue(rowi,3,s.getAttribute('rank')) + self.SetCellValue(rowi,3,s.get('rank')) self.SetCellValue(rowi,4,str(self.stats.get_mod(stat))) self.SetReadOnly(rowi,4) - self.SetCellValue(rowi,5,s.getAttribute('misc')) + self.SetCellValue(rowi,5,s.get('misc')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,6,mod) self.SetReadOnly(rowi,6) @@ -1395,7 +1377,7 @@ class feat_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Feats') + pname = handler.xml.set("name", 'Feats') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -1410,9 +1392,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, remove_btn) self.Bind(wx.EVT_BUTTON, self.on_add, add_btn) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),2,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Feat") @@ -1423,8 +1405,8 @@ def refresh_row(self,i): feat = self.n_list[i] - name = feat.getAttribute('name') - type = feat.getAttribute('type') + name = feat.get('name') + type = feat.get('type') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,type) @@ -1435,23 +1417,20 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["SWd20"]+"d20feats.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('feat') + tree = parse(dir_struct["SWd20"]+"d20feats.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('feat') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() @@ -1470,7 +1449,7 @@ class attack_grid(wx.grid.Grid): """grid for attacks""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.parent = parent self.handler = handler @@ -1493,19 +1472,19 @@ try: int(value) if col==1: - self.rows[row].setAttribute('base',value) + self.rows[row].set('base',value) elif col==2: - self.rows[row].setAttribute('second',value) + self.rows[row].set('second',value) elif col==3: - self.rows[row].setAttribute('third',value) + self.rows[row].set('third',value) elif col==4: - self.rows[row].setAttribute('forth',value) + self.rows[row].set('forth',value) elif col==5: - self.rows[row].setAttribute('fifth',value) + self.rows[row].set('fifth',value) elif col==6: - self.rows[row].setAttribute('sixth',value) + self.rows[row].set('sixth',value) elif col==8: - self.rows[row].setAttribute('misc',value) + self.rows[row].set('misc',value) self.parent.refresh_data() except: self.SetCellValue(row,col,"0") @@ -1538,7 +1517,7 @@ class weapon_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Weapons') + pname = handler.xml.set("name", 'Weapons') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -1553,9 +1532,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, remove_btn) self.Bind(wx.EVT_BUTTON, self.on_add, add_btn) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom.getElementsByTagName('weapon') + n_list = handler.xml.findall('weapon') self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.handler = handler self.grid.CreateGrid(len(n_list),9,1) self.grid.SetRowLabelSize(0) @@ -1570,31 +1549,31 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('name',value) + self.n_list[row].set('name',value) elif col == 2: try: int(value) - self.n_list[row].setAttribute('mod',value) + self.n_list[row].set('mod',value) self.refresh_row(row) except: self.grid.SetCellValue(row,col,"1") else: - self.n_list[row].setAttribute(self.grid.GetColLabelValue(col),value) + self.n_list[row].set(self.grid.GetColLabelValue(col),value) def refresh_row(self,i): n = self.n_list[i] - name = n.getAttribute('name') - mod = n.getAttribute('mod') - ran = n.getAttribute('range') + name = n.get('name') + mod = n.get('mod') + ran = n.get('range') total = str(int(mod) + self.handler.get_mod(ran)) self.grid.SetCellValue(i,0,name) - self.grid.SetCellValue(i,1,n.getAttribute('damage')) + self.grid.SetCellValue(i,1,n.get('damage')) self.grid.SetCellValue(i,2,mod) - self.grid.SetCellValue(i,3,n.getAttribute('critical')) - self.grid.SetCellValue(i,4,n.getAttribute('type')) - self.grid.SetCellValue(i,5,n.getAttribute('weight')) + self.grid.SetCellValue(i,3,n.get('critical')) + self.grid.SetCellValue(i,4,n.get('type')) + self.grid.SetCellValue(i,5,n.get('weight')) self.grid.SetCellValue(i,6,ran) - self.grid.SetCellValue(i,7,n.getAttribute('size') ) + self.grid.SetCellValue(i,7,n.get('size') ) self.grid.SetCellValue(i,8,total) self.grid.SetReadOnly(i,8) @@ -1603,27 +1582,24 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_weapons() def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["SWd20"]+"d20weapons.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('weapon') + tree = parse(dir_struct["SWd20"]+"d20weapons.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('weapon') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.n_list = self.xml.findall('weapon') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_weapons() dlg.Destroy() @@ -1646,7 +1622,7 @@ class attack_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') wx.Panel.__init__(self, parent, -1) self.a_grid = attack_grid(self, handler) @@ -1668,7 +1644,7 @@ class ac_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Armor') + pname = handler.xml.set("name", 'Armor') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -1683,8 +1659,8 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, remove_btn) self.Bind(wx.EVT_BUTTON, self.on_add, add_btn) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.master_dom = handler.master_dom - n_list = handler.master_dom._get_childNodes() + self.xml = handler.xml + n_list = handler.xml.getchildren() self.n_list = n_list col_names = ['Armor','DR','Max Dex','Check Penalty','Weight','Speed (10)','Speed (6)','type'] self.grid.CreateGrid(len(n_list),len(col_names),1) @@ -1704,39 +1680,36 @@ if col >= 1 and col <= 5: try: int(value) - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) except: self.grid.SetCellValue(row,col,"0") else: - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) def refresh_row(self,i): n = self.n_list[i] for y in range(len(self.atts)): - self.grid.SetCellValue(i,y,n.getAttribute(self.atts[y])) + self.grid.SetCellValue(i,y,n.get(self.atts[y])) def on_remove(self,evt): rows = self.grid.GetNumberRows() for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["SWd20"]+"d20armor.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('armor') + tree = parse(dir_struct["SWd20"]+"d20armor.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('armor') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() @@ -1755,7 +1728,7 @@ class class_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Class') + pname = handler.xml.set("name", 'Class') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -1771,9 +1744,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, add_btn) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),2,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Class") @@ -1788,15 +1761,15 @@ value = self.grid.GetCellValue(row,col) try: int(value) - self.n_list[row].setAttribute('level',value) + self.n_list[row].set('level',value) except: self.grid.SetCellValue(row,col,"1") def refresh_row(self,i): n = self.n_list[i] - name = n.getAttribute('name') - level = n.getAttribute('level') + name = n.get('name') + level = n.get('level') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -1807,23 +1780,20 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["SWd20"]+"SWd20classes.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('class') + tree = parse(dir_struct["SWd20"]+"SWd20classes.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('class') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/containers.py --- a/orpg/gametree/nodehandlers/containers.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/containers.py Tue Nov 10 14:11:28 2009 -0600 @@ -39,36 +39,30 @@ """ should not be used! only a base class! """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self, xml, tree_node): + node_handler.__init__(self, xml, tree_node) self.load_children() def load_children(self): - children = self.master_dom._get_childNodes() - for c in children: - self.tree.load_xml(c,self.mytree_node) + for child_xml in self.xml: + self.tree.load_xml(child_xml,self.mytree_node) - def check_map_aware(self, obj, evt): - if hasattr(obj,"map_aware") and obj.map_aware(): - obj.on_send_to_map(evt) - + def check_map_aware(self, treenode, evt): + node = self.tree.GetPyData(treenode) + if hasattr(node,"map_aware") and node.map_aware(): + node.on_send_to_map(evt) def on_send_to_map(self, evt): - child = self.tree.GetFirstChild(self.mytree_node) - if child[0].IsOk(): - self.traverse(child[0], self.check_map_aware, 0, evt) + self.tree.traverse(self.mytree_node, self.check_map_aware, evt) - - def checkChildToMap(self, obj, evt): - if hasattr(obj,"map_aware") and obj.map_aware(): + def checkChildToMap(self, treenode, evt): + node = self.tree.GetPyData(treenode) + if hasattr(node,"map_aware") and node.map_aware(): self.mapcheck = True def checkToMapMenu(self): self.mapcheck = False - child = self.tree.GetFirstChild(self.mytree_node) - if child[0].IsOk(): - self.traverse(child[0], self.checkChildToMap, 0, self.mapcheck) - + self.tree.traverse(self.mytree_node, self.checkChildToMap) return self.mapcheck def on_drop(self,evt): @@ -77,24 +71,24 @@ return opt = wx.MessageBox("Add node as child?","Container Node",wx.YES_NO|wx.CANCEL) if opt == wx.YES: - xml_dom = self.tree.drag_obj.delete() - xml_dom = self.master_dom.insertBefore(xml_dom,None) - self.tree.load_xml(xml_dom, self.mytree_node) + drop_xml = self.tree.drag_obj.delete() + self.xml.insert(0, drop_xml) + self.tree.load_xml(drop_xml, self.mytree_node) self.tree.Expand(self.mytree_node) elif opt == wx.NO: node_handler.on_drop(self,evt) - def gen_html(self, obj, evt): - self.html_str += "

" + obj.tohtml() - + def gen_html(self, treenode, evt): + node = self.tree.GetPyData(treenode) + self.html_str += "

" + node.tohtml() + def tohtml(self): self.html_str = "\n" self.html_str += "
" - self.html_str += ""+self.master_dom.getAttribute("name") + "" + self.html_str += ""+self.xml.get("name") + "" self.html_str += "
" - child = self.tree.GetFirstChild(self.mytree_node) - self.traverse(child[0], self.gen_html, 0, None) + self.tree.traverse(self.mytree_node, self.gen_html, recurse=False) self.html_str += "
" return self.html_str @@ -111,22 +105,24 @@ This handler will continue parsing child xml data. """ - def __init__(self,xml_dom,tree_node): - container_handler.__init__(self,xml_dom,tree_node) + def __init__(self, xml, tree_node): + container_handler.__init__(self, xml, tree_node) def load_children(self): self.atts = None - children = self.master_dom._get_childNodes() - for c in children: - if c._get_tagName() == "group_atts": - self.atts = c - else: - self.tree.load_xml(c,self.mytree_node) + for child_xml in self.xml: + if child_xml.get == "group_atts": #having the group attributes as a child is bad! + self.xml.remove(child_xml) + elif child_xml: + self.tree.load_xml(child_xml, self.mytree_node) + if not self.xml.get('cols'): self.xml.set('cols', '1') + if not self.xml.get('border'): self.xml.set('border', '1') + """ if not self.atts: - elem = self.xml.minidom.Element('group_atts') - elem.setAttribute("cols","1") - elem.setAttribute("border","1") - self.atts = self.master_dom.appendChild(elem) + self.atts = Element('group_atts') + self.atts.set("cols","1") + self.atts.set("border","1") + self.xml.append(self.atts)""" def get_design_panel(self,parent): return group_edit_panel(parent,self) @@ -134,29 +130,25 @@ def on_use(self,evt): return - def gen_html(self, obj, evt): + def gen_html(self, treenode, evt): + node = self.tree.GetPyData(treenode) if self.i not in self.tdatas: self.tdatas[self.i] = '' - self.tdatas[self.i] += "

" + obj.tohtml() + self.tdatas[self.i] += "

" + node.tohtml() self.i += 1 if self.i >= self.cols: self.i = 0 def tohtml(self): - cols = self.atts.getAttribute("cols") - border = self.atts.getAttribute("border") + cols = self.xml.get("cols") + border = self.xml.get("border") self.html_str = "\n" - self.cols = int(cols) self.i = 0 self.tdatas = {} - - child = self.tree.GetFirstChild(self.mytree_node) - if child[0].IsOk(): - self.traverse(child[0], self.gen_html, 0, None) - + self.tree.traverse(self.mytree_node, self.gen_html, recurse=False) for td in self.tdatas: self.html_str += "\n"; self.html_str += "
" - self.html_str += ""+self.master_dom.getAttribute("name") + "" + self.html_str += ""+self.xml.get("name") + "" self.html_str += "
" + self.tdatas[td] + "
" @@ -170,19 +162,19 @@ wx.Panel.__init__(self, parent, -1) self.handler = handler sizer = wx.BoxSizer(wx.VERTICAL) - self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')) + self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.xml.get('name')) } sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) sizer.Add(wx.Size(10,10)) radio_c = wx.RadioBox(self, GROUP_COLS, "Columns", choices=["1","2","3","4"]) - cols = handler.atts.getAttribute("cols") + cols = handler.xml.get("cols") if cols != "": radio_c.SetSelection(int(cols)-1) radio_b = wx.RadioBox(self, GROUP_BOR, "Border", choices=["no","yes"]) - border = handler.atts.getAttribute("border") + border = handler.xml.get("border") if border != "": radio_b.SetSelection(int(border)) @@ -204,16 +196,16 @@ id = evt.GetId() index = evt.GetInt() if id == GROUP_COLS: - self.handler.atts.setAttribute("cols",str(index+1)) + self.handler.xml.set("cols",str(index+1)) elif id == GROUP_BOR: - self.handler.atts.setAttribute("border",str(index)) + self.handler.xml.set("border",str(index)) def on_text(self,evt): id = evt.GetId() if id == P_TITLE: txt = self.text[id].GetValue() if txt != "": - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) @@ -224,8 +216,8 @@ class tabber_handler(container_handler): """ """ - def __init__(self,xml_dom,tree_node): - container_handler.__init__(self,xml_dom,tree_node) + def __init__(self, xml, tree_node): + container_handler.__init__(self, xml, tree_node) def get_design_panel(self,parent): return tabbed_panel(parent,self,1) @@ -239,23 +231,16 @@ orpgTabberWnd.__init__(self, parent, style=FNB.FNB_NO_X_BUTTON) self.handler = handler self.parent = parent - tree = self.handler.tree - child = tree.GetFirstChild(handler.mytree_node) - if child[0].IsOk(): - handler.traverse(child[0], self.pick_panel, 0, mode, False) + handler.tree.traverse(handler.mytree_node, self.pick_panel, mode, False) parent.SetSize(self.GetBestSize()) - def pick_panel(self, obj, mode): - if mode == 1: - panel = obj.get_design_panel(self) - else: - panel = obj.get_use_panel(self) - - name = obj.master_dom.getAttribute("name") - - if panel: - self.AddPage(panel, name, False) + def pick_panel(self, treenode, mode): + node = self.handler.tree.GetPyData(treenode) + if mode == 1: panel = node.get_design_panel(self) + else: panel = node.get_use_panel(self) + name = node.xml.get("name") + if panel: self.AddPage(panel, name, False) ################################# ## Splitter container @@ -264,21 +249,19 @@ class splitter_handler(container_handler): """ """ - def __init__(self,xml_dom,tree_node): - container_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + container_handler.__init__(self,xml,tree_node) def load_children(self): self.atts = None - children = self.master_dom._get_childNodes() - for c in children: - if c._get_tagName() == "splitter_atts": - self.atts = c - else: - self.tree.load_xml(c,self.mytree_node) - if not self.atts: - elem = self.xml.minidom.Element('splitter_atts') - elem.setAttribute("horizontal","0") - self.atts = self.master_dom.appendChild(elem) + for child_xml in self.xml: + if child_xml.tag == "splitter_atts": self.xml.remove(child_xml) #Same here! + elif child_xml: self.tree.load_xml(child_xml,self.mytree_node) + if not self.xml.get('horizontal'): self.xml.set('horizontal', '0') + """if not self.atts: + self.atts = Element('splitter_atts') + self.atts.set("horizontal","0") + self.xml.append(self.atts)""" def get_design_panel(self,parent): return self.build_splitter_wnd(parent, 1) @@ -291,7 +274,7 @@ container_handler.on_drop(self,evt) def build_splitter_wnd(self, parent, mode): - self.split = self.atts.getAttribute("horizontal") + self.split = self.xml.get("horizontal") self.pane = splitter_panel(parent, self) @@ -305,10 +288,7 @@ self.bestSizex = -1 self.bestSizey = -1 - cookie = 0 - (child, cookie) = self.tree.GetFirstChild(self.mytree_node) - if child.IsOk(): - self.traverse(child, self.doSplit, 0, mode, False) + self.tree.traverse(self.mytree_node, self.doSplit, mode, False) self.pane.sizer.Add(self.splitter, 1, wx.EXPAND) @@ -321,11 +301,10 @@ parent.SetSize(self.pane.GetSize()) return self.pane - def doSplit(self, obj, mode): - if mode == 1: - tmp = obj.get_design_panel(self.splitter) - else: - tmp = obj.get_use_panel(self.splitter) + def doSplit(self, treenode, mode): + node = self.tree.GetPyData(treenode) + if mode == 1: tmp = node.get_design_panel(self.splitter) + else: tmp = node.get_use_panel(self.splitter) if self.split == '1': sash = tmp.GetBestSize()[1]+1 @@ -350,7 +329,7 @@ sizer = wx.BoxSizer(wx.VERTICAL) self.hozCheck = wx.CheckBox(self, -1, "Horizontal Split") - hoz = self.handler.atts.getAttribute("horizontal") + hoz = self.handler.xml.get("horizontal") if hoz == '1': self.hozCheck.SetValue(True) @@ -372,6 +351,6 @@ def on_check_box(self,evt): state = self.hozCheck.GetValue() if state: - self.handler.atts.setAttribute("horizontal", "1") + self.handler.xml.set("horizontal", "1") else: - self.handler.atts.setAttribute("horizontal", "0") + self.handler.xml.set("horizontal", "0") diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/core.py --- a/orpg/gametree/nodehandlers/core.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/core.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,6 +29,7 @@ __version__ = "$Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $" from nodehandler_version import NODEHANDLER_VERSION + try: from orpg.orpg_windows import * from orpg.dirpath import dir_struct @@ -36,9 +37,12 @@ import webbrowser from orpg.mapper import map import os + from wx import DisplaySize except: import wx +from xml.etree.ElementTree import ElementTree, Element, tostring, XML +from orpg.tools.orpg_log import debug #html defaults TH_BG = "#E9E9E9" @@ -47,22 +51,33 @@ ########################## class node_handler: """ Base nodehandler with virtual functions and standard implmentations """ - def __init__(self,xml_dom,tree_node): - self.master_dom = xml_dom + def __init__(self,xml,tree_node): + self.xml = xml self.mytree_node = tree_node - self.tree = component.get('tree') - self.frame = component.get('frame') - self.chat = component.get('chat') - self.xml = component.get('xml') #Not used? + self.tree = open_rpg.get_component('tree') + self.frame = open_rpg.get_component('frame') + self.chat = open_rpg.get_component('chat') self.drag = True self.myeditor = None # designing - self.myviewer = None # pretty print + self.myviewer = None # prett print self.mywindow = None # using # call version hook - self.on_version(self.master_dom.getAttribute("version")) + self.on_version(self.xml.get("version")) # set to current version - self.master_dom.setAttribute("version",NODEHANDLER_VERSION) + self.xml.set("version",NODEHANDLER_VERSION) # null events + self.frame_size = None + self.frame_pos = None + try: + frame = self.xml.get("frame") + if len(frame): + (sx,sy,px,py) = [int(value) for value in frame.split(',')] + self.frame_size = (sx, sy) + (maxx, maxy) = DisplaySize() + if px < maxx-80 and py < maxy-50:#if it's off screen ignore the saved pos + self.frame_pos = (px, py) + except: + pass def on_version(self,old_version): ## added version control code here or implement a new on_version in your derived class. @@ -75,32 +90,13 @@ def on_ldclick(self,evt): return 0 - def traverse(self, traverseroot, function, cookie=0, event=None, recursive=True): - """ walk tree control """ - if traverseroot.IsOk(): - # step in subtree if there are items or ... - if self.tree.ItemHasChildren(traverseroot) and recursive: - firstchild, cookie = self.tree.GetFirstChild(traverseroot) - obj = self.tree.GetPyData(firstchild) - function(obj, event) - self.traverse(firstchild, function, cookie, event, recursive) - - # ... loop siblings - obj = self.tree.GetPyData(traverseroot) - function(obj, event) - - child = self.tree.GetNextSibling(traverseroot) - if child.IsOk(): - self.traverse(child, function, cookie, event, recursive) - - def usefulness(self,text): if text=="useful": - self.master_dom.setAttribute('status',"useful") + self.xml.set('status',"useful") elif text=="useless": - self.master_dom.setAttribute('status',"useless") + self.xml.set('status',"useless") elif text=="indifferent": - self.master_dom.setAttribute('status',"indifferent") + self.xml.set('status',"indifferent") def on_design(self,evt): try: @@ -114,13 +110,12 @@ else: return wx.CallAfter(self.myeditor.Layout) - def create_designframe(self): - title = self.master_dom.getAttribute('name') + " Editor" + title = self.xml.get('name') + " Editor" self.myeditor = wx.Frame(None, -1, title) self.myeditor.Freeze() if wx.Platform == '__WXMSW__': - icon = wx.Icon(dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) self.myeditor.SetIcon(icon) del icon @@ -135,15 +130,12 @@ self.myeditor.SetAutoLayout(True) (x, y) = self.myeditor.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 + if x < 400: x = 400 + if y < 400: y = 400 self.myeditor.SetSize((x, y)) self.myeditor.Layout() self.myeditor.Thaw() - return True def on_use(self,evt): @@ -153,6 +145,9 @@ except: del self.mywindow if self.create_useframe(): + self.mywindow.SetSize(self.frame_size) + if self.frame_pos: + self.mywindow.SetPosition(self.frame_pos) self.mywindow.Show() self.mywindow.Raise() else: @@ -160,12 +155,12 @@ wx.CallAfter(self.mywindow.Layout) def create_useframe(self): - caption = self.master_dom.getAttribute('name') + caption = self.xml.get('name', '') self.mywindow = wx.Frame(None, -1, caption) self.mywindow.Freeze() if wx.Platform == '__WXMSW__': - icon = wx.Icon(dir_struct["icon"] + 'note.ico', wx.BITMAP_TYPE_ICO) + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'note.ico', wx.BITMAP_TYPE_ICO) self.mywindow.SetIcon(icon) del icon self.mywindow.panel = self.get_use_panel(self.mywindow) @@ -178,27 +173,31 @@ self.mywindow.SetSizer(sizer) self.mywindow.SetAutoLayout(True) - (x, y) = self.mywindow.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 + if self.frame_size is None: + self.frame_size = self.mywindow.GetSize() + if self.frame_size.x < 400: self.frame_size.x = 400 + if self.frame_size.y < 400: self.frame_size.y = 400 - self.mywindow.SetSize((x, y)) self.mywindow.Layout() self.mywindow.Thaw() + self.mywindow.Bind(wx.EVT_CLOSE, self.close_useframe) + return True - return True + def close_useframe(self, evt): + self.frame_size = self.mywindow.GetSize() + self.frame_pos = self.mywindow.GetPosition() + frame_values = str(self.frame_size.x)+','+str(self.frame_size.y)+','+str(self.frame_pos.x)+','+str(self.frame_pos.y) + self.xml.set("frame", frame_values) + self.mywindow.Destroy() def on_html_view(self,evt): - try: - self.myviewer.Raise() + try: self.myviewer.Raise() except: - caption = self.master_dom.getAttribute('name') + caption = self.xml.get('name') self.myviewer = wx.Frame(None, -1, caption) if wx.Platform == '__WXMSW__': - icon = wx.Icon(dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) + icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) self.myviewer.SetIcon(icon) del icon self.myviewer.panel = self.get_html_panel(self.myviewer) @@ -213,7 +212,7 @@ def on_del(self,evt): print "on del" - def on_new_data(self,xml_dom): + def on_new_data(self,xml): pass def get_scaled_bitmap(self,x,y): @@ -225,53 +224,64 @@ def on_send_to_chat(self,evt): self.chat.ParsePost(self.tohtml(),True,True) - def on_drop(self,evt): - drag_obj = self.tree.drag_obj - if drag_obj == self or self.tree.is_parent_node(self.mytree_node,drag_obj.mytree_node): + def on_drop(self, evt): + drag_obj = self.tree.drag_obj + if drag_obj == self or self.tree.is_parent_node(self.mytree_node, drag_obj.mytree_node): return - #if self.is_my_child(self.mytree_node,drag_obj.mytree_node): - # return - xml_dom = self.tree.drag_obj.delete() - parent = self.master_dom._get_parentNode() - xml_dom = parent.insertBefore(xml_dom,self.master_dom) + drop_xml = self.tree.drag_obj.delete() parent_node = self.tree.GetItemParent(self.mytree_node) prev_sib = self.tree.GetPrevSibling(self.mytree_node) + if parent_node == self.tree.root: parent_xml = self.tree.GetPyData(parent_node) + else: parent_xml = self.tree.GetPyData(parent_node).xml + for i in range(len(parent_xml)): + if parent_xml[i] is self.xml: + parent_xml.insert(i, drop_xml) + break if not prev_sib.IsOk(): prev_sib = parent_node - self.tree.load_xml(xml_dom, parent_node, prev_sib) + self.tree.load_xml(drop_xml, parent_node, prev_sib) + + def get_tree(self): + family = [] + test = treenode + while test != self.tree.root: + test = self.tree.GetItemParent(test) + parent = self.tree.GetItemText(test) + family.append(parent) + return family def toxml(self,pretty=0): - return component.get('xml').toxml(self.master_dom,pretty) + return tostring(self.xml) #toxml(self.master_dom,pretty) def tohtml(self): - return self.master_dom.getAttribute("name") + return self.xml.get("name") def delete(self): """ removes the tree_node and xml_node, and returns the removed xml_node """ - + parent_node = self.tree.GetItemParent(self.mytree_node) + if parent_node == self.tree.root: + parent_xml = self.tree.GetPyData(parent_node) + else: + parent_xml = self.tree.GetPyData(parent_node).xml + parent_xml.remove(self.xml) self.tree.Delete(self.mytree_node) - parent = self.master_dom._get_parentNode() - return parent.removeChild(self.master_dom) + return self.xml def rename(self,name): if len(name): self.tree.SetItemText(self.mytree_node,name) - self.master_dom.setAttribute('name', name) + self.xml.set('name', name) def change_icon(self,icon): - self.master_dom.setAttribute("icon",icon) + self.xml.set("icon",icon) self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon]) self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon], wx.TreeItemIcon_Selected) self.tree.Refresh() def on_save(self,evt): - f = wx.FileDialog(self.tree,"Select a file", dir_struct["user"],"","XML files (*.xml)|*.xml",wx.SAVE) + f = wx.FileDialog(self.tree,"Select a file", orpg.dirpath.dir_struct["user"],"","XML files (*.xml)|*.xml",wx.SAVE) if f.ShowModal() == wx.ID_OK: - type = f.GetFilterIndex() - if f.GetPath()[:len(f.GetPath())-4] != '.xml': file = open(f.GetPath()+'.xml',"w") - else: file = open(f.GetPath(),"w") - file.write(self.toxml(1)) - file.close() + ElementTree(self.xml).write(f.GetPath()) f.Destroy() def get_design_panel(self,parent): @@ -283,17 +293,21 @@ def get_html_panel(self,parent): html_str = ""+self.tohtml()+"" wnd = wx.html.HtmlWindow(parent,-1) - html_str = self.chat.ParseDice(html_str) - wnd.SetPage(html_str) + html_str = self.chat.ParseDice(html_str) + wnd.SetPage(html_str) return wnd def get_size_constraint(self): return 0 def about(self): - html_str = ""+ self.master_dom.getAttribute('class') + html_str = ""+ self.xml.get('class') html_str += " Applet
by Chris Davis
chris@rpgarchive.com" - return html_str + return html_str + + def get_value(self): + return None + P_TITLE = 10 P_BODY = 20 @@ -302,11 +316,9 @@ wx.Panel.__init__(self, parent, -1) self.handler = handler sizer = wx.BoxSizer(wx.VERTICAL) - self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')), - P_BODY : html_text_edit(self,P_BODY,handler.text._get_nodeValue(),self.on_text) + self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.xml.get('name')), + P_BODY : html_text_edit(self,P_BODY,handler.text,self.on_text) } - #P_BODY : wx.TextCtrl(self, P_BODY,handler.text._get_nodeValue(), style=wx.TE_MULTILINE) - sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) sizer.Add(wx.StaticText(self, -1, "Text Body:"), 0, wx.EXPAND) @@ -331,7 +343,7 @@ wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") txt = u_txt if txt != "": - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) elif id == P_BODY: txt = self.text[id].get_text() @@ -357,21 +369,19 @@ """ clones childe node and insert it at top of tree """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) def on_rclick(self,evt): pass def on_ldclick(self,evt): - title = self.master_dom.getAttribute('name') - new_node = self.master_dom._get_firstChild() - new_node = new_node.cloneNode(True) - child = self.tree.master_dom._get_firstChild() - new_node = self.tree.master_dom.insertBefore(new_node,child) - tree_node = self.tree.load_xml(new_node,self.tree.root,self.tree.root) - obj = self.tree.GetPyData(tree_node) + title = self.xml.get('name') + new_xml = XML(tostring(self.xml[0])) + self.tree.root_xml.insert(0, new_xml) + tree_node = self.tree.load_xml(new_xml,self.tree.root,self.tree.root) return 1 + #obj = self.tree.GetPyData(tree_node) #obj.on_design(None) ########################## @@ -384,25 +394,25 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = component.get('frame') + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.file_node = self.xml[0] + self.frame = open_rpg.get_component('frame') def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("name") - self.tree.insert_xml(open(dir_struct["nodes"] + file_name,"r").read()) + file_name = self.file_node.get("name") + self.tree.insert_xml(open(orpg.dirpath.dir_struct["nodes"] + file_name,"r").read()) return 1 def on_design(self,evt): tlist = ['Title','File Name'] - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("name")] + vlist = [self.xml.get("name"), + self.file_node.get("name")] dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") if dlg.ShowModal() == wx.ID_OK: vlist = dlg.get_values() - self.file_node.setAttribute('name', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) + self.file_node.set('name', vlist[1]) + self.xml.set('name', vlist[0]) self.tree.SetItemText(self.mytree_node,vlist[0]) dlg.Destroy() @@ -416,27 +426,27 @@
""" - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = component.get('frame') + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.file_node = self.xml[0] + self.frame = open_rpg.get_component('frame') def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("url") + file_name = self.file_node.get("url") file = urllib.urlopen(file_name) self.tree.insert_xml(file.read()) return 1 def on_design(self,evt): tlist = ['Title','URL'] - print "design filename",self.master_dom.getAttribute('name') - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("url")] + print "design filename",self.xml.get('name') + vlist = [self.xml.get("name"), + self.file_node.get("url")] dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") if dlg.ShowModal() == wx.ID_OK: vlist = dlg.get_values() - self.file_node.setAttribute('url', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) + self.file_node.set('url', vlist[1]) + self.xml.set('name', vlist[0]) self.tree.SetItemText(self.mytree_node,vlist[0]) dlg.Destroy() @@ -448,11 +458,11 @@ """ clones childe node and insert it at top of tree """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.map = component.get('map') - self.mapdata = self.master_dom._get_firstChild() + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.map = open_rpg.get_component('map') + self.mapdata = self.xml[0] def on_ldclick(self,evt): - self.map.new_data(toxml(self.mapdata)) - return 1 + self.map.new_data(tostring(self.mapdata)) + return 1 diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/core.py~ --- a/orpg/gametree/nodehandlers/core.py~ Fri Sep 25 20:47:16 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,462 +0,0 @@ -# Copyright (C) 2000-2001 The OpenRPG Project -# -# openrpg-dev@lists.sourceforge.net -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# -- -# -# File: core.py -# Author: Chris Davis -# Maintainer: -# Version: -# $Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $ -# -# Description: The file contains code for the core nodehanlers -# - -__version__ = "$Id: core.py,v 1.49 2007/12/07 20:39:48 digitalxero Exp $" - -from nodehandler_version import NODEHANDLER_VERSION -try: - from orpg.orpg_windows import * - import orpg.dirpath - from orpg.orpg_xml import * - from orpg.orpgCore import open_rpg - import webbrowser - from orpg.mapper import map - import os -except: - import wx - - - - -#html defaults -TH_BG = "#E9E9E9" -########################## -## base node handler -########################## -class node_handler: - """ Base nodehandler with virtual functions and standard implmentations """ - def __init__(self,xml_dom,tree_node): - self.master_dom = xml_dom - self.mytree_node = tree_node - self.tree = open_rpg.get_component('tree') - self.frame = open_rpg.get_component('frame') - self.chat = open_rpg.get_component('chat') - self.xml = open_rpg.get_component('xml') - self.drag = True - self.myeditor = None # designing - self.myviewer = None # prett print - self.mywindow = None # using - # call version hook - self.on_version(self.master_dom.getAttribute("version")) - # set to current version - self.master_dom.setAttribute("version",NODEHANDLER_VERSION) - # null events - - def on_version(self,old_version): - ## added version control code here or implement a new on_version in your derived class. - ## always call the base class on_version ! - pass - - def on_rclick(self,evt): - self.tree.do_std_menu(evt,self) - - def on_ldclick(self,evt): - return 0 - - def traverse(self, traverseroot, function, cookie=0, event=None, recursive=True): - """ walk tree control """ - if traverseroot.IsOk(): - # step in subtree if there are items or ... - if self.tree.ItemHasChildren(traverseroot) and recursive: - firstchild, cookie = self.tree.GetFirstChild(traverseroot) - obj = self.tree.GetPyData(firstchild) - function(obj, event) - self.traverse(firstchild, function, cookie, event, recursive) - - # ... loop siblings - obj = self.tree.GetPyData(traverseroot) - function(obj, event) - - child = self.tree.GetNextSibling(traverseroot) - if child.IsOk(): - self.traverse(child, function, cookie, event, recursive) - - - def usefulness(self,text): - if text=="useful": - self.master_dom.setAttribute('status',"useful") - elif text=="useless": - self.master_dom.setAttribute('status',"useless") - elif text=="indifferent": - self.master_dom.setAttribute('status',"indifferent") - - def on_design(self,evt): - try: - self.myeditor.Show() - self.myeditor.Raise() - except: - del self.myeditor - if self.create_designframe(): - self.myeditor.Show() - self.myeditor.Raise() - else: - return - wx.CallAfter(self.myeditor.Layout) - - - def create_designframe(self): - title = self.master_dom.getAttribute('name') + " Editor" - self.myeditor = wx.Frame(None, -1, title) - self.myeditor.Freeze() - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) - self.myeditor.SetIcon(icon) - del icon - - self.myeditor.panel = self.get_design_panel(self.myeditor) - if self.myeditor.panel == None: - self.myeditor.Destroy() - return False - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.myeditor.panel, 1, wx.EXPAND) - - self.myeditor.SetSizer(sizer) - self.myeditor.SetAutoLayout(True) - - (x, y) = self.myeditor.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 - - self.myeditor.SetSize((x, y)) - self.myeditor.Layout() - self.myeditor.Thaw() - - return True - - def on_use(self,evt): - try: - self.mywindow.Show() - self.mywindow.Raise() - except: - del self.mywindow - if self.create_useframe(): - self.mywindow.Show() - self.mywindow.Raise() - else: - return - wx.CallAfter(self.mywindow.Layout) - - - def create_useframe(self): - caption = self.master_dom.getAttribute('name') - self.mywindow = wx.Frame(None, -1, caption) - self.mywindow.Freeze() - - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'note.ico', wx.BITMAP_TYPE_ICO) - self.mywindow.SetIcon(icon) - del icon - self.mywindow.panel = self.get_use_panel(self.mywindow) - if self.mywindow.panel == None: - self.mywindow.Destroy() - return False - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.mywindow.panel, 2, wx.EXPAND) - - self.mywindow.SetSizer(sizer) - self.mywindow.SetAutoLayout(True) - - (x, y) = self.mywindow.GetSize() - if x < 400: - x = 400 - if y < 400: - y = 400 - - self.mywindow.SetSize((x, y)) - self.mywindow.Layout() - self.mywindow.Thaw() - - return True - - - def on_html_view(self,evt): - try: - self.myviewer.Raise() - except: - caption = self.master_dom.getAttribute('name') - self.myviewer = wx.Frame(None, -1, caption) - if wx.Platform == '__WXMSW__': - icon = wx.Icon(orpg.dirpath.dir_struct["icon"] + 'grid.ico', wx.BITMAP_TYPE_ICO) - self.myviewer.SetIcon(icon) - del icon - self.myviewer.panel = self.get_html_panel(self.myviewer) - self.myviewer.Show() - - def map_aware(self): - return 0 - - def can_clone(self): - return 1; - - def on_del(self,evt): - print "on del" - - def on_new_data(self,xml_dom): - pass - - def get_scaled_bitmap(self,x,y): - return None - - def on_send_to_map(self,evt): - pass - - def on_send_to_chat(self,evt): - self.chat.ParsePost(self.tohtml(),True,True) - - def on_drop(self,evt): - drag_obj = self.tree.drag_obj - if drag_obj == self or self.tree.is_parent_node(self.mytree_node,drag_obj.mytree_node): - return - #if self.is_my_child(self.mytree_node,drag_obj.mytree_node): - # return - xml_dom = self.tree.drag_obj.delete() - parent = self.master_dom._get_parentNode() - xml_dom = parent.insertBefore(xml_dom,self.master_dom) - parent_node = self.tree.GetItemParent(self.mytree_node) - prev_sib = self.tree.GetPrevSibling(self.mytree_node) - if not prev_sib.IsOk(): - prev_sib = parent_node - self.tree.load_xml(xml_dom, parent_node, prev_sib) - - def toxml(self,pretty=0): - return toxml(self.master_dom,pretty) - - def tohtml(self): - return self.master_dom.getAttribute("name") - - def delete(self): - """ removes the tree_node and xml_node, and returns the removed xml_node """ - - self.tree.Delete(self.mytree_node) - parent = self.master_dom._get_parentNode() - return parent.removeChild(self.master_dom) - - def rename(self,name): - if len(name): - self.tree.SetItemText(self.mytree_node,name) - self.master_dom.setAttribute('name', name) - - def change_icon(self,icon): - self.master_dom.setAttribute("icon",icon) - self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon]) - self.tree.SetItemImage(self.mytree_node, self.tree.icons[icon], wx.TreeItemIcon_Selected) - self.tree.Refresh() - - def on_save(self,evt): - f = wx.FileDialog(self.tree,"Select a file", orpg.dirpath.dir_struct["user"],"","XML files (*.xml)|*.xml",wx.SAVE) - if f.ShowModal() == wx.ID_OK: - type = f.GetFilterIndex() - file = open(f.GetPath(),"w") - file.write(self.toxml(1)) - file.close() - f.Destroy() - - def get_design_panel(self,parent): - return None - - def get_use_panel(self,parent): - return None - - def get_html_panel(self,parent): - html_str = ""+self.tohtml()+"" - wnd = wx.HTMLpanel(parent,-1) - html_str = self.chat.ParseDice(html_str) - wnd.load_text(html_str) - return wnd - - def get_size_constraint(self): - return 0 - - def about(self): - html_str = ""+ self.master_dom.getAttribute('class') - html_str += " Applet
by Chris Davis
chris@rpgarchive.com" - return html_str - -P_TITLE = 10 -P_BODY = 20 -class text_edit_panel(wx.Panel): - def __init__(self, parent, handler): - wx.Panel.__init__(self, parent, -1) - self.handler = handler - sizer = wx.BoxSizer(wx.VERTICAL) - self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')), - P_BODY : html_text_edit(self,P_BODY,handler.text._get_nodeValue(),self.on_text) - } - #P_BODY : wx.TextCtrl(self, P_BODY,handler.text._get_nodeValue(), style=wx.TE_MULTILINE) - - sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) - sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) - sizer.Add(wx.StaticText(self, -1, "Text Body:"), 0, wx.EXPAND) - sizer.Add(self.text[P_BODY], 1, wx.EXPAND) - self.sizer = sizer - self.outline = wx.StaticBox(self,-1,"Text Block") - self.Bind(wx.EVT_TEXT, self.on_text, id=P_TITLE) - - def on_text(self,evt): - id = evt.GetId() - if id == P_TITLE: - txt = self.text[id].GetValue() - # The following block strips out 8-bit characters - u_txt = "" - bad_txt_found = 0 - for c in txt: - if ord(c) < 128: - u_txt += c - else: - bad_txt_found = 1 - if bad_txt_found: - wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") - txt = u_txt - if txt != "": - self.handler.master_dom.setAttribute('name',txt) - self.handler.rename(txt) - elif id == P_BODY: - txt = self.text[id].get_text() - u_txt = "" - bad_txt_found = 0 - for c in txt: - if ord(c) < 128: - u_txt += c - else: - bad_txt_found = 1 - - if bad_txt_found: - wx.MessageBox("Some non 7-bit ASCII characters found and stripped","Warning!") - txt = u_txt - self.handler.text._set_nodeValue(txt) - - - -########################## -## node loader -########################## -class node_loader(node_handler): - """ clones childe node and insert it at top of tree - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - - def on_rclick(self,evt): - pass - - def on_ldclick(self,evt): - title = self.master_dom.getAttribute('name') - new_node = self.master_dom._get_firstChild() - new_node = new_node.cloneNode(True) - child = self.tree.master_dom._get_firstChild() - new_node = self.tree.master_dom.insertBefore(new_node,child) - tree_node = self.tree.load_xml(new_node,self.tree.root,self.tree.root) - obj = self.tree.GetPyData(tree_node) - return 1 - #obj.on_design(None) - -########################## -## file loader -########################## - -class file_loader(node_handler): - """ loads file and insert into game tree - - - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = open_rpg.get_component('frame') - - def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("name") - self.tree.insert_xml(open(orpg.dirpath.dir_struct["nodes"] + file_name,"r").read()) - return 1 - - def on_design(self,evt): - tlist = ['Title','File Name'] - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("name")] - dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") - if dlg.ShowModal() == wx.ID_OK: - vlist = dlg.get_values() - self.file_node.setAttribute('name', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) - self.tree.SetItemText(self.mytree_node,vlist[0]) - dlg.Destroy() - -########################## -## URL loader -########################## - -class url_loader(node_handler): - """ loads file from url and insert into game tree - - - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.file_node = self.master_dom._get_firstChild() - self.frame = open_rpg.get_component('frame') - - def on_ldclick(self,evt): - file_name = self.file_node.getAttribute("url") - file = urllib.urlopen(file_name) - self.tree.insert_xml(file.read()) - return 1 - - def on_design(self,evt): - tlist = ['Title','URL'] - print "design filename",self.master_dom.getAttribute('name') - vlist = [self.master_dom.getAttribute("name"), - self.file_node.getAttribute("url")] - dlg = orpgMultiTextEntry(self.tree.GetParent(),tlist,vlist,"File Loader Edit") - if dlg.ShowModal() == wx.ID_OK: - vlist = dlg.get_values() - self.file_node.setAttribute('url', vlist[1]) - self.master_dom.setAttribute('name', vlist[0]) - self.tree.SetItemText(self.mytree_node,vlist[0]) - dlg.Destroy() - - -########################## -## minature map loader -########################## -class min_map(node_handler): - """ clones childe node and insert it at top of tree - - """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.map = open_rpg.get_component('map') - self.mapdata = self.master_dom._get_firstChild() - - def on_ldclick(self,evt): - self.map.new_data(toxml(self.mapdata)) - return 1 diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/d20.py --- a/orpg/gametree/nodehandlers/d20.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/d20.py Tue Nov 10 14:11:28 2009 -0600 @@ -30,6 +30,9 @@ from core import * import re +from xml.etree.ElementTree import ElementTree, Element, iselement +from xml.etree.ElementTree import fromstring, tostring, parse, XML +from orpg.tools.orpg_log import debug D20_EXPORT = wx.NewId() ############################ @@ -42,12 +45,12 @@ """ should not be used! only a base class! """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) self.load_children() def load_children(self): - children = self.master_dom._get_childNodes() + children = self.xml.getchildren() for c in children: self.tree.load_xml(c,self.mytree_node) @@ -60,9 +63,9 @@ return opt = wx.MessageBox("Add node as child?","Container Node",wx.YES_NO|wx.CANCEL) if opt == wx.YES: - xml_dom = self.tree.drag_obj.delete() - xml_dom = self.master_dom.insertBefore(xml_dom,None) - self.tree.load_xml(xml_dom, self.mytree_node) + xml = self.tree.drag_obj.delete() + xml = self.xml.append(xml,None) + self.tree.load_xml(xml, self.mytree_node) self.tree.Expand(self.mytree_node) elif opt == wx.NO: node_handler.on_drop(self,evt) @@ -70,7 +73,7 @@ def tohtml(self): cookie = 0 html_str = "\n" html_str += "" html_str += '
" - html_str += ""+self.master_dom.getAttribute("name") + "" + html_str += ""+self.xml.get("name") + "" html_str += "
" @@ -110,8 +113,8 @@ """ Node handler for a d20 charactor """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) self.frame = component.get('frame') self.child_handlers = {} self.new_child_handler('howtouse','HowTO use this tool',d20howto,'note') @@ -136,38 +139,32 @@ def on_version(self,old_version): node_handler.on_version(self,old_version) if old_version == "": - tmp = open(orpg.dirpath.dir_struct["nodes"]+"d20character.xml","r") - xml_dom = parseXml_with_dlg(self.tree,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - ## add new nodes + data = parse(orpg.dirpath.dir_struct["nodes"]+"d20character.xml").getroot() for tag in ("howtouse","inventory","powers","divine","pp"): - node_list = xml_dom.getElementsByTagName(tag) - self.master_dom.appendChild(node_list[0]) + self.xml.append(data.find(tag)) ## add new atts - melee_attack = self.master_dom.getElementsByTagName('melee')[0] - melee_attack.setAttribute("second","0") - melee_attack.setAttribute("third","0") - melee_attack.setAttribute("forth","0") - melee_attack.setAttribute("fifth","0") - melee_attack.setAttribute("sixth","0") - range_attack = self.master_dom.getElementsByTagName('ranged')[0] - range_attack.setAttribute("second","0") - range_attack.setAttribute("third","0") - range_attack.setAttribute("forth","0") - range_attack.setAttribute("fifth","0") - range_attack.setAttribute("sixth","0") + melee_attack = self.xml.find('melee') + melee_attack.set("second","0") + melee_attack.set("third","0") + melee_attack.set("forth","0") + melee_attack.set("fifth","0") + melee_attack.set("sixth","0") + range_attack = self.xml.find('ranged') + range_attack.set("second","0") + range_attack.set("third","0") + range_attack.set("forth","0") + range_attack.set("fifth","0") + range_attack.set("sixth","0") - gen_list = self.master_dom.getElementsByTagName('general')[0] + gen_list = self.xml.find('general') for tag in ("currentxp","xptolevel"): - node_list = xml_dom.getElementsByTagName(tag) - gen_list.appendChild(node_list[0]) + gen_list.append(data.find(tag)) ## temp fix - #parent = self.master_dom._get_parentNode() - #old_dom = parent.replaceChild(xml_dom,self.master_dom) - #self.master_dom = xml_dom + #parent = self.xml._get_parentNode() + #old_dom = parent.replaceChild(xml,self.xml) + #self.xml = xml print old_version @@ -185,11 +182,10 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) - handler = handler_class(node_list[0],new_tree_node,self) + handler = handler_class(self.xml.find(tag),new_tree_node,self) tree.SetPyData(new_tree_node,handler) self.child_handlers[tag] = handler @@ -264,7 +260,7 @@ panel = obj.get_design_panel(self) else: panel = obj.get_use_panel(self) - name = obj.master_dom.getAttribute("name") + name = obj.xml.get("name") if panel: self.AddPage(panel,name) @@ -297,14 +293,13 @@ """ Node Handler for skill. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node,parent): + node_handler.__init__(self,xml,tree_node) self.char_hander = parent self.drag = False self.frame = component.get('frame') self.myeditor = None - def on_drop(self,evt): pass @@ -314,7 +309,7 @@ def on_ldclick(self,evt): return if self.myeditor == None or self.myeditor.destroyed: - title = self.master_dom.getAttribute('name') + " Editor" + title = self.xml.get('name') + " Editor" self.myeditor = wx.Frame(self.frame, -1, title) if wx.Platform == '__WXMSW__': icon = wx.Icon(orpg.dirpath.dir_struct["icon"]+'grid.ico', wx.BITMAP_TYPE_ICO) @@ -330,7 +325,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -348,27 +343,26 @@ """ Node Handler for skill. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('skill') self.skills={} - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall('skill'): + name = n.get('name') self.skills[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) def get_mod(self,name): skill = self.skills[name] - stat = skill.getAttribute('stat') - ac = int(skill.getAttribute('armorcheck')) + stat = skill.get('stat') + ac = int(skill.get('armorcheck')) if ac: ac = self.char_hander.child_handlers['ac'].get_check_pen() stat_mod = self.char_hander.child_handlers['abilities'].get_mod(stat) - rank = int(skill.getAttribute('rank')) - misc = int(skill.getAttribute('misc')) + rank = int(skill.get('rank')) + misc = int(skill.get('misc')) total = stat_mod + rank + misc + ac return total @@ -382,8 +376,8 @@ #self.frame.add_panel(wnd) else: skill = self.skills[name]; - untrained = skill.getAttribute('untrained'); - rank = skill.getAttribute('rank'); + untrained = skill.get('untrained'); + rank = skill.get('rank'); if untrained == "0" and rank == "0": txt = '%s Skill Check: Untrained' % (name) else: @@ -404,14 +398,13 @@ def tohtml(self): html_str = """""" - node_list = self.master_dom.getElementsByTagName('skill') - for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - rank = n.getAttribute('rank') + for n in self.xml.findall('skill'): + name = n.get('name') + stat = n.get('stat') + rank = n.get('rank') html_str = html_str + "" stat_mod = str(self.char_hander.child_handlers['abilities'].get_mod(stat)) - misc = n.getAttribute('misc') + misc = n.get('misc') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -426,14 +419,13 @@ """ Node Handler for ability. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) self.abilities = {} - node_list = self.master_dom.getElementsByTagName('stat') tree = self.tree icons = tree.icons - for n in node_list: - name = n.getAttribute('abbr') + for n in self.xml.findall('stat'): + name = n.get('abbr') self.abilities[name] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -454,13 +446,13 @@ chat.ParsePost( txt, True, True ) def get_mod(self,abbr): - score = int(self.abilities[abbr].getAttribute('base')) + score = int(self.abilities[abbr].get('base')) mod = (score - 10) / 2 return mod def set_score(self,abbr,score): if score >= 0: - self.abilities[abbr].setAttribute("base",str(score)) + self.abilities[abbr].set("base",str(score)) def get_design_panel(self,parent): wnd = outline_panel(parent,self,abil_grid,"Abilities") @@ -469,12 +461,11 @@ def tohtml(self): html_str = """
SkillKey RankAbilMiscTotal
"+name+""+stat+""+rank+"
- """ - node_list = self.master_dom.getElementsByTagName('stat') - for n in node_list: - name = n.getAttribute('name') - abbr = n.getAttribute('abbr') - base = n.getAttribute('base') + """ + for n in self.xml.findall('stat'): + name = n.get('name') + abbr = n.get('abbr') + base = n.get('base') mod = str(self.get_mod(abbr)) if mod >= 0: mod1 = "+" @@ -484,29 +475,29 @@ html_str = html_str + "
AbilityBaseModifier
BaseModifier
" return html_str + class d20saves(d20_char_child): """ Node Handler for saves. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('save') self.saves={} - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall('save'): + name = n.get('name') self.saves[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) def get_mod(self,name): save = self.saves[name] - stat = save.getAttribute('stat') + stat = save.get('stat') stat_mod = self.char_hander.child_handlers['abilities'].get_mod(stat) - base = int(save.getAttribute('base')) - miscmod = int(save.getAttribute('miscmod')) - magmod = int(save.getAttribute('magmod')) + base = int(save.get('base')) + miscmod = int(save.get('miscmod')) + magmod = int(save.get('magmod')) total = stat_mod + base + miscmod + magmod return total @@ -537,15 +528,14 @@ html_str = """""" - node_list = self.master_dom.getElementsByTagName('save') - for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - base = n.getAttribute('base') + for n in self.xml.findall('save'): + name = n.get('name') + stat = n.get('stat') + base = n.get('base') html_str = html_str + "" stat_mod = str(self.char_hander.child_handlers['abilities'].get_mod(stat)) - mag = n.getAttribute('magmod') - misc = n.getAttribute('miscmod') + mag = n.get('magmod') + misc = n.get('miscmod') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -561,8 +551,8 @@ """ Node Handler for general information. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,gen_grid,"General Information") @@ -570,12 +560,10 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() html_str = "
Save KeyBaseAbilMagic MiscTotal
"+name+""+stat+""+base+"
General Information
" - for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + ", " + for n in self.xml: + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -583,17 +571,15 @@ self.char_hander.rename(name) def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + return self.xml.find( 'name' ).text class d20classes(d20_char_child): """ Node Handler for classes. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,class_panel,"Classes") @@ -602,17 +588,15 @@ def tohtml(self): html_str = "
Classes
" - n_list = self.master_dom._get_childNodes() - for n in n_list: - html_str += n.getAttribute('name') + " ("+n.getAttribute('level')+"), " + for n in self.xml: + html_str += n.get('name') + " ("+n.get('level')+"), " html_str = html_str[:len(html_str)-2] + "
" return html_str def get_char_lvl( self, attr ): - node_list = self.master_dom.getElementsByTagName('class') - for n in node_list: - lvl = n.getAttribute('level') - type = n.getAttribute('name') + for n in self.xml.findall('class'): + lvl = n.get('level') + type = n.get('name') if attr == "level": return lvl elif attr == "class": @@ -623,8 +607,8 @@ """ Node Handler for classes. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,feat_panel,"Feats") @@ -633,24 +617,23 @@ def tohtml(self): html_str = "
Feats
" - n_list = self.master_dom._get_childNodes() - for n in n_list: - html_str += n.getAttribute('name')+ ", " + for n in self.xml: + html_str += n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str + class d20spells(d20_char_child): """ Node Handler for classes. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) - node_list = self.master_dom.getElementsByTagName( 'spell' ) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) self.spells = {} tree = self.tree icons = self.tree.icons - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall( 'spell' ): + name = n.get('name') self.spells[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -661,10 +644,10 @@ if item == self.mytree_node: d20_char_child.on_ldclick( self, evt ) else: - level = self.spells[ name ].getAttribute( 'level' ) - descr = self.spells[ name ].getAttribute( 'desc' ) - use = self.spells[ name ].getAttribute( 'used' ) - memrz = self.spells[ name ].getAttribute( 'memrz' ) + level = self.spells[ name ].get( 'level' ) + descr = self.spells[ name ].get( 'desc' ) + use = self.spells[ name ].get( 'used' ) + memrz = self.spells[ name ].get( 'memrz' ) cname = self.char_hander.get_char_name() use += '+1' left = eval( '%s - ( %s )' % ( memrz, use ) ) @@ -679,16 +662,15 @@ s = 's' txt = '%s can cast %s %d more time%s' % ( cname, name, left, s ) self.chat.ParsePost( txt, False, False ) - self.spells[ name ].setAttribute( 'used', `eval( use )` ) + self.spells[ name ].set( 'used', `eval( use )` ) def refresh_spells(self): self.spells = {} tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('spell') - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall('spell'): + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) self.spells[name]=n @@ -700,9 +682,8 @@ def tohtml(self): html_str = "
Arcane Spells

" - n_list = self.master_dom._get_childNodes() - for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + for n in self.xml: + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -713,14 +694,13 @@ """ Node Handler for classes. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,component,parent) - node_list = self.master_dom.getElementsByTagName( 'gift' ) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) self.spells = {} tree = self.tree icons = self.tree.icons - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall( 'gift' ): + name = n.get('name') self.spells[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['flask'], icons['flask'] ) tree.SetPyData( new_tree_node, self ) @@ -731,10 +711,10 @@ if item == self.mytree_node: d20_char_child.on_ldclick( self, evt ) else: - level = self.spells[ name ].getAttribute( 'level' ) - descr = self.spells[ name ].getAttribute( 'desc' ) - use = self.spells[ name ].getAttribute( 'used' ) - memrz = self.spells[ name ].getAttribute( 'memrz' ) + level = self.spells[ name ].get( 'level' ) + descr = self.spells[ name ].get( 'desc' ) + use = self.spells[ name ].get( 'used' ) + memrz = self.spells[ name ].get( 'memrz' ) cname = self.char_hander.get_char_name() use += '+1' left = eval( '%s - ( %s )' % ( memrz, use ) ) @@ -749,20 +729,19 @@ s = 's' txt = '%s can cast %s %d more time%s' % ( cname, name, left, s ) self.chat.ParsePost( txt, False, False ) - self.spells[ name ].setAttribute( 'used', `eval( use )` ) + self.spells[ name ].set( 'used', `eval( use )` ) def refresh_spells(self): self.spells = {} tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('gift') - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall('gift'): + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['flask'],icons['flask']) tree.SetPyData(new_tree_node,self) self.spells[name]=n - + def get_design_panel(self,parent): wnd = outline_panel(parent,self,divine_panel,"Spells") wnd.title = "Spells" @@ -770,9 +749,8 @@ def tohtml(self): html_str = "
Divine Spells

" - n_list = self.master_dom._get_childNodes() - for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + for n in self.xml: + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -783,15 +761,14 @@ """ Node Handler for classes. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) - node_list = self.master_dom.getElementsByTagName( 'power' ) - #cpp = self.master_dom.getElementsByTagName( 'pp' ).getAttribute('current1') + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) + #cpp = self.xml.findall( 'pp' ).get('current1') self.powers = {} tree = self.tree icons = self.tree.icons - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall( 'power' ): + name = n.get('name') self.powers[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -802,10 +779,10 @@ if item == self.mytree_node: d20_char_child.on_ldclick( self, evt ) else: - level = self.powers[ name ].getAttribute( 'level' ) - descr = self.powers[ name ].getAttribute( 'desc' ) - use = self.powers[ name ].getAttribute( 'used' ) - points = self.powers[ name ].getAttribute( 'point' ) + level = self.powers[ name ].get( 'level' ) + descr = self.powers[ name ].get( 'desc' ) + use = self.powers[ name ].get( 'used' ) + points = self.powers[ name ].get( 'point' ) cpp = self.char_hander.get_char_pp('current1') fre = self.char_hander.get_char_pp('free') cname = self.char_hander.get_char_name() @@ -837,7 +814,7 @@ if left != 1: s = 's' txt = '%s can use %s %d more time%s' % ( cname, name, numcast, s ) - txt += ' - And has %d more PowerpointsP left' % (left) + txt += ' - And has %d more Powerpoints left' % (left) self.chat.ParsePost( txt, False, False ) self.char_hander.set_char_pp('current1', left) @@ -846,9 +823,8 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('power') - for n in node_list: - name = n.getAttribute('name') + for n in self.xml.findall('power'): + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['questionhead'],icons['questionhead']) tree.SetPyData(new_tree_node,self) self.powers[name]=n @@ -860,9 +836,8 @@ def tohtml(self): html_str = "
Powers

" - n_list = self.master_dom._get_childNodes() - for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + for n in self.xml: + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -879,8 +854,8 @@ """ Node Handler for hit points. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,howto_panel,"How To") @@ -891,8 +866,8 @@ """ Node Handler for general information. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,inventory_grid,"Inventory") @@ -900,12 +875,10 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() html_str = "
General Information
" - for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + "
" + for n in self.xml: + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + "
" html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -913,16 +886,15 @@ self.char_hander.rename(name) def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + return self.xml.find( 'name' ).text + class d20hp(d20_char_child): """ Node Handler for hit points. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,hp_panel,"Hit Points") @@ -931,19 +903,19 @@ def tohtml(self): html_str = "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" html_str += "
Hit Points
Max:"+self.master_dom.getAttribute('max')+"Current:"+self.master_dom.getAttribute('current')+"
Max:"+self.xml.get('max')+"Current:"+self.xml.get('current')+"
" return html_str def get_max_hp( self ): try: - return eval( self.master_dom.getAttribute( 'max' ) ) + return eval( self.xml.get( 'max' ) ) except: return 0 def get_current_hp( self ): try: - return eval( self.master_dom.getAttribute( 'current' ) ) + return eval( self.xml.get( 'current' ) ) except: return 0 @@ -951,8 +923,8 @@ """ Node Handler for power points. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_design_panel(self,parent): wnd = outline_panel(parent,self,pp_panel,"Power Points") @@ -961,30 +933,30 @@ def tohtml(self): html_str = "" - html_str += "" - html_str += "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" + html_str += "" + html_str += "" html_str += "
Power Points
Max:"+self.master_dom.getAttribute('max1')+"Current:"+self.master_dom.getAttribute('current1')+"Current Talents/day:"+self.master_dom.getAttribute('free')+"Max Talents/day:"+self.master_dom.getAttribute('maxfree')+"
Max:"+self.xml.get('max1')+"Current:"+self.xml.get('current1')+"Current Talents/day:"+self.xml.get('free')+"Max Talents/day:"+self.xml.get('maxfree')+"
" return html_str def get_char_pp( self, attr ): - pp = self.master_dom.getAttribute(attr) + pp = self.xml.get(attr) return pp def set_char_pp( self, attr, evl ): - pp = self.master_dom.setAttribute(attr, evl) + pp = self.xml.set(attr, evl) return pp class d20attacks(d20_char_child): """ Node Handler for attacks. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) - node_list = self.master_dom.getElementsByTagName('melee') + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) + node_list = self.xml.findall('melee') self.melee = node_list[0] - node_list = self.master_dom.getElementsByTagName('ranged') + node_list = self.xml.findall('ranged') self.ranged = node_list[0] self.refresh_weapons() @@ -993,22 +965,22 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('weapon') + node_list = self.xml.findall('weapon') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['sword'],icons['sword']) tree.SetPyData(new_tree_node,self) self.weapons[name]=n def get_attack_data(self): temp = self.melee - base = int(temp.getAttribute('base')) - base2 = int(temp.getAttribute('second')) - base3 = int(temp.getAttribute('third')) - base4 = int(temp.getAttribute('forth')) - base5 = int(temp.getAttribute('fifth')) - base6 = int(temp.getAttribute('sixth')) - misc = int(temp.getAttribute('misc')) + base = int(temp.get('base')) + base2 = int(temp.get('second')) + base3 = int(temp.get('third')) + base4 = int(temp.get('forth')) + base5 = int(temp.get('fifth')) + base6 = int(temp.get('sixth')) + misc = int(temp.get('misc')) return (base, base2, base3, base4, base5, base6, misc) # Replace any 'S' and 'D' in an attack modifier and damage modifier with the @@ -1038,10 +1010,10 @@ #self.frame.add_panel(self.get_design_panel(self.frame.note)) else: # Weapon/attack specific attack modifier (e.g. "S+1" for a longsword+1). - attack_mod_str = self.weapons[name].getAttribute('mod') + attack_mod_str = self.weapons[name].get('mod') # Weapon/attack specific damage (e.g. "1d8+S+1" for a longsword+1). - damage_str = self.weapons[name].getAttribute('damage') + damage_str = self.weapons[name].get('damage') (num_damage_dice, damage_die, damage_mods, extra_damage) = self.decompose_damage( damage_str ) # Replace any 'S' and 'D' in attack_mod_str and damage_str with the @@ -1052,10 +1024,10 @@ bab_attributes = ['base', 'second', 'third', 'forth', 'fifth', 'sixth'] bab = [] for b in bab_attributes: - bab.append( int(self.melee.getAttribute( b )) ) + bab.append( int(self.melee.get( b )) ) # Misc. attack modifier to be applied to *all* attacks. - misc_mod = int(self.melee.getAttribute( 'misc' )); + misc_mod = int(self.melee.get( 'misc' )); # Attack modifier (except BAB) attack_mod = misc_mod + eval( attack_mod_str ) @@ -1067,7 +1039,7 @@ damage_mod = 0 # Determine critical hit range and multiplier. - critical_str = self.weapons[name].getAttribute( 'critical' ) + critical_str = self.weapons[name].get( 'critical' ) m = re.match( r"(((?P\d+)-)?\d+/)?x(?P\d+)", critical_str ) crit_min = m.group( 'min' ) crit_mult = m.group( 'mult' ) @@ -1120,32 +1092,32 @@ html_str += "
Misc. Attack Bonus%+d
' % babs[6] - n_list = self.master_dom.getElementsByTagName('weapon') + n_list = self.xml.findall('weapon') for n in n_list: - (attack_mod, damage_mod) = self.process_mod_codes( n.getAttribute( 'mod' ), \ - n.getAttribute( 'damage' ) ) + (attack_mod, damage_mod) = self.process_mod_codes( n.get( 'mod' ), \ + n.get( 'damage' ) ) attack_mod = eval( attack_mod ) html_str += """

""" \ + "' % (attack_mod, damage_mod) html_str += """""" \ + "
Weapon AttackDamage
" \ - + n.getAttribute('name') + "" + + n.get('name') + "" html_str += '%+d%s
CriticalRangeWeight TypeSize
" \ - + n.getAttribute( 'critical' ) + "" \ - + n.getAttribute( 'range' ) + "" \ - + n.getAttribute( 'weight' )+"" \ - + n.getAttribute( 'type' ) + "" \ - + n.getAttribute( 'size' ) + "
" + + n.get( 'critical' ) + "" \ + + n.get( 'range' ) + "" \ + + n.get( 'weight' )+"" \ + + n.get( 'type' ) + "" \ + + n.get( 'size' ) + "" return html_str class d20armor(d20_char_child): """ Node Handler for ac. This handler will be created by d20char_handler. """ - def __init__(self,xml_dom,tree_node,parent): - d20_char_child.__init__(self,xml_dom,tree_node,parent) + def __init__(self,xml,tree_node,parent): + d20_char_child.__init__(self,xml,tree_node,parent) def get_spell_failure(self): return self.get_total('spellfailure') @@ -1168,19 +1140,19 @@ return ac_total def get_max_dex(self): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') dex = 10 for a in armor_list: - temp = int(a.getAttribute("maxdex")) + temp = int(a.get("maxdex")) if temp < dex: dex = temp return dex def get_total(self,attr): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') total = 0 for a in armor_list: - total += int(a.getAttribute(attr)) + total += int(a.get(attr)) return total def get_design_panel(self,parent): @@ -1196,20 +1168,19 @@ html_str += ""+str(self.get_spell_failure())+"" html_str += ""+str(self.get_max_dex())+"" html_str += ""+str(self.get_total_weight())+"" - n_list = self.master_dom._get_childNodes() - for n in n_list: + for n in self.xml: html_str += """

""" - html_str += "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" + html_str += "" html_str += """""" - html_str += "" - html_str += "" - html_str += "" - html_str += "" - html_str += "
Armor TypeBonus
"+n.getAttribute('name')+""+n.getAttribute('type')+""+n.getAttribute('bonus')+"
"+n.get('name')+""+n.get('type')+""+n.get('bonus')+"
Check PenaltySpell Failure Max DexSpeedWeight
"+n.getAttribute('checkpenalty')+""+n.getAttribute('spellfailure')+""+n.getAttribute('maxdex')+""+n.getAttribute('speed')+""+n.getAttribute('weight')+"
" + html_str += ""+n.get('checkpenalty')+"" + html_str += ""+n.get('spellfailure')+"" + html_str += ""+n.get('maxdex')+"" + html_str += ""+n.get('speed')+"" + html_str += ""+n.get('weight')+"" return html_str @@ -1242,7 +1213,7 @@ class char_panel(wx.ScrolledWindow): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'TWO') + pname = handler.xml.set("name", 'TWO') wx.ScrolledWindow.__init__(self, parent, -1,style=wx.VSCROLL | wx.SUNKEN_BORDER ) self.height = 1200 self.SetScrollbars(10, 10,80, self.height/10) @@ -1297,14 +1268,12 @@ class howto_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'How To') + pname = handler.xml.set("name", 'How To') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom - n_list = self.master_dom._get_childNodes() - for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - self.sizer.AddMany([ (wx.StaticText(self, -1, t_node._get_nodeValue()), 0, wx.ALIGN_CENTER_VERTICAL), - ]) + self.sizer.AddMany([ (wx.StaticText(self, -1, + handler.xml.find('howto').text), + 0, wx.ALIGN_CENTER_VERTICAL), + ]) self.sizer.AddGrowableCol(1) self.SetSizer(self.sizer) @@ -1315,13 +1284,13 @@ class hp_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'HitPoints') + pname = handler.xml.set("name", 'HitPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "HP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, HP_CUR, self.master_dom.getAttribute('current')), 0, wx.EXPAND), + (wx.TextCtrl(self, HP_CUR, self.xml.get('current')), 0, wx.EXPAND), (wx.StaticText(self, -1, "HP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, HP_MAX, self.master_dom.getAttribute('max')), 0, wx.EXPAND), + (wx.TextCtrl(self, HP_MAX, self.xml.get('max')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) self.SetSizer(self.sizer) @@ -1332,9 +1301,9 @@ def on_text(self,evt): id = evt.GetId() if id == HP_CUR: - self.master_dom.setAttribute('current',evt.GetString()) + self.xml.set('current',evt.GetString()) elif id == HP_MAX: - self.master_dom.setAttribute('max',evt.GetString()) + self.xml.set('max',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1348,18 +1317,18 @@ class pp_panel(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'PowerPoints') + pname = handler.xml.set("name", 'PowerPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "PP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, PP_CUR, self.master_dom.getAttribute('current1')), 0, wx.EXPAND), + (wx.TextCtrl(self, PP_CUR, self.xml.get('current1')), 0, wx.EXPAND), (wx.StaticText(self, -1, "PP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, PP_MAX, self.master_dom.getAttribute('max1')), 0, wx.EXPAND), + (wx.TextCtrl(self, PP_MAX, self.xml.get('max1')), 0, wx.EXPAND), (wx.StaticText(self, -1, "Current Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, PP_FRE, self.master_dom.getAttribute('free')), 0, wx.EXPAND), + (wx.TextCtrl(self, PP_FRE, self.xml.get('free')), 0, wx.EXPAND), (wx.StaticText(self, -1, "Max Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, PP_MFRE, self.master_dom.getAttribute('maxfree')), 0, wx.EXPAND), + (wx.TextCtrl(self, PP_MFRE, self.xml.get('maxfree')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) self.SetSizer(self.sizer) @@ -1372,13 +1341,13 @@ def on_text(self,evt): id = evt.GetId() if id == PP_CUR: - self.master_dom.setAttribute('current1',evt.GetString()) + self.xml.set('current1',evt.GetString()) elif id == PP_MAX: - self.master_dom.setAttribute('max1',evt.GetString()) + self.xml.set('max1',evt.GetString()) elif id == PP_FRE: - self.master_dom.setAttribute('free',evt.GetString()) + self.xml.set('free',evt.GetString()) elif id == PP_MFRE: - self.master_dom.setAttribute('maxfree',evt.GetString()) + self.xml.set('maxfree',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1388,12 +1357,12 @@ class gen_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'General') + pname = handler.xml.set("name", 'General') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -1406,15 +1375,13 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + self.n_list[row].text = value if row==0: self.handler.on_name_change(value) def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) + self.SetCellValue(rowi,0,self.n_list[rowi].tag) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + self.SetCellValue(rowi,1,self.n_list[rowi].text) def on_size(self,evt): (w,h) = self.GetClientSizeTuple() @@ -1428,12 +1395,12 @@ class inventory_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Money and Inventory') + pname = handler.xml.set("name", 'Money and Inventory') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -1446,15 +1413,13 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + self.n_list[row].text = value if row==0: self.handler.on_name_change(value) def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) + self.SetCellValue(rowi,0,self.n_list[rowi].tag) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + self.SetCellValue(rowi,1,self.n_list[rowi].text) def on_size(self,evt): (w,h) = self.GetClientSizeTuple() @@ -1468,12 +1433,12 @@ class abil_grid(wx.grid.Grid): """grid for abilities""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Stats') + pname = handler.xml.set("name", 'Stats') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - stats = handler.master_dom.getElementsByTagName('stat') + stats = handler.xml.findall('stat') self.CreateGrid(len(stats),3) self.SetRowLabelSize(0) col_names = ['Ability','Score','Modifier'] @@ -1491,7 +1456,7 @@ value = self.GetCellValue(row,col) try: int(value) - self.stats[row].setAttribute('base',value) + self.stats[row].set('base',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -1500,11 +1465,11 @@ def refresh_row(self,rowi): s = self.stats[rowi] - name = s.getAttribute('name') - abbr = s.getAttribute('abbr') + name = s.get('name') + abbr = s.get('abbr') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,s.getAttribute('base')) + self.SetCellValue(rowi,1,s.get('base')) self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr))) self.SetReadOnly(rowi,2) @@ -1526,12 +1491,12 @@ class save_grid(wx.grid.Grid): """grid for saves""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Saves') + pname = handler.xml.set("name", 'Saves') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - saves = handler.master_dom.getElementsByTagName('save') + saves = handler.xml.findall('save') self.stats = handler.char_hander.child_handlers['abilities'] self.CreateGrid(len(saves),7) self.SetRowLabelSize(0) @@ -1550,28 +1515,28 @@ try: int(value) if col == 2: - self.saves[row].setAttribute('base',value) + self.saves[row].set('base',value) elif col ==4: - self.saves[row].setAttribute('magmod',value) + self.saves[row].set('magmod',value) elif col ==4: - self.saves[row].setAttribute('miscmod',value) + self.saves[row].set('miscmod',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") def refresh_row(self,rowi): s = self.saves[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('base')) + self.SetCellValue(rowi,2,s.get('base')) self.SetCellValue(rowi,3,str(self.stats.get_mod(stat))) self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('magmod')) - self.SetCellValue(rowi,5,s.getAttribute('miscmod')) + self.SetCellValue(rowi,4,s.get('magmod')) + self.SetCellValue(rowi,5,s.get('miscmod')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,6,mod) self.SetReadOnly(rowi,6) @@ -1594,12 +1559,12 @@ class skill_grid(wx.grid.Grid): """ panel for skills """ def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Skills') + pname = handler.xml.set("name", 'Skills') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - skills = handler.master_dom.getElementsByTagName('skill') + skills = handler.xml.findall('skill') self.stats = handler.char_hander.child_handlers['abilities'] self.CreateGrid(len(skills),6) self.SetRowLabelSize(0) @@ -1618,25 +1583,25 @@ try: int(value) if col == 2: - self.skills[row].setAttribute('rank',value) + self.skills[row].set('rank',value) elif col ==4: - self.skills[row].setAttribute('misc',value) + self.skills[row].set('misc',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") def refresh_row(self,rowi): s = self.skills[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('rank')) + self.SetCellValue(rowi,2,s.get('rank')) self.SetCellValue(rowi,3,str(self.stats.get_mod(stat))) self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('misc')) + self.SetCellValue(rowi,4,s.get('misc')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,5,mod) self.SetReadOnly(rowi,5) @@ -1659,7 +1624,7 @@ class feat_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Feats') + pname = handler.xml.set("name", 'Feats') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -1671,9 +1636,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),2,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Feat") @@ -1685,8 +1650,8 @@ def refresh_row(self,i): feat = self.n_list[i] - name = feat.getAttribute('name') - type = feat.getAttribute('type') + name = feat.get('name') + type = feat.get('type') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,type) @@ -1697,28 +1662,25 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.removeChild(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20feats.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('feat') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20feats.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('feat') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) + self.n_list = self.xml.findall('feat') self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() - def on_size(self,event): s = self.GetClientSizeTuple() self.grid.SetDimensions(0,0,s[0],s[1]-25) @@ -1731,7 +1693,7 @@ class spell_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Arcane Spells') + pname = handler.xml.set("name", 'Arcane Spells') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.handler = handler @@ -1748,9 +1710,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),4,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"No.") @@ -1766,15 +1728,14 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('memrz',value) - + self.n_list[row].set('memrz',value) def refresh_row(self,i): spell = self.n_list[i] - memrz = spell.getAttribute('memrz') - name = spell.getAttribute('name') - type = spell.getAttribute('desc') - level = spell.getAttribute('level') + memrz = spell.get('memrz') + name = spell.get('name') + type = spell.get('desc') + level = spell.get('level') self.grid.SetCellValue(i,0,memrz) self.grid.SetCellValue(i,2,name) self.grid.SetReadOnly(i,2) @@ -1788,42 +1749,39 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.removeChild(self.n_list[i]) self.handler.refresh_spells() def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20spells.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('spell') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20spells.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('spell') opts = [] # lvl = int(self.handler.get_char_lvl('level')) # castlvl = lvl / 2 for f in f_list: - opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) - # spelllvl = f.getAttribute('level') + opts.append("(" + f.get('level') + ")" + f.get('name')) + # spelllvl = f.get('level') # if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) # else: # if eval('%d >= %s' %(castlvl, spelllvl)): - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('spell') + self.n_list = self.xml.findall('spell') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_spells() dlg.Destroy() def on_refresh_spells( self, evt ): - f_list = self.master_dom.getElementsByTagName('spell') + f_list = self.xml.findall('spell') for spell in f_list: - spell.setAttribute( 'used', '0' ) + spell.set( 'used', '0' ) def on_size(self,event): s = self.GetClientSizeTuple() @@ -1845,7 +1803,7 @@ class divine_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Divine Spells') + pname = handler.xml.set("name", 'Divine Spells') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.handler = handler @@ -1862,9 +1820,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),4,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"No.") @@ -1880,15 +1838,15 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('memrz',value) + self.n_list[row].set('memrz',value) def refresh_row(self,i): spell = self.n_list[i] - memrz = spell.getAttribute('memrz') - name = spell.getAttribute('name') - type = spell.getAttribute('desc') - level = spell.getAttribute('level') + memrz = spell.get('memrz') + name = spell.get('name') + type = spell.get('desc') + level = spell.get('level') self.grid.SetCellValue(i,0,memrz) self.grid.SetCellValue(i,2,name) self.grid.SetReadOnly(i,2) @@ -1902,42 +1860,39 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) self.handler.refresh_spells() def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20divine.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('gift') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20divine.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('gift') opts = [] # lvl = int(self.handler.get_char_lvl('level')) # castlvl = lvl / 2 for f in f_list: - opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) - # spelllvl = f.getAttribute('level') + opts.append("(" + f.get('level') + ")" + f.get('name')) + # spelllvl = f.get('level') # if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) # else: # if eval('%d >= %s' %(castlvl, spelllvl)): - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('gift') + self.n_list = self.xml.findall('gift') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_spells() dlg.Destroy() def on_refresh_spells( self, evt ): - f_list = self.master_dom.getElementsByTagName('gift') + f_list = self.xml.findall('gift') for spell in f_list: - spell.setAttribute( 'used', '0' ) + spell.set( 'used', '0' ) def on_size(self,event): s = self.GetClientSizeTuple() @@ -1960,7 +1915,7 @@ class power_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Pionic Powers') + pname = handler.xml.set("name", 'Pionic Powers') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.handler = handler @@ -1976,9 +1931,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.Bind(wx.EVT_BUTTON, self.on_refresh_powers, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),5,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"PP") @@ -1997,16 +1952,16 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) """if col == 0: - self.n_list[row].setAttribute('memrz',value)""" + self.n_list[row].set('memrz',value)""" def refresh_row(self,i): power = self.n_list[i] - point = power.getAttribute('point') - name = power.getAttribute('name') - type = power.getAttribute('desc') - test = power.getAttribute('test') - level = power.getAttribute('level') + point = power.get('point') + name = power.get('name') + type = power.get('desc') + test = power.get('test') + level = power.get('level') self.grid.SetCellValue(i,0,point) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -2023,34 +1978,31 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.removeChild(self.n_list[i]) self.handler.refresh_powers() def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20powers.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('power') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20powers.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('power') opts = [] # lvl = int(self.handler.get_char_lvl('level')) # castlvl = lvl / 2 for f in f_list: - opts.append("(" + f.getAttribute('level') + ") - " + f.getAttribute('name') + " - " + f.getAttribute('test')) - # spelllvl = f.getAttribute('level') + opts.append("(" + f.get('level') + ") - " + f.get('name') + " - " + f.get('test')) + # spelllvl = f.get('level') # if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ") - " + f.getAttribute('name') + " - " + f.getAttribute('test')) + # opts.append("(" + f.get('level') + ") - " + f.get('name') + " - " + f.get('test')) # else: # if eval('%d >= %s' %(castlvl, spelllvl)): - # opts.append("(" + f.getAttribute('level') + ") - " + f.getAttribute('name') + " - " + f.getAttribute('test')) + # opts.append("(" + f.get('level') + ") - " + f.get('name') + " - " + f.get('test')) dlg = wx.SingleChoiceDialog(self,'Choose Power','Powers',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('power') + self.n_list = self.xml.findall('power') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_powers() dlg.Destroy() @@ -2060,8 +2012,8 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_weapons() @@ -2093,7 +2045,7 @@ class attack_grid(wx.grid.Grid): """grid for attacks""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.parent = parent self.handler = handler @@ -2117,7 +2069,7 @@ value = "0" self.SetCellValue( row, col, value ) attribs = ['base','second','third','forth','fifth','sixth','misc'] - self.babs.setAttribute( attribs[col], value ) + self.babs.set( attribs[col], value ) self.parent.refresh_data() def refresh_data(self): @@ -2136,7 +2088,7 @@ class weapon_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Weapons') + pname = handler.xml.set("name", 'Weapons') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -2148,9 +2100,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom.getElementsByTagName('weapon') + n_list = handler.xml.findall('weapon') self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.handler = handler self.grid.CreateGrid(len(n_list),8,1) self.grid.SetRowLabelSize(0) @@ -2166,51 +2118,48 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('name',value) + self.n_list[row].set('name',value) self.handler.refresh_weapons(); else: - self.n_list[row].setAttribute(self.grid.GetColLabelValue(col),value) + self.n_list[row].set(self.grid.GetColLabelValue(col),value) def refresh_row(self,i): n = self.n_list[i] - name = n.getAttribute('name') - mod = n.getAttribute('mod') - ran = n.getAttribute('range') + name = n.get('name') + mod = n.get('mod') + ran = n.get('range') self.grid.SetCellValue(i,0,name) - self.grid.SetCellValue(i,1,n.getAttribute('damage')) + self.grid.SetCellValue(i,1,n.get('damage')) self.grid.SetCellValue(i,2,mod) - self.grid.SetCellValue(i,3,n.getAttribute('critical')) - self.grid.SetCellValue(i,4,n.getAttribute('type')) - self.grid.SetCellValue(i,5,n.getAttribute('weight')) + self.grid.SetCellValue(i,3,n.get('critical')) + self.grid.SetCellValue(i,4,n.get('type')) + self.grid.SetCellValue(i,5,n.get('weight')) self.grid.SetCellValue(i,6,ran) - self.grid.SetCellValue(i,7,n.getAttribute('size') ) + self.grid.SetCellValue(i,7,n.get('size') ) def on_remove(self,evt): rows = self.grid.GetNumberRows() for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_weapons() def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20weapons.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('weapon') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20weapons.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('weapon') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.n_list = self.xml.findall('weapon') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_weapons() dlg.Destroy() @@ -2233,7 +2182,7 @@ class attack_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') wx.Panel.__init__(self, parent, -1) self.a_grid = attack_grid(self, handler) @@ -2255,7 +2204,7 @@ class ac_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Armor') + pname = handler.xml.set("name", 'Armor') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -2267,8 +2216,8 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.master_dom = handler.master_dom - n_list = handler.master_dom._get_childNodes() + self.xml = handler.xml + n_list = handler.xml[:] self.n_list = n_list col_names = ['Armor','bonus','maxdex','cp','sf','weight','speed','type'] self.grid.CreateGrid(len(n_list),len(col_names),1) @@ -2288,43 +2237,42 @@ if col >= 1 and col <= 5: try: int(value) - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) except: self.grid.SetCellValue(row,col,"0") else: - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) def refresh_row(self,i): n = self.n_list[i] for y in range(len(self.atts)): - self.grid.SetCellValue(i,y,n.getAttribute(self.atts[y])) + self.grid.SetCellValue(i,y,n.get(self.atts[y])) def on_remove(self,evt): rows = self.grid.GetNumberRows() for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20armor.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('armor') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20armor.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('armor') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) + self.n_list = self.xml.findall('armor') self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() + def on_size(self,event): s = self.GetClientSizeTuple() self.grid.SetDimensions(0,0,s[0],s[1]-25) @@ -2339,7 +2287,7 @@ class class_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Class') + pname = handler.xml.set("name", 'Class') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) sizer = wx.BoxSizer(wx.HORIZONTAL) @@ -2353,9 +2301,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml[:] self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),2,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Class") @@ -2370,15 +2318,14 @@ value = self.grid.GetCellValue(row,col) try: int(value) - self.n_list[row].setAttribute('level',value) + self.n_list[row].set('level',value) except: self.grid.SetCellValue(row,col,"1") - def refresh_row(self,i): n = self.n_list[i] - name = n.getAttribute('name') - level = n.getAttribute('level') + name = n.get('name') + level = n.get('level') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -2389,28 +2336,25 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(orpg.dirpath.dir_struct["d20"]+"d20classes.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('class') + tree = parse(orpg.dirpath.dir_struct["d20"]+"d20classes.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('class') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(XML(tostring(f_list[i]))) self.grid.AppendRows(1) + self.n_list = self.xml.findall('class') self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() - def on_size(self,event): s = self.GetClientSizeTuple() self.grid.SetDimensions(0,0,s[0],s[1]-25) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/dnd35.py --- a/orpg/gametree/nodehandlers/dnd35.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/dnd35.py Tue Nov 10 14:11:28 2009 -0600 @@ -5,6 +5,7 @@ from string import * #a 1.6003 from inspect import * #a 1.9001 from orpg.dirpath import dir_struct +from xml.etree.ElementTree import parse dnd35_EXPORT = wx.NewId() ############Global Stuff############## @@ -21,12 +22,12 @@ return root #a 1.6 convinience function added safeGetAttr -def safeGetAttr(node,lable,defRetV=None): - cna=node.attributes - for i2 in range(len(cna)): - if cna.item(i2).name == lable: - return cna.item(i2).value - #retV=node.getAttribute(lable) # getAttribute does not distingish between +def safeGetAttr(node, label, defRetV=None): + cna=node.attrib + for key in cna: + if key == label: + return cna[key] + #retV=node.get(lable) # get does not distingish between # the attribute not being present vs it having a value of "" # This is bad for this routine, thus not used. return defRetV @@ -58,7 +59,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -137,7 +138,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -167,12 +168,11 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
General Information
" for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + ", " + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -183,14 +183,13 @@ def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + node = self.xml.findall( 'name' )[0] + return node.text class gen_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'General') + pname = handler.xml.set("name", 'General') self.hparent = handler #a 1.5002 allow ability to run up tree, needed # a 1.5002 parent is functional parent, not invoking parent. @@ -199,7 +198,7 @@ #self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -212,18 +211,16 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + t_node = self.n_list[row] + t_node.text = value if row==0: self.handler.on_name_change(value) #self.AutoSizeColumn(1) - def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) - self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + def refresh_row(self, rowi): + self.SetCellValue(rowi, 0, self.n_list[rowi].tag) + self.SetReadOnly(rowi, 0) + self.SetCellValue(rowi, 1, self.n_list[rowi].text) self.AutoSizeColumn(1) class dnd35inventory(dnd35_char_child): @@ -242,12 +239,11 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
Inventory
" for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + "
" + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + "
" html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -255,7 +251,7 @@ def __init__(self, parent, handler): wx.Panel.__init__(self, parent, wx.ID_ANY) - self.n_list = handler.master_dom._get_childNodes() + self.n_list = handler.xml.getchildren() self.autosize = False self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, "Inventroy"), wx.VERTICAL) @@ -317,13 +313,11 @@ for node in self.n_list: if node._get_tagName() == nodeName: - t_node = component.get('xml').safe_get_text_node(node) - t_node._set_nodeValue(value) + node.text = value def saveMoney(self, row, col): value = self.grid.GetCellValue(row, col) - t_node = component.get('xml').safe_get_text_node(self.n_list[row]) - t_node._set_nodeValue(value) + self.n_list[row].text = value def on_cell_change(self, evt): row = evt.GetRow() @@ -334,9 +328,8 @@ def refresh_row(self, row): - t_node = component.get('xml').safe_get_text_node(self.n_list[row]) - tagname = self.n_list[row]._get_tagName() - value = t_node._get_nodeValue() + tagname = self.n_list[row].tag + value = self.n_list[row].text if tagname == 'Gear': self.fillTextNode(tagname, value) elif tagname == 'Magic': @@ -367,7 +360,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -405,7 +398,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -429,12 +422,12 @@ self.root.abilities = self #a 1.5002 let other classes find me. self.abilities = {} - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') tree = self.tree icons = tree.icons for n in node_list: - name = n.getAttribute('abbr') + name = n.get('abbr') self.abilities[name] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -457,14 +450,14 @@ chat.ParsePost( txt, True, True ) def get_mod(self,abbr): - score = int(self.abilities[abbr].getAttribute('base')) + score = int(self.abilities[abbr].get('base')) mod = (score - 10) / 2 mod = int(mod) return mod def set_score(self,abbr,score): if score >= 0: - self.abilities[abbr].setAttribute("base",str(score)) + self.abilities[abbr].set("base",str(score)) def get_design_panel(self,parent): wnd = outline_panel(parent,self,abil_grid,"Abilities") @@ -474,11 +467,11 @@ def tohtml(self): html_str = """""" - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') for n in node_list: - name = n.getAttribute('name') - abbr = n.getAttribute('abbr') - base = n.getAttribute('base') + name = n.get('name') + abbr = n.get('abbr') + base = n.get('base') mod = str(self.get_mod(abbr)) if int(mod) >= 0: #m 1.6013 added "int(" and ")" mod1 = "+" @@ -492,7 +485,7 @@ class abil_grid(wx.grid.Grid): """grid for abilities""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Stats') + pname = handler.xml.set("name", 'Stats') self.hparent = handler #a 1.5002 allow ability to run up tree. self.root = getRoot(self) #a 1.5002 in this case, we need the functional parent, not the invoking parent. @@ -501,7 +494,7 @@ self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - stats = handler.master_dom.getElementsByTagName('stat') + stats = handler.xml.findall('stat') self.CreateGrid(len(stats),3) self.SetRowLabelSize(0) col_names = ['Ability','Score','Modifier'] @@ -520,7 +513,7 @@ #print value try: int(value) - self.stats[row].setAttribute('base',value) + self.stats[row].set('base',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -532,11 +525,11 @@ def refresh_row(self,rowi): s = self.stats[rowi] - name = s.getAttribute('name') - abbr = s.getAttribute('abbr') + name = s.get('name') + abbr = s.get('abbr') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,s.getAttribute('base')) + self.SetCellValue(rowi,1,s.get('base')) self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr))) self.SetReadOnly(rowi,2) #if self.root.saves.saveGrid: #a 1.6018 d 1.9002 whole if clause @@ -578,20 +571,20 @@ def tohtml(self): html_str = "
Ability BaseModifier
Classes
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name') + " ("+n.getAttribute('level')+"), " + html_str += n.get('name') + " ("+n.get('level')+"), " html_str = html_str[:len(html_str)-2] + "
" return html_str def get_char_lvl( self, attr ): - node_list = self.master_dom.getElementsByTagName('class') + node_list = self.xml.findall('class') # print "eclasses - get_char_lvl node_list",node_list tot = 0 #a 1.5009 actually, slipping in a quick enhancement ;-) for n in node_list: - lvl = n.getAttribute('level') #o 1.5009 not sure of the value of this + lvl = n.get('level') #o 1.5009 not sure of the value of this tot += int(lvl) #a 1.5009 - type = n.getAttribute('name') #o 1.5009 not sure of the value of this + type = n.get('name') #o 1.5009 not sure of the value of this #print type,lvl #a (debug) 1.5009 if attr == "level": return lvl #o 1.5009 this returns the level of someone's first class. ??? @@ -602,17 +595,17 @@ def get_class_lvl( self, classN ): #a 1.5009 need to be able to get monk lvl #a 1.5009 this function is new. - node_list = self.master_dom.getElementsByTagName('class') + node_list = self.xml.findall('class') #print "eclasses - get_class_lvl node_list",node_list for n in node_list: - lvl = n.getAttribute('level') - type = n.getAttribute('name') + lvl = n.get('level') + type = n.get('name') if classN == type: return lvl class class_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Class') + pname = handler.xml.set("name", 'Class') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -634,9 +627,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),3,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Class") @@ -652,7 +645,7 @@ value = self.grid.GetCellValue(row,col) try: int(value) - self.n_list[row].setAttribute('level',value) + self.n_list[row].set('level',value) except: self.grid.SetCellValue(row,col,"1") @@ -660,9 +653,9 @@ def refresh_row(self,i): n = self.n_list[i] - name = n.getAttribute('name') - level = n.getAttribute('level') - book = n.getAttribute('book') + name = n.get('name') + level = n.get('level') + book = n.get('book') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -677,23 +670,21 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+"dnd35classes.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd35"]+"dnd35classes.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('class') + f_list = self.temp_dom.findall('class') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() @@ -725,10 +716,10 @@ self.root = getRoot(self) #a 1.5002 self.root.saves = self #a 1.6009 - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') self.saves={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.saves[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) @@ -746,13 +737,13 @@ def get_mod(self,name): save = self.saves[name] - stat = save.getAttribute('stat') + stat = save.get('stat') #print "dnd35saves, get_mod: self,root",self,self.root #a (debug) 1.5002 #print "and abilities",self.root.abilities #a (debug) 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - base = int(save.getAttribute('base')) - miscmod = int(save.getAttribute('miscmod')) - magmod = int(save.getAttribute('magmod')) + base = int(save.get('base')) + miscmod = int(save.get('miscmod')) + magmod = int(save.get('magmod')) total = stat_mod + base + miscmod + magmod return total @@ -788,17 +779,17 @@ Save KeyBaseAbilMagic MiscTotal""" - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - base = n.getAttribute('base') + name = n.get('name') + stat = n.get('stat') + base = n.get('base') html_str = html_str + ""+name+""+stat+""+base+"" #stat_mod = str(dnd_globals["stats"][stat]) #d 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - mag = n.getAttribute('magmod') - misc = n.getAttribute('miscmod') + mag = n.get('magmod') + misc = n.get('miscmod') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -814,7 +805,7 @@ class save_grid(wx.grid.Grid): """grid for saves""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Saves') + pname = handler.xml.set("name", 'Saves') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 in this case, we need the functional parent, not the invoking parent. self.root = getRoot(self) @@ -826,7 +817,7 @@ self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - saves = handler.master_dom.getElementsByTagName('save') + saves = handler.xml.findall('save') self.CreateGrid(len(saves),7) self.SetRowLabelSize(0) col_names = ['Save','Key','base','Abil','Magic','Misc','Total'] @@ -857,11 +848,11 @@ try: int(value) if col == 2: - self.saves[row].setAttribute('base',value) + self.saves[row].set('base',value) elif col ==4: - self.saves[row].setAttribute('magmod',value) + self.saves[row].set('magmod',value) elif col ==5: # 1.5001 - self.saves[row].setAttribute('miscmod',value) + self.saves[row].set('miscmod',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -869,17 +860,17 @@ def refresh_row(self,rowi): s = self.saves[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('base')) + self.SetCellValue(rowi,2,s.get('base')) self.SetCellValue(rowi,3,str(self.root.abilities.get_mod(stat))) self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('magmod')) - self.SetCellValue(rowi,5,s.getAttribute('miscmod')) + self.SetCellValue(rowi,4,s.get('magmod')) + self.SetCellValue(rowi,5,s.get('miscmod')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,6,mod) self.SetReadOnly(rowi,6) @@ -916,7 +907,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -956,7 +947,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -982,16 +973,16 @@ skills_char_child.__init__(self,xml_dom,tree_node,parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') self.skills={} #Adding code to not display skills you can not use -mgt for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.skills[name] = n skill_check = self.skills[name] - ranks = int(skill_check.getAttribute('rank')) - trained = int(skill_check.getAttribute('untrained')) + ranks = int(skill_check.get('rank')) + trained = int(skill_check.get('untrained')) if ranks > 0 or trained == 1: new_tree_node = tree.AppendItem(self.mytree_node,name, @@ -1009,15 +1000,15 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') self.skills={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.skills[name] = n skill_check = self.skills[name] - ranks = int(skill_check.getAttribute('rank')) - trained = int(skill_check.getAttribute('untrained')) + ranks = int(skill_check.get('rank')) + trained = int(skill_check.get('untrained')) if ranks > 0 or trained == 1: new_tree_node = tree.AppendItem(self.mytree_node,name, @@ -1029,11 +1020,11 @@ def get_mod(self,name): skill = self.skills[name] - stat = skill.getAttribute('stat') + stat = skill.get('stat') #stat_mod = int(dnd_globals["stats"][stat]) #d 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - rank = int(skill.getAttribute('rank')) - misc = int(skill.getAttribute('misc')) + rank = int(skill.get('rank')) + misc = int(skill.get('misc')) total = stat_mod + rank + misc return total @@ -1052,8 +1043,8 @@ skill = self.skills[name] - untr = skill.getAttribute('untrained') #a 1.6004 - rank = skill.getAttribute('rank') #a 1.6004 + untr = skill.get('untrained') #a 1.6004 + rank = skill.get('rank') #a 1.6004 if eval('%s == 0' % (untr)): #a 1.6004 if eval('%s == 0' % (rank)): #a 1.6004 res = 'You fumble around, accomplishing nothing' #a 1.6004 @@ -1065,7 +1056,7 @@ armor = '' acCp = '' if ac < 0: #acCp >= 1 #m 1.5004 this is stored as negatives. - armorCheck = int(skill.getAttribute('armorcheck')) + armorCheck = int(skill.get('armorcheck')) #print "ac,armorCheck",ac,armorCheck if armorCheck == 1: acCp=ac @@ -1095,13 +1086,13 @@ html_str = """""" - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - rank = n.getAttribute('rank') - untr = n.getAttribute('untrained') #a 1.6004 + name = n.get('name') + stat = n.get('stat') + rank = n.get('rank') + untr = n.get('untrained') #a 1.6004 #Filter unsuable skills out of pretty print -mgt if eval('%s > 0' % (rank)) or eval('%s == 1' % (untr)): if eval('%s >=1' % (rank)): @@ -1118,7 +1109,7 @@ continue stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 #stat_mod = str(dnd_globals["stats"][stat]) #d 1.5002 - misc = n.getAttribute('misc') + misc = n.get('misc') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -1135,13 +1126,13 @@ def __init__(self, parent, handler): self.hparent = handler #a 1.5002 need function parent, not invoker self.root = getRoot(self) #a 1.5002 - pname = handler.master_dom.setAttribute("name", 'Skills') + pname = handler.xml.set("name", 'Skills') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - skills = handler.master_dom.getElementsByTagName('skill') + skills = handler.xml.findall('skill') #xelf.stats = dnd_globals["stats"] #d 1.5002 self.CreateGrid(len(skills),6) @@ -1162,9 +1153,9 @@ try: int(value) if col == 2: - self.skills[row].setAttribute('rank',value) + self.skills[row].set('rank',value) elif col ==4: - self.skills[row].setAttribute('misc',value) + self.skills[row].set('misc',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -1174,13 +1165,13 @@ def refresh_row(self,rowi): s = self.skills[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('rank')) + self.SetCellValue(rowi,2,s.get('rank')) #self.SetCellValue(rowi,3,str(dnd_globals["stats"][stat])) #d 1.5002 if self.root.abilities: #a 1.5002 sanity check. stat_mod=self.root.abilities.get_mod(stat) #a 1.5002 @@ -1191,7 +1182,7 @@ self.SetCellValue(rowi,3,str(stat_mod)) #a 1.5002 self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('misc')) + self.SetCellValue(rowi,4,s.get('misc')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,5,mod) self.SetReadOnly(rowi,5) @@ -1235,9 +1226,9 @@ def tohtml(self): html_str = "
SkillKey RankAbilMiscTotal
Feats
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name')+ ", " + html_str += n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -1248,8 +1239,8 @@ #a 1.5002 in this case, we need the functional parent, not the invoking parent. self.root = getRoot(self) #a 1.5002 #tempTitle= 'Feats - ' + self.root.general.charName #a 1.5010 - #pname = handler.master_dom.setAttribute("name", tempTitle) #a 1.5010 - pname = handler.master_dom.setAttribute("name", 'Feats') #d 1.5010 + #pname = handler.xml.set("name", tempTitle) #a 1.5010 + pname = handler.xml.set("name", 'Feats') #d 1.5010 wx.Panel.__init__(self, parent, -1) self.grid = wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -1272,9 +1263,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),3,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Feat") @@ -1291,9 +1282,9 @@ def refresh_row(self,i): feat = self.n_list[i] - name = feat.getAttribute('name') - type = feat.getAttribute('type') - desc = feat.getAttribute('desc') #m 1.6 correct typo + name = feat.get('name') + type = feat.get('type') + desc = feat.get('desc') #m 1.6 correct typo self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,type) @@ -1310,25 +1301,23 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+"dnd35feats.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd35"]+"dnd35feats.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('feat') + f_list = self.temp_dom.findall('feat') opts = [] for f in f_list: - opts.append(f.getAttribute('name') + " - [" + - f.getAttribute('type') + "] - " + f.getAttribute('desc')) + opts.append(f.get('name') + " - [" + + f.get('type') + "] - " + f.get('desc')) dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) f_list=0; opts=0 @@ -1371,7 +1360,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -1410,7 +1399,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -1439,8 +1428,8 @@ return wnd def on_rclick( self, evt ): - chp = self.master_dom.getAttribute('current') - mhp = self.master_dom.getAttribute('max') + chp = self.xml.get('current') + mhp = self.xml.get('max') txt = '((HP: %s / %s))' % ( chp, mhp ) self.chat.ParsePost( txt, True, True ) @@ -1448,9 +1437,9 @@ html_str = "" html_str += "" html_str += "" - html_str += "" + html_str += "" html_str += "" - html_str += "" + html_str += "" html_str += "
Hit Points
Max:"+self.master_dom.getAttribute('max')+""+self.xml.get('max')+"Current:"+self.master_dom.getAttribute('current')+""+self.xml.get('current')+"
" return html_str @@ -1460,15 +1449,15 @@ self.hparent = handler #a 1.5002 allow ability to run up tree. In this #a 1.5002 case, we need the functional parent, not the invoking parent. - pname = handler.master_dom.setAttribute("name", 'HitPoints') + pname = handler.xml.set("name", 'HitPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "HP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, HP_CUR, - self.master_dom.getAttribute('current')), 0, wx.EXPAND), + self.xml.get('current')), 0, wx.EXPAND), (wx.StaticText(self, -1, "HP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, HP_MAX, self.master_dom.getAttribute('max')), + (wx.TextCtrl(self, HP_MAX, self.xml.get('max')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) @@ -1483,9 +1472,9 @@ def on_text(self,evt): id = evt.GetId() if id == HP_CUR: - self.master_dom.setAttribute('current',evt.GetString()) + self.xml.set('current',evt.GetString()) elif id == HP_MAX: - self.master_dom.setAttribute('max',evt.GetString()) + self.xml.set('max',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1525,9 +1514,9 @@ self.temp_dom={} #a 1.5012 end a1b - node_list = self.master_dom.getElementsByTagName('melee') + node_list = self.xml.findall('melee') self.melee = node_list[0] - node_list = self.master_dom.getElementsByTagName('ranged') + node_list = self.xml.findall('ranged') self.ranged = node_list[0] self.refresh_weapons() # this causes self.weapons to be loaded. @@ -1545,7 +1534,7 @@ fnFrame.panel.SetPage(self.html_str) fnFrame.Show() - #weaponsH = self.master_dom.getElementsByTagName('attacks') + #weaponsH = self.xml.findall('attacks') #mark7 #a 1.9001 this whole method @@ -1565,9 +1554,9 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('weapon') + node_list = self.xml.findall('weapon') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') fn = safeGetAttr(n,'fn') #a 1.5012 can be removed when #a 1.5012 confident all characters in the world have footnotes. #if self.updateFootNotes: @@ -1581,33 +1570,28 @@ def updateFootN(self,n):#a 1.5012 this whole function if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+"dnd35weapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012 - self.temp_dom = xml.dom.minidom.parse(tmp) - - #self.temp_dom = parseXml_with_dlg(self,tmp.read()) - self.temp_dom = self.temp_dom._get_firstChild() - tmp.close() - nameF = n.getAttribute('name') - w_list = self.temp_dom.getElementsByTagName('weapon') + tree = parse(dir_struct["dnd35"]+"dnd35weapons.xml") + self.temp_dom = tree.getroot() + nameF = n.get('name') + w_list = self.temp_dom.findall('weapon') found = False for w in w_list: - if nameF == w.getAttribute('name'): + if nameF == w.get('name'): found = True fnN = safeGetAttr(n,'fn') if fnN == None or fnN == 'None': - fnW = w.getAttribute('fn') + fnW = w.get('fn') #print "weapon",nameF,"footnotes are updated to",fnW self.html_str += (""+nameF+""+ ""+fnW+"\n") - n.setAttribute('fn',fnW) + n.set('fn',fnW) break if not found: self.html_str += (""+nameF+" - Custom "+ "Weapon, research "+ "and update manually; setting footnote to indicate custom"+ ""+'X'+"\n") - n.setAttribute('fn','X') + n.set('fn','X') def get_mod(self,type='m'): @@ -1625,13 +1609,13 @@ stat_mod = -7 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 #print "Big test - stat_mod",stat_mod #a (debug) 1.6000 - base = int(temp.getAttribute('base')) - base2 = int(temp.getAttribute('second')) - base3 = int(temp.getAttribute('third')) - base4 = int(temp.getAttribute('forth')) - base5 = int(temp.getAttribute('fifth')) - base6 = int(temp.getAttribute('sixth')) - misc = int(temp.getAttribute('misc')) + base = int(temp.get('base')) + base2 = int(temp.get('second')) + base3 = int(temp.get('third')) + base4 = int(temp.get('forth')) + base5 = int(temp.get('fifth')) + base6 = int(temp.get('sixth')) + misc = int(temp.get('misc')) return (base, base2, base3, base4, base5, base6, stat_mod ,misc) def on_rclick(self,evt): @@ -1645,10 +1629,10 @@ #self.frame.add_panel(self.get_design_panel(self.frame.note)) else: #print "entering attack phase" - mod = int(self.weapons[name].getAttribute('mod')) + mod = int(self.weapons[name].get('mod')) wepMod = mod #a 1.5008 footNotes = safeGetAttr(self.weapons[name],'fn','') - cat = self.weapons[name].getAttribute('category') #a1.6001 + cat = self.weapons[name].get('category') #a1.6001 result = split(cat,"-",2) #a 1.6001 if len(result) < 2: #a 1.6021 this if & else print "warning: 1.6002 unable to interpret weapon category" @@ -1660,7 +1644,7 @@ tres=result[1] #print "print FootNotes,tres",footNotes,tres if tres == 'Melee': #a 1.6001 #m 1.6022 use of tres here and... - #if self.weapons[name].getAttribute('range') == '0':#d 1.6001 + #if self.weapons[name].get('range') == '0':#d 1.6001 rangeOrMelee = 'm' #a 1.5008 code demote for next comment block elif tres == 'Ranged': #m 1.6001 (was just else) #m 1.6022 here rangeOrMelee = 'r' #a 1.5008 @@ -1671,7 +1655,7 @@ rangeOrMelee ='m' mod = mod + self.get_mod(rangeOrMelee) #a 1.5008 chat = self.chat - dmg = self.weapons[name].getAttribute('damage') + dmg = self.weapons[name].get('damage') #a 1.6003 start code fix instance a result = split(dmg,"/",2) @@ -1858,28 +1842,28 @@ html_str += ""+str(ranged[1])+"" html_str += ""+str(ranged[2])+"" - n_list = self.master_dom.getElementsByTagName('weapon') + n_list = self.xml.findall('weapon') for n in n_list: - mod = n.getAttribute('mod') + mod = n.get('mod') if mod >= 0: mod1 = "+" else: mod1 = "" - ran = n.getAttribute('range') + ran = n.get('range') total = str(int(mod) + self.get_mod(ran)) html_str += """

""" html_str += "" - html_str += "" + html_str += n.get('name')+"" + html_str += "" html_str += """""" html_str += "" - html_str += "" + html_str += n.get('weight')+"" + html_str += "" html_str += '' % (mod1, mod) #a 1.5012 add next two lines to pretty print footnotes. html_str += """""" @@ -1890,7 +1874,7 @@ class attack_grid(wx.grid.Grid): """grid for attacks""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 we need the functional parent, not the invoking parent. @@ -1932,19 +1916,19 @@ try: int(value) if col==1: - self.rows[row].setAttribute('base',value) + self.rows[row].set('base',value) elif col==2: - self.rows[row].setAttribute('second',value) + self.rows[row].set('second',value) elif col==3: - self.rows[row].setAttribute('third',value) + self.rows[row].set('third',value) elif col==4: - self.rows[row].setAttribute('forth',value) + self.rows[row].set('forth',value) elif col==5: - self.rows[row].setAttribute('fifth',value) + self.rows[row].set('fifth',value) elif col==6: - self.rows[row].setAttribute('sixth',value) + self.rows[row].set('sixth',value) elif col==8: - self.rows[row].setAttribute('misc',value) + self.rows[row].set('misc',value) self.parent.refresh_data() except: self.SetCellValue(row,col,"0") @@ -1983,7 +1967,7 @@ self.hparent = handler #a 1.5012 self.root = getRoot(self) - pname = handler.master_dom.setAttribute("name", 'Weapons') + pname = handler.xml.set("name", 'Weapons') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -2008,9 +1992,9 @@ self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.on_gridRclick)#a 1.5012 - n_list = handler.master_dom.getElementsByTagName('weapon') + n_list = handler.xml.findall('weapon') self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.handler = handler #trash=input("weapon panel init colnames") self.colAttr = ['name','damage','mod','critical','type','weight', @@ -2038,7 +2022,7 @@ #print "wp, on rclick,grid row,col,value",row,col,value if col == 9 and value != 'None': n = self.n_list[row] - name = n.getAttribute('name') + name = n.get('name') #print "we want a panel!" handler = self.hparent #print "handler:",handler @@ -2050,18 +2034,13 @@ fnFrame = wx.Frame(masterFrame, -1, title) fnFrame.panel = wx.html.HtmlWindow(fnFrame,-1) if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+ - "dnd35weapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('f') # the footnotes + tree = parse(dir_struct["dnd35"]+ "dnd35weapons.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('f') # the footnotes #print "weapon_panel - on_rclick f_list",f_list#a 1.6 n = self.n_list[row] - name = n.getAttribute('name') - footnotes = n.getAttribute('fn') + name = n.get('name') + footnotes = n.get('fn') html_str = "" html_str += """
Weapon AttackDamageCritical
" - html_str += n.getAttribute('name')+""+total+""+n.getAttribute('damage')+"" - html_str += n.getAttribute('critical')+"
"+total+""+n.get('damage')+"" + html_str += n.get('critical')+"
RangeWeight TypeSizeMisc Mod
"+ran+"" - html_str += n.getAttribute('weight')+""+n.getAttribute('type')+"" - html_str += n.getAttribute('size')+""+n.get('type')+"" + html_str += n.get('size')+"%s%s
Footnotes:
\n""" @@ -2073,9 +2052,9 @@ aNote=footnotes[i] found=False for f in f_list: - if f.getAttribute('mark') == aNote: + if f.get('mark') == aNote: found=True - text=f.getAttribute('txt') + text=f.get('txt') html_str += (""+ "\n") if not found: @@ -2099,29 +2078,29 @@ value = self.grid.GetCellValue(row,col) if col == 2 and not int(value): # special case for mod, demoted value = "0" #a 5.012 demoted - self.n_list[row].setAttribute('mod',value) # a 5.012 demoted + self.n_list[row].set('mod',value) # a 5.012 demoted if not (col == 9 and value == "None" and - self.n_list[row].getAttribute('fn') == "None" + self.n_list[row].get('fn') == "None" ): #a 5.012 special case for footnotes - self.n_list[row].setAttribute(self.colAttr[col],value)#a 5.012 + self.n_list[row].set(self.colAttr[col],value)#a 5.012 def refresh_row(self,i): n = self.n_list[i] - fn = n.getAttribute('fn') + fn = n.get('fn') #print "fn=",fn - name = n.getAttribute('name') - mod = n.getAttribute('mod') - ran = n.getAttribute('range') + name = n.get('name') + mod = n.get('mod') + ran = n.get('range') total = str(int(mod) + self.handler.get_mod(ran)) self.grid.SetCellValue(i,0,name) - self.grid.SetCellValue(i,1,n.getAttribute('damage')) + self.grid.SetCellValue(i,1,n.get('damage')) self.grid.SetCellValue(i,2,mod) - self.grid.SetCellValue(i,3,n.getAttribute('critical')) - self.grid.SetCellValue(i,4,n.getAttribute('type')) - self.grid.SetCellValue(i,5,n.getAttribute('weight')) + self.grid.SetCellValue(i,3,n.get('critical')) + self.grid.SetCellValue(i,4,n.get('type')) + self.grid.SetCellValue(i,5,n.get('weight')) self.grid.SetCellValue(i,6,ran) - self.grid.SetCellValue(i,7,n.getAttribute('size') ) + self.grid.SetCellValue(i,7,n.get('size') ) self.grid.SetCellValue(i,8,total) self.grid.SetCellValue(i,9,safeGetAttr(n,'fn','None')) #a 1.5012 self.grid.SetCellValue(i,10,safeGetAttr(n,'comment','')) #a 1.5012 @@ -2137,31 +2116,27 @@ for i in range(rows-1,-1,-1): #a 1.6011 or you lose context if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_weapons() def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+"dnd35weapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012 - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close(); print - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('weapon') + tree = parse(dir_struct["dnd35"]+"dnd35weapons.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('weapon') opts = [] #print "weapon_panel - on_add f_list",f_list#a 1.6 for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() #print f_list[i] # DOM Element: weapon. - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) #print self.grid.AppendRows # a bound method of wxGrid self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.n_list = self.xml.findall('weapon') #print "self.n_list",self.n_list # list of DOM weapons self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_weapons() @@ -2187,7 +2162,7 @@ class attack_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') self.parent = parent #a 1.9001 wx.Panel.__init__(self, parent, -1) @@ -2243,19 +2218,19 @@ return ac_total def get_max_dex(self): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') dex = 10 for a in armor_list: - temp = int(a.getAttribute("maxdex")) + temp = int(a.get("maxdex")) if temp < dex: dex = temp return dex def get_total(self,attr): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') total = 0 for a in armor_list: - total += int(a.getAttribute(attr)) + total += int(a.get(attr)) return total def get_design_panel(self,parent): @@ -2280,29 +2255,29 @@ html_str += "" html_str += "" html_str += "
NoteDescription
"+aNote+""+text+"
"+str(self.get_spell_failure())+""+str(self.get_max_dex())+""+str(self.get_total_weight())+"
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: html_str += """

""" html_str += "" - html_str += "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" + html_str += "" html_str += """""" html_str += "" html_str += "" html_str += "" - html_str += "" - html_str += "" - html_str += "" - html_str += "" - html_str += "
ArmorTypeBonus
"+n.getAttribute('name')+""+n.getAttribute('type')+""+n.getAttribute('bonus')+"
"+n.get('name')+""+n.get('type')+""+n.get('bonus')+"
Check PenaltySpell FailureMax DexSpeedWeight
"+n.getAttribute('checkpenalty')+""+n.getAttribute('spellfailure')+""+n.getAttribute('maxdex')+""+n.getAttribute('speed')+""+n.getAttribute('weight')+"
" + html_str += ""+n.get('checkpenalty')+"" + html_str += ""+n.get('spellfailure')+"" + html_str += ""+n.get('maxdex')+"" + html_str += ""+n.get('speed')+"" + html_str += ""+n.get('weight')+"" return html_str class ac_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Armor') + pname = handler.xml.set("name", 'Armor') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 we need the functional parent, not the invoking parent. @@ -2327,8 +2302,8 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.master_dom = handler.master_dom - n_list = handler.master_dom._get_childNodes() + self.xml = handler.xml + n_list = handler.xml.getchildren() self.n_list = n_list col_names = ['Armor','bonus','maxdex','cp','sf','weight','speed','type'] self.grid.CreateGrid(len(n_list),len(col_names),1) @@ -2349,40 +2324,37 @@ if col >= 1 and col <= 5: try: int(value) - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) except: self.grid.SetCellValue(row,col,"0") else: - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) def refresh_row(self,i): n = self.n_list[i] for y in range(len(self.atts)): - self.grid.SetCellValue(i,y,n.getAttribute(self.atts[y])) + self.grid.SetCellValue(i,y,n.get(self.atts[y])) def on_remove(self,evt): rows = self.grid.GetNumberRows() for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd35"]+"dnd35armor.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('armor') + tree = parse(dir_struct["dnd35"]+"dnd35armor.xml") + self.temp_dom = tree.getroot() + f_list = self.temp_dom.findall('armor') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/dnd3e.py --- a/orpg/gametree/nodehandlers/dnd3e.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/dnd3e.py Tue Nov 10 14:11:28 2009 -0600 @@ -151,6 +151,8 @@ from string import * #a 1.6003 from inspect import * #a 1.9001 from orpg.dirpath import dir_struct +from orpg.tools.orpg_log import debug +from xml.etree.ElementTree import parse dnd3e_EXPORT = wx.NewId() ############Global Stuff############## @@ -209,12 +211,12 @@ # return None #a 1.6 convinience function added safeGetAttr -def safeGetAttr(node,lable,defRetV=None): - cna=node.attributes - for i2 in range(len(cna)): - if cna.item(i2).name == lable: - return cna.item(i2).value - #retV=node.getAttribute(lable) # getAttribute does not distingish between +def safeGetAttr(node, label, defRetV=None): + cna=node.attrib + for key in cna: + if key == label: + return cna[key] + #retV=node.get(lable) # get does not distingish between # the attribute not being present vs it having a value of "" # This is bad for this routine, thus not used. return defRetV @@ -230,7 +232,7 @@ node_handler.__init__(self,xml_dom,tree_node) self.Version = "v1.901" #a 1.6000 general documentation, usage. - print "dnd3echar_handler - version:",self.Version #m 1.6000 + print "dnd3echar_handler - version:", self.Version #m 1.6000 self.hparent = None #a 1.5002 allow ability to run up tree, this is the #a 1.5002 top of the handler tree, this is used to flag where to stop @@ -252,7 +254,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -353,7 +355,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -384,13 +386,12 @@ def __init__(self, parent, handler): wx.Panel.__init__(self, parent, -1) - pname = handler.master_dom.setAttribute("name", 'How To') + pname = handler.xml.set("name", 'How To') self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, 'How To'), wx.VERTICAL) - self.master_dom = handler.master_dom - n_list = self.master_dom._get_childNodes() + self.xml = handler.xml + n_list = self.xml.getchildren() for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - self.sizer.Add(wx.StaticText(self, -1, t_node._get_nodeValue()), 1, wx.EXPAND) + self.sizer.Add(wx.StaticText(self, -1, n.text), 1, wx.EXPAND) self.SetSizer(self.sizer) self.SetAutoLayout(True) self.Fit() @@ -412,12 +413,12 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
General Information
" - for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + ", " + for n in n_list: + debug(n) + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -428,14 +429,13 @@ def get_char_name( self ): - node = self.master_dom.getElementsByTagName( 'name' )[0] - t_node = component.get('xml').safe_get_text_node( node ) - return t_node._get_nodeValue() + node = self.xml.findall( 'name' )[0] + return node.text class gen_grid(wx.grid.Grid): """grid for gen info""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'General') + pname = handler.xml.set("name", 'General') self.hparent = handler #a 1.5002 allow ability to run up tree, needed # a 1.5002 parent is functional parent, not invoking parent. @@ -444,7 +444,7 @@ #self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.CreateGrid(len(n_list),2) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -457,18 +457,15 @@ row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - t_node = self.n_list[row]._get_firstChild() - t_node._set_nodeValue(value) + self.n_list[row].text = value if row==0: self.handler.on_name_change(value) #self.AutoSizeColumn(1) - def refresh_row(self,rowi): - t_node = component.get('xml').safe_get_text_node(self.n_list[rowi]) - - self.SetCellValue(rowi,0,self.n_list[rowi]._get_tagName()) + def refresh_row(self,rowi): + self.SetCellValue(rowi,0,self.n_list[rowi].tag) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,t_node._get_nodeValue()) + self.SetCellValue(rowi,1,self.n_list[rowi].text) self.AutoSizeColumn(1) class dnd3einventory(dnd3e_char_child): @@ -487,69 +484,53 @@ return wnd def tohtml(self): - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() html_str = "
General Information
" - for n in n_list: - t_node = component.get('xml').safe_get_text_node(n) - html_str += ""+n._get_tagName().capitalize() +": " - html_str += t_node._get_nodeValue() + "
" + for n in n_list: + debug((n)) + html_str += ""+n.tag.capitalize() +": " + html_str += n.text + "
" html_str = html_str[:len(html_str)-2] + "
" return html_str class inventory_pane(wx.Panel): def __init__(self, parent, handler): wx.Panel.__init__(self, parent, wx.ID_ANY) - - self.n_list = handler.master_dom._get_childNodes() + self.n_list = handler.xml.getchildren() self.autosize = False - self.sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, "Inventroy"), wx.VERTICAL) - self.lang = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Languages") self.gear = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Gear") self.magic = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_MULTILINE | wx.TE_BESTWRAP, name="Magic") self.grid = wx.grid.Grid(self, wx.ID_ANY, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) - self.grid.CreateGrid(len(self.n_list)-3,2) self.grid.SetRowLabelSize(0) self.grid.SetColLabelSize(0) - for i in xrange(len(self.n_list)): self.refresh_row(i) - sizer1 = wx.BoxSizer(wx.HORIZONTAL) sizer1.Add(self.grid, 1, wx.EXPAND) sizer1.Add(self.lang, 1, wx.EXPAND) - self.sizer.Add(sizer1, 0, wx.EXPAND) - sizer2 = wx.BoxSizer(wx.HORIZONTAL) sizer2.Add(self.gear, 1, wx.EXPAND) sizer2.Add(self.magic, 1, wx.EXPAND) - self.sizer.Add(sizer2, 1, wx.EXPAND) - self.SetSizer(self.sizer) self.SetAutoLayout(True) self.Fit() - self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.lang) self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.gear) self.Bind(wx.EVT_TEXT, self.onTextNodeChange, self.magic) self.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self.on_cell_change, self.grid) - def fillTextNode(self, name, value): - if name == 'Languages': - self.lang.SetValue(value) - elif name == 'Gear': - self.gear.SetValue(value) - elif name == 'Magic': - self.magic.SetValue(value) + if name == 'Languages': self.lang.SetValue(value) + elif name == 'Gear': self.gear.SetValue(value) + elif name == 'Magic': self.magic.SetValue(value) def onTextNodeChange(self, event): id = event.GetId() - if id == self.gear.GetId(): nodeName = 'Gear' value = self.gear.GetValue() @@ -559,16 +540,15 @@ elif id == self.lang.GetId(): nodeName = 'Languages' value = self.lang.GetValue() - for node in self.n_list: - if node._get_tagName() == nodeName: - t_node = component.get('xml').safe_get_text_node(node) - t_node._set_nodeValue(value) + if node.tag == nodeName: + debug(node) + node.text = value def saveMoney(self, row, col): - value = self.grid.GetCellValue(row, col) - t_node = component.get('xml').safe_get_text_node(self.n_list[row]) - t_node._set_nodeValue(value) + value = self.grid.GetCellValue(row, col) + debug(self.n_list[row]) + self.n_list[row].text = value def on_cell_change(self, evt): row = evt.GetRow() @@ -576,18 +556,12 @@ self.grid.AutoSizeColumn(col) wx.CallAfter(self.saveMoney, row, col) - - - def refresh_row(self, row): - t_node = component.get('xml').safe_get_text_node(self.n_list[row]) - tagname = self.n_list[row]._get_tagName() - value = t_node._get_nodeValue() - if tagname == 'Gear': - self.fillTextNode(tagname, value) - elif tagname == 'Magic': - self.fillTextNode(tagname, value) - elif tagname == 'Languages': - self.fillTextNode(tagname, value) + def refresh_row(self, row): + tagname = self.n_list[row].tag + value = self.n_list[row].text + if tagname == 'Gear': self.fillTextNode(tagname, value) + elif tagname == 'Magic': self.fillTextNode(tagname, value) + elif tagname == 'Languages': self.fillTextNode(tagname, value) else: self.grid.SetCellValue(row, 0, tagname) self.grid.SetReadOnly(row, 0) @@ -612,7 +586,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -650,7 +624,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -672,14 +646,12 @@ self.hparent = parent #a 1.5002 allow ability to run up tree. self.root = getRoot(self) #a 1.5002 get top of our local function tree. self.root.abilities = self #a 1.5002 let other classes find me. - self.abilities = {} - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') tree = self.tree icons = tree.icons - for n in node_list: - name = n.getAttribute('abbr') + name = n.get('abbr') self.abilities[name] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -702,14 +674,14 @@ chat.ParsePost( txt, True, True ) def get_mod(self,abbr): - score = int(self.abilities[abbr].getAttribute('base')) + score = int(self.abilities[abbr].get('base')) mod = (score - 10) / 2 mod = int(mod) return mod def set_score(self,abbr,score): if score >= 0: - self.abilities[abbr].setAttribute("base",str(score)) + self.abilities[abbr].set("base",str(score)) def get_design_panel(self,parent): wnd = outline_panel(parent,self,abil_grid,"Abilities") @@ -719,11 +691,11 @@ def tohtml(self): html_str = """""" - node_list = self.master_dom.getElementsByTagName('stat') + node_list = self.xml.findall('stat') for n in node_list: - name = n.getAttribute('name') - abbr = n.getAttribute('abbr') - base = n.getAttribute('base') + name = n.get('name') + abbr = n.get('abbr') + base = n.get('base') mod = str(self.get_mod(abbr)) if int(mod) >= 0: #m 1.6013 added "int(" and ")" mod1 = "+" @@ -737,7 +709,7 @@ class abil_grid(wx.grid.Grid): """grid for abilities""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Stats') + pname = handler.xml.set("name", 'Stats') self.hparent = handler #a 1.5002 allow ability to run up tree. self.root = getRoot(self) #a 1.5002 in this case, we need the functional parent, not the invoking parent. @@ -746,7 +718,7 @@ self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - stats = handler.master_dom.getElementsByTagName('stat') + stats = handler.xml.findall('stat') self.CreateGrid(len(stats),3) self.SetRowLabelSize(0) col_names = ['Ability','Score','Modifier'] @@ -765,23 +737,21 @@ #print value try: int(value) - self.stats[row].setAttribute('base',value) + self.stats[row].set('base',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") if self.char_wnd: self.char_wnd.refresh_data() - #mark5 - def refresh_row(self,rowi): s = self.stats[rowi] - name = s.getAttribute('name') - abbr = s.getAttribute('abbr') + name = s.get('name') + abbr = s.get('abbr') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - self.SetCellValue(rowi,1,s.getAttribute('base')) + self.SetCellValue(rowi,1,s.get('base')) self.SetCellValue(rowi,2,str(self.handler.get_mod(abbr))) self.SetReadOnly(rowi,2) #if self.root.saves.saveGrid: #a 1.6018 d 1.9002 whole if clause @@ -823,20 +793,20 @@ def tohtml(self): html_str = "
Ability BaseModifier
Classes
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name') + " ("+n.getAttribute('level')+"), " + html_str += n.get('name') + " ("+n.get('level')+"), " html_str = html_str[:len(html_str)-2] + "
" return html_str def get_char_lvl( self, attr ): - node_list = self.master_dom.getElementsByTagName('class') + node_list = self.xml.findall('class') # print "eclasses - get_char_lvl node_list",node_list tot = 0 #a 1.5009 actually, slipping in a quick enhancement ;-) for n in node_list: - lvl = n.getAttribute('level') #o 1.5009 not sure of the value of this + lvl = n.get('level') #o 1.5009 not sure of the value of this tot += int(lvl) #a 1.5009 - type = n.getAttribute('name') #o 1.5009 not sure of the value of this + type = n.get('name') #o 1.5009 not sure of the value of this #print type,lvl #a (debug) 1.5009 if attr == "level": return lvl #o 1.5009 this returns the level of someone's first class. ??? @@ -847,17 +817,17 @@ def get_class_lvl( self, classN ): #a 1.5009 need to be able to get monk lvl #a 1.5009 this function is new. - node_list = self.master_dom.getElementsByTagName('class') + node_list = self.xml.findall('class') #print "eclasses - get_class_lvl node_list",node_list for n in node_list: - lvl = n.getAttribute('level') - type = n.getAttribute('name') + lvl = n.get('level') + type = n.get('name') if classN == type: return lvl class class_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Class') + pname = handler.xml.set("name", 'Class') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -880,9 +850,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),2,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Class") @@ -897,16 +867,13 @@ value = self.grid.GetCellValue(row,col) try: int(value) - self.n_list[row].setAttribute('level',value) - except: - self.grid.SetCellValue(row,col,"1") - + self.n_list[row].set('level',value) + except: self.grid.SetCellValue(row,col,"1") def refresh_row(self,i): n = self.n_list[i] - - name = n.getAttribute('name') - level = n.getAttribute('level') + name = n.get('name') + level = n.get('level') self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -917,28 +884,25 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3eclasses.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd3e"]+"dnd3eclasses.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('class') + f_list = self.temp_dom.findall('class') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Class','Classes',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() - def on_size(self,event): s = self.GetClientSizeTuple() self.grid.SetDimensions(0,0,s[0],s[1]-25) @@ -965,10 +929,10 @@ self.root = getRoot(self) #a 1.5002 self.root.saves = self #a 1.6009 - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') self.saves={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.saves[name] = n new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) @@ -986,18 +950,17 @@ def get_mod(self,name): save = self.saves[name] - stat = save.getAttribute('stat') + stat = save.get('stat') #print "dnd3esaves, get_mod: self,root",self,self.root #a (debug) 1.5002 #print "and abilities",self.root.abilities #a (debug) 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - base = int(save.getAttribute('base')) - miscmod = int(save.getAttribute('miscmod')) - magmod = int(save.getAttribute('magmod')) + base = int(save.get('base')) + miscmod = int(save.get('miscmod')) + magmod = int(save.get('magmod')) total = stat_mod + base + miscmod + magmod return total def on_rclick(self,evt): - item = self.tree.GetSelection() name = self.tree.GetItemText(item) if item == self.mytree_node: @@ -1010,10 +973,8 @@ #self.frame.add_panel(wnd) else: mod = self.get_mod(name) - if mod >= 0: - mod1 = "+" - else: - mod1 = "" + if mod >= 0: mod1 = "+" + else: mod1 = "" chat = self.chat txt = '%s save: [1d20%s%s]' % (name, mod1, mod) chat.ParsePost( txt, True, True ) @@ -1028,17 +989,17 @@ Save KeyBaseAbilMagic MiscTotal""" - node_list = self.master_dom.getElementsByTagName('save') + node_list = self.xml.findall('save') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - base = n.getAttribute('base') + name = n.get('name') + stat = n.get('stat') + base = n.get('base') html_str = html_str + ""+name+""+stat+""+base+"" #stat_mod = str(dnd_globals["stats"][stat]) #d 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - mag = n.getAttribute('magmod') - misc = n.getAttribute('miscmod') + mag = n.get('magmod') + misc = n.get('miscmod') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -1054,19 +1015,16 @@ class save_grid(wx.grid.Grid): """grid for saves""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Saves') + pname = handler.xml.set("name", 'Saves') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 in this case, we need the functional parent, not the invoking parent. self.root = getRoot(self) - #self.hparent.saveGrid = self #a 1.6018 d 1.9001 - - wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - saves = handler.master_dom.getElementsByTagName('save') + saves = handler.xml.findall('save') self.CreateGrid(len(saves),7) self.SetRowLabelSize(0) col_names = ['Save','Key','base','Abil','Magic','Misc','Total'] @@ -1074,10 +1032,7 @@ self.SetColLabelValue(i,col_names[i]) self.saves = saves i = 0 - for i in range(len(saves)): - self.refresh_row(i) - - + for i in range(len(saves)): self.refresh_row(i) #a 1.9002 remainder of code in this method. climber = parent nameNode = climber.GetClassName() @@ -1087,7 +1042,6 @@ masterFrame=climber masterFrame.refresh_data=self.refresh_data #print getmembers(masterFrame) - handler.saveGridFrame.append(masterFrame) def on_cell_change(self,evt): @@ -1097,29 +1051,28 @@ try: int(value) if col == 2: - self.saves[row].setAttribute('base',value) + self.saves[row].set('base',value) elif col ==4: - self.saves[row].setAttribute('magmod',value) + self.saves[row].set('magmod',value) elif col ==5: # 1.5001 - self.saves[row].setAttribute('miscmod',value) + self.saves[row].set('miscmod',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") def refresh_row(self,rowi): s = self.saves[rowi] - - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('base')) + self.SetCellValue(rowi,2,s.get('base')) self.SetCellValue(rowi,3,str(self.root.abilities.get_mod(stat))) self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('magmod')) - self.SetCellValue(rowi,5,s.getAttribute('miscmod')) + self.SetCellValue(rowi,4,s.get('magmod')) + self.SetCellValue(rowi,5,s.get('miscmod')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,6,mod) self.SetReadOnly(rowi,6) @@ -1159,9 +1112,8 @@ #wxMenuItem(self.tree.std_menu, dnd3e_EXPORT, "Export...", "Export") self.myeditor = None - def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -1172,7 +1124,6 @@ def get_design_panel(self,parent): return tabbed_panel(parent,self,1) - def get_use_panel(self,parent): return tabbed_panel(parent,self,2) @@ -1187,8 +1138,6 @@ self.frame = component.get('frame') self.myeditor = None - - def on_drop(self,evt): pass @@ -1201,7 +1150,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -1227,58 +1176,53 @@ skills_char_child.__init__(self,xml_dom,tree_node,parent) tree = self.tree icons = self.tree.icons - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') self.skills={} #Adding code to not display skills you can not use -mgt for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.skills[name] = n skill_check = self.skills[name] - ranks = int(skill_check.getAttribute('rank')) - trained = int(skill_check.getAttribute('untrained')) + ranks = int(skill_check.get('rank')) + trained = int(skill_check.get('untrained')) if ranks > 0 or trained == 1: new_tree_node = tree.AppendItem(self.mytree_node,name, icons['gear'],icons['gear']) - else: - continue - + else: continue tree.SetPyData(new_tree_node,self) - def refresh_skills(self): - #Adding this so when you update the grid the tree will reflect - #The change. -mgt + #Adding this so when you update the grid the tree will reflect + #The change. -mgt tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') self.skills={} for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.skills[name] = n skill_check = self.skills[name] - ranks = int(skill_check.getAttribute('rank')) - trained = int(skill_check.getAttribute('untrained')) + ranks = int(skill_check.get('rank')) + trained = int(skill_check.get('untrained')) if ranks > 0 or trained == 1: new_tree_node = tree.AppendItem(self.mytree_node,name, icons['gear'],icons['gear']) - else: - continue - + else: continue tree.SetPyData(new_tree_node,self) def get_mod(self,name): skill = self.skills[name] - stat = skill.getAttribute('stat') + stat = skill.get('stat') #stat_mod = int(dnd_globals["stats"][stat]) #d 1.5002 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 - rank = int(skill.getAttribute('rank')) - misc = int(skill.getAttribute('misc')) + rank = int(skill.get('rank')) + misc = int(skill.get('misc')) total = stat_mod + rank + misc return total @@ -1294,11 +1238,9 @@ #dnd3e_char_child.on_ldclick(self,evt) #d 1.6014 # it's what it used to try to do. ac = self.root.ac.get_check_pen() #a 1.5002 for 1.5004 verify fix. - skill = self.skills[name] - - untr = skill.getAttribute('untrained') #a 1.6004 - rank = skill.getAttribute('rank') #a 1.6004 + untr = skill.get('untrained') #a 1.6004 + rank = skill.get('rank') #a 1.6004 if eval('%s == 0' % (untr)): #a 1.6004 if eval('%s == 0' % (rank)): #a 1.6004 res = 'You fumble around, accomplishing nothing' #a 1.6004 @@ -1310,7 +1252,7 @@ armor = '' acCp = '' if ac < 0: #acCp >= 1 #m 1.5004 this is stored as negatives. - armorCheck = int(skill.getAttribute('armorcheck')) + armorCheck = int(skill.get('armorcheck')) #print "ac,armorCheck",ac,armorCheck if armorCheck == 1: acCp=ac @@ -1322,10 +1264,8 @@ #self.frame.add_panel(wnd) else: mod = self.get_mod(name) - if mod >= 0: - mod1 = "+" - else: - mod1 = "" + if mod >= 0: mod1 = "+" + else: mod1 = "" chat = self.chat txt = '%s Skill Check: [1d20%s%s%s] %s' % ( name, mod1, mod, acCp, armor) @@ -1340,13 +1280,13 @@ html_str = """""" - node_list = self.master_dom.getElementsByTagName('skill') + node_list = self.xml.findall('skill') for n in node_list: - name = n.getAttribute('name') - stat = n.getAttribute('stat') - rank = n.getAttribute('rank') - untr = n.getAttribute('untrained') #a 1.6004 + name = n.get('name') + stat = n.get('stat') + rank = n.get('rank') + untr = n.get('untrained') #a 1.6004 #Filter unsuable skills out of pretty print -mgt if eval('%s > 0' % (rank)) or eval('%s == 1' % (untr)): if eval('%s >=1' % (rank)): @@ -1363,7 +1303,7 @@ continue stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 #stat_mod = str(dnd_globals["stats"][stat]) #d 1.5002 - misc = n.getAttribute('misc') + misc = n.get('misc') mod = str(self.get_mod(name)) if mod >= 0: mod1 = "+" @@ -1380,13 +1320,13 @@ def __init__(self, parent, handler): self.hparent = handler #a 1.5002 need function parent, not invoker self.root = getRoot(self) #a 1.5002 - pname = handler.master_dom.setAttribute("name", 'Skills') + pname = handler.xml.set("name", 'Skills') wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.handler = handler - skills = handler.master_dom.getElementsByTagName('skill') + skills = handler.xml.findall('skill') #xelf.stats = dnd_globals["stats"] #d 1.5002 self.CreateGrid(len(skills),6) @@ -1407,9 +1347,9 @@ try: int(value) if col == 2: - self.skills[row].setAttribute('rank',value) + self.skills[row].set('rank',value) elif col ==4: - self.skills[row].setAttribute('misc',value) + self.skills[row].set('misc',value) self.refresh_row(row) except: self.SetCellValue(row,col,"0") @@ -1419,13 +1359,13 @@ def refresh_row(self,rowi): s = self.skills[rowi] - name = s.getAttribute('name') + name = s.get('name') self.SetCellValue(rowi,0,name) self.SetReadOnly(rowi,0) - stat = s.getAttribute('stat') + stat = s.get('stat') self.SetCellValue(rowi,1,stat) self.SetReadOnly(rowi,1) - self.SetCellValue(rowi,2,s.getAttribute('rank')) + self.SetCellValue(rowi,2,s.get('rank')) #self.SetCellValue(rowi,3,str(dnd_globals["stats"][stat])) #d 1.5002 if self.root.abilities: #a 1.5002 sanity check. stat_mod=self.root.abilities.get_mod(stat) #a 1.5002 @@ -1436,7 +1376,7 @@ self.SetCellValue(rowi,3,str(stat_mod)) #a 1.5002 self.SetReadOnly(rowi,3) - self.SetCellValue(rowi,4,s.getAttribute('misc')) + self.SetCellValue(rowi,4,s.get('misc')) mod = str(self.handler.get_mod(name)) self.SetCellValue(rowi,5,mod) self.SetReadOnly(rowi,5) @@ -1480,9 +1420,9 @@ def tohtml(self): html_str = "
SkillKey RankAbilMiscTotal
Feats
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += n.getAttribute('name')+ ", " + html_str += n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -1493,8 +1433,8 @@ #a 1.5002 in this case, we need the functional parent, not the invoking parent. self.root = getRoot(self) #a 1.5002 #tempTitle= 'Feats - ' + self.root.general.charName #a 1.5010 - #pname = handler.master_dom.setAttribute("name", tempTitle) #a 1.5010 - pname = handler.master_dom.setAttribute("name", 'Feats') #d 1.5010 + #pname = handler.xml.set("name", tempTitle) #a 1.5010 + pname = handler.xml.set("name", 'Feats') #d 1.5010 wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -1517,9 +1457,9 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),3,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"Feat") @@ -1532,9 +1472,9 @@ def refresh_row(self,i): feat = self.n_list[i] - name = feat.getAttribute('name') - type = feat.getAttribute('type') - desc = feat.getAttribute('desc') #m 1.6 correct typo + name = feat.get('name') + type = feat.get('type') + desc = feat.get('desc') #m 1.6 correct typo self.grid.SetCellValue(i,0,name) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,type) @@ -1547,25 +1487,22 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3efeats.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - temp_dom = xml_dom - f_list = temp_dom.getElementsByTagName('feat') + tree = parse(dir_struct["dnd3e"]+"dnd3efeats.xml") + temp_dom = tree.getroot() + f_list = temp_dom.findall('feat') opts = [] for f in f_list: - opts.append(f.getAttribute('name') + " - [" + - f.getAttribute('type') + "] - " + f.getAttribute('desc')) + opts.append(f.get('name') + " - [" + + f.get('type') + "] - " + f.get('desc')) dlg = wx.SingleChoiceDialog(self,'Choose Feat','Feats',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() @@ -1607,7 +1544,7 @@ def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -1646,7 +1583,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -1675,8 +1612,8 @@ return wnd def on_rclick( self, evt ): - chp = self.master_dom.getAttribute('current') - mhp = self.master_dom.getAttribute('max') + chp = self.xml.get('current') + mhp = self.xml.get('max') txt = '((HP: %s / %s))' % ( chp, mhp ) self.chat.ParsePost( txt, True, True ) @@ -1684,9 +1621,9 @@ html_str = "" html_str += "" html_str += "" - html_str += "" + html_str += "" html_str += "" - html_str += "" + html_str += "" html_str += "
Hit Points
Max:"+self.master_dom.getAttribute('max')+""+self.xml.get('max')+"Current:"+self.master_dom.getAttribute('current')+""+self.xml.get('current')+"
" return html_str @@ -1696,15 +1633,15 @@ self.hparent = handler #a 1.5002 allow ability to run up tree. In this #a 1.5002 case, we need the functional parent, not the invoking parent. - pname = handler.master_dom.setAttribute("name", 'HitPoints') + pname = handler.xml.set("name", 'HitPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.sizer.AddMany([ (wx.StaticText(self, -1, "HP Current:"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, HP_CUR, - self.master_dom.getAttribute('current')), 0, wx.EXPAND), + self.xml.get('current')), 0, wx.EXPAND), (wx.StaticText(self, -1, "HP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), - (wx.TextCtrl(self, HP_MAX, self.master_dom.getAttribute('max')), + (wx.TextCtrl(self, HP_MAX, self.xml.get('max')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) @@ -1719,9 +1656,9 @@ def on_text(self,evt): id = evt.GetId() if id == HP_CUR: - self.master_dom.setAttribute('current',evt.GetString()) + self.xml.set('current',evt.GetString()) elif id == HP_MAX: - self.master_dom.setAttribute('max',evt.GetString()) + self.xml.set('max',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() @@ -1761,9 +1698,9 @@ self.temp_dom={} #a 1.5012 end a1b - node_list = self.master_dom.getElementsByTagName('melee') + node_list = self.xml.findall('melee') self.melee = node_list[0] - node_list = self.master_dom.getElementsByTagName('ranged') + node_list = self.xml.findall('ranged') self.ranged = node_list[0] self.refresh_weapons() # this causes self.weapons to be loaded. @@ -1781,7 +1718,7 @@ fnFrame.panel.SetPage(self.html_str) fnFrame.Show() - #weaponsH = self.master_dom.getElementsByTagName('attacks') + #weaponsH = self.xml.findall('attacks') #mark7 #a 1.9001 this whole method @@ -1801,9 +1738,9 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('weapon') + node_list = self.xml.findall('weapon') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') fn = safeGetAttr(n,'fn') #a 1.5012 can be removed when #a 1.5012 confident all characters in the world have footnotes. #if self.updateFootNotes: @@ -1817,33 +1754,28 @@ def updateFootN(self,n):#a 1.5012 this whole function if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3eweapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012 - self.temp_dom = xml.dom.minidom.parse(tmp) - - #self.temp_dom = parseXml_with_dlg(self,tmp.read()) - self.temp_dom = self.temp_dom._get_firstChild() - tmp.close() - nameF = n.getAttribute('name') - w_list = self.temp_dom.getElementsByTagName('weapon') + tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml") + self.temp_dom = tree.getroot() + nameF = n.get('name') + w_list = self.temp_dom.findall('weapon') found = False for w in w_list: - if nameF == w.getAttribute('name'): + if nameF == w.get('name'): found = True fnN = safeGetAttr(n,'fn') if fnN == None or fnN == 'None': - fnW = w.getAttribute('fn') + fnW = w.get('fn') #print "weapon",nameF,"footnotes are updated to",fnW self.html_str += (""+nameF+""+ ""+fnW+"\n") - n.setAttribute('fn',fnW) + n.set('fn',fnW) break if not found: self.html_str += (""+nameF+" - Custom "+ "Weapon, research "+ "and update manually; setting footnote to indicate custom"+ ""+'X'+"\n") - n.setAttribute('fn','X') + n.set('fn','X') def get_mod(self,type='m'): @@ -1861,13 +1793,13 @@ stat_mod = -7 stat_mod = self.root.abilities.get_mod(stat) #a 1.5002 #print "Big test - stat_mod",stat_mod #a (debug) 1.6000 - base = int(temp.getAttribute('base')) - base2 = int(temp.getAttribute('second')) - base3 = int(temp.getAttribute('third')) - base4 = int(temp.getAttribute('forth')) - base5 = int(temp.getAttribute('fifth')) - base6 = int(temp.getAttribute('sixth')) - misc = int(temp.getAttribute('misc')) + base = int(temp.get('base')) + base2 = int(temp.get('second')) + base3 = int(temp.get('third')) + base4 = int(temp.get('forth')) + base5 = int(temp.get('fifth')) + base6 = int(temp.get('sixth')) + misc = int(temp.get('misc')) return (base, base2, base3, base4, base5, base6, stat_mod ,misc) def on_rclick(self,evt): @@ -1881,10 +1813,10 @@ #self.frame.add_panel(self.get_design_panel(self.frame.note)) else: #print "entering attack phase" - mod = int(self.weapons[name].getAttribute('mod')) + mod = int(self.weapons[name].get('mod')) wepMod = mod #a 1.5008 footNotes = safeGetAttr(self.weapons[name],'fn','') - cat = self.weapons[name].getAttribute('category') #a1.6001 + cat = self.weapons[name].get('category') #a1.6001 result = split(cat,"-",2) #a 1.6001 if len(result) < 2: #a 1.6021 this if & else print "warning: 1.6002 unable to interpret weapon category" @@ -1896,7 +1828,7 @@ tres=result[1] #print "print FootNotes,tres",footNotes,tres if tres == 'Melee': #a 1.6001 #m 1.6022 use of tres here and... - #if self.weapons[name].getAttribute('range') == '0':#d 1.6001 + #if self.weapons[name].get('range') == '0':#d 1.6001 rangeOrMelee = 'm' #a 1.5008 code demote for next comment block elif tres == 'Ranged': #m 1.6001 (was just else) #m 1.6022 here rangeOrMelee = 'r' #a 1.5008 @@ -1907,7 +1839,7 @@ rangeOrMelee ='m' mod = mod + self.get_mod(rangeOrMelee) #a 1.5008 chat = self.chat - dmg = self.weapons[name].getAttribute('damage') + dmg = self.weapons[name].get('damage') #a 1.6003 start code fix instance a result = split(dmg,"/",2) @@ -2037,28 +1969,28 @@ html_str += ""+str(ranged[1])+"" html_str += ""+str(ranged[2])+"" - n_list = self.master_dom.getElementsByTagName('weapon') + n_list = self.xml.findall('weapon') for n in n_list: - mod = n.getAttribute('mod') + mod = n.get('mod') if mod >= 0: mod1 = "+" else: mod1 = "" - ran = n.getAttribute('range') + ran = n.get('range') total = str(int(mod) + self.get_mod(ran)) html_str += """

""" html_str += "" - html_str += "" + html_str += n.get('name')+"" + html_str += "" html_str += """""" html_str += "" - html_str += "" + html_str += n.get('weight')+"" + html_str += "" html_str += '' % (mod1, mod) #a 1.5012 add next two lines to pretty print footnotes. html_str += """""" @@ -2069,7 +2001,7 @@ class attack_grid(wx.grid.Grid): """grid for attacks""" def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 we need the functional parent, not the invoking parent. @@ -2128,19 +2060,19 @@ try: int(value) if col==1: - self.rows[row].setAttribute('base',value) + self.rows[row].set('base',value) elif col==2: - self.rows[row].setAttribute('second',value) + self.rows[row].set('second',value) elif col==3: - self.rows[row].setAttribute('third',value) + self.rows[row].set('third',value) elif col==4: - self.rows[row].setAttribute('forth',value) + self.rows[row].set('forth',value) elif col==5: - self.rows[row].setAttribute('fifth',value) + self.rows[row].set('fifth',value) elif col==6: - self.rows[row].setAttribute('sixth',value) + self.rows[row].set('sixth',value) elif col==8: - self.rows[row].setAttribute('misc',value) + self.rows[row].set('misc',value) #print "row:",row,"value",value,self.rows[row] self.parent.refresh_data() except: @@ -2181,7 +2113,7 @@ self.hparent = handler #a 1.5012 self.root = getRoot(self) - pname = handler.master_dom.setAttribute("name", 'Weapons') + pname = handler.xml.set("name", 'Weapons') wx.Panel.__init__(self, parent, -1) self.grid =wx.grid.Grid(self, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS) @@ -2207,9 +2139,9 @@ self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) self.grid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.on_gridRclick)#a 1.5012 - n_list = handler.master_dom.getElementsByTagName('weapon') + n_list = handler.xml.findall('weapon') self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.handler = handler #trash=input("weapon panel init colnames") self.colAttr = ['name','damage','mod','critical','type','weight', @@ -2238,7 +2170,7 @@ #print "wp, on rclick,grid row,col,value",row,col,value if col == 9 and value != 'None': n = self.n_list[row] - name = n.getAttribute('name') + name = n.get('name') #print "we want a panel!" handler = self.hparent #print "handler:",handler @@ -2250,18 +2182,14 @@ fnFrame = wx.Frame(masterFrame, -1, title) fnFrame.panel = wx.html.HtmlWindow(fnFrame,-1) if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+ - "dnd3eweapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('f') # the footnotes + f_list = self.temp_dom.findall('f') # the footnotes #print "weapon_panel - on_rclick f_list",f_list#a 1.6 n = self.n_list[row] - name = n.getAttribute('name') - footnotes = n.getAttribute('fn') + name = n.get('name') + footnotes = n.get('fn') html_str = "" html_str += """
Weapon AttackDamageCritical
" - html_str += n.getAttribute('name')+""+total+""+n.getAttribute('damage')+"" - html_str += n.getAttribute('critical')+"
"+total+""+n.get('damage')+"" + html_str += n.get('critical')+"
RangeWeight TypeSizeMisc Mod
"+ran+"" - html_str += n.getAttribute('weight')+""+n.getAttribute('type')+"" - html_str += n.getAttribute('size')+""+n.get('type')+"" + html_str += n.get('size')+"%s%s
Footnotes:
\n""" @@ -2273,9 +2201,9 @@ aNote=footnotes[i] found=False for f in f_list: - if f.getAttribute('mark') == aNote: + if f.get('mark') == aNote: found=True - text=f.getAttribute('txt') + text=f.get('txt') html_str += (""+ "\n") if not found: @@ -2299,42 +2227,42 @@ value = self.grid.GetCellValue(row,col) if col == 2 and not int(value): # special case for mod, demoted value = "0" #a 5.012 demoted - self.n_list[row].setAttribute('mod',value) # a 5.012 demoted + self.n_list[row].set('mod',value) # a 5.012 demoted if not (col == 9 and value == "None" and - self.n_list[row].getAttribute('fn') == "None" + self.n_list[row].get('fn') == "None" ): #a 5.012 special case for footnotes - self.n_list[row].setAttribute(self.colAttr[col],value)#a 5.012 + self.n_list[row].set(self.colAttr[col],value)#a 5.012 #print "cell change",row,col,value #if col == 0:#d 5.012 use of colAttr removed need for this. - # self.n_list[row].setAttribute('name',value) #d 5.012 + # self.n_list[row].set('name',value) #d 5.012 #elif col == 2: #d 5.012 # try:#d 5.012 simplifying... remove this block. # int(value) - # self.n_list[row].setAttribute('mod',value) + # self.n_list[row].set('mod',value) # #self.refresh_row(row) #d 5.012 did nothing. # except: # value = "0" - # self.n_list[row].setAttribute('mod',value) + # self.n_list[row].set('mod',value) #else: #d 5.012 demoted self.n set. - # self.n_list[row].setAttribute(self.grid.GetColLabelValue(col),value) + # self.n_list[row].set(self.grid.GetColLabelValue(col),value) def refresh_row(self,i): n = self.n_list[i] - fn = n.getAttribute('fn') + fn = n.get('fn') #print "fn=",fn - name = n.getAttribute('name') - mod = n.getAttribute('mod') - ran = n.getAttribute('range') + name = n.get('name') + mod = n.get('mod') + ran = n.get('range') total = str(int(mod) + self.handler.get_mod(ran)) self.grid.SetCellValue(i,0,name) - self.grid.SetCellValue(i,1,n.getAttribute('damage')) + self.grid.SetCellValue(i,1,n.get('damage')) self.grid.SetCellValue(i,2,mod) - self.grid.SetCellValue(i,3,n.getAttribute('critical')) - self.grid.SetCellValue(i,4,n.getAttribute('type')) - self.grid.SetCellValue(i,5,n.getAttribute('weight')) + self.grid.SetCellValue(i,3,n.get('critical')) + self.grid.SetCellValue(i,4,n.get('type')) + self.grid.SetCellValue(i,5,n.get('weight')) self.grid.SetCellValue(i,6,ran) - self.grid.SetCellValue(i,7,n.getAttribute('size') ) + self.grid.SetCellValue(i,7,n.get('size') ) self.grid.SetCellValue(i,8,total) self.grid.SetCellValue(i,9,safeGetAttr(n,'fn','None')) #a 1.5012 self.grid.SetCellValue(i,10,safeGetAttr(n,'comment','')) #a 1.5012 @@ -2350,31 +2278,28 @@ for i in range(rows-1,-1,-1): #a 1.6011 or you lose context if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_weapons() def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3eweapons.xml","r") - #tmp = open("c:\clh\codeSamples\sample1.xml","r") #a (debug) 1.5012 - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd3e"]+"dnd3eweapons.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('weapon') + f_list = self.temp_dom.findall('weapon') opts = [] #print "weapon_panel - on_add f_list",f_list#a 1.6 for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Weapon','Weapon List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() #print f_list[i] # DOM Element: weapon. - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) #print self.grid.AppendRows # a bound method of wxGrid self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.n_list = self.xml.findall('weapon') #print "self.n_list",self.n_list # list of DOM weapons self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_weapons() @@ -2400,7 +2325,7 @@ class attack_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Melee') + pname = handler.xml.set("name", 'Melee') self.parent = parent #a 1.9001 wx.Panel.__init__(self, parent, -1) @@ -2456,19 +2381,19 @@ return ac_total def get_max_dex(self): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') dex = 10 for a in armor_list: - temp = int(a.getAttribute("maxdex")) + temp = int(a.get("maxdex")) if temp < dex: dex = temp return dex def get_total(self,attr): - armor_list = self.master_dom.getElementsByTagName('armor') + armor_list = self.xml.findall('armor') total = 0 for a in armor_list: - total += int(a.getAttribute(attr)) + total += int(a.get(attr)) return total def get_design_panel(self,parent): @@ -2493,29 +2418,29 @@ html_str += "" html_str += "" html_str += "
NoteDescription
"+aNote+""+text+"
"+str(self.get_spell_failure())+""+str(self.get_max_dex())+""+str(self.get_total_weight())+"
" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: html_str += """

""" html_str += "" - html_str += "" - html_str += "" - html_str += "" + html_str += "" + html_str += "" + html_str += "" html_str += """""" html_str += "" html_str += "" html_str += "" - html_str += "" - html_str += "" - html_str += "" - html_str += "" - html_str += "
ArmorTypeBonus
"+n.getAttribute('name')+""+n.getAttribute('type')+""+n.getAttribute('bonus')+"
"+n.get('name')+""+n.get('type')+""+n.get('bonus')+"
Check PenaltySpell FailureMax DexSpeedWeight
"+n.getAttribute('checkpenalty')+""+n.getAttribute('spellfailure')+""+n.getAttribute('maxdex')+""+n.getAttribute('speed')+""+n.getAttribute('weight')+"
" + html_str += ""+n.get('checkpenalty')+"" + html_str += ""+n.get('spellfailure')+"" + html_str += ""+n.get('maxdex')+"" + html_str += ""+n.get('speed')+"" + html_str += ""+n.get('weight')+"" return html_str class ac_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Armor') + pname = handler.xml.set("name", 'Armor') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 we need the functional parent, not the invoking parent. @@ -2540,8 +2465,8 @@ self.Bind(wx.EVT_BUTTON, self.on_remove, id=10) self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.master_dom = handler.master_dom - n_list = handler.master_dom._get_childNodes() + self.xml = handler.xml + n_list = handler.xml.getchildren() self.n_list = n_list col_names = ['Armor','bonus','maxdex','cp','sf','weight','speed','type'] self.grid.CreateGrid(len(n_list),len(col_names),1) @@ -2562,40 +2487,38 @@ if col >= 1 and col <= 5: try: int(value) - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) except: self.grid.SetCellValue(row,col,"0") else: - self.n_list[row].setAttribute(self.atts[col],value) + self.n_list[row].set(self.atts[col],value) def refresh_row(self,i): n = self.n_list[i] for y in range(len(self.atts)): - self.grid.SetCellValue(i,y,n.getAttribute(self.atts[y])) + self.grid.SetCellValue(i,y,n.get(self.atts[y])) def on_remove(self,evt): rows = self.grid.GetNumberRows() for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3earmor.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd3e"]+"dnd3earmor.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('armor') + f_list = self.temp_dom.findall('armor') opts = [] for f in f_list: - opts.append(f.getAttribute('name')) + opts.append(f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Armor:','Armor List',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) self.refresh_row(self.grid.GetNumberRows()-1) dlg.Destroy() @@ -2631,7 +2554,7 @@ self.myeditor = None def new_child_handler(self,tag,text,handler_class,icon='gear'): - node_list = self.master_dom.getElementsByTagName(tag) + node_list = self.xml.findall(tag) tree = self.tree i = self.tree.icons[icon] new_tree_node = tree.AppendItem(self.mytree_node,text,i,i) @@ -2677,7 +2600,7 @@ def on_html(self,evt): html_str = self.tohtml() wnd = http_html_window(self.frame.note,-1) - wnd.title = self.master_dom.getAttribute('name') + wnd.title = self.xml.get('name') self.frame.add_panel(wnd) wnd.SetPage(html_str) @@ -2702,12 +2625,12 @@ self.root.spells = self #a 1.6009 - node_list = self.master_dom.getElementsByTagName( 'spell' ) + node_list = self.xml.findall( 'spell' ) self.spells = {} tree = self.tree icons = self.tree.icons for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.spells[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) tree.SetPyData( new_tree_node, self ) @@ -2718,10 +2641,10 @@ if item == self.mytree_node: dnd3e_char_child.on_ldclick( self, evt ) else: - level = self.spells[ name ].getAttribute( 'level' ) - descr = self.spells[ name ].getAttribute( 'desc' ) - use = self.spells[ name ].getAttribute( 'used' ) - memrz = self.spells[ name ].getAttribute( 'memrz' ) + level = self.spells[ name ].get( 'level' ) + descr = self.spells[ name ].get( 'desc' ) + use = self.spells[ name ].get( 'used' ) + memrz = self.spells[ name ].get( 'memrz' ) use += '+1' charNameL=self.root.general.charName #a 1.5002 left = eval( '%s - ( %s )' % ( memrz, use ) ) @@ -2740,16 +2663,16 @@ #txt = '%s can cast %s %d more time%s' % ( dnd_globals["gen"]["Name"], name, left, s )#d 1.5002 txt = '%s can cast %s %d more time%s' % ( charNameL, name, left, s ) #a 1.5002 self.chat.ParsePost( txt, False, False ) - self.spells[ name ].setAttribute( 'used', `eval( use )` ) + self.spells[ name ].set( 'used', `eval( use )` ) def refresh_spells(self): self.spells = {} tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('spell') + node_list = self.xml.findall('spell') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) self.spells[name]=n @@ -2761,9 +2684,9 @@ def tohtml(self): html_str = "
Arcane Spells

" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -2772,7 +2695,7 @@ class spells_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Arcane Spells') + pname = handler.xml.set("name", 'Arcane Spells') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 in this case, we need the functional parent, not the invoking parent. @@ -2800,9 +2723,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),4,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"No.") @@ -2818,16 +2741,16 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('memrz',value) + self.n_list[row].set('memrz',value) def refresh_row(self,i): spell = self.n_list[i] - memrz = spell.getAttribute('memrz') - name = spell.getAttribute('name') - type = spell.getAttribute('desc') - level = spell.getAttribute('level') + memrz = spell.get('memrz') + name = spell.get('name') + type = spell.get('desc') + level = spell.get('level') self.grid.SetCellValue(i,0,memrz) self.grid.SetCellValue(i,2,name) self.grid.SetReadOnly(i,2) @@ -2841,42 +2764,41 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): - + debug() if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3espells.xml","r") - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('spell') + tree = parse(dir_struct["dnd3e"]+"dnd3espells.xml") + xml_dom = tree.getroot() + self.temp_dom = xml_dom + debug(self.temp_dom) + f_list = self.temp_dom.findall('spell') opts = [] #lvl = int(dnd3e_char_child.get_char_lvl('level')) #castlvl = eval('%s/2' % (lvl)) for f in f_list: - spelllvl = f.getAttribute('level') + spelllvl = f.get('level') #if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) #else: # if eval('%d >= %s' %(castlvl, spelllvl)): - opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + opts.append("(" + f.get('level') + ")" + f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('spell') + self.n_list = self.xml.findall('spell') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_spells() dlg.Destroy() def on_refresh_spells( self, evt ): - f_list = self.master_dom.getElementsByTagName('spell') + f_list = self.xml.findall('spell') for spell in f_list: - spell.setAttribute( 'used', '0' ) + spell.set( 'used', '0' ) def on_size(self,event): s = self.GetClientSizeTuple() @@ -2907,12 +2829,12 @@ self.root.divine = self #a 1.6009 - node_list = self.master_dom.getElementsByTagName( 'gift' ) + node_list = self.xml.findall( 'gift' ) self.spells = {} tree = self.tree icons = self.tree.icons for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.spells[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['flask'], icons['flask'] ) tree.SetPyData( new_tree_node, self ) @@ -2924,10 +2846,10 @@ if item == self.mytree_node: dnd3e_char_child.on_ldclick( self, evt ) else: - level = self.spells[ name ].getAttribute( 'level' ) - descr = self.spells[ name ].getAttribute( 'desc' ) - use = self.spells[ name ].getAttribute( 'used' ) - memrz = self.spells[ name ].getAttribute( 'memrz' ) + level = self.spells[ name ].get( 'level' ) + descr = self.spells[ name ].get( 'desc' ) + use = self.spells[ name ].get( 'used' ) + memrz = self.spells[ name ].get( 'memrz' ) use += '+1' left = eval( '%s - ( %s )' % ( memrz, use ) ) if left < 0: @@ -2945,7 +2867,7 @@ #txt = '%s can cast %s %d more time%s' % ( dnd_globals["gen"]["Name"], name, left, s ) #d 1.5002 txt = '%s can cast %s %d more time%s' % ( charNameL, name, left, s ) #a 1.5002 self.chat.ParsePost( txt, False, False ) - self.spells[ name ].setAttribute( 'used', `eval( use )` ) + self.spells[ name ].set( 'used', `eval( use )` ) def refresh_spells(self): self.spells = {} @@ -2953,9 +2875,9 @@ icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('gift') + node_list = self.xml.findall('gift') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['flask'],icons['flask']) tree.SetPyData(new_tree_node,self) self.spells[name]=n @@ -2967,9 +2889,9 @@ def tohtml(self): html_str = "
Divine Spells

" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -2978,7 +2900,7 @@ class divine_panel(wx.Panel): def __init__(self, parent, handler): - pname = handler.master_dom.setAttribute("name", 'Divine Spells') + pname = handler.xml.set("name", 'Divine Spells') self.hparent = handler #a 1.5002 allow ability to run up tree. #a 1.5002 in this case, we need the functional parent, not the invoking parent. @@ -3007,9 +2929,9 @@ self.Bind(wx.EVT_BUTTON, self.on_refresh_spells, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),4,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"No.") @@ -3026,16 +2948,16 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) if col == 0: - self.n_list[row].setAttribute('memrz',value) + self.n_list[row].set('memrz',value) def refresh_row(self,i): spell = self.n_list[i] - memrz = spell.getAttribute('memrz') - name = spell.getAttribute('name') - type = spell.getAttribute('desc') - level = spell.getAttribute('level') + memrz = spell.get('memrz') + name = spell.get('name') + type = spell.get('desc') + level = spell.get('level') self.grid.SetCellValue(i,0,memrz) self.grid.SetCellValue(i,2,name) self.grid.SetReadOnly(i,2) @@ -3049,41 +2971,38 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) + self.xml.remove(self.n_list[i]) def on_add(self,evt): if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3edivine.xml","r") - - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() + tree = parse(dir_struct["dnd3e"]+"dnd3edivine.xml") + xml_dom = tree.getroot() self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('gift') + f_list = self.temp_dom.findall('gift') opts = [] #lvl = int(dnd3e_char_child.get_char_lvl('level')) #castlvl = lvl / 2 for f in f_list: - spelllvl = f.getAttribute('level') + spelllvl = f.get('level') #if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + # opts.append("(" + f.get('level') + ")" + f.get('name')) #else: # if eval('%d >= %s' %(castlvl, spelllvl)): - opts.append("(" + f.getAttribute('level') + ")" + f.getAttribute('name')) + opts.append("(" + f.get('level') + ")" + f.get('name')) dlg = wx.SingleChoiceDialog(self,'Choose Spell','Spells',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('gift') + self.n_list = self.xml.findall('gift') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_spells() dlg.Destroy() def on_refresh_spells( self, evt ): - f_list = self.master_dom.getElementsByTagName('gift') + f_list = self.xml.findall('gift') for spell in f_list: - spell.setAttribute( 'used', '0' ) + spell.set( 'used', '0' ) def on_size(self,event): s = self.GetClientSizeTuple() @@ -3115,12 +3034,12 @@ self.root = getRoot(self) #a 1.5002 self.root.powers = self #a 1.6009 - node_list = self.master_dom.getElementsByTagName( 'power' ) + node_list = self.xml.findall( 'power' ) self.powers = {} tree = self.tree icons = self.tree.icons for n in node_list: - name = n.getAttribute('name') + name = n.get('name') self.powers[ name ] = n new_tree_node = tree.AppendItem( self.mytree_node, name, icons['gear'], icons['gear'] ) @@ -3135,11 +3054,11 @@ if item == self.mytree_node: dnd3e_char_child.on_ldclick( self, evt ) else: - level = int(self.powers[ name ].getAttribute( 'level' )) - descr = self.powers[ name ].getAttribute( 'desc' ) + level = int(self.powers[ name ].get( 'level' )) + descr = self.powers[ name ].get( 'desc' ) #use can be removed -mgt - #use = self.powers[ name ].getAttribute( 'used' ) - points = self.powers[ name ].getAttribute( 'point' ) + #use = self.powers[ name ].get( 'used' ) + points = self.powers[ name ].get( 'point' ) #cpp and fre are strings without the eval -mgt cpp = eval(self.root.pp.get_char_pp('current1')) #a 1.5002 fre = eval(self.root.pp.get_char_pp('free')) #a 1.5002 @@ -3186,9 +3105,9 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('power') + node_list = self.xml.findall('power') for n in node_list: - name = n.getAttribute('name') + name = n.get('name') new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) tree.SetPyData(new_tree_node,self) self.powers[name]=n @@ -3200,9 +3119,9 @@ def tohtml(self): html_str = "
Powers

" - n_list = self.master_dom._get_childNodes() + n_list = self.xml.getchildren() for n in n_list: - html_str += "(" + n.getAttribute('level') + ") " + n.getAttribute('name')+ ", " + html_str += "(" + n.get('level') + ") " + n.get('name')+ ", " html_str = html_str[:len(html_str)-2] + "
" return html_str @@ -3210,7 +3129,7 @@ class power_panel(wx.Panel): def __init__(self, parent, handler): #m 1.5015 corrected typo, was Pionic. - pname = handler.master_dom.setAttribute("name", 'Psionic Powers') + pname = handler.xml.set("name", 'Psionic Powers') self.hparent = handler #a 1.5002 allow ability to run up tree. In this #a 1.5002 case, we need the functional parent, not the invoking parent. self.root = getRoot(self) #a (debug) 1.5002,1.5014 @@ -3239,9 +3158,9 @@ self.Bind(wx.EVT_BUTTON, self.on_add, id=20) self.Bind(wx.EVT_BUTTON, self.on_refresh_powers, id=30) self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - n_list = handler.master_dom._get_childNodes() + n_list = handler.xml.getchildren() self.n_list = n_list - self.master_dom = handler.master_dom + self.xml = handler.xml self.grid.CreateGrid(len(n_list),5,1) self.grid.SetRowLabelSize(0) self.grid.SetColLabelValue(0,"PP") @@ -3259,17 +3178,17 @@ col = evt.GetCol() value = self.grid.GetCellValue(row,col) """if col == 0: - self.n_list[row].setAttribute('memrz',value)""" + self.n_list[row].set('memrz',value)""" def refresh_row(self,i): power = self.n_list[i] - point = power.getAttribute('point') - name = power.getAttribute('name') - type = power.getAttribute('desc') - test = power.getAttribute('test') - level = power.getAttribute('level') + point = power.get('point') + name = power.get('name') + type = power.get('desc') + test = power.get('test') + level = power.get('level') self.grid.SetCellValue(i,0,point) self.grid.SetReadOnly(i,0) self.grid.SetCellValue(i,1,level) @@ -3286,34 +3205,33 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - - def on_add(self,evt): + self.xml.remove(self.n_list[i]) + + def on_add(self,evt): + debug() if not self.temp_dom: - tmp = open(dir_struct["dnd3e"]+"dnd3epowers.xml","r") - - xml_dom = parseXml_with_dlg(self,tmp.read()) - xml_dom = xml_dom._get_firstChild() - tmp.close() - self.temp_dom = xml_dom - f_list = self.temp_dom.getElementsByTagName('power') + tree = parse(dir_struct["dnd3e"]+"dnd3epowers.xml") + xml_dom = tree.getroot() + self.temp_dom = xml_dom + debug(self.temp) + f_list = self.temp_dom.findall('power') opts = [] #lvl = int(dnd3e_char_child.get_char_lvl('level')) #castlvl = lvl / 2 for f in f_list: - spelllvl = f.getAttribute('level') + spelllvl = f.get('level') #if spelllvl <= "1": - # opts.append("(" + f.getAttribute('level') + ") - " + f.getAttribute('name') + " - " + f.getAttribute('test')) + # opts.append("(" + f.get('level') + ") - " + f.get('name') + " - " + f.get('test')) #else: # if eval('%d >= %s' %(castlvl, spelllvl)): - opts.append("(" + f.getAttribute('level') + ") - " + - f.getAttribute('name') + " - " + f.getAttribute('test')) + opts.append("(" + f.get('level') + ") - " + + f.get('name') + " - " + f.get('test')) dlg = wx.SingleChoiceDialog(self,'Choose Power','Powers',opts) if dlg.ShowModal() == wx.ID_OK: i = dlg.GetSelection() - new_node = self.master_dom.appendChild(f_list[i].cloneNode(False)) + new_node = self.xml.append(f_list[i]) self.grid.AppendRows(1) - self.n_list = self.master_dom.getElementsByTagName('power') + self.n_list = self.xml.findall('power') self.refresh_row(self.grid.GetNumberRows()-1) self.handler.refresh_powers() dlg.Destroy() @@ -3323,8 +3241,8 @@ for i in range(rows): if self.grid.IsInSelection(i,0): self.grid.DeleteRows(i) - self.master_dom.removeChild(self.n_list[i]) - self.n_list = self.master_dom.getElementsByTagName('weapon') + self.xml.remove(self.n_list[i]) + self.n_list = self.xml.findall('weapon') self.handler.refresh_powers() def on_refresh_powers( self, evt ): @@ -3380,25 +3298,25 @@ #m 1.6010 rearanged everything below to "return html_str" html_str += "" html_str += "Max:" - html_str += ""+self.master_dom.getAttribute('max1')+"" + html_str += ""+self.xml.get('max1')+"" html_str += "Max Talents/day:" - html_str += ""+self.master_dom.getAttribute('maxfree')+"" + html_str += ""+self.xml.get('maxfree')+"" html_str += "" html_str += "Current:" - html_str += ""+self.master_dom.getAttribute('current1')+"" + html_str += ""+self.xml.get('current1')+"" html_str += "Current Talents/day:" - html_str += ""+self.master_dom.getAttribute('free')+"" + html_str += ""+self.xml.get('free')+"" html_str += "" return html_str def get_char_pp( self, attr ): - pp = self.master_dom.getAttribute(attr) + pp = self.xml.get(attr) #print "dnd3epp -get_char_pp: attr,pp",attr,pp return pp def set_char_pp( self, attr, evl ): qSub = str(evl) #a 1.5014 must force it to be a string for next call. - self.master_dom.setAttribute(attr, qSub) + self.xml.set(attr, qSub) #This function needs to be looked at the idea is to refresh the power panel #But it causes a seg fault when you refresh from powers -mgt #if self.ppPanel: #a 1.5015 @@ -3412,33 +3330,33 @@ #a 1.5002 we need the functional parent, not the invoking parent. self.hparent.ppPanel=self #a 1.5xx - pname = handler.master_dom.setAttribute("name", 'PowerPoints') + pname = handler.xml.set("name", 'PowerPoints') self.sizer = wx.FlexGridSizer(2, 4, 2, 2) # rows, cols, hgap, vgap - self.master_dom = handler.master_dom + self.xml = handler.xml self.static1= wx.StaticText(self, -1, "PP Current:") #a 1.5015 self.dyn1= wx.TextCtrl(self, PP_CUR, - self.master_dom.getAttribute('current1')) #a 1.5015 + self.xml.get('current1')) #a 1.5015 self.dyn3= wx.TextCtrl(self, PP_FRE, - self.master_dom.getAttribute('free')) #a 1.5015 + self.xml.get('free')) #a 1.5015 # self.sizer.AddMany([ (wx.StaticText(self, -1, "PP Current:"), #d 1.5015 # 0, wx.ALIGN_CENTER_VERTICAL), # (wx.TextCtrl(self, PP_CUR, #d 1.5015 -# self.master_dom.getAttribute('current1')), 0, wx.EXPAND), +# self.xml.get('current1')), 0, wx.EXPAND), self.sizer.AddMany([ (self.static1, 0, wx.ALIGN_CENTER_VERTICAL), (self.dyn1, 0, wx.EXPAND), (wx.StaticText(self, -1, "PP Max:"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, PP_MAX, - self.master_dom.getAttribute('max1')), 0, wx.EXPAND), + self.xml.get('max1')), 0, wx.EXPAND), (wx.StaticText(self, -1, "Current Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), (self.dyn3, 0, wx.EXPAND), #a 1.5015 # (wx.TextCtrl(self, PP_FRE, -# self.master_dom.getAttribute('free')), 0, wx.EXPAND),#d 1.5015 +# self.xml.get('free')), 0, wx.EXPAND),#d 1.5015 (wx.StaticText(self, -1, "Max Free Talants per day:"), 0, wx.ALIGN_CENTER_VERTICAL), (wx.TextCtrl(self, PP_MFRE, - self.master_dom.getAttribute('maxfree')), 0, wx.EXPAND), + self.xml.get('maxfree')), 0, wx.EXPAND), ]) self.sizer.AddGrowableCol(1) @@ -3455,13 +3373,13 @@ def on_text(self,evt): id = evt.GetId() if id == PP_CUR: - self.master_dom.setAttribute('current1',evt.GetString()) + self.xml.set('current1',evt.GetString()) elif id == PP_MAX: - self.master_dom.setAttribute('max1',evt.GetString()) + self.xml.set('max1',evt.GetString()) elif id == PP_FRE: - self.master_dom.setAttribute('free',evt.GetString()) + self.xml.set('free',evt.GetString()) elif id == PP_MFRE: - self.master_dom.setAttribute('maxfree',evt.GetString()) + self.xml.set('maxfree',evt.GetString()) def on_size(self,evt): s = self.GetClientSizeTuple() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/forms.py --- a/orpg/gametree/nodehandlers/forms.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/forms.py Tue Nov 10 14:11:28 2009 -0600 @@ -28,8 +28,8 @@ __version__ = "$Id: forms.py,v 1.53 2007/04/21 23:00:51 digitalxero Exp $" -from containers import * -import orpg.minidom as minidom +from containers import * +import orpg.minidom as minidom from orpg.orpg_xml import xml from wx.lib.scrolledpanel import ScrolledPanel @@ -39,10 +39,8 @@ #if it was an int already, nothing changes. The difference between 1.0 #and 1, i.e. between ints and floats, is potentially dangerous when we #use str() on it, but it seems to work fine right now. - if b: - return 1 - else: - return 0 + if b: return 1 + else: return 0 ################################# ## form container @@ -54,23 +52,16 @@

""" - - def __init__(self,xml_dom,tree_node): - container_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + container_handler.__init__(self, xml, tree_node) def load_children(self): self.atts = None - children = self.master_dom._get_childNodes() - for c in children: - if c._get_tagName() == "form": - self.atts = c - else: - self.tree.load_xml(c,self.mytree_node) - if not self.atts: - elem = minidom.Element('form') - elem.setAttribute("width","400") - elem.setAttribute("height","600") - self.atts = self.master_dom.appendChild(elem) + for child_xml in self.xml: + if child_xml.tag == "form": self.xml.remove(child_xml) + elif child_xml: self.tree.load_xml(child_xml, self.mytree_node) + if not self.xml.get('width'): self.xml.set('width', '400') + if not self.xml.get('height'): self.xml.set('height', '600') def get_design_panel(self,parent): return form_edit_panel(parent,self) @@ -82,32 +73,22 @@ # make sure its a contorl node container_handler.on_drop(self,evt) - class form_panel(ScrolledPanel): def __init__(self, parent, handler): ScrolledPanel.__init__(self, parent, wx.ID_ANY, style=wx.NO_BORDER|wx.VSCROLL|wx.HSCROLL) - self.height = int(handler.atts.getAttribute("height")) - self.width = int(handler.atts.getAttribute("width")) - - + self.height = int(handler.xml.get("height")) + self.width = int(handler.xml.get("width")) self.SetSize((0,0)) self.handler = handler self.parent = parent self.main_sizer = wx.BoxSizer(wx.VERTICAL) - tree = self.handler.tree - child = tree.GetFirstChild(handler.mytree_node) - if child[0].IsOk(): - handler.traverse(child[0], self.create_child_wnd, 0, None, False) - + handler.tree.traverse(handler.mytree_node, self.create_child_wnd, None, False) self.SetSizer(self.main_sizer) self.SetAutoLayout(True) - self.SetupScrolling() - parent.SetSize(self.GetSize()) self.Fit() - def SetSize(self, xy): (x, y) = self.GetSize() (nx, ny) = xy @@ -116,16 +97,14 @@ y += ny+11 ScrolledPanel.SetSize(self, (x, y)) - - def create_child_wnd(self, obj, evt): - panel = obj.get_use_panel(self) - size = obj.get_size_constraint() + def create_child_wnd(self, treenode, evt): + node = self.handler.tree.GetPyData(treenode) + panel = node.get_use_panel(self) + size = node.get_size_constraint() if panel: self.main_sizer.Add(panel, size, wx.EXPAND) self.main_sizer.Add(wx.Size(10,10)) - - F_HEIGHT = wx.NewId() F_WIDTH = wx.NewId() class form_edit_panel(wx.Panel): @@ -134,9 +113,9 @@ self.handler = handler sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Form Properties"), wx.VERTICAL) wh_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')), - F_HEIGHT : wx.TextCtrl(self, F_HEIGHT, handler.atts.getAttribute('height')), - F_WIDTH : wx.TextCtrl(self, F_WIDTH, handler.atts.getAttribute('width')) + self.text = { P_TITLE : wx.TextCtrl(self, P_TITLE, handler.xml.get('name')), + F_HEIGHT : wx.TextCtrl(self, F_HEIGHT, handler.xml.get('height')), + F_WIDTH : wx.TextCtrl(self, F_WIDTH, handler.xml.get('width')) } wh_sizer.Add(wx.StaticText(self, -1, "Width:"), 0, wx.ALIGN_CENTER) @@ -166,21 +145,13 @@ txt = self.text[id].GetValue() if not len(txt): return if id == P_TITLE: - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) elif id == F_HEIGHT or id == F_WIDTH: - try: - int(txt) - except: - return 0 - if id == F_HEIGHT: - self.handler.atts.setAttribute("height",txt) - elif id == F_WIDTH: - self.handler.atts.setAttribute("width",txt) - - - - + try: int(txt) + except: return 0 + if id == F_HEIGHT: self.handler.xml.set("height",txt) + elif id == F_WIDTH: self.handler.xml.set("width",txt) ########################## ## control handler @@ -189,13 +160,12 @@ """ A nodehandler for form controls. """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self, xml, tree_node): + node_handler.__init__(self, xml, tree_node) def get_size_constraint(self): return 0 - ########################## ## textctrl handler ########################## @@ -210,16 +180,12 @@ Text In Node """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.text_elem = self.master_dom.getElementsByTagName('text')[0] - self.text = component.get('xml').safe_get_text_node(self.text_elem) - if self.text_elem.getAttribute("send_button") == "": - self.text_elem.setAttribute("send_button","0") - if self.text_elem.getAttribute("raw_mode") == "": - self.text_elem.setAttribute("raw_mode","0") - if self.text_elem.getAttribute("hide_title") == "": - self.text_elem.setAttribute("hide_title","0") + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.text_elem = self.xml.find('text') + if self.text_elem.get("send_button") == "": self.text_elem.set("send_button","0") + if self.text_elem.get("raw_mode") == "": self.text_elem.set("raw_mode","0") + if self.text_elem.get("hide_title") == "": self.text_elem.set("hide_title","0") def get_design_panel(self,parent): return textctrl_edit_panel(parent,self) @@ -228,28 +194,32 @@ return text_panel(parent,self) def get_size_constraint(self): - return int(self.text_elem.getAttribute("multiline")) + return int(self.text_elem.get("multiline",0)) def is_multi_line(self): - return int(self.text_elem.getAttribute("multiline")) + return int(self.text_elem.get("multiline",0)) def is_raw_send(self): - return int(self.text_elem.getAttribute("raw_mode")) + return int(self.text_elem.get("raw_mode",0)) def is_hide_title(self): - return int(self.text_elem.getAttribute("hide_title")) + return int(self.text_elem.get("hide_title",0)) def has_send_button(self): - return int(self.text_elem.getAttribute("send_button")) - + return int(self.text_elem.get("send_button",0)) def tohtml(self): - txt = self.text._get_nodeValue() + txt = self.get_value() txt = string.replace(txt,'\n',"
") if not self.is_hide_title(): - txt = ""+self.master_dom.getAttribute("name")+": "+txt + txt = ""+self.xml.get("name")+": "+txt return txt + def get_value(self): + return self.text_elem.text + + def set_value(self, new_value): + self.text_elem.text = str(new_value) FORM_TEXT_CTRL = wx.NewId() FORM_SEND_BUTTON = wx.NewId() @@ -268,9 +238,10 @@ text_style = 0 sizer = wx.BoxSizer(wx.HORIZONTAL) - txt = handler.text._get_nodeValue() + txt = handler.get_value() + if txt == None: txt = '' self.text = wx.TextCtrl(self, FORM_TEXT_CTRL, txt, style=text_style) - sizer.Add(wx.StaticText(self, -1, handler.master_dom.getAttribute('name')+": "), 0, sizer_style) + sizer.Add(wx.StaticText(self, -1, handler.xml.get('name')+": "), 0, sizer_style) sizer.Add(wx.Size(5,0)) sizer.Add(self.text, 1, sizer_style) @@ -283,24 +254,25 @@ parent.SetSize(self.GetBestSize()) self.Bind(wx.EVT_TEXT, self.on_text, id=FORM_TEXT_CTRL) - self.Bind(wx.EVT_BUTTON, self.on_send, id=FORM_SEND_BUTTON) - - def on_text(self,evt): + self.Bind(wx.EVT_BUTTON, self.on_send, id=FORM_SEND_BUTTON) + + def on_text(self, evt): + debug() txt = self.text.GetValue() - txt = xml.strip_text(txt) - self.handler.text._set_nodeValue(txt) + #txt = strip_text(txt) ##Does not seem to exist. + self.handler.text_elem.text = txt - def on_send(self,evt): - txt = self.text.GetValue() + def on_send(self, evt): + txt = self.text.GetValue() + txt = self.chat.ParseMap(txt, self.handler.xml) if not self.handler.is_raw_send(): - #self.chat.ParsePost(self.tohtml(),True,True) - self.chat.ParsePost(self.handler.tohtml(),True,True) + self.chat.ParsePost(self.handler.tohtml(), True, True) return 1 actionlist = txt.split("\n") for line in actionlist: if(line != ""): if line[0] != "/": ## it's not a slash command - self.chat.ParsePost(line,True,True) + self.chat.ParsePost(line, True, True) else: action = line self.chat.chat_cmds.docmd(action) @@ -318,7 +290,7 @@ self.handler = handler sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Text Properties"), wx.VERTICAL) - self.title = wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')) + self.title = wx.TextCtrl(self, P_TITLE, handler.xml.get('name')) self.multi = wx.CheckBox(self, F_MULTI, " Multi-Line") self.multi.SetValue(handler.is_multi_line()) self.raw_send = wx.CheckBox(self, F_RAW_SEND, " Send as Macro") @@ -344,7 +316,7 @@ sizer_style=wx.EXPAND text_style = 0 multi = 0 - self.text = wx.TextCtrl(self, F_TEXT, handler.text._get_nodeValue(),style=text_style) + self.text = wx.TextCtrl(self, F_TEXT, handler.get_value(),style=text_style) sizer.Add(wx.Size(5,0)) sizer.Add(self.text, multi, sizer_style) self.SetSizer(sizer) @@ -362,28 +334,24 @@ if id == P_TITLE: txt = self.title.GetValue() if not len(txt): return - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) if id == F_TEXT: txt = self.text.GetValue() - txt = xml.strip_text(txt) - self.handler.text._set_nodeValue(txt) + #txt = strip_text(txt) ##Does not seem to exist. + self.handler.text_elem.text = txt def on_button(self,evt): - self.handler.text_elem.setAttribute("multiline",str(bool2int(evt.Checked()))) + self.handler.text_elem.set("multiline",str(bool2int(evt.Checked()))) def on_raw_button(self,evt): - self.handler.text_elem.setAttribute("raw_mode",str(bool2int(evt.Checked()))) + self.handler.text_elem.set("raw_mode",str(bool2int(evt.Checked()))) def on_hide_button(self,evt): - self.handler.text_elem.setAttribute("hide_title",str(bool2int(evt.Checked()))) + self.handler.text_elem.set("hide_title",str(bool2int(evt.Checked()))) def on_send_button(self,evt): - self.handler.text_elem.setAttribute("send_button",str(bool2int(evt.Checked()))) - - - - + self.handler.text_elem.set("send_button",str(bool2int(evt.Checked()))) ####################### @@ -409,14 +377,14 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.list = self.master_dom.getElementsByTagName('list')[0] - self.options = self.list.getElementsByTagName('option') - if self.list.getAttribute("send_button") == "": - self.list.setAttribute("send_button","0") - if self.list.getAttribute("hide_title") == "": - self.list.setAttribute("hide_title","0") + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.list = self.xml.find('list') + self.options = self.list.findall('option') + if self.list.get("send_button") == "": + self.list.set("send_button","0") + if self.list.get("hide_title") == "": + self.list.set("hide_title","0") def get_design_panel(self,parent): return listbox_edit_panel(parent,self) @@ -425,24 +393,24 @@ return listbox_panel(parent,self) def get_type(self): - return int(self.list.getAttribute("type")) + return int(self.list.get("type")) def set_type(self,type): - self.list.setAttribute("type",str(type)) + self.list.set("type",str(type)) def is_hide_title(self): - return int(self.list.getAttribute("hide_title")) + return int(self.list.get("hide_title", 0)) # single selection methods def get_selected_node(self): for opt in self.options: - if opt.getAttribute("selected") == "1": return opt + if opt.get("selected") == "1": return opt return None def get_selected_index(self): i = 0 for opt in self.options: - if opt.getAttribute("selected") == "1": + if opt.get("selected") == "1": return i i += 1 return 0 @@ -450,95 +418,87 @@ def get_selected_text(self): node = self.get_selected_node() if node: - return component.get('xml').safe_get_text_node(node)._get_nodeValue() + return node.text else: return "" # mult selection methods - def get_selections(self): opts = [] for opt in self.options: - if opt.getAttribute("selected") == "1": + if opt.get("selected") == "1": opts.append(opt) return opts def get_selections_text(self): opts = [] for opt in self.options: - if opt.getAttribute("selected") == "1": - opts.append(component.get('xml').safe_get_text_node(opt)._get_nodeValue()) + if opt.get("selected") == "1": + opts.append(opt.text) return opts def get_selections_index(self): opts = [] i = 0 for opt in self.options: - if opt.getAttribute("selected") == "1": + if opt.get("selected") == "1": opts.append(i) i += 1 return opts # setting selection method - def set_selected_node(self,index,selected=1): if self.get_type() != L_CHECK: self.clear_selections() - self.options[index].setAttribute("selected", str(bool2int(selected))) + self.options[index].set("selected", str(bool2int(selected))) def clear_selections(self): for opt in self.options: - opt.setAttribute("selected","0") + opt.set("selected","0") # misc methods - def get_options(self): opts = [] - for opt in self.options: - opts.append(component.get('xml').safe_get_text_node(opt)._get_nodeValue()) + for opt in self.options: opts.append(opt.text) return opts def get_option(self,index): - return component.get('xml').safe_get_text_node(self.options[index])._get_nodeValue() + return self.options[index].text def add_option(self,opt): - elem = minidom.Element('option') - elem.setAttribute("value","0") - elem.setAttribute("selected","0") - t_node = minidom.Text(opt) - t_node = elem.appendChild(t_node) - self.list.appendChild(elem) - self.options = self.list.getElementsByTagName('option') + elem = Element('option') + elem.set("value","0") + elem.set("selected","0") + elem.text = opt + self.list.append(elem) + self.options = self.list.findall('option') def remove_option(self,index): - self.list.removeChild(self.options[index]) - self.options = self.list.getElementsByTagName('option') + self.list.remove(self.options[index]) + self.options = self.list.findall('option') def edit_option(self,index,value): - component.get('xml').safe_get_text_node(self.options[index])._set_nodeValue(value) + self.options[index].text = value def has_send_button(self): - if self.list.getAttribute("send_button") == '0': - return False - else: - return True + if self.list.get("send_button") == '0': return False + else: return True def get_size_constraint(self): - if self.get_type() == L_DROP: - return 0 - else: - return 1 + if self.get_type() == L_DROP: return 0 + else: return 1 def tohtml(self): opts = self.get_selections_text() text = "" - if not self.is_hide_title(): - text = ""+self.master_dom.getAttribute("name")+": " + if not self.is_hide_title(): text = ""+self.xml.get("name")+": " comma = ", " text += comma.join(opts) return text + def get_value(self): + return "\n".join(self.get_selections_text()) F_LIST = wx.NewId() @@ -553,7 +513,7 @@ opts = handler.get_options() cur_opt = handler.get_selected_text() type = handler.get_type() - label = handler.master_dom.getAttribute('name') + label = handler.xml.get('name') if type == L_DROP: self.list = wx.ComboBox(self, F_LIST, cur_opt, choices=opts, style=wx.CB_READONLY) @@ -635,7 +595,7 @@ self.handler = handler sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "List Box Properties"), wx.VERTICAL) - self.text = wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')) + self.text = wx.TextCtrl(self, P_TITLE, handler.xml.get('name')) opts = handler.get_options() self.listbox = wx.ListBox(self, F_LIST, choices=opts, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_NEEDED_SB) @@ -717,15 +677,14 @@ txt = self.text.GetValue() if not len(txt): return if id == P_TITLE: - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) def on_send_button(self,evt): - self.handler.list.setAttribute("send_button", str( bool2int(evt.Checked()) )) + self.handler.list.set("send_button", str( bool2int(evt.Checked()) )) def on_hide_button(self,evt): - print "hide_title, " + str(bool2int(evt.Checked())) - self.handler.list.setAttribute("hide_title", str( bool2int(evt.Checked()) )) + self.handler.list.set("hide_title", str( bool2int(evt.Checked()) )) ############################### @@ -738,12 +697,12 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.link = self.master_dom._get_firstChild() + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.link = self.xml[0] def on_use(self,evt): - href = self.link.getAttribute("href") + href = self.link.get("href") wb = webbrowser.get() wb.open(href) @@ -754,14 +713,14 @@ return link_panel(parent,self) def tohtml(self): - href = self.link.getAttribute("href") - title = self.master_dom.getAttribute("name") + href = self.link.get("href") + title = self.xml.get("name") return ""+title+"" class link_panel(wx.StaticText): def __init__(self,parent,handler): self.handler = handler - label = handler.master_dom.getAttribute('name') + label = handler.xml.get('name') wx.StaticText.__init__(self,parent,-1,label) self.SetForegroundColour(wx.BLUE) self.Bind(wx.EVT_LEFT_DOWN, self.handler.on_use) @@ -776,8 +735,8 @@ sizer = wx.StaticBoxSizer(wx.StaticBox(self, -1, "Link Properties"), wx.VERTICAL) self.text = {} - self.text[P_TITLE] = wx.TextCtrl(self, P_TITLE, handler.master_dom.getAttribute('name')) - self.text[P_URL] = wx.TextCtrl(self, P_URL, handler.link.getAttribute('href')) + self.text[P_TITLE] = wx.TextCtrl(self, P_TITLE, handler.xml.get('name')) + self.text[P_URL] = wx.TextCtrl(self, P_URL, handler.link.get('href')) sizer.Add(wx.StaticText(self, -1, "Title:"), 0, wx.EXPAND) sizer.Add(self.text[P_TITLE], 0, wx.EXPAND) @@ -793,10 +752,10 @@ txt = self.text[id].GetValue() if not len(txt): return if id == P_TITLE: - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) elif id == P_URL: - self.handler.link.setAttribute('href',txt) + self.handler.link.set('href',txt) ########################## ## webimg node handler @@ -807,21 +766,20 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.link = self.master_dom._get_firstChild() + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.link = self.xml[0] def get_design_panel(self,parent): return link_edit_panel(parent,self) def get_use_panel(self,parent): - img = img_helper().load_url(self.link.getAttribute("href")) - #print img + img = img_helper().load_url(self.link.get("href")) if not img is None: return wx.StaticBitmap(parent,-1,img,size= wx.Size(img.GetWidth(),img.GetHeight())) return wx.EmptyBitmap(1, 1) def tohtml(self): - href = self.link.getAttribute("href") - title = self.master_dom.getAttribute("name") + href = self.link.get("href") + title = self.xml.get("name") return "+title+" diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/map_miniature_nodehandler.py --- a/orpg/gametree/nodehandlers/map_miniature_nodehandler.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/map_miniature_nodehandler.py Tue Nov 10 14:11:28 2009 -0600 @@ -42,17 +42,15 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) self.mapper = component.get("map") self.session = component.get("session") - self.miniature_dom = self.master_dom.getElementsByTagName("miniature") - if self.miniature_dom: - self.miniature_dom = self.miniature_dom[0] # convert to scalar - + self.miniature_xml = self.xml.find("miniature") + def get_scaled_bitmap(self,x,y): my_mini_msg = mini_msg() - my_mini_msg.init_from_dom(self.miniature_dom) + my_mini_msg.init_from_dom(self.miniature_xml) bmp = None path = my_mini_msg.get_prop("path") @@ -61,11 +59,11 @@ if ImageHandler.Cache.has_key(path): bmp = ImageHandler.Cache[path] else: - #bmp = ImageHandler.directLoad(path, 'miniature', id) + #bmp = ImageHandler.directLoad(path, 'miniature', id) bmp = ImageHandler.directLoad(path)# Old Code TaS. if bmp: - img = wx.ImageFromMime(ImageHandler.Cache[path][1], ImageHandler.Cache[path][2]) + img = wx.ImageFromMime(ImageHandler.Cache[path][1], ImageHandler.Cache[path][2]) #img = wx.ImageFromBitmap(bmp) scaled_img = img.Scale(x,y) scaled_bmp = scaled_img.ConvertToBitmap() @@ -89,9 +87,9 @@ def get_miniature_XML(self): my_mini_msg = mini_msg() - my_mini_msg.init_from_dom(self.miniature_dom) + my_mini_msg.init_from_dom(self.miniature_xml) my_mini_msg.init_prop("id",self.session.get_next_id()) - label = self.master_dom.getAttribute("name") + label = self.xml.get("name") my_mini_msg.init_prop("label",label) new_xml = my_mini_msg.get_all_xml() return new_xml @@ -109,14 +107,14 @@ dc.SetUserScale(grid.mapscale, grid.mapscale) pos = evt.GetLogicalPosition(dc) try: - align = int(self.miniature_dom.getAttribute("align")) - width = int(self.miniature_dom.getAttribute("width")) - height = int(self.miniature_dom.getAttribute("height")) + align = int(self.xml.get("align")) + width = int(self.xml.get("width")) + height = int(self.xml.get("height")) pos = grid.get_snapped_to_pos(pos, align, width, height) except: pass - self.miniature_dom.setAttribute("posx", str(pos.x)) - self.miniature_dom.setAttribute("posy", str(pos.y)) + self.miniature_xml.set("posx", str(pos.x)) + self.miniature_xml.set("posy", str(pos.y)) new_xml = self.get_to_map_XML() if (self.session.my_role() != self.session.ROLE_GM) and (self.session.my_role() != self.session.ROLE_PLAYER): component.get("chat").InfoPost("You must be either a player or GM to use the miniature Layer") @@ -133,7 +131,7 @@ def tohtml(self): html_str = "\n" - html_str += "
" - html_str += "
" + html_str += "
" html_str += "
" + self.master_dom.getAttribute("name") + "
" - return html_str + html_str += "
" + self.xml.get("name") + "
" + return html_str \ No newline at end of file diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/minilib.py --- a/orpg/gametree/nodehandlers/minilib.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/minilib.py Tue Nov 10 14:11:28 2009 -0600 @@ -35,8 +35,8 @@ from orpg.dirpath import dir_struct import string import map_miniature_nodehandler -import orpg.mapper.map_msg -import orpg.minidom as minidom +import orpg.mapper.map_msg +import orpg.minidom as minidom # import scriptkit # Constants @@ -68,15 +68,13 @@ </nodehandler> """ - def __init__(self, xml_dom, tree_node): + def __init__(self, xml, tree_node): """Instantiates the class, and sets all vars to their default state """ - node_handler.__init__(self, xml_dom, tree_node) - + node_handler.__init__(self, xml, tree_node) self.myeditor = None self.mywindow = None self.tree_node = tree_node - # self.xml_dom = xml_dom self.update_leaves() self.sanity_check_nodes() @@ -101,17 +99,15 @@ expecting for a given column. """ str = '' - list = self.master_dom.getElementsByTagName(TAG_MINIATURE) - str += "" - for mini in list: - url = mini.getAttribute(ATTRIBUTE_URL) - label = mini.getAttribute(ATTRIBUTE_NAME) + str += "" + for mini in self.xml.findall(TAG_MINIATURE): + url = mini.get(ATTRIBUTE_URL) + label = mini.get(ATTRIBUTE_NAME) flag = 0 try: - flag = eval( mini.getAttribute(ATTRIBUTE_UNIQUE) ) + flag = eval( mini.get(ATTRIBUTE_UNIQUE) ) except: pass - show = 'yes' if flag: show = 'no' @@ -124,7 +120,6 @@ """ % ( label, url, url, show ) str += "
LabelImageURLUnique
LabelImageURLUnique
" - print str return str def html_view( self ): @@ -136,59 +131,55 @@ drag_obj = self.tree.drag_obj if drag_obj == self or self.tree.is_parent_node( self.mytree_node, drag_obj.mytree_node ): return - if isinstance( drag_obj, minilib_handler ): - item = self.tree.GetSelection() - name = self.tree.GetItemText( item ) - if isinstance( drag_obj, map_miniature_nodehandler.map_miniature_handler ): - xml_dom = self.tree.drag_obj.master_dom#.delete() - obj = xml_dom.firstChild - print obj.getAttributeKeys() + elif isinstance( drag_obj, map_miniature_nodehandler.map_miniature_handler ): + drop_xml = self.tree.drag_obj.xml#.delete() + obj = drop_xml[0] dict = {} unique = '' - for attrib in obj.getAttributeKeys(): + for attrib in obj.keys(): key = TO_MINILIB_MAP.get( attrib, attrib ) if key != None: - dict[ key ] = obj.getAttribute( attrib ) + dict[ key ] = obj.get( attrib ) dict[ ATTRIBUTE_UNIQUE ] = unique self.new_mini( dict ) + else: + node_handler.on_drop(self, evt) def new_mini( self, data={}, add=1 ): - mini = minidom.Element( TAG_MINIATURE ) + mini = Element( TAG_MINIATURE ) for key in data.keys(): - mini.setAttribute( key, data[ key ] ) + mini.set( key, data[ key ] ) for key in CORE_ATTRIBUTES: - if mini.getAttribute( key ) == '': - mini.setAttribute( key, '0' ) + if mini.get( key ) == '': + mini.set( key, '0' ) if add: self.add_mini( mini ) self.add_leaf( mini ) return mini def add_mini( self, mini ): - self.master_dom.appendChild( mini ) + self.xml.append( mini ) def add_leaf( self, mini, icon='gear' ): tree = self.tree icons = tree.icons - key = mini.getAttribute( ATTRIBUTE_NAME ) + key = mini.get( ATTRIBUTE_NAME ) self.mydata.append( mini ) def update_leaves( self ): self.mydata = [] - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: + for n in self.xml.findall(TAG_MINIATURE): self.add_leaf( n ) - def on_drag( self, evt ): print 'drag event caught' def send_mini_to_map( self, mini, count=1, addName=True ): if mini == None: return - if mini.getAttribute( ATTRIBUTE_URL ) == '' or mini.getAttribute( ATTRIBUTE_URL ) == 'http://': - self.chat.ParsePost( self.chat.colorize(self.chat.syscolor, '"%s" is not a valid URL, the mini "%s" will not be added to the map' % ( mini.getAttribute( ATTRIBUTE_URL ), mini.getAttribute( ATTRIBUTE_NAME ) )) ) + if mini.get( ATTRIBUTE_URL ) == '' or mini.get( ATTRIBUTE_URL ) == 'http://': + self.chat.ParsePost( self.chat.colorize(self.chat.syscolor, '"%s" is not a valid URL, the mini "%s" will not be added to the map' % ( mini.get( ATTRIBUTE_URL ), mini.get( ATTRIBUTE_NAME ) )) ) return session = component.get( COMPONENT_SESSION ) if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER): @@ -201,57 +192,50 @@ map.new_data( msg ) session.send( msg ) - def get_miniature_XML( self, mini, addName = True ): + def get_miniature_XML( self, mini_xml, addName = True ): msg = orpg.mapper.map_msg.mini_msg() map = component.get( COMPONENT_MAP ) session = component.get( COMPONENT_SESSION ) msg.init_prop( ATTRIBUTE_ID, session.get_next_id() ) - for k in mini.getAttributeKeys(): + msg.init_prop('selected', '1')# this will make the mini initially selected + for k in mini_xml.keys(): # translate our attributes to map attributes key = FROM_MINILIB_MAP.get( k, k ) if key != None: if not addName and k == 'name': pass else: - msg.init_prop( key, mini.getAttribute( k ) ) - unique = self.is_unique( mini ) + msg.init_prop( key, mini_xml.get( k ) ) + unique = self.is_unique( mini_xml ) if addName: - label = mini.getAttribute( ATTRIBUTE_NAME ) + label = mini_xml.get( ATTRIBUTE_NAME ) else: label = '' return msg.get_all_xml() def is_unique( self, mini ): - unique = mini.getAttribute( ATTRIBUTE_UNIQUE ) + unique = mini.get( ATTRIBUTE_UNIQUE ) val = 0 - try: - val = eval( unique ) - except: - val = len( unique ) + try: val = eval( unique ) + except: val = len( unique ) return val def sanity_check_nodes( self ): - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for node in nl: - if node.getAttribute( ATTRIBUTE_POSX ) == '': - node.setAttribute( ATTRIBUTE_POSX, '0' ) - if node.getAttribute( ATTRIBUTE_POSY ) == '': - node.setAttribute( ATTRIBUTE_POSY, '0' ) + for node in self.xml.findall(TAG_MINIATURE): + if node.get( ATTRIBUTE_POSX ) == '': node.set( ATTRIBUTE_POSX, '0' ) + if node.get( ATTRIBUTE_POSY ) == '': node.set( ATTRIBUTE_POSY, '0' ) def get_mini( self, index ): - try: - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - return nl[ index ] - except: - return None + try: return self.xml.findall(TAG_MINIATURE)[index] + except: return None class mini_handler( node_handler ): - def __init__( self, xml_dom, tree_node, handler ): - node_handler.__init__( self, xml_dom, tree_node) + def __init__( self, xml, tree_node, handler ): + node_handler.__init__( self, xml, tree_node) self.handler = handler def on_ldclick( self, evt ): - self.handler.send_mini_to_map( self.master_dom ) + self.handler.send_mini_to_map( self.xml ) def on_drop( self, evt ): pass @@ -313,17 +297,10 @@ def buildList( self ): """Returns a dictionary of label => game tree miniature DOM node mappings. """ - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) self.list = [] - for mini in list: - self.list.append( mini.getAttribute( ATTRIBUTE_NAME ) ) + for mini in self.handler.xml.findall(TAG_MINIATURE): + self.list.append( mini.get( ATTRIBUTE_NAME ) ) return self.list - # self.list = {} - # for mini in list: - # name = mini.getAttribute( ATTRIBUTE_NAME ) - # if name == '': - # name = self.map.canvas.get_label_from_url( mini.getAttribute( ATTRIBUTE_URL ) ) - # self.list[ name ] = mini def on_close(self, evt): self.frame.Close() @@ -454,9 +431,8 @@ self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.select_cell) def update_cols( self ): - nl = self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: - for k in n.getAttributeKeys(): + for n in self.handler.xml.findall(TAG_MINIATURE): + for k in n.keys(): if k not in self.keys: self.keys.append( k ) @@ -475,7 +451,7 @@ """Returns the list of 'miniature' DOM elements associated with this miniature library. """ - return self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) + return self.handler.xml.findall( TAG_MINIATURE ) def add_row( self, count = 1 ): """creates a new miniature node, and then adds it to the current @@ -486,7 +462,7 @@ ATTRIBUTE_NAME :' ', ATTRIBUTE_URL :'http://'} )# minidom.Element( TAG_MINIATURE ) self.update_all() - #self.handler.master_dom.appendChild( node ) + #self.handler.xml.append( node ) def del_row( self ): """deletes the miniature associated with the currently selected @@ -495,8 +471,8 @@ """ if self.selectedRow > -1: pos = self.selectedRow - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) - self.handler.master_dom.removeChild( list[pos] ) + list = self.handler.xml.findall(TAG_MINIATURE) + self.handler.xml.remove( list[pos] ) self.DeleteRows( pos, 1 ) list = self.getList() del list[ pos ] @@ -515,7 +491,7 @@ list = self.getList() count = 0 for n in list: - for k in n.getAttributeKeys(): + for k in n.keys(): if k not in self.keys: self.keys.append( k ) count = len( self.keys ) @@ -555,11 +531,8 @@ """ list = self.getList() item = list[ row ] - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) for key in self.keys: - self.GetTable().SetValue( row, self.keys.index( key ), item.getAttribute( key ) ) + self.GetTable().SetValue( row, self.keys.index( key ), item.get( key ) ) def update_data_row( self, row ): """Updates the DOM nodw 'row' with grid data from 'row' @@ -567,10 +540,4 @@ list = self.getList() item = list[ row ] for key in self.keys: - item.setAttribute( key, string.strip( self.GetTable().GetValue( row, self.keys.index( key ) ) ) ) - # item.setAttribute( ATTRIBUTE_NAME, string.strip( self.GetTable().GetValue( row, 0 ) ) ) - # item.setAttribute( ATTRIBUTE_URL, string.strip( self.GetTable().GetValue( row, 1 ) ) ) - # item.setAttribute( ATTRIBUTE_UNIQUE, string.strip( self.GetTable().GetValue( row, 2 ) ) ) - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) + item.set( key, string.strip( self.GetTable().GetValue( row, self.keys.index( key ) ) ) ) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/minilib.py~ --- a/orpg/gametree/nodehandlers/minilib.py~ Fri Sep 25 20:47:16 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,578 +0,0 @@ -# Copyright (C) 2000-2001 The OpenRPG Project -# -# openrpg-dev@lists.sourceforge.net -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# -- -# -# File: minilib.py -# Author: Ted Berg -# Maintainer: -# Version: -# $Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $ -# -# Description: nodehandler for a collection of miniatures. -# - -__version__ = "$Id: minilib.py,v 1.28 2007/04/22 22:00:18 digitalxero Exp $" - -"""Nodehandler for collections of miniatures. User can add, delete, edit -miniatures as sending them to the map singly or in batches. -""" -from core import * -import orpg.dirpath -import string -import map_miniature_nodehandler -import orpg.mapper.map_msg -from orpg.mapper.images import ImageHandler -# import scriptkit - -# Constants -TO_MINILIB_MAP = {'path':'url', 'label':'name', 'id':None, 'action':None} -FROM_MINILIB_MAP = {'url':'path', 'name':'label', 'unique':None} -CORE_ATTRIBUTES = ['name', 'url', 'unique', 'posy', 'posx', 'hide', 'face', 'heading', 'align', 'locked', 'width', 'height'] - -ATTRIBUTE_NAME = 'name' -ATTRIBUTE_URL = 'url' -ATTRIBUTE_UNIQUE = 'unique' -ATTRIBUTE_ID = 'id' -ATTRIBUTE_POSX = 'posx' -ATTRIBUTE_POSY = 'posy' - -TAG_MINIATURE = 'miniature' - -COMPONENT_MAP = 'map' -COMPONENT_SESSION = 'session' -# -# -# - -class minilib_handler( node_handler ): - """A nodehandler that manages a collection of miniatures for the - map. -
-        <nodehandler name='?' module='minilib' class='minilib_handler'>
-            <miniature name='?' url='?' unique='?'></miniature>
-        </nodehandler>
-    
- """ - def __init__(self, xml_dom, tree_node): - """Instantiates the class, and sets all vars to their default state - """ - node_handler.__init__(self, xml_dom, tree_node) - - self.myeditor = None - self.mywindow = None - self.tree_node = tree_node - # self.xml_dom = xml_dom - self.update_leaves() - self.sanity_check_nodes() - - def get_design_panel( self, parent ): - """returns an instance of the miniature library edit control ( see - on_design ). This is for use with the the 'edit multiple nodes in a - single frame' code. - """ - return minpedit( parent, self ) - - def get_use_panel( self, parent ): - """returns an instance of the miniature library view control ( see - on_use ). This is for use with the the 'view multiple nodes in a - single frame' code. - """ - return minilib_use_panel( parent, self ) - - def tohtml( self ): - """Returns an HTML representation of this node in string format. - The table columnwidths are currently being forced, as the wxHTML - widgets being used don't handle cells wider than the widgets are - expecting for a given column. - """ - str = '' - list = self.master_dom.getElementsByTagName(TAG_MINIATURE) - str += "" - for mini in list: - url = mini.getAttribute(ATTRIBUTE_URL) - label = mini.getAttribute(ATTRIBUTE_NAME) - flag = 0 - try: - flag = eval( mini.getAttribute(ATTRIBUTE_UNIQUE) ) - except: - pass - - show = 'yes' - if flag: - show = 'no' - - str += """ - - - - - """ % ( label, url, url, show ) - - str += "
LabelImageURLUnique
%s %s %s
" - print str - return str - - def html_view( self ): - """see to_html - """ - return self.tohtml() - - def on_drop(self, evt): - drag_obj = self.tree.drag_obj - if drag_obj == self or self.tree.is_parent_node( self.mytree_node, drag_obj.mytree_node ): - return - if isinstance( drag_obj, minilib_handler ): - item = self.tree.GetSelection() - name = self.tree.GetItemText( item ) - if isinstance( drag_obj, map_miniature_nodehandler.map_miniature_handler ): - xml_dom = self.tree.drag_obj.master_dom#.delete() - obj = xml_dom.firstChild - print obj.getAttributeKeys() - dict = {} - unique = '' - for attrib in obj.getAttributeKeys(): - key = TO_MINILIB_MAP.get( attrib, attrib ) - if key != None: - dict[ key ] = obj.getAttribute( attrib ) - dict[ ATTRIBUTE_UNIQUE ] = unique - self.new_mini( dict ) - - - def new_mini( self, data={}, add=1 ): - mini = minidom.Element( TAG_MINIATURE ) - for key in data.keys(): - mini.setAttribute( key, data[ key ] ) - for key in CORE_ATTRIBUTES: - if mini.getAttribute( key ) == '': - mini.setAttribute( key, '0' ) - if add: - self.add_mini( mini ) - self.add_leaf( mini ) - return mini - - def add_mini( self, mini ): - self.master_dom.appendChild( mini ) - - def add_leaf( self, mini, icon='gear' ): - tree = self.tree - icons = tree.icons - key = mini.getAttribute( ATTRIBUTE_NAME ) - self.mydata.append( mini ) - - def update_leaves( self ): - self.mydata = [] - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: - self.add_leaf( n ) - - - def on_drag( self, evt ): - print 'drag event caught' - - def send_mini_to_map( self, mini, count=1, addName=True ): - if mini == None: - return - if mini.getAttribute( ATTRIBUTE_URL ) == '' or mini.getAttribute( ATTRIBUTE_URL ) == 'http://': - self.chat.ParsePost( self.chat.colorize(self.chat.syscolor, '"%s" is not a valid URL, the mini "%s" will not be added to the map' % ( mini.getAttribute( ATTRIBUTE_URL ), mini.getAttribute( ATTRIBUTE_NAME ) )) ) - return - session = open_rpg.get_component( COMPONENT_SESSION ) - if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER): - open_rpg.get_component("chat").InfoPost("You must be either a player or GM to use the miniature Layer") - return - map = open_rpg.get_component(COMPONENT_MAP) - for loop in range( count ): - msg = self.get_miniature_XML( mini, addName) - msg = str("" + msg + "") - map.new_data( msg ) - session.send( msg ) - - def get_miniature_XML( self, mini, addName = True ): - msg = orpg.mapper.map_msg.mini_msg() - map = open_rpg.get_component( COMPONENT_MAP ) - session = open_rpg.get_component( COMPONENT_SESSION ) - msg.init_prop( ATTRIBUTE_ID, session.get_next_id() ) - for k in mini.getAttributeKeys(): - # translate our attributes to map attributes - key = FROM_MINILIB_MAP.get( k, k ) - if key != None: - if not addName and k == 'name': - pass - else: - msg.init_prop( key, mini.getAttribute( k ) ) - unique = self.is_unique( mini ) - if addName: - label = mini.getAttribute( ATTRIBUTE_NAME ) - else: - label = '' - return msg.get_all_xml() - - def is_unique( self, mini ): - unique = mini.getAttribute( ATTRIBUTE_UNIQUE ) - val = 0 - try: - val = eval( unique ) - except: - val = len( unique ) - return val - - def sanity_check_nodes( self ): - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - for node in nl: - if node.getAttribute( ATTRIBUTE_POSX ) == '': - node.setAttribute( ATTRIBUTE_POSX, '0' ) - if node.getAttribute( ATTRIBUTE_POSY ) == '': - node.setAttribute( ATTRIBUTE_POSY, '0' ) - - def get_mini( self, index ): - try: - nl = self.master_dom.getElementsByTagName( TAG_MINIATURE ) - return nl[ index ] - except: - return None - -class mini_handler( node_handler ): - def __init__( self, xml_dom, tree_node, handler ): - node_handler.__init__( self, xml_dom, tree_node) - self.handler = handler - - def on_ldclick( self, evt ): - self.handler.send_mini_to_map( self.master_dom ) - - def on_drop( self, evt ): - pass - - def on_lclick( self, evt ): - print 'hi' - evt.Skip() - -class minilib_use_panel(wx.Panel): - """This panel will be displayed when the user double clicks on the - miniature library node. It is a sorted listbox of miniature labels, - a text field for entering a count ( for batch adds ) and 'add'/'done' - buttons. - """ - def __init__( self, frame, handler ): - """Constructor. - """ - wx.Panel.__init__( self, frame, -1 ) - self.handler = handler - self.frame = frame - - self.map = open_rpg.get_component('map') - names = self.buildList() - # self.keys = self.list.keys() - # self.keys.sort() - - - s = self.GetClientSizeTuple() - - self.sizer = wx.BoxSizer(wx.VERTICAL) - box = wx.BoxSizer(wx.HORIZONTAL) - self.listbox = wx.BitmapButton(self, wx.ID_ANY, names, wx.LB_EXTENDED) - self.count = wx.TextCtrl(self, wx.ID_ANY, '1') - - box.Add( wx.StaticText( self, -1, 'Minis to add' ), 0, wx.EXPAND ) - box.Add(wx.Size(10,10)) - box.Add(self.count, 1, wx.EXPAND) - - self.sizer.Add( self.listbox, 1, wx.EXPAND ) - self.sizer.Add( box, 0, wx.EXPAND ) - - box = wx.BoxSizer( wx.HORIZONTAL ) - self.okBtn = wx.Button(self, wx.ID_ANY, 'Add') - box.Add(self.okBtn, 0, wx.EXPAND) - self.addBtn = wx.Button(self, wx.ID_ANY, 'Add No Label') - box.Add(self.addBtn, 0, wx.EXPAND) - self.cancleBtn = wx.Button(self, wx.ID_ANY, 'Done') - box.Add(self.cancleBtn, 0, wx.EXPAND) - - self.sizer.Add(wx.Size(10,10)) - self.sizer.Add(box, 0, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, self.on_ok, self.okBtn) - self.Bind(wx.EVT_BUTTON, self.on_ok, self.addBtn) - self.Bind(wx.EVT_BUTTON, self.on_close, self.cancleBtn) - self.SetSizer(self.sizer) - self.SetAutoLayout(True) - self.Fit() - - def buildList( self ): - """Returns a dictionary of label => game tree miniature DOM node mappings. - """ - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) - self.list = [] - for mini in list: - min_path = mini.getAttribute( ATTRIBUTE_URL ) - min_img = ImageHandler.load( min_path, 'miniature', 1) - self.list.append( mini.getAttribute( min_img ) ) - return self.list - # self.list = {} - # for mini in list: - # name = mini.getAttribute( ATTRIBUTE_NAME ) - # if name == '': - # name = self.map.canvas.get_label_from_url( mini.getAttribute( ATTRIBUTE_URL ) ) - # self.list[ name ] = mini - - def on_close(self, evt): - self.frame.Close() - - def on_ok( self, evt ): - """Event handler for the 'add' button. - """ - btn = self.FindWindowById(evt.GetId()) - sendName = True - try: - count = eval( self.count.GetValue() ) - except: - count = 1 - - try: - if eval( unique ): - count = 1 - unique = eval( unique ) - except: - pass - - if btn.GetLabel() == 'Add No Label': - sendName = False - for index in self.listbox.GetSelections(): - self.handler.send_mini_to_map( self.handler.get_mini( index ), count, sendName ) - - -class minpedit(wx.Panel): - """Panel for editing game tree miniature nodes. Node information - is displayed in a grid, and buttons are provided for adding, deleting - nodes, and for sending minis to the map ( singly and in batches ). - """ - def __init__( self, frame, handler ): - """Constructor. - """ - wx.Panel.__init__( self, frame, -1 ) - self.handler = handler - self.frame = frame - - self.sizer = wx.BoxSizer( wx.VERTICAL ) - self.grid = minilib_grid( self, handler ) - - bbox = wx.BoxSizer( wx.HORIZONTAL ) - newMiniBtn = wx.Button( self, wx.ID_ANY, "New mini" ) - delMiniBtn = wx.Button( self, wx.ID_ANY, "Del mini" ) - addMiniBtn = wx.Button( self, wx.ID_ANY, "Add 1" ) - addBatchBtn = wx.Button( self, wx.ID_ANY, "Add Batch" ) - bbox.Add(newMiniBtn, 0, wx.EXPAND ) - bbox.Add(delMiniBtn, 0, wx.EXPAND ) - bbox.Add(wx.Size(10,10)) - bbox.Add(addMiniBtn, 0, wx.EXPAND ) - bbox.Add(addBatchBtn, 0, wx.EXPAND ) - - self.sizer.Add( self.grid, 1, wx.EXPAND) - self.sizer.Add( bbox, 0) - self.SetSizer(self.sizer) - self.SetAutoLayout(True) - self.Fit() - - self.Bind(wx.EVT_BUTTON, self.add_mini, newMiniBtn) - self.Bind(wx.EVT_BUTTON, self.del_mini, delMiniBtn) - self.Bind(wx.EVT_BUTTON, self.send_to_map, addMiniBtn) - self.Bind(wx.EVT_BUTTON, self.send_group_to_map, addBatchBtn) - - def add_mini( self, evt=None ): - """Event handler for the 'New mini' button. It calls - minilib_grid.add_row - """ - self.grid.add_row() - - def del_mini( self, evt=None ): - """Event handler for the 'Del mini' button. It calls - minilib_grid.del_row - """ - self.grid.del_row() - - def send_to_map( self, evt=None ): - """Event handler for the 'Add 1' button. Sends the - miniature defined by the currently selected row to the map, once. - """ - index = self.grid.GetGridCursorRow() - self.handler.send_mini_to_map( self.handler.get_mini( index ) ) - - def send_group_to_map( self, evt=None ): - """Event handler for the 'Add batch' button. Querys the user - for a mini count and sends the miniature defined by the currently - selected row to the map, the specified number of times. - """ - if self.grid.GetNumberRows() > 0: - dlg = wx.TextEntryDialog( self.frame, - 'How many %s\'s do you want to add?' % - ( self.grid.getSelectedLabel() ), 'Batch mini add', '2' ) - if dlg.ShowModal() == wx.ID_OK: - try: - value = eval( dlg.GetValue() ) - except: - value = 0 - # for loop in range( 0, value ): - # self.send_to_map() - print 'getting selected index for batch send' - index = self.grid.GetGridCursorRow() - print 'sending batch to map' - self.handler.send_mini_to_map( self.handler.get_mini( index ), value ) - -class minilib_grid(wx.grid.Grid): - """A wxGrid subclass designed for editing game tree miniature library - nodes. - """ - def __init__( self, parent, handler ): - """Constructor. - """ - wx.grid.Grid.__init__(self, parent, -1, style=wx.SUNKEN_BORDER | wx.WANTS_CHARS ) - self.parent = parent - self.handler = handler - #self.keys = [ ATTRIBUTE_NAME, ATTRIBUTE_URL, ATTRIBUTE_UNIQUE ] - self.keys = CORE_ATTRIBUTES - self.CreateGrid( 1, len( self.keys ) ) - # self.SetColLabelValue( 0, 'Name' ) - # self.SetColLabelValue( 1, 'URL' ) - # self.SetColSize( 1, 250 ) - # self.SetColLabelValue( 2, 'Unique' ) - for key in self.keys: - self.SetColLabelValue( self.keys.index( key ), key ) - self.update_all() - self.selectedRow = 0 - self.AutoSizeColumns() - self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.on_cell_change) - self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.select_cell) - - def update_cols( self ): - nl = self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) - for n in nl: - for k in n.getAttributeKeys(): - if k not in self.keys: - self.keys.append( k ) - - def select_cell( self, evt ): - """Event handler for grid cell selection changes. It stores the - last selected row in a variable for use by the add[*] and del_row - operations. - """ - self.BeginBatch() - self.selectedRow = evt.GetRow() - self.SelectRow( self.selectedRow ) - self.EndBatch() - evt.Skip() - - def getList( self ): - """Returns the list of 'miniature' DOM elements associated with this - miniature library. - """ - return self.handler.master_dom.getElementsByTagName( TAG_MINIATURE ) - - def add_row( self, count = 1 ): - """creates a new miniature node, and then adds it to the current - miniature library, and to the grid. - """ - self.AppendRows( count ) - node = self.handler.new_mini( { - ATTRIBUTE_NAME :' ', - ATTRIBUTE_URL :'http://'} )# minidom.Element( TAG_MINIATURE ) - self.update_all() - #self.handler.master_dom.appendChild( node ) - - def del_row( self ): - """deletes the miniature associated with the currently selected - row. BUG BUG BUG this method should drop a child from the DOM but - does not. - """ - if self.selectedRow > -1: - pos = self.selectedRow - list = self.handler.master_dom.getElementsByTagName(TAG_MINIATURE) - self.handler.master_dom.removeChild( list[pos] ) - self.DeleteRows( pos, 1 ) - list = self.getList() - del list[ pos ] - - def on_cell_change( self, evt ): - """Event handler for cell selection changes. selected row is used - to update data for that row. - """ - row = evt.GetRow() - self.update_data_row( row ) - - def update_all( self ): - """ensures that the grid is displaying the correct number of - rows, and then updates all data displayed by the grid - """ - list = self.getList() - count = 0 - for n in list: - for k in n.getAttributeKeys(): - if k not in self.keys: - self.keys.append( k ) - count = len( self.keys ) - if self.GetNumberCols() < count: - self.AppendCols( count - self.GetNumberCols() ) - for k in self.keys: - self.SetColLabelValue( self.keys.index( k ), k ) - count = len( list ) - rowcount = self.GetNumberRows() - if ( count > rowcount ): - total = count - rowcount - self.AppendRows( total ) - elif ( count < rowcount ): - total = rowcount - count - self.DeleteRows( 0, total ); - for index in range( 0, count ): - self.update_grid_row( index ) - - def getSelectedLabel( self ): - """Returns the label for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 0 ) - - def getSelectedURL( self ): - """Returns the URL for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 1 ) - - def getSelectedSerial( self ): - """Returns the ATTRIBUTE_UNIQUE value for the selected row - """ - return self.GetTable().GetValue( self.selectedRow, 2 ) - - def update_grid_row( self, row ): - """Updates the specified grid row with data from the DOM node - specified by 'row' - """ - list = self.getList() - item = list[ row ] - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) - for key in self.keys: - self.GetTable().SetValue( row, self.keys.index( key ), item.getAttribute( key ) ) - - def update_data_row( self, row ): - """Updates the DOM nodw 'row' with grid data from 'row' - """ - list = self.getList() - item = list[ row ] - for key in self.keys: - item.setAttribute( key, string.strip( self.GetTable().GetValue( row, self.keys.index( key ) ) ) ) - # item.setAttribute( ATTRIBUTE_NAME, string.strip( self.GetTable().GetValue( row, 0 ) ) ) - # item.setAttribute( ATTRIBUTE_URL, string.strip( self.GetTable().GetValue( row, 1 ) ) ) - # item.setAttribute( ATTRIBUTE_UNIQUE, string.strip( self.GetTable().GetValue( row, 2 ) ) ) - # self.GetTable().SetValue( row, 0, item.getAttribute(ATTRIBUTE_NAME) ) - # self.GetTable().SetValue( row, 1, item.getAttribute(ATTRIBUTE_URL) ) - # self.GetTable().SetValue( row, 2, item.getAttribute(ATTRIBUTE_UNIQUE) ) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/gametree/nodehandlers/rpg_grid.py --- a/orpg/gametree/nodehandlers/rpg_grid.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/gametree/nodehandlers/rpg_grid.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,8 +29,7 @@ __version__ = "$Id: rpg_grid.py,v 1.20 2006/11/15 12:11:24 digitalxero Exp $" from core import * -from forms import * -from orpg.minidom import Element, Text +from forms import * class rpg_grid_handler(node_handler): """ Node handler for rpg grid tool @@ -50,14 +49,14 @@ """ - def __init__(self,xml_dom,tree_node): - node_handler.__init__(self,xml_dom,tree_node) - self.grid = self.master_dom.getElementsByTagName('grid')[0] - if self.grid.getAttribute("border") == "": - self.grid.setAttribute("border","1") - if self.grid.getAttribute("autosize") == "": - self.grid.setAttribute("autosize","1") - self.macros = self.master_dom.getElementsByTagName('macros')[0] + def __init__(self,xml,tree_node): + node_handler.__init__(self,xml,tree_node) + self.grid = self.xml.find('grid') + if self.grid.get("border") == "": + self.grid.set("border","1") + if self.grid.get("autosize") == "": + self.grid.set("autosize","1") + self.macros = self.xml.find('macros') self.myeditor = None self.refresh_rows() @@ -69,38 +68,31 @@ tree = self.tree icons = self.tree.icons tree.CollapseAndReset(self.mytree_node) - node_list = self.master_dom.getElementsByTagName('row') - for n in node_list: - cells = n.getElementsByTagName('cell') - t_node = cells[0]._get_firstChild() - if t_node == None: - name = "Row" - else: - name = t_node._get_nodeValue() - if name == "": + for row in self.grid.findall('row'): + first_cell = row.find('cell') + name = first_cell.text + if name == None or name == '': name = "Row" new_tree_node = tree.AppendItem(self.mytree_node,name,icons['gear'],icons['gear']) - handler = grid_row_handler(n,new_tree_node,self) + handler = grid_row_handler(row,new_tree_node,self) tree.SetPyData(new_tree_node,handler) - def tohtml(self): - border = self.grid.getAttribute("border") - name = self.master_dom.getAttribute('name') - rows = self.grid.getElementsByTagName('row') - colspan = str(len(rows[0].getElementsByTagName('cell'))) + border = self.grid.get("border") + name = self.xml.get('name') + rows = self.grid.findall('row') + colspan = str(len(rows[0].findall('cell'))) html_str = "" for r in rows: - cells = r.getElementsByTagName('cell') + cells = r.findall('cell') html_str += "" for c in cells: - #html_str += "" - else: - html_str += t_node._get_nodeValue() + "" + text = c.text + if text == None or text == '': + text = '
' + html_str += text + "" html_str += "" html_str += "
"+name+"
" bug here + #html_str += "" bug here html_str += "" - t_node = c._get_firstChild() - if t_node == None: - html_str += "
" return html_str @@ -115,16 +107,16 @@ return 1 def is_autosized(self): - return self.grid.getAttribute("autosize") + return self.grid.get("autosize") def set_autosize(self,autosize=1): - self.grid.setAttribute("autosize",str(autosize)) + self.grid.set("autosize",str(autosize)) class grid_row_handler(node_handler): """ Node Handler grid row. """ - def __init__(self,xml_dom,tree_node,parent): - node_handler.__init__(self,xml_dom,tree_node) + def __init__(self,xml,tree_node,parent): + node_handler.__init__(self,xml,tree_node) self.drag = False def on_drop(self,evt): @@ -134,19 +126,30 @@ return 0; def tohtml(self): - cells = self.master_dom.getElementsByTagName('cell') + cells = self.xml.findall('cell') html_str = "" - for c in cells: + for c in cells: # should loop over rows first, then cells html_str += "" - else: - html_str += t_node._get_nodeValue() + "" + text = c.text + if text == '' or text is None: + text = '
' + html_str += text + "" html_str += "" html_str += "
" - t_node = c._get_firstChild() - if t_node == None: - html_str += "
" return html_str + def get_value(self): + cells = self.xml.findall('cell') + if len(cells) == 2: + return getText(cells[1]) + else: + return None + + def set_value(self, new_value): + cells = self.xml.findall('cell') + if len(cells) == 2: + cells[1].text = new_value + class MyCellEditor(wx.grid.PyGridCellEditor): """ This is a sample GridCellEditor that shows you how to make your own custom @@ -284,6 +287,7 @@ evt.Skip() + def Destroy(self): """final cleanup""" self.base_Destroy() @@ -308,9 +312,9 @@ # Registers a "custom" cell editor (really the example from Robin Dunn with minor mods self.RegisterDataType(wx.grid.GRID_VALUE_STRING, wx.grid.GridCellStringRenderer(),MyCellEditor()) - self.rows = handler.grid.getElementsByTagName('row') + self.rows = handler.grid.findall('row') rows = len(self.rows) - cols = len(self.rows[0].getElementsByTagName('cell')) + cols = len(self.rows[0].findall('cell')) self.CreateGrid(rows,cols) self.SetRowLabelSize(0) self.SetColLabelSize(0) @@ -330,85 +334,76 @@ def on_col_size(self, evt): col = evt.GetRowOrCol() - cells = self.rows[0].getElementsByTagName('cell') + cells = self.rows[0].findall('cell') size = self.GetColSize(col) - cells[col].setAttribute('size',str(size)) + cells[col].set('size',str(size)) evt.Skip() def on_cell_change(self,evt): row = evt.GetRow() col = evt.GetCol() value = self.GetCellValue(row,col) - cells = self.rows[row].getElementsByTagName('cell') - t_node = cells[col]._get_firstChild() - t_node._set_nodeValue(value) + cells = self.rows[row].findall('cell') + cells[col].text = value if col == 0: self.handler.refresh_rows() def set_col_widths(self): - cells = self.rows[0].getElementsByTagName('cell') + cells = self.rows[0].findall('cell') for i in range(0,len(cells)): try: - size = int(cells[i].getAttribute('size')) + size = int(cells[i].get('size')) self.SetColSize(i,size) except: continue def refresh_row(self,rowi): - cells = self.rows[rowi].getElementsByTagName('cell') + cells = self.rows[rowi].findall('cell') for i in range(0,len(cells)): - t_node = cells[i]._get_firstChild() - if t_node == None: - #doc = cells[i].ownerDocument - #t_node = doc.createTextNode("") - t_node = Text("") - t_node = cells[i].appendChild(t_node) - self.SetCellValue(rowi,i,t_node._get_nodeValue()) + text = cells[i].text + if text == None or text == '': + text = '' + cells[i].text = text + self.SetCellValue(rowi,i,text) def add_row(self,evt=None): cols = self.GetNumberCols() - #doc = self.handler.grid.ownerDocument - #row = doc.createElement('row') row = Element('row') for i in range(0,cols): - #cell = doc.createElement('cell') cell = Element('cell') - #t_node = doc.createTextNode("") - t_node = Text("") - t_node = cell.appendChild(t_node) - row.appendChild(cell) - self.handler.grid.appendChild(row) + cell.text = '' + row.append(cell) + self.handler.grid.append(row) self.AppendRows(1) - self.rows = self.handler.grid.getElementsByTagName('row') + self.rows = self.handler.grid.findall('row') self.handler.refresh_rows() def add_col(self,evt=None): - #doc = self.handler.grid.ownerDocument for r in self.rows: - #cell = doc.createElement('cell') cell = Element('cell') - #t_node = doc.createTextNode("") - t_node = Text("") - t_node = cell.appendChild(t_node) - r.appendChild(cell) + cell.text = '' + r.append(cell) self.AppendCols(1) - #self.fit_cols()::Where did this go? TaS. + self.set_col_widths() def del_row(self,evt=None): num = self.GetNumberRows() - row = self.rows[num-1] - self.handler.grid.removeChild(row) + if num == 1: + return + self.handler.grid.remove(self.handler.grid[num-1])#always remove last row -- nasty self.DeleteRows(num-1,1) - self.rows = self.handler.grid.getElementsByTagName('row') + self.rows = self.handler.grid.findall('row') self.handler.refresh_rows() def del_col(self,evt=None): num = self.GetNumberCols() + if num == 1: + return for r in self.rows: - cells = r.getElementsByTagName('cell') - r.removeChild(cells[num-1]) + cells = r.findall('cell') + r.remove(r[num-1])# always remove the last column -- nasty self.DeleteCols(num-1,1) - #self.fit_cols()::Where did this go? TaS. + self.set_col_widths() G_TITLE = wx.NewId() @@ -418,7 +413,7 @@ wx.Panel.__init__(self, parent, -1) self.handler = handler self.grid = rpg_grid(self,handler) - label = handler.master_dom.getAttribute('name') + label = handler.xml.get('name') self.main_sizer = wx.BoxSizer(wx.VERTICAL) self.main_sizer.Add(wx.StaticText(self, -1, label+": "), 0, wx.EXPAND) self.main_sizer.Add(self.grid,1,wx.EXPAND) @@ -439,10 +434,10 @@ wx.Panel.__init__(self, parent, -1) self.handler = handler self.grid = rpg_grid(self,handler) - self.title = wx.TextCtrl(self, G_TITLE, handler.master_dom.getAttribute('name')) + self.title = wx.TextCtrl(self, G_TITLE, handler.xml.get('name')) radio_b = wx.RadioBox(self, GRID_BOR, "Border (HTML)", choices=["no","yes"]) - border = handler.grid.getAttribute("border") + border = handler.grid.get("border") radio_b.SetSelection(int(border)) self.auto_size = wx.CheckBox(self, G_AUTO_SIZE, " Auto Size") @@ -487,10 +482,10 @@ id = evt.GetId() index = evt.GetInt() if id == GRID_BOR: - self.handler.grid.setAttribute("border",str(index)) + self.handler.grid.set("border",str(index)) def on_text(self,evt): txt = self.title.GetValue() if txt != "": - self.handler.master_dom.setAttribute('name',txt) + self.handler.xml.set('name',txt) self.handler.rename(txt) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/main.py --- a/orpg/main.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/main.py Tue Nov 10 14:11:28 2009 -0600 @@ -35,7 +35,7 @@ from orpg.orpg_windows import * import wx.py -from orpg import minidom + import orpg.player_list import orpg.tools.pluginui as pluginUI import orpg.tools.aliaslib @@ -49,20 +49,23 @@ import orpg.mapper.map import orpg.mapper.images +#Update Manager# Un remark if you have Mercurial installed import upmana.updatemana import upmana.manifest as manifest from orpg.dirpath import dir_struct from orpg.dieroller.utils import DiceManager -from orpg.tools.orpg_settings import settings +from orpg.tools.settings import settings from orpg.tools.validate import validate from orpg.tools.passtool import PassTool -from orpg.tools.orpg_log import logger, crash -from orpg.tools.decorators import debugging +from orpg.tools.orpg_log import logger, crash, debug from orpg.tools.metamenus import MenuBarEx -#from xml.etree.ElementTree import ElementTree, Element -#from xml.etree.ElementTree import fromstring, tostring +from xml.etree.ElementTree import ElementTree, Element, parse +from xml.etree.ElementTree import fromstring, tostring +## Element Tree usage will require users to convert to and from string data quite often until users of older versions update. +## This is a problem that users of older versions will need to cross as it is both Core and Traipse that will make the change. +## Older versions have a problem with correct XML. from orpg.orpg_xml import xml #to be replaced by etree @@ -72,7 +75,7 @@ class orpgFrame(wx.Frame): - @debugging + def __init__(self, parent, id, title): wx.Frame.__init__(self, parent, id, title, wx.Point(100, 100), wx.Size(600,420), style=wx.DEFAULT_FRAME_STYLE) self.validate = component.get("validate") @@ -95,7 +98,7 @@ "on_password_signal":self.on_password_signal, "orpgFrame":self} self.settings = component.get('settings') #Arbitrary until settings updated with Core. - self.session = orpg.networking.mplay_client.mplay_client(self.settings.get_setting("player"), call_backs) + self.session = orpg.networking.mplay_client.mplay_client(self.settings.get("player"), call_backs) self.poll_timer = wx.Timer(self, wx.NewId()) self.Bind(wx.EVT_TIMER, self.session.poll, self.poll_timer) self.poll_timer.Start(100) @@ -103,7 +106,7 @@ self.Bind(wx.EVT_TIMER, self.session.update, self.ping_timer) # create roller manager - self.DiceManager = DiceManager(settings.get_setting("dieroller")) + self.DiceManager = DiceManager(settings.get("dieroller")) component.add('DiceManager', self.DiceManager) #create password manager --SD 8/03 @@ -122,7 +125,7 @@ component.add("alias", self.aliaslib) logger.debug("openrpg components all added") - self.tree.load_tree(settings.get_setting("gametree")) + self.tree.load_tree(settings.get("gametree")) logger.debug("Tree Loaded") self.players.size_cols() @@ -137,29 +140,29 @@ logger.debug("update manager reloaded") self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) - #Load Update Manager + #Load Debug Console component.add('debugconsole', self.debugger) logger.debug("debugger window") self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) - @debugging + def post_show_init(self): """Some Actions need to be done after the main fram is drawn""" self.players.size_cols() - @debugging + def get_activeplugins(self): try: tmp = self.pluginsFrame.get_activeplugins() except: tmp = {} return tmp - @debugging + def get_startplugins(self): try: tmp = self.pluginsFrame.get_startplugins() except: tmp = {} return tmp - @debugging + def on_password_signal(self,signal,type,id,data): try: msg = ["DEBUG: password response= ", @@ -181,7 +184,7 @@ else: pass except: traceback.print_exc() - @debugging + def build_menu(self): menu = \ [[ @@ -234,10 +237,11 @@ ]] self.mainmenu = MenuBarEx(self, menu) - if settings.get_setting('Heartbeat') == '1': + if settings.get('Heartbeat') == '1': self.mainmenu.SetMenuState("GameServerServerHeartbeat", True) - tabtheme = settings.get_setting('TabTheme') #This change is stable. TaS. + self.mainmenu.SetMenuState('ToolsPasswordManager', True if settings.get('PWMannager') == 'On' else False) + tabtheme = settings.get('TabTheme') #This change is stable. TaS. self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful') self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedBlackandWhite", tabtheme == 'slanted&bw') self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedAqua", tabtheme == 'slanted&aqua') @@ -246,7 +250,7 @@ self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedCustom", tabtheme == 'customslant') self.mainmenu.SetMenuState("OpenRPGTabStylesFlatCustom", tabtheme == 'customflat') - lvl = int(settings.get_setting('LoggingLevel')) + lvl = int(settings.get('LoggingLevel')) if lvl & ORPG_DEBUG: self.mainmenu.SetMenuState("ToolsLoggingLevelDebug", True) if lvl & ORPG_DEBUG: self.mainmenu.SetMenuState("ToolsLoggingLevelNote", True) if lvl & ORPG_INFO: self.mainmenu.SetMenuState("ToolsLoggingLevelInfo", True) @@ -264,6 +268,7 @@ self.traipseSuite = wx.Menu() self.mainmenu.Insert(5, self.traipseSuite, "&Traipse Suite") + #Update Manager mana = wx.MenuItem(self.traipseSuite, wx.ID_ANY, "Update Manager", "Update Manager") self.Bind(wx.EVT_MENU, self.OnMB_UpdateManagerPanel, mana) self.traipseSuite.AppendItem(mana) @@ -275,7 +280,9 @@ def TraipseSuiteWarn(self, menuitem): ### Beta ### Allows for the reuse of the 'Attention' menu. ### component.get('frame').TraipseSuiteWarn('item') ### Portable - self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite!') + self.mainmenu.Remove(8) + self.mainmenu.Insert(8, self.traipseSuite, "&Traipse Suite!") + #self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite!') if menuitem == 'debug': if self.debugger.IsShown() == True: self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite') @@ -287,7 +294,9 @@ def TraipseSuiteWarnCleanup(self, menuitem): ### Beta ### Allows for portable cleanup of the 'Attention' menu. ### component.get('frame').TraipseSuiteWarnCleanup('item') ### Portable - self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite') + self.mainmenu.Remove(8) + self.mainmenu.Insert(8, self.traipseSuite, "&Traipse Suite") + #self.mainmenu.Replace(8, self.traipseSuite, '&Traipse Suite') if menuitem == 'debug': self.traipseSuite.RemoveItem(self.debugConsole) self.debugConsole.SetBitmap(wx.Bitmap(dir_struct["icon"] + 'clear.gif')) @@ -298,10 +307,10 @@ ## All Menu Events ################################# #Tab Styles Menus - @debugging + def SetTabStyles(self, *args, **kwargs): - tabtheme = settings.get_setting('TabTheme') #This change is stable. TaS. + tabtheme = settings.get('TabTheme') #This change is stable. TaS. self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", tabtheme == 'slanted&colorful') self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedBlackandWhite", tabtheme == 'slanted&bw') self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedAqua", tabtheme == 'slanted&aqua') @@ -336,7 +345,7 @@ component.add("tabbedWindows", tabbedwindows) #Run though the new list and set the proper styles - tabbg = settings.get_setting('TabBackgroundGradient') + tabbg = settings.get('TabBackgroundGradient') (red, green, blue) = self.rgb.rgb_tuple(tabbg) for wnd in tabbedwindows: @@ -352,77 +361,77 @@ if textColor != None: wnd.SetNonActiveTabTextColour(textColor) wnd.Refresh() - @debugging + def OnMB_OpenRPGNewMap(self): pass #Not Implemented yet! - @debugging + def OnMB_OpenRPGTabStylesSlantedColorful(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesSlantedColorful"): - settings.set_setting('TabTheme', 'slanted&colorful') + settings.change('TabTheme', 'slanted&colorful') self.SetTabStyles("OpenRPGTabStylesSlantedColorful", FNB.FNB_VC8|FNB.FNB_COLORFUL_TABS) else: self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedColorful", True) - @debugging + def OnMB_OpenRPGTabStylesSlantedBlackandWhite(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesSlantedBlackandWhite"): - settings.set_setting('TabTheme', 'slanted&bw') + settings.change('TabTheme', 'slanted&bw') self.SetTabStyles("OpenRPGTabStylesSlantedBlackandWhite", FNB.FNB_VC8, graidentTo=wx.WHITE, graidentFrom=wx.WHITE, textColor=wx.BLACK) else: self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedBlackandWhite", True) - @debugging + def OnMB_OpenRPGTabStylesSlantedAqua(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesSlantedAqua"): - settings.set_setting('TabTheme', 'slanted&aqua') + settings.change('TabTheme', 'slanted&aqua') self.SetTabStyles("OpenRPGTabStylesSlantedAqua", FNB.FNB_VC8, graidentTo=wx.Color(0, 128, 255), graidentFrom=wx.WHITE, textColor=wx.BLACK) else: self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedAqua", True) - @debugging + def OnMB_OpenRPGTabStylesSlantedCustom(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesSlantedCustom"): - settings.set_setting('TabTheme', 'customslant') - gfrom = settings.get_setting('TabGradientFrom') + settings.change('TabTheme', 'customslant') + gfrom = settings.get('TabGradientFrom') (fred, fgreen, fblue) = self.rgb.rgb_tuple(gfrom) - gto = settings.get_setting('TabGradientTo') + gto = settings.get('TabGradientTo') (tored, togreen, toblue) = self.rgb.rgb_tuple(gto) - tabtext = settings.get_setting('TabTextColor') + tabtext = settings.get('TabTextColor') (tred, tgreen, tblue) = self.rgb.rgb_tuple(tabtext) - tabbg = settings.get_setting('TabBackgroundGradient') + tabbg = settings.get('TabBackgroundGradient') (red, green, blue) = self.rgb.rgb_tuple(tabbg) self.SetTabStyles("OpenRPGTabStylesSlantedCustom", FNB.FNB_VC8, graidentTo=wx.Color(tored, togreen, toblue), graidentFrom=wx.Color(fred, fgreen, fblue), textColor=wx.Color(tred, tgreen, tblue)) else: self.mainmenu.SetMenuState("OpenRPGTabStylesSlantedCustom", True) - @debugging + def OnMB_OpenRPGTabStylesFlatBlackandWhite(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesFlatBlackandWhite"): - settings.set_setting('TabTheme', 'flat&bw') + settings.change('TabTheme', 'flat&bw') self.SetTabStyles("OpenRPGTabStylesFlatBlackandWhite", FNB.FNB_FANCY_TABS, graidentTo=wx.WHITE, graidentFrom=wx.WHITE, textColor=wx.BLACK) else: self.mainmenu.SetMenuState("OpenRPGTabStylesFlatBlackandWhite", True) - @debugging + def OnMB_OpenRPGTabStylesFlatAqua(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesFlatAqua"): - settings.set_setting('TabTheme', 'flat&aqua') + settings.change('TabTheme', 'flat&aqua') self.SetTabStyles("OpenRPGTabStylesFlatAqua", FNB.FNB_FANCY_TABS, graidentTo=wx.Color(0, 128, 255), graidentFrom=wx.WHITE, textColor=wx.BLACK) else: self.mainmenu.SetMenuState("OpenRPGTabStylesFlatAqua", True) - @debugging + def OnMB_OpenRPGTabStylesFlatCustom(self): if self.mainmenu.GetMenuState("OpenRPGTabStylesFlatCustom"): - settings.set_setting('TabTheme', 'customflat') - gfrom = settings.get_setting('TabGradientFrom') + settings.change('TabTheme', 'customflat') + gfrom = settings.get('TabGradientFrom') (fred, fgreen, fblue) = self.rgb.rgb_tuple(gfrom) - gto = settings.get_setting('TabGradientTo') + gto = settings.get('TabGradientTo') (tored, togreen, toblue) = self.rgb.rgb_tuple(gto) - tabtext = settings.get_setting('TabTextColor') + tabtext = settings.get('TabTextColor') (tred, tgreen, tblue) = self.rgb.rgb_tuple(tabtext) - tabbg = settings.get_setting('TabBackgroundGradient') + tabbg = settings.get('TabBackgroundGradient') (red, green, blue) = self.rgb.rgb_tuple(tabbg) self.SetTabStyles("OpenRPGTabStylesFlatCustom", FNB.FNB_FANCY_TABS, graidentTo=wx.Color(tored, togreen, toblue), graidentFrom=wx.Color(fred, fgreen, fblue), @@ -430,7 +439,7 @@ else: self.mainmenu.SetMenuState("OpenRPGTabStylesFlatCustom", True) #Window Menu - @debugging + def OnMB_WindowsMenu(self, event): menuid = event.GetId() name = self.mainwindows[menuid] @@ -443,7 +452,7 @@ self._mgr.Update() #OpenRPG Menu - @debugging + def OnMB_OpenRPGSettings(self): dlg = orpg.tools.orpg_settings.orpgSettingsWnd(self) dlg.Centre() @@ -453,18 +462,18 @@ self.OnCloseWindow(0) #Game Server Menu - @debugging + def OnMB_GameServerBrowseServers(self): if self._mgr.GetPane("Browse Server Window").IsShown() == True: self._mgr.GetPane("Browse Server Window").Hide() else: self._mgr.GetPane("Browse Server Window").Show() self._mgr.Update() - @debugging + def OnMB_GameServerServerHeartbeat(self): - if self.mainmenu.GetMenuState("GameServerServerHeartbeat"): settings.set_setting('Heartbeat', '1') - else: settings.set_setting('Heartbeat', '0') + if self.mainmenu.GetMenuState("GameServerServerHeartbeat"): settings.change('Heartbeat', '1') + else: settings.change('Heartbeat', '0') - @debugging + def OnMB_GameServerStartServer(self): start_dialog = wx.ProgressDialog( "Server Loading", "Server Loading, Please Wait...", 1, self ) # Spawn the new process and close the stdout handle from it @@ -483,23 +492,23 @@ start_dialog.Destroy() # Tools Menu - @debugging + def OnMB_PluginControlPanel(self, evt): if self.pluginsFrame.IsShown() == True: self.pluginsFrame.Hide() else: self.pluginsFrame.Show() - @debugging + def OnMB_UpdateManagerPanel(self, evt): if self.updateMana.IsShown() == True: self.updateMana.Hide() else: self.updateMana.Show() - @debugging + def OnMB_DebugConsole(self, evt): self.TraipseSuiteWarnCleanup('debug') ### Beta ### if self.debugger.IsShown() == True: self.debugger.Hide() else: self.debugger.Show() - @debugging + def OnMB_ToolsLoggingLevelDebug(self): lvl = logger.log_level if self.mainmenu.GetMenuState("ToolsLoggingLevelDebug"): lvl |= ORPG_DEBUG @@ -507,7 +516,7 @@ logger.log_level = lvl settings.set('LoggingLevel', lvl) - @debugging + def OnMB_ToolsLoggingLevelNote(self): lvl = logger.log_level if self.mainmenu.GetMenuState("ToolsLoggingLevelNote"): lvl |= ORPG_DEBUG @@ -515,7 +524,7 @@ logger.log_level = lvl settings.set('LoggingLevel', lvl) - @debugging + def OnMB_ToolsLoggingLevelInfo(self): lvl = logger.log_level if self.mainmenu.GetMenuState("ToolsLoggingLevelInfo"): lvl |= ORPG_INFO @@ -523,7 +532,7 @@ logger.log_level = lvl settings.set('LoggingLevel', lvl) - @debugging + def OnMB_ToolsLoggingLevelGeneral(self): lvl = logger.log_level if self.mainmenu.GetMenuState("ToolsLoggingLevelGeneral"): lvl |= ORPG_GENERAL @@ -531,12 +540,12 @@ logger.log_level = lvl settings.set('LoggingLevel', lvl) - @debugging + def OnMB_ToolsPasswordManager(self): if self.mainmenu.GetMenuState("ToolsPasswordManager"): self.password_manager.Enable() else: self.password_manager.Disable() - @debugging + def OnMB_ToolsStatusBar(self): if self._mgr.GetPane("Status Window").IsShown() == True: self.mainmenu.SetMenuState("ToolsStatusBar", False) @@ -546,7 +555,7 @@ self._mgr.GetPane("Status Window").Show() self._mgr.Update() - @debugging + def OnMB_ToolsSoundToolbar(self): if self._mgr.GetPane("Sound Control Toolbar").IsShown() == True: self.mainmenu.SetMenuState("ToolsSoundToolbar", False) @@ -556,7 +565,7 @@ self._mgr.GetPane("Sound Control Toolbar").Show() self._mgr.Update() - @debugging + def OnMB_ToolsDiceBar(self): if self._mgr.GetPane("Dice Tool Bar").IsShown() == True: self.mainmenu.SetMenuState("ToolsDiceBar", False) @@ -566,7 +575,7 @@ self._mgr.GetPane("Dice Tool Bar").Show() self._mgr.Update() - @debugging + def OnMB_ToolsMapBar(self): if self._mgr.GetPane("Map Tool Bar").IsShown() == True: self.mainmenu.SetMenuState("ToolsMapBar", False) @@ -577,22 +586,22 @@ self._mgr.Update() #Help Menu #Needs a custom Dialog because it is ugly on Windows - @debugging + def OnMB_HelpAbout(self): if self.AboutORPG.IsShown() == True: self.AboutORPG.Hide() else: self.AboutORPG.Show() - @debugging + def OnMB_HelpOnlineUserGuide(self): wb = webbrowser.get() wb.open("http://www.assembla.com/wiki/show/traipse/User_Manual") - @debugging + def OnMB_HelpChangeLog(self): wb = webbrowser.get() wb.open("http://www.assembla.com/spaces/milestones/index/traipse?spaces_tool_id=Milestones") - @debugging + def OnMB_HelpReportaBug(self): wb = webbrowser.get() wb.open("http://www.assembla.com/spaces/tickets/index/traipse_dev?spaces_tool_id=Tickets") @@ -601,24 +610,14 @@ ################################# ## Build the GUI ################################# - @debugging + def build_gui(self): self.Freeze() self.validate.config_file("layout.xml","default_layout.xml") - filename = dir_struct["user"] + "layout.xml" - temp_file = open(filename) - txt = temp_file.read() - xml_dom = xml.parseXml(txt)._get_documentElement() - temp_file.close() + layout = parse(dir_struct["user"] + "layout.xml") + xml_dom = layout.getroot() - """ Would a component work better? - etree = ElementTree() - with open(dir_struct['user'] + 'layout.xml') as f: - etree.parse(f) - - base = etree.getroot() - """ self.windowsmenu = wx.Menu() self.mainwindows = {} @@ -630,35 +629,35 @@ component.add("plugins", self.get_activeplugins()) component.add("startplugs", self.get_startplugins()) logger.debug("Menu Created") - h = int(xml_dom.getAttribute("height")) - w = int(xml_dom.getAttribute("width")) - posx = int(xml_dom.getAttribute("posx")) - posy = int(xml_dom.getAttribute("posy")) - maximized = int(xml_dom.getAttribute("maximized")) + h = int(xml_dom.get("height")) + w = int(xml_dom.get("width")) + posx = int(xml_dom.get("posx")) + posy = int(xml_dom.get("posy")) + maximized = int(xml_dom.get("maximized")) self.SetDimensions(posx, posy, w, h) logger.debug("Dimensions Set") - # Update Manager + # Update Manager self.manifest = manifest.ManifestChanges() self.updateMana = upmana.updatemana.updaterFrame(self, "OpenRPG Update Manager Beta 0.8", component, self.manifest, True) logger.debug("Menu Created") - h = int(xml_dom.getAttribute("height")) - w = int(xml_dom.getAttribute("width")) - posx = int(xml_dom.getAttribute("posx")) - posy = int(xml_dom.getAttribute("posy")) - maximized = int(xml_dom.getAttribute("maximized")) + h = int(xml_dom.get("height")) + w = int(xml_dom.get("width")) + posx = int(xml_dom.get("posx")) + posy = int(xml_dom.get("posy")) + maximized = int(xml_dom.get("maximized")) self.SetDimensions(posx, posy, w, h) logger.debug("Dimensions Set") # Debug Console self.debugger = orpg.tools.orpg_log.DebugConsole(self) logger.debug("Menu Created") - h = int(xml_dom.getAttribute("height")) - w = int(xml_dom.getAttribute("width")) - posx = int(xml_dom.getAttribute("posx")) - posy = int(xml_dom.getAttribute("posy")) - maximized = int(xml_dom.getAttribute("maximized")) + h = int(xml_dom.get("height")) + w = int(xml_dom.get("width")) + posx = int(xml_dom.get("posx")) + posy = int(xml_dom.get("posy")) + maximized = int(xml_dom.get("maximized")) self.SetDimensions(posx, posy, w, h) logger.debug("Dimensions Set") @@ -675,7 +674,7 @@ wndinfo.ToolbarPane() wndinfo.Hide() self._mgr.AddPane(self.sound_player, wndinfo) - children = xml_dom._get_childNodes() + children = xml_dom.getchildren() for c in children: self.build_window(c, self) # status window @@ -751,12 +750,10 @@ logger.debug("AUI Bindings Done") #Load the layout if one exists - layout = xml_dom.getElementsByTagName("DockLayout") + layout = xml_dom.find("DockLayout") try: - textnode = xml.safe_get_text_node(layout[0]) - self._mgr.LoadPerspective(textnode._get_nodeValue()) + self._mgr.LoadPerspective(layout.text) except: pass - xml_dom.unlink() logger.debug("Perspective Loaded") self._mgr.GetPane("Browse Server Window").Hide() self._mgr.Update() @@ -764,16 +761,16 @@ logger.debug("GUI is all created") self.Thaw() - @debugging + def do_tab_window(self,xml_dom,parent_wnd): #def do_tab_window(self, etreeEl, parent_wnd): # if container window loop through childern and do a recursive call temp_wnd = orpgTabberWnd(parent_wnd, style=FNB.FNB_ALLOW_FOREIGN_DND) - children = xml_dom._get_childNodes() + children = xml_dom.getchildren() for c in children: wnd = self.build_window(c,temp_wnd) - name = c.getAttribute("name") + name = c.get("name") temp_wnd.AddPage(wnd, name, False) """ @@ -783,17 +780,17 @@ """ return temp_wnd - @debugging + def build_window(self, xml_dom, parent_wnd): - name = xml_dom._get_nodeName() + name = xml_dom.tag if name == "DockLayout" or name == "dock": return - dirc = xml_dom.getAttribute("direction") #should NOT use dir, it is a built in function. - pos = xml_dom.getAttribute("pos") - height = xml_dom.getAttribute("height") - width = xml_dom.getAttribute("width") - cap = xml_dom.getAttribute("caption") - dockable = xml_dom.getAttribute("dockable") - layer = xml_dom.getAttribute("layer") + dirc = xml_dom.get("direction") #should NOT use dir, it is a built in function. + pos = xml_dom.get("pos") + height = xml_dom.get("height") + width = xml_dom.get("width") + cap = xml_dom.get("caption") + dockable = xml_dom.get("dockable") + layer = xml_dom.get("layer") try: layer = int(layer); dockable = int(dockable) except: layer = 0; dockable = 1 @@ -805,9 +802,9 @@ elif name == "tree": temp_wnd = orpg.gametree.gametree.game_tree(parent_wnd, -1) self.tree = temp_wnd - if settings.get_setting('ColorTree') == '1': - self.tree.SetBackgroundColour(settings.get_setting('bgcolor')) - self.tree.SetForegroundColour(settings.get_setting('textcolor')) + if settings.get('ColorTree') == '1': + self.tree.SetBackgroundColour(settings.get('bgcolor')) + self.tree.SetForegroundColour(settings.get('textcolor')) else: self.tree.SetBackgroundColour('white') self.tree.SetForegroundColour('black') @@ -820,9 +817,9 @@ elif name == "player": temp_wnd = orpg.player_list.player_list(parent_wnd) self.players = temp_wnd - if settings.get_setting('ColorTree') == '1': - self.players.SetBackgroundColour(settings.get_setting('bgcolor')) - self.players.SetForegroundColour(settings.get_setting('textcolor')) + if settings.get('ColorTree') == '1': + self.players.SetBackgroundColour(settings.get('bgcolor')) + self.players.SetForegroundColour(settings.get('textcolor')) else: self.players.SetBackgroundColour('white') self.players.SetForegroundColour('black') @@ -857,7 +854,7 @@ self._mgr.AddPane(temp_wnd, wndinfo) return temp_wnd - @debugging + def onPaneClose(self, evt): pane = evt.GetPane() #Arbitrary If ELIF fix. Items had incorrect ID's set. Finding correct ID will fix it for the iteration. @@ -873,55 +870,53 @@ evt.Skip() self._mgr.Update() - @debugging + def saveLayout(self): filename = dir_struct["user"] + "layout.xml" - temp_file = open(filename) - txt = temp_file.read() - xml_dom = xml.parseXml(txt)._get_documentElement() - temp_file.close() + layout = parse(filename) + xml_dom = layout.getroot() (x_size,y_size) = self.GetClientSize() (x_pos,y_pos) = self.GetPositionTuple() if self.IsMaximized(): max = 1 else: max = 0 - xml_dom.setAttribute("height", str(y_size)) - xml_dom.setAttribute("width", str(x_size)) - xml_dom.setAttribute("posx", str(x_pos)) - xml_dom.setAttribute("posy", str(y_pos)) - xml_dom.setAttribute("maximized", str(max)) - layout = xml_dom.getElementsByTagName("DockLayout") + xml_dom.set("height", str(y_size)) + xml_dom.set("width", str(x_size)) + xml_dom.set("posx", str(x_pos)) + xml_dom.set("posy", str(y_pos)) + xml_dom.set("maximized", str(max)) try: - textnode = xml.safe_get_text_node(layout[0]) - textnode._set_nodeValue(str(self._mgr.SavePerspective())) + xml_dom.find("DockLayout").text = str(self._mgr.SavePerspective()) except: - elem = minidom.Element('DockLayout') - elem.setAttribute("DO_NO_EDIT","True") - textnode = xml.safe_get_text_node(elem) - textnode._set_nodeValue(str(self._mgr.SavePerspective())) - xml_dom.appendChild(elem) + elem = Element('DockLayout') + elem.set("DO_NO_EDIT","True") + elem.text = str(self._mgr.SavePerspective()) + xml_dom.append(elem) + + layout.write(filename) + """ temp_file = open(filename, "w") - temp_file.write(xml_dom.toxml(1)) - temp_file.close() + temp_file.write(tostring(xml_dom)) + temp_file.close()""" - @debugging + def build_hotkeys(self): self.mainmenu.accel.xaccel.extend(self.chat.get_hot_keys()) self.mainmenu.accel.xaccel.extend(self.map.get_hot_keys()) - @debugging + def start_timer(self): self.poll_timer.Start(100) s = component.get('settings') - if s.get_setting("Heartbeat") == "1": + if s.get("Heartbeat") == "1": self.ping_timer.Start(1000*60) logger.debug("starting heartbeat...", True) - @debugging + def kill_mplay_session(self): self.game_name = "" self.session.start_disconnect() - @debugging + def quit_game(self, evt): dlg = wx.MessageDialog(self,"Exit gaming session?","Game Session",wx.YES_NO) if dlg.ShowModal() == wx.ID_YES: @@ -929,13 +924,13 @@ dlg.Destroy() self.kill_mplay_session() - @debugging + def on_status_event(self, evt): id = evt.get_id() status = evt.get_data() if id == orpg.networking.mplay_client.STATUS_SET_URL: self.status.set_url(status) - @debugging + def on_player_event(self, evt): id = evt.get_id() player = evt.get_data() @@ -951,89 +946,53 @@ self.players.update_player(player) self.players.Refresh() - @debugging + def on_group_event(self, evt): id = evt.get_id() data = evt.get_data() if id == orpg.networking.mplay_client.GROUP_NEW: self.gs.add_room(data) elif id == orpg.networking.mplay_client.GROUP_DEL: - self.password_manager.RemoveGroupData(data) + # self.password_manager.RemoveGroupData(data) #Removed for debugging. self.gs.del_room(data) elif id == orpg.networking.mplay_client.GROUP_UPDATE: self.gs.update_room(data) - @debugging + def on_receive(self, data, player): # see if we are ignoring this user - (ignore_id,ignore_name) = self.session.get_ignore_list() + (ignore_id, ignore_name) = self.session.get_ignore_list() for m in ignore_id: if m == player[2]: logger.debug("ignoring message from player:" + player[0], True); return # ok we are not ignoring this message #recvSound = "RecvSound" # this will be the default sound. Whisper will change this below + + ### Alpha ### + etreeEl = Element('msg') + try: etreeEl.append(fromstring(data)) + except: etreeEl.text = data + ### Remove after Element Tree is integrated further ### if player: display_name = self.chat.chat_display_name(player) else: display_name = "Server Administrator" - if data[:5] == "' for the font within the - first 22 values - """ - index = data[:22].find( ">" ) - if index == -1: - data = "** " + self.chat.colorize( self.chat.infocolor, display_name + data ) + " **" - - else: - """ - This means that we found a valid font string, so we can - simply plug the name into the string between the start and - stop font delimiter - """ - print "pre data = " + data - data = data[:22] + "** " + display_name + " " + data[22:] + " **" - print "post data = " + data - - elif data[:2] == "/w": - data = data.replace("/w","") - data = "" + display_name + " (whispering): " + data - - else: - # Normal text - if player: data = "" + display_name + ": " + data - else: data = "" + display_name + "-> " + data - self.chat.Post(data) - - @debugging + for child in etreeEl.getchildren(): + if child.tag == 'tree': + ### Alpha ### Allows users to decide if they want the node or not. + dlg = wx.MessageDialog(None, display_name + ' is trying to send you a tree node. Accept?', 'Question', + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + if dlg.ShowModal() == wx.ID_YES: + dlg.Destroy() + debug(child) + self.tree.on_receive_data(tostring(child)) #Removed player object because it was unused. + self.chat.InfoPost(display_name + " has sent you a tree node...") + elif child.tag == 'map': + #TODO: Fix map to accepts elements + self.map.new_data(tostring(child)) + elif child.tag == 'chat': + msg = orpg.chat.chat_msg.chat_msg(tostring(child)) + self.chat.post_incoming_msg(msg, player) + def on_mplay_event(self, evt): id = evt.get_id() if id == orpg.networking.mplay_client.MPLAY_CONNECTED: @@ -1052,28 +1011,28 @@ ####Begin changes for Custom Exit Message by mDuo13###### elif id == orpg.networking.mplay_client.MPLAY_DISCONNECTING: settings = component.get('settings') - custom_msg = settings.get_setting("dcmsg") + custom_msg = settings.get("dcmsg") custom_msg=custom_msg[:80] if custom_msg[:3]=="/me": self.chat.send_chat_message(custom_msg[3:], 3) else: self.chat.system_message(custom_msg) #####End Changes for Custom Exit Message by mDuo13 - elif id== orpg.networking.mplay_client.MPLAY_GROUP_CHANGE: + elif id == orpg.networking.mplay_client.MPLAY_GROUP_CHANGE: group = evt.get_data() self.chat.InfoPost("Moving to room '"+group[1]+"'..") if self.gs : self.gs.set_cur_room_text(group[1]) self.players.reset() - elif id== orpg.networking.mplay_client.MPLAY_GROUP_CHANGE_F: + elif id == orpg.networking.mplay_client.MPLAY_GROUP_CHANGE_F: self.chat.SystemPost("Room access denied!") - @debugging + def OnCloseWindow(self, event): dlg = wx.MessageDialog(self, "Quit OpenRPG?", "OpenRPG", wx.YES_NO) if dlg.ShowModal() == wx.ID_YES: dlg.Destroy() self.closed_confirmed() - @debugging + def closed_confirmed(self): self.activeplugins = component.get('plugins') self.aliaslib.OnMB_FileSave(None) @@ -1096,9 +1055,9 @@ logger.general("[WARNING] Map error pre_exit_cleanup()", True) try: - save_tree = string.upper(settings.get_setting("SaveGameTreeOnExit")) + save_tree = string.upper(settings.get("SaveGameTreeOnExit")) if (save_tree != "0") and (save_tree != "False") and (save_tree != "NO"): - self.tree.save_tree(settings.get_setting("gametree")) + self.tree.save_tree(settings.get("gametree")) except Exception: logger.general("[WARNING] Error saving gametree", True) @@ -1202,7 +1161,7 @@ ## Application class ######################################## class orpgSplashScreen(wx.SplashScreen): - @debugging + def __init__(self, parent, bitmapfile, duration, callback): wx.SplashScreen.__init__(self, wx.Bitmap(bitmapfile), wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT, duration, None, -1) @@ -1211,18 +1170,15 @@ self.Bind(wx.EVT_CLOSE, self.callback) class orpgApp(wx.App): - @debugging + def OnInit(self): - component.add('log', logger) component.add('xml', xml) component.add('settings', settings) component.add('validate', validate) component.add("tabbedWindows", []) - logger._set_log_level = int(settings.get_setting('LoggingLevel')) - logger._set_log_to_console(False) - + #Update Manager self.manifest = manifest.ManifestChanges() self.called = False @@ -1233,13 +1189,13 @@ wx.Yield() return True - @debugging + def OnKeyPress(self, evt): #Event handler if evt.AltDown() and evt.CmdDown() and evt.KeyCode == ord('I'): self.ShowShell() else: evt.Skip() - @debugging + def ShowShell(self): #Show the PyCrust window. if not self._crust: @@ -1249,7 +1205,7 @@ self._crust.shell.interp.locals['win'] = win self._crust.Show() - @debugging + def AfterSplash(self,evt): if not self.called: self.splash.Hide() @@ -1264,7 +1220,7 @@ wx.CallAfter(self.splash.Close) return True - @debugging + def OnExit_CleanUp(self): logger.debug("Preforming cleanup\n") try: del os.environ["OPENRPG_BASE"] @@ -1274,7 +1230,7 @@ try: os.remove(os.environ["OPENRPG_BASE"] + os.sep + 'orpg' + os.sep + 'dirpath' + os.sep + 'approot.pyc') except: pass - @debugging + def OnExit(self): self.OnExit_CleanUp() #Exit diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/background.py --- a/orpg/mapper/background.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/background.py Tue Nov 10 14:11:28 2009 -0600 @@ -134,6 +134,12 @@ if self.bg_bmp == None or not self.bg_bmp.Ok() or ((self.type != BG_TEXTURE) and (self.type != BG_IMAGE)): return False dc2 = wx.MemoryDC() + + ### Temporary ### + try: self.bg_bmp = self.bg_bmp.ConvertToBitmap() + except: pass + ################# + dc2.SelectObject(self.bg_bmp) topLeft = [int(topleft[0]/scale), int(topleft[1]/scale)] topRight = [int((topleft[0]+size[0]+1)/scale)+1, int((topleft[1]+size[1]+1)/scale)+1] diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/background_msg.py --- a/orpg/mapper/background_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/background_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -27,7 +27,8 @@ # __version__ = "$Id: background_msg.py,v 1.8 2006/11/04 21:24:21 digitalxero Exp $" -from base_msg import * +from base_msg import map_element_msg_base +from xml.etree.ElementTree import ElementTree class bg_msg(map_element_msg_base): diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/base_msg.py --- a/orpg/mapper/base_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/base_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -30,13 +30,15 @@ from threading import RLock from orpg.networking.mplay_client import * +from xml.etree.ElementTree import XML, fromstring, parse + class map_element_msg_base: # This is a base class def __init__(self,reentrant_lock_object = None): - if not hasattr(self,"tagname"): - raise Exception, "This is a virtual class that cannot be directly instantiated. Set self.tagname in derived class." + if not self.tagname: + raise Exception, "This is a virtual class that cannot be directly instantiated. Set self.tagname in derived class."; exit() self._props = {} # This is a dictionary that holds (value,changed) 2-tuples, indexed by attribute @@ -195,40 +197,38 @@ ######################################### # XML importers begin - def _from_dom(self,xml_dom,prop_func): + def _from_dom(self,xml,prop_func): self.p_lock.acquire() - if xml_dom.tagName == self.tagname: - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - prop_func(k,xml_dom.getAttribute(k)) + if xml.tag == self.tagname: + if xml.keys(): + for k in xml.keys(): + prop_func(k, xml.get(k)) else: self.p_lock.release() raise Exception, "Error attempting to modify a " + self.tagname + " from a non-<" + self.tagname + "/> element" self.p_lock.release() - def init_from_dom(self,xml_dom): - # xml_dom must be pointing to an empty tag. Override in a derived class for and other similar tags. - self._from_dom(xml_dom,self.init_prop) + def init_from_dom(self, xml): + # xml must be pointing to an empty tag. Override in a derived class for and other similar tags. + self._from_dom(xml,self.init_prop) - def set_from_dom(self,xml_dom): - # xml_dom must be pointing to an empty tag. Override in a derived class for and other similar tags - self._from_dom(xml_dom,self.set_prop) + def set_from_dom(self, xml): + # xml must be pointing to an empty tag. Override in a derived class for and other similar tags + self._from_dom(xml, self.set_prop) - def init_from_xml(self,xml): - xml_dom = parseXml(xml) - node_list = xml_dom.getElementsByTagName(self.tagname) + def init_from_xml(self, tree): + #tree = XML(xmlString) + node_list = tree.findall(self.tagname) if len(node_list) < 1: print "Warning: no <" + self.tagname + "/> elements found in DOM." else: - while len(node_list): self.init_from_dom(node_list.pop()) - if xml_dom: xml_dom.unlink() + while len(node_list): + self.init_from_dom(node_list.pop()) - def set_from_xml(self,xml): - xml_dom = parseXml(xml) - node_list = xml_dom.getElementsByTagName(self.tagname) + def set_from_xml(self, tree): + #tree = XML(xmlString) + node_list = tree.findall(self.tagname) if len(node_list) < 1: print "Warning: no <" + self.tagname + "/> elements found in DOM." else: while len(node_list): self.set_from_dom(node_list.pop()) - if xml_dom: xml_dom.unlink() - # XML importers end ######################################### diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/fog.py --- a/orpg/mapper/fog.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/fog.py Tue Nov 10 14:11:28 2009 -0600 @@ -82,8 +82,8 @@ self.canvas = canvas self.log = component.get('log') layer_base.__init__(self) - self.color = wx.Color(128,128,128) - if "__WXGTK__" not in wx.PlatformInfo: self.color = wx.Color(128,128,128, 128) + self.color = wx.Color(128, 128, 128) + #if "__WXGTK__" not in wx.PlatformInfo: self.color = wx.Color(128,128,128, 128) self.fogregion = wx.Region() self.fogregion.Clear() self.fog_bmp = None @@ -116,71 +116,40 @@ if not self.use_fog: return size = self.canvas.size - self.width = size[0]/COURSE+1 - self.height = size[1]/COURSE+1 - self.fog_bmp = wx.EmptyBitmap(self.width+2,self.height+2) + self.width = size[0] + self.height = size[1] + self.fog_bmp = wx.EmptyBitmap(self.width,self.height) self.fill_fog() def fill_fog(self): if not self.use_fog: return - if "__WXGTK__" in wx.PlatformInfo: - mdc = wx.MemoryDC() - mdc.SelectObject(self.fog_bmp) - mdc.SetPen(wx.TRANSPARENT_PEN) - if (self.canvas.frame.session.role == "GM"): color = self.color - else: color = wx.BLACK - self.last_role = self.canvas.frame.session.role - mdc.SetBrush(wx.Brush(color,wx.SOLID)) - mdc.DestroyClippingRegion() + mdc = wx.MemoryDC() + mdc.SelectObject(self.fog_bmp) + mdc.SetPen(wx.TRANSPARENT_PEN) + if (self.canvas.frame.session.role == "GM"): color = self.color + else: color = wx.BLACK + self.last_role = self.canvas.frame.session.role + mdc.SetBrush(wx.Brush(color,wx.SOLID)) + mdc.DestroyClippingRegion() + mdc.DrawRectangle(0, 0, self.width+2, self.height+2) + mdc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID)) + if self.fogregion.GetBox().GetWidth()>0: + mdc.SetClippingRegionAsRegion(self.fogregion) mdc.DrawRectangle(0, 0, self.width+2, self.height+2) - mdc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID)) - if self.fogregion.GetBox().GetWidth()>0: - mdc.SetClippingRegionAsRegion(self.fogregion) - mdc.DrawRectangle(0, 0, self.width+2, self.height+2) - mdc.SelectObject(wx.NullBitmap) - del mdc + mdc.SelectObject(wx.NullBitmap) + del mdc def layerDraw(self, dc, topleft, size): if self.fog_bmp == None or not self.fog_bmp.Ok() or not self.use_fog: return if self.last_role != self.canvas.frame.session.role: self.fill_fog() - if "__WXGTK__" not in wx.PlatformInfo: - gc = wx.GraphicsContext.Create(dc) - gc.SetBrush(wx.Brush(wx.BLACK)) - if (self.canvas.frame.session.role == "GM"): - gc.SetBrush(wx.Brush(self.color)) - rgn = wx.Region(0, 0, self.canvas.size[0]+2, self.canvas.size[1]+2) - if not self.fogregion.IsEmpty(): rgn.SubtractRegion(self.fogregion) - gc.ClipRegion(rgn) - gc.DrawRectangle(0, 0, self.canvas.size[0]+2, self.canvas.size[1]+2) - else: - sc = dc.GetUserScale() - bmp = wx.EmptyBitmap(size[0],size[1]) - mdc = wx.MemoryDC() - mdc.BeginDrawing() - mdc.SelectObject(bmp) - mdc.SetPen(wx.TRANSPARENT_PEN) - mdc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID)) - mdc.DrawRectangle(0,0,size[0],size[1]) - srct = [int(topleft[0]/(sc[0]*COURSE)), int(topleft[1]/(sc[1]*COURSE))] - srcsz = [int((int(size[0]/COURSE+1)*COURSE)/(sc[0]*COURSE))+2, - int((int(size[1]/COURSE+1)*COURSE)/(sc[1]*COURSE))+2] - if (srct[0]+srcsz[0] > self.width): srcsz[0] = self.width-srct[0] - if (srct[1]+srcsz[1] > self.height): srcsz[1] = self.height-srct[1] - img = wx.ImageFromBitmap(self.fog_bmp).GetSubImage(wx.Rect(srct[0], srct[1], srcsz[0], srcsz[1])) - img.Rescale(srcsz[0]*COURSE*sc[0], srcsz[1]*COURSE*sc[1]) - fog = wx.BitmapFromImage(img) - mdc.SetDeviceOrigin(-topleft[0], -topleft[1]) - mdc.DrawBitmap(fog, srct[0]*COURSE*sc[0], srct[1]*COURSE*sc[1]) - mdc.SetDeviceOrigin(0,0) - mdc.SetUserScale(1,1) - mdc.EndDrawing() - dc.SetUserScale(1,1) - dc.Blit(topleft[0], topleft[1], size[0], size[1], mdc,0,0,wx.AND) - dc.SetUserScale(sc[0],sc[1]) - mdc.SelectObject(wx.NullBitmap) - del mdc + + mdc = wx.MemoryDC() + mdc.SelectObject(self.fog_bmp) + dc.Blit(0, 0, self.canvas.size[0], self.canvas.size[1], mdc, 0, 0, wx.AND) + mdc.SelectObject(wx.NullBitmap) + del mdc def createregn2(self, polyline, mode, show): regn = self.scanConvert(polyline) @@ -198,7 +167,7 @@ if not self.fogregion.IsEmpty(): self.fogregion.SubtractRegion(regn) else: - self.fogregion = wx.Region(0, 0, self.canvas.size[0]+2, self.canvas.size[1]+2) + self.fogregion = wx.Region(0, 0, self.canvas.size[0], self.canvas.size[1]) self.fogregion.SubtractRegion(regn) self.del_area(area, show) @@ -216,13 +185,13 @@ list = IRegion().scan_Convert(polypt) for i in list: if regn.IsEmpty(): - if "__WXGTK__" not in wx.PlatformInfo: regn = wx.Region(i.left*COURSE, i.y*COURSE, - i.right*COURSE+1-i.left*COURSE, 1*COURSE) - else: regn = wx.Region(i.left, i.y, i.right+1-i.left, 1) + #if "__WXGTK__" not in wx.PlatformInfo: + regn = wx.Region(i.left*COURSE, i.y*COURSE, i.right*COURSE+1-i.left*COURSE, 1*COURSE) + #else: regn = wx.Region(i.left, i.y, i.right+1-i.left, 1) else: - if "__WXGTK__" not in wx.PlatformInfo: regn.Union(i.left*COURSE, i.y*COURSE, - i.right*COURSE+1-i.left*COURSE, 1*COURSE) - else: regn.Union(i.left, i.y, i.right+1-i.left, 1) + #if "__WXGTK__" not in wx.PlatformInfo: + regn.Union(i.left*COURSE, i.y*COURSE, i.right*COURSE+1-i.left*COURSE, 1*COURSE) + #else: regn.Union(i.left, i.y, i.right+1-i.left, 1) return regn def add_area(self, area="", show="Yes"): @@ -246,16 +215,16 @@ ri = wx.RegionIterator(self.fogregion) if not (ri.HaveRects()): fog_string = FogArea("all", self.log).toxml("del") while ri.HaveRects(): - if "__WXGTK__" not in wx.PlatformInfo: - x1 = ri.GetX()/COURSE - x2 = x1+(ri.GetW()/COURSE)-1 - y1 = ri.GetY()/COURSE - y2 = y1+(ri.GetH()/COURSE)-1 - else: - x1 = ri.GetX() - x2 = x1+ri.GetW()-1 - y1 = ri.GetY() - y2 = y1+ri.GetH()-1 + #if "__WXGTK__" not in wx.PlatformInfo: + x1 = ri.GetX()/COURSE + x2 = x1+(ri.GetW()/COURSE)-1 + y1 = ri.GetY()/COURSE + y2 = y1+(ri.GetH()/COURSE)-1 + #else: + # x1 = ri.GetX() + # x2 = x1+ri.GetW()-1 + # y1 = ri.GetY() + # y2 = y1+ri.GetH()-1 poly = FogArea(str(x1) + "," + str(y1) + ";" + str(x2) + "," + str(y1) + ";" + str(x2) + "," + str(y2) + ";" + diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/fog_msg.py --- a/orpg/mapper/fog_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/fog_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -27,13 +27,14 @@ from base_msg import * from region import * -from orpg.minidom import Element +#from orpg.minidom import Element import string +from xml.etree.ElementTree import ElementTree class fog_msg(map_element_msg_base): def __init__(self,reentrant_lock_object = None): - self.tagname = "fog" + self.tag = "fog" map_element_msg_base.__init__(self,reentrant_lock_object) self.use_fog = 0 self.fogregion=IRegion() @@ -41,16 +42,16 @@ def get_line(self,outline,action,output_act): elem = Element( "poly" ) - if ( output_act ): elem.setAttribute( "action", action ) - if ( outline == 'all' ) or ( outline == 'none' ): elem.setAttribute( "outline", outline ) + if ( output_act ): elem.set( "action", action ) + if ( outline == 'all' ) or ( outline == 'none' ): elem.set( "outline", outline ) else: - elem.setAttribute( "outline", "points" ) + elem.set( "outline", "points" ) for pair in string.split( outline, ";" ): p = string.split( pair, "," ) point = Element( "point" ) - point.setAttribute( "x", p[ 0 ] ) - point.setAttribute( "y", p[ 1 ] ) - elem.appendChild( point ) + point.set( "x", p[ 0 ] ) + point.set( "y", p[ 1 ] ) + elem.append( point ) str = elem.toxml() elem.unlink() return str @@ -94,11 +95,11 @@ def interpret_dom(self,xml_dom): self.use_fog=1 #print 'fog_msg.interpret_dom called' - children = xml_dom._get_childNodes() + children = xml_dom.getchildren() #print "children",children for l in children: - action = l.getAttribute("action") - outline = l.getAttribute("outline") + action = l.get("action") + outline = l.get("outline") #print "action/outline",action, outline if (outline=="all"): polyline=[] @@ -109,9 +110,9 @@ self.fogregion.Clear() else: polyline=[] - list = l._get_childNodes() + list = l.getchildren() for node in list: - polyline.append( IPoint().make( int(node.getAttribute("x")), int(node.getAttribute("y")) ) ) + polyline.append( IPoint().make( int(node.get("x")), int(node.get("y")) ) ) # pointarray = outline.split(";") # for m in range(len(pointarray)): # pt=pointarray[m].split(",") diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/grid_msg.py --- a/orpg/mapper/grid_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/grid_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -30,8 +30,10 @@ from base_msg import map_element_msg_base #from base_msg import * ## ?? import all? Deprecated!? +from xml.etree.ElementTree import ElementTree + class grid_msg(map_element_msg_base): def __init__(self,reentrant_lock_object = None): self.tagname = "grid" - map_element_msg_base.__init__(self,reentrant_lock_object) + map_element_msg_base.__init__(self, reentrant_lock_object) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/images.py --- a/orpg/mapper/images.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/images.py Tue Nov 10 14:11:28 2009 -0600 @@ -25,131 +25,137 @@ # # Description: # -__version__ = "$Id: images.py,v 1.21 2007/12/11 04:07:15 digitalxero Exp $" +from __future__ import with_statement -import urllib -import Queue -import thread +import urllib, Queue, thread, time from threading import Lock -import time +from orpg.orpg_wx import * +from orpg.orpgCore import * -from orpg.orpg_wx import * -from orpg.orpgCore import component from orpg.dirpath import dir_struct from orpg.tools.orpg_log import logger - -def singleton(cls): - instances = {} - def getinstance(): - if cls not in instances: - instances[cls] = cls() - return instances[cls] - return getinstance() +from orpg.tools.settings import settings class ImageHandlerClass(object): __cache = {} __fetching = {} __queue = Queue.Queue(0) __lock = Lock() - chat = component.get("chat") + + def __new__(cls): + it = cls.__dict__.get("__it__") + if it is not None: + return it + cls.__it__ = it = object.__new__(cls) + return it def load(self, path, image_type, imageId): - """Load an image, with a intermideary fetching image shown while it loads in a background thread""" - if self.__cache.has_key(path): return wx.ImageFromMime(self.__cache[path][1], - self.__cache[path][2]).ConvertToBitmap() - if not self.__fetching.has_key(path): + # Load an image, with a intermideary fetching image shown while it loads in a background thread + if self.__cache.has_key(path): + return wx.ImageFromMime(self.__cache[path][1], + self.__cache[path][2]) + if path not in self.__fetching: self.__fetching[path] = True - """Start Image Loading Thread""" - thread.start_new_thread(self.__loadThread, (path, image_type, imageId)) + #Start Image Loading Thread + thread.start_new_thread(self.__loadThread, + (path, image_type, imageId)) else: - if self.__fetching[path] is True: thread.start_new_thread(self.__loadCacheThread, (path, image_type, imageId)) + if self.__fetching[path]: + thread.start_new_thread(self.__loadCacheThread, + (path, image_type, imageId)) return wx.Bitmap(dir_struct["icon"] + "fetching.png", wx.BITMAP_TYPE_PNG) def directLoad(self, path): - """Directly load an image, no threads""" - if self.__cache.has_key(path): return wx.ImageFromMime(self.__cache[path][1], - self.__cache[path][2]).ConvertToBitmap() + # Directly load an image, no threads + if path in self.__cache: + return wx.ImageFromMime(self.__cache[path][1], + self.__cache[path][2]) uriPath = urllib.unquote(path) try: d = urllib.urlretrieve(uriPath) - """We have to make sure that not only did we fetch something, but that - it was an image that we got back.""" + # We have to make sure that not only did we fetch something, but that + # it was an image that we got back. if d[0] and d[1].getmaintype() == "image": - self.__cache[path] = (path, d[0], d[1].gettype(), None) - return wx.ImageFromMime(self.__cache[path][1], self.__cache[path][2]).ConvertToBitmap() + with self.__lock: + self.__cache[path] = (path, d[0], d[1].gettype(), None) + return wx.ImageFromMime(self.__cache[path][1], self.__cache[path][2]) else: - logger.exception(str("Image refused to load or URI did not reference a valid image: " + path), True) + logger.general("Image refused to load or URI did not " + "reference a valid image: " + path, True) return None except IOError: - logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True) + logger.general("Unable to resolve/open the specified URI; " + "image was NOT loaded: " + path, True) return None def cleanCache(self): - """Shrinks the Cache down to the proper size""" - try: cacheSize = int(component.get('settings').get_setting("ImageCacheSize")) - except: cacheSize = 32 + # Shrinks the Cache down to the proper size + try: + cacheSize = int(settings.get("ImageCacheSize")) + except: + cacheSize = 32 cache = self.__cache.keys() cache.sort() - for key in cache[cacheSize:]: del self.__cache[key] + for key in cache[cacheSize:]: + del self.__cache[key] def flushCache(self): - """This function will flush all images contained within the image cache.""" - self.__lock.acquire() - try: + # This function will flush all images contained within the image cache. + with self.__lock: self.__cache = {} self.__fetching = {} - finally: - self.__lock.release() urllib.urlcleanup() - """Private Methods""" +#Private Methods def __loadThread(self, path, image_type, imageId): uriPath = urllib.unquote(path) - self.__lock.acquire() try: d = urllib.urlretrieve(uriPath) - """We have to make sure that not only did we fetch something, but that - it was an image that we got back.""" + # We have to make sure that not only did we fetch something, but that + # it was an image that we got back. if d[0] and d[1].getmaintype() == "image": - self.__cache[path] = (path, d[0], d[1].gettype(), imageId) - self.__queue.put((self.__cache[path], image_type, imageId)) - if self.__fetching.has_key(path): del self.__fetching[path] + with self.__lock: + self.__cache[path] = (path, d[0], d[1].gettype(), imageId) + self.__queue.put((self.__cache[path], image_type, imageId)) + if path in self.__fetching: del self.__fetching[path] else: - logger.exception(str("Image refused to load or URI did not reference a valid image: " + path), True) + logger.general("Image refused to load or URI did not " + "reference a valid image: " + path, True) del self.__fetching[path] except IOError: del self.__fetching[path] - logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True) - finally: self.__lock.release() + logger.general("Unable to resolve/open the specified URI; " + "image was NOT laoded: " + path, True) def __loadCacheThread(self, path, image_type, imageId): try: st = time.time() - while self.__fetching.has_key(path) and self.__fetching[path] is not False: + while path in self.__fetching and self.__fetching[path] is not False: time.sleep(0.025) if (time.time()-st) > 120: - logger.general("Timeout: " + path) + logger.general("Timeout: " + path, True) + del self.__fetching[path] break except: del self.__fetching[path] - logger.exception(str("Unable to resolve/open the specified URI; image was NOT loaded: " + path), True) - return - self.__lock.acquire() - try: - logger.info("Adding Image to Queue from Cache: " + str(self.__cache[path]), True) - self.__queue.put((self.__cache[path], image_type, imageId)) - finally: self.__lock.release() + logger.general("Unable to resolve/open the specified URI; " + "image was NOT loaded: " + path, True) + return + with self.__lock: + if path in self.__cache: + logger.debug("Adding Image to Queue from Cache: " + str(self.__cache[path])) + self.__queue.put((self.__cache[path], image_type, imageId)) + else: self.__loadThread(path, image_type, imageId) - """Property Methods""" + #Property Methods def _getCache(self): return self.__cache def _getQueue(self): return self.__queue - """Properties""" + #Properties Cache = property(_getCache) Queue = property(_getQueue) -ImageHandler = singleton(ImageHandlerClass) -component.add('ImageHandler', ImageHandler) +ImageHandler = ImageHandlerClass() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/map.py --- a/orpg/mapper/map.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/map.py Tue Nov 10 14:11:28 2009 -0600 @@ -62,6 +62,7 @@ self.session = component.get("session") wx.ScrolledWindow.__init__(self, parent, ID, style=wx.HSCROLL | wx.VSCROLL | wx.FULL_REPAINT_ON_RESIZE | wx.SUNKEN_BORDER ) + self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.frame = parent self.MAP_MODE = 1 #Mode 1 = MINI, 2 = DRAW, 3 = TAPE MEASURE self.layers = {} @@ -108,6 +109,15 @@ # miniatures drag self.drag = None + #self.Bind(wx.EVT_MOUSEWHEEL, self.MouseWheel) + + def MouseWheel(self, evt): + if evt.CmdDown(): + print evt.GetWheelRotation() + if evt.GetWheelRotation() > 0: self.on_zoom_in(None) + elif evt.GetWheelRotation() < 0: self.on_zoom_out(None) + else: pass + def better_refresh(self, event=None): self.Refresh(True) @@ -116,43 +126,21 @@ def processImages(self, evt=None): self.session = component.get("session") + tabs = ['Background', 'Grid', 'Miniatures', 'Whiteboard', 'Fog', 'General'] if self.session.my_role() == self.session.ROLE_LURKER or (str(self.session.group_id) == '0' and str(self.session.status) == '1'): - cidx = self.parent.get_tab_index("Background") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Grid") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Miniatures") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Whiteboard") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Fog") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("General") - self.parent.layer_tabs.EnableTab(cidx, False) - else: - cidx = self.parent.get_tab_index("Background") - if not self.parent.layer_tabs.GetEnabled(cidx): - cidx = self.parent.get_tab_index("Miniatures") - self.parent.layer_tabs.EnableTab(cidx, True) - cidx = self.parent.get_tab_index("Whiteboard") - self.parent.layer_tabs.EnableTab(cidx, True) - cidx = self.parent.get_tab_index("Background") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Grid") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("Fog") - self.parent.layer_tabs.EnableTab(cidx, False) - cidx = self.parent.get_tab_index("General") - self.parent.layer_tabs.EnableTab(cidx, False) - if self.session.my_role() == self.session.ROLE_GM: - cidx = self.parent.get_tab_index("Background") - self.parent.layer_tabs.EnableTab(cidx, True) - cidx = self.parent.get_tab_index("Grid") - self.parent.layer_tabs.EnableTab(cidx, True) - cidx = self.parent.get_tab_index("Fog") - self.parent.layer_tabs.EnableTab(cidx, True) - cidx = self.parent.get_tab_index("General") - self.parent.layer_tabs.EnableTab(cidx, True) + for tab in tabs: + cidx = self.parent.get_tab_index(tab) + self.parent.tabs.EnableTab(cidx, False) + elif self.session.my_role() == self.session.ROLE_PLAYER: + for tab in tabs: + cidx = self.parent.get_tab_index(tab) + if tab == "Miniatures" or tab == "Whiteboard": + self.parent.tabs.EnableTab(cidx, True) + else: self.parent.tabs.EnableTab(cidx, False) + elif self.session.my_role() == self.session.ROLE_GM and str(self.session.group_id) != '0': + for tab in tabs: + cidx = self.parent.get_tab_index(tab) + self.parent.tabs.EnableTab(cidx, True) if not self.cacheSizeSet: self.cacheSizeSet = True cacheSize = component.get('settings').get_setting("ImageCacheSize") @@ -160,19 +148,16 @@ else: pass if not ImageHandler.Queue.empty(): (path, image_type, imageId) = ImageHandler.Queue.get() - img = wx.ImageFromMime(path[1], path[2]).ConvertToBitmap() - try: - # Now, apply the image to the proper object - if image_type == "miniature": - min = self.layers['miniatures'].get_miniature_by_id(imageId) - min.set_bmp(img) - elif image_type == "background" or image_type == "texture": - self.layers['bg'].bg_bmp = img - if image_type == "background": self.set_size([img.GetWidth(), img.GetHeight()]) - except: pass + img = wx.ImageFromMime(path[1], path[2]) + # Now, apply the image to the proper object + if image_type == "miniature": + min = self.layers['miniatures'].get_miniature_by_id(imageId) + if min: min.set_bmp(img) + elif image_type == "background" or image_type == "texture": + self.layers['bg'].bg_bmp = img.ConvertToBitmap() + if image_type == "background": self.set_size([img.GetWidth(), img.GetHeight()]) # Flag that we now need to refresh! self.requireRefresh += 1 - """ Randomly purge an item from the cache, while this is lamo, it does keep the cache from growing without bounds, which is pretty important!""" if len(ImageHandler.Cache) >= self.cacheSize: @@ -253,9 +238,10 @@ topleft1 = self.GetViewStart() topleft = [topleft1[0]*scrollsize[0], topleft1[1]*scrollsize[1]] if (clientsize[0] > 1) and (clientsize[1] > 1): - dc = wx.MemoryDC() - bmp = wx.EmptyBitmap(clientsize[0]+1, clientsize[1]+1) - dc.SelectObject(bmp) + #dc = wx.MemoryDC() + #bmp = wx.EmptyBitmap(clientsize[0]+1, clientsize[1]+1) + #dc.SelectObject(bmp) + dc = wx.AutoBufferedPaintDC(self) dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(wx.Brush(self.GetBackgroundColour(), wx.SOLID)) dc.DrawRectangle(0,0,clientsize[0]+1,clientsize[1]+1) @@ -270,12 +256,12 @@ self.layers['fog'].layerDraw(dc, topleft, clientsize) dc.SetPen(wx.NullPen) dc.SetBrush(wx.NullBrush) - dc.SelectObject(wx.NullBitmap) - del dc - wdc = self.preppaint() - wdc.DrawBitmap(bmp, topleft[0], topleft[1]) + #dc.SelectObject(wx.NullBitmap) + #del dc + #wdc = self.preppaint() + #wdc.DrawBitmap(bmp, topleft[0], topleft[1]) if settings.get_setting("AlwaysShowMapScale") == "1": - self.showmapscale(wdc) + self.showmapscale(dc) try: evt.Skip() except: pass @@ -721,30 +707,35 @@ self.top_frame = component.get('frame') self.root_dir = os.getcwd() self.current_layer = 2 - self.layer_tabs = orpgTabberWnd(self, style=FNB.FNB_NO_X_BUTTON|FNB.FNB_BOTTOM|FNB.FNB_NO_NAV_BUTTONS) - self.layer_handlers = [] - self.layer_handlers.append(background_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[0],"Background") - self.layer_handlers.append(grid_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[1],"Grid") - self.layer_handlers.append(miniatures_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[2],"Miniatures", True) - self.layer_handlers.append(whiteboard_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[3],"Whiteboard") - self.layer_handlers.append(fog_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[4],"Fog") - self.layer_handlers.append(map_handler(self.layer_tabs,-1,self.canvas)) - self.layer_tabs.AddPage(self.layer_handlers[5],"General") - self.layer_tabs.SetSelection(2) + self.tabs = orpgTabberWnd(self, style=FNB.FNB_NO_X_BUTTON|FNB.FNB_BOTTOM|FNB.FNB_NO_NAV_BUTTONS) + self.handlers = {} + self.handlers[0]=(background_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[0],"Background") + self.handlers[1]=(grid_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[1],"Grid") + self.handlers[2]=(miniatures_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[2],"Miniatures", True) + self.handlers[3]=(whiteboard_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[3],"Whiteboard") + self.handlers[4]=(fog_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[4],"Fog") + self.handlers[5]=(map_handler(self.tabs,-1,self.canvas)) + self.tabs.AddPage(self.handlers[5],"General") + self.tabs.SetSelection(2) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.canvas, 1, wx.EXPAND) - self.sizer.Add(self.layer_tabs, 0, wx.EXPAND) + self.sizer.Add(self.tabs, 0, wx.EXPAND) self.SetSizer(self.sizer) self.Bind(FNB.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.on_layer_change) #self.Bind(wx.EVT_SIZE, self.on_size) self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) self.load_default() + ## Components for making Map Based plugins that create new tabs. + component.add('map_tabs', self.tabs) + component.add('map_layers', self.handlers) + component.add('map_wnd', self) + def OnLeave(self, evt): if "__WXGTK__" in wx.PlatformInfo: wx.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) @@ -798,41 +789,41 @@ os.chdir(self.root_dir) def get_current_layer_handler(self): - return self.layer_handlers[self.current_layer] + return self.handlers[self.current_layer] def get_tab_index(self, layer): """Return the index of a chatpanel in the wxNotebook.""" - for i in xrange(self.layer_tabs.GetPageCount()): - if (self.layer_tabs.GetPageText(i) == layer): + for i in xrange(self.tabs.GetPageCount()): + if (self.tabs.GetPageText(i) == layer): return i return 0 def on_layer_change(self, evt): - layer = self.layer_tabs.GetPage(evt.GetSelection()) - for i in xrange(0, len(self.layer_handlers)): - if layer == self.layer_handlers[i]: self.current_layer = i + layer = self.tabs.GetPage(evt.GetSelection()) + for i in xrange(0, len(self.handlers)): + if layer == self.handlers[i]: self.current_layer = i if self.current_layer == 0: - bg = self.layer_handlers[0] + bg = self.handlers[0] if (self.session.my_role() != self.session.ROLE_GM): bg.url_path.Show(False) else: bg.url_path.Show(True) self.canvas.Refresh(False) evt.Skip() def on_left_down(self, evt): - self.layer_handlers[self.current_layer].on_left_down(evt) + self.handlers[self.current_layer].on_left_down(evt) #double click handler added by Snowdog 5/03 def on_left_dclick(self, evt): - self.layer_handlers[self.current_layer].on_left_dclick(evt) + self.handlers[self.current_layer].on_left_dclick(evt) def on_right_down(self, evt): - self.layer_handlers[self.current_layer].on_right_down(evt) + self.handlers[self.current_layer].on_right_down(evt) def on_left_up(self, evt): - self.layer_handlers[self.current_layer].on_left_up(evt) + self.handlers[self.current_layer].on_left_up(evt) def on_motion(self, evt): - self.layer_handlers[self.current_layer].on_motion(evt) + self.handlers[self.current_layer].on_motion(evt) def MapBar(self, id, data): self.canvas.MAP_MODE = data @@ -849,8 +840,8 @@ except: pass def update_tools(self): - for h in self.layer_handlers: - h.update_info() + for h in self.handlers: + self.handlers[h].update_info() def on_hk_map_layer(self, evt): id = self.top_frame.mainmenu.GetHelpString(evt.GetId()) @@ -860,7 +851,7 @@ elif id == "Whiteboard Layer": self.current_layer = self.get_tab_index("Whiteboard") elif id == "Fog Layer": self.current_layer = self.get_tab_index("Fog") elif id == "General Properties": self.current_layer = self.get_tab_index("General") - self.layer_tabs.SetSelection(self.current_layer) + self.tabs.SetSelection(self.current_layer) def on_flush_cache(self, evt): ImageHandler.flushCache() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/map_msg.py --- a/orpg/mapper/map_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/map_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -34,6 +34,11 @@ from miniatures_msg import * from whiteboard_msg import * from fog_msg import * +import traceback +from orpg.dirpath import dir_struct + +from xml.etree.ElementTree import ElementTree, Element, iselement +from xml.etree.ElementTree import fromstring, tostring, parse """ @@ -46,25 +51,37 @@ """ +def Crash(type, value, crash): + crash_report = open(dir_struct["home"] + 'crash-report.txt', "w") + traceback.print_exception(type, value, crash, file=crash_report) + crash_report.close() + msg = '' + crash_report = open(dir_struct["home"] + 'crash-report.txt', "r") + for line in crash_report: msg += line + print msg + crash_report.close() + class map_msg(map_element_msg_base): def __init__(self,reentrant_lock_object = None): + print 'class map_msg' self.tagname = "map" - map_element_msg_base.__init__(self,reentrant_lock_object) + map_element_msg_base.__init__(self, reentrant_lock_object) - def init_from_dom(self,xml_dom): + def init_from_dom(self, xml_dom): + print 'init_from_dom', self.tagname self.p_lock.acquire() - if xml_dom.tagName == self.tagname: + if xml_dom.tag == self.tagname: # If this is a map message, look for the "action=new" # Notice we only do this when the root is a map tag - if self.tagname == "map" and xml_dom.hasAttribute("action") and xml_dom.getAttribute("action") == "new": + if self.tagname == "map" and xml_dom.get("action") == "new": self.clear() # Process all of the properties in each tag - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.init_prop(k,xml_dom.getAttribute(k)) - for c in xml_dom._get_childNodes(): - name = c._get_nodeName() + if xml_dom.keys(): + for k in xml_dom.keys(): + self.init_prop(k, xml_dom.get(k)) + for c in xml_dom.getchildren(): + name = c.tag if not self.children.has_key(name): if name == "miniatures": self.children[name] = minis_msg(self.p_lock) elif name == "grid": self.children[name] = grid_msg(self.p_lock) @@ -84,18 +101,19 @@ self.p_lock.release() def set_from_dom(self,xml_dom): + print 'set_from_dom' self.p_lock.acquire() - if xml_dom.tagName == self.tagname: + if xml_dom.tag == self.tagname: # If this is a map message, look for the "action=new" # Notice we only do this when the root is a map tag - if self.tagname == "map" and xml_dom.hasAttribute("action") and xml_dom.getAttribute("action") == "new": + if self.tagname == "map" and xml_dom.get("action") == "new": self.clear() # Process all of the properties in each tag - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.set_prop(k,xml_dom.getAttribute(k)) - for c in xml_dom._get_childNodes(): - name = c._get_nodeName() + if xml_dom.keys(): + for k in xml_dom.keys(): + self.set_prop(k,xml_dom.get(k)) + for c in xml_dom.getchildren(): + name = c.tag if not self.children.has_key(name): if name == "miniatures": self.children[name] = minis_msg(self.p_lock) elif name == "grid": self.children[name] = grid_msg(self.p_lock) @@ -119,3 +137,4 @@ def get_changed_xml(self, action="update", output_action=1): return map_element_msg_base.get_changed_xml(self, action, output_action) +crash = sys.excepthook = Crash diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/miniatures.py --- a/orpg/mapper/miniatures.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/miniatures.py Tue Nov 10 14:11:28 2009 -0600 @@ -38,6 +38,9 @@ import xml.dom.minidom as minidom from orpg.tools.orpg_settings import settings +from xml.etree.ElementTree import ElementTree, Element +from xml.etree.ElementTree import fromstring, tostring + MIN_STICKY_BACK = -0XFFFFFF MIN_STICKY_FRONT = 0xFFFFFF @@ -68,15 +71,14 @@ return value class BmpMiniature: - def __init__(self, id,path, bmp, pos=cmpPoint(0,0), + def __init__(self, id, path, bmp, pos=cmpPoint(0,0), heading=FACE_NONE, face=FACE_NONE, label="", locked=False, hide=False, snap_to_align=SNAPTO_ALIGN_CENTER, - zorder=0, width=0, height=0, log=None, local=False, localPath='', localTime=-1): + zorder=0, width=0, height=0, log=None, local=False, localPath='', localTime=-1, func='none'): self.heading = heading self.face = face self.label = label self.path = path - self.bmp = bmp self.pos = pos self.selected = False self.locked = locked @@ -97,13 +99,30 @@ self.bottom = bmp.GetHeight() self.isUpdated = False self.gray = False + print bmp + self.set_bmp(bmp) def __del__(self): - del self.bmp - self.bmp = None + del self.image + self.image = None def set_bmp(self, bmp): - self.bmp = bmp + ### Alpha ### + try: bmp = bmp.ConvertToImage() + except: pass + ### Still ironing out some kinks to FlexiRPG's map features ### + self.image = bmp + self.image.ConvertAlphaToMask() + self.generate_bmps() + + def generate_bmps(self): + if self.width: + bmp = self.image.Copy() + bmp.Rescale(int(self.width), int(self.height)) + else: + bmp = self.image + self.bmp = bmp.ConvertToBitmap() + self.bmp_gray = bmp.ConvertToGreyscale().ConvertToBitmap() def set_min_props(self, heading=FACE_NONE, face=FACE_NONE, label="", locked=False, hide=False, width=0, height=0): self.heading = heading @@ -116,6 +135,7 @@ self.width = int(width) self.height = int(height) self.isUpdated = True + self.generate_bmps() def hit_test(self, pt): rect = self.get_rect() @@ -128,41 +148,39 @@ return ret def draw(self, dc, mini_layer, op=wx.COPY): - if isinstance(self.bmp, tuple): - self.bmp = wx.ImageFromMime(self.bmp[1], self.bmp[2]).ConvertToBitmap() - if self.bmp != None and self.bmp.Ok(): - # check if hidden and GM: we outline the mini in grey (little bit smaller than the actual size) - # and write the label in the center of the mini - if self.hide and mini_layer.canvas.frame.session.my_role() == mini_layer.canvas.frame.session.ROLE_GM: - # set the width and height of the image - if self.width and self.height: - tmp_image = self.bmp.ConvertToImage() - tmp_image.Rescale(int(self.width), int(self.height)) - tmp_image.ConvertAlphaToMask() - self.bmp = tmp_image.ConvertToBitmap() - mask = wx.Mask(self.bmp, wx.Colour(tmp_image.GetMaskRed(), - tmp_image.GetMaskGreen(), tmp_image.GetMaskBlue())) - self.bmp.SetMask(mask) - del tmp_image - del mask - self.left = 0 - self.right = self.bmp.GetWidth() - self.top = 0 - self.bottom = self.bmp.GetHeight() - # grey outline - graypen = wx.Pen("gray", 1, wx.DOT) - dc.SetPen(graypen) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - #if width or height < 20 then offset = 1 - if self.bmp.GetWidth() <= 20: xoffset = 1 - else: xoffset = 5 - if self.bmp.GetHeight() <= 20: yoffset = 1 - else: yoffset = 5 - dc.DrawRectangle(self.pos.x + xoffset, - self.pos.y + yoffset, self.bmp.GetWidth() - (xoffset * 2), - self.bmp.GetHeight() - (yoffset * 2)) - dc.SetBrush(wx.NullBrush) - dc.SetPen(wx.NullPen) + if self.hide and mini_layer.canvas.frame.session.my_role() == mini_layer.canvas.frame.session.ROLE_GM: + # set the width and height of the image + """ + if self.width and self.height: + tmp_image = self.bmp.ConvertToImage() + tmp_image.Rescale(int(self.width), int(self.height)) + tmp_image.ConvertAlphaToMask() + self.bmp = tmp_image.ConvertToBitmap() + mask = wx.Mask(self.bmp, wx.Colour(tmp_image.GetMaskRed(), + tmp_image.GetMaskGreen(), tmp_image.GetMaskBlue())) + self.bmp.SetMask(mask) + del tmp_image + del mask + """ + self.left = 0 + self.right = self.bmp.GetWidth() + self.top = 0 + self.bottom = self.bmp.GetHeight() + # grey outline + graypen = wx.Pen("gray", 1, wx.DOT) + dc.SetPen(graypen) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + #if width or height < 20 then offset = 1 + if self.bmp.GetWidth() <= 20: xoffset = 1 + else: xoffset = 5 + if self.bmp.GetHeight() <= 20: yoffset = 1 + else: yoffset = 5 + dc.DrawRectangle(self.pos.x + xoffset, + self.pos.y + yoffset, self.bmp.GetWidth(), + self.bmp.GetHeight()) + dc.SetBrush(wx.NullBrush) + dc.SetPen(wx.NullPen) + if mini_layer.show_labels: ## draw label in the center of the mini label = mini_layer.get_mini_label(self) if len(label): @@ -180,220 +198,163 @@ dc.SetPen(wx.NullPen) dc.SetBrush(wx.NullBrush) dc.DrawText(label, x+1, y+1) - - #selected outline - if self.selected: - dc.SetPen(wx.RED_PEN) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.DrawRectangle(self.pos.x, self.pos.y, self.bmp.GetWidth(), self.bmp.GetHeight()) - dc.SetBrush(wx.NullBrush) - dc.SetPen(wx.NullPen) - return True - elif self.hide: return True - - else: - # set the width and height of the image - bmp = self.bmp - if self.width and self.height: - tmp_image = self.bmp.ConvertToImage() - tmp_image.Rescale(int(self.width), int(self.height)) - tmp_image.ConvertAlphaToMask() - self.bmp = tmp_image.ConvertToBitmap() - mask = wx.Mask(self.bmp, wx.Colour(tmp_image.GetMaskRed(), - tmp_image.GetMaskGreen(), tmp_image.GetMaskBlue())) - self.bmp.SetMask(mask) - if self.gray: - tmp_image = tmp_image.ConvertToGreyscale() - bmp = tmp_image.ConvertToBitmap() - else: bmp = self.bmp - dc.DrawBitmap(bmp, self.pos.x, self.pos.y, True) - self.left = 0 - self.right = self.bmp.GetWidth() - self.top = 0 - self.bottom = self.bmp.GetHeight() - - # Draw the facing marker if needed - if self.face != 0: - x_mid = self.pos.x + (self.bmp.GetWidth()/2) - x_right = self.pos.x + self.bmp.GetWidth() - y_mid = self.pos.y + (self.bmp.GetHeight()/2) - y_bottom = self.pos.y + self.bmp.GetHeight() - dc.SetPen(wx.WHITE_PEN) - dc.SetBrush(wx.RED_BRUSH) - triangle = [] - - # Figure out which direction to draw the marker!! - if self.face == FACE_WEST: - triangle.append(cmpPoint(self.pos.x,self.pos.y)) - triangle.append(cmpPoint(self.pos.x - 5, y_mid)) - triangle.append(cmpPoint(self.pos.x, y_bottom)) - elif self.face == FACE_EAST: - triangle.append(cmpPoint(x_right, self.pos.y)) - triangle.append(cmpPoint(x_right + 5, y_mid)) - triangle.append(cmpPoint(x_right, y_bottom)) - elif self.face == FACE_SOUTH: - triangle.append(cmpPoint(self.pos.x, y_bottom)) - triangle.append(cmpPoint(x_mid, y_bottom + 5)) - triangle.append(cmpPoint(x_right, y_bottom)) - elif self.face == FACE_NORTH: - triangle.append(cmpPoint(self.pos.x, self.pos.y)) - triangle.append(cmpPoint(x_mid, self.pos.y - 5)) - triangle.append(cmpPoint(x_right, self.pos.y)) - elif self.face == FACE_NORTHEAST: - triangle.append(cmpPoint(x_mid, self.pos.y)) - triangle.append(cmpPoint(x_right + 5, self.pos.y - 5)) - triangle.append(cmpPoint(x_right, y_mid)) - triangle.append(cmpPoint(x_right, self.pos.y)) - elif self.face == FACE_SOUTHEAST: - triangle.append(cmpPoint(x_right, y_mid)) - triangle.append(cmpPoint(x_right + 5, y_bottom + 5)) - triangle.append(cmpPoint(x_mid, y_bottom)) - triangle.append(cmpPoint(x_right, y_bottom)) - elif self.face == FACE_SOUTHWEST: - triangle.append(cmpPoint(x_mid, y_bottom)) - triangle.append(cmpPoint(self.pos.x - 5, y_bottom + 5)) - triangle.append(cmpPoint(self.pos.x, y_mid)) - triangle.append(cmpPoint(self.pos.x, y_bottom)) - elif self.face == FACE_NORTHWEST: - triangle.append(cmpPoint(self.pos.x, y_mid)) - triangle.append(cmpPoint(self.pos.x - 5, self.pos.y - 5)) - triangle.append(cmpPoint(x_mid, self.pos.y)) - triangle.append(cmpPoint(self.pos.x, self.pos.y)) - dc.DrawPolygon(triangle) - dc.SetBrush(wx.NullBrush) - dc.SetPen(wx.NullPen) - - # Draw the heading if needed - if self.heading: - x_adjust = 0 - y_adjust = 4 - x_half = self.bmp.GetWidth()/2 - y_half = self.bmp.GetHeight()/2 - x_quarter = self.bmp.GetWidth()/4 - y_quarter = self.bmp.GetHeight()/4 - x_3quarter = x_quarter*3 - y_3quarter = y_quarter*3 - x_full = self.bmp.GetWidth() - y_full = self.bmp.GetHeight() - x_center = self.pos.x + x_half - y_center = self.pos.y + y_half - # Remember, the pen/brush must be a different color than the - # facing marker!!!! We'll use black/cyan for starters. - # Also notice that we will draw the heading on top of the - # larger facing marker. - dc.SetPen(wx.BLACK_PEN) - dc.SetBrush(wx.CYAN_BRUSH) - triangle = [] - - # Figure out which direction to draw the marker!! - if self.heading == FACE_NORTH: - triangle.append(cmpPoint(x_center - x_quarter, y_center - y_half )) - triangle.append(cmpPoint(x_center, y_center - y_3quarter )) - triangle.append(cmpPoint(x_center + x_quarter, y_center - y_half )) - elif self.heading == FACE_SOUTH: - triangle.append(cmpPoint(x_center - x_quarter, y_center + y_half )) - triangle.append(cmpPoint(x_center, y_center + y_3quarter )) - triangle.append(cmpPoint(x_center + x_quarter, y_center + y_half )) - elif self.heading == FACE_NORTHEAST: - triangle.append(cmpPoint(x_center + x_quarter, y_center - y_half )) - triangle.append(cmpPoint(x_center + x_3quarter, y_center - y_3quarter )) - triangle.append(cmpPoint(x_center + x_half, y_center - y_quarter )) - elif self.heading == FACE_EAST: - triangle.append(cmpPoint(x_center + x_half, y_center - y_quarter )) - triangle.append(cmpPoint(x_center + x_3quarter, y_center )) - triangle.append(cmpPoint(x_center + x_half, y_center + y_quarter )) - elif self.heading == FACE_SOUTHEAST: - triangle.append(cmpPoint(x_center + x_half, y_center + y_quarter )) - triangle.append(cmpPoint(x_center + x_3quarter, y_center + y_3quarter )) - triangle.append(cmpPoint(x_center + x_quarter, y_center + y_half )) - elif self.heading == FACE_SOUTHWEST: - triangle.append(cmpPoint(x_center - x_quarter, y_center + y_half )) - triangle.append(cmpPoint(x_center - x_3quarter, y_center + y_3quarter )) - triangle.append(cmpPoint(x_center - x_half, y_center + y_quarter )) - elif self.heading == FACE_WEST: - triangle.append(cmpPoint(x_center - x_half, y_center + y_quarter )) - triangle.append(cmpPoint(x_center - x_3quarter, y_center )) - triangle.append(cmpPoint(x_center - x_half, y_center - y_quarter )) - elif self.heading == FACE_NORTHWEST: - triangle.append(cmpPoint(x_center - x_half, y_center - y_quarter )) - triangle.append(cmpPoint(x_center - x_3quarter, y_center - y_3quarter )) - triangle.append(cmpPoint(x_center - x_quarter, y_center - y_half )) - dc.DrawPolygon(triangle) - dc.SetBrush(wx.NullBrush) - dc.SetPen(wx.NullPen) - #selected outline - if self.selected: - dc.SetPen(wx.RED_PEN) - dc.SetBrush(wx.TRANSPARENT_BRUSH) - dc.DrawRectangle(self.pos.x, self.pos.y, self.bmp.GetWidth(), self.bmp.GetHeight()) - dc.SetBrush(wx.NullBrush) - dc.SetPen(wx.NullPen) + + #selected outline + if self.selected: + dc.SetPen(wx.RED_PEN) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangle(self.pos.x, self.pos.y, self.bmp.GetWidth(), self.bmp.GetHeight()) + dc.SetBrush(wx.NullBrush) + dc.SetPen(wx.NullPen) + return True + elif self.hide: return True + + else: + bmp = self.bmp_gray if self.gray else self.bmp + try: dc.DrawBitmap(bmp, self.pos.x, self.pos.y, True) + except: print bmp + self.left = 0 + self.right = self.bmp.GetWidth() + self.top = 0 + self.bottom = self.bmp.GetHeight() + + # Draw the facing marker if needed + if self.face != 0: + x_mid = self.pos.x + (self.bmp.GetWidth()/2) + x_right = self.pos.x + self.bmp.GetWidth() + y_mid = self.pos.y + (self.bmp.GetHeight()/2) + y_bottom = self.pos.y + self.bmp.GetHeight() + dc.SetPen(wx.WHITE_PEN) + dc.SetBrush(wx.RED_BRUSH) + triangle = [] + + # Figure out which direction to draw the marker!! + tri_list = { + FACE_WEST: [cmpPoint(self.pos.x, self.pos.y), cmpPoint(self.pos.x-5, y_mid), cmpPoint(self.pos.x, y_bottom)], + FACE_EAST: [cmpPoint(x_right, self.pos.y), cmpPoint(x_right + 5, y_mid), cmpPoint(x_right, y_bottom)], + FACE_SOUTH: [cmpPoint(self.pos.x, y_bottom), cmpPoint(x_mid, y_bottom + 5), cmpPoint(x_right, y_bottom)], + FACE_NORTH: [cmpPoint(self.pos.x, self.pos.y), cmpPoint(x_mid, self.pos.y - 5), cmpPoint(x_right, self.pos.y)], + FACE_NORTHEAST: [cmpPoint(x_mid, self.pos.y), cmpPoint(x_right + 5, self.pos.y - 5), cmpPoint(x_right, y_mid), cmpPoint(x_right, self.pos.y)], + FACE_SOUTHEAST: [cmpPoint(x_right, y_mid), cmpPoint(x_right + 5, y_bottom + 5), cmpPoint(x_mid, y_bottom), cmpPoint(x_right, y_bottom)], + FACE_SOUTHWEST: [cmpPoint(x_mid, y_bottom), cmpPoint(self.pos.x - 5, y_bottom + 5), + cmpPoint(self.pos.x, y_mid), cmpPoint(self.pos.x, y_bottom)], + FACE_NORTHWEST: [cmpPoint(self.pos.x, y_mid), cmpPoint(self.pos.x - 5, self.pos.y - 5), cmpPoint(x_mid, self.pos.y), cmpPoint(self.pos.x, self.pos.y)] + } + for tri in tri_list[self.face]: + triangle.append(tri) + del tri_list + dc.DrawPolygon(triangle) + dc.SetBrush(wx.NullBrush) + dc.SetPen(wx.NullPen) + + # Draw the heading if needed + if self.heading: + x_adjust = 0 + y_adjust = 4 + x_half = self.bmp.GetWidth()/2 + y_half = self.bmp.GetHeight()/2 + x_quarter = self.bmp.GetWidth()/4 + y_quarter = self.bmp.GetHeight()/4 + x_3quarter = x_quarter*3 + y_3quarter = y_quarter*3 + x_full = self.bmp.GetWidth() + y_full = self.bmp.GetHeight() + x_center = self.pos.x + x_half + y_center = self.pos.y + y_half + # Remember, the pen/brush must be a different color than the + # facing marker!!!! We'll use black/cyan for starters. + # Also notice that we will draw the heading on top of the + # larger facing marker. + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.CYAN_BRUSH) + triangle = [] + # Figure out which direction to draw the marker!! + tri_list = { + FACE_NORTH: [cmpPoint(x_center - x_quarter, y_center - y_half ), cmpPoint(x_center, y_center - y_3quarter ), cmpPoint(x_center + x_quarter, y_center - y_half)], + FACE_SOUTH: [cmpPoint(x_center - x_quarter, y_center + y_half ), cmpPoint(x_center, y_center + y_3quarter ), cmpPoint(x_center + x_quarter, y_center + y_half )], + FACE_NORTHEAST: [cmpPoint(x_center + x_quarter, y_center - y_half ), cmpPoint(x_center + x_3quarter, y_center - y_3quarter ), cmpPoint(x_center + x_half, y_center - y_quarter)], + FACE_EAST: [cmpPoint(x_center + x_half, y_center - y_quarter ), cmpPoint(x_center + x_3quarter, y_center ), cmpPoint(x_center + x_half, y_center + y_quarter )], + FACE_SOUTHEAST: [cmpPoint(x_center + x_half, y_center + y_quarter ), cmpPoint(x_center + x_3quarter, y_center + y_3quarter ), cmpPoint(x_center + x_quarter, y_center + y_half )], + FACE_SOUTHWEST: [cmpPoint(x_center - x_quarter, y_center + y_half ), cmpPoint(x_center - x_3quarter, y_center + y_3quarter ), cmpPoint(x_center - x_half, y_center + y_quarter )], + FACE_WEST: [cmpPoint(x_center - x_half, y_center + y_quarter ), cmpPoint(x_center - x_3quarter, y_center ), cmpPoint(x_center - x_half, y_center - y_quarter )], + FACE_NORTHWEST: [cmpPoint(x_center - x_half, y_center - y_quarter ), cmpPoint(x_center - x_3quarter, y_center - y_3quarter ), cmpPoint(x_center - x_quarter, y_center - y_half )]} + for tri in tri_list[self.heading]: + triangle.append(tri) + del tri_list + dc.DrawPolygon(triangle) + dc.SetBrush(wx.NullBrush) + dc.SetPen(wx.NullPen) + #selected outline + if self.selected: + dc.SetPen(wx.RED_PEN) + dc.SetBrush(wx.TRANSPARENT_BRUSH) + dc.DrawRectangle(self.pos.x, self.pos.y, self.bmp.GetWidth(), self.bmp.GetHeight()) + dc.SetBrush(wx.NullBrush) + dc.SetPen(wx.NullPen) + if mini_layer.show_labels: # draw label - label = mini_layer.get_mini_label(self) - if len(label): - dc.SetTextForeground(wx.RED) - (textWidth,textHeight) = dc.GetTextExtent(label) - x = self.pos.x +((self.bmp.GetWidth() - textWidth) /2) - 1 - y = self.pos.y + self.bmp.GetHeight() + 6 - dc.SetPen(wx.WHITE_PEN) - dc.SetBrush(wx.WHITE_BRUSH) - dc.DrawRectangle(x,y,textWidth+2,textHeight+2) - if (textWidth+2 > self.right): - self.right += int((textWidth+2-self.right)/2)+1 - self.left -= int((textWidth+2-self.right)/2)+1 - self.bottom = y+textHeight+2-self.pos.y - dc.SetPen(wx.NullPen) - dc.SetBrush(wx.NullBrush) - dc.DrawText(label,x+1,y+1) - self.top-=5 - self.bottom+=5 - self.left-=5 - self.right+=5 - return True - else: return False + self.mini_label(mini_layer, dc) + self.top-=5 + self.bottom+=5 + self.left-=5 + self.right+=5 + return True + + + def mini_label(self, mini_layer, dc): + label = mini_layer.get_mini_label(self) + if len(label): + dc.SetTextForeground(wx.RED) + (textWidth,textHeight) = dc.GetTextExtent(label) + x = self.pos.x +((self.bmp.GetWidth() - textWidth) /2) - 1 + y = self.pos.y + self.bmp.GetHeight() + 6 + dc.SetPen(wx.WHITE_PEN) + dc.SetBrush(wx.WHITE_BRUSH) + dc.DrawRectangle(x,y,textWidth+2,textHeight+2) + if (textWidth+2 > self.right): + self.right += int((textWidth+2-self.right)/2)+1 + self.left -= int((textWidth+2-self.right)/2)+1 + self.bottom = y+textHeight+2-self.pos.y + dc.SetPen(wx.NullPen) + dc.SetBrush(wx.NullBrush) + dc.DrawText(label,x+1,y+1) def toxml(self, action="update"): - if action == "del": - xml_str = "" - return xml_str - xml_str = "' - #print "Sending this XML to insert_xml:" + node_xml - gametree.insert_xml(str(node_xml)) + ### Alpha ### implements ElementTree + mini = self.sel_rmin.toxml(action="new") + node = Element('nodehandler') + node.set('module', 'map_miniature_nodehandler') + node.set('class', 'map_miniature_handler') + name = self.sel_rmin.label if self.sel_rmin.label else 'Unnamed Miniature' + node.set('name', name) + node.append(mini) + gametree = component.get('tree') + gametree.insert_xml(tostring(node)) + ### ElementTree is a nice decision from Core, kudos!! ### elif id == MIN_SHOW_HIDE: if self.sel_rmin.hide: self.sel_rmin.hide = 0 else: self.sel_rmin.hide = 1 @@ -493,6 +497,7 @@ def on_miniature(self, evt): session = self.canvas.frame.session if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER) and (session.use_roles()): + print session.my_role() self.infoPost("You must be either a player or GM to use the miniature Layer") return min_url = self.min_url.GetValue() @@ -500,6 +505,8 @@ if min_url == "" or min_url == "http://": return if min_url[:7] != "http://" : min_url = "http://" + min_url # make label + if self.auto_label: print 'auto-label' + if not self.auto_label: print 'False' if self.auto_label and min_url[-4:-3] == '.': start = min_url.rfind("/") + 1 min_label = min_url[start:len(min_url)-4] @@ -510,6 +517,7 @@ try: id = 'mini-' + self.canvas.frame.session.get_next_id() # make the new mini appear in top left of current viewable map + print id dc = wx.ClientDC(self.canvas) self.canvas.PrepareDC(dc) dc.SetUserScale(self.canvas.layers['grid'].mapscale,self.canvas.layers['grid'].mapscale) @@ -529,9 +537,14 @@ #except Exception, e: #wx.MessageBox(str(e),"Miniature Error") - def on_label(self,evt): + def on_show_labels(self, evt): + show_labels = not self.canvas.layers['miniatures'].show_labels + self.canvas.layers['miniatures'].show_labels = show_labels + self.canvas.Refresh() + + def on_auto_label(self,evt): self.auto_label = not self.auto_label - self.auto_label_cb.SetValue(self.auto_label) + #self.auto_label_cb.SetValue(self.auto_label) #self.send_map_data() #self.Refresh() @@ -541,7 +554,7 @@ self.infoPost("You must be a GM to use this feature") return #d = min_list_panel(self.frame.GetParent(),self.canvas.layers,"Miniature list") - d = min_list_panel(self.canvas.frame,self.canvas.layers,"Miniature list") + d = min_list_panel(self.canvas.frame, self.canvas.layers, "Miniature list") if d.ShowModal() == wx.ID_OK: d.Destroy() self.canvas.Refresh(False) @@ -630,9 +643,11 @@ pos = wx.Point(dc.DeviceToLogicalX(pos.x), dc.DeviceToLogicalY(pos.y)) mini_list = self.getMiniListOrSelectedMini(pos) if len(mini_list) > 0: - tooltip = self.get_mini_tooltip(mini_list) - self.canvas.SetToolTipString(tooltip) - else: self.canvas.SetToolTipString("") + tooltip = mini_list[0].label + #self.canvas.SetToolTipString(mini_list[0].label) + #Once set, it never unsets, so it sucks. + else: + self.canvas.SetToolTipString('') def on_motion(self,evt): if evt.Dragging() and evt.LeftIsDown(): @@ -640,8 +655,6 @@ drag_bmp = self.drag_mini.bmp if self.drag_mini.width and self.drag_mini.height: tmp_image = drag_bmp.ConvertToImage() - tmp_image.Rescale(int(self.drag_mini.width * self.canvas.layers['grid'].mapscale), - int(self.drag_mini.height * self.canvas.layers['grid'].mapscale)) tmp_image.ConvertAlphaToMask() drag_bmp = tmp_image.ConvertToBitmap() mask = wx.Mask(drag_bmp, wx.Colour(tmp_image.GetMaskRed(), @@ -662,7 +675,7 @@ self.canvas.drag.Move(evt.GetPosition()) self.canvas.drag.Show() # reset tool tip timer - self.canvas.SetToolTipString("") + self.canvas.SetToolTipString('') self.tooltip_timer.Restart(self.tooltip_delay_miliseconds, evt.GetPosition()) def on_left_up(self,evt): @@ -758,14 +771,16 @@ def role_is_gm_or_player(self): session = self.canvas.frame.session - if (session.my_role() <> session.ROLE_GM) and (session.my_role() <> session.ROLE_PLAYER) and (session.use_roles()): + print session.my_role(), session.ROLE_GM + if (session.my_role() != session.ROLE_GM) and (session.my_role() != session.ROLE_PLAYER) and (session.use_roles()): + print 'role is gm or player' self.infoPost("You must be either a player or GM to use the miniature Layer") return False return True def role_is_gm(self): session = self.canvas.frame.session - if (session.my_role() <> session.ROLE_GM) and (session.use_roles()): return False + if (session.my_role() != session.ROLE_GM) and (session.use_roles()): return False return True def alreadyDealingWithMenu(self): diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/miniatures_msg.py --- a/orpg/mapper/miniatures_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/miniatures_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -79,12 +79,12 @@ def init_from_dom(self,xml_dom): self.p_lock.acquire() - if xml_dom.tagName == self.tagname: - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.init_prop(k,xml_dom.getAttribute(k)) + if xml_dom.tag == self.tagname: + if xml_dom.keys(): + for k in xml_dom.keys(): + self.init_prop(k, xml_dom.get(k)) - for c in xml_dom._get_childNodes(): + for c in xml_dom.getchildren(): mini = mini_msg(self.p_lock) try: mini.init_from_dom(c) except Exception, e: print e; continue @@ -107,12 +107,12 @@ def set_from_dom(self,xml_dom): self.p_lock.acquire() - if xml_dom.tagName == self.tagname: - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.set_prop(k,xml_dom.getAttribute(k)) + if xml_dom.tag == self.tagname: + if xml_dom.keys(): + for k in xml_dom.keys(): + self.set_prop(k,xml_dom.get(k)) - for c in xml_dom._get_childNodes(): + for c in xml_dom.getchildren(): mini = mini_msg(self.p_lock) try: mini.set_from_dom(c) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/mapper/whiteboard_msg.py --- a/orpg/mapper/whiteboard_msg.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/mapper/whiteboard_msg.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,6 +29,7 @@ __version__ = "$Id: whiteboard_msg.py,v 1.12 2007/03/09 14:11:56 digitalxero Exp $" from base_msg import * +from xml.etree.ElementTree import ElementTree class item_msg(map_element_msg_base): @@ -80,12 +81,12 @@ def init_from_dom(self,xml_dom): self.p_lock.acquire() - if xml_dom.tagName == self.tagname: - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.init_prop(k,xml_dom.getAttribute(k)) - for c in xml_dom._get_childNodes(): - item = item_msg(self.p_lock,c._get_nodeName()) + if xml_dom.tag == self.tagname: + if xml_dom.keys(): + for k in xml_dom.keys(): + self.init_prop(k,xml_dom.get(k)) + for c in xml_dom.getchildren(): + item = item_msg(self.p_lock, c.tag) try: item.init_from_dom(c) except Exception, e: print e @@ -106,12 +107,12 @@ def set_from_dom(self,xml_dom): self.p_lock.acquire() - if xml_dom.tagName == self.tagname: - if xml_dom.getAttributeKeys(): - for k in xml_dom.getAttributeKeys(): - self.set_prop(k,xml_dom.getAttribute(k)) - for c in xml_dom._get_childNodes(): - item = item_msg(self.p_lock, c._get_nodeName()) + if xml_dom.tag == self.tagname: + if xml_dom.keys(): + for k in xml_dom.keys(): + self.set_prop(k, xml_dom.get(k)) + for c in xml_dom.getchildren(): + item = item_msg(self.p_lock, c.tag) try: item.set_from_dom(c) except Exception, e: print e diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/gsclient.py --- a/orpg/networking/gsclient.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/gsclient.py Tue Nov 10 14:11:28 2009 -0600 @@ -26,34 +26,28 @@ # Description: The file contains code for the game server browser # +from __future__ import with_statement __version__ = "$Id: gsclient.py,v 1.53 2007/10/25 21:49:34 digitalxero Exp $" +import meta_server_lib +#import orpg.tools.orpg_settings +import orpg.tools.rgbhex +import traceback + from orpg.dirpath import dir_struct from orpg.orpg_windows import * -import meta_server_lib -import orpg.tools.orpg_settings +from orpg.tools.validate import validate +from orpg.orpgCore import component from orpg.tools.orpg_settings import settings -import orpg.tools.rgbhex -from orpg.orpgCore import component -import traceback +from orpg.tools.orpg_log import debug + +from xml.etree.ElementTree import ElementTree, Element +from xml.etree.ElementTree import fromstring, tostring gs_host = 1 gs_join = 2 # constants -LIST_SERVER = wx.NewId() -LIST_ROOM = wx.NewId() -ADDRESS = wx.NewId() -GS_CONNECT = wx.NewId() -GS_DISCONNECT = wx.NewId() -GS_SERVER_REFRESH = wx.NewId() -GS_JOIN = wx.NewId() -GS_JOINLOBBY = wx.NewId() -GS_CREATE_ROOM = wx.NewId() -GS_PWD = wx.NewId() -GS_CLOSE = wx.NewId() -OR_CLOSE = wx.NewId() - class server_instance: def __init__(self, id, name="[Unknown]", users="0", address="127.0.0.1", port="9557"): self.id = id @@ -64,57 +58,49 @@ def server_instance_compare(x,y): """compares server insances for list sort""" + ### Alpha ### makes changes from core. DEV_SERVER = "OpenRPG DEV" - xuser = 0 - yuser = 0 - try: xuser = int(x.user) - except: pass - try: yuser = int(y.user) - except: pass + xname = x.name + xuser = int(x.user) + yname = y.name + yuser = int(y.user) + + who_name = cmp(yname, xname) + who_user = cmp(yuser, xuser) + if x.name.startswith(DEV_SERVER): if y.name.startswith(DEV_SERVER): - if x.name > y.name: return 1 - elif x.name == y.name: - if xuser < yuser: return 1 - elif xuser > yuser: return -1 - else: return 0 - else: return -1 + if not who_name: return who_user + else: return who_name else: return -1 + elif y.name.startswith(DEV_SERVER): return 1 - elif xuser < yuser: return 1 - elif xuser == yuser: - if x.name > y.name: return 1 - elif x.name == y.name: return 0 - else: return -1 - else: return -1 + elif not who_user: return who_name + else: return who_user def roomCmp(room1, room2): - if int(room1) > int(room2): - return 1 - elif int(room1) < int(room2): - return -1 + if int(room1) > int(room2): return 1 + elif int(room1) < int(room2): return -1 return 0 class game_server_panel(wx.Panel): def __init__(self,parent): wx.Panel.__init__(self, parent, -1) self.parent = parent - self.password_manager = component.get('password_manager') # passtool --SD 8/03 + self.password_manager = component.get('password_manager') self.frame = component.get('frame') self.session = component.get('session') - self.xml = component.get('xml') #Not used?? self.serverNameSet = 0 self.last_motd = "" self.buttons = {} self.texts = {} self.svrList = [] self.build_ctrls() + self.bookmarks() self.refresh_server_list() self.refresh_room_list() + self.build_bookmark_menu() -#--------------------------------------------------------- -# [START] Snowdog: Updated Game Server Window 12/02 -#--------------------------------------------------------- def build_ctrls(self): ## Section Sizers (with frame edges and text captions) self.box_sizers = {} @@ -133,18 +119,19 @@ #Build Server Sizer adder = wx.StaticText(self, -1, "Address:") - self.texts["address"] = wx.TextCtrl(self, ADDRESS) + self.texts["address"] = wx.TextCtrl(self, wx.ID_ANY) servers = wx.StaticText(self, -1, "Servers:") - self.server_list = wx.ListCtrl(self, LIST_SERVER, style=wx.LC_REPORT | wx.SUNKEN_BORDER ) - self.server_list.InsertColumn(0, "Players", wx.LIST_FORMAT_LEFT, 0) - self.server_list.InsertColumn(1, "Name", wx.LIST_FORMAT_LEFT, 0) - self.buttons[GS_CONNECT] = wx.Button(self, GS_CONNECT, "Connect") - self.buttons[GS_SERVER_REFRESH] = wx.Button(self, GS_SERVER_REFRESH, "Refresh") - self.buttons[GS_DISCONNECT] = wx.Button(self, GS_DISCONNECT, "Disconnect") + self.server_list = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.SUNKEN_BORDER ) + self.server_list.InsertColumn(0, '', wx.LIST_FORMAT_LEFT, 0) + self.server_list.InsertColumn(1, "Players", wx.LIST_FORMAT_LEFT, 0) + self.server_list.InsertColumn(2, "Name", wx.LIST_FORMAT_LEFT, 0) + self.buttons['gs_connect'] = wx.Button(self, wx.ID_ANY, "Connect") + self.buttons['gs_refresh'] = wx.Button(self, wx.ID_ANY, "Refresh") + self.buttons['gs_disconnect'] = wx.Button(self, wx.ID_ANY, "Disconnect") self.sizers["svrbtns"] = wx.BoxSizer(wx.HORIZONTAL) - self.sizers["svrbtns"].Add(self.buttons[GS_CONNECT], 0, wx.EXPAND) - self.sizers["svrbtns"].Add(self.buttons[GS_SERVER_REFRESH], 0, wx.EXPAND) - self.sizers["svrbtns"].Add(self.buttons[GS_DISCONNECT], 0, wx.EXPAND) + self.sizers["svrbtns"].Add(self.buttons['gs_connect'], 0, wx.EXPAND) + self.sizers["svrbtns"].Add(self.buttons['gs_refresh'], 0, wx.EXPAND) + self.sizers["svrbtns"].Add(self.buttons['gs_disconnect'], 0, wx.EXPAND) self.sizers["server"].Add(adder, 0, wx.EXPAND) self.sizers["server"].Add(self.texts["address"], 0, wx.EXPAND) self.sizers["server"].Add(servers, 0, wx.EXPAND) @@ -152,33 +139,33 @@ self.sizers["server"].Add(self.sizers["svrbtns"], 0, wx.EXPAND) #Build Rooms Sizer - self.room_list = wx.ListCtrl(self, LIST_ROOM, style=wx.LC_REPORT | wx.SUNKEN_BORDER) + self.room_list = wx.ListCtrl(self, wx.ID_ANY, style=wx.LC_REPORT | wx.SUNKEN_BORDER) self.room_list.InsertColumn(0,"Game", wx.LIST_FORMAT_LEFT,-1) self.room_list.InsertColumn(1,"Players", wx.LIST_FORMAT_LEFT,-1) self.room_list.InsertColumn(2,"PW", wx.LIST_FORMAT_LEFT,-1) - self.buttons[GS_JOIN] = wx.Button(self, GS_JOIN, "Join Room") - self.buttons[GS_JOINLOBBY] = wx.Button(self, GS_JOINLOBBY, "Lobby") + self.buttons['gs_join_room'] = wx.Button(self, wx.ID_ANY, "Join Room") + self.buttons['gs_join_lobby'] = wx.Button(self, wx.ID_ANY, "Lobby") self.sizers["roombtns"] = wx.BoxSizer(wx.HORIZONTAL) - self.sizers["roombtns"].Add(self.buttons[GS_JOIN], 0, wx.EXPAND) - self.sizers["roombtns"].Add(self.buttons[GS_JOINLOBBY], 0, wx.EXPAND) + self.sizers["roombtns"].Add(self.buttons['gs_join_room'], 0, wx.EXPAND) + self.sizers["roombtns"].Add(self.buttons['gs_join_lobby'], 0, wx.EXPAND) self.sizers["rooms"].Add(self.room_list, 1, wx.EXPAND) self.sizers["rooms"].Add(self.sizers["roombtns"], 0, wx.EXPAND) #Build Close Sizer - self.buttons[OR_CLOSE] = wx.Button(self, OR_CLOSE,"Exit OpenRPG") - self.buttons[GS_CLOSE] = wx.Button(self, GS_CLOSE,"Close Window") - self.sizers["close"].Add(self.buttons[OR_CLOSE], 1, wx.ALIGN_CENTER_VERTICAL) - self.sizers["close"].Add(self.buttons[GS_CLOSE], 1, wx.ALIGN_CENTER_VERTICAL) + self.buttons['close_orpg'] = wx.Button(self, wx.ID_ANY,"Exit OpenRPG") + self.buttons['gs_close'] = wx.Button(self, wx.ID_ANY,"Close Window") + self.sizers["close"].Add(self.buttons['close_orpg'], 1, wx.ALIGN_CENTER_VERTICAL) + self.sizers["close"].Add(self.buttons['gs_close'], 1, wx.ALIGN_CENTER_VERTICAL) #Build Create Room Sizer rname = wx.StaticText(self,-1, "Room Name:") self.texts["room_name"] = wx.TextCtrl(self, -1) rpass = wx.StaticText(self,-1, "Password:") - self.buttons[GS_PWD] = wx.CheckBox(self, GS_PWD, "") + self.buttons['gs_pwd'] = wx.CheckBox(self, wx.ID_ANY, "") self.texts["room_pwd"] = wx.TextCtrl(self, -1) self.texts["room_pwd"].Enable(0) pwsizer = wx.BoxSizer(wx.HORIZONTAL) - pwsizer.Add(self.buttons[GS_PWD],0,0) + pwsizer.Add(self.buttons['gs_pwd'],0,0) pwsizer.Add(self.texts["room_pwd"], 1, wx.EXPAND) apass = wx.StaticText(self,-1, "Admin Password:") self.texts["room_boot_pwd"] = wx.TextCtrl(self, -1) @@ -194,9 +181,9 @@ self.sizers["c_room_layout"].Add(minver, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALL) self.sizers["c_room_layout"].Add(self.texts["room_min_version"], 0, wx.EXPAND) self.sizers["c_room_layout"].AddGrowableCol(1) - self.buttons[GS_CREATE_ROOM] = wx.Button(self, GS_CREATE_ROOM, "Create Room") + self.buttons['gs_create_room'] = wx.Button(self, wx.ID_ANY, "Create Room") self.sizers["c_room"].Add(self.sizers["c_room_layout"], 1, wx.EXPAND) - self.sizers["c_room"].Add(self.buttons[GS_CREATE_ROOM], 0, wx.EXPAND) + self.sizers["c_room"].Add(self.buttons['gs_create_room'], 0, wx.EXPAND) #Build Main Sizer self.sizers["main"].Add(self.sizers["server"], (0,0), span=(2,1), flag=wx.EXPAND) @@ -211,42 +198,105 @@ self.Fit() ## Event Handlers - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_CONNECT) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_DISCONNECT) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_CREATE_ROOM) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_JOIN) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_JOINLOBBY) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_SERVER_REFRESH) - self.Bind(wx.EVT_BUTTON, self.on_button, id=GS_CLOSE) - self.Bind(wx.EVT_BUTTON, self.on_button, id=OR_CLOSE) - self.Bind(wx.EVT_CHECKBOX, self.on_button, id=GS_PWD) + self.Bind(wx.EVT_BUTTON, self.gs_connect, self.buttons['gs_connect']) + self.Bind(wx.EVT_BUTTON, self.gs_disconnect, self.buttons['gs_disconnect']) + self.Bind(wx.EVT_BUTTON, self.gs_create_room, self.buttons['gs_create_room']) + self.Bind(wx.EVT_BUTTON, self.gs_join, self.buttons['gs_join_room']) + self.Bind(wx.EVT_BUTTON, self.gs_join_lobby, self.buttons['gs_join_lobby']) + self.Bind(wx.EVT_BUTTON, self.gs_server_refresh, self.buttons['gs_refresh']) + self.Bind(wx.EVT_BUTTON, self.gs_close, self.buttons['gs_close']) + self.Bind(wx.EVT_BUTTON, self.close_orpg, self.buttons['close_orpg']) + self.Bind(wx.EVT_CHECKBOX, self.gs_pwd, self.buttons['gs_pwd']) # Added double click handlers 5/05 -- Snowdog - self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_server_dbclick, id=LIST_SERVER) - self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_room_dbclick, id=LIST_ROOM) - self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_select, id=LIST_ROOM) - self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_select, id=LIST_SERVER) + self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_server_dbclick, self.server_list) + self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_room_dbclick, self.room_list) + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_room_select, self.room_list) + self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_server_select, self.server_list) self.texts['address'].Bind(wx.EVT_SET_FOCUS, self.on_text) self.set_connected(self.session.is_connected()) self.cur_room_index = -1 self.cur_server_index = -1 self.rmList = {} + + # Create Book Mark Image List + self.server_list.Bind(wx.EVT_LEFT_DOWN, self.on_hit) + self._imageList = wx.ImageList( 16, 16, False ) + img = wx.Image(dir_struct["icon"]+"add.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap() + self._imageList.Add( img ) + img = wx.Image(dir_struct["icon"]+"star.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap() + self._imageList.Add( img ) + self.server_list.SetImageList( self._imageList, wx.IMAGE_LIST_SMALL ) + + def bookmarks(self): + validate.config_file('server_bookmarks.xml', + 'default_server_bookmarks.xml') + self.bookmarks = ElementTree() + self.bookmarks.parse(dir_struct['user'] + 'server_bookmarks.xml') -#--------------------------------------------------------- -# [END] Snowdog: Updated Game Server Window 12/02 -#--------------------------------------------------------- + def build_bookmark_menu(self): + gsm = self.frame.mainmenu.GetMenu( + self.frame.mainmenu.FindMenu('Game Server')) + self.bookmarks_menu = wx.Menu() + x = 0 + for server in self.bookmarks.findall('server'): + for svr in self.svrList: + address = svr.addy + if server.get('address') == address: self.server_list.SetItemImage(x, 1) + x += 1 + item = wx.MenuItem(self.bookmarks_menu, wx.ID_ANY, + server.get('name'), server.get('name')) + open_rpg.get_component('frame').Bind(wx.EVT_MENU, + self.on_bookmarks_menu, item) + self.bookmarks_menu.AppendItem(item) + gsm.AppendSubMenu(self.bookmarks_menu, "Bookmarks") - #----------------------------------------------------- - # on_server_dbclick() - # support for double click selection of server. - # 5/16/05 -- Snowdog - #----------------------------------------------------- + def on_bookmarks_menu(self, evt): + id = evt.GetId() + menu = self.bookmarks_menu.FindItemById(id) + for server in self.bookmarks.findall('server'): + if server.get('name') == menu.GetLabel(): + address = server.get('address') + self.cur_server_index = 999 + self.name = server.get('name') + if self.session.is_connected(): + if self.session.host_server == address : return + else: self.frame.kill_mplay_session() + self.do_connect(address) + break + + def bookmark(self, item, flag): + name = self.svrList[item].name + address = self.svrList[item].addy + port = self.svrList[item].port + self.server_list.SetItemImage(item, 1) + + for server in self.bookmarks.findall('server'): + if server.get('name') == name: + self.bookmarks_menu.Remove( + self.bookmarks_menu.FindItem(server.get('name'))) + self.bookmarks.getroot().remove(server) + self.server_list.SetItemImage(item, 0) + break + else: + server = Element('server') + server.set('name', name) + server.set('address', address + ':' + port) + self.bookmarks.getroot().append(server) + item = wx.MenuItem(self.bookmarks_menu, wx.ID_ANY, name, name) + open_rpg.get_component('frame').Bind(wx.EVT_MENU, + self.on_bookmarks_menu, item) + self.bookmarks_menu.AppendItem(item) + self.save_bookmarks() + + def save_bookmarks(self): + with open(dir_struct['user'] + 'server_bookmarks.xml', 'w') as f: + self.bookmarks.write(f) + def on_server_dbclick(self, evt=None): #make sure address is updated just in case list select wasn't done - try: - self.on_select(evt) - except: - pass + try: self.on_select(evt) + except: pass address = self.texts["address"].GetValue() if self.session.is_connected(): if self.session.host_server == address : @@ -257,19 +307,10 @@ self.frame.kill_mplay_session() self.do_connect(address) - - #----------------------------------------------------- - # on_room_dbclick() - # support for double click selection of server. - # 5/16/05 -- Snowdog - #----------------------------------------------------- - def on_room_dbclick(self, evt=None): #make sure address is updated just in case list select wasn't done - try: - self.on_select(evt) - except: - pass + try: self.on_select(evt) + except: pass group_id = str(self.room_list.GetItemData(self.cur_room_index)) if self.NoGroups: @@ -279,39 +320,42 @@ return if self.cur_room_index >= 0: - if self.cur_room_index != 0: - self.set_lobbybutton(1); - else: - self.set_lobbybutton(0); + if self.cur_room_index != 0: self.set_lobbybutton(1); + else: self.set_lobbybutton(0); group = self.session.get_group_info(group_id) pwd = "" if (group[2] == "True") or (group[2] == "1"): pwd = self.password_manager.GetPassword("room", group_id) - else: - pwd = "" + else: pwd = "" self.session.send_join_group(group_id, pwd) + def on_room_select(self,evt): + self.cur_room_index = evt.m_itemIndex - def on_select(self,evt): + def on_hit(self, evt): + pos = wx.Point( evt.GetX(), evt.GetY() ) + (item, flag) = self.server_list.HitTest( pos ) + ## Item == list[server], flag == (32 = 0 colum, 128 = else) ## + if flag == 32: self.bookmark(item, flag) + evt.Skip() + + def on_server_select(self,evt): + self.cur_server_index = evt.m_itemIndex + self.name = self.svrList[self.cur_server_index].name + address = self.svrList[self.cur_server_index].addy + port = self.svrList[self.cur_server_index].port + self.texts["address"].SetValue(address + ":" + str(port)) + self.refresh_room_list() + + def on_text(self, evt): id = evt.GetId() - if id == LIST_ROOM: - self.cur_room_index = evt.m_itemIndex - elif id==LIST_SERVER: - self.cur_server_index = evt.m_itemIndex - self.name = self.svrList[self.cur_server_index].name - address = self.svrList[self.cur_server_index].addy - port = self.svrList[self.cur_server_index].port - self.texts["address"].SetValue(address+":"+str(port)) - self.refresh_room_list() - - def on_text(self,evt): - id = evt.GetId() - if (id == ADDRESS) and (self.cur_server_index >= 0): + if (id == self.texts["address"].GetValue()) and (self.cur_server_index >= 0): #print "ADDRESS id = ", id, "index = ", self.cur_server_index self.cur_server_index = -1 evt.Skip() - def add_room(self,data): + def add_room(self, data): + #debug() i = self.room_list.GetItemCount() if (data[2]=="1") or (data[2]=="True"): pwd="yes" else: pwd="no" @@ -322,20 +366,12 @@ self.refresh_room_list() def del_room(self, data): + #debug(data) i = self.room_list.FindItemData(-1, int(data[0])) self.room_list.DeleteItem(i) self.refresh_room_list() -#--------------------------------------------------------- -# [START] Snowdog Password/Room Name altering code 12/02 -#--------------------------------------------------------- - - def update_room(self,data): - - #------------------------------------------------------- - # Udated 12/02 by Snowdog - # allows refresh of all room data not just player counts - #------------------------------------------------------- + def update_room(self, data): i = self.room_list.FindItemData(-1,int(data[0])) if data[2]=="1" : pwd="yes" else: pwd="no" @@ -344,25 +380,21 @@ self.room_list.SetStringItem(i,2,pwd) self.refresh_room_list() -#--------------------------------------------------------- -# [END] Snowdog Password/Room Name altering code 12/02 -#--------------------------------------------------------- - - def set_cur_room_text(self,name): + def set_cur_room_text(self, name): pass #self.texts["cur_room"].SetLabel(name) #self.sizers["room"].Layout() - def set_lobbybutton(self,allow): - self.buttons[GS_JOINLOBBY].Enable(allow) + def set_lobbybutton(self, allow): + self.buttons['gs_join_lobby'].Enable(allow) - def set_connected(self,connected): - self.buttons[GS_CONNECT].Enable(not connected) - self.buttons[GS_DISCONNECT].Enable(connected) - self.buttons[GS_JOIN].Enable(connected) - self.buttons[GS_CREATE_ROOM].Enable(connected) + def set_connected(self, connected): + self.buttons['gs_connect'].Enable(not connected) + self.buttons['gs_disconnect'].Enable(connected) + self.buttons['gs_join_room'].Enable(connected) + self.buttons['gs_create_room'].Enable(connected) if not connected: - self.buttons[GS_JOINLOBBY].Enable(connected) + self.buttons['gs_join_lobby'].Enable(connected) self.room_list.DeleteAllItems() self.set_cur_room_text("Not Connected!") self.cur_room_index = -1 @@ -372,38 +404,45 @@ self.frame.status.set_connect_status(self.name) self.set_cur_room_text("Lobby") - def on_button(self,evt): - id = evt.GetId() - if id == GS_CONNECT: - address = self.texts["address"].GetValue() - ### check to see if this is a manual entry vs. list entry. - try: - dummy = self.name - except: - self.name = `address` - self.do_connect(address) - elif id == GS_DISCONNECT: self.frame.kill_mplay_session() - elif id == GS_CREATE_ROOM: self.do_create_group() - elif id == GS_JOIN: self.do_join_group() - elif id == GS_JOINLOBBY: self.do_join_lobby() - elif id == GS_SERVER_REFRESH: self.refresh_server_list() - elif id == GS_PWD: self.texts["room_pwd"].Enable(evt.Checked()) - elif id == OR_CLOSE: - dlg = wx.MessageDialog(self,"Quit OpenRPG?","OpenRPG",wx.YES_NO) - if dlg.ShowModal() == wx.ID_YES: - dlg.Destroy() - self.frame.kill_mplay_session() - self.frame.closed_confirmed() - elif id == GS_CLOSE: - self.parent.OnMB_GameServerBrowseServers() - + def gs_connect(self, evt): + address = self.texts['address'].GetValue() + try: dummy = self.name + except: self.name = `address` + self.do_connect(address) + + def gs_disconnect(self, evt): + self.frame.kill_mplay_session() + + def gs_create_room(self, evt): + self.do_create_group() + + def gs_join(self, evt): + self.do_join_group() + + def gs_join_lobby(self, evt): + self.do_join_lobby() + + def gs_server_refresh(self, evt): + self.refresh_server_list() + + def gs_pwd(self, evt): + self.texts['room_pwd'].Enable(evt.Checked()) + + def close_orpg(self, evt): + dlg = wx.MessageDialog(self, 'Quit OpenRPG?', "OpenRPG", wx.YES_NO) + if dlg.ShowModal() == wx.ID_YES: + dlg.Destroy() + self.frame.kill_mplay_session() + self.frame.closed_confirmed() + + def gs_close(self, evt): + self.parent.OnMB_GameServerBrowseServers() + def refresh_room_list(self): self.room_list.DeleteAllItems() address = self.texts["address"].GetValue() - try: - cadder = self.session.host_server - except: - cadder = '' + try: cadder = self.session.host_server + except: cadder = '' if self.rmList.has_key(address) and len(self.rmList[address]) > 0 and cadder != address: groups = self.rmList[address] self.NoGroups = True @@ -433,41 +472,41 @@ try: self.svrList = [] self.server_list.DeleteAllItems() - xml_dom = meta_server_lib.get_server_list(["2"]); - node_list = xml_dom.getElementsByTagName('server') + etreeEl = meta_server_lib.get_server_list(["2"]); + node_list = etreeEl.findall('server') hex = orpg.tools.rgbhex.RGBHex() - color1 = settings.get_setting("RoomColor_Active") - color2 = settings.get_setting("RoomColor_Locked") - color3 = settings.get_setting("RoomColor_Empty") - color4 = settings.get_setting("RoomColor_Lobby") + color1 = settings.get("RoomColor_Active") + color2 = settings.get("RoomColor_Locked") + color3 = settings.get("RoomColor_Empty") + color4 = settings.get("RoomColor_Lobby") if len(node_list): length = len(node_list) part = 0 partLength = 1.0/length for n in node_list: - if n.hasAttribute('id') and n.hasAttribute('name') and n.hasAttribute('num_users') and n.hasAttribute('address') and n.hasAttribute('port'): - self.svrList.append(server_instance(n.getAttribute('id'), n.getAttribute('name'), - n.getAttribute('num_users'), n.getAttribute('address'), - n.getAttribute('port'))) - address = n.getAttribute('address') + ':' + n.getAttribute('port') - self.rmList[address] = [] - rooms = n.getElementsByTagName('room') + #if n.get('id') and n.get('name') and n.get('num_users') and n.get('address') and n.get('port'): + self.svrList.append(server_instance(n.get('id'), n.get('name'), + n.get('num_users'), n.get('address'), + n.get('port'))) + address = n.get('address') + ':' + n.get('port') + self.rmList[address] = [] + rooms = n.findall('room') - for room in rooms: - pwd = room.getAttribute("pwd") - id = room.getAttribute("id") - name = room.getAttribute("name") - nump = room.getAttribute("num_users") - self.rmList[address].append((id, name, pwd, nump)) + for room in rooms: + self.rmList[address].append((room.get("id"), room.get("name"), + room.get("pwd"), room.get("num_users"))) self.svrList.sort(server_instance_compare) for n in self.svrList: i = self.server_list.GetItemCount() name = n.name players = n.user - self.server_list.InsertStringItem(i,players) - self.server_list.SetStringItem(i,1,name) + self.server_list.InsertImageStringItem(i, '', 0) + for server in self.bookmarks.findall('server'): + if server.get('name') == name: self.server_list.SetItemImage(i, 1) + self.server_list.SetStringItem(i,1,players) + self.server_list.SetStringItem(i,2,name) r,g,b = hex.rgb_tuple(color1) svrcolor = wx.Colour(red=r,green=g,blue=b) @@ -488,64 +527,49 @@ # No server is currently selected!!! Versus the broken and random 0! self.cur_server_index = -1 self.server_list.SetColumnWidth(0, wx.LIST_AUTOSIZE) - if self.server_list.GetColumnWidth(0) < 70: self.server_list.SetColumnWidth(0, 70) self.server_list.SetColumnWidth(1, wx.LIST_AUTOSIZE) + if self.server_list.GetColumnWidth(1) < 70: self.server_list.SetColumnWidth(1, 70) + self.server_list.SetColumnWidth(2, wx.LIST_AUTOSIZE) if self.serverNameSet == 0: - # Pointless to constantly set the address field to random - # server. This way, at least, if someone has their own local - # server it can now easily be connected with. Nor do we want - # to reset this every time as it would wipe any previously - # entered data or even the previously selected server data! - # Localhost should probably be used but some systems are ultra/lame - # broken about localhost use. self.texts["address"].SetValue("127.0.0.1:6774") self.serverNameSet = 1 - else: - pass - + else: pass # Allow xml_dom to be collected - try: - xml_dom.unlink() - except: - pass except Exception, e: print "Server List not available." traceback.print_exc() + def failed_connection(self): if(self.cur_server_index >= 0): - id = self.servers[self.cur_server_index].getAttribute('id') - meta = self.servers[self.cur_server_index].getAttribute('meta') - address = self.servers[self.cur_server_index].getAttribute('address') - port = self.servers[self.cur_server_index].getAttribute('port') + server_index = self.servers[self.cur_server_index] # post_failed_connection will return a non-zero if the server # was removed. If it was, refresh the display - if(meta_server_lib.post_failed_connection(id,meta=meta,address=address,port=port)): + if(meta_server_lib.post_failed_connection(server_index.get('id'), + meta=server_index.get('meta'), address=server_index.get('address'), + port=server_index.get('port'))): self.refresh_server_list() def do_connect(self, address): chat = component.get('chat') chat.InfoPost("Locating server at " + address + "...") - if self.session.connect(address): - self.frame.start_timer() + if self.session.connect(address): self.frame.start_timer() else: chat.SystemPost("Failed to connect to game server...") self.failed_connection() def do_join_lobby(self): self.cur_room_index = 0 - self.session.send_join_group("0","") + self.session.send_join_group("0", "") self.set_lobbybutton(0); def do_join_group(self): - if self.cur_room_index >= 0: - if self.cur_room_index != 0: - self.set_lobbybutton(1); - else: - self.set_lobbybutton(0); - group_id = str(self.room_list.GetItemData(self.cur_room_index)) - group = self.session.get_group_info(group_id) + if self.cur_room_index >= 0: + if self.cur_room_index != 0: self.set_lobbybutton(1); + else: self.set_lobbybutton(0); + group_id = str(self.room_list.GetItemData(self.cur_room_index)) + group = self.session.get_group_info(group_id) pwd = "" if (group[2] == "True") or (group[2] == "1"): pwd = self.password_manager.GetPassword("room", group_id) @@ -553,10 +577,9 @@ #if dlg.ShowModal() == wx.ID_OK: # pwd = dlg.GetValue() #dlg.Destroy() - else: - pwd = "" - if pwd != None: #pwd==None means the user clicked "Cancel" - self.session.send_join_group(group_id,pwd) + else: pwd = "" + if pwd != None: #pwd == None means the user clicked "Cancel" + self.session.send_join_group(group_id, pwd) def do_create_group(self): name = self.texts["room_name"].GetValue() @@ -592,63 +615,42 @@ e = name[loc+1:] name = b + "'" + e oldloc = loc+1 - if self.buttons[GS_PWD].GetValue(): - pwd = self.texts["room_pwd"].GetValue() - else: - pwd = "" - if name == "": - wx.MessageBox("Invalid Name","Error"); + if self.buttons['gs_pwd'].GetValue(): pwd = self.texts["room_pwd"].GetValue() + else: pwd = "" + if name == "": wx.MessageBox("Invalid Name","Error"); else: msg = "%s is creating room \'%s.\'" % (self.session.name, name) - self.session.send( msg ) - self.session.send_create_group(name,pwd,boot_pwd,minversion) + self.session.send(msg) + self.session.send_create_group(name, pwd, boot_pwd, minversion) self.set_lobbybutton(1); #enable the Lobby quickbutton - #--------------------------------------------------------- # [START] Snowdog: Updated Game Server Window 12/02 #--------------------------------------------------------- - def on_size(self,evt): + def on_size(self, evt): # set column widths for room list - - # set column widths for server list pass - - #--------------------------------------------------------- # [END] Snowdog: Updated Game Server Window 12/02 #--------------------------------------------------------- - def colorize_group_list(self, groups): try: hex = orpg.tools.rgbhex.RGBHex() - for gr in groups: item_list_location = self.room_list.FindItemData(-1,int(gr[0])) if item_list_location != -1: item = self.room_list.GetItem(item_list_location) - if gr[0] == "0": - - r,g,b = hex.rgb_tuple(settings.get_setting("RoomColor_Lobby")) + if gr[0] == "0": r,g,b = hex.rgb_tuple(settings.get("RoomColor_Lobby")) elif gr[3] <> "0": - if gr[2] == "True" or gr[2] == "1": - - r,g,b = hex.rgb_tuple(settings.get_setting("RoomColor_Locked")) - else: - - r,g,b = hex.rgb_tuple(settings.get_setting("RoomColor_Active")) - else: - - r,g,b = hex.rgb_tuple(settings.get_setting("RoomColor_Empty")) - - + r,g,b = hex.rgb_tuple(settings.get("RoomColor_Locked")) + else: r,g,b = hex.rgb_tuple(settings.get("RoomColor_Active")) + else: r,g,b = hex.rgb_tuple(settings.get("RoomColor_Empty")) color = wx.Colour(red=r,green=g,blue=b) item.SetTextColour(color) self.room_list.SetItem(item) - except: - traceback.print_exc() + except: traceback.print_exc() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/meta_server_lib.py --- a/orpg/networking/meta_server_lib.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/meta_server_lib.py Tue Nov 10 14:11:28 2009 -0600 @@ -37,20 +37,22 @@ from orpg.orpgCore import component from orpg.tools.validate import validate from orpg.dirpath import dir_struct -import urllib -import orpg.minidom + +import urllib, time, sys, traceback, re +#import orpg.minidom + from threading import * -import time -import sys -import random -import traceback -import re +from random import uniform +from urllib import urlopen, urlencode +from orpg.tools.orpg_log import debug + +from xml.etree.ElementTree import Element, fromstring metacache_lock = RLock() -def get_server_dom(data=None,path=None): +def get_server_dom(data=None,path=None, string=False): # post data at server and get the resulting DOM - + #debug() if path == None: # get meta server URI path = getMetaServerBaseURL() @@ -62,7 +64,7 @@ print "==========================================" print data print - file = urllib.urlopen(path, data) + file = urlopen(path, data) data = file.read() file.close() @@ -78,122 +80,95 @@ print data print # build dom - xml = component.get('xml') - xml_dom = xml.parseXml(data) - xml_dom = xml_dom._get_documentElement() - return xml_dom + etreeEl = data + if not string: return fromstring(etreeEl) + else: return etreeEl -def post_server_data( name, realHostName=None): - if realHostName: - data = urllib.urlencode( {"server_data[name]":name, +def post_server_data(name, realHostName=None): + #debug() + if realHostName: data = urlencode({"server_data[name]":name, "server_data[version]":PROTOCOL_VERSION, "act":"new", - "REMOTE_ADDR": realHostName } ) - - else: - #print "Letting meta server decide the hostname to list..." - data = urllib.urlencode( {"server_data[name]":name, + "REMOTE_ADDR": realHostName }) + # print "Letting meta server decide the hostname to list..." + else: data = urlencode({"server_data[name]":name, "server_data[version]":PROTOCOL_VERSION, - "act":"new"} ) - - #xml_dom = get_server_dom( data , "http://openrpg.sf.net/openrpg_servers.php")#Sourceforge still? - path = component.get('settings').get_setting('MetaServerBaseURL') #getMetaServerBaseURL() - xml_dom = get_server_dom(data, path) - ret_val = int( xml_dom.getAttribute( "id" ) ) - return ret_val + "act":"new"}) + path = component.get('settings').get('MetaServerBaseURL') #getMetaServerBaseURL() + etreeEl = get_server_dom(data, path) + return int(etreeEl.get('id')) def post_failed_connection(id,meta=None,address=None,port=None): + #debug((meta, address, port)) # For now, turning this off. This needs to be re-vamped for # handling multiple Metas. return 0 - #data = urllib.urlencode({"id":id,"act":"failed"}); + #data = urlencode({"id":id,"act":"failed"}); #xml_dom = get_server_dom(data) #ret_val = int(xml_dom.getAttribute("return")) #return ret_val def remove_server(id): - data = urllib.urlencode({"id":id,"act":"del"}); - xml_dom = get_server_dom(data) - ret_val = int(xml_dom.getAttribute("return")) - return ret_val - + #debug(id) + data = urlencode({"id":id,"act":"del"}); + etreeEl = get_server_dom(data) + return int(etreeEl.get("return")) -def byStartAttribute(first,second): - # This function is used to easily sort a list of nodes - # by their start time +def byStartAttribute(first, second): + # This function is used to easily sort a list of nodes by their start time + # Ensure there is something to sort with for each - if first.hasAttribute("start"): first_start = int(first.getAttribute("start")) - else: first_start = 0 - - if second.hasAttribute("start"): second_start = int(second.getAttribute("start")) - else: second_start = 0 + first_start = int(first.get('start')) or 0 + second_start = int(second.get('start')) or 0 # Return the result of the cmp function on the two strings - return cmp(first_start,second_start) + return cmp(first_start, second_start) -def byNameAttribute(first,second): - # This function is used to easily sort a list of nodes - # by their name attribute - +def byNameAttribute(first, second): + # This function is used to easily sort a list of nodes by their name attribute # Ensure there is something to sort with for each - if first.hasAttribute("name"): first_name = str(first.getAttribute("name")).lower() - else: first_name = "" - - if second.hasAttribute("name"): second_name = str(second.getAttribute("name")).lower() - else: second_name = "" - + first_name = first.get('name') or '' + second_name = second.get('name') or '' + # Return the result of the cmp function on the two strings return cmp(first_name,second_name) -def get_server_list(versions = None,sort_by="start"): - data = urllib.urlencode({"version":PROTOCOL_VERSION,"ports":"%"}) - all_metas = getMetaServers(versions,1) # get the list of metas +def get_server_list(versions = None, sort_by="start"): + data = urlencode({"version":PROTOCOL_VERSION,"ports":"%"}) + all_metas = getMetaServers(versions, 1) # get the list of metas base_meta = getMetaServerBaseURL() #all_metas.reverse() # The last one checked will take precedence, so reverse the order - # so that the top one on the actual list is checked last + # so that the top one on the actual list is checked last + return_hash = {} # this will end up with an amalgamated list of servers - return_hash = {} # this will end up with an amalgamated list of servers - - for meta in all_metas: # check all of the metas - + for meta in all_metas: # check all of the metas #get the server's xml from the current meta bad_meta = 0 #print "Getting server list from " + meta + "..." - try: xml_dom = get_server_dom(data=data,path=meta) - except: - #print "Trouble getting servers from " + meta + "..." - bad_meta = 1 - - if bad_meta: - continue - - if base_meta == meta: - #print "This is our base meta: " + meta - updateMetaCache(xml_dom) - - node_list = xml_dom.getElementsByTagName('server') - - if len(node_list): # if there are entries in the node list - # otherwise, just loop to next meta + try: xml_dom = get_server_dom(data=data, path=meta) + except: bad_meta = 1 #print "Trouble getting servers from " + meta + "..." + if bad_meta: continue + if base_meta == meta: updateMetaCache(xml_dom) #print "This is our base meta: " + meta + node_list = xml_dom.findall('server') + if len(node_list): # if there are entries in the node list + # otherwise, just loop to next meta # for each node found, we're going to check the nodes from prior # metas in the list. If a match is found, then use the new values. for n in node_list: - # set them from current node - - if not n.hasAttribute('name'): n.setAttribute('name','NO_NAME_GIVEN') - name = n.getAttribute('name') - if not n.hasAttribute('num_users'): n.setAttribute('num_users','N/A') - num_users = n.getAttribute('num_users') - if not n.hasAttribute('address'): n.setAttribute('address','NO_ADDRESS_GIVEN') - address = n.getAttribute('address') - if not n.hasAttribute('port'): n.setAttribute('port','6774') - port = n.getAttribute('port') - n.setAttribute('meta',meta) + if not n.get('name'): n.set('name','NO_NAME_GIVEN') + name = n.get('name') + if not n.get('num_users'): n.set('num_users','N/A') + num_users = n.get('num_users') + if not n.get('address'): n.set('address','NO_ADDRESS_GIVEN') + address = n.get('address') + if not n.get('port'): n.set('port','6774') + port = n.get('port') + n.set('meta',meta) end_point = str(address) + ":" + str(port) if return_hash.has_key(end_point): if META_DEBUG: print "Replacing duplicate server entry at " + end_point @@ -203,20 +178,19 @@ # Now, we have to construct a new DOM to pass back. # Create a servers element - return_dom = orpg.minidom.Element("servers") + server_list = Element('servers') # get the nodes stored in return_hash - return_list = return_hash.values() + sort_list = return_hash.values() # sort them by their name attribute. Uses byNameAttribute() # defined above as a comparison function - - if sort_by == "start": return_list.sort(byStartAttribute) - elif sort_by == "name": return_list.sort(byNameAttribute) + if sort_by == "start": sort_list.sort(byStartAttribute) + elif sort_by == "name": sort_list.sort(byNameAttribute) # Add each node to the DOM - for n in return_list: return_dom.appendChild(n) - return return_dom + for n in sort_list: server_list.append(n) + return server_list ## List Format: ## @@ -226,21 +200,21 @@ def updateMetaCache(xml_dom): try: if META_DEBUG: print "Updating Meta Server Cache" - metaservers = xml_dom.getElementsByTagName( 'metaservers' ) # pull out the metaservers bit + metaservers = xml_dom.findall('metaservers') # pull out the metaservers bit if len(metaservers) == 0: cmetalist = getRawMetaList() xml_dom = get_server_dom(cmetalist[0]) - metaservers = xml_dom.getElementsByTagName( 'metaservers' ) - authoritative = metaservers[0].getAttribute('auth') + metaservers = xml_dom.findall('metaservers') + authoritative = metaservers[0].get('auth') if META_DEBUG: print " Authoritive Meta: "+str(authoritative) - metas = metaservers[0].getElementsByTagName("meta") # get the list of metas + metas = metaservers[0].findall("meta") # get the list of metas if META_DEBUG: print " Meta List ("+str(len(metas))+" servers)" try: metacache_lock.acquire() ini = open(dir_struct["user"]+"metaservers.cache","w") for meta in metas: - if META_DEBUG: print " Writing: "+str(meta.getAttribute('path')) - ini.write(str(meta.getAttribute('path')) + " " + str(meta.getAttribute('versions')) + "\n") + if META_DEBUG: print " Writing: "+str(meta.get('path')) + ini.write(str(meta.get('path')) + " " + str(meta.get('versions')) + "\n") ini.close() finally: metacache_lock.release() @@ -248,7 +222,18 @@ if META_DEBUG: traceback.print_exc() print "Meta Server Lib: UpdateMetaCache(): " + str(e) -def getRawMetaList(): +def getRawMetaList(path=None): + ### Alpha ### + """This code will allow for a list of metas to be created. Future developement will more integrate the list of metas""" + if path != None: + metas = [] + data = urlencode({"version":PROTOCOL_VERSION,"ports":"%"}) + xml_dom = get_server_dom(data, path) + node_list = fromstring(xml_dom).findall('meta_server') + if len(node_list): + for n in node_list: + metas.append(n.get('path')) + return metas try: try: metacache_lock.acquire() @@ -268,16 +253,15 @@ def getMetaServers(versions = None, pick_random=0): """ get meta server URLs as a list - versions is a list of acceptable version numbers. A False truth value will use getMetaServerBaseURL() - set a default if we have weird reading problems default_url = "http://www.openrpg.com/openrpg_servers.php" """ + ### Pre Alpha Design ### + """ Here is how to handle Multiple Meta servers, and probably the best way to do it. Create an XML file that contains nodes with the various servers. Users will grab that meta data and have the option to connect to multiple meta servers which will allow them to find all the rooms. A check box should be used so if one server faile the users can continue without much lag. When creating a server hosts will need to select a meta to go too. This should be in the final of Ornery Orc.""" meta_names = [] - if(versions): # If versions are supplied, then look in metaservers.conf try: """ @@ -287,15 +271,13 @@ handle. Generally, this will be either a 1 for the original Meta format, or 2 for the new one. """ - # Read in the metas + #Adding a path object will attempt to look for a meta_network. metas = getRawMetaList() - #print str(metas) # go through each one to check if it should be returned, based on the # version numbers allowed. for meta in metas: - # split the line on whitespace # obviously, your meta servers urls shouldn't contain whitespace. duh. words = meta.split() @@ -316,7 +298,7 @@ # if we have more than one and want a random one elif pick_random: if META_DEBUG: print "choosing random meta from: " + str(meta_names) - i = int(random.uniform(0,len(meta_names))) + i = int(uniform(0,len(meta_names))) #meta = meta_names[i] meta_names = [meta_names[i]] if META_DEBUG: print "using: " + str(meta_names) @@ -347,7 +329,6 @@ node_list = tree.getElementsByTagName("MetaServerBaseURL") if node_list: url = node_list[0].getAttribute("value") - # allow tree to be collected try: tree.unlink() except: pass @@ -391,21 +372,19 @@ just written, i.e. TestDeleteStatus() and Delete() """ - def __init__(self,name=None,realHostName=None,num_users = "Hmmm",MetaPath=None,port=6774,register_callback=None): - - Thread.__init__(self,name="registerThread") - self.rlock = RLock() # Re-entrant lock used to make this class thread safe - self.die_event = Event() # The main loop in run() will wait with timeout on this - if name: - self.name = name # Name that the server want's displayed on the Meta - else: - self.name = "Unnamed server" # But use this if for some crazy reason no name is - # passed to the constructor - self.num_users = num_users # the number of users currently on this server + def __init__(self, name=None, realHostName=None, num_users="0", + MetaPath=None, port=6774, register_callback=None): + Thread.__init__(self, name="registerThread") + self.rlock = RLock() # Re-entrant lock used to make this class thread safe + self.die_event = Event() # The main loop in run() will wait with timeout on this + self.name = name or 'Unnamed Server' # Name that the server want's displayed on the Meta + # But use Unnamed Server if for some crazy reason + # no name is passed to the constructor + self.num_users = num_users # the number of users currently on this server self.realHostName = realHostName # Name to advertise for connection self.id = "0" # id returned from Meta. Defaults to "0", which # indicates a new registration. - self.cookie = "0" # cookie returned from Meta. Defaults to "0",which + self.cookie = "0" # cookie returned from Meta. Defaults to "0", which # indicates a new registration. self.interval = 0 # interval returned from Meta. Is how often to # re-register, in minutes. @@ -422,11 +401,8 @@ it easier to have multiple registerThreads going to keep the server registered on multiple (compatible) Metas. """ - - if MetaPath == None: - self.path = getMetaServerBaseURL() # Do this if no Meta specified - else: - self.path = MetaPath + if MetaPath == None: self.path = getMetaServerBaseURL() # Do this if no Meta specified + else: self.path = MetaPath def getIdAndCookie(self): return self.id, self.cookie @@ -435,15 +411,13 @@ try: self.rlock.acquire() return self.die_event.isSet() - finally: - self.rlock.release() + finally: self.rlock.release() def Delete(self): try: self.rlock.acquire() self.die_event.set() - finally: - self.rlock.release() + finally: self.rlock.release() def run(self): """ @@ -455,24 +429,23 @@ re-registers this server and sleeps Interval minutes until the thread is ordered to die in place """ - while(not self.TestDeleteStatus()): # Loop while until told to die + while(not self.TestDeleteStatus()): # Loop while until told to die # Otherwise, call thread safe register(). self.register(self.name, self.realHostName, self.num_users) if META_DEBUG: print "Sent Registration Data" # register() will end up setting the state variables # for us, including self.interval. try: - self.rlock.acquire() # Serialize access to this state information + self.rlock.acquire() # Serialize access to this state information - if self.interval >= 3: # If the number of minutes is one or greater - self.interval -= 1 # wake up with 30 seconds left to re-register + if self.interval >= 3: # If the number of minutes is one or greater + self.interval -= 1 # wake up with 30 seconds left to re-register else: - self.interval = .5 # Otherwise, we probably experienced some kind - # of error from the Meta in register(). Sleep - # for 6 seconds and start from scratch. + self.interval = .5 # Otherwise, we probably experienced some kind + # of error from the Meta in register(). Sleep + # for 6 seconds and start from scratch. - finally: # no matter what, release the lock - self.rlock.release() + finally: self.rlock.release() # no matter what, release the lock # Wait interval minutes for a command to die die_signal = self.die_event.wait(self.interval*60) @@ -501,14 +474,14 @@ if not self.isAlive(): # check to see if this thread is dead return 1 # If so, return an error result # Do the actual unregistering here - data = urllib.urlencode( {"server_data[id]":self.id, + data = urlencode( {"server_data[id]":self.id, "server_data[cookie]":self.cookie, "server_data[version]":PROTOCOL_VERSION, "act":"unregister"} ) - try: - xml_dom = get_server_dom(data=data, path=self.path) # this POSTS the request and returns the result - if xml_dom.hasAttribute("errmsg"): - print "Error durring unregistration: " + xml_dom.getAttribute("errmsg") + try: # this POSTS the request and returns the result + xml_dom = get_server_dom(data=data, path=self.path) + if xml_dom.get("errmsg"): + print "Error durring unregistration: " + xml_dom.get("errmsg") except: if META_DEBUG: print "Problem talking to Meta. Will go ahead and die, letting Meta remove us." # If there's an error, echo it to the console @@ -524,6 +497,7 @@ finally: self.rlock.release() def register(self, name=None, realHostName=None, num_users=None): + #debug((name, realHostName, num_users)) """ Designed to handle the registration, both new and repeated. @@ -545,7 +519,7 @@ if realHostName: self.realHostName = realHostName # build POST data if self.realHostName: - data = urllib.urlencode( {"server_data[id]":self.id, + data = urlencode( {"server_data[id]":self.id, "server_data[cookie]":self.cookie, "server_data[name]":self.name, "server_data[port]":self.port, @@ -555,15 +529,16 @@ "server_data[address]": self.realHostName } ) else: if META_DEBUG: print "Letting meta server decide the hostname to list..." - data = urllib.urlencode( {"server_data[id]":self.id, + data = urlencode( {"server_data[id]":self.id, "server_data[cookie]":self.cookie, "server_data[name]":self.name, "server_data[port]":self.port, "server_data[version]":PROTOCOL_VERSION, "server_data[num_users]":self.num_users, "act":"register"} ) - try: - xml_dom = get_server_dom(data=data,path=self.path) # this POSTS the request and returns the result + try: # this POSTS the request and returns the result + etreeEl = get_server_dom(data=data, path=self.path) + #debug(etreeEl) except: if META_DEBUG: print "Problem talking to server. Setting interval for retry ..." if META_DEBUG: print data @@ -584,10 +559,11 @@ return 0 # indicates that it was okay to call, not that no errors occurred # If there is a DOM returned .... - if xml_dom: + if etreeEl: + #debug(etreeEl) # If there's an error, echo it to the console - if xml_dom.hasAttribute("errmsg"): - print "Error durring registration: " + xml_dom.getAttribute("errmsg") + if etreeEl.get("errmsg"): + print "Error durring registration: " + etreeEl.get("errmsg") if META_DEBUG: print data if META_DEBUG: print """ @@ -609,24 +585,20 @@ Is it wrong to have a method where there's more comments than code? :) """ try: - self.interval = int(xml_dom.getAttribute("interval")) - self.id = xml_dom.getAttribute("id") - self.cookie = xml_dom.getAttribute("cookie") - if not xml_dom.hasAttribute("errmsg"): - updateMetaCache(xml_dom) + self.interval = int(etreeEl.get("interval")) + self.id = etreeEl.get("id") + self.cookie = etreeEl.get("cookie") + if not etreeEl.get("errmsg"): updateMetaCache(xml_dom) except: if META_DEBUG: print if META_DEBUG: print "OOPS! Is the Meta okay? It should be returning an id, cookie, and interval." if META_DEBUG: print "Check to see what it really returned.\n" # Let xml_dom get garbage collected - try: - xml_dom.unlink() - except: - pass + try: xml_dom.unlink() + except: pass else: # else if no DOM is returned from get_server_dom() print "Error - no DOM constructed from Meta message!" return 0 # Let caller know it was okay to call us - finally: - self.rlock.release() + finally: self.rlock.release() # End of class registerThread ################################################################################ diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/mplay_client.py --- a/orpg/networking/mplay_client.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/mplay_client.py Tue Nov 10 14:11:28 2009 -0600 @@ -31,20 +31,21 @@ ### Alpha ### ##import orpg.minidom ## Deprecated. xml.parseXml calls minidom.parseString so it was superfluous and wasteful. -import socket -import Queue -import thread -import traceback +import socket, Queue, thread, traceback, errno, os, time + from threading import Event, Lock from xml.sax.saxutils import escape from struct import pack, unpack, calcsize from string import * from orpg.orpg_version import CLIENT_STRING, PROTOCOL_VERSION, VERSION -import errno -import os -import time + from orpg.orpgCore import component from orpg.orpg_xml import xml +from orpg.tools.orpg_log import debug + +from xml.etree.ElementTree import ElementTree, Element, iselement +from xml.etree.ElementTree import fromstring, tostring +from xml.parsers.expat import ExpatError try: import bz2 @@ -56,8 +57,7 @@ cmpZLIB = True except: cmpZLIB = False - -# This should be configurable +# Default, is configurable OPENRPG_PORT = 9557 # We should be sending a length for each packet @@ -92,7 +92,7 @@ return escape(data,{"\"":""}) class mplay_event: - def __init__(self,id,data=None): + def __init__(self, id, data=None): self.id = id self.data = data @@ -105,7 +105,6 @@ BOOT_MSG = "YoU ArE ThE WeAkEsT LiNk. GoOdByE." class client_base: - # Player role definitions def __init__(self): self.outbox = Queue.Queue(0) @@ -147,7 +146,7 @@ # Loop as long as we have a connection while( self.get_status() == MPLAY_CONNECTED ): - try: readMsg = self.outbox.get( block=1 ) + try: readMsg = self.outbox.get(block=1) except Exception, text: self.log_msg( ("outbox.get() got an exception: ", text) ) @@ -166,7 +165,6 @@ def recvThread( self, arg ): "Receiving thread. This thread reads from the socket and writes to the data queue." - # Wait to be told it's okay to start running self.startedEvent.wait() @@ -189,14 +187,13 @@ if bytes == 0: self.log_msg( "Remote has disconnected!" ) self.set_status( MPLAY_DISCONNECTING ) - self.outbox.put( "" ) # Make sure the other thread is woken up! + self.outbox.put("") # Make sure the other thread is woken up! self.sendThreadExitEvent.set() self.log_msg( "recvThread has terminated..." ) def sendMsg( self, sock, msg ): """Very simple function that will properly encode and send a message to te remote on the specified socket.""" - if self.useCompression and self.compressionType != None: mpacket = self.compressionType.compress(msg) lpacket = pack('!i', len(mpacket)) @@ -208,16 +205,10 @@ offset += sent sentm = offset else: - # Calculate our message length - length = len(msg) - - # Encode the message length into network byte order - lp = pack('!i', length) - + length = len(msg) # Calculate our message length + lp = pack('!i', length) # Encode the message length into network byte order try: - # Send the encoded length - sentl = sock.send( lp ) - + sentl = sock.send( lp ) # Send the encoded length # Now, send the message the the length was describing sentm = sock.send( msg ) if self.isServer(): self.log_msg(("data_sent", sentl+sentm)) @@ -248,27 +239,18 @@ data = "" return data - def recvMsg( self, sock ): - """This method now expects to receive a message having a 4-byte prefix length. It will ONLY read - completed messages. In the event that the remote's connection is terminated, it will throw an - exception which should allow for the caller to more gracefully handle this exception event. + def recvMsg(self, sock): + """This method now expects to receive a message having a 4-byte prefix length. It will ONLY read completed messages. In the event that the remote's connection is terminated, it will throw an exception which should allow for the caller to more gracefully handle this exception event. - Because we use strictly reading ONLY based on the length that is told to use, we no longer have to - worry about partially adjusting for fragmented buffers starting somewhere within a buffer that we've - read. Rather, it will get ONLY a whole message and nothing more. Everything else will remain buffered - with the OS until we attempt to read the next complete message.""" - + Because we use strictly reading ONLY based on the length that is told to use, we no longer have to worry about partially adjusting for fragmented buffers starting somewhere within a buffer that we've read. Rather, it will get ONLY a whole message and nothing more. Everything else will remain buffered with the OS until we attempt to read the next complete message.""" msgData = "" try: lenData = self.recvData( sock, MPLAY_LENSIZE ) - # Now, convert to a usable form - (length,) = unpack('!i', lenData) - # Read exactly the remaining amount of data - msgData = self.recvData( sock, length ) + (length,) = unpack('!i', lenData) # Now, convert to a usable form + msgData = self.recvData( sock, length ) # Read exactly the remaining amount of data if self.isServer(): self.log_msg(("data_recv", length+4)) - # Make the peer IP address available for reference later - if self.remote_ip is None: + if self.remote_ip is None: # Make the peer IP address available for reference later self.remote_ip = self.sock.getpeername() except IOError, e: self.log_msg( e ) except Exception, e: self.log_msg( e ) @@ -279,15 +261,16 @@ self.status = MPLAY_CONNECTED self.sock.setblocking(1) # Confirm that our threads have started - thread.start_new_thread( self.sendThread,(0,) ) - thread.start_new_thread( self.recvThread,(0,) ) + thread.start_new_thread( self.sendThread,(0,)) + thread.start_new_thread( self.recvThread,(0,)) self.startedEvent.set() def disconnect(self): + debug() self.set_status(MPLAY_DISCONNECTING) self.log_msg("client stub " + self.ip +" disconnecting...") self.log_msg("closing sockets...") - try: self.sock.shutdown( 2 ) + try: self.sock.shutdown(2) except Exception, e: print "Caught exception: " + str(e) print @@ -304,10 +287,8 @@ self.role = role def use_roles(self): - if self.useroles: - return 1 - else: - return 0 + if self.useroles: return 1 + else: return 0 def update_self_from_player(self, player): try: (self.name, self.ip, self.id, self.text_status, self.version, @@ -318,26 +299,30 @@ The IP field should really be deprecated as too many systems are NAT'd and/or behind firewalls for a client provided IP address to have much value. As such, we now label it as deprecated. """ - def toxml(self,action): - xml_data = '' - self.send(msg,id) + el = Element('boot') + el.set('boot_pwd', boot_pwd) + self.send(tostring(el), id) #--------------------------------------------------------- # [START] Snowdog Password/Room Name altering code 12/02 #--------------------------------------------------------- - def set_room_pass(self,npwd,pwd=""): - recycle_bin = "" - self.outbox.put(recycle_bin); del recycle_bin #makes line easier to read. --TaS + def set_room_pass(self, npwd, pwd=""): + el = Element('alter') + el.set('key', 'pwd') + el.set('val', npwd) + el.set('bpw', pwd) + el.set('plr', self.id) + el.set('gid', self.group_id) + self.outbox.put(tostring(el)) self.update() - def set_room_name(self,name,pwd=""): + def set_room_name(self, name, pwd=""): loc = name.find("&") oldloc=0 while loc > -1: @@ -588,10 +566,13 @@ e = name[loc+1:] name = b + "'" + e oldloc = loc+1 - recycle_bin = "" - self.outbox.put(recycle_bin); del recycle_bin #makes line easier to read. --TaS + el = Element('alter') + el.set('key', 'pwd') + #el.set('val', npwd) + el.set('bpw', str(pwd)) + el.set('plr', self.id) + el.set('gid', self.group_id) + self.outbox.put(tostring(el)) self.update() #--------------------------------------------------------- @@ -599,19 +580,39 @@ #--------------------------------------------------------- def display_roles(self): - self.outbox.put("") + el = Element('role') + el.set('action', 'display') + el.set('player', self.id) + el.set('group_id', self.group_id) + self.outbox.put(tostring(el)) def get_role(self): - self.outbox.put("") + el = Element('role') + el.set('action', 'get') + el.set('player', self.id) + el.set('group_id', self.group_id) + self.outbox.put(tostring(el)) - def set_role(self,player,role,pwd=""): - recycle_bin = "" - self.outbox.put(recycle_bin); del recycle_bin #makes line easer to read. --TaS + def set_role(self, player, role, pwd=""): + el = Element('role') + el.set('action', 'set') + el.set('player', player) + el.set('role', role) + el.set('boot_pwd', pwd) + el.set('group_id', self.group_id) + self.outbox.put(tostring(el)) self.update() - def send(self,msg,player="all"): + def send(self, msg, player="all"): if self.status == MPLAY_CONNECTED and player != self.id: + ### Pre Alpha ### + #el = Element('msg') + #el.set('to', player) + #el.set('from', self.id) + #el.set('group_id', self.group_id) + #el.append(fromstring(msg)) + #self.outbox.put(tostring(el)) + ### Current chat is not ready for Element Tree ### self.outbox.put(""+msg) self.check_my_status() @@ -620,19 +621,26 @@ self.outbox.put(snd_xml) self.check_my_status() - def send_create_group(self,name,pwd,boot_pwd,minversion): - recycle_bin = "" - self.outbox.put(recycle_bin); del recycle_bin #makes line easier to read. --TaS + def send_create_group(self, name, pwd, boot_pwd, min_version): + el = Element('create_group') + el.set('from', self.id) + el.set('pwd', pwd) + el.set('name', name) + el.set('boot_pwd', boot_pwd) + el.set('min_version', min_version) + self.outbox.put(tostring(el)) - def send_join_group(self,group_id,pwd): + def send_join_group(self, group_id, pwd): if (group_id != 0): self.update_role("Lurker") - self.outbox.put("") + el = Element('join_group') + el.set('from', self.id) + el.set('pwd', pwd) + el.set('group_id', str(group_id)) + + self.outbox.put(tostring(el)) def poll(self, evt=None): - try: - msg = self.inbox.get_nowait() + try: msg = self.inbox.get_nowait() except: if self.get_status() != MPLAY_CONNECTED: self.check_my_status() @@ -663,29 +671,32 @@ self.add_msg_handler('password', self.on_password, True) self.add_msg_handler('sound', self.on_sound, True) - def pretranslate(self,data): + def pretranslate(self, data): # Pre-qualify our data. If we don't have atleast 5-bytes, then there is # no way we even have a valid message! if len(data) < 5: return - end = data.find(">") - head = data[:end+1] - msg = data[end+1:] - xml_dom = self.xml.parseXml(head) - xml_dom = xml_dom._get_documentElement() - tag_name = xml_dom._get_tagName() - id = xml_dom.getAttribute("from") - if id == '': id = xml_dom.getAttribute("id") - if self.msg_handlers.has_key(tag_name): self.msg_handlers[tag_name](id, data, xml_dom) - else: - #Unknown messages recived ignoring - #using pass insted or printing an error message - #because plugins should now be able to send and proccess messages - #if someone is using a plugin to send messages and this user does not - #have the plugin they would be getting errors - pass - if xml_dom: xml_dom.unlink() + try: el = fromstring(data) + except ExpatError: + end = data.find(">") + head = data[:end+1] + msg = data[end+1:] + ### Alpha ### + if head[end:] != '/': + if head[end:] != '>': head = head[:end] + '/>' + ### This if statement should help close invalid messages. Since it needs fixing, use the try except message for now. + try: el = fromstring(head) + except: el = fromstring(head[:end] +'/>') - def on_sound(self, id, data, xml_dom): + try: + el1 = fromstring(msg) + el.append(el1) + except ExpatError: + el.text = msg + #logger.general("Bad Message: \n" + data) + id = el.get('from') or el.get('id') + if el.tag in self.msg_handlers: self.msg_handlers[el.tag](id, data, el) + + def on_sound(self, id, data, etreeEl): (ignore_id,ignore_name) = self.get_ignore_list() for m in ignore_id: if m == id: @@ -693,96 +704,79 @@ print "ignoring sound from player:" return chat = self.get_chat() - snd = xml_dom.getAttribute("url") - loop_sound = xml_dom.getAttribute("loop") - chat.sound_player.play(snd, "remote", loop_sound) + chat.sound_player.play(etreeEl.get('url'), 'remote', etreeEl.get('loop')) - def on_msg(self, id, data, xml_dom): + def on_msg(self, id, data, etreeEl): end = data.find(">") head = data[:end+1] msg = data[end+1:] - if id == "0": - self.on_receive(msg,None) # None get's interpreted in on_receive as the sys admin. - # Doing it this way makes it harder to impersonate the admin - else: - if self.is_valid_id(id): self.on_receive(msg,self.players[id]) - if xml_dom: xml_dom.unlink() + if msg[-6:] == '': msg = msg[:-6] + if id == "0": self.on_receive(msg, None) + # None get's interpreted in on_receive as the sys admin. + # Doing it this way makes it harder to impersonate the admin + elif self.is_valid_id(id): self.on_receive(msg, self.players[id]) - def on_ping(self, id, msg, xml_dom): + def on_ping(self, id, msg, etreeEl): #a REAL ping time implementation by Snowdog 8/03 # recieves special server command # where ### is a returning time from the clients ping command #get current time, pull old time from object and compare them # the difference is the latency between server and client * 2 ct = time.clock() - ot = xml_dom.getAttribute("time") + ot = etreeEl.get("time") latency = float(float(ct) - float(ot)) - latency = int( latency * 10000.0 ) - latency = float( latency) / 10.0 + latency = int(latency * 10000.0) + latency = float(latency) / 10.0 ping_msg = "Ping Results: " + str(latency) + " ms (parsed message, round trip)" - self.on_receive(ping_msg,None) - if xml_dom: xml_dom.unlink() + self.on_receive(ping_msg, None) - def on_group(self, id, msg, xml_dom): - name = xml_dom.getAttribute("name") - players = xml_dom.getAttribute("players") - act = xml_dom.getAttribute("action") - pwd = xml_dom.getAttribute("pwd") - group_data = (id, name, pwd, players) - - if act == 'new': + def on_group(self, id, msg, etreeEl): + act = etreeEl.get("action") + group_data = (id, etreeEl.get("name"), etreeEl.get("pwd"), etreeEl.get("players")) + if act == ('new' or 'update'): self.groups[id] = group_data - self.on_group_event(mplay_event(GROUP_NEW, group_data)) + if act == 'update': self.on_group_event(mplay_event(GROUP_UPDATE, group_data)) + elif act == 'new': self.on_group_event(mplay_event(GROUP_NEW, group_data)) elif act == 'del': del self.groups[id] self.on_group_event(mplay_event(GROUP_DEL, group_data)) - elif act == 'update': - self.groups[id] = group_data - self.on_group_event(mplay_event(GROUP_UPDATE, group_data)) - if xml_dom: xml_dom.unlink() - def on_role(self, id, msg, xml_dom): - act = xml_dom.getAttribute("action") - role = xml_dom.getAttribute("role") + def on_role(self, id, msg, etreeEl): + act = etreeEl.get("action") if (act == "set") or (act == "update"): try: (a,b,c,d,e,f,g,h) = self.players[id] if id == self.id: - self.players[id] = (a,b,c,d,e,f,g,role) - self.update_role(role) - else: self.players[id] = (a,b,c,d,e,f,g,role) - self.on_player_event(mplay_event(PLAYER_UPDATE,self.players[id])) + self.players[id] = (a,b,c,d,e,f,g,etreeEl.get("role")) + self.update_role(etreeEl.get("role")) + else: self.players[id] = (a,b,c,d,e,f,g,etreeEl.get("role")) + self.on_player_event(mplay_event(PLAYER_UPDATE, self.players[id])) except: pass - if xml_dom: xml_dom.unlink() - def on_player(self, id, msg, xml_dom): - act = xml_dom.getAttribute("action") - ip = xml_dom.getAttribute("ip") - name = xml_dom.getAttribute("name") - status = xml_dom.getAttribute("status") - version = xml_dom.getAttribute("version") - protocol_version = xml_dom.getAttribute("protocol_version") - client_string = xml_dom.getAttribute("client_string") - try: player = (name, ip, id, status, - version, protocol_version, - client_string, self.players[id][7]) + def on_player(self, id, msg, etreeEl): + act = etreeEl.get("action") + try: player = (etreeEl.get("name"), etreeEl.get("ip"), id, etreeEl.get("status"), + etreeEl.get("version"), etreeEl.get("protocol_version"), + etreeEl.get("client_string"), self.players[id][7]) except Exception, e: - player = (name, ip, id, status, - version, protocol_version, - client_string, "Player") + player = (etreeEl.get("name"), etreeEl.get("ip"), id, etreeEl.get("status"), + etreeEl.get("version"), etreeEl.get("protocol_version"), + etreeEl.get("client_string"), "Player") + print e if act == "new": self.players[id] = player self.on_player_event(mplay_event(PLAYER_NEW, self.players[id])) elif act == "group": - self.group_id = xml_dom.getAttribute("group_id") + self.group_id = etreeEl.get('group_id') self.clear_players() self.on_mplay_event(mplay_event(MPLAY_GROUP_CHANGE, self.groups[self.group_id])) - self.players[self.id] = self.get_my_info() #(self.name,self.ip,self.id,self.text_status) + self.players[self.id] = self.get_my_info() + #(self.name,self.ip,self.id,self.text_status) self.on_player_event(mplay_event(PLAYER_NEW, self.players[self.id])) elif act == "failed": self.on_mplay_event(mplay_event(MPLAY_GROUP_CHANGE_F)) elif act == "del": - self.on_player_event(mplay_event(PLAYER_DEL,self.players[id])) + self.on_player_event(mplay_event(PLAYER_DEL, self.players[id])) if self.players.has_key(id): del self.players[id] if id == self.id: self.do_disconnect() # the next two cases handle the events that are used to let you know when others are typing @@ -792,20 +786,13 @@ self.update_self_from_player(player) else: self.players[id] = player dont_send = 0 - for m in self.ignore_id: + for m in self.ignore_id: if m == id: dont_send=1 - if dont_send != 1: self.on_player_event(mplay_event(PLAYER_UPDATE,self.players[id])) - if xml_dom: xml_dom.unlink() + if dont_send != 1: self.on_player_event(mplay_event(PLAYER_UPDATE, self.players[id])) - def on_password(self, id, msg, xml_dom): - signal = type = id = data = None - id = xml_dom.getAttribute("id") - type = xml_dom.getAttribute("type") - signal = xml_dom.getAttribute("signal") - data = xml_dom.getAttribute("data") - self.on_password_signal( signal,type,id,data ) - if xml_dom: - xml_dom.unlink() + def on_password(self, id, msg, etreeEl): + self.on_password_signal(etreeEl.get("signal"), etreeEl.get("type"), + etreeEl.get("id"), etreeEl.get("data")) def check_my_status(self): status = self.get_status() @@ -817,7 +804,6 @@ if self.is_connected(): self.log_msg( "Client is already connected to a server?!? Need to disconnect first." ) return 0 - xml_dom = None self.inbox = Queue.Queue(0) self.outbox = Queue.Queue(0) addressport_ar = addressport.split(":") @@ -832,29 +818,29 @@ try: self.sock.connect((address,port)) # send client into with id=0 - self.sendMsg( self.sock, self.toxml("new") ) - data = self.recvMsg( self.sock ) + outgoing = self.toxml('new') + if iselement(outgoing): outgoing = tostring(outgoing) + self.sendMsg(self.sock, outgoing) + data = self.recvMsg(self.sock) # get new id and group_id - xml_dom = self.xml.parseXml(data) - xml_dom = xml_dom._get_documentElement() - self.id = xml_dom.getAttribute("id") - self.group_id = xml_dom.getAttribute("group_id") - if xml_dom.hasAttribute('useCompression') and xml_dom.getAttribute('useCompression') == 'True': + el = fromstring(data) + self.id = el.get('id') + self.group_id = el.get('group_id') + if el.get('useCompression') == 'True': self.useCompression = True - if xml_dom.hasAttribute('cmpType'): - if cmpBZ2 and xml_dom.getAttribute('cmpType') == 'bz2': - self.compressionType = bz2 - elif cmpZLIB and xml_dom.getAttribute('cmpType') == 'zlib': - self.compressionType = zlib - else: self.compressionType = None - else: self.compressionType = bz2 + if cmpBZ2 and el.get('cmpType') == 'bz2': + self.compressionType = bz2 + elif cmpZLIB and el.get('cmpType') == 'zlib': + self.compressionType = zlib + else: self.compressionType = None #send confirmation - self.sendMsg( self.sock, self.toxml("new") ) + outgoing = self.toxml('new') + if iselement(outgoing): outgoing = tostring(outgoing) + self.sendMsg(self.sock, outgoing) except Exception, e: + print e self.log_msg(e) - if xml_dom: xml_dom.unlink() return 0 - # Start things rollings along self.initialize_threads() self.on_mplay_event(mplay_event(MPLAY_CONNECTED)) @@ -862,12 +848,13 @@ self.text_status, self.version, self.protocol_version, self.client_string, self.role) self.on_player_event(mplay_event(PLAYER_NEW,self.players[self.id])) - if xml_dom: xml_dom.unlink() return 1 def start_disconnect(self): self.on_mplay_event(mplay_event(MPLAY_DISCONNECTING)) - self.outbox.put( self.toxml("del") ) + outgoing = self.toxml('del') + if iselement(outgoing): outgoing = tostring(outgoing) + self.outbox.put(outgoing) ## Client Side Disconect Forced -- Snowdog 10-09-2003 #pause to allow GUI events time to sync. time.sleep(1) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/mplay_groups.py --- a/orpg/networking/mplay_groups.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/mplay_groups.py Tue Nov 10 14:11:28 2009 -0600 @@ -1,4 +1,5 @@ from orpg.mapper.map_msg import * +from orpg.tools.orpg_log import debug class game_group: def __init__( self, id, name, pwd, desc="", @@ -17,19 +18,16 @@ self.moderated = 0 self.voice = {} self.persistant = persist - + ### Needs to use Element Tree closer if mapFile != None: f = open( mapFile ) tree = f.read() f.close() - else: f = open(orpg.dirpath.dir_struct["template"] + "default_map.xml") tree = f.read() f.close() - - self.game_map.init_from_xml(tree) - + self.game_map.init_from_xml(fromstring(tree)) def add_player(self,id): self.players.append(id) @@ -68,7 +66,7 @@ return 1 #depreciated - see send_group_list() - def toxml(self,act="new"): + def toxml(self, act="new"): # Please don't add the boot_pwd to the xml, as this will give it away to players watching their console xml_data = "update_self_from_player():\n" + traceback.format_exc()) @@ -218,12 +225,9 @@ idletime = self.idle_time() idlemins = idletime / 60 status = "Unknown" - if idlemins < 3: - status = "Active" - elif idlemins < 10: - status = "Idle ("+str(int(idlemins))+" mins)" - else: - status = "Inactive ("+str(int(idlemins))+" mins)" + if idlemins < 3: status = "Active" + elif idlemins < 10: status = "Idle ("+str(int(idlemins))+" mins)" + else: status = "Inactive ("+str(int(idlemins))+" mins)" return status def connected_time(self): @@ -245,18 +249,14 @@ self.timeout_time = None def check_time_out(self): - if self.timeout_time==None: - self.timeout_time = time.time() + if self.timeout_time == None: self.timeout_time = time.time() curtime = time.time() diff = curtime - self.timeout_time - if diff > 1800: - return 1 - else: - return 0 + if diff > 1800: return 1 + else: return 0 def send(self, msg): - if self.get_status() == 'connected': - self.outbox.put(msg) + if self.get_status() == 'connected': self.outbox.put(msg) def change_group(self, group_id, groups): old_group_id = str(self.group_id) @@ -275,17 +275,13 @@ def add_msg_handler(self, tag, function, core=False): if not self.msg_handlers.has_key(tag): self.msg_handlers[tag] = function - if core: - self.core_msg_handlers.append(tag) - else: - print 'XML Messages ' + tag + ' already has a handler' + if core: self.core_msg_handlers.append(tag) + else: print 'XML Messages ' + tag + ' already has a handler' def remove_msg_handler(self, tag): if self.msg_handlers.has_key(tag) and not tag in self.core_msg_handlers: del self.msg_handlers[tag] - else: - print 'XML Messages ' + tag + ' already deleted' - + else: print 'XML Messages ' + tag + ' already deleted' #Message Handaling def message_handler(self, arg): diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/mplay_server.py --- a/orpg/networking/mplay_server.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/mplay_server.py Tue Nov 10 14:11:28 2009 -0600 @@ -31,6 +31,8 @@ # 04-15-2005 [Snowdog]: Added patch from Brandan Yares (xeriar). Reference: patch tracker id #1182076 +from __future__ import with_statement + __version__ = "$Id: mplay_server.py,v 1.155 2008/01/24 03:52:03 digitalxero Exp $" #!/usr/bin/env python @@ -43,14 +45,7 @@ """ -import re -import gc -import cgi -import sys -import string -import time -import urllib -import traceback +import re, gc, cgi, sys, string, time, urllib, traceback from mplay_client import * from mplay_client import MPLAY_LENSIZE @@ -62,8 +57,11 @@ from struct import pack, unpack, calcsize from meta_server_lib import * -# Import the minidom XML module -from xml.dom import minidom +from xml.etree.ElementTree import ElementTree, Element, iselement +from xml.etree.ElementTree import fromstring, tostring, parse, XML + +from orpg.tools.orpg_log import logger, crash, debug +from orpg.tools.decorators import debugging # Snag the version number from orpg.orpg_version import VERSION, PROTOCOL_VERSION, CLIENT_STRING, SERVER_MIN_CLIENT_VERSION @@ -77,7 +75,8 @@ class game_group(object): - def __init__( self, id, name, pwd, desc="", boot_pwd="", minVersion="", mapFile=None, messageFile=None, persist =0 ): + def __init__(self, id, name, pwd, desc="", boot_pwd="", + minVersion="", mapFile=None, messageFile=None, persist=0): self.id = id self.name = name self.desc = desc @@ -92,19 +91,16 @@ self.voice = {} self.persistant = persist self.mapFile = None - + ### Needs to use Element Tree closer if mapFile != None: - self.mapFile = mapFile f = open( mapFile ) tree = f.read() f.close() - else: - f = open(dir_struct["template"] + "default_map.xml") + f = open(orpg.dirpath.dir_struct["template"] + "default_map.xml") tree = f.read() f.close() - - self.game_map.init_from_xml(tree) + self.game_map.init_from_xml(fromstring(tree)) def save_map(self): if self.mapFile is not None and self.persistant == 1 and self.mapFile.find("default_map.xml") == -1: @@ -116,8 +112,7 @@ self.players.append(id) def remove_player(self,id): - if self.voice.has_key(id): - del self.voice[id] + if self.voice.has_key(id): del self.voice[id] self.players.remove(id) def get_num_players(self): @@ -149,18 +144,18 @@ return 1 #depreciated - see send_group_list() - def toxml(self,act="new"): + def toxml(self, act="new"): # Please don't add the boot_pwd to the xml, as this will give it away to players watching their console - xml_data = "" - return xml_data - + el = Element('group') + el.set('id', self.id) + el.set('name', self.name) + el.set('pwd', str(self.pwd!="")) + el.set('players', str(self.get_num_players())) + el.set('action', act) + return tostring(el) class client_stub(client_base): - def __init__(self,inbox,sock,props,log): + def __init__(self, inbox, sock, props, log): client_base.__init__(self) self.ip = props['ip'] self.role = props['role'] @@ -190,11 +185,16 @@ if diff > 1800: return 1 else: return 0 - def send(self,msg,player,group): + def send(self, msg, player, group): if self.get_status() == MPLAY_CONNECTED: + #el = Element('msg') + #el.set('to', player) + #el.set('from', '0') + #el.set('group_id', group) + #el.text(msg) self.outbox.put("" + msg) - def change_group(self,group_id,groups): + def change_group(self, group_id, groups): old_group_id = str(self.group_id) groups[group_id].add_player(self.id) groups[old_group_id].remove_player(self.id) @@ -205,11 +205,11 @@ return old_group_id def self_message(self,act): - self.send(act,self.id,self.group_id) + self.send(act, self.id, self.group_id) def take_dom(self,xml_dom): - self.name = xml_dom.getAttribute("name") - self.text_status = xml_dom.getAttribute("status") + self.name = xml_dom.get("name") + self.text_status = xml_dom.get("status") """ ###################################################################### @@ -225,6 +225,8 @@ class mplay_server: def __init__(self, log_console=None, name=None): + logger._set_log_level = 16 + logger._set_log_to_console(True) self.log_to_console = 1 self.log_console = log_console self.alive = 1 @@ -238,9 +240,9 @@ self.next_group_id = 100 self.metas = {} # This holds the registerThread objects for each meta self.be_registered = 0 # Status flag for whether we want to be registered. - self.serverName = name # Name of this server in the metas + self.serverName = name # Name of this server in the metas self.boot_pwd = "" - self.server_address = None # IP or Name of server to post to the meta. None means the meta will auto-detect it. + self.server_address = None # IP or Name of server to post to the meta. None means the meta will auto-detect it. self.defaultMessageFile = None self.userPath = dir_struct["user"] self.lobbyMapFile = "Lobby_map.xml" @@ -316,13 +318,13 @@ # try to use it. try: - self.banDom = minidom.parse(self.userPath + 'ban_list.xml') - self.banDom.normalize() - self.banDoc = self.banDom.documentElement + self.banDom = parse(self.userPath + 'ban_list.xml') + #self.banDom.normalize() + self.banDoc = self.banDom.getroot() - for element in self.banDom.getElementsByTagName('banned'): - playerName = element.getAttribute( 'name' ).replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">") - playerIP = element.getAttribute('ip') + for element in self.banDom.findall('banned'): + playerName = element.get('name').replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">") + playerIP = element.get('ip') self.ban_list[playerIP] = {} self.ban_list[playerIP]['ip'] = playerIP self.ban_list[playerIP]['name'] = playerName @@ -337,50 +339,49 @@ # try to use it. try: - data = [] - data.append("\n") + etreeEl = Element('server') for ip in self.ban_list: - data.append(' ")) + '" ip="' + str(self.ban_list[ip]['ip']) + '" />' + "\n") - data.append("") + el = Element('banned') + el.set('name', str(self.ban_list[ip]['name'].replace("&", "&").replace("<", "<").replace(""", '"').replace(">", ">"))) + el.set('ip', str(self.ban_list[ip]['ip'])) + etreeEl.append(el) file = open(self.userPath + self.banFile ,"w") - file.write("".join(data)) + file.write(tostring(etreeEl)) file.close() except Exception, e: self.log_msg("Exception in saveBanList() " + str(e)) # This method reads in the server's configuration file and reconfigs the server # as needed, over-riding any default values as requested. + def initServerConfig(self): + debug(log=False) self.log_msg("Processing Server Configuration File... " + self.userPath) # make sure the server_ini.xml exists! validate.config_file( "server_ini.xml", "default_server_ini.xml" ) # try to use it. try: - self.configDom = minidom.parse(self.userPath + 'server_ini.xml') - self.configDom.normalize() - self.configDoc = self.configDom.documentElement - + self.configDom = parse(self.userPath + 'server_ini.xml') + #self.configDom.normalize() + self.configDoc = self.configDom.getroot() if hasattr(self, 'bootPassword'): self.boot_pwd = self.bootPassword - else: - if self.configDoc.hasAttribute("admin"): self.boot_pwd = self.configDoc.getAttribute("admin") - elif self.configDoc.hasAttribute("boot"): self.boot_pwd = self.configDoc.getAttribute("boot") - + if self.configDoc.get("admin"): self.boot_pwd = self.configDoc.get("admin") + elif self.configDoc.get("boot"): self.boot_pwd = self.configDoc.get("boot") if len(self.boot_pwd) < 1: self.boot_pwd = raw_input("Enter admin password: ") - - if not hasattr(self, 'reg') and self.configDoc.hasAttribute("register"): - self.reg = self.configDoc.getAttribute("register") + if not hasattr(self, 'reg') and self.configDoc.get("register"): + self.reg = self.configDoc.get("register") if not len(self.reg) > 0 or self.reg[0].upper() not in ("Y", "N"): opt = raw_input("Do you want to post your server to the OpenRPG Meta Server list? (y,n) ") if len(opt) and (opt[0].upper() == 'Y'): self.reg = 'Y' else: self.reg = 'N' LobbyName = 'Lobby' - if self.configDoc.hasAttribute("lobbyname"): LobbyName = self.configDoc.getAttribute("lobbyname") - map_node = service_node = self.configDoc.getElementsByTagName("map")[0] - msg_node = service_node = self.configDoc.getElementsByTagName("message")[0] - mapFile = map_node.getAttribute('file') - msgFile = msg_node.getAttribute('file') + if self.configDoc.get("lobbyname"): LobbyName = self.configDoc.get("lobbyname") + map_node = service_node = self.configDoc.findall("map")[0] + msg_node = service_node = self.configDoc.findall("message")[0] + mapFile = map_node.get('file') + msgFile = msg_node.get('file') if mapFile == '': mapFile = 'Lobby_map.xml' if msgFile == '': msgFile = 'LobbyMessage.html' # Update the lobby with the passwords if they've been specified @@ -392,15 +393,15 @@ } # set ip or dns name to send to meta server - service_node = self.configDoc.getElementsByTagName("service")[0] - address = service_node.getAttribute("address") + service_node = self.configDoc.findall("service")[0] + address = service_node.get("address") address = address.lower() if address == "" or address == "hostname/address" or address == "localhost": self.server_address = None else: self.server_address = address self.server_port = OPENRPG_PORT - if service_node.hasAttribute("port"): self.server_port = int(service_node.getAttribute("port")) - if self.configDoc.hasAttribute("name") and len(self.configDoc.getAttribute("name")) > 0 : - self.name = self.configDoc.getAttribute("name") + if service_node.get("port"): self.server_port = int(service_node.get("port")) + if self.configDoc.get("name") and len(self.configDoc.get("name")) > 0 : + self.name = self.configDoc.get("name") else: if self.reg[0].upper() == "Y": if self.name == None: self.name = raw_input("Server Name? ") @@ -414,8 +415,8 @@ """ try: - mver = self.configDoc.getElementsByTagName("version")[0] - self.minClientVersion = mver.getAttribute("min") + mver = self.configDoc.findall("version")[0] + self.minClientVersion = mver.get("min") except: self.minClientVersion = SERVER_MIN_CLIENT_VERSION #from orpg/orpg_version.py self.defaultMessageFile = "" # This try/except bit is to allow older versions of python to continue without a list error. @@ -431,13 +432,13 @@ """ try: - ak = self.configDoc.getElementsByTagName("autokick")[0] - if ak.hasAttribute("silent"): - if ((ak.getAttribute("silent")).lower() == "yes"): self.silent_auto_kick = 1 + ak = self.configDoc.findall("autokick")[0] + if ak.get("silent"): + if ((ak.get("silent")).lower() == "yes"): self.silent_auto_kick = 1 else: self.silent_auto_kick = 0 - if ak.hasAttribute("delay"): + if ak.get("delay"): try: - delay = int(ak.getAttribute("delay")) + delay = int(ak.get("delay")) self.zombie_time = delay except: #delay value cannot be converted into an int use defaut @@ -474,28 +475,28 @@ #pull information from config file DOM try: - roomdefaults = self.configDom.getElementsByTagName("room_defaults")[0] + roomdefaults = self.configDom.findall("room_defaults")[0] #rd.normalize() #roomdefaults = self.rd.documentElement try: - setting = roomdefaults.getElementsByTagName('passwords')[0] - rpw = setting.getAttribute('allow') + setting = roomdefaults.findall('passwords')[0] + rpw = setting.get('allow') if rpw == "no" or rpw == "0": roomdefault_pass = 0 self.log_msg("Room Defaults: Disallowing Passworded Rooms") else: self.log_msg("Room Defaults: Allowing Passworded Rooms") except: self.log_msg("Room Defaults: [Warning] Allowing Passworded Rooms") try: - setting = roomdefaults.getElementsByTagName('map')[0] - map = setting.getAttribute('file') + setting = roomdefaults.findall('map')[0] + map = setting.get('file') if map != "": roomdefault_map = self.userPath + map.replace("myfiles/", "") self.log_msg("Room Defaults: Using " + str(map) + " for room map") except: self.log_msg("Room Defaults: [Warning] Using Default Map") try: - setting = roomdefaults.getElementsByTagName('message')[0] - msg = setting.getAttribute('file') + setting = roomdefaults.findall('message')[0] + msg = setting.get('file') if msg != "": if msg[:4].lower() == 'http': roomdefault_msg = msg else: roomdefault_msg = self.userPath + msg.replace("myfiles/", "") @@ -520,16 +521,16 @@ ###Server Cheat message try: - cheat_node = self.configDoc.getElementsByTagName("cheat")[0] - self.cheat_msg = cheat_node.getAttribute("text") + cheat_node = self.configDoc.findall("cheat")[0] + self.cheat_msg = cheat_node.get("text") except: self.cheat_msg = "**FAKE ROLL**" self.log_msg("**WARNING** tag missing from server configuration file. Using empty string.") # should validate protocal - validate_protocol_node = self.configDom.getElementsByTagName("validate_protocol ") + validate_protocol_node = self.configDom.findall("validate_protocol") self.validate_protocol = 1 - if(validate_protocol_node): self.validate_protocol = (validate_protocol_node[0].getAttribute("value") == "True") + if(validate_protocol_node): self.validate_protocol = (validate_protocol_node[0].get("value") == "True") if(self.validate_protocol != 1): self.log_msg("Protocol Validation: OFF") self.makePersistentRooms() self.log_msg("Server Configuration File: Processing Completed.") @@ -539,28 +540,28 @@ def makePersistentRooms(self): 'Creates rooms on the server as defined in the server config file.' - for element in self.configDom.getElementsByTagName('room'): - roomName = element.getAttribute('name') - roomPassword = element.getAttribute('password') - bootPassword = element.getAttribute('boot') + for element in self.configDom.findall('room'): + roomName = element.get('name') + roomPassword = element.get('password') + bootPassword = element.get('boot') # Conditionally check for minVersion attribute - if element.hasAttribute('minVersion'): minVersion = element.getAttribute('minVersion') + if element.get('minVersion'): minVersion = element.get('minVersion') else: minVersion = "" # Extract the map filename attribute from the map node # we only care about the first map element found -- others are ignored - mapElement = element.getElementsByTagName('map')[0] - mapFile = self.userPath + mapElement.getAttribute('file').replace("myfiles/", "") - messageElement = element.getElementsByTagName('message')[0] - messageFile = messageElement.getAttribute('file') + mapElement = element.findall('map')[0] + mapFile = self.userPath + mapElement.get('file').replace("myfiles/", "") + messageElement = element.findall('message')[0] + messageFile = messageElement.get('file') if messageFile[:4] != 'http': messageFile = self.userPath + messageFile.replace("myfiles/", "") # Make sure we have a message to even mess with if(len(messageFile) == 0): messageFile = self.defaultMessageFile if(len(mapFile) == 0): mapFile = self.defaultMapFile moderated = 0 - if element.hasAttribute('moderated') and element.getAttribute('moderated').lower() == "true": moderated = 1 + if element.get('moderated') and element.get('moderated').lower() == "true": moderated = 1 #create the new persistant group self.new_group(roomName, roomPassword, @@ -620,7 +621,7 @@ elif self.log_network_messages == 2: return "Network Traffic Log: Logging (inbound/outbound files)" else: self.log_msg("Network Traffic Log: [Unknown]") - def register_callback(instance, xml_dom = None,source=None): + def register_callback(instance, xml_dom = None, source=None): if xml_dom: # if we get something if source == getMetaServerBaseURL(): # if the source of this DOM is the authoritative meta try: @@ -666,7 +667,7 @@ id, cookie = self.metas[meta].getIdAndCookie() data = urllib.urlencode( {"room_data[server_id]":id, "act":'registerrooms'}) - get_server_dom(data+'&'+rooms, self.metas[meta].path) + get_server_dom(data+'&'+rooms, self.metas[meta].path, string=True) def register(self,name_given=None): if name_given == None: name = self.name @@ -757,21 +758,21 @@ data = ServerPlugins.preParseOutgoing() for msg in data: try: - xml_dom = parseXml(msg) - xml_dom = xml_dom._get_documentElement() - if xml_dom.hasAttribute('from') and int(xml_dom.getAttribute('from')) > -1: - xml_dom.setAttribute('from', '-1') - xml_dom.setAttribute('to', 'all') + #xml_dom = parseXml(msg) + xml_dom = fromstring(msg).getroot() + if xml_dom.get('from') and int(xml_dom.get('from')) > -1: + xml_dom.set('from', '-1') + xml_dom.set('to', 'all') self.incoming_msg_handler(xml_dom, msg) - xml_dom.unlink() + #xml_dom.unlink() except: pass self.p_lock.release() time.sleep(0.250) def sendMsg( self, sock, msg, useCompression=False, cmpType=None): - """Very simple function that will properly encode and send a message to te + """Very simple function that will properly encode and send a message to the remote on the specified socket.""" - if useCompression and cmpType != None: + if not useCompression and cmpType != None: mpacket = cmpType.compress(msg) lpacket = pack('!i', len(mpacket)) sock.send(lpacket) @@ -859,6 +860,7 @@ self.log_msg("Server stopped!") def log_msg(self,msg): + debug(parents=True) if self.log_to_console: if self.log_console: self.log_console(msg) else: print str(msg) @@ -899,7 +901,7 @@ print def broadcast(self,msg): - self.send_to_all("0","" + msg + "") + self.send_to_all("0","" + msg + "") def console_log(self): if self.log_to_console == 1: @@ -959,6 +961,10 @@ elif self.players[id].protocol_version.find(patern)>-1: self.print_player_info(self.players[id]) elif self.players[id].client_string.find(patern)>-1: self.print_player_info(self.players[id]) + def obtain_by_id(self, id, objects): + ### Alpha ### Basic way to obtain information for the Server GUI, currently reports the Client String only + return self.players[id].client_string + def print_player_info(self,player): print player.id, player.name, player.ip, player.group_id, player.role, player.version, player.protocol_version, player.client_string @@ -1055,13 +1061,13 @@ self.log_msg(str(e)) self.p_lock.release() - def update_request(self,newsock,xml_dom): + def update_request(self,newsock, xml_dom): # handle reconnects self.log_msg( "update_request() has been called." ) # get player id - id = xml_dom.getAttribute("id") - group_id = xml_dom.getAttribute("group_id") + id = xml_dom.get("id") + group_id = xml_dom.get("group_id") self.p_lock.acquire() if self.players.has_key(id): self.sendMsg(newsock, self.players[id].toxml("update"), @@ -1072,33 +1078,31 @@ need_new = 0 else: need_new = 1 self.p_lock.release() - if need_new: self.new_request(newsock,xml_dom) + if need_new: self.new_request(newsock, xml_dom) else: msg = self.groups[group_id].game_map.get_all_xml() self.send(msg,id,group_id) - def new_request(self,newsock,xml_dom,LOBBY_ID='0'): + def new_request(self,newsock, xml_dom, LOBBY_ID='0'): #build client stub props = {} # Don't trust what the client tells us...trust what they connected as! props['ip'] = socket.gethostbyname( newsock.getpeername()[0] ) - try: props['role'] = xml_dom.getAttribute("role") - except: props['role'] = "GM" - - props['name'] = xml_dom.getAttribute("name") + props['role'] = xml_dom.get("role") or 'GM' + props['name'] = xml_dom.get("name") props['group_id'] = LOBBY_ID props['id'] = str(self.next_player_id) - props['version'] = xml_dom.getAttribute("version") - props['protocol_version'] = xml_dom.getAttribute("protocol_version") - props['client_string'] = xml_dom.getAttribute("client_string") + props['version'] = xml_dom.get("version") + props['protocol_version'] = xml_dom.get("protocol_version") + props['client_string'] = xml_dom.get("client_string") self.next_player_id += 1 - new_stub = client_stub(self.incoming,newsock,props,self.log_console) - if xml_dom.hasAttribute('useCompression'): + new_stub = client_stub(self.incoming, newsock, props, self.log_console) + if xml_dom.get('useCompression'): new_stub.useCompression = True - if xml_dom.hasAttribute('cmpType'): - cmpType = xml_dom.getAttribute('cmpType') + if xml_dom.get('cmpType'): + cmpType = xml_dom.get('cmpType') if cmpBZ2 and cmpType == 'bz2': new_stub.compressionType = bz2 elif cmpZLIB and cmpType == 'zlib': new_stub.compressionType = zlib else: new_stub.compressionType = None @@ -1110,14 +1114,14 @@ self.sendMsg(newsock, new_stub.toxml("new"), False, None) # try to remove circular refs - if xml_dom: - xml_dom.unlink() + #if xml_dom: + # xml_dom.unlink() # send confirmation data = self.recvMsg(newsock, new_stub.useCompression, new_stub.compressionType) try: - xml_dom = parseXml(data) - xml_dom = xml_dom._get_documentElement() + xml_dom = XML(data) + #xml_dom = xml_dom._get_documentElement() except Exception, e: print e (remote_host,remote_port) = newsock.getpeername() @@ -1132,7 +1136,7 @@ print "Error in parse found from " + str(remote_host) + ". Disconnected." print " Offending data(" + str(len(data)) + "bytes)=" + data print "Exception=" + str(e) - if xml_dom: xml_dom.unlink() + #if xml_dom: xml_dom.unlink() return #start threads and store player @@ -1161,8 +1165,7 @@ time.sleep(1) self.log_msg("Connection terminating due to version incompatibility with client (ver: " + props['version'] + " protocol: " + props['protocol_version'] + ")" ) newsock.close() - if xml_dom: - xml_dom.unlink() + #if xml_dom: xml_dom.unlink() return None ip = props['ip'] @@ -1175,8 +1178,7 @@ # Give messages time to flow time.sleep(1) newsock.close() - if xml_dom: - xml_dom.unlink() + #if xml_dom: xml_dom.unlink() return None """ @@ -1194,7 +1196,7 @@ """ try: - if xml_dom.getAttribute("id") == props['id']: + if xml_dom.get("id") == props['id']: new_stub.initialize_threads() self.p_lock.acquire() self.players[props['id']] = new_stub @@ -1203,12 +1205,12 @@ self.send_player_list(props['id'],LOBBY_ID) self.p_lock.release() msg = self.groups[LOBBY_ID].game_map.get_all_xml() - self.send(msg,props['id'],LOBBY_ID) - self.send_to_group(props['id'],LOBBY_ID,self.players[props['id']].toxml('new')) - self.return_room_roles(props['id'],LOBBY_ID) + self.send(msg, props['id'], LOBBY_ID) + self.send_to_group(props['id'], LOBBY_ID, self.players[props['id']].toxml('new')) + self.return_room_roles(props['id'], LOBBY_ID) # Re-initialize the role for this player incase they came from a different server - self.handle_role("set",props['id'], "GM",self.groups[LOBBY_ID].boot_pwd, LOBBY_ID) + self.handle_role("set", props['id'], "GM", self.groups[LOBBY_ID].boot_pwd, LOBBY_ID) cmsg = "Client Connect: (" + str(props['id']) + ") " + str(props['name']) + " [" + str(props['ip']) + "]" self.log_msg(cmsg) cmsg = ("connect", props) ################################################# @@ -1223,7 +1225,7 @@ #something didn't go right. Notify client and drop the connection err_string = "
" - err_string += "
The server has encountered an error while processing your connection request.
" + err_string += "
The server has encountered an error while processing your connection request.
" err_string += "
You are being disconnected from the server.
" err_string += "This error may represent a problem with the server. If you continue to get this message " err_string += "please contact the servers administrator to correct the issue.
" @@ -1233,8 +1235,7 @@ # Display the lobby message self.SendLobbyMessage(newsock,props['id']) - if xml_dom: - xml_dom.unlink() + #if xml_dom: xml_dom.unlink() def checkClientVersion(self, clientversion): minv = self.minClientVersion.split('.') @@ -1258,10 +1259,8 @@ lobbyMsg += "server, version '" + VERSION + "'" # See if we have a server name to report! - if len(self.serverName): - lobbyMsg += ", named '" + self.serverName + "'." - else: - lobbyMsg += "." + if len(self.serverName): lobbyMsg += ", named '" + self.serverName + "'." + else: lobbyMsg += "." # Add extra line spacing lobbyMsg += "\n\n" @@ -1343,8 +1342,7 @@ except: pass return #returning causes connection thread instance to terminate # Clear out the xml_dom in preparation for new stuff, if necessary - try: - if xml_dom: xml_dom.unlink() + """try: if xml_dom: xml_dom.unlink() except: self.log_msg( "The following exception caught unlinking xml_dom:") @@ -1352,10 +1350,10 @@ try: newsock.close() except: pass return #returning causes connection thread instance to terminate - # Parse the XML received from the connecting client + # Parse the XML received from the connecting client""" try: - xml_dom = parseXml(data) - xml_dom = xml_dom._get_documentElement() + xml_dom = XML(data) + #xml_dom = xml_dom._get_documentElement() except: try: newsock.close() @@ -1369,11 +1367,11 @@ # Determine the correct action and execute it try: # get action - action = xml_dom.getAttribute("action") + action = xml_dom.get("action") # Figure out what type of connection we have going on now - if action == "new": self.new_request(newsock,xml_dom) - elif action == "update": self.update_request(newsock,xml_dom) + if action == "new": self.new_request(newsock, xml_dom) + elif action == "update": self.update_request(newsock, xml_dom) else: self.log_msg("Unknown Join Request!") except Exception, e: @@ -1383,12 +1381,12 @@ return #returning causes connection thread instance to terminate # Again attempt to clean out DOM stuff - try: - if xml_dom: xml_dom.unlink() + """ + try: if xml_dom: xml_dom.unlink() except: print "The following exception caught unlinking xml_dom:" traceback.print_exc() - return #returning causes connection thread instance to terminate + return #returning causes connection thread instance to terminate""" """ #======================================================== @@ -1402,7 +1400,7 @@ # and player load capacity -- Snowdog 3/04 """ - def message_handler(self,arg): + def message_handler(self, arg): xml_dom = None self.log_msg( "message handler thread running..." ) while self.alive: @@ -1422,40 +1420,40 @@ data = None except Exception, e: self.log_msg(str(e)) - if xml_dom: xml_dom.unlink() - if xml_dom: xml_dom.unlink() + #if xml_dom: xml_dom.unlink() + #if xml_dom: xml_dom.unlink() self.log_msg("message handler thread exiting...") self.incoming_event.set() - def parse_incoming_dom(self,data): + def parse_incoming_dom(self, data): + #debug((data, tostring(data) if iselement(data) else 'None element')) Sometimes I catch an error. end = data.find(">") #locate end of first element of message head = data[:end+1] #self.log_msg(head) xml_dom = None try: - xml_dom = parseXml(head) - xml_dom = xml_dom._get_documentElement() - self.message_action(xml_dom,data) + xml_dom = XML(head) + #xml_dom = xml_dom._get_documentElement() + self.message_action(xml_dom, data) except Exception, e: print "Error in parse of inbound message. Ignoring message." print " Offending data(" + str(len(data)) + "bytes)=" + data print "Exception=" + str(e) - if xml_dom: xml_dom.unlink() - + def message_action(self, xml_dom, data): - tag_name = xml_dom._get_tagName() - if self.svrcmds.has_key(tag_name): self.svrcmds[tag_name]['function'](xml_dom,data) + tag_name = xml_dom.tag + if self.svrcmds.has_key(tag_name): self.svrcmds[tag_name]['function'](xml_dom, data) else: raise Exception, "Not a valid header!" #Message Action thread expires and closes here. return def do_alter(self, xml_dom, data): - target = xml_dom.getAttribute("key") - value = xml_dom.getAttribute("val") - player = xml_dom.getAttribute("plr") - group_id = xml_dom.getAttribute("gid") - boot_pwd = xml_dom.getAttribute("bpw") + target = xml_dom.get("key") or 'None' + value = xml_dom.get("val") or 'None' + player = xml_dom.get("plr") or 'None' + group_id = xml_dom.get("gid") or 'None' + boot_pwd = xml_dom.get("bpw") or 'None' actual_boot_pwd = self.groups[group_id].boot_pwd if self.allow_room_passwords == 0: @@ -1474,7 +1472,7 @@ elif target == "name": # Check for & in name. We want to allow this because of its common # use in d&d games - result = self.change_group_name(group_id,value,player) + result = self.change_group_name(group_id, value, player) msg ="" + result self.players[player].outbox.put(msg) else: @@ -1484,39 +1482,39 @@ def do_role(self, xml_dom, data): role = "" boot_pwd = "" - act = xml_dom.getAttribute("action") - player = xml_dom.getAttribute("player") - group_id = xml_dom.getAttribute("group_id") + act = xml_dom.get("action") + player = xml_dom.get("player") + group_id = xml_dom.get("group_id") if act == "set": - role = xml_dom.getAttribute("role") - boot_pwd = xml_dom.getAttribute("boot_pwd") - xml_dom.unlink() + role = xml_dom.get("role") + boot_pwd = xml_dom.get("boot_pwd") + #xml_dom.unlink() if group_id != "0": self.handle_role(act, player, role, boot_pwd, group_id) self.log_msg(("role", (player, role))) def do_ping(self, xml_dom, data): - player = xml_dom.getAttribute("player") - group_id = xml_dom.getAttribute("group_id") + player = xml_dom.get("player") + group_id = xml_dom.get("group_id") sent_time = "" msg = "" - try: sent_time = xml_dom.getAttribute("time") + try: sent_time = xml_dom.get("time") except: pass if sent_time != "": msg ="" #because a time was sent return a ping response else: msg ="" msg += "PONG!?!" self.players[player].outbox.put(msg) - xml_dom.unlink() + #xml_dom.unlink() def do_system(self, xml_dom, data): pass def moderate_group(self,xml_dom,data): try: - action = xml_dom.getAttribute("action") - from_id = xml_dom.getAttribute("from") - if xml_dom.hasAttribute("pwd"): pwd=xml_dom.getAttribute("pwd") + action = xml_dom.get("action") + from_id = xml_dom.get("from") + if xml_dom.get("pwd"): pwd=xml_dom.get("pwd") else: pwd="" group_id=self.players[from_id].group_id if action == "list": @@ -1542,10 +1540,10 @@ self.groups[group_id].moderated = 0 self.players[from_id].self_message("This channel is now unmoderated") if action == 'addvoice': - users = xml_dom.getAttribute("users").split(',') + users = xml_dom.get("users").split(',') for i in users: self.groups[group_id].voice[i.strip()]=1 if action == 'delvoice': - users = xml_dom.getAttribute("users").split(',') + users = xml_dom.get("users").split(',') for i in users: if self.groups[group_id].voice.has_key(i.strip()): del self.groups[group_id].voice[i.strip()] else: @@ -1553,11 +1551,11 @@ except Exception,e: self.log_msg(str(e)) - def join_group(self,xml_dom,data): + def join_group(self, xml_dom, data): try: - from_id = xml_dom.getAttribute("from") - pwd = xml_dom.getAttribute("pwd") - group_id = xml_dom.getAttribute("group_id") + from_id = xml_dom.get("from") + pwd = xml_dom.get("pwd") + group_id = xml_dom.get("group_id") ver = self.players[from_id].version allowed = 1 @@ -1567,9 +1565,8 @@ if not self.groups[group_id].check_pwd(pwd): allowed = 0 - #tell the clients password manager the password failed -- SD 8/03 - pm = "" + pm = "" self.players[from_id].outbox.put(pm) msg = 'failed - incorrect room password' @@ -1578,7 +1575,7 @@ #the following line makes sure that their role is reset to normal, #since it is briefly set to lurker when they even TRY to change #rooms - msg = "" + msg = "" self.players[from_id].outbox.put(msg) return @@ -1599,17 +1596,15 @@ "move a player from one group to another" try: try: - if group_id == "0": - self.players[from_id].role = "GM" - else: - self.players[from_id].role = "Lurker" + if group_id == "0": self.players[from_id].role = "GM" + else: self.players[from_id].role = "Lurker" except Exception, e: print "exception in move_player() " traceback.print_exc() - old_group_id = self.players[from_id].change_group(group_id,self.groups) - self.send_to_group(from_id,old_group_id,self.players[from_id].toxml('del')) - self.send_to_group(from_id,group_id,self.players[from_id].toxml('new')) + old_group_id = self.players[from_id].change_group(group_id, self.groups) + self.send_to_group(from_id, old_group_id, self.players[from_id].toxml('del')) + self.send_to_group(from_id, group_id, self.players[from_id].toxml('new')) self.check_group(from_id, old_group_id) """ @@ -1650,10 +1645,11 @@ self.log_msg(str(e)) thread.start_new_thread(self.registerRooms,(0,)) - def return_room_roles(self,from_id,group_id): + def return_room_roles(self, from_id, group_id): for m in self.players.keys(): if self.players[m].group_id == group_id: - msg = "" + try: msg = "" + except: exit() self.players[from_id].outbox.put(msg) """ @@ -1664,7 +1660,7 @@ # conditions written all over them. Ack! Ack! """ def new_group( self, name, pwd, boot, minVersion, mapFile, messageFile, persist = 0, moderated=0 ): - group_id = str( self.next_group_id ) + group_id = str(self.next_group_id) self.next_group_id += 1 self.groups[group_id] = game_group( group_id, name, pwd, "", boot, minVersion, mapFile, messageFile, persist ) self.groups[group_id].moderated = moderated @@ -1672,9 +1668,9 @@ if persist !=0: ins="Persistant " lmsg = "Creating " + ins + "Group... (" + str(group_id) + ") " + str(name) self.log_msg( lmsg ) - self.log_msg(("create_group", (str(name), int(group_id), 0) )) ##-99 works, could be better. + self.log_msg(("create_group", (str(name), int(group_id), pwd, 0) )) - def change_group_name(self,gid,name,pid): + def change_group_name(self, gid, name, pid): "Change the name of a group" # Check for & in name. We want to allow this because of its common # use in d&d games. @@ -1710,83 +1706,86 @@ self.groups[gid].name = str(name) lmessage = "Room name changed to from \"" + oldroomname + "\" to \"" + name + "\"" self.log_msg(lmessage + " by " + str(pid) ) + self.log_msg(("update_group", (str(name), group_id, False))) + self.send_to_all('0',self.groups[gid].toxml('update')) return lmessage except: return "An error occured during rename of room!" thread.start_new_thread(self.registerRooms,(0,)) - def create_group(self,xml_dom,data): - try: - from_id = xml_dom.getAttribute("from") - pwd = xml_dom.getAttribute("pwd") - name = xml_dom.getAttribute("name") - boot_pwd = xml_dom.getAttribute("boot_pwd") - minVersion = xml_dom.getAttribute("min_version") - #added var reassign -- akoman - messageFile = self.defaultMessageFile + def create_group(self, xml_dom, data): + #try: + from_id = xml_dom.get("from") + pwd = xml_dom.get("pwd") + name = xml_dom.get("name") + boot_pwd = xml_dom.get("boot_pwd") + minVersion = xml_dom.get("min_version") + #added var reassign -- akoman + messageFile = self.defaultMessageFile - # see if passwords are allowed on this server and null password if not - if self.allow_room_passwords != 1: pwd = "" + # see if passwords are allowed on this server and null password if not + if self.allow_room_passwords != 1: pwd = "" - # Check for & in name. We want to allow this because of its common - # use in d&d games. + # Check for & in name. We want to allow this because of its common + # use in d&d games. - loc = name.find("&") - oldloc = 0 - while loc > -1: - loc = name.find("&",oldloc) - if loc > -1: - b = name[:loc] - e = name[loc+1:] - name = b + "&" + e - oldloc = loc+1 - loc = name.find("'") - oldloc = 0 - while loc > -1: - loc = name.find("'",oldloc) - if loc > -1: - b = name[:loc] - e = name[loc+1:] - name = b + "'" + e - oldloc = loc+1 - loc = name.find('"') - oldloc = 0 - while loc > -1: - loc = name.find('"',oldloc) - if loc > -1: - b = name[:loc] - e = name[loc+1:] - name = b + """ + e - oldloc = loc+1 - group_id = str(self.next_group_id) - self.next_group_id += 1 - self.groups[group_id] = game_group(group_id,name,pwd,"",boot_pwd, minVersion, None, messageFile ) - self.groups[group_id].voice[from_id]=1 - self.players[from_id].outbox.put(self.groups[group_id].toxml('new')) - old_group_id = self.players[from_id].change_group(group_id,self.groups) - self.send_to_group(from_id,old_group_id,self.players[from_id].toxml('del')) - self.check_group(from_id, old_group_id) - self.send_to_all(from_id,self.groups[group_id].toxml('new')) - self.send_to_all('0',self.groups[group_id].toxml('update')) - self.handle_role("set",from_id,"GM",boot_pwd, group_id) - lmsg = "Creating Group... (" + str(group_id) + ") " + str(name) - self.log_msg( lmsg ) - jmsg = "moving to room " + str(group_id) + "." - self.log_msg( jmsg ) - self.log_msg(("create_group", (str(name), group_id, from_id))) - #even creators of the room should see the HTML --akoman - #edit: jan10/03 - was placed in the except statement. Silly me. - if self.defaultMessageFile != None: - if self.defaultMessageFile[:4] == 'http': - data = urllib.urlretrieve(self.defaultMessageFile) - open_msg = open(data[0]) - urllib.urlcleanup() - else: open_msg = open( self.defaultMessageFile, "r" ) - roomMsg = open_msg.read() - open_msg.close() - # Send the rooms message to the client no matter what - self.players[from_id].outbox.put( "" + roomMsg ) - except Exception, e: self.log_msg( "Exception: create_group(): " + str(e)) + loc = name.find("&") + oldloc = 0 + while loc > -1: + loc = name.find("&",oldloc) + if loc > -1: + b = name[:loc] + e = name[loc+1:] + name = b + "&" + e + oldloc = loc+1 + loc = name.find("'") + oldloc = 0 + while loc > -1: + loc = name.find("'",oldloc) + if loc > -1: + b = name[:loc] + e = name[loc+1:] + name = b + "'" + e + oldloc = loc+1 + loc = name.find('"') + oldloc = 0 + while loc > -1: + loc = name.find('"',oldloc) + if loc > -1: + b = name[:loc] + e = name[loc+1:] + name = b + """ + e + oldloc = loc+1 + group_id = str(self.next_group_id) + self.next_group_id += 1 + + self.groups[group_id] = game_group(group_id, name, pwd, "", boot_pwd, minVersion, None, messageFile) + self.groups[group_id].voice[from_id]=1 + self.players[from_id].outbox.put(self.groups[group_id].toxml('new')) + old_group_id = self.players[from_id].change_group(group_id, self.groups) + self.send_to_group(from_id, old_group_id, self.players[from_id].toxml('del')) + self.check_group(from_id, old_group_id) + self.send_to_all(from_id,self.groups[group_id].toxml('new')) + self.send_to_all('0',self.groups[group_id].toxml('update')) + self.handle_role("set",from_id,"GM", boot_pwd, group_id) + lmsg = "Creating Group... (" + str(group_id) + ") " + str(name) + self.log_msg( lmsg ) + jmsg = "moving to room " + str(group_id) + "." + self.log_msg( jmsg ) + self.log_msg(("create_group", (str(name), group_id, from_id, 'No' if pwd == '' else 'Yes'))) + #even creators of the room should see the HTML --akoman + #edit: jan10/03 - was placed in the except statement. Silly me. + if self.defaultMessageFile != None: + if self.defaultMessageFile[:4] == 'http': + data = urllib.urlretrieve(self.defaultMessageFile) + open_msg = open(data[0]) + urllib.urlcleanup() + else: open_msg = open( self.defaultMessageFile, "r" ) + roomMsg = open_msg.read() + open_msg.close() + # Send the rooms message to the client no matter what + self.players[from_id].outbox.put( "" + roomMsg ) + #except Exception, e: self.log_msg( "Exception: create_group(): " + str(e)) thread.start_new_thread(self.registerRooms,(0,)) def check_group(self, from_id, group_id): @@ -1800,13 +1799,11 @@ del self.groups[group_id] self.log_msg(("delete_group", (group_id, from_id))) else: self.send_to_all("0",self.groups[group_id].toxml('update')) - #The register Rooms thread thread.start_new_thread(self.registerRooms,(0,)) - except Exception, e: self.log_msg(str(e)) - def del_player(self,id,group_id): + def del_player(self, id, group_id): try: dmsg = "Client Disconnect: (" + str(id) + ") " + str(self.players[id].name) self.players[id].disconnect() @@ -1825,10 +1822,10 @@ except Exception, e: self.log_msg(str(e)) self.log_msg("Explicit garbage collection shows %s undeletable items." % str(gc.collect())) - def incoming_player_handler(self,xml_dom,data): - id = xml_dom.getAttribute("id") - act = xml_dom.getAttribute("action") - #group_id = xml_dom.getAttribute("group_id") + def incoming_player_handler(self, xml_dom, data): + id = xml_dom.get("id") + act = xml_dom.get("action") + #group_id = xml_dom.get("group_id") group_id = self.players[id].group_id ip = self.players[id].ip self.log_msg("Player with IP: " + str(ip) + " joined.") @@ -1840,21 +1837,20 @@ self.send_group_list(id) except Exception, e: traceback.print_exc() elif act=="del": - #print "del player" self.del_player(id,group_id) self.check_group(id, group_id) elif act=="update": self.players[id].take_dom(xml_dom) self.log_msg(("update", {"id": id, - "name": xml_dom.getAttribute("name"), - "status": xml_dom.getAttribute("status"), - "role": xml_dom.getAttribute("role"), + "name": xml_dom.get("name"), + "status": xml_dom.get("status"), + "role": xml_dom.get("role"), "ip": str(ip), - "group": xml_dom.getAttribute("group_id"), - "room": xml_dom.getAttribute("name"), - "boot": xml_dom.getAttribute("rm_boot"), - "version": xml_dom.getAttribute("version"), - "ping": xml_dom.getAttribute("time") \ + "group": xml_dom.get("group_id"), + "room": xml_dom.get("name"), + "boot": xml_dom.get("rm_boot"), + "version": xml_dom.get("version"), + "ping": xml_dom.get("time") \ })) def strip_cheat_roll(self, string): @@ -1877,9 +1873,10 @@ def incoming_msg_handler(self,xml_dom,data): xml_dom, data = ServerPlugins.preParseIncoming(xml_dom, data) - to_id = xml_dom.getAttribute("to") - from_id = xml_dom.getAttribute("from") - group_id = xml_dom.getAttribute("group_id") + ########################################################### + to_id = xml_dom.get("to") + from_id = xml_dom.get("from") + group_id = xml_dom.get("group_id") end = data.find(">") msg = data[end+1:] @@ -1924,14 +1921,14 @@ return def sound_msg_handler(self, xml_dom, data): - from_id = xml_dom.getAttribute("from") - group_id = xml_dom.getAttribute("group_id") + from_id = xml_dom.get("from") + group_id = xml_dom.get("group_id") if group_id != 0: self.send_to_group(from_id, group_id, data) def plugin_msg_handler(self,xml_dom,data): - to_id = xml_dom.getAttribute("to") - from_id = xml_dom.getAttribute("from") - group_id = xml_dom.getAttribute("group_id") + to_id = xml_dom.get("to") + from_id = xml_dom.get("from") + group_id = xml_dom.get("group_id") end = data.find(">") msg = data[end+1:] @@ -1983,9 +1980,9 @@ try: given_boot_pwd = None try: - xml_dom = parseXml(msg) - xml_dom = xml_dom._get_documentElement() - given_boot_pwd = xml_dom.getAttribute("boot_pwd") + xml_dom = XML(msg) + #xml_dom = xml_dom._get_documentElement() + given_boot_pwd = xml_dom.get("boot_pwd") except: print "Error in parse of boot message, Ignoring." @@ -2083,14 +2080,14 @@ ### Alpha ### Addition added to assist in Un Banning users. def admin_build_banlist(self): validate.config_file("ban_list.xml", "default_ban_list.xml" ) - configDom = minidom.parse(dir_struct["user"] + 'ban_list.xml') + configDom = parse(dir_struct["user"] + 'ban_list.xml') self.ban_list = {} - for element in configDom.getElementsByTagName('banned'): - player = element.getAttribute('name').replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">") - ip = element.getAttribute('ip') + for element in configDom.findall('banned'): + player = element.get('name').replace("&", "&").replace("<", "<").replace('"', """).replace(">", ">") + ip = element.get('ip') self.ban_list[ip] = {} self.ban_list[ip]['ip'] = ip - self.ban_list[ip]['name'] = element.getAttribute('name') + self.ban_list[ip]['name'] = element.get('name') ################ def admin_banip(self, ip, name="", silent = 0): @@ -2190,7 +2187,6 @@ def send_to_all(self,from_id,data): try: - print data self.p_lock.acquire() keys = self.players.keys() self.p_lock.release() @@ -2226,8 +2222,10 @@ def send_group_list(self, to_id, action="new"): try: + print self.groups for key in self.groups: xml = self.groups[key].toxml(action) + print xml, key self.players[to_id].outbox.put(xml) except Exception, e: self.log_msg("Exception: send_group_list(): (client #"+to_id+") : " + str(e)) @@ -2284,10 +2282,10 @@ """ if not self.allowRemoteAdmin: return try: - pid = xml_dom.getAttribute("id") + pid = xml_dom.get("id") gid = "" - given_pwd = xml_dom.getAttribute("pwd") - cmd = xml_dom.getAttribute("cmd") + given_pwd = xml_dom.get("pwd") + cmd = xml_dom.get("cmd") server_admin_pwd = self.groups["0"].boot_pwd p_id = "" p_name= "" @@ -2326,17 +2324,24 @@ msg ="" + self.player_list_remote() self.players[pid].outbox.put(msg) elif cmd == "banip": - ip = xml_dom.getAttribute("bip") - name = xml_dom.getAttribute("bname") + ip = xml_dom.get("bip") + name = xml_dom.get("bname") msg = " Banned: " + str(ip) self.admin_banip(ip, name) elif cmd == "ban": - id = xml_dom.getAttribute("bid") + id = xml_dom.get("bid") msg = " Banned!" self.players[pid].outbox.put(msg) self.admin_ban(id, "") + ### Alpha ### and untested + elif cmd == "boot": + id = xml_dom.get("bid") + msg = " Booted!" + self.players[pid].outbox.put(msg) + self.admin_kick(id, "") + ############# elif cmd == "unban": - ip = xml_dom.getAttribute("ip") + ip = xml_dom.get("ip") self.admin_unban(ip) msg = " Unbaned: " + str(ip) self.players[pid].outbox.put(msg) @@ -2344,7 +2349,7 @@ msg = "" + self.admin_banlist() self.players[pid].outbox.put(msg) elif cmd == "killgroup": - ugid = xml_dom.getAttribute("gid") + ugid = xml_dom.get("gid") if ugid == "0": m = "" m += "Cannot Remove Lobby! Remote administrator request denied!" @@ -2355,15 +2360,15 @@ self.players[pid].outbox.put(msg) elif cmd == "message": - tuid = xml_dom.getAttribute("to_id") - msg = xml_dom.getAttribute("msg") + tuid = xml_dom.get("to_id") + msg = xml_dom.get("msg") pmsg = "" + msg try: self.players[tuid].outbox.put(pmsg) except: msg = "" + result self.players[pid].outbox.put(msg) elif cmd == "nameroom": - rm_id = xml_dom.getAttribute("rmid") - rm_name = xml_dom.getAttribute("name") + rm_id = xml_dom.get("rmid") + rm_name = xml_dom.get("name") result = self.change_group_name(rm_id,rm_name,pid) msg ="" + result self.players[pid].outbox.put(msg) elif cmd == "passwd": - tgid = xml_dom.getAttribute("gid") - npwd = xml_dom.getAttribute("pass") + tgid = xml_dom.get("gid") + npwd = xml_dom.get("pass") if tgid == "0": msg ="" msg += "Server password may not be changed remotely!" @@ -2533,30 +2538,32 @@ # in chat window on remote client """ def player_list_remote(self): - COLOR1 = "\"#004080\"" #header/footer background color - COLOR2 = "\"#DDDDDD\"" #group line background color - COLOR3 = "\"#FFFFFF\"" #player line background color - COLOR4 = "\"#FFFFFF\"" #header/footer text color - PCOLOR = "\"#000000\"" #Player text color - LCOLOR = "\"#404040\"" #Lurker text color - GCOLOR = "\"#FF0000\"" #GM text color - SIZE = "size=\"-1\"" #player info text size + """Does not work!!!""" # TaS. + COLOR1 = "'#004080'" #header/footer background color + COLOR2 = "'#DDDDDD'" #group line background color + COLOR3 = "'#FFFFFF'" #player line background color + COLOR4 = "'#FFFFFF'" #header/footer text color + PCOLOR = "'#000000'" #Player text color + LCOLOR = "'#404040'" #Lurker text color + GCOLOR = "'#FF0000'" #GM text color + SIZE = "size='-1'" #player info text size FG = PCOLOR "display a condensed list of players on the server" self.p_lock.acquire() - pl = "
" - pl += "" + pl = "
" - pl += "GROUP & PLAYER LIST
" + pl += "" try: keys = self.groups.keys() keys.sort(id_compare) for k in keys: - groupstring = "" - groupstring += "" - pl += groupstring + groupstring += "" + groupstring += "" + pl += groupstring; debug(groupstring) ids = self.groups[k].get_player_ids() ids.sort(id_compare) for id in ids: diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/networking/mplay_server_gui.py --- a/orpg/networking/mplay_server_gui.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/networking/mplay_server_gui.py Tue Nov 10 14:11:28 2009 -0600 @@ -9,20 +9,25 @@ __cvsinfo__='$Id: mplay_server_gui.py,v 1.26 2007/11/06 00:32:39 digitalxero Exp $'[5:-2] __doc__="""OpenRPG Server Graphical Interface""" -import os -import sys -import time -import types +import os, sys, time, types + from orpg.dirpath import dir_struct -#import orpg.systempath looks old from orpg.tools.validate import validate from orpg.orpg_wx import * +from threading import Thread + import webbrowser -from threading import Thread + from meta_server_lib import post_server_data, remove_server from mplay_server import mplay_server, server + from xml.dom import minidom from orpg.orpgCore import component +from orpg.tools.orpg_log import debug +from orpg.tools.orpg_settings import settings + +from xml.etree.ElementTree import ElementTree, Element, iselement +from xml.etree.ElementTree import fromstring, tostring, parse # Constants ###################################### SERVER_RUNNING = 1 @@ -45,6 +50,7 @@ MENU_ADMIN_BAN = wx.NewId() MENU_BAN_LIST = wx.NewId() MENU_ADMIN_UNBAN = wx.NewId() +PLAYER_SHOW_VERSION = wx.NewId() ############# MENU_PLAYER_CREATE_ROOM = wx.NewId() MENU_PLAYER_SEND_MESSAGE = wx.NewId() @@ -91,12 +97,15 @@ # ServerConfig Object ############################ class ServerConfig: + #debug(log=False) """ This class contains configuration setting used to control the server.""" def __init__(self, owner ): """ Loads default configuration settings.""" - validate.config_file("server_ini.xml", "default_server_ini.xml" ) + validate.config_file("server_ini.xml", "default_server_ini.xml") + config_xml = parse(dir_struct['user'] + 'server_ini.xml') + #debug(config_xml, log=False) configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') port = configDom.childNodes[0].childNodes[1].getAttribute('port') OPENRPG_PORT = 6774 if port == '' else int(port) #Pretty ugly, but I couldn't find the tag any other way. @@ -125,6 +134,7 @@ self.bootPwd = pwd def log(self, mesg): + #debug(log=False) if type(mesg) == types.TupleType: func, msg = mesg event = MessageFunctionEvent( func, msg ) @@ -133,6 +143,7 @@ del event def run(self): + #debug(log=False) """ Start the server. """ self.server = mplay_server(self.log, self.serverName ) self.server.initServer(bootPassword=self.bootPwd, reg="No") @@ -140,6 +151,7 @@ while self.alive: time.sleep(3) def stop(self): + #debug(log=False) """ Stop the server. """ self.server.kill_server() self.alive = 0 @@ -147,6 +159,45 @@ # GUI Server ##################################### # Parent = notebook # Main = GUI +class Groups(wx.ListCtrl): + def __init__(self, parent, main): + wx.ListCtrl.__init__(self, parent, -1, wx.DefaultPosition, + wx.DefaultSize, wx.LC_REPORT|wx.SUNKEN_BORDER|wx.EXPAND|wx.LC_HRULES) + self.main = main + + """Not completed. Creates room, delets rooms. Does not track players. Nor does gsclient, ftm.""" + + ### Alpha ### Get Lobby Name # Moving to etree. + validate.config_file("server_ini.xml", "default_server_ini.xml" ) + configDom = minidom.parse(dir_struct["user"] + 'server_ini.xml') + lobbyname = configDom.childNodes[0].getAttribute('lobbyname') + ############# + self.roomList = { 0 : lobbyname } + + self.InsertColumn(0, 'Group ID') + self.InsertColumn(1, 'Game') + self.InsertColumn(2, 'Players') + self.InsertColumn(3, 'Passworded') + self.AddGroup((self.roomList[0], '0', 'Need to Find', 'No')) + + def AddGroup(self, data): + (room, room_id, players, passworded) = data + i = self.InsertStringItem(0, str(room_id)) + self.SetStringItem(i, 1, room) + self.SetStringItem(i, 2, players) + self.SetStringItem(i, 3, str(passworded)) + + def DeleteGroup(self, data): + i = self.FindItem(-1, str(data)) + self.DeleteItem(i) + + def UpdateRoom(self, data): + (room, room_id, players) = data + i = self.FindItem( -1, str(room_id)) + self.SetStringItem( i, 1, room ) + if players: self.SetStringItem(i, 2, players) + ### Need to add room for Password Updates ### + class Connections(wx.ListCtrl): def __init__( self, parent, main ): wx.ListCtrl.__init__( self, parent, -1, wx.DefaultPosition, @@ -185,6 +236,7 @@ self.menu.SetTitle( "Player Menu" ) self.menu.Append( MENU_PLAYER_BOOT, "Boot Player" ) self.menu.Append( MENU_ADMIN_BAN, 'Ban Player' ) + self.menu.Append( PLAYER_SHOW_VERSION, "Player Version" ) self.menu.AppendSeparator() self.menu.Append( MENU_PLAYER_SEND_MESSAGE, "Send Player Message" ) self.menu.Append( MENU_PLAYER_SEND_ROOM_MESSAGE, "Send Room Message" ) @@ -197,22 +249,24 @@ self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_MESSAGE) self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_ROOM_MESSAGE) self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=MENU_PLAYER_SEND_SERVER_MESSAGE) + self.Bind(wx.EVT_MENU, self.OnPopupMenuItem, id=PLAYER_SHOW_VERSION) def add(self, player): i = self.InsertImageStringItem( 0, player["id"], 0 ) - self.SetStringItem( i, 1, self.stripHtml( player["name"] ) ) - self.SetStringItem( i, 2, "NEW" ) - self.SetStringItem( i, 3, self.roomList[0] ) - self.SetStringItem( i, 4, self.stripHtml( player["version"] ) ) - self.SetStringItem( i, 5, 'Lurker' if self.stripHtml( player["role"] ) == '' else self.stripHtml( player["role"] )) - self.SetStringItem( i, 6, self.stripHtml( player["ip"] ) ) - self.SetStringItem (i, 7, "PING" ) - self.SetItemData( i, int(player["id"]) ) + self.SetStringItem(i, 1, self.stripHtml(player["name"])) + self.SetStringItem(i, 2, "NEW") + self.SetStringItem(i, 3, self.roomList[0]) + self.SetStringItem(i, 4, self.stripHtml(player["version"])) + self.SetStringItem(i, 5, 'Lurker' if player["role"] == None else self.stripHtml(player["role"])) + self.SetStringItem(i, 6, self.stripHtml(player["ip"])) + self.SetStringItem(i, 7, "PING") + self.SetItemData(i, int(player["id"])) + self.colorize_player_list(player) self.AutoAjust() def remove(self, id): - i = self.FindItemData( -1, int(id) ) - self.DeleteItem( i ) + i = self.FindItemData( -1, int(id)) + self.DeleteItem(i) self.AutoAjust() def AutoAjust(self): @@ -226,12 +280,31 @@ self.SetColumnWidth(7, -1) self.Refresh() + def colorize_player_list(self, player): + if player == 0: return + for m in player.keys(): + id = player['id'] + item_list_location = self.FindItemData(-1, int(id)) + if item_list_location == -1: continue + item = self.GetItem(item_list_location) + role = player['role'] + try: #Players that turn up Blue are not passing the correct arguments. + try: + if player['group_id'] != "0": item.SetTextColour(settings.get_setting(role + "RoleColor")) + except KeyError: # Needed because group_id turns into group somewhere. + if player['group'] != "0": item.SetTextColour(settings.get_setting(role + "RoleColor")) + except: + item.SetTextColour('BLUE') + self.SetItem(item) + def update(self, player): + #try: int(player); i = self.FindItemData( -1, int(player) ) i = self.FindItemData( -1, int(player["id"]) ) if i > -1: self.SetStringItem(i, 1, self.stripHtml(player["name"])) self.SetStringItem(i, 2, self.stripHtml(player["status"])) - self.SetStringItem(i, 5, 'Lurker' if self.stripHtml(player["role"]) == '' else self.stripHtml(player["role"])) + self.SetStringItem(i, 5, 'Lurker' if player["role"] == None else self.stripHtml(player["role"])) + self.colorize_player_list(player) self.AutoAjust() else: self.add(player) @@ -240,11 +313,12 @@ i = self.FindItemData( -1, int(player) ) if player > 0: self.SetStringItem( i, 3, room ) self.AutoAjust() + #self.update(player) # player object doesn't send role object. def setPlayerRole( self, id, role ): i = self.FindItemData( -1, int(id) ) self.SetStringItem( i, 5, role ) - self.AutoAjust + self.AutoAjust() def stripHtml( self, name ): ret_string = "" @@ -310,6 +384,10 @@ msg = self.GetMessageInput( "Broadcast Server Message" ) # If we got a message back, send it if len(msg): self.main.server.server.broadcast( msg ) + elif menuItem == PLAYER_SHOW_VERSION: + version_string = self.main.server.server.obtain_by_id(playerID, 'client_string') + if version_string: wx.MessageBox("Running client version " + version_string,"Version") + else: wx.MessageBox("No client version available for this player", "Version") def GetMessageInput( self, title ): prompt = "Please enter the message you wish to send:" @@ -354,6 +432,7 @@ cb["create_group"] = self.OnCreateGroup cb["delete_group"] = self.OnDeleteGroup cb["join_group"] = self.OnJoinGroup + cb['update_group'] = self.OnUpdateGroup cb["role"] = self.OnSetRole self.callbacks = cb @@ -416,13 +495,15 @@ """ Create the ViewNotebook and logger. """ splitter = wx.SplitterWindow(self, -1, style=wx.NO_3D | wx.SP_3D) nb = wx.Notebook( splitter, -1 ) - self.conns = Connections( nb, self ) - nb.AddPage( self.conns, "Players" ) + self.conns = Connections(nb, self) + self.groups = Groups(nb, self) + nb.AddPage(self.conns, "Players") + nb.AddPage(self.groups, 'Rooms') #Not sure why this is Remarked TaS - Sirebral #nb.AddPage( self.conns, "Rooms" ) - #self.msgWindow = HTMLMessageWindow( nb ) - #nb.AddPage( self.msgWindow, "Messages" ) + self.msgWindow = HTMLMessageWindow( nb ) + nb.AddPage( self.msgWindow, "Messages" ) log = wx.TextCtrl(splitter, -1, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL) wx.Log.SetActiveTarget( wx.LogTextCtrl(log) ) @@ -458,7 +539,7 @@ self.Log( event.message ) # Event handler for out logging event - def OnFunctionMessage( self, event ): + def OnFunctionMessage(self, event): self.callbacks[event.func]( event.message ) ### Server Callbacks ##################################### @@ -485,22 +566,28 @@ self.sb.SetStatusText("Recv: %s (%d)" % (format_bytes(self.total_data_received), self.total_messages_received), 2) def OnCreateGroup( self, data ): - room_id = data[1] - name = data[0] + (room, room_id, player, pwd) = data + self.groups.AddGroup(data) self.conns.roomList[room_id] = name - (room, room_id, player) = data + data = (room, room_id, player) self.conns.updateRoom(data) - def OnDeleteGroup( self, data ): + def OnDeleteGroup(self, data): (room_id, player) = data + self.groups.DeleteGroup(room_id) del self.conns.roomList[room_id] - def OnJoinGroup( self, data ): - self.conns.updateRoom( data ) + def OnJoinGroup(self, data): + #debug(data) + self.conns.updateRoom(data) + + def OnUpdateGroup(self, data): + (room, room_id, players) = data + self.groups.UpdateGroup(data) def OnSetRole( self, data ): (id, role) = data - self.conns.setPlayerRole( id, role ) + self.conns.setPlayerRole(id, role) ### Misc. ################################################ def OnStart(self, event = None): @@ -516,7 +603,7 @@ except: pass if self.serverName == '': self.serverName = 'Server Name' - serverNameEntry = wx.TextEntryDialog( self, "Please Enter The Server Name You Wish To Use:", + serverNameEntry = wx.TextEntryDialog(self, "Please Enter The Server Name You Wish To Use:", "Server's Name", self.serverName, wx.OK|wx.CANCEL|wx.CENTRE ) if serverNameEntry.ShowModal() == wx.ID_OK: self.serverName = serverNameEntry.GetValue() # see if we already have password specified @@ -608,7 +695,13 @@ def ConfigPingInterval( self, event = None ): "Configure the player ping interval. Note that all players are pinged on a single timer." - def OnExit(self, event = None): + def OnExit(self, event): + dlg = wx.MessageDialog(self, "Exit the Server?", "OpenRPG- Server", wx.YES_NO) + if dlg.ShowModal() == wx.ID_YES: + dlg.Destroy() + self.ExitConfirmed() + + def ExitConfirmed(self, event=None): """ Quit the program. """ self.OnStop() self.BanListDialog.Destroy() ### Alpha ### diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/orpg_version.py --- a/orpg/orpg_version.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/orpg_version.py Tue Nov 10 14:11:28 2009 -0600 @@ -4,7 +4,7 @@ #BUILD NUMBER FORMAT: "YYMMDD-##" where ## is the incremental daily build index (if needed) DISTRO = "Traipse Beta" DIS_VER = "Ornery Orc" -BUILD = "090925-01" +BUILD = "091010-00" # This version is for network capability. PROTOCOL_VERSION = "1.2" diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/orpg_windows.py --- a/orpg/orpg_windows.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/orpg_windows.py Tue Nov 10 14:11:28 2009 -0600 @@ -32,6 +32,7 @@ from orpg.orpgCore import * import orpg.tools.rgbhex from orpg.dirpath import dir_struct +from orpg.tools.orpg_log import debug from orpg.tools.metamenus import MenuEx class img_helper: @@ -55,11 +56,10 @@ pos = string.rfind(file_name,'.') ext = string.lower(file_name[pos+1:]) img_type = 0 - # TaS - sirebral. Replaces 10 lines with 6 lines. recycle_bin = {"gif": wx.BITMAP_TYPE_GIF, "jpg": wx.BITMAP_TYPE_JPEG, "jpeg": wx.BITMAP_TYPE_JPEG, "bmp": wx.BITMAP_TYPE_BMP, "png": wx.BITMAP_TYPE_PNG} if recycle_bin.has_key(ext): img_type = recycle_bin[ext] - else: img_type = None ## this was imf_type = None. imf? + else: img_type = None del recycle_bin; return img_type ################################ @@ -87,7 +87,7 @@ if style: nbstyle |= style self.SetWindowStyleFlag(nbstyle) - #Tas - sirebral. Planned changes to the huge statement below. + # Tas - sirebral. Planned changes to the huge statement below. if tabtheme == 'slanted&aqua': self.SetGradientColourTo(wx.Color(0, 128, 255)) self.SetGradientColourFrom(wx.WHITE) @@ -188,14 +188,14 @@ sash = self.GetSashPosition() if splitmode == wx.SPLIT_VERTICAL: - pos = mouse_X # Position of the mouse pointer - second = second_x # Beginning of the second (Right) pane + pos = mouse_X # Position of the mouse pointer + second = second_x # Beginning of the second (Right) pane second_size = second_w # Size of the second pane else: - pos = mouse_Y # Position of the mouse pointer - second = second_y # Beginning of the second (Bottom) pane + pos = mouse_Y # Position of the mouse pointer + second = second_y # Beginning of the second (Bottom) pane second_size = second_h # Size of the second pane - sash_size = second - sash # Beginning of sash to beginning of second is the sash size + sash_size = second - sash # Beginning of sash to beginning of second is the sash size if (pos > sash + sash_size and second_size < sash) or (pos < sash and second_size > sash): # Equivalent to the following diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/orpg_xml.py --- a/orpg/orpg_xml.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/orpg_xml.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,19 +29,16 @@ from orpg import minidom import string -from orpg.tools.orpg_log import logger -from orpg.tools.decorators import debugging +from orpg.tools.orpg_log import logger, debug -class xml: - @debugging +class xml: + debug('Deprecated call to orpg_xml!!') def __init__(self): pass - - @debugging + def toxml(self, root, pretty=0): return root.toxml(pretty) - - @debugging + def parseXml(self, s): "parse and return doc" try: @@ -51,8 +48,7 @@ except Exception, e: print e return None - - @debugging + def safe_get_text_node(self, xml_dom): """ returns the child text node or creates one if doesnt exist """ t_node = xml_dom._get_firstChild() @@ -60,16 +56,14 @@ t_node = minidom.Text("") t_node = xml_dom.appendChild(t_node) return t_node - - @debugging + def strip_unicode(self, txt): for i in xrange(len(txt)): if txt[i] not in string.printable: try: txt = txt.replace(txt[i], '&#' + str(ord(txt[i])) + ';') except: txt = txt.replace(txt[i], '{?}') return txt - - @debugging + def strip_text(self, txt): # The following block strips out 8-bit characters u_txt = "" @@ -81,5 +75,4 @@ if bad_txt_found: print "Some non 7-bit ASCII characters found and stripped" return u_txt - xml = xml() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/plugindb.py --- a/orpg/plugindb.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/plugindb.py Tue Nov 10 14:11:28 2009 -0600 @@ -1,184 +1,211 @@ -import xmltramp +from __future__ import with_statement + from orpg.dirpath import dir_struct -#import orpg.tools.validate -from types import * -from orpg.orpgCore import component +from orpg.tools.validate import validate +from orpg.tools.orpg_log import logger -class PluginDB: - def __init__(self, filename="plugindb.xml"): - self.filename = dir_struct["user"] + filename - component.get('validate').config_file(filename,"default_plugindb.xml") - self.xml_dom = self.LoadDoc() +from xml.etree.ElementTree import ElementTree, Element, parse +from xml.etree.ElementPath import find + +class PluginDB(object): + etree = ElementTree() + filename = dir_struct["user"] + "plugindb.xml" - def GetString(self, plugname, strname, defaultval, verbose=0): + def __new__(cls, *args, **kwargs): + it = cls.__dict__.get("__it__") + if it is not None: + return it + cls.__it__ = it = object.__new__(cls) + it._init() + return it + + def _init(self): + validate.config_file("plugindb.xml", "default_plugindb.xml") + self.LoadDoc() + + def GetString(self, plugname, strname, defaultval="", verbose=False): strname = self.safe(strname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == strname: - #str() on this to make sure it's ASCII, not unicode, since orpg can't handle unicode. - if verbose: print "successfully found the value" - if len(child): return str( self.normal(child[0]) ) - else: return "" - else: - if verbose: - print "plugindb: no value has been stored for " + strname + " in " + plugname + " so the default has been returned" + + plugin = self.etree.find(plugname) + if plugin is None or plugin.find(strname) is None: + msg = ["plugindb: no value has been stored for", strname, "in", + plugname, "so the default has been returned"] + logger.info(' '.join(msg), verbose) return defaultval + logger.debug("successfully found the str value", verbose) + return self.normal(plugin.find(strname).text) + def SetString(self, plugname, strname, val): val = self.safe(val) strname = self.safe(strname) - for plugin in self.xml_dom:##this isn't absolutely necessary, but it saves the trouble of sending a parsed object instead of a simple string. - if plugname == plugin._name: - plugin[strname] = val - plugin[strname]._attrs["type"] = "string" - self.SaveDoc() - return "found plugin" - else: - self.xml_dom[plugname] = xmltramp.parse("<" + strname + " type=\"string\">" + val + "") - self.SaveDoc() - return "added plugin" + + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + + str_el = plugin.find(strname) + if str_el is None: + str_el = Element(strname) + str_el.set('type', 'str') + plugin.append(str_el) + str_el.text = val + self.SaveDoc() def FetchList(self, parent): retlist = [] - if not len(parent): return [] - for litem in parent[0]._dir: - if len(litem): - if litem._attrs["type"] == "int": retlist += [int(litem[0])] - elif litem._attrs["type"] == "long": retlist += [long(litem[0])] - elif litem._attrs["type"] == "float": retlist += [float(litem[0])] - elif litem._attrs["type"] == "list": retlist += [self.FetchList(litem)] - elif litem._attrs["type"] == "dict": retlist += [self.FetchDict(litem)] - else: retlist += [str( self.normal(litem[0]) )] - else: retlist += [""] + for litem in parent.findall('lobject'): + if litem.get('type') == 'int': retlist.append(int(litem.text)) + if litem.get('type') == 'bool': retlist.append(litem.text == 'True') + elif litem.get('type') == 'float': retlist.append(float(litem.text)) + elif litem.get('type') == 'list': retlist.append(self.FetchList(litem)) + elif litem.get('type') == 'dict': retlist.append(self.FetchDict(litem)) + else: retlist.append(str(self.normal(litem.text))) + return retlist + + def GetList(self, plugname, listname, defaultval=list(), verbose=False): + listname = self.safe(listname) + plugin = self.etree.find(plugname) + + if plugin is None or plugin.find(listname) is None: + msg = ["plugindb: no value has been stored for", listname, "in", + plugname, "so the default has been returned"] + logger.info(' '.join(msg), verbose) + return defaultval + + retlist = self.FetchList(plugin.find(listname)) + logger.debug("successfully found the list value", verbose) return retlist - def GetList(self, plugname, listname, defaultval, verbose=0): - listname = self.safe(listname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == listname and child._attrs["type"] == "list": - retlist = self.FetchList(child) - if verbose: print "successfully found the value" - return retlist - else: - if verbose: - print "plugindb: no value has been stored for " + listname + " in " + plugname + " so the default has been returned" - return defaultval - def BuildList(self, val): - listerine = "" + list_el = Element('list') for item in val: - if isinstance(item, basestring):#it's a string - listerine += "" + self.safe(item) + "" - elif isinstance(item, IntType):#it's an int - listerine += "" + str(item) + "" - elif isinstance(item, FloatType):#it's a float - listerine += "" + str(item) + "" - elif isinstance(item, LongType):#it's a long - listerine += "" + str(item) + "" - elif isinstance(item, ListType):#it's a list - listerine += "" + self.BuildList(item) + "" - elif isinstance(item, DictType):#it's a dictionary - listerine += "" + self.BuildDict(item) + "" + i = Element('lobject') + if isinstance(item, bool): + i.set('type', 'bool') + i.text = str(item) + elif isinstance(item, int):#it's an int + i.set('type', 'int') + i.text = str(item) + elif isinstance(item, float):#it's a float + i.set('type', 'float') + i.text = str(item) + elif isinstance(item, (list, tuple)):#it's a list + i.set('type', 'list') + i.append(self.BuildList(item)) + elif isinstance(item, dict):#it's a dictionary + i.set('type', 'dict') + i.append(self.BuildDict(item)) else: - return "type unknown" - listerine += "" - return listerine + i.set('type', 'str') + i.text = self.safe(item) + list_el.append(i) + return list_el def SetList(self, plugname, listname, val): listname = self.safe(listname) - list = xmltramp.parse(self.BuildList(val)) - for plugin in self.xml_dom: - if plugname == plugin._name: - plugin[listname] = list - plugin[listname]._attrs["type"] = "list" - self.SaveDoc() - return "found plugin" + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + list_el = plugin.find(listname) + if list_el is None: + list_el = Element(listname) + list_el.set('type', 'list') + plugin.append(list_el) else: - self.xml_dom[plugname] = xmltramp.parse("<" + listname + ">") - self.xml_dom[plugname][listname] = list - self.xml_dom[plugname][listname]._attrs["type"] = "list" - self.SaveDoc() - return "added plugin" + list_el.remove(list_el.find('list')) + list_el.append(self.BuildList(val)) + self.SaveDoc() def BuildDict(self, val): - dictator = "" - for item in val.keys(): - if isinstance(val[item], basestring): - dictator += "" + self.safe(val[item]) + "" - elif isinstance(val[item], IntType):#it's an int - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], FloatType):#it's a float - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], LongType):#it's a long - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], DictType):#it's a dictionary - dictator += "" + self.BuildDict(val[item]) + "" - elif isinstance(val[item], ListType):#it's a list - dictator += "" + self.BuildList(val[item]) + "" + dict_el = Element('dict') + for key, item in val.iteritems(): + i = Element('dobject') + + if isinstance(item, bool): + i.set('type', 'bool') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, int):#it's an int + i.set('type', 'int') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, float):#it's a float + i.set('type', 'float') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, (list, tuple)):#it's a list + i.set('type', 'list') + i.set('name', self.safe(key)) + i.append(self.BuildList(item)) + elif isinstance(item, dict):#it's a dictionary + i.set('type', 'dict') + i.set('name', self.safe(key)) + i.append(self.BuildDict(item)) else: - return str(val[item]) + ": type unknown" - dictator += "" - return dictator + i.set('type', 'str') + i.set('name', self.safe(key)) + i.text = self.safe(item) + dict_el.append(i) + return dict_el - def SetDict(self, plugname, dictname, val, file="plugindb.xml"): + def SetDict(self, plugname, dictname, val): dictname = self.safe(dictname) - dict = xmltramp.parse(self.BuildDict(val)) - for plugin in self.xml_dom: - if plugname == plugin._name: - plugin[dictname] = dict - plugin[dictname]._attrs["type"] = "dict" - self.SaveDoc() - return "found plugin" + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + dict_el = plugin.find(dictname) + if dict_el is None: + dict_el = Element(dictname) + dict_el.set('type', 'dict') + plugin.append(dict_el) else: - self.xml_dom[plugname] = xmltramp.parse("<" + dictname + ">") - self.xml_dom[plugname][dictname] = dict - self.xml_dom[plugname][dictname]._attrs["type"] = "dict" - self.SaveDoc() - return "added plugin" + dict_el.remove(list_el.find('dict')) + dict_el.append(self.BuildDict(val)) + self.SaveDoc() def FetchDict(self, parent): retdict = {} - if not len(parent): - return {} - for ditem in parent[0]._dir: - if len(ditem): - ditem._attrs["name"] = self.normal(ditem._attrs["name"]) - if ditem._attrs["type"] == "int": retdict[ditem._attrs["name"]] = int(ditem[0]) - elif ditem._attrs["type"] == "long": retdict[ditem._attrs["name"]] = long(ditem[0]) - elif ditem._attrs["type"] == "float": retdict[ditem._attrs["name"]] = float(ditem[0]) - elif ditem._attrs["type"] == "list": retdict[ditem._attrs["name"]] = self.FetchList(ditem) - elif ditem._attrs["type"] == "dict": retdict[ditem._attrs["name"]] = self.FetchDict(ditem) - else: retdict[ditem._attrs["name"]] = str( self.normal(ditem[0]) ) - else: retdict[ditem._attrs["name"]] = "" + for ditem in parent.findall('dobject'): + key = self.normal(ditem.get('name')) + if ditem.get('type') == 'int': value = int(ditem.text) + elif ditem.get('type') == 'bool': value = ditem.text == 'True' + elif ditem.get('type') == 'float': value = float(ditem.text) + elif ditem.get('type') == 'list': value = self.FetchList(ditem) + elif ditem.get('type') == 'dict': value = self.FetchDict(ditem) + else: value = str(self.normal(ditem[0])) + retdict[key] = value return retdict - def GetDict(self, plugname, dictname, defaultval, verbose=0): + def GetDict(self, plugname, dictname, defaultval=dict(), verbose=False): dictname = self.safe(dictname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == dictname and child._attrs["type"] == "dict": return self.FetchDict(child) - else: - if verbose: - print "plugindb: no value has been stored for " + dictname + " in " + plugname + " so the default has been returned" + plugin = self.etree.find(plugname) + if plugin is None or plugin.find(dictname) is None: + msg = ["plugindb: no value has been stored for", dictname, "in", + plugname, "so the default has been returned"] + logger.info(' '.join(msg), verbose) return defaultval + retdict = self.FetchDict(plugin.find(dictname)) + logger.debug("successfully found dict value", verbose) + return retdict def safe(self, string): - return string.replace("<", "$$lt$$").replace(">", "$$gt$$").replace("&","$$amp$$").replace('"',"$$quote$$") + return string.replace("<", "$$lt$$").replace(">", "$$gt$$")\ + .replace("&","$$amp$$").replace('"',"$$quote$$") def normal(self, string): - return string.replace("$$lt$$", "<").replace("$$gt$$", ">").replace("$$amp$$","&").replace("$$quote$$",'"') + return string.replace("$$lt$$", "<").replace("$$gt$$", ">")\ + .replace("$$amp$$","&").replace("$$quote$$",'"') def SaveDoc(self): - f = open(self.filename, "w") - f.write(self.xml_dom.__repr__(1, 1)) - f.close() + with open(self.filename, "w") as f: + self.etree.write(f) def LoadDoc(self): - xml_file = open(self.filename) - plugindb = xml_file.read() - xml_file.close() - return xmltramp.parse(plugindb) + with open(self.filename) as f: + self.etree.parse(f) + +plugindb = PluginDB() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_LobbyMessage.html --- a/orpg/templates/default_LobbyMessage.html Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/templates/default_LobbyMessage.html Tue Nov 10 14:11:28 2009 -0600 @@ -20,7 +20,8 @@ Bernhard Bergbauer, Chris Blocher ,Ben Collins-Sussman, Robin Cook, Greg Copeland, Chris Davis, Michael Edwards, Andrew Ettinger, Dj Gilcrease, Todd Faris, Christopher Hickman, Paul Hosking, Scott Mackay, Brian Manning, - Jesse McConnell, Brian Osman, Rome Reginelli, Christopher Rouse, Dave Sanders and Mark Tarrabain. + Jesse McConnell, Brian Osman, Rome Reginelli, Christopher Rouse, Dave Sanders, Mark Tarrabain, + David Byron, and Tyler Starke. diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_alias.alias diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_ban_list.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_gui.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_ignorelist.txt diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_ini.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_layout.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_manifest.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_map.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_plugindb.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_server_bookmarks.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orpg/templates/default_server_bookmarks.xml Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,1 @@ + \ No newline at end of file diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_server_ini.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_settings.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/default_tree.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/feature.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/metaservers.cache diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/Bastion_adventure.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/Darwin_adventure.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/FFE_adventure.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/Idiots_guide_to_openrpg.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/MiniatureLibrary.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/StarWars_d20character.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/Userguide098.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/Userguide13.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/adnd_2e_char_sheet.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/alias.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/browser.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/d20character.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/d20sites.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/d20srd.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/d20wizards.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/default_map.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/die_macro.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/die_roller_notes.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/dnd3.5.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/dnd3e.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/encounter.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/form.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/grid.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/group.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/image.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/link.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/listbox.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/macro.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/minlib.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/openrpg_links.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/split.xml --- a/orpg/templates/nodes/split.xml Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/templates/nodes/split.xml Tue Nov 10 14:11:28 2009 -0600 @@ -1,2 +1,3 @@ - - \ No newline at end of file + + + \ No newline at end of file diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/tabber.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/text.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/textctrl.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/u_idiots_guide_to_openrpg.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/urloader.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/nodes/wizards.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/templates/templates.xml diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/orpg_log.py --- a/orpg/tools/orpg_log.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/tools/orpg_log.py Tue Nov 10 14:11:28 2009 -0600 @@ -27,7 +27,7 @@ # from __future__ import with_statement -import sys, os, os.path, wx, time, traceback +import sys, os, os.path, time, traceback, inspect, wx from orpg.orpgCore import component from orpg.external.terminalwriter import TerminalWriter @@ -37,12 +37,14 @@ ######################### ## Error Types ######################### +ORPG_PRINT = 0 ORPG_CRITICAL = 1 ORPG_GENERAL = 2 ORPG_INFO = 4 ORPG_NOTE = 8 ORPG_DEBUG = 16 + def Crash(type, value, crash): crash_report = open(dir_struct["home"] + 'crash-report.txt', "w") traceback.print_exception(type, value, crash, file=crash_report) @@ -55,25 +57,74 @@ logger.exception("Crash Report Created!!") logger.info("Printed out crash-report.txt in your System folder", True) +class Term2Win(object): + # A stdout redirector. Allows the messages from Mercurial to be seen in the Install Window + def write(self, text): + #logger.stdout(text) + wx.Yield() + sys.__stdout__.write(text) + +class TrueDebug(object): + ### Alpha ### + """A simple debugger. Add debug() to a function and it prints the function name and any objects included. Add an object or a group of objects in ()'s. + Adding True to locale prints the file name where the function is. Adding False to log turns the log off. + Adding True to parents will print out the parent functions, starting from TrueDebug. + This feature can be modified to trace deeper and find the bugs faster, ending the puzzle box.""" + def __init__(self, objects=None, locale=False, log=True, parents=False): + if log == False: return + current = inspect.currentframe() + if parents: self.get_parents(current) + else: self.true_debug(current, objects, locale) + + def true_debug(self, current, objects, locale): + debug_string = 'Function: ' + str(inspect.getouterframes(current)[1][3]) + #if locale == 'all': print inspect.getouterframes(current)[4]; return + if objects != None: debug_string += ' Objects: ' + str(objects) + if locale: debug_string += ' File: ' + str(inspect.getouterframes(current)[1][1]) + logger.debug(debug_string, True) + return + + def get_parents(self, current): + debug_string = 'Function: ' + str(inspect.getouterframes(current)[1][3]) + ' Parents:' + family = list(inspect.getouterframes(current)) + for parent in family: + debug_string += ' ' + str(parent[4]) + logger.debug(debug_string, True) + return + class DebugConsole(wx.Frame): def __init__(self, parent): super(DebugConsole, self).__init__(parent, -1, "Debug Console") - icon = None icon = wx.Icon(dir_struct["icon"]+'note.ico', wx.BITMAP_TYPE_ICO) - self.SetIcon( icon ) + self.SetIcon(icon) self.console = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE | wx.TE_READONLY) - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(self.console, 1, wx.EXPAND) + self.bt_clear = wx.Button(self, wx.ID_CLEAR) + self.report = wx.Button(self, wx.ID_ANY, 'Bug Report') + sizer = wx.GridBagSizer(hgap=1, vgap=1) + sizer.Add(self.console, (0,0), span=(1,2), flag=wx.EXPAND) + sizer.Add(self.bt_clear, (1,0), flag=wx.ALIGN_LEFT) + sizer.Add(self.report, (1,1), flag=wx.ALIGN_LEFT) + sizer.AddGrowableCol(0) + sizer.AddGrowableRow(0) self.SetSizer(sizer) self.SetAutoLayout(True) - self.SetSize((300, 175)) + self.SetSize((450, 175)) self.Bind(wx.EVT_CLOSE, self.Min) + self.Bind(wx.EVT_BUTTON, self.clear, self.bt_clear) + self.Bind(wx.EVT_BUTTON, self.bug_report, self.report) self.Min(None) + #sys.stdout = Term2Win() component.add('debugger', self.console) def Min(self, evt): self.Hide() + def clear(self, evt): + self.console.SetValue('') + + def bug_report(self, evt): + pass + class orpgLog(object): _log_level = 7 _log_name = None @@ -127,7 +178,7 @@ try: component.get('debugger').AppendText(".. " + str(msg) +'\n') except: pass - if log_type & self.log_level or to_console: + if log_type and (self.log_level or to_console): atr = {'msg': msg, 'level': self._lvl_args[log_type]['log_string']} atr['time'] = time.strftime('[%x %X]', time.localtime(time.time())) logMsg = '%(time)s (%(level)s) - %(msg)s\n' % (atr) @@ -192,3 +243,4 @@ logger = orpgLog(dir_struct.get("user") + "runlogs/") crash = sys.excepthook = Crash +debug = TrueDebug diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/orpg_settings.py --- a/orpg/tools/orpg_settings.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/tools/orpg_settings.py Tue Nov 10 14:11:28 2009 -0600 @@ -30,126 +30,11 @@ from orpg.orpgCore import component from orpg.dirpath import dir_struct from rgbhex import * -import sys -import os -from orpg.orpg_xml import xml + from orpg.tools.orpg_log import logger from orpg.tools.validate import validate from orpg.orpg_xml import xml - -class orpgSettings: - def __init__(self): - self.xml = component.get("xml") - self.changes = [] - validate.config_file("settings.xml","default_settings.xml") - self.filename = dir_struct["user"] + "settings.xml" - temp_file = open(self.filename) - txt = temp_file.read() - temp_file.close() - - self.xml_dom = xml.parseXml(txt) - - if self.xml_dom is None: self.rebuildSettings() - self.xml_dom = self.xml_dom._get_documentElement() - - def rebuildSettings(self): - logger.info("Settings file has be corrupted, rebuilding settings.", True) - try: os.remove(self.filename) - except: pass - - validate.config_file("settings.xml","default_settings.xml") - temp_file = open(self.filename) - txt = temp_file.read() - temp_file.close() - self.xml_dom = xml.parseXml(txt) - - def get_setting(self, name): ##Depricated - return self.get(name) - - def get(self, name): - try: return self.xml_dom.getElementsByTagName(name)[0].getAttribute("value") - except: return 0 - - def get_setting_keys(self): ##Depricated - return self.get_keys() - - def get_keys(self): - keys = [] - tabs = self.xml_dom.getElementsByTagName("tab") - for i in xrange(0, len(tabs)): - if tabs[i].getAttribute("type") == 'grid': - children = tabs[i]._get_childNodes() - for c in children: keys.append(c._get_tagName()) - return keys - - def set_setting(self, name, value): ##Depricated - self.change(name, value) - - def change(self, name, value): - self.xml_dom.getElementsByTagName(name)[0].setAttribute("value", value) - - def add_setting(self, tab, setting, value, options, help): ##Depricated - return self.add(tab, setting, value, options, help) - - def add(self, tab, setting, value, options, help): - if len(self.xml_dom.getElementsByTagName(setting)) > 0: return False - tabs = self.xml_dom.getElementsByTagName("tab") - newsetting = xml.parseXml('<' + setting + ' value="' + value + '" options="' + - options + '" help="' + help + '" />')._get_documentElement() - for i in xrange(0, len(tabs)): - if tabs[i].getAttribute("name") == tab and tabs[i].getAttribute("type") == 'grid': - tabs[i].appendChild(newsetting) - return True - return False - - def add_tab(self, parent, tabname, tabtype): - tab_xml = '' - else: tab_xml += 'name="' + tabname + '" type="' + tabtype + '">' - newtab = xml.parseXml(tab_xml)._get_documentElement() - if parent != None: - tabs = self.xml_dom.getElementsByTagName("tab") - for i in xrange(0, len(tabs)): - if tabs[i].getAttribute("name") == parent and tabs[i].getAttribute("type") == 'tab': - children = tabs[i]._get_childNodes() - for c in children: - if c.getAttribute("name") == tabname: return False - tabs[i].appendChild(newtab) - return True - else: - children = self.xml_dom._get_childNodes() - for c in children: - if c.getAttribute("name") == tabname: return False - self.xml_dom.appendChild(newtab) - return True - return False - - def updateIni(self): - defaultFile = orpg.dirpath.dir_struct['template'] + 'default_settings.xml' - temp_file = open(defaultFile) - txt = temp_file.read() - temp_file.close() - default_dom = xml.parseXml(txt)._get_documentElement() - for child in default_dom.getChildren(): - if child._get_tagName() == 'tab' and child.hasChildNodes(): self.proccessChildren(child) - default_dom.unlink() - - def proccessChildren(self, dom, parent=None): - if dom._get_tagName() == 'tab': - self.add_tab(parent, dom.getAttribute("name"), dom.getAttribute("type")) - - for child in dom.getChildren(): - if child._get_tagName() == 'tab' and child.hasChildNodes(): - self.proccessChildren(child, dom.getAttribute("name")) - else: - self.add_setting(dom.getAttribute("name"), child._get_tagName(), - child.getAttribute("value"), child.getAttribute("options"), - child.getAttribute("help")) - - def save(self): - temp_file = open(self.filename, "w") - temp_file.write(xml.toxml(self.xml_dom,1)) - temp_file.close() +from orpg.tools.settings import settings class orpgSettingsWnd(wx.Dialog): def __init__(self, parent): @@ -401,5 +286,4 @@ for i in range(0,cols): self.SetColSize(i,col_w) self.Refresh() -settings = orpgSettings() -component.add('settings', settings) +#settings = orpg.tools.settings.Settings() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/passtool.py --- a/orpg/tools/passtool.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/tools/passtool.py Tue Nov 10 14:11:28 2009 -0600 @@ -29,7 +29,7 @@ import orpg.orpg_windows from orpg.orpgCore import component import traceback -#from orpg.tools.settings import settings +from orpg.tools.settings import settings ##################### ## Password Assistant @@ -100,8 +100,18 @@ def GetSilentPassword( self, type="server", groupid = 0): try: self.CheckGroupData( groupid ) + ### Pre-Alpha ### How types will work + # Server - top dog + # Admin - second in command + # Mod - some privileges + # Room - You got it, unchanged (passwords the room entry) + # Boot - This is the room admin password. if type == "admin": - if self.groups[int(groupid)].admin != None: return str(self.groups[int(groupid)].admin) + try: print self.groups[int(groupid)].admin + except: pass + try: print str(self.groups[int(groupid)].admin) + except: pass + if self.groups[int(groupid)].admin != None: print str(self.groups[int(groupid)].admin); return str(self.groups[int(groupid)].admin) else: return None elif type == "room": if self.groups[int(groupid)].room != None: return str(self.groups[int(groupid)].room) diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/pluginui.py --- a/orpg/tools/pluginui.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/tools/pluginui.py Tue Nov 10 14:11:28 2009 -0600 @@ -1,17 +1,16 @@ from orpg.orpg_wx import * from orpg.orpgCore import * -import orpg.plugindb as plugindb +from orpg.plugindb import plugindb from orpg.dirpath import dir_struct -sys.path.append(dir_struct["plugins"]) +#sys.path.append(dir_struct["plugins"]) class PluginFrame(wx.Frame): def __init__(self, parent): wx.Frame.__init__(self, parent, wx.ID_ANY, "Plugin Control Panel") self.panel = wx.Panel(self, wx.ID_ANY) - self.plugindb = plugindb.PluginDB() self.parent = parent - self.startplugs = self.plugindb.GetList("plugincontroller", "startup_plugins", []) + self.startplugs = plugindb.GetList("plugincontroller", "startup_plugins", []) self.available_plugins = {} self.enabled_plugins = {} self.pluginNames = [] @@ -44,16 +43,15 @@ self.btn_sizer = wx.BoxSizer(wx.HORIZONTAL) self.btn_sizer2 = wx.BoxSizer(wx.HORIZONTAL) self.head_sizer = wx.BoxSizer(wx.HORIZONTAL) - #pnl = wx.Panel(self.panel, wx.ID_ANY) self.err_sizer = wx.BoxSizer(wx.VERTICAL) self.err_sizer.Add(self.head_sizer, 0, wx.EXPAND) self.errorMessage = wx.StaticText(self.panel, wx.ID_ANY, "") self.err_sizer.Add(self.errorMessage, 0, wx.EXPAND) self.main_sizer.Add(self.err_sizer, 0, wx.EXPAND) - self.pluginList = wx.ListCtrl(self.panel, wx.ID_ANY, style=wx.LC_SINGLE_SEL|wx.LC_REPORT|wx.LC_HRULES|wx.LC_SORT_ASCENDING) - self.pluginList.InsertColumn(1, "Name") - self.pluginList.InsertColumn(2, "Autostart") - self.pluginList.InsertColumn(3, "Author") + self.pluginList = wx.ListCtrl(self.panel, wx.ID_ANY, + style=wx.LC_SINGLE_SEL|wx.LC_REPORT|wx.LC_HRULES|wx.LC_SORT_ASCENDING) + self.pluginList.InsertColumn(0, "Name") + self.pluginList.InsertColumn(1, "Author") self.Bind(wx.EVT_LIST_ITEM_SELECTED, self._selectPlugin, self.pluginList) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self._deselectPlugin, self.pluginList) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self._togglePlugin, self.pluginList) @@ -90,6 +88,22 @@ self.main_sizer.Add(self.btn_sizer, 0, wx.EXPAND) self.main_sizer.Add(self.btn_sizer2, 0, wx.EXPAND) + # Create Book Mark Image List + self.pluginList.Bind(wx.EVT_LEFT_DOWN, self.on_hit) + self._imageList = wx.ImageList( 16, 16, False ) + img = wx.Image(dir_struct["icon"]+"add_button.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap() + self._imageList.Add( img ) + img = wx.Image(dir_struct["icon"]+"check_button.png", wx.BITMAP_TYPE_PNG).ConvertToBitmap() + self._imageList.Add( img ) + self.pluginList.SetImageList( self._imageList, wx.IMAGE_LIST_SMALL ) + + def on_hit(self, evt): + pos = wx.Point( evt.GetX(), evt.GetY() ) + (item, flag, sub) = self.pluginList.HitTestSubItem( pos ) + ## Item == list[server], flag == (32 = 0 colum, 128 = else) ## + if flag == 32: self._autostart(item) + evt.Skip() + def __disablePluginBtns(self): self.enableBtn.Disable() self.disableBtn.Disable() @@ -112,12 +126,12 @@ self.__doLayout() def __checkIdx(self, evt): - if isinstance(evt, int): - return evt - elif self._selectedPlugin is not None: - return self._selectedPlugin + if isinstance(evt, int): return evt + elif self._selectedPlugin is not None: return self._selectedPlugin else: - dlg = wx.MessageDialog(None, "You need to select a plugin before you can use this!", 'ERROR', wx.OK) + dlg = wx.MessageDialog(None, + "You need to select a plugin before you can use this!", + 'ERROR', wx.OK) dlg.ShowModal() dlg.Destroy() return None @@ -125,10 +139,12 @@ def __impPlugin(self, pname): try: if "plugins." + pname in sys.modules: - del sys.modules["plugins." + pname]#to ensure that the newly-imported one will be used correctly. No, reload() is not a better way to do this. + del sys.modules["plugins." + pname] + #to ensure that the newly-imported one will be used correctly. + #No, reload() is not a better way to do this. mod = __import__("plugins." + pname) plugin = getattr(mod, pname) - pdata = plugin.Plugin(self.plugindb, self.parent) + pdata = plugin.Plugin(plugindb, self.parent) self.available_plugins[pdata.name] = [pname, pdata, pdata.author, pdata.help] return plugin @@ -157,11 +173,13 @@ self.enableBtn.Disable() else: self.disableBtn.Disable() - if self.pluginList.GetItem(self._selectedPlugin, 1).GetText() == "X": + if pname in self.startplugs: self.autostartBtn.Label = "Disable Autostart" self.__doLayout() - self.pluginList.SetItemState(self._selectedPlugin, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED) + self.pluginList.SetItemState(self._selectedPlugin, + wx.LIST_STATE_SELECTED, + wx.LIST_STATE_SELECTED) def _deselectPlugin(self, evt): self.__disablePluginBtns() @@ -171,12 +189,8 @@ idx = evt.GetIndex() pname = self.pluginList.GetItem(idx, 0).GetText() info = self.available_plugins[pname] - - if info[0] in self.enabled_plugins: - self._disable(idx) - else: - self._enable(idx) - + if info[0] in self.enabled_plugins: self._disable(idx) + else: self._enable(idx) self.pluginList.SetItemState(self._selectedPlugin, 0, wx.LIST_STATE_SELECTED) def _enableAll(self, evt): @@ -236,36 +250,44 @@ def _autostart(self, evt): idx = self.__checkIdx(evt) - if idx is None: - return - if self.pluginList.GetItem(idx, 1).GetText() == "X": + if idx is None: return + pname = self.pluginList.GetItem(idx, 0).GetText() + info = self.available_plugins[pname] + if self.pluginList.GetItem(idx, 0).GetText() in self.startplugs: self.startplugs.remove(self.pluginList.GetItem(idx, 0).GetText()) - self.pluginList.SetStringItem(idx, 1, "") + self.pluginList.SetItemImage(idx, 0, 0) self.autostartBtn.Label = "Autostart" + if info[0] in self.enabled_plugins: + dlg = wx.MessageDialog(None, 'Disable Plugin Now?', 'Plugin Enabled', + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + dlg.ShowModal() + if dlg.ShowModal() == wx.ID_YES: self._disable(evt) else: self.startplugs.append(self.pluginList.GetItem(idx, 0).GetText()) - self.pluginList.SetStringItem(idx, 1, "X") + self.pluginList.SetItemImage(idx, 1, 0) self.autostartBtn.Label = "Disable Autostart" + if info[0] not in self.enabled_plugins: + dlg = wx.MessageDialog(None, 'Enable Plugin Now?', 'Plugin Disabled', + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + if dlg.ShowModal() == wx.ID_YES: self._enable(evt) - self.plugindb.SetList("plugincontroller", "startup_plugins", self.startplugs) + plugindb.SetList("plugincontroller", "startup_plugins", self.startplugs) self.__doLayout() def _help(self, evt): - if isinstance(evt, int): - idx = evt - elif self._selectedPlugin is not None: - idx = self._selectedPlugin + if isinstance(evt, int): idx = evt + elif self._selectedPlugin is not None: idx = self._selectedPlugin else: - dlg = wx.MessageDialog(None, "You need to select a plugin before you can use this!", 'ERROR', wx.OK) + dlg = wx.MessageDialog(None, + "You need to select a plugin before you can use this!", + 'ERROR', wx.OK) dlg.ShowModal() dlg.Destroy() return pname = self.pluginList.GetItem(idx, 0).GetText() info = self.available_plugins[pname] - msg = "Author(s):\t" + info[2] + "\n\n" + info[3] - dlg = wx.MessageDialog(None, msg, 'Plugin Information: ' + pname, wx.OK) dlg.ShowModal() dlg.Destroy() @@ -280,7 +302,6 @@ list_of_plugin_dir = os.listdir(dir_struct["plugins"]) for p in list_of_plugin_dir: - #print p[:2]; print p[-4:] if p[:2].lower()=="xx" and p[-3:]==".py": self.__impPlugin(p[:-3]) elif p[:2].lower()=="xx" and p[-4:]==".pyc": @@ -290,13 +311,15 @@ for plugname, info in self.available_plugins.iteritems(): self.pluginNames.append(plugname) idx = self.pluginList.InsertStringItem(self.pluginList.GetItemCount(), plugname) - self.pluginList.SetStringItem(idx, 2, info[2]) + self.pluginList.SetStringItem(idx, 1, info[2]) + self.pluginList.SetItemImage(idx, 0, 0) if plugname in self.startplugs: - self.pluginList.SetStringItem(idx, 1, "X") + self.pluginList.SetItemImage(idx, 1, 0) self._enable(idx) self.pluginList.SetItemData(idx, i) i += 1 self.pluginList.SetColumnWidth(0, wx.LIST_AUTOSIZE) + self.pluginList.SetColumnWidth(1, wx.LIST_AUTOSIZE) self.__doLayout() self.__disablePluginBtns() diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/predTextCtrl.py --- a/orpg/tools/predTextCtrl.py Fri Sep 25 20:47:16 2009 -0500 +++ b/orpg/tools/predTextCtrl.py Tue Nov 10 14:11:28 2009 -0600 @@ -33,8 +33,9 @@ import string from orpg.orpg_windows import * +import wx #wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) from wx.lib.expando import ExpandoTextCtrl -from orpg.tools.orpg_log import logger +from orpg.tools.orpg_log import logger, debug # This line added to test CVS commit @@ -122,33 +123,22 @@ # # Purpose: Sets or increments the priority of a word, adding the word if necessary def setWord(self,wordText,priority = 1,sumFlag = 0): - cur = self.rootNode # start from the root - for ch in wordText: # for each character in the word if cur.children.has_key(ch): # check to see if we've found a new word - cur = cur.children[ch] # if we haven't found a new word, move to the next letter and try again - else: # in this clause, we're creating a new branch, as the word is new newLetter = Letter(ch,cur) # create a new class Letter using this ascii code and the current letter as a parent - if cur is self.rootNode: # special case: Others expect the top level letters to point to None, not self.rootNode newLetter.parentNode = None - cur.children[ch] = newLetter # add the new letter to the list of children of the current letter cur = newLetter # make the new letter the current one for the next time through # at this point, cur is pointing to the last letter of either a new or existing word. - - if sumFlag: # if the caller wants to add to the existing (0 if new) - cur.priority += priority - else: # else, the set the priority directly - cur.priority = priority - + if sumFlag: cur.priority += priority # if the caller wants to add to the existing (0 if new) + else: cur.priority = priority # else, the set the priority directly self.updateMostCommon(cur) # this will run back through the tree to fix up the mostCommon members - # addWord subroutine. # # self : instance of self @@ -160,7 +150,6 @@ def addWord(self,wordText): self.setWord(wordText,priority = 1) - # incWord subroutine. # # self : instance of self @@ -170,10 +159,10 @@ # # Purpose: Convenience method that wraps setWord. Used to increment the priority of existing words and add new words. # Note: Generally, this method can be used instead of addWord. + def incWord(self,wordText): self.setWord(wordText,priority = 1, sumFlag = 1) - # setWordPriority subroutine. # # self : instance of self @@ -183,6 +172,7 @@ # Returns: None # # Purpose: Convenience method that wraps setWord. Sets existing words to priority or adds new words with priority = priority + def setWordPriority(self,wordText,priority): self.setWord(wordText,priority = priority) @@ -196,16 +186,13 @@ # # Purpose: Given a word, it returns the class Letter node that corresponds to the word. Used mostly in prep for a call to # getPrediction() + def findWordNode(self,wordText): #returns class Letter that represents the last letter in the word - cur = self.rootNode # start at the root - for ch in wordText: # move through each letter in the word if cur.children.has_key(ch): # if the next letter exists, make cur equal that letter and loop cur = cur.children[ch] - else: - return None # return None if letter not found - + else: return None # return None if letter not found return cur # return cur, as this points to the last letter if we got this far @@ -217,30 +204,24 @@ # Returns: Int representing the word's priority or 0 if not found. # # Purpose: Returns the priority of the given word + def findWordPriority(self,wordText): cur = self.findWordNode(wordText) # find the class Letter node that corresponds to this word - if cur: - return cur.priority # if it was found, return it's priority - else: - return 0 # else, return 0, meaning word not found - + if cur: return cur.priority # if it was found, return it's priority + else: return 0 # else, return 0, meaning word not found def printTree(self, current=None): letters = [] if current is None: current = self.rootNode - for l, letter in current.children.iteritems(): letters.append(str(letter)) if letter.children != {}: m = self.printTree(letter) letters.append(m) - return letters - - # getPrediction subroutine. # # self : instance of self @@ -340,17 +321,36 @@ self.tree = LetterTree # Instantiate a new LetterTree. # TODO: make name of word file an argument. - - - - - self.parent = parent # Save parent for later use in passing KeyEvents + self.parent = parent # Save parent for later use in passing KeyEvents + self.cur = self.tree.rootNode # self.cur is a short cut placeholder for typing consecutive chars + # It may be vestigal + self.keyHook = keyHook # Save the keyHook passed in + ExpandoTextCtrl._wrapLine = self._wrapLine + - self.cur = self.tree.rootNode # self.cur is a short cut placeholder for typing consecutive chars - # It may be vestigal - - self.keyHook = keyHook # Save the keyHook passed in - + def _wrapLine(self, line, dc, width): + # Estimate where the control will wrap the lines and + # return the count of extra lines needed. + # Re writes ExpandoTextCtrl _wrapLine function + pte = dc.GetPartialTextExtents(line) + width -= wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X) + idx = 0 + start = 0 + count = 0 + spc = -1 + while idx < len(pte): + if line[idx] == ' ': debug((line)); spc = idx + if pte[idx] - start > width: + # we've reached the max width, add a new line + count += 1 + # did we see a space? if so restart the count at that pos + if spc != -1: + idx = spc + 1 + spc = -1 + start = pte[idx] + else: + idx += 1 + return count # findWord subroutine. # @@ -368,7 +368,7 @@ # # Purpose: This function is generally used to figure out the beginning of the # current word being typed, for later use in a LetterTree.getPrediction() - def findWord(self,insert,st): + def findWord(self, insert, st): # Good luck reading this one. Basically, I started out with an idea, and fiddled with the # constants as best I could until it worked. It's not a piece of work of which I'm too @@ -404,29 +404,21 @@ self.parent.OnChar(event) return - - # This bit converts the GetKeyCode() return (int) to a char if it's in a certain range asciiKey = "" if (event.GetKeyCode() < 256) and (event.GetKeyCode() > 19): asciiKey = chr(event.GetKeyCode()) - - if asciiKey == "": # If we didn't convert it to a char, then process based on the int GetKeyCodes - - if event.GetKeyCode() == wx.WXK_TAB: # We want to hook tabs to allow the user to signify acceptance of a - # predicted word. + if asciiKey == "": # If we didn't convert it to a char, then process based on the int GetKeyCodes + if event.GetKeyCode() == wx.WXK_TAB: # We want to hook tabs to allow the user to signify acceptance of a + # predicted word. # Handle Tab key - - fromPos = toPos = 0 # get the current selection range + fromPos = toPos = 0 # get the current selection range (fromPos,toPos) = self.GetSelection() - - if (toPos - fromPos) == 0: # if there is no selection, pass tab on + if (toPos - fromPos) == 0: # if there is no selection, pass tab on self.parent.OnChar(event) return - else: # This means at least one char is selected - self.SetInsertionPoint(toPos) # move the insertion point to the end of the selection self.SetSelection(toPos,toPos) # and set the selection to no chars # The prediction, if any, had been inserted into the text earlier, so @@ -441,12 +433,9 @@ st += '
' return - elif event.GetKeyCode() == wx.WXK_RETURN: # We want to hook returns, so that we can update the word list - st = self.GetValue() # Grab the text from the control newSt = "" # Init a buffer - # This block of code, by popular demand, changes the behavior of the control to ignore any prediction that # hasn't been "accepted" when the enter key is struck. (startSel,endSel) = self.GetSelection() # get the curren selection @@ -475,7 +464,6 @@ newSt += " " else: newSt += ch - # Now that we've got a string of just letter sequences (words) and spaces # split it and to a LetterTree.incWord on the lowercase version of it. # Reminder: incWord will increment the priority of existing words and add @@ -502,23 +490,17 @@ self.SetInsertionPoint(startSel) self.parent.OnChar(event) return - - else: # Handle any other non-ascii events by calling parent's OnChar() self.parent.OnChar(event) #Call super.OnChar to get default behavior return - - elif asciiKey in string.letters: # This is the real meat and potatoes of predTextCtrl. This is where most of the # wx.TextCtrl logic is changed. - (startSel,endSel) = self.GetSelection() # get the curren selection st = self.GetValue() # and the text in the control front = st[:startSel] # Slice off the text to the front of where we are back = st[endSel:] # Slice off the text to the end from where we are - st = front + asciiKey + back # This expression creates a string that will insert the # typed character (asciiKey is generated at the # beginning of OnChar()) into the text. If there @@ -528,9 +510,7 @@ insert = startSel + 1 # creates an int that denotes where the new InsertionPoint # should be. - curWord = "" # Assume there's a problem with finding the curWord - if (len(back) == 0) or (back[0] not in string.letters): # We should only insert a prediction if we are typing # at the end of a word, not in the middle. There are # three cases: we are typing at the end of the string or @@ -611,11 +591,8 @@ # reach. return # Done! Do NOT pass the event on at this point, because it's all done. - - else: # Handle every other non-letter ascii (e.g. semicolon) by passing the event on. self.parent.OnChar(event) #Call super.OnChar to get default behavior return - # End of class predTextCtrl! diff -r 394ebb3b6a0f -r dcf4fbe09b70 orpg/tools/settings.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/orpg/tools/settings.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,124 @@ +import os + +from orpg.tools.orpg_log import logger +from orpg.tools.validate import validate +from orpg.orpg_xml import xml +from orpg.orpgCore import component +from orpg.dirpath import dir_struct + +class Settings: + def __init__(self): + self.xml = component.get("xml") + self.changes = [] + validate.config_file("settings.xml","default_settings.xml") + self.filename = dir_struct["user"] + "settings.xml" + temp_file = open(self.filename) + txt = temp_file.read() + temp_file.close() + + self.xml_dom = xml.parseXml(txt) + + if self.xml_dom is None: self.rebuildSettings() + self.xml_dom = self.xml_dom._get_documentElement() + + def rebuildSettings(self): + logger.info("Settings file has be corrupted, rebuilding settings.", True) + try: os.remove(self.filename) + except: pass + + validate.config_file("settings.xml","default_settings.xml") + temp_file = open(self.filename) + txt = temp_file.read() + temp_file.close() + self.xml_dom = xml.parseXml(txt) + + def get_setting(self, name): ##Depricated + return self.get(name) + + def get(self, name): + try: return self.xml_dom.getElementsByTagName(name)[0].getAttribute("value") + except: return 0 + + def get_setting_keys(self): ##Depricated + return self.get_keys() + + def get_keys(self): + keys = [] + tabs = self.xml_dom.getElementsByTagName("tab") + for i in xrange(0, len(tabs)): + if tabs[i].getAttribute("type") == 'grid': + children = tabs[i]._get_childNodes() + for c in children: keys.append(c._get_tagName()) + return keys + + def set_setting(self, name, value): ##Depricated + self.change(name, value) + + def change(self, name, value): + self.xml_dom.getElementsByTagName(name)[0].setAttribute("value", value) + + def add_setting(self, tab, setting, value, options, help): ##Depricated + return self.add(tab, setting, value, options, help) + + def add(self, tab, setting, value, options, help): + if len(self.xml_dom.getElementsByTagName(setting)) > 0: return False + tabs = self.xml_dom.getElementsByTagName("tab") + newsetting = xml.parseXml('<' + setting + ' value="' + value + '" options="' + + options + '" help="' + help + '" />')._get_documentElement() + for i in xrange(0, len(tabs)): + if tabs[i].getAttribute("name") == tab and tabs[i].getAttribute("type") == 'grid': + tabs[i].appendChild(newsetting) + return True + return False + + def add_tab(self, parent, tabname, tabtype): + tab_xml = '' + else: tab_xml += 'name="' + tabname + '" type="' + tabtype + '">' + newtab = xml.parseXml(tab_xml)._get_documentElement() + if parent != None: + tabs = self.xml_dom.getElementsByTagName("tab") + for i in xrange(0, len(tabs)): + if tabs[i].getAttribute("name") == parent and tabs[i].getAttribute("type") == 'tab': + children = tabs[i]._get_childNodes() + for c in children: + if c.getAttribute("name") == tabname: return False + tabs[i].appendChild(newtab) + return True + else: + children = self.xml_dom._get_childNodes() + for c in children: + if c.getAttribute("name") == tabname: return False + self.xml_dom.appendChild(newtab) + return True + return False + + def updateIni(self): + defaultFile = orpg.dirpath.dir_struct['template'] + 'default_settings.xml' + temp_file = open(defaultFile) + txt = temp_file.read() + temp_file.close() + default_dom = xml.parseXml(txt)._get_documentElement() + for child in default_dom.getChildren(): + if child._get_tagName() == 'tab' and child.hasChildNodes(): self.proccessChildren(child) + default_dom.unlink() + + def proccessChildren(self, dom, parent=None): + if dom._get_tagName() == 'tab': + self.add_tab(parent, dom.getAttribute("name"), dom.getAttribute("type")) + + for child in dom.getChildren(): + if child._get_tagName() == 'tab' and child.hasChildNodes(): + self.proccessChildren(child, dom.getAttribute("name")) + else: + self.add_setting(dom.getAttribute("name"), child._get_tagName(), + child.getAttribute("value"), child.getAttribute("options"), + child.getAttribute("help")) + + def save(self): + temp_file = open(self.filename, "w") + temp_file.write(xml.toxml(self.xml_dom,1)) + temp_file.close() + +settings = Settings() +component.add('settings', settings) diff -r 394ebb3b6a0f -r dcf4fbe09b70 plugins/xxmouse-zoom.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/xxmouse-zoom.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,37 @@ +import os +import orpg.pluginhandler +try: from orpg.orpgCore import component +except: from orpg.orpgCore import open_rpg +import wx + +class Plugin(orpg.pluginhandler.PluginHandler): + # Initialization subroutine. + # + # !self : instance of self + # !openrpg : instance of the the base openrpg control + def __init__(self, plugindb, parent): + orpg.pluginhandler.PluginHandler.__init__(self, plugindb, parent) + + # The Following code should be edited to contain the proper information + self.name = 'Mouse Zoom' + self.author = 'Tyler Starke (Prof. Ebral)' + self.help = 'This plugin allows users to zoom their map with super ease. Hold Ctrl or Cmd and scroll your mouse ' + self.help += 'wheel and the map will zoom in or out. And FAST too! \n' + self.help += 'This plugin is designed for Grumpy Goblin and Ornery Orc.' + + def plugin_enabled(self): + try: self.canvas = component.get('map').canvas + except: self.canvas = open_rpg.get_component('map').canvas + self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.MouseWheel) + + def MouseWheel(self, evt): + if evt.CmdDown(): + print evt.GetWheelRotation() + if evt.GetWheelRotation() > 0: self.canvas.on_zoom_in(None) + elif evt.GetWheelRotation() < 0: self.canvas.on_zoom_out(None) + else: pass + else: self.canvas.on_scroll(evt) + + def plugin_disabled(self): + self.canvas.Disconnect(-1, -1, wx.wxEVT_MOUSEWHEEL) + diff -r 394ebb3b6a0f -r dcf4fbe09b70 start_server.py --- a/start_server.py Fri Sep 25 20:47:16 2009 -0500 +++ b/start_server.py Tue Nov 10 14:11:28 2009 -0600 @@ -7,6 +7,7 @@ import getopt import traceback + # Simple usuage text for request via help or command line errors def usage( retValue ): print "\nUsage: \n[-d --directory directory] - Directory of where to load config files\n" + \ @@ -21,26 +22,20 @@ "will be prompted for information.\n\n" sys.exit( retValue ) -HG = os.environ["HG"] - -import pyver -pyver.checkPyVersion() - -os.system(HG + ' pull "http://hg.assembla.com/traipse"') -os.system(HG + ' update') import orpg.networking.mplay_server import orpg.networking.meta_server_lib -if __name__ == '__main__': +print __name__ +if __name__ == '__main__' or __name__ == 'start_server': gc.set_debug(gc.DEBUG_UNCOLLECTABLE) gc.enable() orpg_server = orpg.networking.mplay_server.mplay_server() lobby_boot_pwd = orpg_server.boot_pwd server_directory = orpg_server.userPath - server_reg = orpg_server.reg - server_name = orpg_server.name + server_reg = orpg_server.be_registered + server_name = orpg_server.serverName manualStart = False # See if we have command line arguments in need of processing @@ -75,8 +70,8 @@ # start server! - orpg_server.name = server_name - orpg_server.reg = server_reg + orpg_server.serverName = server_name + orpg_server.be_registered = server_reg orpg_server.boot_pwd = lobby_boot_pwd orpg_server.userPath = server_directory orpg_server.remoteadmin = 1 diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/default_manifest.xml --- a/upmana/default_manifest.xml Fri Sep 25 20:47:16 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ - - - http://hg.assembla.com/traipse - http://hg.assembla.com/traipse - - - - - repo-Traipse - - - - diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/default_upmana.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/default_upmana.xml Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,8 @@ + + + http://hg.assembla.com/traipse + + + http://hg.assembla.com/traipse + + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/ignorelist.txt diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/manifest.py --- a/upmana/manifest.py Fri Sep 25 20:47:16 2009 -0500 +++ b/upmana/manifest.py Tue Nov 10 14:11:28 2009 -0600 @@ -1,185 +1,214 @@ -import xmltramp +from __future__ import with_statement + from orpg.dirpath import dir_struct -import upmana.validate -from os import sep +from upmana.validate import validate +from orpg.tools.orpg_log import logger +from os import sep, getcwd from types import * -class ManifestChanges: - def __init__(self, filename="updatemana.xml"): - self.filename = dir_struct["home"] + 'upmana' + sep + filename - upmana.validate.Validate(dir_struct["home"] + 'upmana' + sep).config_file(filename,"default_manifest.xml") - self.xml_dom = self.LoadDoc() +from xml.etree.ElementTree import ElementTree, Element, parse, fromstring +from xml.etree.ElementPath import find + +class ManifestChanges(object): + etree = ElementTree() + filename = dir_struct['home'] + 'upmana' + sep + 'upmana.xml' - def GetString(self, plugname, strname, defaultval, verbose=0): + def __new__(cls, *args, **kwargs): + it = cls.__dict__.get("__it__") + if it is not None: + return it + cls.__it__ = it = object.__new__(cls) + it._init() + return it + + def _init(self): + validate.config_file('upmana.xml', "default_upmana.xml") + self.LoadDoc() + + def PluginChildren(self, plugname): + plugin = self.etree.find(plugname) + children = plugin.getchildren() + nodes = [] + for child in children: + nodes.append(child.tag) + return nodes + + def GetString(self, plugname, strname, defaultval="", verbose=False): strname = self.safe(strname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == strname: - #str() on this to make sure it's ASCII, not unicode, since orpg can't handle unicode. - if verbose: print "successfully found the value" - if len(child): return str( self.normal(child[0]) ) - else: return "" - else: - if verbose: - print "manifest: no value has been stored for " + strname + " in " + plugname + " so the default has been returned" + plugin = self.etree.find(plugname) + if plugin is None or plugin.find(strname) is None: + msg = ["plugindb: no value has been stored for", strname, "in", + plugname, "so the default has been returned"] return defaultval + return self.normal(plugin.find(strname).text or '') + + def DelString(self, plugname, strname): + strname = self.safe(strname) + plugin = self.etree.find(plugname) + plugin.remove(plugin.find(strname)) + self.SaveDoc() def SetString(self, plugname, strname, val): - #Set Node, - #Set Setting, val = self.safe(val) strname = self.safe(strname) - for plugin in self.xml_dom: - ##this isn't absolutely necessary, but it saves the trouble of sending a parsed object instead of a simple string. - if plugname == plugin._name: - plugin[strname] = val - plugin[strname]._attrs["type"] = "string" - self.SaveDoc() - return "found plugin" - else: - self.xml_dom[plugname] = xmltramp.parse("<" + strname + " type=\"string\">" + val + "") - self.SaveDoc() - return "added plugin" - + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + str_el = plugin.find(strname) + if str_el is None: + str_el = Element(strname) + str_el.set('type', 'str') + plugin.append(str_el) + str_el.text = val + self.SaveDoc() def FetchList(self, parent): retlist = [] - if not len(parent): return [] - for litem in parent[0]._dir: - if len(litem): - if litem._attrs["type"] == "int": retlist += [int(litem[0])] - elif litem._attrs["type"] == "long": retlist += [long(litem[0])] - elif litem._attrs["type"] == "float": retlist += [float(litem[0])] - elif litem._attrs["type"] == "list": retlist += [self.FetchList(litem)] - elif litem._attrs["type"] == "dict": retlist += [self.FetchDict(litem)] - else: retlist += [str( self.normal(litem[0]) )] - else: retlist += [""] + for litem in parent.findall('lobject'): + if litem.get('type') == 'int': retlist.append(int(litem.text)) + if litem.get('type') == 'bool': retlist.append(litem.text == 'True') + elif litem.get('type') == 'float': retlist.append(float(litem.text)) + elif litem.get('type') == 'list': retlist.append(self.FetchList(litem)) + elif litem.get('type') == 'dict': retlist.append(self.FetchDict(litem)) + else: retlist.append(str(self.normal(litem.text))) + return retlist + + def GetList(self, plugname, listname, defaultval=list(), verbose=False): + listname = self.safe(listname) + plugin = self.etree.find(plugname) + if plugin is None or plugin.find(listname) is None: + msg = ["plugindb: no value has been stored for", listname, "in", + plugname, "so the default has been returned"] + return defaultval + retlist = self.FetchList(plugin.find(listname)) return retlist - def GetList(self, plugname, listname, defaultval, verbose=0): - listname = self.safe(listname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == listname and child._attrs["type"] == "list": - retlist = self.FetchList(child) - if verbose: print "successfully found the value" - return retlist - else: - if verbose: - print "plugindb: no value has been stored for " + listname + " in " + plugname + " so the default has been returned" - return defaultval - def BuildList(self, val): - listerine = "" + list_el = Element('list') for item in val: - if isinstance(item, basestring):#it's a string - listerine += "" + self.safe(item) + "" - elif isinstance(item, IntType):#it's an int - listerine += "" + str(item) + "" - elif isinstance(item, FloatType):#it's a float - listerine += "" + str(item) + "" - elif isinstance(item, LongType):#it's a long - listerine += "" + str(item) + "" - elif isinstance(item, ListType):#it's a list - listerine += "" + self.BuildList(item) + "" - elif isinstance(item, DictType):#it's a dictionary - listerine += "" + self.BuildDict(item) + "" - else: return "type unknown" - listerine += "" - return listerine + i = Element('lobject') + if isinstance(item, bool): + i.set('type', 'bool') + i.text = str(item) + elif isinstance(item, int):#it's an int + i.set('type', 'int') + i.text = str(item) + elif isinstance(item, float):#it's a float + i.set('type', 'float') + i.text = str(item) + elif isinstance(item, (list, tuple)):#it's a list + i.set('type', 'list') + i.append(self.BuildList(item)) + elif isinstance(item, dict):#it's a dictionary + i.set('type', 'dict') + i.append(self.BuildDict(item)) + else: + i.set('type', 'str') + i.text = self.safe(item) + list_el.append(i) + return list_el def SetList(self, plugname, listname, val): listname = self.safe(listname) - list = xmltramp.parse(self.BuildList(val)) - for plugin in self.xml_dom: - if plugname == plugin._name: - plugin[listname] = list - plugin[listname]._attrs["type"] = "list" - self.SaveDoc() - return "found plugin" + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + list_el = plugin.find(listname) + if list_el is None: + list_el = Element(listname) + list_el.set('type', 'list') + plugin.append(list_el) else: - self.xml_dom[plugname] = xmltramp.parse("<" + listname + ">") - self.xml_dom[plugname][listname] = list - self.xml_dom[plugname][listname]._attrs["type"] = "list" - self.SaveDoc() - return "added plugin" + list_el.remove(list_el.find('list')) + list_el.append(self.BuildList(val)) + self.SaveDoc() def BuildDict(self, val): - dictator = "" - for item in val.keys(): - if isinstance(val[item], basestring): - dictator += "" + self.safe(val[item]) + "" - elif isinstance(val[item], IntType):#it's an int - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], FloatType):#it's a float - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], LongType):#it's a long - dictator += "" + str(val[item]) + "" - elif isinstance(val[item], DictType):#it's a dictionary - dictator += "" + self.BuildDict(val[item]) + "" - elif isinstance(val[item], ListType):#it's a list - dictator += "" + self.BuildList(val[item]) + "" - else: return str(val[item]) + ": type unknown" - dictator += "" - return dictator + dict_el = Element('dict') + for key, item in val.items(): + i = Element('dobject') + if isinstance(item, bool): + i.set('type', 'bool') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, int):#it's an int + i.set('type', 'int') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, float):#it's a float + i.set('type', 'float') + i.set('name', self.safe(key)) + i.text = str(item) + elif isinstance(item, (list, tuple)):#it's a list + i.set('type', 'list') + i.set('name', self.safe(key)) + i.append(self.BuildList(item)) + elif isinstance(item, dict):#it's a dictionary + i.set('type', 'dict') + i.set('name', self.safe(key)) + i.append(self.BuildDict(item)) + else: + i.set('type', 'str') + i.set('name', self.safe(key)) + i.text = self.safe(item) + dict_el.append(i) + return dict_el - def SetDict(self, plugname, dictname, val, file="plugindb.xml"): + def SetDict(self, plugname, dictname, val): dictname = self.safe(dictname) - dict = xmltramp.parse(self.BuildDict(val)) - for plugin in self.xml_dom: - if plugname == plugin._name: - plugin[dictname] = dict - plugin[dictname]._attrs["type"] = "dict" - self.SaveDoc() - return "found plugin" + plugin = self.etree.find(plugname) + if plugin is None: + plugin = Element(plugname) + self.etree.getroot().append(plugin) + dict_el = plugin.find(dictname) + if dict_el is None: + dict_el = Element(dictname) + dict_el.set('type', 'dict') + plugin.append(dict_el) else: - self.xml_dom[plugname] = xmltramp.parse("<" + dictname + ">") - self.xml_dom[plugname][dictname] = dict - self.xml_dom[plugname][dictname]._attrs["type"] = "dict" - self.SaveDoc() - return "added plugin" + dict_el.remove(list_el.find('dict')) + dict_el.append(self.BuildDict(val)) + self.SaveDoc() def FetchDict(self, parent): retdict = {} - if not len(parent): return {} - for ditem in parent[0]._dir: - if len(ditem): - ditem._attrs["name"] = self.normal(ditem._attrs["name"]) - if ditem._attrs["type"] == "int": retdict[ditem._attrs["name"]] = int(ditem[0]) - elif ditem._attrs["type"] == "long": retdict[ditem._attrs["name"]] = long(ditem[0]) - elif ditem._attrs["type"] == "float": retdict[ditem._attrs["name"]] = float(ditem[0]) - elif ditem._attrs["type"] == "list": retdict[ditem._attrs["name"]] = self.FetchList(ditem) - elif ditem._attrs["type"] == "dict": retdict[ditem._attrs["name"]] = self.FetchDict(ditem) - else: retdict[ditem._attrs["name"]] = str( self.normal(ditem[0]) ) - else: retdict[ditem._attrs["name"]] = "" + for ditem in parent.findall('dobject'): + key = self.normal(ditem.get('name')) + if ditem.get('type') == 'int': value = int(ditem.text) + elif ditem.get('type') == 'bool': value = ditem.text == 'True' + elif ditem.get('type') == 'float': value = float(ditem.text) + elif ditem.get('type') == 'list': value = self.FetchList(ditem) + elif ditem.get('type') == 'dict': value = self.FetchDict(ditem) + else: value = str(self.normal(ditem[0])) + retdict[key] = value return retdict - def GetDict(self, plugname, dictname, defaultval, verbose=0): + def GetDict(self, plugname, dictname, defaultval=dict(), verbose=False): dictname = self.safe(dictname) - for plugin in self.xml_dom: - if plugname == plugin._name: - for child in plugin._dir: - if child._name == dictname and child._attrs["type"] == "dict": return self.FetchDict(child) - else: - if verbose: - print "plugindb: no value has been stored for " + dictname + " in " + plugname + " so the default has been returned" + plugin = self.etree.find(plugname) + if plugin is None or plugin.find(dictname) is None: + msg = ["plugindb: no value has been stored for", dictname, "in", + plugname, "so the default has been returned"] return defaultval + retdict = self.FetchDict(plugin.find(dictname)) + return retdict def safe(self, string): - return string.replace("<", "$$lt$$").replace(">", "$$gt$$").replace("&","$$amp$$").replace('"',"$$quote$$") + return string.replace("<", "$$lt$$").replace(">", "$$gt$$")\ + .replace("&","$$amp$$").replace('"',"$$quote$$") def normal(self, string): - return string.replace("$$lt$$", "<").replace("$$gt$$", ">").replace("$$amp$$","&").replace("$$quote$$",'"') + return string.replace("$$lt$$", "<").replace("$$gt$$", ">")\ + .replace("$$amp$$","&").replace("$$quote$$",'"') def SaveDoc(self): - f = open(self.filename, "w") - f.write(self.xml_dom.__repr__(1, 1)) - f.close() + with open(self.filename, "w") as f: + self.etree.write(f) def LoadDoc(self): - xml_file = open(self.filename) - manifest = xml_file.read() - xml_file.close() - return xmltramp.parse(manifest) + with open(self.filename) as f: + self.etree.parse(f) + +manifest = ManifestChanges() diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/__init__.py diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/__version__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/__version__.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,2 @@ +# this file is autogenerated by setup.py +version = "1.3.1" diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/ancestor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/ancestor.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,85 @@ +# ancestor.py - generic DAG ancestor algorithm for mercurial +# +# Copyright 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import heapq + +def ancestor(a, b, pfunc): + """ + return the least common ancestor of nodes a and b or None if there + is no such ancestor. + + pfunc must return a list of parent vertices + """ + + if a == b: + return a + + # find depth from root of all ancestors + parentcache = {} + visit = [a, b] + depth = {} + while visit: + vertex = visit[-1] + pl = pfunc(vertex) + parentcache[vertex] = pl + if not pl: + depth[vertex] = 0 + visit.pop() + else: + for p in pl: + if p == a or p == b: # did we find a or b as a parent? + return p # we're done + if p not in depth: + visit.append(p) + if visit[-1] == vertex: + depth[vertex] = min([depth[p] for p in pl]) - 1 + visit.pop() + + # traverse ancestors in order of decreasing distance from root + def ancestors(vertex): + h = [(depth[vertex], vertex)] + seen = set() + while h: + d, n = heapq.heappop(h) + if n not in seen: + seen.add(n) + yield (d, n) + for p in parentcache[n]: + heapq.heappush(h, (depth[p], p)) + + def generations(vertex): + sg, s = None, set() + for g, v in ancestors(vertex): + if g != sg: + if sg: + yield sg, s + sg, s = g, set((v,)) + else: + s.add(v) + yield sg, s + + x = generations(a) + y = generations(b) + gx = x.next() + gy = y.next() + + # increment each ancestor list until it is closer to root than + # the other, or they match + try: + while 1: + if gx[0] == gy[0]: + for v in gx[1]: + if v in gy[1]: + return v + gy = y.next() + gx = x.next() + elif gx[0] > gy[0]: + gy = y.next() + else: + gx = x.next() + except StopIteration: + return None diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/archival.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/archival.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,226 @@ +# archival.py - revision archival for mercurial +# +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +from node import hex +import util +import cStringIO, os, stat, tarfile, time, zipfile +import zlib, gzip + +def tidyprefix(dest, prefix, suffixes): + '''choose prefix to use for names in archive. make sure prefix is + safe for consumers.''' + + if prefix: + prefix = util.normpath(prefix) + else: + if not isinstance(dest, str): + raise ValueError('dest must be string if no prefix') + prefix = os.path.basename(dest) + lower = prefix.lower() + for sfx in suffixes: + if lower.endswith(sfx): + prefix = prefix[:-len(sfx)] + break + lpfx = os.path.normpath(util.localpath(prefix)) + prefix = util.pconvert(lpfx) + if not prefix.endswith('/'): + prefix += '/' + if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix: + raise util.Abort(_('archive prefix contains illegal components')) + return prefix + +class tarit(object): + '''write archive to tar file or stream. can write uncompressed, + or compress with gzip or bzip2.''' + + class GzipFileWithTime(gzip.GzipFile): + + def __init__(self, *args, **kw): + timestamp = None + if 'timestamp' in kw: + timestamp = kw.pop('timestamp') + if timestamp is None: + self.timestamp = time.time() + else: + self.timestamp = timestamp + gzip.GzipFile.__init__(self, *args, **kw) + + def _write_gzip_header(self): + self.fileobj.write('\037\213') # magic header + self.fileobj.write('\010') # compression method + # Python 2.6 deprecates self.filename + fname = getattr(self, 'name', None) or self.filename + flags = 0 + if fname: + flags = gzip.FNAME + self.fileobj.write(chr(flags)) + gzip.write32u(self.fileobj, long(self.timestamp)) + self.fileobj.write('\002') + self.fileobj.write('\377') + if fname: + self.fileobj.write(fname + '\000') + + def __init__(self, dest, prefix, mtime, kind=''): + self.prefix = tidyprefix(dest, prefix, ['.tar', '.tar.bz2', '.tar.gz', + '.tgz', '.tbz2']) + self.mtime = mtime + + def taropen(name, mode, fileobj=None): + if kind == 'gz': + mode = mode[0] + if not fileobj: + fileobj = open(name, mode + 'b') + gzfileobj = self.GzipFileWithTime(name, mode + 'b', + zlib.Z_BEST_COMPRESSION, + fileobj, timestamp=mtime) + return tarfile.TarFile.taropen(name, mode, gzfileobj) + else: + return tarfile.open(name, mode + kind, fileobj) + + if isinstance(dest, str): + self.z = taropen(dest, mode='w:') + else: + # Python 2.5-2.5.1 have a regression that requires a name arg + self.z = taropen(name='', mode='w|', fileobj=dest) + + def addfile(self, name, mode, islink, data): + i = tarfile.TarInfo(self.prefix + name) + i.mtime = self.mtime + i.size = len(data) + if islink: + i.type = tarfile.SYMTYPE + i.mode = 0777 + i.linkname = data + data = None + i.size = 0 + else: + i.mode = mode + data = cStringIO.StringIO(data) + self.z.addfile(i, data) + + def done(self): + self.z.close() + +class tellable(object): + '''provide tell method for zipfile.ZipFile when writing to http + response file object.''' + + def __init__(self, fp): + self.fp = fp + self.offset = 0 + + def __getattr__(self, key): + return getattr(self.fp, key) + + def write(self, s): + self.fp.write(s) + self.offset += len(s) + + def tell(self): + return self.offset + +class zipit(object): + '''write archive to zip file or stream. can write uncompressed, + or compressed with deflate.''' + + def __init__(self, dest, prefix, mtime, compress=True): + self.prefix = tidyprefix(dest, prefix, ('.zip',)) + if not isinstance(dest, str): + try: + dest.tell() + except (AttributeError, IOError): + dest = tellable(dest) + self.z = zipfile.ZipFile(dest, 'w', + compress and zipfile.ZIP_DEFLATED or + zipfile.ZIP_STORED) + self.date_time = time.gmtime(mtime)[:6] + + def addfile(self, name, mode, islink, data): + i = zipfile.ZipInfo(self.prefix + name, self.date_time) + i.compress_type = self.z.compression + # unzip will not honor unix file modes unless file creator is + # set to unix (id 3). + i.create_system = 3 + ftype = stat.S_IFREG + if islink: + mode = 0777 + ftype = stat.S_IFLNK + i.external_attr = (mode | ftype) << 16L + self.z.writestr(i, data) + + def done(self): + self.z.close() + +class fileit(object): + '''write archive as files in directory.''' + + def __init__(self, name, prefix, mtime): + if prefix: + raise util.Abort(_('cannot give prefix when archiving to files')) + self.basedir = name + self.opener = util.opener(self.basedir) + + def addfile(self, name, mode, islink, data): + if islink: + self.opener.symlink(data, name) + return + f = self.opener(name, "w", atomictemp=True) + f.write(data) + f.rename() + destfile = os.path.join(self.basedir, name) + os.chmod(destfile, mode) + + def done(self): + pass + +archivers = { + 'files': fileit, + 'tar': tarit, + 'tbz2': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'bz2'), + 'tgz': lambda name, prefix, mtime: tarit(name, prefix, mtime, 'gz'), + 'uzip': lambda name, prefix, mtime: zipit(name, prefix, mtime, False), + 'zip': zipit, + } + +def archive(repo, dest, node, kind, decode=True, matchfn=None, + prefix=None, mtime=None): + '''create archive of repo as it was at node. + + dest can be name of directory, name of archive file, or file + object to write archive to. + + kind is type of archive to create. + + decode tells whether to put files through decode filters from + hgrc. + + matchfn is function to filter names of files to write to archive. + + prefix is name of path to put before every archive member.''' + + def write(name, mode, islink, getdata): + if matchfn and not matchfn(name): return + data = getdata() + if decode: + data = repo.wwritedata(name, data) + archiver.addfile(name, mode, islink, data) + + if kind not in archivers: + raise util.Abort(_("unknown archive type '%s'") % kind) + + ctx = repo[node] + archiver = archivers[kind](dest, prefix, mtime or ctx.date()[0]) + + if repo.ui.configbool("ui", "archivemeta", True): + write('.hg_archival.txt', 0644, False, + lambda: 'repo: %s\nnode: %s\n' % ( + hex(repo.changelog.node(0)), hex(node))) + for f in ctx: + ff = ctx.flags(f) + write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data) + archiver.done() diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/base85.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/base85.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,74 @@ +# base85.py: pure python base85 codec +# +# Copyright (C) 2009 Brendan Cully +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import struct + +_b85chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "abcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" +_b85chars2 = [(a + b) for a in _b85chars for b in _b85chars] +_b85dec = {} + +def _mkb85dec(): + for i, c in enumerate(_b85chars): + _b85dec[c] = i + +def b85encode(text, pad=False): + """encode text in base85 format""" + l = len(text) + r = l % 4 + if r: + text += '\0' * (4 - r) + longs = len(text) >> 2 + words = struct.unpack('>%dL' % (longs), text) + + out = ''.join(_b85chars[(word / 52200625) % 85] + + _b85chars2[(word / 7225) % 7225] + + _b85chars2[word % 7225] + for word in words) + + if pad: + return out + + # Trim padding + olen = l % 4 + if olen: + olen += 1 + olen += l / 4 * 5 + return out[:olen] + +def b85decode(text): + """decode base85-encoded text""" + if not _b85dec: + _mkb85dec() + + l = len(text) + out = [] + for i in range(0, len(text), 5): + chunk = text[i:i+5] + acc = 0 + for j, c in enumerate(chunk): + try: + acc = acc * 85 + _b85dec[c] + except KeyError: + raise TypeError('Bad base85 character at byte %d' % (i + j)) + if acc > 4294967295: + raise OverflowError('Base85 overflow in hunk starting at byte %d' % i) + out.append(acc) + + # Pad final chunk if necessary + cl = l % 5 + if cl: + acc *= 85 ** (5 - cl) + if cl > 1: + acc += 0xffffff >> (cl - 2) * 8 + out[-1] = acc + + out = struct.pack('>%dL' % (len(out)), *out) + if cl: + out = out[:-(5 - cl)] + + return out diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/bdiff.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/bdiff.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,76 @@ +# bdiff.py - Python implementation of bdiff.c +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import struct, difflib + +def splitnewlines(text): + '''like str.splitlines, but only split on newlines.''' + lines = [l + '\n' for l in text.split('\n')] + if lines: + if lines[-1] == '\n': + lines.pop() + else: + lines[-1] = lines[-1][:-1] + return lines + +def _normalizeblocks(a, b, blocks): + prev = None + for curr in blocks: + if prev is None: + prev = curr + continue + shift = 0 + + a1, b1, l1 = prev + a1end = a1 + l1 + b1end = b1 + l1 + + a2, b2, l2 = curr + a2end = a2 + l2 + b2end = b2 + l2 + if a1end == a2: + while a1end+shift < a2end and a[a1end+shift] == b[b1end+shift]: + shift += 1 + elif b1end == b2: + while b1end+shift < b2end and a[a1end+shift] == b[b1end+shift]: + shift += 1 + yield a1, b1, l1+shift + prev = a2+shift, b2+shift, l2-shift + yield prev + +def bdiff(a, b): + a = str(a).splitlines(True) + b = str(b).splitlines(True) + + if not a: + s = "".join(b) + return s and (struct.pack(">lll", 0, 0, len(s)) + s) + + bin = [] + p = [0] + for i in a: p.append(p[-1] + len(i)) + + d = difflib.SequenceMatcher(None, a, b).get_matching_blocks() + d = _normalizeblocks(a, b, d) + la = 0 + lb = 0 + for am, bm, size in d: + s = "".join(b[lb:bm]) + if am > la or s: + bin.append(struct.pack(">lll", p[la], p[am], len(s)) + s) + la = am + size + lb = bm + size + + return "".join(bin) + +def blocks(a, b): + an = splitnewlines(a) + bn = splitnewlines(b) + d = difflib.SequenceMatcher(None, an, bn).get_matching_blocks() + d = _normalizeblocks(an, bn, d) + return [(i, i + n, j, j + n) for (i, j, n) in d] + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/bundlerepo.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/bundlerepo.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,303 @@ +# bundlerepo.py - repository class for viewing uncompressed bundles +# +# Copyright 2006, 2007 Benoit Boissinot +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +"""Repository class for viewing uncompressed bundles. + +This provides a read-only repository interface to bundles as if they +were part of the actual repository. +""" + +from node import nullid +from i18n import _ +import os, struct, bz2, zlib, tempfile, shutil +import changegroup, util, mdiff +import localrepo, changelog, manifest, filelog, revlog, error + +class bundlerevlog(revlog.revlog): + def __init__(self, opener, indexfile, bundlefile, + linkmapper=None): + # How it works: + # to retrieve a revision, we need to know the offset of + # the revision in the bundlefile (an opened file). + # + # We store this offset in the index (start), to differentiate a + # rev in the bundle and from a rev in the revlog, we check + # len(index[r]). If the tuple is bigger than 7, it is a bundle + # (it is bigger since we store the node to which the delta is) + # + revlog.revlog.__init__(self, opener, indexfile) + self.bundlefile = bundlefile + self.basemap = {} + def chunkpositer(): + for chunk in changegroup.chunkiter(bundlefile): + pos = bundlefile.tell() + yield chunk, pos - len(chunk) + n = len(self) + prev = None + for chunk, start in chunkpositer(): + size = len(chunk) + if size < 80: + raise util.Abort(_("invalid changegroup")) + start += 80 + size -= 80 + node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80]) + if node in self.nodemap: + prev = node + continue + for p in (p1, p2): + if not p in self.nodemap: + raise error.LookupError(p1, self.indexfile, + _("unknown parent")) + if linkmapper is None: + link = n + else: + link = linkmapper(cs) + + if not prev: + prev = p1 + # start, size, full unc. size, base (unused), link, p1, p2, node + e = (revlog.offset_type(start, 0), size, -1, -1, link, + self.rev(p1), self.rev(p2), node) + self.basemap[n] = prev + self.index.insert(-1, e) + self.nodemap[node] = n + prev = node + n += 1 + + def bundle(self, rev): + """is rev from the bundle""" + if rev < 0: + return False + return rev in self.basemap + def bundlebase(self, rev): return self.basemap[rev] + def chunk(self, rev, df=None, cachelen=4096): + # Warning: in case of bundle, the diff is against bundlebase, + # not against rev - 1 + # XXX: could use some caching + if not self.bundle(rev): + return revlog.revlog.chunk(self, rev, df) + self.bundlefile.seek(self.start(rev)) + return self.bundlefile.read(self.length(rev)) + + def revdiff(self, rev1, rev2): + """return or calculate a delta between two revisions""" + if self.bundle(rev1) and self.bundle(rev2): + # hot path for bundle + revb = self.rev(self.bundlebase(rev2)) + if revb == rev1: + return self.chunk(rev2) + elif not self.bundle(rev1) and not self.bundle(rev2): + return revlog.revlog.revdiff(self, rev1, rev2) + + return mdiff.textdiff(self.revision(self.node(rev1)), + self.revision(self.node(rev2))) + + def revision(self, node): + """return an uncompressed revision of a given""" + if node == nullid: return "" + + text = None + chain = [] + iter_node = node + rev = self.rev(iter_node) + # reconstruct the revision if it is from a changegroup + while self.bundle(rev): + if self._cache and self._cache[0] == iter_node: + text = self._cache[2] + break + chain.append(rev) + iter_node = self.bundlebase(rev) + rev = self.rev(iter_node) + if text is None: + text = revlog.revlog.revision(self, iter_node) + + while chain: + delta = self.chunk(chain.pop()) + text = mdiff.patches(text, [delta]) + + p1, p2 = self.parents(node) + if node != revlog.hash(text, p1, p2): + raise error.RevlogError(_("integrity check failed on %s:%d") + % (self.datafile, self.rev(node))) + + self._cache = (node, self.rev(node), text) + return text + + def addrevision(self, text, transaction, link, p1=None, p2=None, d=None): + raise NotImplementedError + def addgroup(self, revs, linkmapper, transaction): + raise NotImplementedError + def strip(self, rev, minlink): + raise NotImplementedError + def checksize(self): + raise NotImplementedError + +class bundlechangelog(bundlerevlog, changelog.changelog): + def __init__(self, opener, bundlefile): + changelog.changelog.__init__(self, opener) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile) + +class bundlemanifest(bundlerevlog, manifest.manifest): + def __init__(self, opener, bundlefile, linkmapper): + manifest.manifest.__init__(self, opener) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, + linkmapper) + +class bundlefilelog(bundlerevlog, filelog.filelog): + def __init__(self, opener, path, bundlefile, linkmapper): + filelog.filelog.__init__(self, opener, path) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, + linkmapper) + +class bundlerepository(localrepo.localrepository): + def __init__(self, ui, path, bundlename): + self._tempparent = None + try: + localrepo.localrepository.__init__(self, ui, path) + except error.RepoError: + self._tempparent = tempfile.mkdtemp() + localrepo.instance(ui,self._tempparent,1) + localrepo.localrepository.__init__(self, ui, self._tempparent) + + if path: + self._url = 'bundle:' + path + '+' + bundlename + else: + self._url = 'bundle:' + bundlename + + self.tempfile = None + self.bundlefile = open(bundlename, "rb") + header = self.bundlefile.read(6) + if not header.startswith("HG"): + raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename) + elif not header.startswith("HG10"): + raise util.Abort(_("%s: unknown bundle version") % bundlename) + elif (header == "HG10BZ") or (header == "HG10GZ"): + fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-", + suffix=".hg10un", dir=self.path) + self.tempfile = temp + fptemp = os.fdopen(fdtemp, 'wb') + def generator(f): + if header == "HG10BZ": + zd = bz2.BZ2Decompressor() + zd.decompress("BZ") + elif header == "HG10GZ": + zd = zlib.decompressobj() + for chunk in f: + yield zd.decompress(chunk) + gen = generator(util.filechunkiter(self.bundlefile, 4096)) + + try: + fptemp.write("HG10UN") + for chunk in gen: + fptemp.write(chunk) + finally: + fptemp.close() + self.bundlefile.close() + + self.bundlefile = open(self.tempfile, "rb") + # seek right after the header + self.bundlefile.seek(6) + elif header == "HG10UN": + # nothing to do + pass + else: + raise util.Abort(_("%s: unknown bundle compression type") + % bundlename) + # dict with the mapping 'filename' -> position in the bundle + self.bundlefilespos = {} + + @util.propertycache + def changelog(self): + c = bundlechangelog(self.sopener, self.bundlefile) + self.manstart = self.bundlefile.tell() + return c + + @util.propertycache + def manifest(self): + self.bundlefile.seek(self.manstart) + m = bundlemanifest(self.sopener, self.bundlefile, self.changelog.rev) + self.filestart = self.bundlefile.tell() + return m + + @util.propertycache + def manstart(self): + self.changelog + return self.manstart + + @util.propertycache + def filestart(self): + self.manifest + return self.filestart + + def url(self): + return self._url + + def file(self, f): + if not self.bundlefilespos: + self.bundlefile.seek(self.filestart) + while 1: + chunk = changegroup.getchunk(self.bundlefile) + if not chunk: + break + self.bundlefilespos[chunk] = self.bundlefile.tell() + for c in changegroup.chunkiter(self.bundlefile): + pass + + if f[0] == '/': + f = f[1:] + if f in self.bundlefilespos: + self.bundlefile.seek(self.bundlefilespos[f]) + return bundlefilelog(self.sopener, f, self.bundlefile, + self.changelog.rev) + else: + return filelog.filelog(self.sopener, f) + + def close(self): + """Close assigned bundle file immediately.""" + self.bundlefile.close() + + def __del__(self): + bundlefile = getattr(self, 'bundlefile', None) + if bundlefile and not bundlefile.closed: + bundlefile.close() + tempfile = getattr(self, 'tempfile', None) + if tempfile is not None: + os.unlink(tempfile) + if self._tempparent: + shutil.rmtree(self._tempparent, True) + + def cancopy(self): + return False + + def getcwd(self): + return os.getcwd() # always outside the repo + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new bundle repository')) + parentpath = ui.config("bundle", "mainreporoot", "") + if parentpath: + # Try to make the full path relative so we get a nice, short URL. + # In particular, we don't want temp dir names in test outputs. + cwd = os.getcwd() + if parentpath == cwd: + parentpath = '' + else: + cwd = os.path.join(cwd,'') + if parentpath.startswith(cwd): + parentpath = parentpath[len(cwd):] + path = util.drop_scheme('file', path) + if path.startswith('bundle:'): + path = util.drop_scheme('bundle', path) + s = path.split("+", 1) + if len(s) == 1: + repopath, bundlename = parentpath, s[0] + else: + repopath, bundlename = s + else: + repopath, bundlename = parentpath, path + return bundlerepository(ui, repopath, bundlename) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/byterange.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/byterange.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,469 @@ +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +# This file is part of urlgrabber, a high-level cross-protocol url-grabber +# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko + +# $Id: byterange.py,v 1.9 2005/02/14 21:55:07 mstenner Exp $ + +import os +import stat +import urllib +import urllib2 +import email.Utils + +try: + from cStringIO import StringIO +except ImportError, msg: + from StringIO import StringIO + +class RangeError(IOError): + """Error raised when an unsatisfiable range is requested.""" + pass + +class HTTPRangeHandler(urllib2.BaseHandler): + """Handler that enables HTTP Range headers. + + This was extremely simple. The Range header is a HTTP feature to + begin with so all this class does is tell urllib2 that the + "206 Partial Content" reponse from the HTTP server is what we + expected. + + Example: + import urllib2 + import byterange + + range_handler = range.HTTPRangeHandler() + opener = urllib2.build_opener(range_handler) + + # install it + urllib2.install_opener(opener) + + # create Request and set Range header + req = urllib2.Request('http://www.python.org/') + req.header['Range'] = 'bytes=30-50' + f = urllib2.urlopen(req) + """ + + def http_error_206(self, req, fp, code, msg, hdrs): + # 206 Partial Content Response + r = urllib.addinfourl(fp, hdrs, req.get_full_url()) + r.code = code + r.msg = msg + return r + + def http_error_416(self, req, fp, code, msg, hdrs): + # HTTP's Range Not Satisfiable error + raise RangeError('Requested Range Not Satisfiable') + +class RangeableFileObject: + """File object wrapper to enable raw range handling. + This was implemented primarilary for handling range + specifications for file:// urls. This object effectively makes + a file object look like it consists only of a range of bytes in + the stream. + + Examples: + # expose 10 bytes, starting at byte position 20, from + # /etc/aliases. + >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30)) + # seek seeks within the range (to position 23 in this case) + >>> fo.seek(3) + # tell tells where your at _within the range_ (position 3 in + # this case) + >>> fo.tell() + # read EOFs if an attempt is made to read past the last + # byte in the range. the following will return only 7 bytes. + >>> fo.read(30) + """ + + def __init__(self, fo, rangetup): + """Create a RangeableFileObject. + fo -- a file like object. only the read() method need be + supported but supporting an optimized seek() is + preferable. + rangetup -- a (firstbyte,lastbyte) tuple specifying the range + to work over. + The file object provided is assumed to be at byte offset 0. + """ + self.fo = fo + (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup) + self.realpos = 0 + self._do_seek(self.firstbyte) + + def __getattr__(self, name): + """This effectively allows us to wrap at the instance level. + Any attribute not found in _this_ object will be searched for + in self.fo. This includes methods.""" + if hasattr(self.fo, name): + return getattr(self.fo, name) + raise AttributeError(name) + + def tell(self): + """Return the position within the range. + This is different from fo.seek in that position 0 is the + first byte position of the range tuple. For example, if + this object was created with a range tuple of (500,899), + tell() will return 0 when at byte position 500 of the file. + """ + return (self.realpos - self.firstbyte) + + def seek(self, offset, whence=0): + """Seek within the byte range. + Positioning is identical to that described under tell(). + """ + assert whence in (0, 1, 2) + if whence == 0: # absolute seek + realoffset = self.firstbyte + offset + elif whence == 1: # relative seek + realoffset = self.realpos + offset + elif whence == 2: # absolute from end of file + # XXX: are we raising the right Error here? + raise IOError('seek from end of file not supported.') + + # do not allow seek past lastbyte in range + if self.lastbyte and (realoffset >= self.lastbyte): + realoffset = self.lastbyte + + self._do_seek(realoffset - self.realpos) + + def read(self, size=-1): + """Read within the range. + This method will limit the size read based on the range. + """ + size = self._calc_read_size(size) + rslt = self.fo.read(size) + self.realpos += len(rslt) + return rslt + + def readline(self, size=-1): + """Read lines within the range. + This method will limit the size read based on the range. + """ + size = self._calc_read_size(size) + rslt = self.fo.readline(size) + self.realpos += len(rslt) + return rslt + + def _calc_read_size(self, size): + """Handles calculating the amount of data to read based on + the range. + """ + if self.lastbyte: + if size > -1: + if ((self.realpos + size) >= self.lastbyte): + size = (self.lastbyte - self.realpos) + else: + size = (self.lastbyte - self.realpos) + return size + + def _do_seek(self, offset): + """Seek based on whether wrapped object supports seek(). + offset is relative to the current position (self.realpos). + """ + assert offset >= 0 + if not hasattr(self.fo, 'seek'): + self._poor_mans_seek(offset) + else: + self.fo.seek(self.realpos + offset) + self.realpos += offset + + def _poor_mans_seek(self, offset): + """Seek by calling the wrapped file objects read() method. + This is used for file like objects that do not have native + seek support. The wrapped objects read() method is called + to manually seek to the desired position. + offset -- read this number of bytes from the wrapped + file object. + raise RangeError if we encounter EOF before reaching the + specified offset. + """ + pos = 0 + bufsize = 1024 + while pos < offset: + if (pos + bufsize) > offset: + bufsize = offset - pos + buf = self.fo.read(bufsize) + if len(buf) != bufsize: + raise RangeError('Requested Range Not Satisfiable') + pos += bufsize + +class FileRangeHandler(urllib2.FileHandler): + """FileHandler subclass that adds Range support. + This class handles Range headers exactly like an HTTP + server would. + """ + def open_local_file(self, req): + import mimetypes + import mimetools + host = req.get_host() + file = req.get_selector() + localfile = urllib.url2pathname(file) + stats = os.stat(localfile) + size = stats[stat.ST_SIZE] + modified = email.Utils.formatdate(stats[stat.ST_MTIME]) + mtype = mimetypes.guess_type(file)[0] + if host: + host, port = urllib.splitport(host) + if port or socket.gethostbyname(host) not in self.get_names(): + raise urllib2.URLError('file not on local host') + fo = open(localfile,'rb') + brange = req.headers.get('Range', None) + brange = range_header_to_tuple(brange) + assert brange != () + if brange: + (fb, lb) = brange + if lb == '': + lb = size + if fb < 0 or fb > size or lb > size: + raise RangeError('Requested Range Not Satisfiable') + size = (lb - fb) + fo = RangeableFileObject(fo, (fb, lb)) + headers = mimetools.Message(StringIO( + 'Content-Type: %s\nContent-Length: %d\nLast-Modified: %s\n' % + (mtype or 'text/plain', size, modified))) + return urllib.addinfourl(fo, headers, 'file:'+file) + + +# FTP Range Support +# Unfortunately, a large amount of base FTP code had to be copied +# from urllib and urllib2 in order to insert the FTP REST command. +# Code modifications for range support have been commented as +# follows: +# -- range support modifications start/end here + +from urllib import splitport, splituser, splitpasswd, splitattr, \ + unquote, addclosehook, addinfourl +import ftplib +import socket +import sys +import mimetypes +import mimetools + +class FTPRangeHandler(urllib2.FTPHandler): + def ftp_open(self, req): + host = req.get_host() + if not host: + raise IOError('ftp error', 'no host given') + host, port = splitport(host) + if port is None: + port = ftplib.FTP_PORT + + # username/password handling + user, host = splituser(host) + if user: + user, passwd = splitpasswd(user) + else: + passwd = None + host = unquote(host) + user = unquote(user or '') + passwd = unquote(passwd or '') + + try: + host = socket.gethostbyname(host) + except socket.error, msg: + raise urllib2.URLError(msg) + path, attrs = splitattr(req.get_selector()) + dirs = path.split('/') + dirs = map(unquote, dirs) + dirs, file = dirs[:-1], dirs[-1] + if dirs and not dirs[0]: + dirs = dirs[1:] + try: + fw = self.connect_ftp(user, passwd, host, port, dirs) + type = file and 'I' or 'D' + for attr in attrs: + attr, value = splitattr(attr) + if attr.lower() == 'type' and \ + value in ('a', 'A', 'i', 'I', 'd', 'D'): + type = value.upper() + + # -- range support modifications start here + rest = None + range_tup = range_header_to_tuple(req.headers.get('Range', None)) + assert range_tup != () + if range_tup: + (fb, lb) = range_tup + if fb > 0: + rest = fb + # -- range support modifications end here + + fp, retrlen = fw.retrfile(file, type, rest) + + # -- range support modifications start here + if range_tup: + (fb, lb) = range_tup + if lb == '': + if retrlen is None or retrlen == 0: + raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.') + lb = retrlen + retrlen = lb - fb + if retrlen < 0: + # beginning of range is larger than file + raise RangeError('Requested Range Not Satisfiable') + else: + retrlen = lb - fb + fp = RangeableFileObject(fp, (0, retrlen)) + # -- range support modifications end here + + headers = "" + mtype = mimetypes.guess_type(req.get_full_url())[0] + if mtype: + headers += "Content-Type: %s\n" % mtype + if retrlen is not None and retrlen >= 0: + headers += "Content-Length: %d\n" % retrlen + sf = StringIO(headers) + headers = mimetools.Message(sf) + return addinfourl(fp, headers, req.get_full_url()) + except ftplib.all_errors, msg: + raise IOError('ftp error', msg), sys.exc_info()[2] + + def connect_ftp(self, user, passwd, host, port, dirs): + fw = ftpwrapper(user, passwd, host, port, dirs) + return fw + +class ftpwrapper(urllib.ftpwrapper): + # range support note: + # this ftpwrapper code is copied directly from + # urllib. The only enhancement is to add the rest + # argument and pass it on to ftp.ntransfercmd + def retrfile(self, file, type, rest=None): + self.endtransfer() + if type in ('d', 'D'): + cmd = 'TYPE A' + isdir = 1 + else: + cmd = 'TYPE ' + type + isdir = 0 + try: + self.ftp.voidcmd(cmd) + except ftplib.all_errors: + self.init() + self.ftp.voidcmd(cmd) + conn = None + if file and not isdir: + # Use nlst to see if the file exists at all + try: + self.ftp.nlst(file) + except ftplib.error_perm, reason: + raise IOError('ftp error', reason), sys.exc_info()[2] + # Restore the transfer mode! + self.ftp.voidcmd(cmd) + # Try to retrieve as a file + try: + cmd = 'RETR ' + file + conn = self.ftp.ntransfercmd(cmd, rest) + except ftplib.error_perm, reason: + if str(reason).startswith('501'): + # workaround for REST not supported error + fp, retrlen = self.retrfile(file, type) + fp = RangeableFileObject(fp, (rest,'')) + return (fp, retrlen) + elif not str(reason).startswith('550'): + raise IOError('ftp error', reason), sys.exc_info()[2] + if not conn: + # Set transfer mode to ASCII! + self.ftp.voidcmd('TYPE A') + # Try a directory listing + if file: + cmd = 'LIST ' + file + else: + cmd = 'LIST' + conn = self.ftp.ntransfercmd(cmd) + self.busy = 1 + # Pass back both a suitably decorated object and a retrieval length + return (addclosehook(conn[0].makefile('rb'), + self.endtransfer), conn[1]) + + +#################################################################### +# Range Tuple Functions +# XXX: These range tuple functions might go better in a class. + +_rangere = None +def range_header_to_tuple(range_header): + """Get a (firstbyte,lastbyte) tuple from a Range header value. + + Range headers have the form "bytes=-". This + function pulls the firstbyte and lastbyte values and returns + a (firstbyte,lastbyte) tuple. If lastbyte is not specified in + the header value, it is returned as an empty string in the + tuple. + + Return None if range_header is None + Return () if range_header does not conform to the range spec + pattern. + + """ + global _rangere + if range_header is None: + return None + if _rangere is None: + import re + _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)') + match = _rangere.match(range_header) + if match: + tup = range_tuple_normalize(match.group(1, 2)) + if tup and tup[1]: + tup = (tup[0], tup[1]+1) + return tup + return () + +def range_tuple_to_header(range_tup): + """Convert a range tuple to a Range header value. + Return a string of the form "bytes=-" or None + if no range is needed. + """ + if range_tup is None: + return None + range_tup = range_tuple_normalize(range_tup) + if range_tup: + if range_tup[1]: + range_tup = (range_tup[0], range_tup[1] - 1) + return 'bytes=%s-%s' % range_tup + +def range_tuple_normalize(range_tup): + """Normalize a (first_byte,last_byte) range tuple. + Return a tuple whose first element is guaranteed to be an int + and whose second element will be '' (meaning: the last byte) or + an int. Finally, return None if the normalized tuple == (0,'') + as that is equivelant to retrieving the entire file. + """ + if range_tup is None: + return None + # handle first byte + fb = range_tup[0] + if fb in (None, ''): + fb = 0 + else: + fb = int(fb) + # handle last byte + try: + lb = range_tup[1] + except IndexError: + lb = '' + else: + if lb is None: + lb = '' + elif lb != '': + lb = int(lb) + # check if range is over the entire file + if (fb, lb) == (0, ''): + return None + # check that the range is valid + if lb < fb: + raise RangeError('Invalid byte range: %s-%s' % (fb, lb)) + return (fb, lb) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/changegroup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/changegroup.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,140 @@ +# changegroup.py - Mercurial changegroup manipulation functions +# +# Copyright 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import util +import struct, os, bz2, zlib, tempfile + +def getchunk(source): + """get a chunk from a changegroup""" + d = source.read(4) + if not d: + return "" + l = struct.unpack(">l", d)[0] + if l <= 4: + return "" + d = source.read(l - 4) + if len(d) < l - 4: + raise util.Abort(_("premature EOF reading chunk" + " (got %d bytes, expected %d)") + % (len(d), l - 4)) + return d + +def chunkiter(source): + """iterate through the chunks in source""" + while 1: + c = getchunk(source) + if not c: + break + yield c + +def chunkheader(length): + """build a changegroup chunk header""" + return struct.pack(">l", length + 4) + +def closechunk(): + return struct.pack(">l", 0) + +class nocompress(object): + def compress(self, x): + return x + def flush(self): + return "" + +bundletypes = { + "": ("", nocompress), + "HG10UN": ("HG10UN", nocompress), + "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()), + "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()), +} + +# hgweb uses this list to communicate it's preferred type +bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] + +def writebundle(cg, filename, bundletype): + """Write a bundle file and return its filename. + + Existing files will not be overwritten. + If no filename is specified, a temporary file is created. + bz2 compression can be turned off. + The bundle file will be deleted in case of errors. + """ + + fh = None + cleanup = None + try: + if filename: + fh = open(filename, "wb") + else: + fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") + fh = os.fdopen(fd, "wb") + cleanup = filename + + header, compressor = bundletypes[bundletype] + fh.write(header) + z = compressor() + + # parse the changegroup data, otherwise we will block + # in case of sshrepo because we don't know the end of the stream + + # an empty chunkiter is the end of the changegroup + # a changegroup has at least 2 chunkiters (changelog and manifest). + # after that, an empty chunkiter is the end of the changegroup + empty = False + count = 0 + while not empty or count <= 2: + empty = True + count += 1 + for chunk in chunkiter(cg): + empty = False + fh.write(z.compress(chunkheader(len(chunk)))) + pos = 0 + while pos < len(chunk): + next = pos + 2**20 + fh.write(z.compress(chunk[pos:next])) + pos = next + fh.write(z.compress(closechunk())) + fh.write(z.flush()) + cleanup = None + return filename + finally: + if fh is not None: + fh.close() + if cleanup is not None: + os.unlink(cleanup) + +def unbundle(header, fh): + if header == 'HG10UN': + return fh + elif not header.startswith('HG'): + # old client with uncompressed bundle + def generator(f): + yield header + for chunk in f: + yield chunk + elif header == 'HG10GZ': + def generator(f): + zd = zlib.decompressobj() + for chunk in f: + yield zd.decompress(chunk) + elif header == 'HG10BZ': + def generator(f): + zd = bz2.BZ2Decompressor() + zd.decompress("BZ") + for chunk in util.filechunkiter(f, 4096): + yield zd.decompress(chunk) + return util.chunkbuffer(generator(fh)) + +def readbundle(fh, fname): + header = fh.read(6) + if not header.startswith('HG'): + raise util.Abort(_('%s: not a Mercurial bundle file') % fname) + if not header.startswith('HG10'): + raise util.Abort(_('%s: unknown bundle version') % fname) + elif header not in bundletypes: + raise util.Abort(_('%s: unknown bundle compression type') % fname) + return unbundle(header, fh) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/changelog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/changelog.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,230 @@ +# changelog.py - changelog class for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import bin, hex, nullid +from i18n import _ +import util, error, revlog, encoding + +def _string_escape(text): + """ + >>> d = {'nl': chr(10), 'bs': chr(92), 'cr': chr(13), 'nul': chr(0)} + >>> s = "ab%(nl)scd%(bs)s%(bs)sn%(nul)sab%(cr)scd%(bs)s%(nl)s" % d + >>> s + 'ab\\ncd\\\\\\\\n\\x00ab\\rcd\\\\\\n' + >>> res = _string_escape(s) + >>> s == res.decode('string_escape') + True + """ + # subset of the string_escape codec + text = text.replace('\\', '\\\\').replace('\n', '\\n').replace('\r', '\\r') + return text.replace('\0', '\\0') + +def decodeextra(text): + extra = {} + for l in text.split('\0'): + if l: + k, v = l.decode('string_escape').split(':', 1) + extra[k] = v + return extra + +def encodeextra(d): + # keys must be sorted to produce a deterministic changelog entry + items = [_string_escape('%s:%s' % (k, d[k])) for k in sorted(d)] + return "\0".join(items) + +class appender(object): + '''the changelog index must be updated last on disk, so we use this class + to delay writes to it''' + def __init__(self, fp, buf): + self.data = buf + self.fp = fp + self.offset = fp.tell() + self.size = util.fstat(fp).st_size + + def end(self): + return self.size + len("".join(self.data)) + def tell(self): + return self.offset + def flush(self): + pass + def close(self): + self.fp.close() + + def seek(self, offset, whence=0): + '''virtual file offset spans real file and data''' + if whence == 0: + self.offset = offset + elif whence == 1: + self.offset += offset + elif whence == 2: + self.offset = self.end() + offset + if self.offset < self.size: + self.fp.seek(self.offset) + + def read(self, count=-1): + '''only trick here is reads that span real file and data''' + ret = "" + if self.offset < self.size: + s = self.fp.read(count) + ret = s + self.offset += len(s) + if count > 0: + count -= len(s) + if count != 0: + doff = self.offset - self.size + self.data.insert(0, "".join(self.data)) + del self.data[1:] + s = self.data[0][doff:doff+count] + self.offset += len(s) + ret += s + return ret + + def write(self, s): + self.data.append(str(s)) + self.offset += len(s) + +class changelog(revlog.revlog): + def __init__(self, opener): + self._realopener = opener + self._delayed = False + revlog.revlog.__init__(self, self._delayopener, "00changelog.i") + + def delayupdate(self): + "delay visibility of index updates to other readers" + self._delayed = True + self._delaycount = len(self) + self._delaybuf = [] + self._delayname = None + + def finalize(self, tr): + "finalize index updates" + self._delayed = False + # move redirected index data back into place + if self._delayname: + util.rename(self._delayname + ".a", self._delayname) + elif self._delaybuf: + fp = self.opener(self.indexfile, 'a') + fp.write("".join(self._delaybuf)) + fp.close() + self._delaybuf = [] + # split when we're done + self.checkinlinesize(tr) + + def _delayopener(self, name, mode='r'): + fp = self._realopener(name, mode) + # only divert the index + if not self._delayed or not name == self.indexfile: + return fp + # if we're doing an initial clone, divert to another file + if self._delaycount == 0: + self._delayname = fp.name + if not len(self): + # make sure to truncate the file + mode = mode.replace('a', 'w') + return self._realopener(name + ".a", mode) + # otherwise, divert to memory + return appender(fp, self._delaybuf) + + def readpending(self, file): + r = revlog.revlog(self.opener, file) + self.index = r.index + self.nodemap = r.nodemap + self._chunkcache = r._chunkcache + + def writepending(self): + "create a file containing the unfinalized state for pretxnchangegroup" + if self._delaybuf: + # make a temporary copy of the index + fp1 = self._realopener(self.indexfile) + fp2 = self._realopener(self.indexfile + ".a", "w") + fp2.write(fp1.read()) + # add pending data + fp2.write("".join(self._delaybuf)) + fp2.close() + # switch modes so finalize can simply rename + self._delaybuf = [] + self._delayname = fp1.name + + if self._delayname: + return True + + return False + + def checkinlinesize(self, tr, fp=None): + if self.opener == self._delayopener: + return + return revlog.revlog.checkinlinesize(self, tr, fp) + + def read(self, node): + """ + format used: + nodeid\n : manifest node in ascii + user\n : user, no \n or \r allowed + time tz extra\n : date (time is int or float, timezone is int) + : extra is metadatas, encoded and separated by '\0' + : older versions ignore it + files\n\n : files modified by the cset, no \n or \r allowed + (.*) : comment (free text, ideally utf-8) + + changelog v0 doesn't use extra + """ + text = self.revision(node) + if not text: + return (nullid, "", (0, 0), [], "", {'branch': 'default'}) + last = text.index("\n\n") + desc = encoding.tolocal(text[last + 2:]) + l = text[:last].split('\n') + manifest = bin(l[0]) + user = encoding.tolocal(l[1]) + + extra_data = l[2].split(' ', 2) + if len(extra_data) != 3: + time = float(extra_data.pop(0)) + try: + # various tools did silly things with the time zone field. + timezone = int(extra_data[0]) + except: + timezone = 0 + extra = {} + else: + time, timezone, extra = extra_data + time, timezone = float(time), int(timezone) + extra = decodeextra(extra) + if not extra.get('branch'): + extra['branch'] = 'default' + files = l[3:] + return (manifest, user, (time, timezone), files, desc, extra) + + def add(self, manifest, files, desc, transaction, p1, p2, + user, date=None, extra={}): + user = user.strip() + # An empty username or a username with a "\n" will make the + # revision text contain two "\n\n" sequences -> corrupt + # repository since read cannot unpack the revision. + if not user: + raise error.RevlogError(_("empty username")) + if "\n" in user: + raise error.RevlogError(_("username %s contains a newline") + % repr(user)) + + # strip trailing whitespace and leading and trailing empty lines + desc = '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n') + + user, desc = encoding.fromlocal(user), encoding.fromlocal(desc) + + if date: + parseddate = "%d %d" % util.parsedate(date) + else: + parseddate = "%d %d" % util.makedate() + if extra and extra.get("branch") in ("default", ""): + del extra["branch"] + if extra: + extra = encodeextra(extra) + parseddate = "%s %s" % (parseddate, extra) + l = [hex(manifest), user, parseddate] + sorted(files) + ["", desc] + text = "\n".join(l) + return self.addrevision(text, transaction, len(self), p1, p2) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/cmdutil.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/cmdutil.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,1254 @@ +# cmdutil.py - help for command processing in mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import hex, nullid, nullrev, short +from i18n import _ +import os, sys, errno, re, glob +import mdiff, bdiff, util, templater, patch, error, encoding +import match as _match + +revrangesep = ':' + +def findpossible(cmd, table, strict=False): + """ + Return cmd -> (aliases, command table entry) + for each matching command. + Return debug commands (or their aliases) only if no normal command matches. + """ + choice = {} + debugchoice = {} + for e in table.keys(): + aliases = e.lstrip("^").split("|") + found = None + if cmd in aliases: + found = cmd + elif not strict: + for a in aliases: + if a.startswith(cmd): + found = a + break + if found is not None: + if aliases[0].startswith("debug") or found.startswith("debug"): + debugchoice[found] = (aliases, table[e]) + else: + choice[found] = (aliases, table[e]) + + if not choice and debugchoice: + choice = debugchoice + + return choice + +def findcmd(cmd, table, strict=True): + """Return (aliases, command table entry) for command string.""" + choice = findpossible(cmd, table, strict) + + if cmd in choice: + return choice[cmd] + + if len(choice) > 1: + clist = choice.keys() + clist.sort() + raise error.AmbiguousCommand(cmd, clist) + + if choice: + return choice.values()[0] + + raise error.UnknownCommand(cmd) + +def bail_if_changed(repo): + if repo.dirstate.parents()[1] != nullid: + raise util.Abort(_('outstanding uncommitted merge')) + modified, added, removed, deleted = repo.status()[:4] + if modified or added or removed or deleted: + raise util.Abort(_("outstanding uncommitted changes")) + +def logmessage(opts): + """ get the log message according to -m and -l option """ + message = opts.get('message') + logfile = opts.get('logfile') + + if message and logfile: + raise util.Abort(_('options --message and --logfile are mutually ' + 'exclusive')) + if not message and logfile: + try: + if logfile == '-': + message = sys.stdin.read() + else: + message = open(logfile).read() + except IOError, inst: + raise util.Abort(_("can't read commit message '%s': %s") % + (logfile, inst.strerror)) + return message + +def loglimit(opts): + """get the log limit according to option -l/--limit""" + limit = opts.get('limit') + if limit: + try: + limit = int(limit) + except ValueError: + raise util.Abort(_('limit must be a positive integer')) + if limit <= 0: raise util.Abort(_('limit must be positive')) + else: + limit = sys.maxint + return limit + +def remoteui(src, opts): + 'build a remote ui from ui or repo and opts' + if hasattr(src, 'baseui'): # looks like a repository + dst = src.baseui.copy() # drop repo-specific config + src = src.ui # copy target options from repo + else: # assume it's a global ui object + dst = src.copy() # keep all global options + + # copy ssh-specific options + for o in 'ssh', 'remotecmd': + v = opts.get(o) or src.config('ui', o) + if v: + dst.setconfig("ui", o, v) + # copy bundle-specific options + r = src.config('bundle', 'mainreporoot') + if r: + dst.setconfig('bundle', 'mainreporoot', r) + + return dst + +def revpair(repo, revs): + '''return pair of nodes, given list of revisions. second item can + be None, meaning use working dir.''' + + def revfix(repo, val, defval): + if not val and val != 0 and defval is not None: + val = defval + return repo.lookup(val) + + if not revs: + return repo.dirstate.parents()[0], None + end = None + if len(revs) == 1: + if revrangesep in revs[0]: + start, end = revs[0].split(revrangesep, 1) + start = revfix(repo, start, 0) + end = revfix(repo, end, len(repo) - 1) + else: + start = revfix(repo, revs[0], None) + elif len(revs) == 2: + if revrangesep in revs[0] or revrangesep in revs[1]: + raise util.Abort(_('too many revisions specified')) + start = revfix(repo, revs[0], None) + end = revfix(repo, revs[1], None) + else: + raise util.Abort(_('too many revisions specified')) + return start, end + +def revrange(repo, revs): + """Yield revision as strings from a list of revision specifications.""" + + def revfix(repo, val, defval): + if not val and val != 0 and defval is not None: + return defval + return repo.changelog.rev(repo.lookup(val)) + + seen, l = set(), [] + for spec in revs: + if revrangesep in spec: + start, end = spec.split(revrangesep, 1) + start = revfix(repo, start, 0) + end = revfix(repo, end, len(repo) - 1) + step = start > end and -1 or 1 + for rev in xrange(start, end+step, step): + if rev in seen: + continue + seen.add(rev) + l.append(rev) + else: + rev = revfix(repo, spec, None) + if rev in seen: + continue + seen.add(rev) + l.append(rev) + + return l + +def make_filename(repo, pat, node, + total=None, seqno=None, revwidth=None, pathname=None): + node_expander = { + 'H': lambda: hex(node), + 'R': lambda: str(repo.changelog.rev(node)), + 'h': lambda: short(node), + } + expander = { + '%': lambda: '%', + 'b': lambda: os.path.basename(repo.root), + } + + try: + if node: + expander.update(node_expander) + if node: + expander['r'] = (lambda: + str(repo.changelog.rev(node)).zfill(revwidth or 0)) + if total is not None: + expander['N'] = lambda: str(total) + if seqno is not None: + expander['n'] = lambda: str(seqno) + if total is not None and seqno is not None: + expander['n'] = lambda: str(seqno).zfill(len(str(total))) + if pathname is not None: + expander['s'] = lambda: os.path.basename(pathname) + expander['d'] = lambda: os.path.dirname(pathname) or '.' + expander['p'] = lambda: pathname + + newname = [] + patlen = len(pat) + i = 0 + while i < patlen: + c = pat[i] + if c == '%': + i += 1 + c = pat[i] + c = expander[c]() + newname.append(c) + i += 1 + return ''.join(newname) + except KeyError, inst: + raise util.Abort(_("invalid format spec '%%%s' in output filename") % + inst.args[0]) + +def make_file(repo, pat, node=None, + total=None, seqno=None, revwidth=None, mode='wb', pathname=None): + + writable = 'w' in mode or 'a' in mode + + if not pat or pat == '-': + return writable and sys.stdout or sys.stdin + if hasattr(pat, 'write') and writable: + return pat + if hasattr(pat, 'read') and 'r' in mode: + return pat + return open(make_filename(repo, pat, node, total, seqno, revwidth, + pathname), + mode) + +def expandpats(pats): + if not util.expandglobs: + return list(pats) + ret = [] + for p in pats: + kind, name = _match._patsplit(p, None) + if kind is None: + try: + globbed = glob.glob(name) + except re.error: + globbed = [name] + if globbed: + ret.extend(globbed) + continue + ret.append(p) + return ret + +def match(repo, pats=[], opts={}, globbed=False, default='relpath'): + if not globbed and default == 'relpath': + pats = expandpats(pats or []) + m = _match.match(repo.root, repo.getcwd(), pats, + opts.get('include'), opts.get('exclude'), default) + def badfn(f, msg): + repo.ui.warn("%s: %s\n" % (m.rel(f), msg)) + m.bad = badfn + return m + +def matchall(repo): + return _match.always(repo.root, repo.getcwd()) + +def matchfiles(repo, files): + return _match.exact(repo.root, repo.getcwd(), files) + +def findrenames(repo, added, removed, threshold): + '''find renamed files -- yields (before, after, score) tuples''' + ctx = repo['.'] + for a in added: + aa = repo.wread(a) + bestname, bestscore = None, threshold + for r in removed: + if r not in ctx: + continue + rr = ctx.filectx(r).data() + + # bdiff.blocks() returns blocks of matching lines + # count the number of bytes in each + equal = 0 + alines = mdiff.splitnewlines(aa) + matches = bdiff.blocks(aa, rr) + for x1,x2,y1,y2 in matches: + for line in alines[x1:x2]: + equal += len(line) + + lengths = len(aa) + len(rr) + if lengths: + myscore = equal*2.0 / lengths + if myscore >= bestscore: + bestname, bestscore = r, myscore + if bestname: + yield bestname, a, bestscore + +def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None): + if dry_run is None: + dry_run = opts.get('dry_run') + if similarity is None: + similarity = float(opts.get('similarity') or 0) + # we'd use status here, except handling of symlinks and ignore is tricky + added, unknown, deleted, removed = [], [], [], [] + audit_path = util.path_auditor(repo.root) + m = match(repo, pats, opts) + for abs in repo.walk(m): + target = repo.wjoin(abs) + good = True + try: + audit_path(abs) + except: + good = False + rel = m.rel(abs) + exact = m.exact(abs) + if good and abs not in repo.dirstate: + unknown.append(abs) + if repo.ui.verbose or not exact: + repo.ui.status(_('adding %s\n') % ((pats and rel) or abs)) + elif repo.dirstate[abs] != 'r' and (not good or not util.lexists(target) + or (os.path.isdir(target) and not os.path.islink(target))): + deleted.append(abs) + if repo.ui.verbose or not exact: + repo.ui.status(_('removing %s\n') % ((pats and rel) or abs)) + # for finding renames + elif repo.dirstate[abs] == 'r': + removed.append(abs) + elif repo.dirstate[abs] == 'a': + added.append(abs) + if not dry_run: + repo.remove(deleted) + repo.add(unknown) + if similarity > 0: + for old, new, score in findrenames(repo, added + unknown, + removed + deleted, similarity): + if repo.ui.verbose or not m.exact(old) or not m.exact(new): + repo.ui.status(_('recording removal of %s as rename to %s ' + '(%d%% similar)\n') % + (m.rel(old), m.rel(new), score * 100)) + if not dry_run: + repo.copy(old, new) + +def copy(ui, repo, pats, opts, rename=False): + # called with the repo lock held + # + # hgsep => pathname that uses "/" to separate directories + # ossep => pathname that uses os.sep to separate directories + cwd = repo.getcwd() + targets = {} + after = opts.get("after") + dryrun = opts.get("dry_run") + + def walkpat(pat): + srcs = [] + m = match(repo, [pat], opts, globbed=True) + for abs in repo.walk(m): + state = repo.dirstate[abs] + rel = m.rel(abs) + exact = m.exact(abs) + if state in '?r': + if exact and state == '?': + ui.warn(_('%s: not copying - file is not managed\n') % rel) + if exact and state == 'r': + ui.warn(_('%s: not copying - file has been marked for' + ' remove\n') % rel) + continue + # abs: hgsep + # rel: ossep + srcs.append((abs, rel, exact)) + return srcs + + # abssrc: hgsep + # relsrc: ossep + # otarget: ossep + def copyfile(abssrc, relsrc, otarget, exact): + abstarget = util.canonpath(repo.root, cwd, otarget) + reltarget = repo.pathto(abstarget, cwd) + target = repo.wjoin(abstarget) + src = repo.wjoin(abssrc) + state = repo.dirstate[abstarget] + + # check for collisions + prevsrc = targets.get(abstarget) + if prevsrc is not None: + ui.warn(_('%s: not overwriting - %s collides with %s\n') % + (reltarget, repo.pathto(abssrc, cwd), + repo.pathto(prevsrc, cwd))) + return + + # check for overwrites + exists = os.path.exists(target) + if not after and exists or after and state in 'mn': + if not opts['force']: + ui.warn(_('%s: not overwriting - file exists\n') % + reltarget) + return + + if after: + if not exists: + return + elif not dryrun: + try: + if exists: + os.unlink(target) + targetdir = os.path.dirname(target) or '.' + if not os.path.isdir(targetdir): + os.makedirs(targetdir) + util.copyfile(src, target) + except IOError, inst: + if inst.errno == errno.ENOENT: + ui.warn(_('%s: deleted in working copy\n') % relsrc) + else: + ui.warn(_('%s: cannot copy - %s\n') % + (relsrc, inst.strerror)) + return True # report a failure + + if ui.verbose or not exact: + if rename: + ui.status(_('moving %s to %s\n') % (relsrc, reltarget)) + else: + ui.status(_('copying %s to %s\n') % (relsrc, reltarget)) + + targets[abstarget] = abssrc + + # fix up dirstate + origsrc = repo.dirstate.copied(abssrc) or abssrc + if abstarget == origsrc: # copying back a copy? + if state not in 'mn' and not dryrun: + repo.dirstate.normallookup(abstarget) + else: + if repo.dirstate[origsrc] == 'a' and origsrc == abssrc: + if not ui.quiet: + ui.warn(_("%s has not been committed yet, so no copy " + "data will be stored for %s.\n") + % (repo.pathto(origsrc, cwd), reltarget)) + if repo.dirstate[abstarget] in '?r' and not dryrun: + repo.add([abstarget]) + elif not dryrun: + repo.copy(origsrc, abstarget) + + if rename and not dryrun: + repo.remove([abssrc], not after) + + # pat: ossep + # dest ossep + # srcs: list of (hgsep, hgsep, ossep, bool) + # return: function that takes hgsep and returns ossep + def targetpathfn(pat, dest, srcs): + if os.path.isdir(pat): + abspfx = util.canonpath(repo.root, cwd, pat) + abspfx = util.localpath(abspfx) + if destdirexists: + striplen = len(os.path.split(abspfx)[0]) + else: + striplen = len(abspfx) + if striplen: + striplen += len(os.sep) + res = lambda p: os.path.join(dest, util.localpath(p)[striplen:]) + elif destdirexists: + res = lambda p: os.path.join(dest, + os.path.basename(util.localpath(p))) + else: + res = lambda p: dest + return res + + # pat: ossep + # dest ossep + # srcs: list of (hgsep, hgsep, ossep, bool) + # return: function that takes hgsep and returns ossep + def targetpathafterfn(pat, dest, srcs): + if _match.patkind(pat): + # a mercurial pattern + res = lambda p: os.path.join(dest, + os.path.basename(util.localpath(p))) + else: + abspfx = util.canonpath(repo.root, cwd, pat) + if len(abspfx) < len(srcs[0][0]): + # A directory. Either the target path contains the last + # component of the source path or it does not. + def evalpath(striplen): + score = 0 + for s in srcs: + t = os.path.join(dest, util.localpath(s[0])[striplen:]) + if os.path.exists(t): + score += 1 + return score + + abspfx = util.localpath(abspfx) + striplen = len(abspfx) + if striplen: + striplen += len(os.sep) + if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])): + score = evalpath(striplen) + striplen1 = len(os.path.split(abspfx)[0]) + if striplen1: + striplen1 += len(os.sep) + if evalpath(striplen1) > score: + striplen = striplen1 + res = lambda p: os.path.join(dest, + util.localpath(p)[striplen:]) + else: + # a file + if destdirexists: + res = lambda p: os.path.join(dest, + os.path.basename(util.localpath(p))) + else: + res = lambda p: dest + return res + + + pats = expandpats(pats) + if not pats: + raise util.Abort(_('no source or destination specified')) + if len(pats) == 1: + raise util.Abort(_('no destination specified')) + dest = pats.pop() + destdirexists = os.path.isdir(dest) and not os.path.islink(dest) + if not destdirexists: + if len(pats) > 1 or _match.patkind(pats[0]): + raise util.Abort(_('with multiple sources, destination must be an ' + 'existing directory')) + if util.endswithsep(dest): + raise util.Abort(_('destination %s is not a directory') % dest) + + tfn = targetpathfn + if after: + tfn = targetpathafterfn + copylist = [] + for pat in pats: + srcs = walkpat(pat) + if not srcs: + continue + copylist.append((tfn(pat, dest, srcs), srcs)) + if not copylist: + raise util.Abort(_('no files to copy')) + + errors = 0 + for targetpath, srcs in copylist: + for abssrc, relsrc, exact in srcs: + if copyfile(abssrc, relsrc, targetpath(abssrc), exact): + errors += 1 + + if errors: + ui.warn(_('(consider using --after)\n')) + + return errors + +def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None): + '''Run a command as a service.''' + + if opts['daemon'] and not opts['daemon_pipefds']: + rfd, wfd = os.pipe() + args = sys.argv[:] + args.append('--daemon-pipefds=%d,%d' % (rfd, wfd)) + # Don't pass --cwd to the child process, because we've already + # changed directory. + for i in xrange(1,len(args)): + if args[i].startswith('--cwd='): + del args[i] + break + elif args[i].startswith('--cwd'): + del args[i:i+2] + break + pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), + args[0], args) + os.close(wfd) + os.read(rfd, 1) + if parentfn: + return parentfn(pid) + else: + os._exit(0) + + if initfn: + initfn() + + if opts['pid_file']: + fp = open(opts['pid_file'], 'w') + fp.write(str(os.getpid()) + '\n') + fp.close() + + if opts['daemon_pipefds']: + rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')] + os.close(rfd) + try: + os.setsid() + except AttributeError: + pass + os.write(wfd, 'y') + os.close(wfd) + sys.stdout.flush() + sys.stderr.flush() + + nullfd = os.open(util.nulldev, os.O_RDWR) + logfilefd = nullfd + if logfile: + logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND) + os.dup2(nullfd, 0) + os.dup2(logfilefd, 1) + os.dup2(logfilefd, 2) + if nullfd not in (0, 1, 2): + os.close(nullfd) + if logfile and logfilefd not in (0, 1, 2): + os.close(logfilefd) + + if runfn: + return runfn() + +class changeset_printer(object): + '''show changeset information when templating not requested.''' + + def __init__(self, ui, repo, patch, diffopts, buffered): + self.ui = ui + self.repo = repo + self.buffered = buffered + self.patch = patch + self.diffopts = diffopts + self.header = {} + self.hunk = {} + self.lastheader = None + + def flush(self, rev): + if rev in self.header: + h = self.header[rev] + if h != self.lastheader: + self.lastheader = h + self.ui.write(h) + del self.header[rev] + if rev in self.hunk: + self.ui.write(self.hunk[rev]) + del self.hunk[rev] + return 1 + return 0 + + def show(self, ctx, copies=(), **props): + if self.buffered: + self.ui.pushbuffer() + self._show(ctx, copies, props) + self.hunk[ctx.rev()] = self.ui.popbuffer() + else: + self._show(ctx, copies, props) + + def _show(self, ctx, copies, props): + '''show a single changeset or file revision''' + changenode = ctx.node() + rev = ctx.rev() + + if self.ui.quiet: + self.ui.write("%d:%s\n" % (rev, short(changenode))) + return + + log = self.repo.changelog + changes = log.read(changenode) + date = util.datestr(changes[2]) + extra = changes[5] + branch = extra.get("branch") + + hexfunc = self.ui.debugflag and hex or short + + parents = [(p, hexfunc(log.node(p))) + for p in self._meaningful_parentrevs(log, rev)] + + self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode))) + + # don't show the default branch name + if branch != 'default': + branch = encoding.tolocal(branch) + self.ui.write(_("branch: %s\n") % branch) + for tag in self.repo.nodetags(changenode): + self.ui.write(_("tag: %s\n") % tag) + for parent in parents: + self.ui.write(_("parent: %d:%s\n") % parent) + + if self.ui.debugflag: + self.ui.write(_("manifest: %d:%s\n") % + (self.repo.manifest.rev(changes[0]), hex(changes[0]))) + self.ui.write(_("user: %s\n") % changes[1]) + self.ui.write(_("date: %s\n") % date) + + if self.ui.debugflag: + files = self.repo.status(log.parents(changenode)[0], changenode)[:3] + for key, value in zip([_("files:"), _("files+:"), _("files-:")], + files): + if value: + self.ui.write("%-12s %s\n" % (key, " ".join(value))) + elif changes[3] and self.ui.verbose: + self.ui.write(_("files: %s\n") % " ".join(changes[3])) + if copies and self.ui.verbose: + copies = ['%s (%s)' % c for c in copies] + self.ui.write(_("copies: %s\n") % ' '.join(copies)) + + if extra and self.ui.debugflag: + for key, value in sorted(extra.items()): + self.ui.write(_("extra: %s=%s\n") + % (key, value.encode('string_escape'))) + + description = changes[4].strip() + if description: + if self.ui.verbose: + self.ui.write(_("description:\n")) + self.ui.write(description) + self.ui.write("\n\n") + else: + self.ui.write(_("summary: %s\n") % + description.splitlines()[0]) + self.ui.write("\n") + + self.showpatch(changenode) + + def showpatch(self, node): + if self.patch: + prev = self.repo.changelog.parents(node)[0] + chunks = patch.diff(self.repo, prev, node, match=self.patch, + opts=patch.diffopts(self.ui, self.diffopts)) + for chunk in chunks: + self.ui.write(chunk) + self.ui.write("\n") + + def _meaningful_parentrevs(self, log, rev): + """Return list of meaningful (or all if debug) parentrevs for rev. + + For merges (two non-nullrev revisions) both parents are meaningful. + Otherwise the first parent revision is considered meaningful if it + is not the preceding revision. + """ + parents = log.parentrevs(rev) + if not self.ui.debugflag and parents[1] == nullrev: + if parents[0] >= rev - 1: + parents = [] + else: + parents = [parents[0]] + return parents + + +class changeset_templater(changeset_printer): + '''format changeset information.''' + + def __init__(self, ui, repo, patch, diffopts, mapfile, buffered): + changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered) + formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12]) + self.t = templater.templater(mapfile, {'formatnode': formatnode}, + cache={ + 'parent': '{rev}:{node|formatnode} ', + 'manifest': '{rev}:{node|formatnode}', + 'filecopy': '{name} ({source})'}) + + def use_template(self, t): + '''set template string to use''' + self.t.cache['changeset'] = t + + def _meaningful_parentrevs(self, ctx): + """Return list of meaningful (or all if debug) parentrevs for rev. + """ + parents = ctx.parents() + if len(parents) > 1: + return parents + if self.ui.debugflag: + return [parents[0], self.repo['null']] + if parents[0].rev() >= ctx.rev() - 1: + return [] + return parents + + def _show(self, ctx, copies, props): + '''show a single changeset or file revision''' + + def showlist(name, values, plural=None, **args): + '''expand set of values. + name is name of key in template map. + values is list of strings or dicts. + plural is plural of name, if not simply name + 's'. + + expansion works like this, given name 'foo'. + + if values is empty, expand 'no_foos'. + + if 'foo' not in template map, return values as a string, + joined by space. + + expand 'start_foos'. + + for each value, expand 'foo'. if 'last_foo' in template + map, expand it instead of 'foo' for last key. + + expand 'end_foos'. + ''' + if plural: names = plural + else: names = name + 's' + if not values: + noname = 'no_' + names + if noname in self.t: + yield self.t(noname, **args) + return + if name not in self.t: + if isinstance(values[0], str): + yield ' '.join(values) + else: + for v in values: + yield dict(v, **args) + return + startname = 'start_' + names + if startname in self.t: + yield self.t(startname, **args) + vargs = args.copy() + def one(v, tag=name): + try: + vargs.update(v) + except (AttributeError, ValueError): + try: + for a, b in v: + vargs[a] = b + except ValueError: + vargs[name] = v + return self.t(tag, **vargs) + lastname = 'last_' + name + if lastname in self.t: + last = values.pop() + else: + last = None + for v in values: + yield one(v) + if last is not None: + yield one(last, tag=lastname) + endname = 'end_' + names + if endname in self.t: + yield self.t(endname, **args) + + def showbranches(**args): + branch = ctx.branch() + if branch != 'default': + branch = encoding.tolocal(branch) + return showlist('branch', [branch], plural='branches', **args) + + def showparents(**args): + parents = [[('rev', p.rev()), ('node', p.hex())] + for p in self._meaningful_parentrevs(ctx)] + return showlist('parent', parents, **args) + + def showtags(**args): + return showlist('tag', ctx.tags(), **args) + + def showextras(**args): + for key, value in sorted(ctx.extra().items()): + args = args.copy() + args.update(dict(key=key, value=value)) + yield self.t('extra', **args) + + def showcopies(**args): + c = [{'name': x[0], 'source': x[1]} for x in copies] + return showlist('file_copy', c, plural='file_copies', **args) + + files = [] + def getfiles(): + if not files: + files[:] = self.repo.status(ctx.parents()[0].node(), + ctx.node())[:3] + return files + def showfiles(**args): + return showlist('file', ctx.files(), **args) + def showmods(**args): + return showlist('file_mod', getfiles()[0], **args) + def showadds(**args): + return showlist('file_add', getfiles()[1], **args) + def showdels(**args): + return showlist('file_del', getfiles()[2], **args) + def showmanifest(**args): + args = args.copy() + args.update(dict(rev=self.repo.manifest.rev(ctx.changeset()[0]), + node=hex(ctx.changeset()[0]))) + return self.t('manifest', **args) + + def showdiffstat(**args): + diff = patch.diff(self.repo, ctx.parents()[0].node(), ctx.node()) + files, adds, removes = 0, 0, 0 + for i in patch.diffstatdata(util.iterlines(diff)): + files += 1 + adds += i[1] + removes += i[2] + return '%s: +%s/-%s' % (files, adds, removes) + + defprops = { + 'author': ctx.user(), + 'branches': showbranches, + 'date': ctx.date(), + 'desc': ctx.description().strip(), + 'file_adds': showadds, + 'file_dels': showdels, + 'file_mods': showmods, + 'files': showfiles, + 'file_copies': showcopies, + 'manifest': showmanifest, + 'node': ctx.hex(), + 'parents': showparents, + 'rev': ctx.rev(), + 'tags': showtags, + 'extras': showextras, + 'diffstat': showdiffstat, + } + props = props.copy() + props.update(defprops) + + # find correct templates for current mode + + tmplmodes = [ + (True, None), + (self.ui.verbose, 'verbose'), + (self.ui.quiet, 'quiet'), + (self.ui.debugflag, 'debug'), + ] + + types = {'header': '', 'changeset': 'changeset'} + for mode, postfix in tmplmodes: + for type in types: + cur = postfix and ('%s_%s' % (type, postfix)) or type + if mode and cur in self.t: + types[type] = cur + + try: + + # write header + if types['header']: + h = templater.stringify(self.t(types['header'], **props)) + if self.buffered: + self.header[ctx.rev()] = h + else: + self.ui.write(h) + + # write changeset metadata, then patch if requested + key = types['changeset'] + self.ui.write(templater.stringify(self.t(key, **props))) + self.showpatch(ctx.node()) + + except KeyError, inst: + msg = _("%s: no key named '%s'") + raise util.Abort(msg % (self.t.mapfile, inst.args[0])) + except SyntaxError, inst: + raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0])) + +def show_changeset(ui, repo, opts, buffered=False, matchfn=False): + """show one changeset using template or regular display. + + Display format will be the first non-empty hit of: + 1. option 'template' + 2. option 'style' + 3. [ui] setting 'logtemplate' + 4. [ui] setting 'style' + If all of these values are either the unset or the empty string, + regular display via changeset_printer() is done. + """ + # options + patch = False + if opts.get('patch'): + patch = matchfn or matchall(repo) + + tmpl = opts.get('template') + style = None + if tmpl: + tmpl = templater.parsestring(tmpl, quoted=False) + else: + style = opts.get('style') + + # ui settings + if not (tmpl or style): + tmpl = ui.config('ui', 'logtemplate') + if tmpl: + tmpl = templater.parsestring(tmpl) + else: + style = ui.config('ui', 'style') + + if not (tmpl or style): + return changeset_printer(ui, repo, patch, opts, buffered) + + mapfile = None + if style and not tmpl: + mapfile = style + if not os.path.split(mapfile)[0]: + mapname = (templater.templatepath('map-cmdline.' + mapfile) + or templater.templatepath(mapfile)) + if mapname: mapfile = mapname + + try: + t = changeset_templater(ui, repo, patch, opts, mapfile, buffered) + except SyntaxError, inst: + raise util.Abort(inst.args[0]) + if tmpl: t.use_template(tmpl) + return t + +def finddate(ui, repo, date): + """Find the tipmost changeset that matches the given date spec""" + df = util.matchdate(date) + get = util.cachefunc(lambda r: repo[r].changeset()) + changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None}) + results = {} + for st, rev, fns in changeiter: + if st == 'add': + d = get(rev)[2] + if df(d[0]): + results[rev] = d + elif st == 'iter': + if rev in results: + ui.status(_("Found revision %s from %s\n") % + (rev, util.datestr(results[rev]))) + return str(rev) + + raise util.Abort(_("revision matching date not found")) + +def walkchangerevs(ui, repo, pats, change, opts): + '''Iterate over files and the revs in which they changed. + + Callers most commonly need to iterate backwards over the history + in which they are interested. Doing so has awful (quadratic-looking) + performance, so we use iterators in a "windowed" way. + + We walk a window of revisions in the desired order. Within the + window, we first walk forwards to gather data, then in the desired + order (usually backwards) to display it. + + This function returns an (iterator, matchfn) tuple. The iterator + yields 3-tuples. They will be of one of the following forms: + + "window", incrementing, lastrev: stepping through a window, + positive if walking forwards through revs, last rev in the + sequence iterated over - use to reset state for the current window + + "add", rev, fns: out-of-order traversal of the given filenames + fns, which changed during revision rev - use to gather data for + possible display + + "iter", rev, None: in-order traversal of the revs earlier iterated + over with "add" - use to display data''' + + def increasing_windows(start, end, windowsize=8, sizelimit=512): + if start < end: + while start < end: + yield start, min(windowsize, end-start) + start += windowsize + if windowsize < sizelimit: + windowsize *= 2 + else: + while start > end: + yield start, min(windowsize, start-end-1) + start -= windowsize + if windowsize < sizelimit: + windowsize *= 2 + + m = match(repo, pats, opts) + follow = opts.get('follow') or opts.get('follow_first') + + if not len(repo): + return [], m + + if follow: + defrange = '%s:0' % repo['.'].rev() + else: + defrange = '-1:0' + revs = revrange(repo, opts['rev'] or [defrange]) + wanted = set() + slowpath = m.anypats() or (m.files() and opts.get('removed')) + fncache = {} + + if not slowpath and not m.files(): + # No files, no patterns. Display all revs. + wanted = set(revs) + copies = [] + if not slowpath: + # Only files, no patterns. Check the history of each file. + def filerevgen(filelog, node): + cl_count = len(repo) + if node is None: + last = len(filelog) - 1 + else: + last = filelog.rev(node) + for i, window in increasing_windows(last, nullrev): + revs = [] + for j in xrange(i - window, i + 1): + n = filelog.node(j) + revs.append((filelog.linkrev(j), + follow and filelog.renamed(n))) + for rev in reversed(revs): + # only yield rev for which we have the changelog, it can + # happen while doing "hg log" during a pull or commit + if rev[0] < cl_count: + yield rev + def iterfiles(): + for filename in m.files(): + yield filename, None + for filename_node in copies: + yield filename_node + minrev, maxrev = min(revs), max(revs) + for file_, node in iterfiles(): + filelog = repo.file(file_) + if not len(filelog): + if node is None: + # A zero count may be a directory or deleted file, so + # try to find matching entries on the slow path. + if follow: + raise util.Abort(_('cannot follow nonexistent file: "%s"') % file_) + slowpath = True + break + else: + ui.warn(_('%s:%s copy source revision cannot be found!\n') + % (file_, short(node))) + continue + for rev, copied in filerevgen(filelog, node): + if rev <= maxrev: + if rev < minrev: + break + fncache.setdefault(rev, []) + fncache[rev].append(file_) + wanted.add(rev) + if follow and copied: + copies.append(copied) + if slowpath: + if follow: + raise util.Abort(_('can only follow copies/renames for explicit ' + 'filenames')) + + # The slow path checks files modified in every changeset. + def changerevgen(): + for i, window in increasing_windows(len(repo) - 1, nullrev): + for j in xrange(i - window, i + 1): + yield j, change(j)[3] + + for rev, changefiles in changerevgen(): + matches = filter(m, changefiles) + if matches: + fncache[rev] = matches + wanted.add(rev) + + class followfilter(object): + def __init__(self, onlyfirst=False): + self.startrev = nullrev + self.roots = [] + self.onlyfirst = onlyfirst + + def match(self, rev): + def realparents(rev): + if self.onlyfirst: + return repo.changelog.parentrevs(rev)[0:1] + else: + return filter(lambda x: x != nullrev, + repo.changelog.parentrevs(rev)) + + if self.startrev == nullrev: + self.startrev = rev + return True + + if rev > self.startrev: + # forward: all descendants + if not self.roots: + self.roots.append(self.startrev) + for parent in realparents(rev): + if parent in self.roots: + self.roots.append(rev) + return True + else: + # backwards: all parents + if not self.roots: + self.roots.extend(realparents(self.startrev)) + if rev in self.roots: + self.roots.remove(rev) + self.roots.extend(realparents(rev)) + return True + + return False + + # it might be worthwhile to do this in the iterator if the rev range + # is descending and the prune args are all within that range + for rev in opts.get('prune', ()): + rev = repo.changelog.rev(repo.lookup(rev)) + ff = followfilter() + stop = min(revs[0], revs[-1]) + for x in xrange(rev, stop-1, -1): + if ff.match(x): + wanted.discard(x) + + def iterate(): + if follow and not m.files(): + ff = followfilter(onlyfirst=opts.get('follow_first')) + def want(rev): + return ff.match(rev) and rev in wanted + else: + def want(rev): + return rev in wanted + + for i, window in increasing_windows(0, len(revs)): + yield 'window', revs[0] < revs[-1], revs[-1] + nrevs = [rev for rev in revs[i:i+window] if want(rev)] + for rev in sorted(nrevs): + fns = fncache.get(rev) + if not fns: + def fns_generator(): + for f in change(rev)[3]: + if m(f): + yield f + fns = fns_generator() + yield 'add', rev, fns + for rev in nrevs: + yield 'iter', rev, None + return iterate(), m + +def commit(ui, repo, commitfunc, pats, opts): + '''commit the specified files or all outstanding changes''' + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + message = logmessage(opts) + + # extract addremove carefully -- this function can be called from a command + # that doesn't support addremove + if opts.get('addremove'): + addremove(repo, pats, opts) + + return commitfunc(ui, repo, message, match(repo, pats, opts), opts) + +def commiteditor(repo, ctx, subs): + if ctx.description(): + return ctx.description() + return commitforceeditor(repo, ctx, subs) + +def commitforceeditor(repo, ctx, subs): + edittext = [] + modified, added, removed = ctx.modified(), ctx.added(), ctx.removed() + if ctx.description(): + edittext.append(ctx.description()) + edittext.append("") + edittext.append("") # Empty line between message and comments. + edittext.append(_("HG: Enter commit message." + " Lines beginning with 'HG:' are removed.")) + edittext.append(_("HG: Leave message empty to abort commit.")) + edittext.append("HG: --") + edittext.append(_("HG: user: %s") % ctx.user()) + if ctx.p2(): + edittext.append(_("HG: branch merge")) + if ctx.branch(): + edittext.append(_("HG: branch '%s'") + % encoding.tolocal(ctx.branch())) + edittext.extend([_("HG: subrepo %s") % s for s in subs]) + edittext.extend([_("HG: added %s") % f for f in added]) + edittext.extend([_("HG: changed %s") % f for f in modified]) + edittext.extend([_("HG: removed %s") % f for f in removed]) + if not added and not modified and not removed: + edittext.append(_("HG: no files changed")) + edittext.append("") + # run editor in the repository root + olddir = os.getcwd() + os.chdir(repo.root) + text = repo.ui.edit("\n".join(edittext), ctx.user()) + text = re.sub("(?m)^HG:.*\n", "", text) + os.chdir(olddir) + + if not text.strip(): + raise util.Abort(_("empty commit message")) + + return text diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/commands.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/commands.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,3555 @@ +# commands.py - command processing for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import hex, nullid, nullrev, short +from lock import release +from i18n import _, gettext +import os, re, sys, subprocess, difflib, time +import hg, util, revlog, bundlerepo, extensions, copies, context, error +import patch, help, mdiff, tempfile, url, encoding +import archival, changegroup, cmdutil, sshserver, hbisect +from hgweb import server +import merge as merge_ + +# Commands start here, listed alphabetically + +def add(ui, repo, *pats, **opts): + """add the specified files on the next commit + + Schedule files to be version controlled and added to the + repository. + + The files will be added to the repository at the next commit. To + undo an add before that, see hg forget. + + If no names are given, add all files to the repository. + """ + + bad = [] + exacts = {} + names = [] + m = cmdutil.match(repo, pats, opts) + oldbad = m.bad + m.bad = lambda x,y: bad.append(x) or oldbad(x,y) + + for f in repo.walk(m): + exact = m.exact(f) + if exact or f not in repo.dirstate: + names.append(f) + if ui.verbose or not exact: + ui.status(_('adding %s\n') % m.rel(f)) + if not opts.get('dry_run'): + bad += [f for f in repo.add(names) if f in m.files()] + return bad and 1 or 0 + +def addremove(ui, repo, *pats, **opts): + """add all new files, delete all missing files + + Add all new files and remove all missing files from the + repository. + + New files are ignored if they match any of the patterns in + .hgignore. As with add, these changes take effect at the next + commit. + + Use the -s/--similarity option to detect renamed files. With a + parameter > 0, this compares every removed file with every added + file and records those similar enough as renames. This option + takes a percentage between 0 (disabled) and 100 (files must be + identical) as its parameter. Detecting renamed files this way can + be expensive. + """ + try: + sim = float(opts.get('similarity') or 0) + except ValueError: + raise util.Abort(_('similarity must be a number')) + if sim < 0 or sim > 100: + raise util.Abort(_('similarity must be between 0 and 100')) + return cmdutil.addremove(repo, pats, opts, similarity=sim/100.) + +def annotate(ui, repo, *pats, **opts): + """show changeset information by line for each file + + List changes in files, showing the revision id responsible for + each line + + This command is useful for discovering when a change was made and + by whom. + + Without the -a/--text option, annotate will avoid processing files + it detects as binary. With -a, annotate will annotate the file + anyway, although the results will probably be neither useful + nor desirable. + """ + datefunc = ui.quiet and util.shortdate or util.datestr + getdate = util.cachefunc(lambda x: datefunc(x[0].date())) + + if not pats: + raise util.Abort(_('at least one filename or pattern is required')) + + opmap = [('user', lambda x: ui.shortuser(x[0].user())), + ('number', lambda x: str(x[0].rev())), + ('changeset', lambda x: short(x[0].node())), + ('date', getdate), + ('follow', lambda x: x[0].path()), + ] + + if (not opts.get('user') and not opts.get('changeset') and not opts.get('date') + and not opts.get('follow')): + opts['number'] = 1 + + linenumber = opts.get('line_number') is not None + if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))): + raise util.Abort(_('at least one of -n/-c is required for -l')) + + funcmap = [func for op, func in opmap if opts.get(op)] + if linenumber: + lastfunc = funcmap[-1] + funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1]) + + ctx = repo[opts.get('rev')] + + m = cmdutil.match(repo, pats, opts) + for abs in ctx.walk(m): + fctx = ctx[abs] + if not opts.get('text') and util.binary(fctx.data()): + ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs)) + continue + + lines = fctx.annotate(follow=opts.get('follow'), + linenumber=linenumber) + pieces = [] + + for f in funcmap: + l = [f(n) for n, dummy in lines] + if l: + ml = max(map(len, l)) + pieces.append(["%*s" % (ml, x) for x in l]) + + if pieces: + for p, l in zip(zip(*pieces), lines): + ui.write("%s: %s" % (" ".join(p), l[1])) + +def archive(ui, repo, dest, **opts): + '''create an unversioned archive of a repository revision + + By default, the revision used is the parent of the working + directory; use -r/--rev to specify a different revision. + + To specify the type of archive to create, use -t/--type. Valid + types are: + + "files" (default): a directory full of files + "tar": tar archive, uncompressed + "tbz2": tar archive, compressed using bzip2 + "tgz": tar archive, compressed using gzip + "uzip": zip archive, uncompressed + "zip": zip archive, compressed using deflate + + The exact name of the destination archive or directory is given + using a format string; see 'hg help export' for details. + + Each member added to an archive file has a directory prefix + prepended. Use -p/--prefix to specify a format string for the + prefix. The default is the basename of the archive, with suffixes + removed. + ''' + + ctx = repo[opts.get('rev')] + if not ctx: + raise util.Abort(_('no working directory: please specify a revision')) + node = ctx.node() + dest = cmdutil.make_filename(repo, dest, node) + if os.path.realpath(dest) == repo.root: + raise util.Abort(_('repository root cannot be destination')) + matchfn = cmdutil.match(repo, [], opts) + kind = opts.get('type') or 'files' + prefix = opts.get('prefix') + if dest == '-': + if kind == 'files': + raise util.Abort(_('cannot archive plain files to stdout')) + dest = sys.stdout + if not prefix: prefix = os.path.basename(repo.root) + '-%h' + prefix = cmdutil.make_filename(repo, prefix, node) + archival.archive(repo, dest, node, kind, not opts.get('no_decode'), + matchfn, prefix) + +def backout(ui, repo, node=None, rev=None, **opts): + '''reverse effect of earlier changeset + + Commit the backed out changes as a new changeset. The new + changeset is a child of the backed out changeset. + + If you backout a changeset other than the tip, a new head is + created. This head will be the new tip and you should merge this + backout changeset with another head. + + The --merge option remembers the parent of the working directory + before starting the backout, then merges the new head with that + changeset afterwards. This saves you from doing the merge by hand. + The result of this merge is not committed, as with a normal merge. + + See 'hg help dates' for a list of formats valid for -d/--date. + ''' + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not rev: + rev = node + + if not rev: + raise util.Abort(_("please specify a revision to backout")) + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + + cmdutil.bail_if_changed(repo) + node = repo.lookup(rev) + + op1, op2 = repo.dirstate.parents() + a = repo.changelog.ancestor(op1, node) + if a != node: + raise util.Abort(_('cannot backout change on a different branch')) + + p1, p2 = repo.changelog.parents(node) + if p1 == nullid: + raise util.Abort(_('cannot backout a change with no parents')) + if p2 != nullid: + if not opts.get('parent'): + raise util.Abort(_('cannot backout a merge changeset without ' + '--parent')) + p = repo.lookup(opts['parent']) + if p not in (p1, p2): + raise util.Abort(_('%s is not a parent of %s') % + (short(p), short(node))) + parent = p + else: + if opts.get('parent'): + raise util.Abort(_('cannot use --parent on non-merge changeset')) + parent = p1 + + # the backout should appear on the same branch + branch = repo.dirstate.branch() + hg.clean(repo, node, show_stats=False) + repo.dirstate.setbranch(branch) + revert_opts = opts.copy() + revert_opts['date'] = None + revert_opts['all'] = True + revert_opts['rev'] = hex(parent) + revert_opts['no_backup'] = None + revert(ui, repo, **revert_opts) + commit_opts = opts.copy() + commit_opts['addremove'] = False + if not commit_opts['message'] and not commit_opts['logfile']: + # we don't translate commit messages + commit_opts['message'] = "Backed out changeset %s" % (short(node)) + commit_opts['force_editor'] = True + commit(ui, repo, **commit_opts) + def nice(node): + return '%d:%s' % (repo.changelog.rev(node), short(node)) + ui.status(_('changeset %s backs out changeset %s\n') % + (nice(repo.changelog.tip()), nice(node))) + if op1 != node: + hg.clean(repo, op1, show_stats=False) + if opts.get('merge'): + ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip())) + hg.merge(repo, hex(repo.changelog.tip())) + else: + ui.status(_('the backout changeset is a new head - ' + 'do not forget to merge\n')) + ui.status(_('(use "backout --merge" ' + 'if you want to auto-merge)\n')) + +def bisect(ui, repo, rev=None, extra=None, command=None, + reset=None, good=None, bad=None, skip=None, noupdate=None): + """subdivision search of changesets + + This command helps to find changesets which introduce problems. To + use, mark the earliest changeset you know exhibits the problem as + bad, then mark the latest changeset which is free from the problem + as good. Bisect will update your working directory to a revision + for testing (unless the -U/--noupdate option is specified). Once + you have performed tests, mark the working directory as good or + bad, and bisect will either update to another candidate changeset + or announce that it has found the bad revision. + + As a shortcut, you can also use the revision argument to mark a + revision as good or bad without checking it out first. + + If you supply a command, it will be used for automatic bisection. + Its exit status will be used to mark revisions as good or bad: + status 0 means good, 125 means to skip the revision, 127 + (command not found) will abort the bisection, and any other + non-zero exit status means the revision is bad. + """ + def print_result(nodes, good): + displayer = cmdutil.show_changeset(ui, repo, {}) + if len(nodes) == 1: + # narrowed it down to a single revision + if good: + ui.write(_("The first good revision is:\n")) + else: + ui.write(_("The first bad revision is:\n")) + displayer.show(repo[nodes[0]]) + else: + # multiple possible revisions + if good: + ui.write(_("Due to skipped revisions, the first " + "good revision could be any of:\n")) + else: + ui.write(_("Due to skipped revisions, the first " + "bad revision could be any of:\n")) + for n in nodes: + displayer.show(repo[n]) + + def check_state(state, interactive=True): + if not state['good'] or not state['bad']: + if (good or bad or skip or reset) and interactive: + return + if not state['good']: + raise util.Abort(_('cannot bisect (no known good revisions)')) + else: + raise util.Abort(_('cannot bisect (no known bad revisions)')) + return True + + # backward compatibility + if rev in "good bad reset init".split(): + ui.warn(_("(use of 'hg bisect ' is deprecated)\n")) + cmd, rev, extra = rev, extra, None + if cmd == "good": + good = True + elif cmd == "bad": + bad = True + else: + reset = True + elif extra or good + bad + skip + reset + bool(command) > 1: + raise util.Abort(_('incompatible arguments')) + + if reset: + p = repo.join("bisect.state") + if os.path.exists(p): + os.unlink(p) + return + + state = hbisect.load_state(repo) + + if command: + commandpath = util.find_exe(command) + if commandpath is None: + raise util.Abort(_("cannot find executable: %s") % command) + changesets = 1 + try: + while changesets: + # update state + status = subprocess.call([commandpath]) + if status == 125: + transition = "skip" + elif status == 0: + transition = "good" + # status < 0 means process was killed + elif status == 127: + raise util.Abort(_("failed to execute %s") % command) + elif status < 0: + raise util.Abort(_("%s killed") % command) + else: + transition = "bad" + ctx = repo[rev or '.'] + state[transition].append(ctx.node()) + ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition)) + check_state(state, interactive=False) + # bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + # update to next check + cmdutil.bail_if_changed(repo) + hg.clean(repo, nodes[0], show_stats=False) + finally: + hbisect.save_state(repo, state) + return print_result(nodes, not status) + + # update state + node = repo.lookup(rev or '.') + if good: + state['good'].append(node) + elif bad: + state['bad'].append(node) + elif skip: + state['skip'].append(node) + + hbisect.save_state(repo, state) + + if not check_state(state): + return + + # actually bisect + nodes, changesets, good = hbisect.bisect(repo.changelog, state) + if changesets == 0: + print_result(nodes, good) + else: + assert len(nodes) == 1 # only a single node can be tested next + node = nodes[0] + # compute the approximate number of remaining tests + tests, size = 0, 2 + while size <= changesets: + tests, size = tests + 1, size * 2 + rev = repo.changelog.rev(node) + ui.write(_("Testing changeset %s:%s " + "(%s changesets remaining, ~%s tests)\n") + % (rev, short(node), changesets, tests)) + if not noupdate: + cmdutil.bail_if_changed(repo) + return hg.clean(repo, node) + +def branch(ui, repo, label=None, **opts): + """set or show the current branch name + + With no argument, show the current branch name. With one argument, + set the working directory branch name (the branch will not exist + in the repository until the next commit). Standard practice + recommends that primary development take place on the 'default' + branch. + + Unless -f/--force is specified, branch will not let you set a + branch name that already exists, even if it's inactive. + + Use -C/--clean to reset the working directory branch to that of + the parent of the working directory, negating a previous branch + change. + + Use the command 'hg update' to switch to an existing branch. Use + 'hg commit --close-branch' to mark this branch as closed. + """ + + if opts.get('clean'): + label = repo[None].parents()[0].branch() + repo.dirstate.setbranch(label) + ui.status(_('reset working directory to branch %s\n') % label) + elif label: + if not opts.get('force') and label in repo.branchtags(): + if label not in [p.branch() for p in repo.parents()]: + raise util.Abort(_('a branch of the same name already exists' + ' (use --force to override)')) + repo.dirstate.setbranch(encoding.fromlocal(label)) + ui.status(_('marked working directory as branch %s\n') % label) + else: + ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch())) + +def branches(ui, repo, active=False, closed=False): + """list repository named branches + + List the repository's named branches, indicating which ones are + inactive. If -c/--closed is specified, also list branches which have + been marked closed (see hg commit --close-branch). + + If -a/--active is specified, only show active branches. A branch + is considered active if it contains repository heads. + + Use the command 'hg update' to switch to an existing branch. + """ + + hexfunc = ui.debugflag and hex or short + activebranches = [encoding.tolocal(repo[n].branch()) + for n in repo.heads()] + def testactive(tag, node): + realhead = tag in activebranches + open = node in repo.branchheads(tag, closed=False) + return realhead and open + branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag) + for tag, node in repo.branchtags().items()], + reverse=True) + + for isactive, node, tag in branches: + if (not active) or isactive: + if ui.quiet: + ui.write("%s\n" % tag) + else: + hn = repo.lookup(node) + if isactive: + notice = '' + elif hn not in repo.branchheads(tag, closed=False): + if not closed: + continue + notice = ' (closed)' + else: + notice = ' (inactive)' + rev = str(node).rjust(31 - encoding.colwidth(tag)) + data = tag, rev, hexfunc(hn), notice + ui.write("%s %s:%s%s\n" % data) + +def bundle(ui, repo, fname, dest=None, **opts): + """create a changegroup file + + Generate a compressed changegroup file collecting changesets not + known to be in another repository. + + If no destination repository is specified the destination is + assumed to have all the nodes specified by one or more --base + parameters. To create a bundle containing all changesets, use + -a/--all (or --base null). + + You can change compression method with the -t/--type option. + The available compression methods are: none, bzip2, and + gzip (by default, bundles are compressed using bzip2). + + The bundle file can then be transferred using conventional means + and applied to another repository with the unbundle or pull + command. This is useful when direct push and pull are not + available or when exporting an entire repository is undesirable. + + Applying bundles preserves all changeset contents including + permissions, copy/rename information, and revision history. + """ + revs = opts.get('rev') or None + if revs: + revs = [repo.lookup(rev) for rev in revs] + if opts.get('all'): + base = ['null'] + else: + base = opts.get('base') + if base: + if dest: + raise util.Abort(_("--base is incompatible with specifying " + "a destination")) + base = [repo.lookup(rev) for rev in base] + # create the right base + # XXX: nodesbetween / changegroup* should be "fixed" instead + o = [] + has = set((nullid,)) + for n in base: + has.update(repo.changelog.reachable(n)) + if revs: + visit = list(revs) + else: + visit = repo.changelog.heads() + seen = {} + while visit: + n = visit.pop(0) + parents = [p for p in repo.changelog.parents(n) if p not in has] + if len(parents) == 0: + o.insert(0, n) + else: + for p in parents: + if p not in seen: + seen[p] = 1 + visit.append(p) + else: + dest, revs, checkout = hg.parseurl( + ui.expandpath(dest or 'default-push', dest or 'default'), revs) + other = hg.repository(cmdutil.remoteui(repo, opts), dest) + o = repo.findoutgoing(other, force=opts.get('force')) + + if revs: + cg = repo.changegroupsubset(o, revs, 'bundle') + else: + cg = repo.changegroup(o, 'bundle') + + bundletype = opts.get('type', 'bzip2').lower() + btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'} + bundletype = btypes.get(bundletype) + if bundletype not in changegroup.bundletypes: + raise util.Abort(_('unknown bundle type specified with --type')) + + changegroup.writebundle(cg, fname, bundletype) + +def cat(ui, repo, file1, *pats, **opts): + """output the current or given revision of files + + Print the specified files as they were at the given revision. If + no revision is given, the parent of the working directory is used, + or tip if no revision is checked out. + + Output may be to a file, in which case the name of the file is + given using a format string. The formatting rules are the same as + for the export command, with the following additions: + + %s basename of file being printed + %d dirname of file being printed, or '.' if in repository root + %p root-relative path name of file being printed + """ + ctx = repo[opts.get('rev')] + err = 1 + m = cmdutil.match(repo, (file1,) + pats, opts) + for abs in ctx.walk(m): + fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs) + data = ctx[abs].data() + if opts.get('decode'): + data = repo.wwritedata(abs, data) + fp.write(data) + err = 0 + return err + +def clone(ui, source, dest=None, **opts): + """make a copy of an existing repository + + Create a copy of an existing repository in a new directory. + + If no destination directory name is specified, it defaults to the + basename of the source. + + The location of the source is added to the new repository's + .hg/hgrc file, as the default to be used for future pulls. + + If you use the -r/--rev option to clone up to a specific revision, + no subsequent revisions (including subsequent tags) will be + present in the cloned repository. This option implies --pull, even + on local repositories. + + By default, clone will check out the head of the 'default' branch. + If the -U/--noupdate option is used, the new clone will contain + only a repository (.hg) and no working copy (the working copy + parent is the null revision). + + See 'hg help urls' for valid source format details. + + It is possible to specify an ssh:// URL as the destination, but no + .hg/hgrc and working directory will be created on the remote side. + Please see 'hg help urls' for important details about ssh:// URLs. + + For efficiency, hardlinks are used for cloning whenever the source + and destination are on the same filesystem (note this applies only + to the repository data, not to the checked out files). Some + filesystems, such as AFS, implement hardlinking incorrectly, but + do not report errors. In these cases, use the --pull option to + avoid hardlinking. + + In some cases, you can clone repositories and checked out files + using full hardlinks with + + $ cp -al REPO REPOCLONE + + This is the fastest way to clone, but it is not always safe. The + operation is not atomic (making sure REPO is not modified during + the operation is up to you) and you have to make sure your editor + breaks hardlinks (Emacs and most Linux Kernel tools do so). Also, + this is not compatible with certain extensions that place their + metadata under the .hg directory, such as mq. + + """ + hg.clone(cmdutil.remoteui(ui, opts), source, dest, + pull=opts.get('pull'), + stream=opts.get('uncompressed'), + rev=opts.get('rev'), + update=not opts.get('noupdate')) + +def commit(ui, repo, *pats, **opts): + """commit the specified files or all outstanding changes + + Commit changes to the given files into the repository. Unlike a + centralized RCS, this operation is a local operation. See hg push + for a way to actively distribute your changes. + + If a list of files is omitted, all changes reported by "hg status" + will be committed. + + If you are committing the result of a merge, do not provide any + filenames or -I/-X filters. + + If no commit message is specified, the configured editor is + started to prompt you for a message. + + See 'hg help dates' for a list of formats valid for -d/--date. + """ + extra = {} + if opts.get('close_branch'): + extra['close'] = 1 + e = cmdutil.commiteditor + if opts.get('force_editor'): + e = cmdutil.commitforceeditor + + def commitfunc(ui, repo, message, match, opts): + return repo.commit(message, opts.get('user'), opts.get('date'), match, + editor=e, extra=extra) + + node = cmdutil.commit(ui, repo, commitfunc, pats, opts) + if not node: + ui.status(_("nothing changed\n")) + return + cl = repo.changelog + rev = cl.rev(node) + parents = cl.parentrevs(rev) + if rev - 1 in parents: + # one of the parents was the old tip + pass + elif (parents == (nullrev, nullrev) or + len(cl.heads(cl.node(parents[0]))) > 1 and + (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)): + ui.status(_('created new head\n')) + + if ui.debugflag: + ui.write(_('committed changeset %d:%s\n') % (rev,hex(node))) + elif ui.verbose: + ui.write(_('committed changeset %d:%s\n') % (rev,short(node))) + +def copy(ui, repo, *pats, **opts): + """mark files as copied for the next commit + + Mark dest as having copies of source files. If dest is a + directory, copies are put in that directory. If dest is a file, + the source must be a single file. + + By default, this command copies the contents of files as they + exist in the working directory. If invoked with -A/--after, the + operation is recorded, but no copying is performed. + + This command takes effect with the next commit. To undo a copy + before that, see hg revert. + """ + wlock = repo.wlock(False) + try: + return cmdutil.copy(ui, repo, pats, opts) + finally: + wlock.release() + +def debugancestor(ui, repo, *args): + """find the ancestor revision of two revisions in a given index""" + if len(args) == 3: + index, rev1, rev2 = args + r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) + lookup = r.lookup + elif len(args) == 2: + if not repo: + raise util.Abort(_("There is no Mercurial repository here " + "(.hg not found)")) + rev1, rev2 = args + r = repo.changelog + lookup = repo.lookup + else: + raise util.Abort(_('either two or three arguments required')) + a = r.ancestor(lookup(rev1), lookup(rev2)) + ui.write("%d:%s\n" % (r.rev(a), hex(a))) + +def debugcommands(ui, cmd='', *args): + for cmd, vals in sorted(table.iteritems()): + cmd = cmd.split('|')[0].strip('^') + opts = ', '.join([i[1] for i in vals[1]]) + ui.write('%s: %s\n' % (cmd, opts)) + +def debugcomplete(ui, cmd='', **opts): + """returns the completion list associated with the given command""" + + if opts.get('options'): + options = [] + otables = [globalopts] + if cmd: + aliases, entry = cmdutil.findcmd(cmd, table, False) + otables.append(entry[1]) + for t in otables: + for o in t: + if o[0]: + options.append('-%s' % o[0]) + options.append('--%s' % o[1]) + ui.write("%s\n" % "\n".join(options)) + return + + cmdlist = cmdutil.findpossible(cmd, table) + if ui.verbose: + cmdlist = [' '.join(c[0]) for c in cmdlist.values()] + ui.write("%s\n" % "\n".join(sorted(cmdlist))) + +def debugfsinfo(ui, path = "."): + file('.debugfsinfo', 'w').write('') + ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no')) + ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no')) + ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo') + and 'yes' or 'no')) + os.unlink('.debugfsinfo') + +def debugrebuildstate(ui, repo, rev="tip"): + """rebuild the dirstate as it would look like for the given revision""" + ctx = repo[rev] + wlock = repo.wlock() + try: + repo.dirstate.rebuild(ctx.node(), ctx.manifest()) + finally: + wlock.release() + +def debugcheckstate(ui, repo): + """validate the correctness of the current dirstate""" + parent1, parent2 = repo.dirstate.parents() + m1 = repo[parent1].manifest() + m2 = repo[parent2].manifest() + errors = 0 + for f in repo.dirstate: + state = repo.dirstate[f] + if state in "nr" and f not in m1: + ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state)) + errors += 1 + if state in "a" and f in m1: + ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state)) + errors += 1 + if state in "m" and f not in m1 and f not in m2: + ui.warn(_("%s in state %s, but not in either manifest\n") % + (f, state)) + errors += 1 + for f in m1: + state = repo.dirstate[f] + if state not in "nrm": + ui.warn(_("%s in manifest1, but listed as state %s") % (f, state)) + errors += 1 + if errors: + error = _(".hg/dirstate inconsistent with current parent's manifest") + raise util.Abort(error) + +def showconfig(ui, repo, *values, **opts): + """show combined config settings from all hgrc files + + With no arguments, print names and values of all config items. + + With one argument of the form section.name, print just the value + of that config item. + + With multiple arguments, print names and values of all config + items with matching section names. + + With --debug, the source (filename and line number) is printed + for each config item. + """ + + untrusted = bool(opts.get('untrusted')) + if values: + if len([v for v in values if '.' in v]) > 1: + raise util.Abort(_('only one config item permitted')) + for section, name, value in ui.walkconfig(untrusted=untrusted): + sectname = section + '.' + name + if values: + for v in values: + if v == section: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write('%s=%s\n' % (sectname, value)) + elif v == sectname: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write(value, '\n') + else: + ui.debug('%s: ' % + ui.configsource(section, name, untrusted)) + ui.write('%s=%s\n' % (sectname, value)) + +def debugsetparents(ui, repo, rev1, rev2=None): + """manually set the parents of the current working directory + + This is useful for writing repository conversion tools, but should + be used with care. + """ + + if not rev2: + rev2 = hex(nullid) + + wlock = repo.wlock() + try: + repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2)) + finally: + wlock.release() + +def debugstate(ui, repo, nodates=None): + """show the contents of the current dirstate""" + timestr = "" + showdate = not nodates + for file_, ent in sorted(repo.dirstate._map.iteritems()): + if showdate: + if ent[3] == -1: + # Pad or slice to locale representation + locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0))) + timestr = 'unset' + timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr)) + else: + timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3])) + if ent[1] & 020000: + mode = 'lnk' + else: + mode = '%3o' % (ent[1] & 0777) + ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_)) + for f in repo.dirstate.copies(): + ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f)) + +def debugsub(ui, repo, rev=None): + if rev == '': + rev = None + for k,v in sorted(repo[rev].substate.items()): + ui.write('path %s\n' % k) + ui.write(' source %s\n' % v[0]) + ui.write(' revision %s\n' % v[1]) + +def debugdata(ui, file_, rev): + """dump the contents of a data file revision""" + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i") + try: + ui.write(r.revision(r.lookup(rev))) + except KeyError: + raise util.Abort(_('invalid revision identifier %s') % rev) + +def debugdate(ui, date, range=None, **opts): + """parse and display a date""" + if opts["extended"]: + d = util.parsedate(date, util.extendeddateformats) + else: + d = util.parsedate(date) + ui.write("internal: %s %s\n" % d) + ui.write("standard: %s\n" % util.datestr(d)) + if range: + m = util.matchdate(range) + ui.write("match: %s\n" % m(d[0])) + +def debugindex(ui, file_): + """dump the contents of an index file""" + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + ui.write(" rev offset length base linkrev" + " nodeid p1 p2\n") + for i in r: + node = r.node(i) + try: + pp = r.parents(node) + except: + pp = [nullid, nullid] + ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % ( + i, r.start(i), r.length(i), r.base(i), r.linkrev(i), + short(node), short(pp[0]), short(pp[1]))) + +def debugindexdot(ui, file_): + """dump an index DAG as a graphviz dot file""" + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) + ui.write("digraph G {\n") + for i in r: + node = r.node(i) + pp = r.parents(node) + ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i)) + if pp[1] != nullid: + ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i)) + ui.write("}\n") + +def debuginstall(ui): + '''test Mercurial installation''' + + def writetemp(contents): + (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-") + f = os.fdopen(fd, "wb") + f.write(contents) + f.close() + return name + + problems = 0 + + # encoding + ui.status(_("Checking encoding (%s)...\n") % encoding.encoding) + try: + encoding.fromlocal("test") + except util.Abort, inst: + ui.write(" %s\n" % inst) + ui.write(_(" (check that your locale is properly set)\n")) + problems += 1 + + # compiled modules + ui.status(_("Checking extensions...\n")) + try: + import bdiff, mpatch, base85 + except Exception, inst: + ui.write(" %s\n" % inst) + ui.write(_(" One or more extensions could not be found")) + ui.write(_(" (check that you compiled the extensions)\n")) + problems += 1 + + # templates + ui.status(_("Checking templates...\n")) + try: + import templater + templater.templater(templater.templatepath("map-cmdline.default")) + except Exception, inst: + ui.write(" %s\n" % inst) + ui.write(_(" (templates seem to have been installed incorrectly)\n")) + problems += 1 + + # patch + ui.status(_("Checking patch...\n")) + patchproblems = 0 + a = "1\n2\n3\n4\n" + b = "1\n2\n3\ninsert\n4\n" + fa = writetemp(a) + d = mdiff.unidiff(a, None, b, None, os.path.basename(fa), + os.path.basename(fa)) + fd = writetemp(d) + + files = {} + try: + patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files) + except util.Abort, e: + ui.write(_(" patch call failed:\n")) + ui.write(" " + str(e) + "\n") + patchproblems += 1 + else: + if list(files) != [os.path.basename(fa)]: + ui.write(_(" unexpected patch output!\n")) + patchproblems += 1 + a = file(fa).read() + if a != b: + ui.write(_(" patch test failed!\n")) + patchproblems += 1 + + if patchproblems: + if ui.config('ui', 'patch'): + ui.write(_(" (Current patch tool may be incompatible with patch," + " or misconfigured. Please check your .hgrc file)\n")) + else: + ui.write(_(" Internal patcher failure, please report this error" + " to http://mercurial.selenic.com/bts/\n")) + problems += patchproblems + + os.unlink(fa) + os.unlink(fd) + + # editor + ui.status(_("Checking commit editor...\n")) + editor = ui.geteditor() + cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0]) + if not cmdpath: + if editor == 'vi': + ui.write(_(" No commit editor set and can't find vi in PATH\n")) + ui.write(_(" (specify a commit editor in your .hgrc file)\n")) + else: + ui.write(_(" Can't find editor '%s' in PATH\n") % editor) + ui.write(_(" (specify a commit editor in your .hgrc file)\n")) + problems += 1 + + # check username + ui.status(_("Checking username...\n")) + user = os.environ.get("HGUSER") + if user is None: + user = ui.config("ui", "username") + if user is None: + user = os.environ.get("EMAIL") + if not user: + ui.warn(" ") + ui.username() + ui.write(_(" (specify a username in your .hgrc file)\n")) + + if not problems: + ui.status(_("No problems detected\n")) + else: + ui.write(_("%s problems detected," + " please check your install!\n") % problems) + + return problems + +def debugrename(ui, repo, file1, *pats, **opts): + """dump rename information""" + + ctx = repo[opts.get('rev')] + m = cmdutil.match(repo, (file1,) + pats, opts) + for abs in ctx.walk(m): + fctx = ctx[abs] + o = fctx.filelog().renamed(fctx.filenode()) + rel = m.rel(abs) + if o: + ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1]))) + else: + ui.write(_("%s not renamed\n") % rel) + +def debugwalk(ui, repo, *pats, **opts): + """show how files match on given patterns""" + m = cmdutil.match(repo, pats, opts) + items = list(repo.walk(m)) + if not items: + return + fmt = 'f %%-%ds %%-%ds %%s' % ( + max([len(abs) for abs in items]), + max([len(m.rel(abs)) for abs in items])) + for abs in items: + line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '') + ui.write("%s\n" % line.rstrip()) + +def diff(ui, repo, *pats, **opts): + """diff repository (or selected files) + + Show differences between revisions for the specified files. + + Differences between files are shown using the unified diff format. + + NOTE: diff may generate unexpected results for merges, as it will + default to comparing against the working directory's first parent + changeset if no revisions are specified. + + When two revision arguments are given, then changes are shown + between those revisions. If only one revision is specified then + that revision is compared to the working directory, and, when no + revisions are specified, the working directory files are compared + to its parent. + + Without the -a/--text option, diff will avoid generating diffs of + files it detects as binary. With -a, diff will generate a diff + anyway, probably with undesirable results. + + Use the -g/--git option to generate diffs in the git extended diff + format. For more information, read 'hg help diffs'. + """ + + revs = opts.get('rev') + change = opts.get('change') + + if revs and change: + msg = _('cannot specify --rev and --change at the same time') + raise util.Abort(msg) + elif change: + node2 = repo.lookup(change) + node1 = repo[node2].parents()[0].node() + else: + node1, node2 = cmdutil.revpair(repo, revs) + + m = cmdutil.match(repo, pats, opts) + it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts)) + for chunk in it: + ui.write(chunk) + +def export(ui, repo, *changesets, **opts): + """dump the header and diffs for one or more changesets + + Print the changeset header and diffs for one or more revisions. + + The information shown in the changeset header is: author, + changeset hash, parent(s) and commit comment. + + NOTE: export may generate unexpected diff output for merge + changesets, as it will compare the merge changeset against its + first parent only. + + Output may be to a file, in which case the name of the file is + given using a format string. The formatting rules are as follows: + + %% literal "%" character + %H changeset hash (40 bytes of hexadecimal) + %N number of patches being generated + %R changeset revision number + %b basename of the exporting repository + %h short-form changeset hash (12 bytes of hexadecimal) + %n zero-padded sequence number, starting at 1 + %r zero-padded changeset revision number + + Without the -a/--text option, export will avoid generating diffs + of files it detects as binary. With -a, export will generate a + diff anyway, probably with undesirable results. + + Use the -g/--git option to generate diffs in the git extended diff + format. See 'hg help diffs' for more information. + + With the --switch-parent option, the diff will be against the + second parent. It can be useful to review a merge. + """ + if not changesets: + raise util.Abort(_("export requires at least one changeset")) + revs = cmdutil.revrange(repo, changesets) + if len(revs) > 1: + ui.note(_('exporting patches:\n')) + else: + ui.note(_('exporting patch:\n')) + patch.export(repo, revs, template=opts.get('output'), + switch_parent=opts.get('switch_parent'), + opts=patch.diffopts(ui, opts)) + +def forget(ui, repo, *pats, **opts): + """forget the specified files on the next commit + + Mark the specified files so they will no longer be tracked + after the next commit. + + This only removes files from the current branch, not from the + entire project history, and it does not delete them from the + working directory. + + To undo a forget before the next commit, see hg add. + """ + + if not pats: + raise util.Abort(_('no files specified')) + + m = cmdutil.match(repo, pats, opts) + s = repo.status(match=m, clean=True) + forget = sorted(s[0] + s[1] + s[3] + s[6]) + + for f in m.files(): + if f not in repo.dirstate and not os.path.isdir(m.rel(f)): + ui.warn(_('not removing %s: file is already untracked\n') + % m.rel(f)) + + for f in forget: + if ui.verbose or not m.exact(f): + ui.status(_('removing %s\n') % m.rel(f)) + + repo.remove(forget, unlink=False) + +def grep(ui, repo, pattern, *pats, **opts): + """search for a pattern in specified files and revisions + + Search revisions of files for a regular expression. + + This command behaves differently than Unix grep. It only accepts + Python/Perl regexps. It searches repository history, not the + working directory. It always prints the revision number in which a + match appears. + + By default, grep only prints output for the first revision of a + file in which it finds a match. To get it to print every revision + that contains a change in match status ("-" for a match that + becomes a non-match, or "+" for a non-match that becomes a match), + use the --all flag. + """ + reflags = 0 + if opts.get('ignore_case'): + reflags |= re.I + try: + regexp = re.compile(pattern, reflags) + except Exception, inst: + ui.warn(_("grep: invalid match pattern: %s\n") % inst) + return None + sep, eol = ':', '\n' + if opts.get('print0'): + sep = eol = '\0' + + getfile = util.lrucachefunc(repo.file) + + def matchlines(body): + begin = 0 + linenum = 0 + while True: + match = regexp.search(body, begin) + if not match: + break + mstart, mend = match.span() + linenum += body.count('\n', begin, mstart) + 1 + lstart = body.rfind('\n', begin, mstart) + 1 or begin + begin = body.find('\n', mend) + 1 or len(body) + lend = begin - 1 + yield linenum, mstart - lstart, mend - lstart, body[lstart:lend] + + class linestate(object): + def __init__(self, line, linenum, colstart, colend): + self.line = line + self.linenum = linenum + self.colstart = colstart + self.colend = colend + + def __hash__(self): + return hash((self.linenum, self.line)) + + def __eq__(self, other): + return self.line == other.line + + matches = {} + copies = {} + def grepbody(fn, rev, body): + matches[rev].setdefault(fn, []) + m = matches[rev][fn] + for lnum, cstart, cend, line in matchlines(body): + s = linestate(line, lnum, cstart, cend) + m.append(s) + + def difflinestates(a, b): + sm = difflib.SequenceMatcher(None, a, b) + for tag, alo, ahi, blo, bhi in sm.get_opcodes(): + if tag == 'insert': + for i in xrange(blo, bhi): + yield ('+', b[i]) + elif tag == 'delete': + for i in xrange(alo, ahi): + yield ('-', a[i]) + elif tag == 'replace': + for i in xrange(alo, ahi): + yield ('-', a[i]) + for i in xrange(blo, bhi): + yield ('+', b[i]) + + def display(fn, r, pstates, states): + datefunc = ui.quiet and util.shortdate or util.datestr + found = False + filerevmatches = {} + if opts.get('all'): + iter = difflinestates(pstates, states) + else: + iter = [('', l) for l in states] + for change, l in iter: + cols = [fn, str(r)] + if opts.get('line_number'): + cols.append(str(l.linenum)) + if opts.get('all'): + cols.append(change) + if opts.get('user'): + cols.append(ui.shortuser(get(r)[1])) + if opts.get('date'): + cols.append(datefunc(get(r)[2])) + if opts.get('files_with_matches'): + c = (fn, r) + if c in filerevmatches: + continue + filerevmatches[c] = 1 + else: + cols.append(l.line) + ui.write(sep.join(cols), eol) + found = True + return found + + skip = {} + revfiles = {} + get = util.cachefunc(lambda r: repo[r].changeset()) + changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) + found = False + follow = opts.get('follow') + for st, rev, fns in changeiter: + if st == 'window': + matches.clear() + revfiles.clear() + elif st == 'add': + ctx = repo[rev] + pctx = ctx.parents()[0] + parent = pctx.rev() + matches.setdefault(rev, {}) + matches.setdefault(parent, {}) + files = revfiles.setdefault(rev, []) + for fn in fns: + flog = getfile(fn) + try: + fnode = ctx.filenode(fn) + except error.LookupError: + continue + + copied = flog.renamed(fnode) + copy = follow and copied and copied[0] + if copy: + copies.setdefault(rev, {})[fn] = copy + if fn in skip: + if copy: + skip[copy] = True + continue + files.append(fn) + + if not matches[rev].has_key(fn): + grepbody(fn, rev, flog.read(fnode)) + + pfn = copy or fn + if not matches[parent].has_key(pfn): + try: + fnode = pctx.filenode(pfn) + grepbody(pfn, parent, flog.read(fnode)) + except error.LookupError: + pass + elif st == 'iter': + parent = repo[rev].parents()[0].rev() + for fn in sorted(revfiles.get(rev, [])): + states = matches[rev][fn] + copy = copies.get(rev, {}).get(fn) + if fn in skip: + if copy: + skip[copy] = True + continue + pstates = matches.get(parent, {}).get(copy or fn, []) + if pstates or states: + r = display(fn, rev, pstates, states) + found = found or r + if r and not opts.get('all'): + skip[fn] = True + if copy: + skip[copy] = True + +def heads(ui, repo, *branchrevs, **opts): + """show current repository heads or show branch heads + + With no arguments, show all repository head changesets. + + Repository "heads" are changesets that don't have child + changesets. They are where development generally takes place and + are the usual targets for update and merge operations. + + If one or more REV is given, the "branch heads" will be shown for + the named branch associated with that revision. The name of the + branch is called the revision's branch tag. + + Branch heads are revisions on a given named branch that do not have + any descendants on the same branch. A branch head could be a true head + or it could be the last changeset on a branch before a new branch + was created. If none of the branch heads are true heads, the branch + is considered inactive. If -c/--closed is specified, also show branch + heads marked closed (see hg commit --close-branch). + + If STARTREV is specified only those heads (or branch heads) that + are descendants of STARTREV will be displayed. + """ + if opts.get('rev'): + start = repo.lookup(opts['rev']) + else: + start = None + closed = opts.get('closed') + hideinactive, _heads = opts.get('active'), None + if not branchrevs: + if closed: + raise error.Abort(_('you must specify a branch to use --closed')) + # Assume we're looking repo-wide heads if no revs were specified. + heads = repo.heads(start) + else: + if hideinactive: + _heads = repo.heads(start) + heads = [] + visitedset = set() + for branchrev in branchrevs: + branch = repo[branchrev].branch() + if branch in visitedset: + continue + visitedset.add(branch) + bheads = repo.branchheads(branch, start, closed=closed) + if not bheads: + if not opts.get('rev'): + ui.warn(_("no open branch heads on branch %s\n") % branch) + elif branch != branchrev: + ui.warn(_("no changes on branch %s containing %s are " + "reachable from %s\n") + % (branch, branchrev, opts.get('rev'))) + else: + ui.warn(_("no changes on branch %s are reachable from %s\n") + % (branch, opts.get('rev'))) + if hideinactive: + bheads = [bhead for bhead in bheads if bhead in _heads] + heads.extend(bheads) + if not heads: + return 1 + displayer = cmdutil.show_changeset(ui, repo, opts) + for n in heads: + displayer.show(repo[n]) + +def help_(ui, name=None, with_version=False): + """show help for a given topic or a help overview + + With no arguments, print a list of commands with short help messages. + + Given a topic, extension, or command name, print help for that + topic.""" + option_lists = [] + + def addglobalopts(aliases): + if ui.verbose: + option_lists.append((_("global options:"), globalopts)) + if name == 'shortlist': + option_lists.append((_('use "hg help" for the full list ' + 'of commands'), ())) + else: + if name == 'shortlist': + msg = _('use "hg help" for the full list of commands ' + 'or "hg -v" for details') + elif aliases: + msg = _('use "hg -v help%s" to show aliases and ' + 'global options') % (name and " " + name or "") + else: + msg = _('use "hg -v help %s" to show global options') % name + option_lists.append((msg, ())) + + def helpcmd(name): + if with_version: + version_(ui) + ui.write('\n') + + try: + aliases, i = cmdutil.findcmd(name, table, False) + except error.AmbiguousCommand, inst: + select = lambda c: c.lstrip('^').startswith(inst.args[0]) + helplist(_('list of commands:\n\n'), select) + return + + # synopsis + if len(i) > 2: + if i[2].startswith('hg'): + ui.write("%s\n" % i[2]) + else: + ui.write('hg %s %s\n' % (aliases[0], i[2])) + else: + ui.write('hg %s\n' % aliases[0]) + + # aliases + if not ui.quiet and len(aliases) > 1: + ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:])) + + # description + doc = gettext(i[0].__doc__) + if not doc: + doc = _("(no help text available)") + if ui.quiet: + doc = doc.splitlines(0)[0] + ui.write("\n%s\n" % doc.rstrip()) + + if not ui.quiet: + # options + if i[1]: + option_lists.append((_("options:\n"), i[1])) + + addglobalopts(False) + + def helplist(header, select=None): + h = {} + cmds = {} + for c, e in table.iteritems(): + f = c.split("|", 1)[0] + if select and not select(f): + continue + if (not select and name != 'shortlist' and + e[0].__module__ != __name__): + continue + if name == "shortlist" and not f.startswith("^"): + continue + f = f.lstrip("^") + if not ui.debugflag and f.startswith("debug"): + continue + doc = e[0].__doc__ + if doc and 'DEPRECATED' in doc and not ui.verbose: + continue + doc = gettext(doc) + if not doc: + doc = _("(no help text available)") + h[f] = doc.splitlines(0)[0].rstrip() + cmds[f] = c.lstrip("^") + + if not h: + ui.status(_('no commands defined\n')) + return + + ui.status(header) + fns = sorted(h) + m = max(map(len, fns)) + for f in fns: + if ui.verbose: + commands = cmds[f].replace("|",", ") + ui.write(" %s:\n %s\n"%(commands, h[f])) + else: + ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4))) + + if name != 'shortlist': + exts, maxlength = extensions.enabled() + ui.write(help.listexts(_('enabled extensions:'), exts, maxlength)) + + if not ui.quiet: + addglobalopts(True) + + def helptopic(name): + for names, header, doc in help.helptable: + if name in names: + break + else: + raise error.UnknownCommand(name) + + # description + if not doc: + doc = _("(no help text available)") + if hasattr(doc, '__call__'): + doc = doc() + + ui.write("%s\n" % header) + ui.write("%s\n" % doc.rstrip()) + + def helpext(name): + try: + mod = extensions.find(name) + except KeyError: + raise error.UnknownCommand(name) + + doc = gettext(mod.__doc__) or _('no help text available') + doc = doc.splitlines(0) + ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0])) + for d in doc[1:]: + ui.write(d, '\n') + + ui.status('\n') + + try: + ct = mod.cmdtable + except AttributeError: + ct = {} + + modcmds = set([c.split('|', 1)[0] for c in ct]) + helplist(_('list of commands:\n\n'), modcmds.__contains__) + + if name and name != 'shortlist': + i = None + for f in (helptopic, helpcmd, helpext): + try: + f(name) + i = None + break + except error.UnknownCommand, inst: + i = inst + if i: + raise i + + else: + # program name + if ui.verbose or with_version: + version_(ui) + else: + ui.status(_("Mercurial Distributed SCM\n")) + ui.status('\n') + + # list of commands + if name == "shortlist": + header = _('basic commands:\n\n') + else: + header = _('list of commands:\n\n') + + helplist(header) + + # list all option lists + opt_output = [] + for title, options in option_lists: + opt_output.append(("\n%s" % title, None)) + for shortopt, longopt, default, desc in options: + if "DEPRECATED" in desc and not ui.verbose: continue + opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt, + longopt and " --%s" % longopt), + "%s%s" % (desc, + default + and _(" (default: %s)") % default + or ""))) + + if not name: + ui.write(_("\nadditional help topics:\n\n")) + topics = [] + for names, header, doc in help.helptable: + names = [(-len(name), name) for name in names] + names.sort() + topics.append((names[0][1], header)) + topics_len = max([len(s[0]) for s in topics]) + for t, desc in topics: + ui.write(" %-*s %s\n" % (topics_len, t, desc)) + + if opt_output: + opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0]) + for first, second in opt_output: + if second: + second = util.wrap(second, opts_len + 3) + ui.write(" %-*s %s\n" % (opts_len, first, second)) + else: + ui.write("%s\n" % first) + +def identify(ui, repo, source=None, + rev=None, num=None, id=None, branch=None, tags=None): + """identify the working copy or specified revision + + With no revision, print a summary of the current state of the + repository. + + Specifying a path to a repository root or Mercurial bundle will + cause lookup to operate on that repository/bundle. + + This summary identifies the repository state using one or two + parent hash identifiers, followed by a "+" if there are + uncommitted changes in the working directory, a list of tags for + this revision and a branch name for non-default branches. + """ + + if not repo and not source: + raise util.Abort(_("There is no Mercurial repository here " + "(.hg not found)")) + + hexfunc = ui.debugflag and hex or short + default = not (num or id or branch or tags) + output = [] + + revs = [] + if source: + source, revs, checkout = hg.parseurl(ui.expandpath(source), []) + repo = hg.repository(ui, source) + + if not repo.local(): + if not rev and revs: + rev = revs[0] + if not rev: + rev = "tip" + if num or branch or tags: + raise util.Abort( + "can't query remote revision number, branch, or tags") + output = [hexfunc(repo.lookup(rev))] + elif not rev: + ctx = repo[None] + parents = ctx.parents() + changed = False + if default or id or num: + changed = ctx.files() + ctx.deleted() + if default or id: + output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]), + (changed) and "+" or "")] + if num: + output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]), + (changed) and "+" or "")) + else: + ctx = repo[rev] + if default or id: + output = [hexfunc(ctx.node())] + if num: + output.append(str(ctx.rev())) + + if repo.local() and default and not ui.quiet: + b = encoding.tolocal(ctx.branch()) + if b != 'default': + output.append("(%s)" % b) + + # multiple tags for a single parent separated by '/' + t = "/".join(ctx.tags()) + if t: + output.append(t) + + if branch: + output.append(encoding.tolocal(ctx.branch())) + + if tags: + output.extend(ctx.tags()) + + ui.write("%s\n" % ' '.join(output)) + +def import_(ui, repo, patch1, *patches, **opts): + """import an ordered set of patches + + Import a list of patches and commit them individually. + + If there are outstanding changes in the working directory, import + will abort unless given the -f/--force flag. + + You can import a patch straight from a mail message. Even patches + as attachments work (to use the body part, it must have type + text/plain or text/x-patch). From and Subject headers of email + message are used as default committer and commit message. All + text/plain body parts before first diff are added to commit + message. + + If the imported patch was generated by hg export, user and + description from patch override values from message headers and + body. Values given on command line with -m/--message and -u/--user + override these. + + If --exact is specified, import will set the working directory to + the parent of each patch before applying it, and will abort if the + resulting changeset has a different ID than the one recorded in + the patch. This may happen due to character set problems or other + deficiencies in the text patch format. + + With -s/--similarity, hg will attempt to discover renames and + copies in the patch in the same way as 'addremove'. + + To read a patch from standard input, use "-" as the patch name. If + a URL is specified, the patch will be downloaded from it. + See 'hg help dates' for a list of formats valid for -d/--date. + """ + patches = (patch1,) + patches + + date = opts.get('date') + if date: + opts['date'] = util.parsedate(date) + + try: + sim = float(opts.get('similarity') or 0) + except ValueError: + raise util.Abort(_('similarity must be a number')) + if sim < 0 or sim > 100: + raise util.Abort(_('similarity must be between 0 and 100')) + + if opts.get('exact') or not opts.get('force'): + cmdutil.bail_if_changed(repo) + + d = opts["base"] + strip = opts["strip"] + wlock = lock = None + try: + wlock = repo.wlock() + lock = repo.lock() + for p in patches: + pf = os.path.join(d, p) + + if pf == '-': + ui.status(_("applying patch from stdin\n")) + pf = sys.stdin + else: + ui.status(_("applying %s\n") % p) + pf = url.open(ui, pf) + data = patch.extract(ui, pf) + tmpname, message, user, date, branch, nodeid, p1, p2 = data + + if tmpname is None: + raise util.Abort(_('no diffs found')) + + try: + cmdline_message = cmdutil.logmessage(opts) + if cmdline_message: + # pickup the cmdline msg + message = cmdline_message + elif message: + # pickup the patch msg + message = message.strip() + else: + # launch the editor + message = None + ui.debug(_('message:\n%s\n') % message) + + wp = repo.parents() + if opts.get('exact'): + if not nodeid or not p1: + raise util.Abort(_('not a Mercurial patch')) + p1 = repo.lookup(p1) + p2 = repo.lookup(p2 or hex(nullid)) + + if p1 != wp[0].node(): + hg.clean(repo, p1) + repo.dirstate.setparents(p1, p2) + elif p2: + try: + p1 = repo.lookup(p1) + p2 = repo.lookup(p2) + if p1 == wp[0].node(): + repo.dirstate.setparents(p1, p2) + except error.RepoError: + pass + if opts.get('exact') or opts.get('import_branch'): + repo.dirstate.setbranch(branch or 'default') + + files = {} + try: + patch.patch(tmpname, ui, strip=strip, cwd=repo.root, + files=files, eolmode=None) + finally: + files = patch.updatedir(ui, repo, files, similarity=sim/100.) + if not opts.get('no_commit'): + m = cmdutil.matchfiles(repo, files or []) + n = repo.commit(message, opts.get('user') or user, + opts.get('date') or date, match=m, + editor=cmdutil.commiteditor) + if opts.get('exact'): + if hex(n) != nodeid: + repo.rollback() + raise util.Abort(_('patch is damaged' + ' or loses information')) + # Force a dirstate write so that the next transaction + # backups an up-do-date file. + repo.dirstate.write() + finally: + os.unlink(tmpname) + finally: + release(lock, wlock) + +def incoming(ui, repo, source="default", **opts): + """show new changesets found in source + + Show new changesets found in the specified path/URL or the default + pull location. These are the changesets that would have been pulled + if a pull at the time you issued this command. + + For remote repository, using --bundle avoids downloading the + changesets twice if the incoming is followed by a pull. + + See pull for valid source format details. + """ + limit = cmdutil.loglimit(opts) + source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev')) + other = hg.repository(cmdutil.remoteui(repo, opts), source) + ui.status(_('comparing with %s\n') % url.hidepassword(source)) + if revs: + revs = [other.lookup(rev) for rev in revs] + common, incoming, rheads = repo.findcommonincoming(other, heads=revs, + force=opts["force"]) + if not incoming: + try: + os.unlink(opts["bundle"]) + except: + pass + ui.status(_("no changes found\n")) + return 1 + + cleanup = None + try: + fname = opts["bundle"] + if fname or not other.local(): + # create a bundle (uncompressed if other repo is not local) + + if revs is None and other.capable('changegroupsubset'): + revs = rheads + + if revs is None: + cg = other.changegroup(incoming, "incoming") + else: + cg = other.changegroupsubset(incoming, revs, 'incoming') + bundletype = other.local() and "HG10BZ" or "HG10UN" + fname = cleanup = changegroup.writebundle(cg, fname, bundletype) + # keep written bundle? + if opts["bundle"]: + cleanup = None + if not other.local(): + # use the created uncompressed bundlerepo + other = bundlerepo.bundlerepository(ui, repo.root, fname) + + o = other.changelog.nodesbetween(incoming, revs)[0] + if opts.get('newest_first'): + o.reverse() + displayer = cmdutil.show_changeset(ui, other, opts) + count = 0 + for n in o: + if count >= limit: + break + parents = [p for p in other.changelog.parents(n) if p != nullid] + if opts.get('no_merges') and len(parents) == 2: + continue + count += 1 + displayer.show(other[n]) + finally: + if hasattr(other, 'close'): + other.close() + if cleanup: + os.unlink(cleanup) + +def init(ui, dest=".", **opts): + """create a new repository in the given directory + + Initialize a new repository in the given directory. If the given + directory does not exist, it will be created. + + If no directory is given, the current directory is used. + + It is possible to specify an ssh:// URL as the destination. + See 'hg help urls' for more information. + """ + hg.repository(cmdutil.remoteui(ui, opts), dest, create=1) + +def locate(ui, repo, *pats, **opts): + """locate files matching specific patterns + + Print files under Mercurial control in the working directory whose + names match the given patterns. + + By default, this command searches all directories in the working + directory. To search just the current directory and its + subdirectories, use "--include .". + + If no patterns are given to match, this command prints the names + of all files under Mercurial control in the working directory. + + If you want to feed the output of this command into the "xargs" + command, use the -0 option to both this command and "xargs". This + will avoid the problem of "xargs" treating single filenames that + contain whitespace as multiple filenames. + """ + end = opts.get('print0') and '\0' or '\n' + rev = opts.get('rev') or None + + ret = 1 + m = cmdutil.match(repo, pats, opts, default='relglob') + m.bad = lambda x,y: False + for abs in repo[rev].walk(m): + if not rev and abs not in repo.dirstate: + continue + if opts.get('fullpath'): + ui.write(repo.wjoin(abs), end) + else: + ui.write(((pats and m.rel(abs)) or abs), end) + ret = 0 + + return ret + +def log(ui, repo, *pats, **opts): + """show revision history of entire repository or files + + Print the revision history of the specified files or the entire + project. + + File history is shown without following rename or copy history of + files. Use -f/--follow with a filename to follow history across + renames and copies. --follow without a filename will only show + ancestors or descendants of the starting revision. --follow-first + only follows the first parent of merge revisions. + + If no revision range is specified, the default is tip:0 unless + --follow is set, in which case the working directory parent is + used as the starting revision. + + See 'hg help dates' for a list of formats valid for -d/--date. + + By default this command prints revision number and changeset id, + tags, non-trivial parents, user, date and time, and a summary for + each commit. When the -v/--verbose switch is used, the list of + changed files and full commit message are shown. + + NOTE: log -p/--patch may generate unexpected diff output for merge + changesets, as it will only compare the merge changeset against + its first parent. Also, only files different from BOTH parents + will appear in files:. + """ + + get = util.cachefunc(lambda r: repo[r].changeset()) + changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) + + limit = cmdutil.loglimit(opts) + count = 0 + + if opts.get('copies') and opts.get('rev'): + endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1 + else: + endrev = len(repo) + rcache = {} + ncache = {} + def getrenamed(fn, rev): + '''looks up all renames for a file (up to endrev) the first + time the file is given. It indexes on the changerev and only + parses the manifest if linkrev != changerev. + Returns rename info for fn at changerev rev.''' + if fn not in rcache: + rcache[fn] = {} + ncache[fn] = {} + fl = repo.file(fn) + for i in fl: + node = fl.node(i) + lr = fl.linkrev(i) + renamed = fl.renamed(node) + rcache[fn][lr] = renamed + if renamed: + ncache[fn][node] = renamed + if lr >= endrev: + break + if rev in rcache[fn]: + return rcache[fn][rev] + + # If linkrev != rev (i.e. rev not found in rcache) fallback to + # filectx logic. + + try: + return repo[rev][fn].renamed() + except error.LookupError: + pass + return None + + df = False + if opts["date"]: + df = util.matchdate(opts["date"]) + + only_branches = opts.get('only_branch') + + displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn) + for st, rev, fns in changeiter: + if st == 'add': + parents = [p for p in repo.changelog.parentrevs(rev) + if p != nullrev] + if opts.get('no_merges') and len(parents) == 2: + continue + if opts.get('only_merges') and len(parents) != 2: + continue + + if only_branches: + revbranch = get(rev)[5]['branch'] + if revbranch not in only_branches: + continue + + if df: + changes = get(rev) + if not df(changes[2][0]): + continue + + if opts.get('keyword'): + changes = get(rev) + miss = 0 + for k in [kw.lower() for kw in opts['keyword']]: + if not (k in changes[1].lower() or + k in changes[4].lower() or + k in " ".join(changes[3]).lower()): + miss = 1 + break + if miss: + continue + + if opts['user']: + changes = get(rev) + if not [k for k in opts['user'] if k in changes[1]]: + continue + + copies = [] + if opts.get('copies') and rev: + for fn in get(rev)[3]: + rename = getrenamed(fn, rev) + if rename: + copies.append((fn, rename[0])) + displayer.show(context.changectx(repo, rev), copies=copies) + elif st == 'iter': + if count == limit: break + if displayer.flush(rev): + count += 1 + +def manifest(ui, repo, node=None, rev=None): + """output the current or given revision of the project manifest + + Print a list of version controlled files for the given revision. + If no revision is given, the first parent of the working directory + is used, or the null revision if no revision is checked out. + + With -v, print file permissions, symlink and executable bits. + With --debug, print file revision hashes. + """ + + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not node: + node = rev + + decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '} + ctx = repo[node] + for f in ctx: + if ui.debugflag: + ui.write("%40s " % hex(ctx.manifest()[f])) + if ui.verbose: + ui.write(decor[ctx.flags(f)]) + ui.write("%s\n" % f) + +def merge(ui, repo, node=None, **opts): + """merge working directory with another revision + + The current working directory is updated with all changes made in + the requested revision since the last common predecessor revision. + + Files that changed between either parent are marked as changed for + the next commit and a commit must be performed before any further + updates to the repository are allowed. The next commit will have + two parents. + + If no revision is specified, the working directory's parent is a + head revision, and the current branch contains exactly one other + head, the other head is merged with by default. Otherwise, an + explicit revision with which to merge with must be provided. + """ + + if opts.get('rev') and node: + raise util.Abort(_("please specify just one revision")) + if not node: + node = opts.get('rev') + + if not node: + branch = repo.changectx(None).branch() + bheads = repo.branchheads(branch) + if len(bheads) > 2: + raise util.Abort(_("branch '%s' has %d heads - " + "please merge with an explicit rev") % + (branch, len(bheads))) + + parent = repo.dirstate.parents()[0] + if len(bheads) == 1: + if len(repo.heads()) > 1: + raise util.Abort(_("branch '%s' has one head - " + "please merge with an explicit rev") % + branch) + msg = _('there is nothing to merge') + if parent != repo.lookup(repo[None].branch()): + msg = _('%s - use "hg update" instead') % msg + raise util.Abort(msg) + + if parent not in bheads: + raise util.Abort(_('working dir not at a head rev - ' + 'use "hg update" or merge with an explicit rev')) + node = parent == bheads[0] and bheads[-1] or bheads[0] + + if opts.get('preview'): + p1 = repo['.'] + p2 = repo[node] + common = p1.ancestor(p2) + roots, heads = [common.node()], [p2.node()] + displayer = cmdutil.show_changeset(ui, repo, opts) + for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]: + displayer.show(repo[node]) + return 0 + + return hg.merge(repo, node, force=opts.get('force')) + +def outgoing(ui, repo, dest=None, **opts): + """show changesets not found in destination + + Show changesets not found in the specified destination repository + or the default push location. These are the changesets that would + be pushed if a push was requested. + + See pull for valid destination format details. + """ + limit = cmdutil.loglimit(opts) + dest, revs, checkout = hg.parseurl( + ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev')) + if revs: + revs = [repo.lookup(rev) for rev in revs] + + other = hg.repository(cmdutil.remoteui(repo, opts), dest) + ui.status(_('comparing with %s\n') % url.hidepassword(dest)) + o = repo.findoutgoing(other, force=opts.get('force')) + if not o: + ui.status(_("no changes found\n")) + return 1 + o = repo.changelog.nodesbetween(o, revs)[0] + if opts.get('newest_first'): + o.reverse() + displayer = cmdutil.show_changeset(ui, repo, opts) + count = 0 + for n in o: + if count >= limit: + break + parents = [p for p in repo.changelog.parents(n) if p != nullid] + if opts.get('no_merges') and len(parents) == 2: + continue + count += 1 + displayer.show(repo[n]) + +def parents(ui, repo, file_=None, **opts): + """show the parents of the working directory or revision + + Print the working directory's parent revisions. If a revision is + given via -r/--rev, the parent of that revision will be printed. + If a file argument is given, the revision in which the file was + last changed (before the working directory revision or the + argument to --rev if given) is printed. + """ + rev = opts.get('rev') + if rev: + ctx = repo[rev] + else: + ctx = repo[None] + + if file_: + m = cmdutil.match(repo, (file_,), opts) + if m.anypats() or len(m.files()) != 1: + raise util.Abort(_('can only specify an explicit filename')) + file_ = m.files()[0] + filenodes = [] + for cp in ctx.parents(): + if not cp: + continue + try: + filenodes.append(cp.filenode(file_)) + except error.LookupError: + pass + if not filenodes: + raise util.Abort(_("'%s' not found in manifest!") % file_) + fl = repo.file(file_) + p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes] + else: + p = [cp.node() for cp in ctx.parents()] + + displayer = cmdutil.show_changeset(ui, repo, opts) + for n in p: + if n != nullid: + displayer.show(repo[n]) + +def paths(ui, repo, search=None): + """show aliases for remote repositories + + Show definition of symbolic path name NAME. If no name is given, + show definition of all available names. + + Path names are defined in the [paths] section of /etc/mercurial/hgrc + and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too. + + See 'hg help urls' for more information. + """ + if search: + for name, path in ui.configitems("paths"): + if name == search: + ui.write("%s\n" % url.hidepassword(path)) + return + ui.warn(_("not found!\n")) + return 1 + else: + for name, path in ui.configitems("paths"): + ui.write("%s = %s\n" % (name, url.hidepassword(path))) + +def postincoming(ui, repo, modheads, optupdate, checkout): + if modheads == 0: + return + if optupdate: + if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout: + return hg.update(repo, checkout) + else: + ui.status(_("not updating, since new heads added\n")) + if modheads > 1: + ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) + else: + ui.status(_("(run 'hg update' to get a working copy)\n")) + +def pull(ui, repo, source="default", **opts): + """pull changes from the specified source + + Pull changes from a remote repository to a local one. + + This finds all changes from the repository at the specified path + or URL and adds them to a local repository (the current one unless + -R is specified). By default, this does not update the copy of the + project in the working directory. + + Use hg incoming if you want to see what would have been added by a + pull at the time you issued this command. If you then decide to + added those changes to the repository, you should use pull -r X + where X is the last changeset listed by hg incoming. + + If SOURCE is omitted, the 'default' path will be used. + See 'hg help urls' for more information. + """ + source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev')) + other = hg.repository(cmdutil.remoteui(repo, opts), source) + ui.status(_('pulling from %s\n') % url.hidepassword(source)) + if revs: + try: + revs = [other.lookup(rev) for rev in revs] + except error.CapabilityError: + err = _("Other repository doesn't support revision lookup, " + "so a rev cannot be specified.") + raise util.Abort(err) + + modheads = repo.pull(other, heads=revs, force=opts.get('force')) + return postincoming(ui, repo, modheads, opts.get('update'), checkout) + +def push(ui, repo, dest=None, **opts): + """push changes to the specified destination + + Push changes from the local repository to the given destination. + + This is the symmetrical operation for pull. It moves changes from + the current repository to a different one. If the destination is + local this is identical to a pull in that directory from the + current one. + + By default, push will refuse to run if it detects the result would + increase the number of remote heads. This generally indicates the + user forgot to pull and merge before pushing. + + If -r/--rev is used, the named revision and all its ancestors will + be pushed to the remote repository. + + Please see 'hg help urls' for important details about ssh:// + URLs. If DESTINATION is omitted, a default path will be used. + """ + dest, revs, checkout = hg.parseurl( + ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev')) + other = hg.repository(cmdutil.remoteui(repo, opts), dest) + ui.status(_('pushing to %s\n') % url.hidepassword(dest)) + if revs: + revs = [repo.lookup(rev) for rev in revs] + + # push subrepos depth-first for coherent ordering + c = repo[''] + subs = c.substate # only repos that are committed + for s in sorted(subs): + c.sub(s).push(opts.get('force')) + + r = repo.push(other, opts.get('force'), revs=revs) + return r == 0 + +def recover(ui, repo): + """roll back an interrupted transaction + + Recover from an interrupted commit or pull. + + This command tries to fix the repository status after an + interrupted operation. It should only be necessary when Mercurial + suggests it. + """ + if repo.recover(): + return hg.verify(repo) + return 1 + +def remove(ui, repo, *pats, **opts): + """remove the specified files on the next commit + + Schedule the indicated files for removal from the repository. + + This only removes files from the current branch, not from the + entire project history. -A/--after can be used to remove only + files that have already been deleted, -f/--force can be used to + force deletion, and -Af can be used to remove files from the next + revision without deleting them from the working directory. + + The following table details the behavior of remove for different + file states (columns) and option combinations (rows). The file + states are Added [A], Clean [C], Modified [M] and Missing [!] + (as reported by hg status). The actions are Warn, Remove (from + branch) and Delete (from disk). + + A C M ! + none W RD W R + -f R RD RD R + -A W W W R + -Af R R R R + + This command schedules the files to be removed at the next commit. + To undo a remove before that, see hg revert. + """ + + after, force = opts.get('after'), opts.get('force') + if not pats and not after: + raise util.Abort(_('no files specified')) + + m = cmdutil.match(repo, pats, opts) + s = repo.status(match=m, clean=True) + modified, added, deleted, clean = s[0], s[1], s[3], s[6] + + for f in m.files(): + if f not in repo.dirstate and not os.path.isdir(m.rel(f)): + ui.warn(_('not removing %s: file is untracked\n') % m.rel(f)) + + def warn(files, reason): + for f in files: + ui.warn(_('not removing %s: file %s (use -f to force removal)\n') + % (m.rel(f), reason)) + + if force: + remove, forget = modified + deleted + clean, added + elif after: + remove, forget = deleted, [] + warn(modified + added + clean, _('still exists')) + else: + remove, forget = deleted + clean, [] + warn(modified, _('is modified')) + warn(added, _('has been marked for add')) + + for f in sorted(remove + forget): + if ui.verbose or not m.exact(f): + ui.status(_('removing %s\n') % m.rel(f)) + + repo.forget(forget) + repo.remove(remove, unlink=not after) + +def rename(ui, repo, *pats, **opts): + """rename files; equivalent of copy + remove + + Mark dest as copies of sources; mark sources for deletion. If dest + is a directory, copies are put in that directory. If dest is a + file, there can only be one source. + + By default, this command copies the contents of files as they + exist in the working directory. If invoked with -A/--after, the + operation is recorded, but no copying is performed. + + This command takes effect at the next commit. To undo a rename + before that, see hg revert. + """ + wlock = repo.wlock(False) + try: + return cmdutil.copy(ui, repo, pats, opts, rename=True) + finally: + wlock.release() + +def resolve(ui, repo, *pats, **opts): + """retry file merges from a merge or update + + This command will cleanly retry unresolved file merges using file + revisions preserved from the last update or merge. To attempt to + resolve all unresolved files, use the -a/--all switch. + + If a conflict is resolved manually, please note that the changes + will be overwritten if the merge is retried with resolve. The + -m/--mark switch should be used to mark the file as resolved. + + This command also allows listing resolved files and manually + indicating whether or not files are resolved. All files must be + marked as resolved before a commit is permitted. + + The codes used to show the status of files are: + U = unresolved + R = resolved + """ + + all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()] + + if (show and (mark or unmark)) or (mark and unmark): + raise util.Abort(_("too many options specified")) + if pats and all: + raise util.Abort(_("can't specify --all and patterns")) + if not (all or pats or show or mark or unmark): + raise util.Abort(_('no files or directories specified; ' + 'use --all to remerge all files')) + + ms = merge_.mergestate(repo) + m = cmdutil.match(repo, pats, opts) + + for f in ms: + if m(f): + if show: + ui.write("%s %s\n" % (ms[f].upper(), f)) + elif mark: + ms.mark(f, "r") + elif unmark: + ms.mark(f, "u") + else: + wctx = repo[None] + mctx = wctx.parents()[-1] + + # backup pre-resolve (merge uses .orig for its own purposes) + a = repo.wjoin(f) + util.copyfile(a, a + ".resolve") + + # resolve file + ms.resolve(f, wctx, mctx) + + # replace filemerge's .orig file with our resolve file + util.rename(a + ".resolve", a + ".orig") + +def revert(ui, repo, *pats, **opts): + """restore individual files or directories to an earlier state + + (Use update -r to check out earlier revisions, revert does not + change the working directory parents.) + + With no revision specified, revert the named files or directories + to the contents they had in the parent of the working directory. + This restores the contents of the affected files to an unmodified + state and unschedules adds, removes, copies, and renames. If the + working directory has two parents, you must explicitly specify the + revision to revert to. + + Using the -r/--rev option, revert the given files or directories + to their contents as of a specific revision. This can be helpful + to "roll back" some or all of an earlier change. See 'hg help + dates' for a list of formats valid for -d/--date. + + Revert modifies the working directory. It does not commit any + changes, or change the parent of the working directory. If you + revert to a revision other than the parent of the working + directory, the reverted files will thus appear modified + afterwards. + + If a file has been deleted, it is restored. If the executable mode + of a file was changed, it is reset. + + If names are given, all files matching the names are reverted. + If no arguments are given, no files are reverted. + + Modified files are saved with a .orig suffix before reverting. + To disable these backups, use --no-backup. + """ + + if opts["date"]: + if opts["rev"]: + raise util.Abort(_("you can't specify a revision and a date")) + opts["rev"] = cmdutil.finddate(ui, repo, opts["date"]) + + if not pats and not opts.get('all'): + raise util.Abort(_('no files or directories specified; ' + 'use --all to revert the whole repo')) + + parent, p2 = repo.dirstate.parents() + if not opts.get('rev') and p2 != nullid: + raise util.Abort(_('uncommitted merge - please provide a ' + 'specific revision')) + ctx = repo[opts.get('rev')] + node = ctx.node() + mf = ctx.manifest() + if node == parent: + pmf = mf + else: + pmf = None + + # need all matching names in dirstate and manifest of target rev, + # so have to walk both. do not print errors if files exist in one + # but not other. + + names = {} + + wlock = repo.wlock() + try: + # walk dirstate. + + m = cmdutil.match(repo, pats, opts) + m.bad = lambda x,y: False + for abs in repo.walk(m): + names[abs] = m.rel(abs), m.exact(abs) + + # walk target manifest. + + def badfn(path, msg): + if path in names: + return + path_ = path + '/' + for f in names: + if f.startswith(path_): + return + ui.warn("%s: %s\n" % (m.rel(path), msg)) + + m = cmdutil.match(repo, pats, opts) + m.bad = badfn + for abs in repo[node].walk(m): + if abs not in names: + names[abs] = m.rel(abs), m.exact(abs) + + m = cmdutil.matchfiles(repo, names) + changes = repo.status(match=m)[:4] + modified, added, removed, deleted = map(set, changes) + + # if f is a rename, also revert the source + cwd = repo.getcwd() + for f in added: + src = repo.dirstate.copied(f) + if src and src not in names and repo.dirstate[src] == 'r': + removed.add(src) + names[src] = (repo.pathto(src, cwd), True) + + def removeforget(abs): + if repo.dirstate[abs] == 'a': + return _('forgetting %s\n') + return _('removing %s\n') + + revert = ([], _('reverting %s\n')) + add = ([], _('adding %s\n')) + remove = ([], removeforget) + undelete = ([], _('undeleting %s\n')) + + disptable = ( + # dispatch table: + # file state + # action if in target manifest + # action if not in target manifest + # make backup if in target manifest + # make backup if not in target manifest + (modified, revert, remove, True, True), + (added, revert, remove, True, False), + (removed, undelete, None, False, False), + (deleted, revert, remove, False, False), + ) + + for abs, (rel, exact) in sorted(names.items()): + mfentry = mf.get(abs) + target = repo.wjoin(abs) + def handle(xlist, dobackup): + xlist[0].append(abs) + if dobackup and not opts.get('no_backup') and util.lexists(target): + bakname = "%s.orig" % rel + ui.note(_('saving current version of %s as %s\n') % + (rel, bakname)) + if not opts.get('dry_run'): + util.copyfile(target, bakname) + if ui.verbose or not exact: + msg = xlist[1] + if not isinstance(msg, basestring): + msg = msg(abs) + ui.status(msg % rel) + for table, hitlist, misslist, backuphit, backupmiss in disptable: + if abs not in table: continue + # file has changed in dirstate + if mfentry: + handle(hitlist, backuphit) + elif misslist is not None: + handle(misslist, backupmiss) + break + else: + if abs not in repo.dirstate: + if mfentry: + handle(add, True) + elif exact: + ui.warn(_('file not managed: %s\n') % rel) + continue + # file has not changed in dirstate + if node == parent: + if exact: ui.warn(_('no changes needed to %s\n') % rel) + continue + if pmf is None: + # only need parent manifest in this unlikely case, + # so do not read by default + pmf = repo[parent].manifest() + if abs in pmf: + if mfentry: + # if version of file is same in parent and target + # manifests, do nothing + if (pmf[abs] != mfentry or + pmf.flags(abs) != mf.flags(abs)): + handle(revert, False) + else: + handle(remove, False) + + if not opts.get('dry_run'): + def checkout(f): + fc = ctx[f] + repo.wwrite(f, fc.data(), fc.flags()) + + audit_path = util.path_auditor(repo.root) + for f in remove[0]: + if repo.dirstate[f] == 'a': + repo.dirstate.forget(f) + continue + audit_path(f) + try: + util.unlink(repo.wjoin(f)) + except OSError: + pass + repo.dirstate.remove(f) + + normal = None + if node == parent: + # We're reverting to our parent. If possible, we'd like status + # to report the file as clean. We have to use normallookup for + # merges to avoid losing information about merged/dirty files. + if p2 != nullid: + normal = repo.dirstate.normallookup + else: + normal = repo.dirstate.normal + for f in revert[0]: + checkout(f) + if normal: + normal(f) + + for f in add[0]: + checkout(f) + repo.dirstate.add(f) + + normal = repo.dirstate.normallookup + if node == parent and p2 == nullid: + normal = repo.dirstate.normal + for f in undelete[0]: + checkout(f) + normal(f) + + finally: + wlock.release() + +def rollback(ui, repo): + """roll back the last transaction + + This command should be used with care. There is only one level of + rollback, and there is no way to undo a rollback. It will also + restore the dirstate at the time of the last transaction, losing + any dirstate changes since that time. This command does not alter + the working directory. + + Transactions are used to encapsulate the effects of all commands + that create new changesets or propagate existing changesets into a + repository. For example, the following commands are transactional, + and their effects can be rolled back: + + commit + import + pull + push (with this repository as destination) + unbundle + + This command is not intended for use on public repositories. Once + changes are visible for pull by other users, rolling a transaction + back locally is ineffective (someone else may already have pulled + the changes). Furthermore, a race is possible with readers of the + repository; for example an in-progress pull from the repository + may fail if a rollback is performed. + """ + repo.rollback() + +def root(ui, repo): + """print the root (top) of the current working directory + + Print the root directory of the current repository. + """ + ui.write(repo.root + "\n") + +def serve(ui, repo, **opts): + """export the repository via HTTP + + Start a local HTTP repository browser and pull server. + + By default, the server logs accesses to stdout and errors to + stderr. Use the -A/--accesslog and -E/--errorlog options to log to + files. + """ + + if opts["stdio"]: + if repo is None: + raise error.RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) + s = sshserver.sshserver(ui, repo) + s.serve_forever() + + baseui = repo and repo.baseui or ui + optlist = ("name templates style address port prefix ipv6" + " accesslog errorlog webdir_conf certificate encoding") + for o in optlist.split(): + if opts.get(o, None): + baseui.setconfig("web", o, str(opts[o])) + if (repo is not None) and (repo.ui != baseui): + repo.ui.setconfig("web", o, str(opts[o])) + + if repo is None and not ui.config("web", "webdir_conf"): + raise error.RepoError(_("There is no Mercurial repository here" + " (.hg not found)")) + + class service(object): + def init(self): + util.set_signal_handler() + self.httpd = server.create_server(baseui, repo) + + if not ui.verbose: return + + if self.httpd.prefix: + prefix = self.httpd.prefix.strip('/') + '/' + else: + prefix = '' + + port = ':%d' % self.httpd.port + if port == ':80': + port = '' + + bindaddr = self.httpd.addr + if bindaddr == '0.0.0.0': + bindaddr = '*' + elif ':' in bindaddr: # IPv6 + bindaddr = '[%s]' % bindaddr + + fqaddr = self.httpd.fqaddr + if ':' in fqaddr: + fqaddr = '[%s]' % fqaddr + ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') % + (fqaddr, port, prefix, bindaddr, self.httpd.port)) + + def run(self): + self.httpd.serve_forever() + + service = service() + + cmdutil.service(opts, initfn=service.init, runfn=service.run) + +def status(ui, repo, *pats, **opts): + """show changed files in the working directory + + Show status of files in the repository. If names are given, only + files that match are shown. Files that are clean or ignored or + the source of a copy/move operation, are not listed unless + -c/--clean, -i/--ignored, -C/--copies or -A/--all are given. + Unless options described with "show only ..." are given, the + options -mardu are used. + + Option -q/--quiet hides untracked (unknown and ignored) files + unless explicitly requested with -u/--unknown or -i/--ignored. + + NOTE: status may appear to disagree with diff if permissions have + changed or a merge has occurred. The standard diff format does not + report permission changes and diff only reports changes relative + to one merge parent. + + If one revision is given, it is used as the base revision. + If two revisions are given, the differences between them are + shown. + + The codes used to show the status of files are: + M = modified + A = added + R = removed + C = clean + ! = missing (deleted by non-hg command, but still tracked) + ? = not tracked + I = ignored + = origin of the previous file listed as A (added) + """ + + node1, node2 = cmdutil.revpair(repo, opts.get('rev')) + cwd = (pats and repo.getcwd()) or '' + end = opts.get('print0') and '\0' or '\n' + copy = {} + states = 'modified added removed deleted unknown ignored clean'.split() + show = [k for k in states if opts.get(k)] + if opts.get('all'): + show += ui.quiet and (states[:4] + ['clean']) or states + if not show: + show = ui.quiet and states[:4] or states[:5] + + stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts), + 'ignored' in show, 'clean' in show, 'unknown' in show) + changestates = zip(states, 'MAR!?IC', stat) + + if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'): + ctxn = repo[nullid] + ctx1 = repo[node1] + ctx2 = repo[node2] + added = stat[1] + if node2 is None: + added = stat[0] + stat[1] # merged? + + for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems(): + if k in added: + copy[k] = v + elif v in added: + copy[v] = k + + for state, char, files in changestates: + if state in show: + format = "%s %%s%s" % (char, end) + if opts.get('no_status'): + format = "%%s%s" % end + + for f in files: + ui.write(format % repo.pathto(f, cwd)) + if f in copy: + ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end)) + +def tag(ui, repo, name1, *names, **opts): + """add one or more tags for the current or given revision + + Name a particular revision using . + + Tags are used to name particular revisions of the repository and are + very useful to compare different revisions, to go back to significant + earlier versions or to mark branch points as releases, etc. + + If no revision is given, the parent of the working directory is + used, or tip if no revision is checked out. + + To facilitate version control, distribution, and merging of tags, + they are stored as a file named ".hgtags" which is managed + similarly to other project files and can be hand-edited if + necessary. The file '.hg/localtags' is used for local tags (not + shared among repositories). + + See 'hg help dates' for a list of formats valid for -d/--date. + """ + + rev_ = "." + names = (name1,) + names + if len(names) != len(set(names)): + raise util.Abort(_('tag names must be unique')) + for n in names: + if n in ['tip', '.', 'null']: + raise util.Abort(_('the name \'%s\' is reserved') % n) + if opts.get('rev') and opts.get('remove'): + raise util.Abort(_("--rev and --remove are incompatible")) + if opts.get('rev'): + rev_ = opts['rev'] + message = opts.get('message') + if opts.get('remove'): + expectedtype = opts.get('local') and 'local' or 'global' + for n in names: + if not repo.tagtype(n): + raise util.Abort(_('tag \'%s\' does not exist') % n) + if repo.tagtype(n) != expectedtype: + if expectedtype == 'global': + raise util.Abort(_('tag \'%s\' is not a global tag') % n) + else: + raise util.Abort(_('tag \'%s\' is not a local tag') % n) + rev_ = nullid + if not message: + # we don't translate commit messages + message = 'Removed tag %s' % ', '.join(names) + elif not opts.get('force'): + for n in names: + if n in repo.tags(): + raise util.Abort(_('tag \'%s\' already exists ' + '(use -f to force)') % n) + if not rev_ and repo.dirstate.parents()[1] != nullid: + raise util.Abort(_('uncommitted merge - please provide a ' + 'specific revision')) + r = repo[rev_].node() + + if not message: + # we don't translate commit messages + message = ('Added tag %s for changeset %s' % + (', '.join(names), short(r))) + + date = opts.get('date') + if date: + date = util.parsedate(date) + + repo.tag(names, r, message, opts.get('local'), opts.get('user'), date) + +def tags(ui, repo): + """list repository tags + + This lists both regular and local tags. When the -v/--verbose + switch is used, a third column "local" is printed for local tags. + """ + + hexfunc = ui.debugflag and hex or short + tagtype = "" + + for t, n in reversed(repo.tagslist()): + if ui.quiet: + ui.write("%s\n" % t) + continue + + try: + hn = hexfunc(n) + r = "%5d:%s" % (repo.changelog.rev(n), hn) + except error.LookupError: + r = " ?:%s" % hn + else: + spaces = " " * (30 - encoding.colwidth(t)) + if ui.verbose: + if repo.tagtype(t) == 'local': + tagtype = " local" + else: + tagtype = "" + ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype)) + +def tip(ui, repo, **opts): + """show the tip revision + + The tip revision (usually just called the tip) is the changeset + most recently added to the repository (and therefore the most + recently changed head). + + If you have just made a commit, that commit will be the tip. If + you have just pulled changes from another repository, the tip of + that repository becomes the current tip. The "tip" tag is special + and cannot be renamed or assigned to a different changeset. + """ + cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1]) + +def unbundle(ui, repo, fname1, *fnames, **opts): + """apply one or more changegroup files + + Apply one or more compressed changegroup files generated by the + bundle command. + """ + fnames = (fname1,) + fnames + + lock = repo.lock() + try: + for fname in fnames: + f = url.open(ui, fname) + gen = changegroup.readbundle(f, fname) + modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname) + finally: + lock.release() + + return postincoming(ui, repo, modheads, opts.get('update'), None) + +def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False): + """update working directory + + Update the repository's working directory to the specified + revision, or the tip of the current branch if none is specified. + Use null as the revision to remove the working copy (like 'hg + clone -U'). + + When the working directory contains no uncommitted changes, it + will be replaced by the state of the requested revision from the + repository. When the requested revision is on a different branch, + the working directory will additionally be switched to that + branch. + + When there are uncommitted changes, use option -C/--clean to + discard them, forcibly replacing the state of the working + directory with the requested revision. Alternately, use -c/--check + to abort. + + When there are uncommitted changes and option -C/--clean is not + used, and the parent revision and requested revision are on the + same branch, and one of them is an ancestor of the other, then the + new working directory will contain the requested revision merged + with the uncommitted changes. Otherwise, the update will fail with + a suggestion to use 'merge' or 'update -C' instead. + + If you want to update just one file to an older revision, use + revert. + + See 'hg help dates' for a list of formats valid for -d/--date. + """ + if rev and node: + raise util.Abort(_("please specify just one revision")) + + if not rev: + rev = node + + if not clean and check: + # we could use dirty() but we can ignore merge and branch trivia + c = repo[None] + if c.modified() or c.added() or c.removed(): + raise util.Abort(_("uncommitted local changes")) + + if date: + if rev: + raise util.Abort(_("you can't specify a revision and a date")) + rev = cmdutil.finddate(ui, repo, date) + + if clean or check: + return hg.clean(repo, rev) + else: + return hg.update(repo, rev) + +def verify(ui, repo): + """verify the integrity of the repository + + Verify the integrity of the current repository. + + This will perform an extensive check of the repository's + integrity, validating the hashes and checksums of each entry in + the changelog, manifest, and tracked files, as well as the + integrity of their crosslinks and indices. + """ + return hg.verify(repo) + +def version_(ui): + """output version and copyright information""" + ui.write(_("Mercurial Distributed SCM (version %s)\n") + % util.version()) + ui.status(_( + "\nCopyright (C) 2005-2009 Matt Mackall and others\n" + "This is free software; see the source for copying conditions. " + "There is NO\nwarranty; " + "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + )) + +# Command options and aliases are listed here, alphabetically + +globalopts = [ + ('R', 'repository', '', + _('repository root directory or symbolic path name')), + ('', 'cwd', '', _('change working directory')), + ('y', 'noninteractive', None, + _('do not prompt, assume \'yes\' for any required answers')), + ('q', 'quiet', None, _('suppress output')), + ('v', 'verbose', None, _('enable additional output')), + ('', 'config', [], _('set/override config option')), + ('', 'debug', None, _('enable debugging output')), + ('', 'debugger', None, _('start debugger')), + ('', 'encoding', encoding.encoding, _('set the charset encoding')), + ('', 'encodingmode', encoding.encodingmode, + _('set the charset encoding mode')), + ('', 'traceback', None, _('print traceback on exception')), + ('', 'time', None, _('time how long the command takes')), + ('', 'profile', None, _('print command execution profile')), + ('', 'version', None, _('output version information and exit')), + ('h', 'help', None, _('display help and exit')), +] + +dryrunopts = [('n', 'dry-run', None, + _('do not perform actions, just print output'))] + +remoteopts = [ + ('e', 'ssh', '', _('specify ssh command to use')), + ('', 'remotecmd', '', _('specify hg command to run on the remote side')), +] + +walkopts = [ + ('I', 'include', [], _('include names matching the given patterns')), + ('X', 'exclude', [], _('exclude names matching the given patterns')), +] + +commitopts = [ + ('m', 'message', '', _('use as commit message')), + ('l', 'logfile', '', _('read commit message from ')), +] + +commitopts2 = [ + ('d', 'date', '', _('record datecode as commit date')), + ('u', 'user', '', _('record the specified user as committer')), +] + +templateopts = [ + ('', 'style', '', _('display using template map file')), + ('', 'template', '', _('display with template')), +] + +logopts = [ + ('p', 'patch', None, _('show patch')), + ('g', 'git', None, _('use git extended diff format')), + ('l', 'limit', '', _('limit number of changes displayed')), + ('M', 'no-merges', None, _('do not show merges')), +] + templateopts + +diffopts = [ + ('a', 'text', None, _('treat all files as text')), + ('g', 'git', None, _('use git extended diff format')), + ('', 'nodates', None, _("don't include dates in diff headers")) +] + +diffopts2 = [ + ('p', 'show-function', None, _('show which function each change is in')), + ('w', 'ignore-all-space', None, + _('ignore white space when comparing lines')), + ('b', 'ignore-space-change', None, + _('ignore changes in the amount of white space')), + ('B', 'ignore-blank-lines', None, + _('ignore changes whose lines are all blank')), + ('U', 'unified', '', _('number of lines of context to show')) +] + +similarityopts = [ + ('s', 'similarity', '', + _('guess renamed files by similarity (0<=s<=100)')) +] + +table = { + "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')), + "addremove": + (addremove, similarityopts + walkopts + dryrunopts, + _('[OPTION]... [FILE]...')), + "^annotate|blame": + (annotate, + [('r', 'rev', '', _('annotate the specified revision')), + ('f', 'follow', None, _('follow file copies and renames')), + ('a', 'text', None, _('treat all files as text')), + ('u', 'user', None, _('list the author (long with -v)')), + ('d', 'date', None, _('list the date (short with -q)')), + ('n', 'number', None, _('list the revision number (default)')), + ('c', 'changeset', None, _('list the changeset')), + ('l', 'line-number', None, + _('show line number at the first appearance')) + ] + walkopts, + _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')), + "archive": + (archive, + [('', 'no-decode', None, _('do not pass files through decoders')), + ('p', 'prefix', '', _('directory prefix for files in archive')), + ('r', 'rev', '', _('revision to distribute')), + ('t', 'type', '', _('type of distribution to create')), + ] + walkopts, + _('[OPTION]... DEST')), + "backout": + (backout, + [('', 'merge', None, + _('merge with old dirstate parent after backout')), + ('', 'parent', '', _('parent to choose when backing out merge')), + ('r', 'rev', '', _('revision to backout')), + ] + walkopts + commitopts + commitopts2, + _('[OPTION]... [-r] REV')), + "bisect": + (bisect, + [('r', 'reset', False, _('reset bisect state')), + ('g', 'good', False, _('mark changeset good')), + ('b', 'bad', False, _('mark changeset bad')), + ('s', 'skip', False, _('skip testing changeset')), + ('c', 'command', '', _('use command to check changeset state')), + ('U', 'noupdate', False, _('do not update to target'))], + _("[-gbsr] [-c CMD] [REV]")), + "branch": + (branch, + [('f', 'force', None, + _('set branch name even if it shadows an existing branch')), + ('C', 'clean', None, _('reset branch name to parent branch name'))], + _('[-fC] [NAME]')), + "branches": + (branches, + [('a', 'active', False, + _('show only branches that have unmerged heads')), + ('c', 'closed', False, + _('show normal and closed branches'))], + _('[-a]')), + "bundle": + (bundle, + [('f', 'force', None, + _('run even when remote repository is unrelated')), + ('r', 'rev', [], + _('a changeset up to which you would like to bundle')), + ('', 'base', [], + _('a base changeset to specify instead of a destination')), + ('a', 'all', None, _('bundle all changesets in the repository')), + ('t', 'type', 'bzip2', _('bundle compression type to use')), + ] + remoteopts, + _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')), + "cat": + (cat, + [('o', 'output', '', _('print output to file with formatted name')), + ('r', 'rev', '', _('print the given revision')), + ('', 'decode', None, _('apply any matching decode filter')), + ] + walkopts, + _('[OPTION]... FILE...')), + "^clone": + (clone, + [('U', 'noupdate', None, + _('the clone will only contain a repository (no working copy)')), + ('r', 'rev', [], + _('a changeset you would like to have after cloning')), + ('', 'pull', None, _('use pull protocol to copy metadata')), + ('', 'uncompressed', None, + _('use uncompressed transfer (fast over LAN)')), + ] + remoteopts, + _('[OPTION]... SOURCE [DEST]')), + "^commit|ci": + (commit, + [('A', 'addremove', None, + _('mark new/missing files as added/removed before committing')), + ('', 'close-branch', None, + _('mark a branch as closed, hiding it from the branch list')), + ] + walkopts + commitopts + commitopts2, + _('[OPTION]... [FILE]...')), + "copy|cp": + (copy, + [('A', 'after', None, _('record a copy that has already occurred')), + ('f', 'force', None, + _('forcibly copy over an existing managed file')), + ] + walkopts + dryrunopts, + _('[OPTION]... [SOURCE]... DEST')), + "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')), + "debugcheckstate": (debugcheckstate, []), + "debugcommands": (debugcommands, [], _('[COMMAND]')), + "debugcomplete": + (debugcomplete, + [('o', 'options', None, _('show the command options'))], + _('[-o] CMD')), + "debugdate": + (debugdate, + [('e', 'extended', None, _('try extended date formats'))], + _('[-e] DATE [RANGE]')), + "debugdata": (debugdata, [], _('FILE REV')), + "debugfsinfo": (debugfsinfo, [], _('[PATH]')), + "debugindex": (debugindex, [], _('FILE')), + "debugindexdot": (debugindexdot, [], _('FILE')), + "debuginstall": (debuginstall, []), + "debugrebuildstate": + (debugrebuildstate, + [('r', 'rev', '', _('revision to rebuild to'))], + _('[-r REV] [REV]')), + "debugrename": + (debugrename, + [('r', 'rev', '', _('revision to debug'))], + _('[-r REV] FILE')), + "debugsetparents": + (debugsetparents, [], _('REV1 [REV2]')), + "debugstate": + (debugstate, + [('', 'nodates', None, _('do not display the saved mtime'))], + _('[OPTION]...')), + "debugsub": + (debugsub, + [('r', 'rev', '', _('revision to check'))], + _('[-r REV] [REV]')), + "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')), + "^diff": + (diff, + [('r', 'rev', [], _('revision')), + ('c', 'change', '', _('change made by revision')) + ] + diffopts + diffopts2 + walkopts, + _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')), + "^export": + (export, + [('o', 'output', '', _('print output to file with formatted name')), + ('', 'switch-parent', None, _('diff against the second parent')) + ] + diffopts, + _('[OPTION]... [-o OUTFILESPEC] REV...')), + "^forget": + (forget, + [] + walkopts, + _('[OPTION]... FILE...')), + "grep": + (grep, + [('0', 'print0', None, _('end fields with NUL')), + ('', 'all', None, _('print all revisions that match')), + ('f', 'follow', None, + _('follow changeset history, or file history across copies and renames')), + ('i', 'ignore-case', None, _('ignore case when matching')), + ('l', 'files-with-matches', None, + _('print only filenames and revisions that match')), + ('n', 'line-number', None, _('print matching line numbers')), + ('r', 'rev', [], _('search in given revision range')), + ('u', 'user', None, _('list the author (long with -v)')), + ('d', 'date', None, _('list the date (short with -q)')), + ] + walkopts, + _('[OPTION]... PATTERN [FILE]...')), + "heads": + (heads, + [('r', 'rev', '', _('show only heads which are descendants of REV')), + ('a', 'active', False, + _('show only the active branch heads from open branches')), + ('c', 'closed', False, + _('show normal and closed branch heads')), + ] + templateopts, + _('[-r STARTREV] [REV]...')), + "help": (help_, [], _('[TOPIC]')), + "identify|id": + (identify, + [('r', 'rev', '', _('identify the specified revision')), + ('n', 'num', None, _('show local revision number')), + ('i', 'id', None, _('show global revision id')), + ('b', 'branch', None, _('show branch')), + ('t', 'tags', None, _('show tags'))], + _('[-nibt] [-r REV] [SOURCE]')), + "import|patch": + (import_, + [('p', 'strip', 1, + _('directory strip option for patch. This has the same ' + 'meaning as the corresponding patch option')), + ('b', 'base', '', _('base path')), + ('f', 'force', None, + _('skip check for outstanding uncommitted changes')), + ('', 'no-commit', None, _("don't commit, just update the working directory")), + ('', 'exact', None, + _('apply patch to the nodes from which it was generated')), + ('', 'import-branch', None, + _('use any branch information in patch (implied by --exact)'))] + + commitopts + commitopts2 + similarityopts, + _('[OPTION]... PATCH...')), + "incoming|in": + (incoming, + [('f', 'force', None, + _('run even when remote repository is unrelated')), + ('n', 'newest-first', None, _('show newest record first')), + ('', 'bundle', '', _('file to store the bundles into')), + ('r', 'rev', [], + _('a specific revision up to which you would like to pull')), + ] + logopts + remoteopts, + _('[-p] [-n] [-M] [-f] [-r REV]...' + ' [--bundle FILENAME] [SOURCE]')), + "^init": + (init, + remoteopts, + _('[-e CMD] [--remotecmd CMD] [DEST]')), + "locate": + (locate, + [('r', 'rev', '', _('search the repository as it stood at REV')), + ('0', 'print0', None, + _('end filenames with NUL, for use with xargs')), + ('f', 'fullpath', None, + _('print complete paths from the filesystem root')), + ] + walkopts, + _('[OPTION]... [PATTERN]...')), + "^log|history": + (log, + [('f', 'follow', None, + _('follow changeset history, or file history across copies and renames')), + ('', 'follow-first', None, + _('only follow the first parent of merge changesets')), + ('d', 'date', '', _('show revisions matching date spec')), + ('C', 'copies', None, _('show copied files')), + ('k', 'keyword', [], _('do case-insensitive search for a keyword')), + ('r', 'rev', [], _('show the specified revision or range')), + ('', 'removed', None, _('include revisions where files were removed')), + ('m', 'only-merges', None, _('show only merges')), + ('u', 'user', [], _('revisions committed by user')), + ('b', 'only-branch', [], + _('show only changesets within the given named branch')), + ('P', 'prune', [], _('do not display revision or any of its ancestors')), + ] + logopts + walkopts, + _('[OPTION]... [FILE]')), + "manifest": + (manifest, + [('r', 'rev', '', _('revision to display'))], + _('[-r REV]')), + "^merge": + (merge, + [('f', 'force', None, _('force a merge with outstanding changes')), + ('r', 'rev', '', _('revision to merge')), + ('P', 'preview', None, + _('review revisions to merge (no merge is performed)'))], + _('[-f] [[-r] REV]')), + "outgoing|out": + (outgoing, + [('f', 'force', None, + _('run even when remote repository is unrelated')), + ('r', 'rev', [], + _('a specific revision up to which you would like to push')), + ('n', 'newest-first', None, _('show newest record first')), + ] + logopts + remoteopts, + _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')), + "^parents": + (parents, + [('r', 'rev', '', _('show parents from the specified revision')), + ] + templateopts, + _('[-r REV] [FILE]')), + "paths": (paths, [], _('[NAME]')), + "^pull": + (pull, + [('u', 'update', None, + _('update to new tip if changesets were pulled')), + ('f', 'force', None, + _('run even when remote repository is unrelated')), + ('r', 'rev', [], + _('a specific revision up to which you would like to pull')), + ] + remoteopts, + _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')), + "^push": + (push, + [('f', 'force', None, _('force push')), + ('r', 'rev', [], + _('a specific revision up to which you would like to push')), + ] + remoteopts, + _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')), + "recover": (recover, []), + "^remove|rm": + (remove, + [('A', 'after', None, _('record delete for missing files')), + ('f', 'force', None, + _('remove (and delete) file even if added or modified')), + ] + walkopts, + _('[OPTION]... FILE...')), + "rename|mv": + (rename, + [('A', 'after', None, _('record a rename that has already occurred')), + ('f', 'force', None, + _('forcibly copy over an existing managed file')), + ] + walkopts + dryrunopts, + _('[OPTION]... SOURCE... DEST')), + "resolve": + (resolve, + [('a', 'all', None, _('remerge all unresolved files')), + ('l', 'list', None, _('list state of files needing merge')), + ('m', 'mark', None, _('mark files as resolved')), + ('u', 'unmark', None, _('unmark files as resolved'))] + + walkopts, + _('[OPTION]... [FILE]...')), + "revert": + (revert, + [('a', 'all', None, _('revert all changes when no arguments given')), + ('d', 'date', '', _('tipmost revision matching date')), + ('r', 'rev', '', _('revision to revert to')), + ('', 'no-backup', None, _('do not save backup copies of files')), + ] + walkopts + dryrunopts, + _('[OPTION]... [-r REV] [NAME]...')), + "rollback": (rollback, []), + "root": (root, []), + "^serve": + (serve, + [('A', 'accesslog', '', _('name of access log file to write to')), + ('d', 'daemon', None, _('run server in background')), + ('', 'daemon-pipefds', '', _('used internally by daemon mode')), + ('E', 'errorlog', '', _('name of error log file to write to')), + ('p', 'port', 0, _('port to listen on (default: 8000)')), + ('a', 'address', '', _('address to listen on (default: all interfaces)')), + ('', 'prefix', '', _('prefix path to serve from (default: server root)')), + ('n', 'name', '', + _('name to show in web pages (default: working directory)')), + ('', 'webdir-conf', '', _('name of the webdir config file' + ' (serve more than one repository)')), + ('', 'pid-file', '', _('name of file to write process ID to')), + ('', 'stdio', None, _('for remote clients')), + ('t', 'templates', '', _('web templates to use')), + ('', 'style', '', _('template style to use')), + ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')), + ('', 'certificate', '', _('SSL certificate file'))], + _('[OPTION]...')), + "showconfig|debugconfig": + (showconfig, + [('u', 'untrusted', None, _('show untrusted configuration options'))], + _('[-u] [NAME]...')), + "^status|st": + (status, + [('A', 'all', None, _('show status of all files')), + ('m', 'modified', None, _('show only modified files')), + ('a', 'added', None, _('show only added files')), + ('r', 'removed', None, _('show only removed files')), + ('d', 'deleted', None, _('show only deleted (but tracked) files')), + ('c', 'clean', None, _('show only files without changes')), + ('u', 'unknown', None, _('show only unknown (not tracked) files')), + ('i', 'ignored', None, _('show only ignored files')), + ('n', 'no-status', None, _('hide status prefix')), + ('C', 'copies', None, _('show source of copied files')), + ('0', 'print0', None, + _('end filenames with NUL, for use with xargs')), + ('', 'rev', [], _('show difference from revision')), + ] + walkopts, + _('[OPTION]... [FILE]...')), + "tag": + (tag, + [('f', 'force', None, _('replace existing tag')), + ('l', 'local', None, _('make the tag local')), + ('r', 'rev', '', _('revision to tag')), + ('', 'remove', None, _('remove a tag')), + # -l/--local is already there, commitopts cannot be used + ('m', 'message', '', _('use as commit message')), + ] + commitopts2, + _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')), + "tags": (tags, []), + "tip": + (tip, + [('p', 'patch', None, _('show patch')), + ('g', 'git', None, _('use git extended diff format')), + ] + templateopts, + _('[-p]')), + "unbundle": + (unbundle, + [('u', 'update', None, + _('update to new tip if changesets were unbundled'))], + _('[-u] FILE...')), + "^update|up|checkout|co": + (update, + [('C', 'clean', None, _('overwrite locally modified files (no backup)')), + ('c', 'check', None, _('check for uncommitted changes')), + ('d', 'date', '', _('tipmost revision matching date')), + ('r', 'rev', '', _('revision'))], + _('[-C] [-d DATE] [[-r] REV]')), + "verify": (verify, []), + "version": (version_, []), +} + +norepo = ("clone init version help debugcommands debugcomplete debugdata" + " debugindex debugindexdot debugdate debuginstall debugfsinfo") +optionalrepo = ("identify paths serve showconfig debugancestor") diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/config.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/config.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,137 @@ +# config.py - configuration parsing for Mercurial +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import error +import re, os + +class sortdict(dict): + 'a simple sorted dictionary' + def __init__(self, data=None): + self._list = [] + if data: + self.update(data) + def copy(self): + return sortdict(self) + def __setitem__(self, key, val): + if key in self: + self._list.remove(key) + self._list.append(key) + dict.__setitem__(self, key, val) + def __iter__(self): + return self._list.__iter__() + def update(self, src): + for k in src: + self[k] = src[k] + def items(self): + return [(k, self[k]) for k in self._list] + def __delitem__(self, key): + dict.__delitem__(self, key) + self._list.remove(key) + +class config(object): + def __init__(self, data=None): + self._data = {} + self._source = {} + if data: + for k in data._data: + self._data[k] = data[k].copy() + self._source = data._source.copy() + def copy(self): + return config(self) + def __contains__(self, section): + return section in self._data + def __getitem__(self, section): + return self._data.get(section, {}) + def __iter__(self): + for d in self.sections(): + yield d + def update(self, src): + for s in src: + if s not in self: + self._data[s] = sortdict() + self._data[s].update(src._data[s]) + self._source.update(src._source) + def get(self, section, item, default=None): + return self._data.get(section, {}).get(item, default) + def source(self, section, item): + return self._source.get((section, item), "") + def sections(self): + return sorted(self._data.keys()) + def items(self, section): + return self._data.get(section, {}).items() + def set(self, section, item, value, source=""): + if section not in self: + self._data[section] = sortdict() + self._data[section][item] = value + self._source[(section, item)] = source + + def parse(self, src, data, sections=None, remap=None, include=None): + sectionre = re.compile(r'\[([^\[]+)\]') + itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)') + contre = re.compile(r'\s+(\S.*\S)') + emptyre = re.compile(r'(;|#|\s*$)') + unsetre = re.compile(r'%unset\s+(\S+)') + includere = re.compile(r'%include\s+(\S.*\S)') + section = "" + item = None + line = 0 + cont = 0 + + for l in data.splitlines(1): + line += 1 + if cont: + m = contre.match(l) + if m: + if sections and section not in sections: + continue + v = self.get(section, item) + "\n" + m.group(1) + self.set(section, item, v, "%s:%d" % (src, line)) + continue + item = None + m = includere.match(l) + if m: + inc = m.group(1) + base = os.path.dirname(src) + inc = os.path.normpath(os.path.join(base, inc)) + if include: + include(inc, remap=remap, sections=sections) + continue + if emptyre.match(l): + continue + m = sectionre.match(l) + if m: + section = m.group(1) + if remap: + section = remap.get(section, section) + if section not in self: + self._data[section] = sortdict() + continue + m = itemre.match(l) + if m: + item = m.group(1) + cont = 1 + if sections and section not in sections: + continue + self.set(section, item, m.group(2), "%s:%d" % (src, line)) + continue + m = unsetre.match(l) + if m: + name = m.group(1) + if sections and section not in sections: + continue + if self.get(section, name) != None: + del self._data[section][name] + continue + + raise error.ConfigError(_('config error at %s:%d: \'%s\'') + % (src, line, l.rstrip())) + + def read(self, path, fp=None, sections=None, remap=None): + if not fp: + fp = open(path) + self.parse(path, fp.read(), sections, remap, self.read) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/context.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/context.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,816 @@ +# context.py - changeset and file context objects for mercurial +# +# Copyright 2006, 2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import nullid, nullrev, short, hex +from i18n import _ +import ancestor, bdiff, error, util, subrepo +import os, errno + +propertycache = util.propertycache + +class changectx(object): + """A changecontext object makes access to data related to a particular + changeset convenient.""" + def __init__(self, repo, changeid=''): + """changeid is a revision number, node, or tag""" + if changeid == '': + changeid = '.' + self._repo = repo + if isinstance(changeid, (long, int)): + self._rev = changeid + self._node = self._repo.changelog.node(changeid) + else: + self._node = self._repo.lookup(changeid) + self._rev = self._repo.changelog.rev(self._node) + + def __str__(self): + return short(self.node()) + + def __int__(self): + return self.rev() + + def __repr__(self): + return "" % str(self) + + def __hash__(self): + try: + return hash(self._rev) + except AttributeError: + return id(self) + + def __eq__(self, other): + try: + return self._rev == other._rev + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def __nonzero__(self): + return self._rev != nullrev + + @propertycache + def _changeset(self): + return self._repo.changelog.read(self.node()) + + @propertycache + def _manifest(self): + return self._repo.manifest.read(self._changeset[0]) + + @propertycache + def _manifestdelta(self): + return self._repo.manifest.readdelta(self._changeset[0]) + + @propertycache + def _parents(self): + p = self._repo.changelog.parentrevs(self._rev) + if p[1] == nullrev: + p = p[:-1] + return [changectx(self._repo, x) for x in p] + + @propertycache + def substate(self): + return subrepo.state(self) + + def __contains__(self, key): + return key in self._manifest + + def __getitem__(self, key): + return self.filectx(key) + + def __iter__(self): + for f in sorted(self._manifest): + yield f + + def changeset(self): return self._changeset + def manifest(self): return self._manifest + def manifestnode(self): return self._changeset[0] + + def rev(self): return self._rev + def node(self): return self._node + def hex(self): return hex(self._node) + def user(self): return self._changeset[1] + def date(self): return self._changeset[2] + def files(self): return self._changeset[3] + def description(self): return self._changeset[4] + def branch(self): return self._changeset[5].get("branch") + def extra(self): return self._changeset[5] + def tags(self): return self._repo.nodetags(self._node) + + def parents(self): + """return contexts for each parent changeset""" + return self._parents + + def p1(self): + return self._parents[0] + + def p2(self): + if len(self._parents) == 2: + return self._parents[1] + return changectx(self._repo, -1) + + def children(self): + """return contexts for each child changeset""" + c = self._repo.changelog.children(self._node) + return [changectx(self._repo, x) for x in c] + + def ancestors(self): + for a in self._repo.changelog.ancestors(self._rev): + yield changectx(self._repo, a) + + def descendants(self): + for d in self._repo.changelog.descendants(self._rev): + yield changectx(self._repo, d) + + def _fileinfo(self, path): + if '_manifest' in self.__dict__: + try: + return self._manifest[path], self._manifest.flags(path) + except KeyError: + raise error.LookupError(self._node, path, + _('not found in manifest')) + if '_manifestdelta' in self.__dict__ or path in self.files(): + if path in self._manifestdelta: + return self._manifestdelta[path], self._manifestdelta.flags(path) + node, flag = self._repo.manifest.find(self._changeset[0], path) + if not node: + raise error.LookupError(self._node, path, + _('not found in manifest')) + + return node, flag + + def filenode(self, path): + return self._fileinfo(path)[0] + + def flags(self, path): + try: + return self._fileinfo(path)[1] + except error.LookupError: + return '' + + def filectx(self, path, fileid=None, filelog=None): + """get a file context from this changeset""" + if fileid is None: + fileid = self.filenode(path) + return filectx(self._repo, path, fileid=fileid, + changectx=self, filelog=filelog) + + def ancestor(self, c2): + """ + return the ancestor context of self and c2 + """ + n = self._repo.changelog.ancestor(self._node, c2._node) + return changectx(self._repo, n) + + def walk(self, match): + fset = set(match.files()) + # for dirstate.walk, files=['.'] means "walk the whole tree". + # follow that here, too + fset.discard('.') + for fn in self: + for ffn in fset: + # match if the file is the exact name or a directory + if ffn == fn or fn.startswith("%s/" % ffn): + fset.remove(ffn) + break + if match(fn): + yield fn + for fn in sorted(fset): + if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn): + yield fn + + def sub(self, path): + return subrepo.subrepo(self, path) + +class filectx(object): + """A filecontext object makes access to data related to a particular + filerevision convenient.""" + def __init__(self, repo, path, changeid=None, fileid=None, + filelog=None, changectx=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + self._repo = repo + self._path = path + + assert (changeid is not None + or fileid is not None + or changectx is not None) + + if filelog: + self._filelog = filelog + + if changeid is not None: + self._changeid = changeid + if changectx is not None: + self._changectx = changectx + if fileid is not None: + self._fileid = fileid + + @propertycache + def _changectx(self): + return changectx(self._repo, self._changeid) + + @propertycache + def _filelog(self): + return self._repo.file(self._path) + + @propertycache + def _changeid(self): + if '_changectx' in self.__dict__: + return self._changectx.rev() + else: + return self._filelog.linkrev(self._filerev) + + @propertycache + def _filenode(self): + if '_fileid' in self.__dict__: + return self._filelog.lookup(self._fileid) + else: + return self._changectx.filenode(self._path) + + @propertycache + def _filerev(self): + return self._filelog.rev(self._filenode) + + @propertycache + def _repopath(self): + return self._path + + def __nonzero__(self): + try: + self._filenode + return True + except error.LookupError: + # file is missing + return False + + def __str__(self): + return "%s@%s" % (self.path(), short(self.node())) + + def __repr__(self): + return "" % str(self) + + def __hash__(self): + try: + return hash((self._path, self._fileid)) + except AttributeError: + return id(self) + + def __eq__(self, other): + try: + return (self._path == other._path + and self._fileid == other._fileid) + except AttributeError: + return False + + def __ne__(self, other): + return not (self == other) + + def filectx(self, fileid): + '''opens an arbitrary revision of the file without + opening a new filelog''' + return filectx(self._repo, self._path, fileid=fileid, + filelog=self._filelog) + + def filerev(self): return self._filerev + def filenode(self): return self._filenode + def flags(self): return self._changectx.flags(self._path) + def filelog(self): return self._filelog + + def rev(self): + if '_changectx' in self.__dict__: + return self._changectx.rev() + if '_changeid' in self.__dict__: + return self._changectx.rev() + return self._filelog.linkrev(self._filerev) + + def linkrev(self): return self._filelog.linkrev(self._filerev) + def node(self): return self._changectx.node() + def hex(self): return hex(self.node()) + def user(self): return self._changectx.user() + def date(self): return self._changectx.date() + def files(self): return self._changectx.files() + def description(self): return self._changectx.description() + def branch(self): return self._changectx.branch() + def manifest(self): return self._changectx.manifest() + def changectx(self): return self._changectx + + def data(self): return self._filelog.read(self._filenode) + def path(self): return self._path + def size(self): return self._filelog.size(self._filerev) + + def cmp(self, text): return self._filelog.cmp(self._filenode, text) + + def renamed(self): + """check if file was actually renamed in this changeset revision + + If rename logged in file revision, we report copy for changeset only + if file revisions linkrev points back to the changeset in question + or both changeset parents contain different file revisions. + """ + + renamed = self._filelog.renamed(self._filenode) + if not renamed: + return renamed + + if self.rev() == self.linkrev(): + return renamed + + name = self.path() + fnode = self._filenode + for p in self._changectx.parents(): + try: + if fnode == p.filenode(name): + return None + except error.LookupError: + pass + return renamed + + def parents(self): + p = self._path + fl = self._filelog + pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)] + + r = self._filelog.renamed(self._filenode) + if r: + pl[0] = (r[0], r[1], None) + + return [filectx(self._repo, p, fileid=n, filelog=l) + for p,n,l in pl if n != nullid] + + def children(self): + # hard for renames + c = self._filelog.children(self._filenode) + return [filectx(self._repo, self._path, fileid=x, + filelog=self._filelog) for x in c] + + def annotate(self, follow=False, linenumber=None): + '''returns a list of tuples of (ctx, line) for each line + in the file, where ctx is the filectx of the node where + that line was last changed. + This returns tuples of ((ctx, linenumber), line) for each line, + if "linenumber" parameter is NOT "None". + In such tuples, linenumber means one at the first appearance + in the managed file. + To reduce annotation cost, + this returns fixed value(False is used) as linenumber, + if "linenumber" parameter is "False".''' + + def decorate_compat(text, rev): + return ([rev] * len(text.splitlines()), text) + + def without_linenumber(text, rev): + return ([(rev, False)] * len(text.splitlines()), text) + + def with_linenumber(text, rev): + size = len(text.splitlines()) + return ([(rev, i) for i in xrange(1, size + 1)], text) + + decorate = (((linenumber is None) and decorate_compat) or + (linenumber and with_linenumber) or + without_linenumber) + + def pair(parent, child): + for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]): + child[0][b1:b2] = parent[0][a1:a2] + return child + + getlog = util.lrucachefunc(lambda x: self._repo.file(x)) + def getctx(path, fileid): + log = path == self._path and self._filelog or getlog(path) + return filectx(self._repo, path, fileid=fileid, filelog=log) + getctx = util.lrucachefunc(getctx) + + def parents(f): + # we want to reuse filectx objects as much as possible + p = f._path + if f._filerev is None: # working dir + pl = [(n.path(), n.filerev()) for n in f.parents()] + else: + pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)] + + if follow: + r = f.renamed() + if r: + pl[0] = (r[0], getlog(r[0]).rev(r[1])) + + return [getctx(p, n) for p, n in pl if n != nullrev] + + # use linkrev to find the first changeset where self appeared + if self.rev() != self.linkrev(): + base = self.filectx(self.filerev()) + else: + base = self + + # find all ancestors + needed = {base: 1} + visit = [base] + files = [base._path] + while visit: + f = visit.pop(0) + for p in parents(f): + if p not in needed: + needed[p] = 1 + visit.append(p) + if p._path not in files: + files.append(p._path) + else: + # count how many times we'll use this + needed[p] += 1 + + # sort by revision (per file) which is a topological order + visit = [] + for f in files: + fn = [(n.rev(), n) for n in needed if n._path == f] + visit.extend(fn) + + hist = {} + for r, f in sorted(visit): + curr = decorate(f.data(), f) + for p in parents(f): + if p != nullid: + curr = pair(hist[p], curr) + # trim the history of unneeded revs + needed[p] -= 1 + if not needed[p]: + del hist[p] + hist[f] = curr + + return zip(hist[f][0], hist[f][1].splitlines(1)) + + def ancestor(self, fc2): + """ + find the common ancestor file context, if any, of self, and fc2 + """ + + acache = {} + + # prime the ancestor cache for the working directory + for c in (self, fc2): + if c._filerev is None: + pl = [(n.path(), n.filenode()) for n in c.parents()] + acache[(c._path, None)] = pl + + flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog} + def parents(vertex): + if vertex in acache: + return acache[vertex] + f, n = vertex + if f not in flcache: + flcache[f] = self._repo.file(f) + fl = flcache[f] + pl = [(f, p) for p in fl.parents(n) if p != nullid] + re = fl.renamed(n) + if re: + pl.append(re) + acache[vertex] = pl + return pl + + a, b = (self._path, self._filenode), (fc2._path, fc2._filenode) + v = ancestor.ancestor(a, b, parents) + if v: + f, n = v + return filectx(self._repo, f, fileid=n, filelog=flcache[f]) + + return None + +class workingctx(changectx): + """A workingctx object makes access to data related to + the current working directory convenient. + parents - a pair of parent nodeids, or None to use the dirstate. + date - any valid date string or (unixtime, offset), or None. + user - username string, or None. + extra - a dictionary of extra values, or None. + changes - a list of file lists as returned by localrepo.status() + or None to use the repository status. + """ + def __init__(self, repo, parents=None, text="", user=None, date=None, + extra=None, changes=None): + self._repo = repo + self._rev = None + self._node = None + self._text = text + if date: + self._date = util.parsedate(date) + if user: + self._user = user + if parents: + self._parents = [changectx(self._repo, p) for p in parents] + if changes: + self._status = list(changes) + + self._extra = {} + if extra: + self._extra = extra.copy() + if 'branch' not in self._extra: + branch = self._repo.dirstate.branch() + try: + branch = branch.decode('UTF-8').encode('UTF-8') + except UnicodeDecodeError: + raise util.Abort(_('branch name not in UTF-8!')) + self._extra['branch'] = branch + if self._extra['branch'] == '': + self._extra['branch'] = 'default' + + def __str__(self): + return str(self._parents[0]) + "+" + + def __nonzero__(self): + return True + + def __contains__(self, key): + return self._repo.dirstate[key] not in "?r" + + @propertycache + def _manifest(self): + """generate a manifest corresponding to the working directory""" + + man = self._parents[0].manifest().copy() + copied = self._repo.dirstate.copies() + cf = lambda x: man.flags(copied.get(x, x)) + ff = self._repo.dirstate.flagfunc(cf) + modified, added, removed, deleted, unknown = self._status[:5] + for i, l in (("a", added), ("m", modified), ("u", unknown)): + for f in l: + man[f] = man.get(copied.get(f, f), nullid) + i + try: + man.set(f, ff(f)) + except OSError: + pass + + for f in deleted + removed: + if f in man: + del man[f] + + return man + + @propertycache + def _status(self): + return self._repo.status(unknown=True) + + @propertycache + def _user(self): + return self._repo.ui.username() + + @propertycache + def _date(self): + return util.makedate() + + @propertycache + def _parents(self): + p = self._repo.dirstate.parents() + if p[1] == nullid: + p = p[:-1] + self._parents = [changectx(self._repo, x) for x in p] + return self._parents + + def manifest(self): return self._manifest + + def user(self): return self._user or self._repo.ui.username() + def date(self): return self._date + def description(self): return self._text + def files(self): + return sorted(self._status[0] + self._status[1] + self._status[2]) + + def modified(self): return self._status[0] + def added(self): return self._status[1] + def removed(self): return self._status[2] + def deleted(self): return self._status[3] + def unknown(self): return self._status[4] + def clean(self): return self._status[5] + def branch(self): return self._extra['branch'] + def extra(self): return self._extra + + def tags(self): + t = [] + [t.extend(p.tags()) for p in self.parents()] + return t + + def children(self): + return [] + + def flags(self, path): + if '_manifest' in self.__dict__: + try: + return self._manifest.flags(path) + except KeyError: + return '' + + pnode = self._parents[0].changeset()[0] + orig = self._repo.dirstate.copies().get(path, path) + node, flag = self._repo.manifest.find(pnode, orig) + try: + ff = self._repo.dirstate.flagfunc(lambda x: flag or '') + return ff(path) + except OSError: + pass + + if not node or path in self.deleted() or path in self.removed(): + return '' + return flag + + def filectx(self, path, filelog=None): + """get a file context from the working directory""" + return workingfilectx(self._repo, path, workingctx=self, + filelog=filelog) + + def ancestor(self, c2): + """return the ancestor context of self and c2""" + return self._parents[0].ancestor(c2) # punt on two parents for now + + def walk(self, match): + return sorted(self._repo.dirstate.walk(match, True, False)) + + def dirty(self, missing=False): + "check whether a working directory is modified" + + return (self.p2() or self.branch() != self.p1().branch() or + self.modified() or self.added() or self.removed() or + (missing and self.deleted())) + +class workingfilectx(filectx): + """A workingfilectx object makes access to data related to a particular + file in the working directory convenient.""" + def __init__(self, repo, path, filelog=None, workingctx=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + self._repo = repo + self._path = path + self._changeid = None + self._filerev = self._filenode = None + + if filelog: + self._filelog = filelog + if workingctx: + self._changectx = workingctx + + @propertycache + def _changectx(self): + return workingctx(self._repo) + + def __nonzero__(self): + return True + + def __str__(self): + return "%s@%s" % (self.path(), self._changectx) + + def data(self): return self._repo.wread(self._path) + def renamed(self): + rp = self._repo.dirstate.copied(self._path) + if not rp: + return None + return rp, self._changectx._parents[0]._manifest.get(rp, nullid) + + def parents(self): + '''return parent filectxs, following copies if necessary''' + def filenode(ctx, path): + return ctx._manifest.get(path, nullid) + + path = self._path + fl = self._filelog + pcl = self._changectx._parents + renamed = self.renamed() + + if renamed: + pl = [renamed + (None,)] + else: + pl = [(path, filenode(pcl[0], path), fl)] + + for pc in pcl[1:]: + pl.append((path, filenode(pc, path), fl)) + + return [filectx(self._repo, p, fileid=n, filelog=l) + for p,n,l in pl if n != nullid] + + def children(self): + return [] + + def size(self): return os.stat(self._repo.wjoin(self._path)).st_size + def date(self): + t, tz = self._changectx.date() + try: + return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz) + except OSError, err: + if err.errno != errno.ENOENT: raise + return (t, tz) + + def cmp(self, text): return self._repo.wread(self._path) == text + +class memctx(object): + """Use memctx to perform in-memory commits via localrepo.commitctx(). + + Revision information is supplied at initialization time while + related files data and is made available through a callback + mechanism. 'repo' is the current localrepo, 'parents' is a + sequence of two parent revisions identifiers (pass None for every + missing parent), 'text' is the commit message and 'files' lists + names of files touched by the revision (normalized and relative to + repository root). + + filectxfn(repo, memctx, path) is a callable receiving the + repository, the current memctx object and the normalized path of + requested file, relative to repository root. It is fired by the + commit function for every file in 'files', but calls order is + undefined. If the file is available in the revision being + committed (updated or added), filectxfn returns a memfilectx + object. If the file was removed, filectxfn raises an + IOError. Moved files are represented by marking the source file + removed and the new file added with copy information (see + memfilectx). + + user receives the committer name and defaults to current + repository username, date is the commit date in any format + supported by util.parsedate() and defaults to current date, extra + is a dictionary of metadata or is left empty. + """ + def __init__(self, repo, parents, text, files, filectxfn, user=None, + date=None, extra=None): + self._repo = repo + self._rev = None + self._node = None + self._text = text + self._date = date and util.parsedate(date) or util.makedate() + self._user = user + parents = [(p or nullid) for p in parents] + p1, p2 = parents + self._parents = [changectx(self._repo, p) for p in (p1, p2)] + files = sorted(set(files)) + self._status = [files, [], [], [], []] + self._filectxfn = filectxfn + + self._extra = extra and extra.copy() or {} + if 'branch' not in self._extra: + self._extra['branch'] = 'default' + elif self._extra.get('branch') == '': + self._extra['branch'] = 'default' + + def __str__(self): + return str(self._parents[0]) + "+" + + def __int__(self): + return self._rev + + def __nonzero__(self): + return True + + def __getitem__(self, key): + return self.filectx(key) + + def p1(self): return self._parents[0] + def p2(self): return self._parents[1] + + def user(self): return self._user or self._repo.ui.username() + def date(self): return self._date + def description(self): return self._text + def files(self): return self.modified() + def modified(self): return self._status[0] + def added(self): return self._status[1] + def removed(self): return self._status[2] + def deleted(self): return self._status[3] + def unknown(self): return self._status[4] + def clean(self): return self._status[5] + def branch(self): return self._extra['branch'] + def extra(self): return self._extra + def flags(self, f): return self[f].flags() + + def parents(self): + """return contexts for each parent changeset""" + return self._parents + + def filectx(self, path, filelog=None): + """get a file context from the working directory""" + return self._filectxfn(self._repo, self, path) + +class memfilectx(object): + """memfilectx represents an in-memory file to commit. + + See memctx for more details. + """ + def __init__(self, path, data, islink, isexec, copied): + """ + path is the normalized file path relative to repository root. + data is the file content as a string. + islink is True if the file is a symbolic link. + isexec is True if the file is executable. + copied is the source file path if current file was copied in the + revision being committed, or None.""" + self._path = path + self._data = data + self._flags = (islink and 'l' or '') + (isexec and 'x' or '') + self._copied = None + if copied: + self._copied = (copied, nullid) + + def __nonzero__(self): return True + def __str__(self): return "%s@%s" % (self.path(), self._changectx) + def path(self): return self._path + def data(self): return self._data + def flags(self): return self._flags + def isexec(self): return 'x' in self._flags + def islink(self): return 'l' in self._flags + def renamed(self): return self._copied diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/copies.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/copies.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,233 @@ +# copies.py - copy detection for Mercurial +# +# Copyright 2008 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import util +import heapq + +def _nonoverlap(d1, d2, d3): + "Return list of elements in d1 not in d2 or d3" + return sorted([d for d in d1 if d not in d3 and d not in d2]) + +def _dirname(f): + s = f.rfind("/") + if s == -1: + return "" + return f[:s] + +def _dirs(files): + d = set() + for f in files: + f = _dirname(f) + while f not in d: + d.add(f) + f = _dirname(f) + return d + +def _findoldnames(fctx, limit): + "find files that path was copied from, back to linkrev limit" + old = {} + seen = set() + orig = fctx.path() + visit = [(fctx, 0)] + while visit: + fc, depth = visit.pop() + s = str(fc) + if s in seen: + continue + seen.add(s) + if fc.path() != orig and fc.path() not in old: + old[fc.path()] = (depth, fc.path()) # remember depth + if fc.rev() < limit and fc.rev() is not None: + continue + visit += [(p, depth - 1) for p in fc.parents()] + + # return old names sorted by depth + return [o[1] for o in sorted(old.values())] + +def _findlimit(repo, a, b): + "find the earliest revision that's an ancestor of a or b but not both" + # basic idea: + # - mark a and b with different sides + # - if a parent's children are all on the same side, the parent is + # on that side, otherwise it is on no side + # - walk the graph in topological order with the help of a heap; + # - add unseen parents to side map + # - clear side of any parent that has children on different sides + # - track number of interesting revs that might still be on a side + # - track the lowest interesting rev seen + # - quit when interesting revs is zero + + cl = repo.changelog + working = len(cl) # pseudo rev for the working directory + if a is None: + a = working + if b is None: + b = working + + side = {a: -1, b: 1} + visit = [-a, -b] + heapq.heapify(visit) + interesting = len(visit) + limit = working + + while interesting: + r = -heapq.heappop(visit) + if r == working: + parents = [cl.rev(p) for p in repo.dirstate.parents()] + else: + parents = cl.parentrevs(r) + for p in parents: + if p not in side: + # first time we see p; add it to visit + side[p] = side[r] + if side[p]: + interesting += 1 + heapq.heappush(visit, -p) + elif side[p] and side[p] != side[r]: + # p was interesting but now we know better + side[p] = 0 + interesting -= 1 + if side[r]: + limit = r # lowest rev visited + interesting -= 1 + return limit + +def copies(repo, c1, c2, ca, checkdirs=False): + """ + Find moves and copies between context c1 and c2 + """ + # avoid silly behavior for update from empty dir + if not c1 or not c2 or c1 == c2: + return {}, {} + + # avoid silly behavior for parent -> working dir + if c2.node() is None and c1.node() == repo.dirstate.parents()[0]: + return repo.dirstate.copies(), {} + + limit = _findlimit(repo, c1.rev(), c2.rev()) + m1 = c1.manifest() + m2 = c2.manifest() + ma = ca.manifest() + + def makectx(f, n): + if len(n) != 20: # in a working context? + if c1.rev() is None: + return c1.filectx(f) + return c2.filectx(f) + return repo.filectx(f, fileid=n) + + ctx = util.lrucachefunc(makectx) + copy = {} + fullcopy = {} + diverge = {} + + def checkcopies(f, m1, m2): + '''check possible copies of f from m1 to m2''' + c1 = ctx(f, m1[f]) + for of in _findoldnames(c1, limit): + fullcopy[f] = of # remember for dir rename detection + if of in m2: # original file not in other manifest? + # if the original file is unchanged on the other branch, + # no merge needed + if m2[of] != ma.get(of): + c2 = ctx(of, m2[of]) + ca = c1.ancestor(c2) + # related and named changed on only one side? + if ca and (ca.path() == f or ca.path() == c2.path()): + if c1 != ca or c2 != ca: # merge needed? + copy[f] = of + elif of in ma: + diverge.setdefault(of, []).append(f) + + repo.ui.debug(_(" searching for copies back to rev %d\n") % limit) + + u1 = _nonoverlap(m1, m2, ma) + u2 = _nonoverlap(m2, m1, ma) + + if u1: + repo.ui.debug(_(" unmatched files in local:\n %s\n") + % "\n ".join(u1)) + if u2: + repo.ui.debug(_(" unmatched files in other:\n %s\n") + % "\n ".join(u2)) + + for f in u1: + checkcopies(f, m1, m2) + for f in u2: + checkcopies(f, m2, m1) + + diverge2 = set() + for of, fl in diverge.items(): + if len(fl) == 1: + del diverge[of] # not actually divergent + else: + diverge2.update(fl) # reverse map for below + + if fullcopy: + repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n")) + for f in fullcopy: + note = "" + if f in copy: note += "*" + if f in diverge2: note += "!" + repo.ui.debug(" %s -> %s %s\n" % (f, fullcopy[f], note)) + del diverge2 + + if not fullcopy or not checkdirs: + return copy, diverge + + repo.ui.debug(_(" checking for directory renames\n")) + + # generate a directory move map + d1, d2 = _dirs(m1), _dirs(m2) + invalid = set() + dirmove = {} + + # examine each file copy for a potential directory move, which is + # when all the files in a directory are moved to a new directory + for dst, src in fullcopy.iteritems(): + dsrc, ddst = _dirname(src), _dirname(dst) + if dsrc in invalid: + # already seen to be uninteresting + continue + elif dsrc in d1 and ddst in d1: + # directory wasn't entirely moved locally + invalid.add(dsrc) + elif dsrc in d2 and ddst in d2: + # directory wasn't entirely moved remotely + invalid.add(dsrc) + elif dsrc in dirmove and dirmove[dsrc] != ddst: + # files from the same directory moved to two different places + invalid.add(dsrc) + else: + # looks good so far + dirmove[dsrc + "/"] = ddst + "/" + + for i in invalid: + if i in dirmove: + del dirmove[i] + del d1, d2, invalid + + if not dirmove: + return copy, diverge + + for d in dirmove: + repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d])) + + # check unaccounted nonoverlapping files against directory moves + for f in u1 + u2: + if f not in fullcopy: + for d in dirmove: + if f.startswith(d): + # new file added in a directory that was moved, move it + df = dirmove[d] + f[len(d):] + if df not in copy: + copy[f] = df + repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f])) + break + + return copy, diverge diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/demandimport.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/demandimport.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,136 @@ +# demandimport.py - global demand-loading of modules for Mercurial +# +# Copyright 2006, 2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +''' +demandimport - automatic demandloading of modules + +To enable this module, do: + + import demandimport; demandimport.enable() + +Imports of the following forms will be demand-loaded: + + import a, b.c + import a.b as c + from a import b,c # a will be loaded immediately + +These imports will not be delayed: + + from a import * + b = __import__(a) +''' + +import __builtin__ +_origimport = __import__ + +class _demandmod(object): + """module demand-loader and proxy""" + def __init__(self, name, globals, locals): + if '.' in name: + head, rest = name.split('.', 1) + after = [rest] + else: + head = name + after = [] + object.__setattr__(self, "_data", (head, globals, locals, after)) + object.__setattr__(self, "_module", None) + def _extend(self, name): + """add to the list of submodules to load""" + self._data[3].append(name) + def _load(self): + if not self._module: + head, globals, locals, after = self._data + mod = _origimport(head, globals, locals) + # load submodules + def subload(mod, p): + h, t = p, None + if '.' in p: + h, t = p.split('.', 1) + if not hasattr(mod, h): + setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__)) + elif t: + subload(getattr(mod, h), t) + + for x in after: + subload(mod, x) + + # are we in the locals dictionary still? + if locals and locals.get(head) == self: + locals[head] = mod + object.__setattr__(self, "_module", mod) + + def __repr__(self): + if self._module: + return "" % self._data[0] + return "" % self._data[0] + def __call__(self, *args, **kwargs): + raise TypeError("%s object is not callable" % repr(self)) + def __getattribute__(self, attr): + if attr in ('_data', '_extend', '_load', '_module'): + return object.__getattribute__(self, attr) + self._load() + return getattr(self._module, attr) + def __setattr__(self, attr, val): + self._load() + setattr(self._module, attr, val) + +def _demandimport(name, globals=None, locals=None, fromlist=None, level=None): + if not locals or name in ignore or fromlist == ('*',): + # these cases we can't really delay + return _origimport(name, globals, locals, fromlist) + elif not fromlist: + # import a [as b] + if '.' in name: # a.b + base, rest = name.split('.', 1) + # email.__init__ loading email.mime + if globals and globals.get('__name__', None) == base: + return _origimport(name, globals, locals, fromlist) + # if a is already demand-loaded, add b to its submodule list + if base in locals: + if isinstance(locals[base], _demandmod): + locals[base]._extend(rest) + return locals[base] + return _demandmod(name, globals, locals) + else: + if level is not None: + # from . import b,c,d or from .a import b,c,d + return _origimport(name, globals, locals, fromlist, level) + # from a import b,c,d + mod = _origimport(name, globals, locals) + # recurse down the module chain + for comp in name.split('.')[1:]: + if not hasattr(mod, comp): + setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__)) + mod = getattr(mod, comp) + for x in fromlist: + # set requested submodules for demand load + if not(hasattr(mod, x)): + setattr(mod, x, _demandmod(x, mod.__dict__, locals)) + return mod + +ignore = [ + '_hashlib', + '_xmlplus', + 'fcntl', + 'win32com.gen_py', + 'pythoncom', + # imported by tarfile, not available under Windows + 'pwd', + 'grp', + # imported by profile, itself imported by hotshot.stats, + # not available under Windows + 'resource', + ] + +def enable(): + "enable global demand-loading of modules" + __builtin__.__import__ = _demandimport + +def disable(): + "disable global demand-loading of modules" + __builtin__.__import__ = _origimport + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/diffhelpers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/diffhelpers.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,56 @@ +# diffhelpers.py - pure Python implementation of diffhelpers.c +# +# Copyright 2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +def addlines(fp, hunk, lena, lenb, a, b): + while True: + todoa = lena - len(a) + todob = lenb - len(b) + num = max(todoa, todob) + if num == 0: + break + for i in xrange(num): + s = fp.readline() + c = s[0] + if s == "\\ No newline at end of file\n": + fix_newline(hunk, a, b) + continue + if c == "\n": + # Some patches may be missing the control char + # on empty lines. Supply a leading space. + s = " \n" + hunk.append(s) + if c == "+": + b.append(s[1:]) + elif c == "-": + a.append(s) + else: + b.append(s[1:]) + a.append(s) + return 0 + +def fix_newline(hunk, a, b): + l = hunk[-1] + c = l[0] + hline = l[:-1] + + if c == " " or c == "+": + b[-1] = l[1:-1] + if c == " " or c == "-": + a[-1] = hline + hunk[-1] = hline + return 0 + + +def testhunk(a, b, bstart): + alen = len(a) + blen = len(b) + if alen > blen - bstart: + return -1 + for i in xrange(alen): + if a[i][1:] != b[i + bstart]: + return -1 + return 0 diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/dirstate.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/dirstate.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,601 @@ +# dirstate.py - working directory tracking for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import nullid +from i18n import _ +import util, ignore, osutil, parsers +import struct, os, stat, errno +import cStringIO, sys + +_unknown = ('?', 0, 0, 0) +_format = ">cllll" +propertycache = util.propertycache + +def _finddirs(path): + pos = path.rfind('/') + while pos != -1: + yield path[:pos] + pos = path.rfind('/', 0, pos) + +def _incdirs(dirs, path): + for base in _finddirs(path): + if base in dirs: + dirs[base] += 1 + return + dirs[base] = 1 + +def _decdirs(dirs, path): + for base in _finddirs(path): + if dirs[base] > 1: + dirs[base] -= 1 + return + del dirs[base] + +class dirstate(object): + + def __init__(self, opener, ui, root): + self._opener = opener + self._root = root + self._rootdir = os.path.join(root, '') + self._dirty = False + self._dirtypl = False + self._ui = ui + + @propertycache + def _map(self): + self._read() + return self._map + + @propertycache + def _copymap(self): + self._read() + return self._copymap + + @propertycache + def _foldmap(self): + f = {} + for name in self._map: + f[os.path.normcase(name)] = name + return f + + @propertycache + def _branch(self): + try: + return self._opener("branch").read().strip() or "default" + except IOError: + return "default" + + @propertycache + def _pl(self): + try: + st = self._opener("dirstate").read(40) + l = len(st) + if l == 40: + return st[:20], st[20:40] + elif l > 0 and l < 40: + raise util.Abort(_('working directory state appears damaged!')) + except IOError, err: + if err.errno != errno.ENOENT: raise + return [nullid, nullid] + + @propertycache + def _dirs(self): + dirs = {} + for f,s in self._map.iteritems(): + if s[0] != 'r': + _incdirs(dirs, f) + return dirs + + @propertycache + def _ignore(self): + files = [self._join('.hgignore')] + for name, path in self._ui.configitems("ui"): + if name == 'ignore' or name.startswith('ignore.'): + files.append(os.path.expanduser(path)) + return ignore.ignore(self._root, files, self._ui.warn) + + @propertycache + def _slash(self): + return self._ui.configbool('ui', 'slash') and os.sep != '/' + + @propertycache + def _checklink(self): + return util.checklink(self._root) + + @propertycache + def _checkexec(self): + return util.checkexec(self._root) + + @propertycache + def _checkcase(self): + return not util.checkcase(self._join('.hg')) + + def _join(self, f): + # much faster than os.path.join() + # it's safe because f is always a relative path + return self._rootdir + f + + def flagfunc(self, fallback): + if self._checklink: + if self._checkexec: + def f(x): + p = self._join(x) + if os.path.islink(p): + return 'l' + if util.is_exec(p): + return 'x' + return '' + return f + def f(x): + if os.path.islink(self._join(x)): + return 'l' + if 'x' in fallback(x): + return 'x' + return '' + return f + if self._checkexec: + def f(x): + if 'l' in fallback(x): + return 'l' + if util.is_exec(self._join(x)): + return 'x' + return '' + return f + return fallback + + def getcwd(self): + cwd = os.getcwd() + if cwd == self._root: return '' + # self._root ends with a path separator if self._root is '/' or 'C:\' + rootsep = self._root + if not util.endswithsep(rootsep): + rootsep += os.sep + if cwd.startswith(rootsep): + return cwd[len(rootsep):] + else: + # we're outside the repo. return an absolute path. + return cwd + + def pathto(self, f, cwd=None): + if cwd is None: + cwd = self.getcwd() + path = util.pathto(self._root, cwd, f) + if self._slash: + return util.normpath(path) + return path + + def __getitem__(self, key): + ''' current states: + n normal + m needs merging + r marked for removal + a marked for addition + ? not tracked''' + return self._map.get(key, ("?",))[0] + + def __contains__(self, key): + return key in self._map + + def __iter__(self): + for x in sorted(self._map): + yield x + + def parents(self): + return self._pl + + def branch(self): + return self._branch + + def setparents(self, p1, p2=nullid): + self._dirty = self._dirtypl = True + self._pl = p1, p2 + + def setbranch(self, branch): + self._branch = branch + self._opener("branch", "w").write(branch + '\n') + + def _read(self): + self._map = {} + self._copymap = {} + try: + st = self._opener("dirstate").read() + except IOError, err: + if err.errno != errno.ENOENT: raise + return + if not st: + return + + p = parsers.parse_dirstate(self._map, self._copymap, st) + if not self._dirtypl: + self._pl = p + + def invalidate(self): + for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split(): + if a in self.__dict__: + delattr(self, a) + self._dirty = False + + def copy(self, source, dest): + """Mark dest as a copy of source. Unmark dest if source is None. + """ + if source == dest: + return + self._dirty = True + if source is not None: + self._copymap[dest] = source + elif dest in self._copymap: + del self._copymap[dest] + + def copied(self, file): + return self._copymap.get(file, None) + + def copies(self): + return self._copymap + + def _droppath(self, f): + if self[f] not in "?r" and "_dirs" in self.__dict__: + _decdirs(self._dirs, f) + + def _addpath(self, f, check=False): + oldstate = self[f] + if check or oldstate == "r": + if '\r' in f or '\n' in f: + raise util.Abort( + _("'\\n' and '\\r' disallowed in filenames: %r") % f) + if f in self._dirs: + raise util.Abort(_('directory %r already in dirstate') % f) + # shadows + for d in _finddirs(f): + if d in self._dirs: + break + if d in self._map and self[d] != 'r': + raise util.Abort( + _('file %r in dirstate clashes with %r') % (d, f)) + if oldstate in "?r" and "_dirs" in self.__dict__: + _incdirs(self._dirs, f) + + def normal(self, f): + 'mark a file normal and clean' + self._dirty = True + self._addpath(f) + s = os.lstat(self._join(f)) + self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime)) + if f in self._copymap: + del self._copymap[f] + + def normallookup(self, f): + 'mark a file normal, but possibly dirty' + if self._pl[1] != nullid and f in self._map: + # if there is a merge going on and the file was either + # in state 'm' or dirty before being removed, restore that state. + entry = self._map[f] + if entry[0] == 'r' and entry[2] in (-1, -2): + source = self._copymap.get(f) + if entry[2] == -1: + self.merge(f) + elif entry[2] == -2: + self.normaldirty(f) + if source: + self.copy(source, f) + return + if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: + return + self._dirty = True + self._addpath(f) + self._map[f] = ('n', 0, -1, -1) + if f in self._copymap: + del self._copymap[f] + + def normaldirty(self, f): + 'mark a file normal, but dirty' + self._dirty = True + self._addpath(f) + self._map[f] = ('n', 0, -2, -1) + if f in self._copymap: + del self._copymap[f] + + def add(self, f): + 'mark a file added' + self._dirty = True + self._addpath(f, True) + self._map[f] = ('a', 0, -1, -1) + if f in self._copymap: + del self._copymap[f] + + def remove(self, f): + 'mark a file removed' + self._dirty = True + self._droppath(f) + size = 0 + if self._pl[1] != nullid and f in self._map: + entry = self._map[f] + if entry[0] == 'm': + size = -1 + elif entry[0] == 'n' and entry[2] == -2: + size = -2 + self._map[f] = ('r', 0, size, 0) + if size == 0 and f in self._copymap: + del self._copymap[f] + + def merge(self, f): + 'mark a file merged' + self._dirty = True + s = os.lstat(self._join(f)) + self._addpath(f) + self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime)) + if f in self._copymap: + del self._copymap[f] + + def forget(self, f): + 'forget a file' + self._dirty = True + try: + self._droppath(f) + del self._map[f] + except KeyError: + self._ui.warn(_("not in dirstate: %s\n") % f) + + def _normalize(self, path, knownpath): + norm_path = os.path.normcase(path) + fold_path = self._foldmap.get(norm_path, None) + if fold_path is None: + if knownpath or not os.path.exists(os.path.join(self._root, path)): + fold_path = path + else: + fold_path = self._foldmap.setdefault(norm_path, + util.fspath(path, self._root)) + return fold_path + + def clear(self): + self._map = {} + if "_dirs" in self.__dict__: + delattr(self, "_dirs"); + self._copymap = {} + self._pl = [nullid, nullid] + self._dirty = True + + def rebuild(self, parent, files): + self.clear() + for f in files: + if 'x' in files.flags(f): + self._map[f] = ('n', 0777, -1, 0) + else: + self._map[f] = ('n', 0666, -1, 0) + self._pl = (parent, nullid) + self._dirty = True + + def write(self): + if not self._dirty: + return + st = self._opener("dirstate", "w", atomictemp=True) + + try: + gran = int(self._ui.config('dirstate', 'granularity', 1)) + except ValueError: + gran = 1 + limit = sys.maxint + if gran > 0: + limit = util.fstat(st).st_mtime - gran + + cs = cStringIO.StringIO() + copymap = self._copymap + pack = struct.pack + write = cs.write + write("".join(self._pl)) + for f, e in self._map.iteritems(): + if f in copymap: + f = "%s\0%s" % (f, copymap[f]) + if e[3] > limit and e[0] == 'n': + e = (e[0], 0, -1, -1) + e = pack(_format, e[0], e[1], e[2], e[3], len(f)) + write(e) + write(f) + st.write(cs.getvalue()) + st.rename() + self._dirty = self._dirtypl = False + + def _dirignore(self, f): + if f == '.': + return False + if self._ignore(f): + return True + for p in _finddirs(f): + if self._ignore(p): + return True + return False + + def walk(self, match, unknown, ignored): + ''' + walk recursively through the directory tree, finding all files + matched by the match function + + results are yielded in a tuple (filename, stat), where stat + and st is the stat result if the file was found in the directory. + ''' + + def fwarn(f, msg): + self._ui.warn('%s: %s\n' % (self.pathto(f), msg)) + return False + + def badtype(mode): + kind = _('unknown') + if stat.S_ISCHR(mode): kind = _('character device') + elif stat.S_ISBLK(mode): kind = _('block device') + elif stat.S_ISFIFO(mode): kind = _('fifo') + elif stat.S_ISSOCK(mode): kind = _('socket') + elif stat.S_ISDIR(mode): kind = _('directory') + return _('unsupported file type (type is %s)') % kind + + ignore = self._ignore + dirignore = self._dirignore + if ignored: + ignore = util.never + dirignore = util.never + elif not unknown: + # if unknown and ignored are False, skip step 2 + ignore = util.always + dirignore = util.always + + matchfn = match.matchfn + badfn = match.bad + dmap = self._map + normpath = util.normpath + listdir = osutil.listdir + lstat = os.lstat + getkind = stat.S_IFMT + dirkind = stat.S_IFDIR + regkind = stat.S_IFREG + lnkkind = stat.S_IFLNK + join = self._join + work = [] + wadd = work.append + + if self._checkcase: + normalize = self._normalize + else: + normalize = lambda x, y: x + + exact = skipstep3 = False + if matchfn == match.exact: # match.exact + exact = True + dirignore = util.always # skip step 2 + elif match.files() and not match.anypats(): # match.match, no patterns + skipstep3 = True + + files = set(match.files()) + if not files or '.' in files: + files = [''] + results = {'.hg': None} + + # step 1: find all explicit files + for ff in sorted(files): + nf = normalize(normpath(ff), False) + if nf in results: + continue + + try: + st = lstat(join(nf)) + kind = getkind(st.st_mode) + if kind == dirkind: + skipstep3 = False + if nf in dmap: + #file deleted on disk but still in dirstate + results[nf] = None + match.dir(nf) + if not dirignore(nf): + wadd(nf) + elif kind == regkind or kind == lnkkind: + results[nf] = st + else: + badfn(ff, badtype(kind)) + if nf in dmap: + results[nf] = None + except OSError, inst: + if nf in dmap: # does it exactly match a file? + results[nf] = None + else: # does it match a directory? + prefix = nf + "/" + for fn in dmap: + if fn.startswith(prefix): + match.dir(nf) + skipstep3 = False + break + else: + badfn(ff, inst.strerror) + + # step 2: visit subdirectories + while work: + nd = work.pop() + skip = None + if nd == '.': + nd = '' + else: + skip = '.hg' + try: + entries = listdir(join(nd), stat=True, skip=skip) + except OSError, inst: + if inst.errno == errno.EACCES: + fwarn(nd, inst.strerror) + continue + raise + for f, kind, st in entries: + nf = normalize(nd and (nd + "/" + f) or f, True) + if nf not in results: + if kind == dirkind: + if not ignore(nf): + match.dir(nf) + wadd(nf) + if nf in dmap and matchfn(nf): + results[nf] = None + elif kind == regkind or kind == lnkkind: + if nf in dmap: + if matchfn(nf): + results[nf] = st + elif matchfn(nf) and not ignore(nf): + results[nf] = st + elif nf in dmap and matchfn(nf): + results[nf] = None + + # step 3: report unseen items in the dmap hash + if not skipstep3 and not exact: + visit = sorted([f for f in dmap if f not in results and matchfn(f)]) + for nf, st in zip(visit, util.statfiles([join(i) for i in visit])): + if not st is None and not getkind(st.st_mode) in (regkind, lnkkind): + st = None + results[nf] = st + + del results['.hg'] + return results + + def status(self, match, ignored, clean, unknown): + listignored, listclean, listunknown = ignored, clean, unknown + lookup, modified, added, unknown, ignored = [], [], [], [], [] + removed, deleted, clean = [], [], [] + + dmap = self._map + ladd = lookup.append + madd = modified.append + aadd = added.append + uadd = unknown.append + iadd = ignored.append + radd = removed.append + dadd = deleted.append + cadd = clean.append + + for fn, st in self.walk(match, listunknown, listignored).iteritems(): + if fn not in dmap: + if (listignored or match.exact(fn)) and self._dirignore(fn): + if listignored: + iadd(fn) + elif listunknown: + uadd(fn) + continue + + state, mode, size, time = dmap[fn] + + if not st and state in "nma": + dadd(fn) + elif state == 'n': + if (size >= 0 and + (size != st.st_size + or ((mode ^ st.st_mode) & 0100 and self._checkexec)) + or size == -2 + or fn in self._copymap): + madd(fn) + elif time != int(st.st_mtime): + ladd(fn) + elif listclean: + cadd(fn) + elif state == 'm': + madd(fn) + elif state == 'a': + aadd(fn) + elif state == 'r': + radd(fn) + + return (lookup, modified, added, removed, deleted, unknown, ignored, + clean) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/dispatch.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/dispatch.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,501 @@ +# dispatch.py - command dispatching for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import os, sys, atexit, signal, pdb, socket, errno, shlex, time +import util, commands, hg, fancyopts, extensions, hook, error +import cmdutil, encoding +import ui as _ui + +def run(): + "run the command in sys.argv" + sys.exit(dispatch(sys.argv[1:])) + +def dispatch(args): + "run the command specified in args" + try: + u = _ui.ui() + if '--traceback' in args: + u.setconfig('ui', 'traceback', 'on') + except util.Abort, inst: + sys.stderr.write(_("abort: %s\n") % inst) + return -1 + return _runcatch(u, args) + +def _runcatch(ui, args): + def catchterm(*args): + raise error.SignalInterrupt + + for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': + num = getattr(signal, name, None) + if num: signal.signal(num, catchterm) + + try: + try: + # enter the debugger before command execution + if '--debugger' in args: + pdb.set_trace() + try: + return _dispatch(ui, args) + finally: + ui.flush() + except: + # enter the debugger when we hit an exception + if '--debugger' in args: + pdb.post_mortem(sys.exc_info()[2]) + ui.traceback() + raise + + # Global exception handling, alphabetically + # Mercurial-specific first, followed by built-in and library exceptions + except error.AmbiguousCommand, inst: + ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") % + (inst.args[0], " ".join(inst.args[1]))) + except error.ConfigError, inst: + ui.warn(_("hg: %s\n") % inst.args[0]) + except error.LockHeld, inst: + if inst.errno == errno.ETIMEDOUT: + reason = _('timed out waiting for lock held by %s') % inst.locker + else: + reason = _('lock held by %s') % inst.locker + ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason)) + except error.LockUnavailable, inst: + ui.warn(_("abort: could not lock %s: %s\n") % + (inst.desc or inst.filename, inst.strerror)) + except error.ParseError, inst: + if inst.args[0]: + ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1])) + commands.help_(ui, inst.args[0]) + else: + ui.warn(_("hg: %s\n") % inst.args[1]) + commands.help_(ui, 'shortlist') + except error.RepoError, inst: + ui.warn(_("abort: %s!\n") % inst) + except error.ResponseError, inst: + ui.warn(_("abort: %s") % inst.args[0]) + if not isinstance(inst.args[1], basestring): + ui.warn(" %r\n" % (inst.args[1],)) + elif not inst.args[1]: + ui.warn(_(" empty string\n")) + else: + ui.warn("\n%r\n" % util.ellipsis(inst.args[1])) + except error.RevlogError, inst: + ui.warn(_("abort: %s!\n") % inst) + except error.SignalInterrupt: + ui.warn(_("killed!\n")) + except error.UnknownCommand, inst: + ui.warn(_("hg: unknown command '%s'\n") % inst.args[0]) + commands.help_(ui, 'shortlist') + except util.Abort, inst: + ui.warn(_("abort: %s\n") % inst) + except ImportError, inst: + m = str(inst).split()[-1] + ui.warn(_("abort: could not import module %s!\n") % m) + if m in "mpatch bdiff".split(): + ui.warn(_("(did you forget to compile extensions?)\n")) + elif m in "zlib".split(): + ui.warn(_("(is your Python install correct?)\n")) + except IOError, inst: + if hasattr(inst, "code"): + ui.warn(_("abort: %s\n") % inst) + elif hasattr(inst, "reason"): + try: # usually it is in the form (errno, strerror) + reason = inst.reason.args[1] + except: # it might be anything, for example a string + reason = inst.reason + ui.warn(_("abort: error: %s\n") % reason) + elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE: + if ui.debugflag: + ui.warn(_("broken pipe\n")) + elif getattr(inst, "strerror", None): + if getattr(inst, "filename", None): + ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) + else: + ui.warn(_("abort: %s\n") % inst.strerror) + else: + raise + except OSError, inst: + if getattr(inst, "filename", None): + ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename)) + else: + ui.warn(_("abort: %s\n") % inst.strerror) + except KeyboardInterrupt: + try: + ui.warn(_("interrupted!\n")) + except IOError, inst: + if inst.errno == errno.EPIPE: + if ui.debugflag: + ui.warn(_("\nbroken pipe\n")) + else: + raise + except MemoryError: + ui.warn(_("abort: out of memory\n")) + except SystemExit, inst: + # Commands shouldn't sys.exit directly, but give a return code. + # Just in case catch this and and pass exit code to caller. + return inst.code + except socket.error, inst: + ui.warn(_("abort: %s\n") % inst.args[-1]) + except: + ui.warn(_("** unknown exception encountered, details follow\n")) + ui.warn(_("** report bug details to " + "http://mercurial.selenic.com/bts/\n")) + ui.warn(_("** or mercurial@selenic.com\n")) + ui.warn(_("** Mercurial Distributed SCM (version %s)\n") + % util.version()) + ui.warn(_("** Extensions loaded: %s\n") + % ", ".join([x[0] for x in extensions.extensions()])) + raise + + return -1 + +def _findrepo(p): + while not os.path.isdir(os.path.join(p, ".hg")): + oldp, p = p, os.path.dirname(p) + if p == oldp: + return None + + return p + +def aliasargs(fn): + if hasattr(fn, 'args'): + return fn.args + return [] + +class cmdalias(object): + def __init__(self, name, definition, cmdtable): + self.name = name + self.definition = definition + self.args = [] + self.opts = [] + self.help = '' + self.norepo = True + + try: + cmdutil.findcmd(self.name, cmdtable, True) + self.shadows = True + except error.UnknownCommand: + self.shadows = False + + if not self.definition: + def fn(ui, *args): + ui.warn(_("no definition for alias '%s'\n") % self.name) + return 1 + self.fn = fn + + return + + args = shlex.split(self.definition) + cmd = args.pop(0) + opts = [] + help = '' + + try: + self.fn, self.opts, self.help = cmdutil.findcmd(cmd, cmdtable, False)[1] + self.args = aliasargs(self.fn) + args + if cmd not in commands.norepo.split(' '): + self.norepo = False + except error.UnknownCommand: + def fn(ui, *args): + ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \ + % (self.name, cmd)) + return 1 + self.fn = fn + except error.AmbiguousCommand: + def fn(ui, *args): + ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \ + % (self.name, cmd)) + return 1 + self.fn = fn + + def __call__(self, ui, *args, **opts): + if self.shadows: + ui.debug(_("alias '%s' shadows command\n") % self.name) + + return self.fn(ui, *args, **opts) + +def addaliases(ui, cmdtable): + # aliases are processed after extensions have been loaded, so they + # may use extension commands. Aliases can also use other alias definitions, + # but only if they have been defined prior to the current definition. + for alias, definition in ui.configitems('alias'): + aliasdef = cmdalias(alias, definition, cmdtable) + cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help) + if aliasdef.norepo: + commands.norepo += ' %s' % alias + +def _parse(ui, args): + options = {} + cmdoptions = {} + + try: + args = fancyopts.fancyopts(args, commands.globalopts, options) + except fancyopts.getopt.GetoptError, inst: + raise error.ParseError(None, inst) + + if args: + cmd, args = args[0], args[1:] + aliases, i = cmdutil.findcmd(cmd, commands.table, + ui.config("ui", "strict")) + cmd = aliases[0] + args = aliasargs(i[0]) + args + defaults = ui.config("defaults", cmd) + if defaults: + args = shlex.split(defaults) + args + c = list(i[1]) + else: + cmd = None + c = [] + + # combine global options into local + for o in commands.globalopts: + c.append((o[0], o[1], options[o[1]], o[3])) + + try: + args = fancyopts.fancyopts(args, c, cmdoptions, True) + except fancyopts.getopt.GetoptError, inst: + raise error.ParseError(cmd, inst) + + # separate global options back out + for o in commands.globalopts: + n = o[1] + options[n] = cmdoptions[n] + del cmdoptions[n] + + return (cmd, cmd and i[0] or None, args, options, cmdoptions) + +def _parseconfig(ui, config): + """parse the --config options from the command line""" + for cfg in config: + try: + name, value = cfg.split('=', 1) + section, name = name.split('.', 1) + if not section or not name: + raise IndexError + ui.setconfig(section, name, value) + except (IndexError, ValueError): + raise util.Abort(_('malformed --config option: %s') % cfg) + +def _earlygetopt(aliases, args): + """Return list of values for an option (or aliases). + + The values are listed in the order they appear in args. + The options and values are removed from args. + """ + try: + argcount = args.index("--") + except ValueError: + argcount = len(args) + shortopts = [opt for opt in aliases if len(opt) == 2] + values = [] + pos = 0 + while pos < argcount: + if args[pos] in aliases: + if pos + 1 >= argcount: + # ignore and let getopt report an error if there is no value + break + del args[pos] + values.append(args.pop(pos)) + argcount -= 2 + elif args[pos][:2] in shortopts: + # short option can have no following space, e.g. hg log -Rfoo + values.append(args.pop(pos)[2:]) + argcount -= 1 + else: + pos += 1 + return values + +def runcommand(lui, repo, cmd, fullargs, ui, options, d): + # run pre-hook, and abort if it fails + ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs)) + if ret: + return ret + ret = _runcommand(ui, options, cmd, d) + # run post-hook, passing command result + hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs), + result = ret) + return ret + +_loaded = set() +def _dispatch(ui, args): + # read --config before doing anything else + # (e.g. to change trust settings for reading .hg/hgrc) + _parseconfig(ui, _earlygetopt(['--config'], args)) + + # check for cwd + cwd = _earlygetopt(['--cwd'], args) + if cwd: + os.chdir(cwd[-1]) + + # read the local repository .hgrc into a local ui object + path = _findrepo(os.getcwd()) or "" + if not path: + lui = ui + if path: + try: + lui = ui.copy() + lui.readconfig(os.path.join(path, ".hg", "hgrc")) + except IOError: + pass + + # now we can expand paths, even ones in .hg/hgrc + rpath = _earlygetopt(["-R", "--repository", "--repo"], args) + if rpath: + path = lui.expandpath(rpath[-1]) + lui = ui.copy() + lui.readconfig(os.path.join(path, ".hg", "hgrc")) + + extensions.loadall(lui) + for name, module in extensions.extensions(): + if name in _loaded: + continue + + # setup extensions + # TODO this should be generalized to scheme, where extensions can + # redepend on other extensions. then we should toposort them, and + # do initialization in correct order + extsetup = getattr(module, 'extsetup', None) + if extsetup: + extsetup() + + cmdtable = getattr(module, 'cmdtable', {}) + overrides = [cmd for cmd in cmdtable if cmd in commands.table] + if overrides: + ui.warn(_("extension '%s' overrides commands: %s\n") + % (name, " ".join(overrides))) + commands.table.update(cmdtable) + _loaded.add(name) + + addaliases(lui, commands.table) + + # check for fallback encoding + fallback = lui.config('ui', 'fallbackencoding') + if fallback: + encoding.fallbackencoding = fallback + + fullargs = args + cmd, func, args, options, cmdoptions = _parse(lui, args) + + if options["config"]: + raise util.Abort(_("Option --config may not be abbreviated!")) + if options["cwd"]: + raise util.Abort(_("Option --cwd may not be abbreviated!")) + if options["repository"]: + raise util.Abort(_( + "Option -R has to be separated from other options (e.g. not -qR) " + "and --repository may only be abbreviated as --repo!")) + + if options["encoding"]: + encoding.encoding = options["encoding"] + if options["encodingmode"]: + encoding.encodingmode = options["encodingmode"] + if options["time"]: + def get_times(): + t = os.times() + if t[4] == 0.0: # Windows leaves this as zero, so use time.clock() + t = (t[0], t[1], t[2], t[3], time.clock()) + return t + s = get_times() + def print_time(): + t = get_times() + ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") % + (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3])) + atexit.register(print_time) + + if options['verbose'] or options['debug'] or options['quiet']: + ui.setconfig('ui', 'verbose', str(bool(options['verbose']))) + ui.setconfig('ui', 'debug', str(bool(options['debug']))) + ui.setconfig('ui', 'quiet', str(bool(options['quiet']))) + if options['traceback']: + ui.setconfig('ui', 'traceback', 'on') + if options['noninteractive']: + ui.setconfig('ui', 'interactive', 'off') + + if options['help']: + return commands.help_(ui, cmd, options['version']) + elif options['version']: + return commands.version_(ui) + elif not cmd: + return commands.help_(ui, 'shortlist') + + repo = None + if cmd not in commands.norepo.split(): + try: + repo = hg.repository(ui, path=path) + ui = repo.ui + if not repo.local(): + raise util.Abort(_("repository '%s' is not local") % path) + ui.setconfig("bundle", "mainreporoot", repo.root) + except error.RepoError: + if cmd not in commands.optionalrepo.split(): + if args and not path: # try to infer -R from command args + repos = map(_findrepo, args) + guess = repos[0] + if guess and repos.count(guess) == len(repos): + return _dispatch(ui, ['--repository', guess] + fullargs) + if not path: + raise error.RepoError(_("There is no Mercurial repository" + " here (.hg not found)")) + raise + args.insert(0, repo) + elif rpath: + ui.warn("warning: --repository ignored\n") + + d = lambda: util.checksignature(func)(ui, *args, **cmdoptions) + return runcommand(lui, repo, cmd, fullargs, ui, options, d) + +def _runcommand(ui, options, cmd, cmdfunc): + def checkargs(): + try: + return cmdfunc() + except error.SignatureError: + raise error.ParseError(cmd, _("invalid arguments")) + + if options['profile']: + format = ui.config('profiling', 'format', default='text') + + if not format in ['text', 'kcachegrind']: + ui.warn(_("unrecognized profiling format '%s'" + " - Ignored\n") % format) + format = 'text' + + output = ui.config('profiling', 'output') + + if output: + path = os.path.expanduser(output) + path = ui.expandpath(path) + ostream = open(path, 'wb') + else: + ostream = sys.stderr + + try: + from mercurial import lsprof + except ImportError: + raise util.Abort(_( + 'lsprof not available - install from ' + 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) + p = lsprof.Profiler() + p.enable(subcalls=True) + try: + return checkargs() + finally: + p.disable() + + if format == 'kcachegrind': + import lsprofcalltree + calltree = lsprofcalltree.KCacheGrind(p) + calltree.output(ostream) + else: + # format == 'text' + stats = lsprof.Stats(p.getstats()) + stats.sort() + stats.pprint(top=10, file=ostream, climit=5) + + if output: + ostream.close() + else: + return checkargs() diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/encoding.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/encoding.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,75 @@ +# encoding.py - character transcoding support for Mercurial +# +# Copyright 2005-2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import error +import sys, unicodedata, locale, os + +_encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'} + +try: + encoding = os.environ.get("HGENCODING") + if sys.platform == 'darwin' and not encoding: + # On darwin, getpreferredencoding ignores the locale environment and + # always returns mac-roman. We override this if the environment is + # not C (has been customized by the user). + locale.setlocale(locale.LC_CTYPE, '') + encoding = locale.getlocale()[1] + if not encoding: + encoding = locale.getpreferredencoding() or 'ascii' + encoding = _encodingfixup.get(encoding, encoding) +except locale.Error: + encoding = 'ascii' +encodingmode = os.environ.get("HGENCODINGMODE", "strict") +fallbackencoding = 'ISO-8859-1' + +def tolocal(s): + """ + Convert a string from internal UTF-8 to local encoding + + All internal strings should be UTF-8 but some repos before the + implementation of locale support may contain latin1 or possibly + other character sets. We attempt to decode everything strictly + using UTF-8, then Latin-1, and failing that, we use UTF-8 and + replace unknown characters. + """ + for e in ('UTF-8', fallbackencoding): + try: + u = s.decode(e) # attempt strict decoding + return u.encode(encoding, "replace") + except LookupError, k: + raise error.Abort("%s, please check your locale settings" % k) + except UnicodeDecodeError: + pass + u = s.decode("utf-8", "replace") # last ditch + return u.encode(encoding, "replace") + +def fromlocal(s): + """ + Convert a string from the local character encoding to UTF-8 + + We attempt to decode strings using the encoding mode set by + HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown + characters will cause an error message. Other modes include + 'replace', which replaces unknown characters with a special + Unicode character, and 'ignore', which drops the character. + """ + try: + return s.decode(encoding, encodingmode).encode("utf-8") + except UnicodeDecodeError, inst: + sub = s[max(0, inst.start-10):inst.start+10] + raise error.Abort("decoding near '%s': %s!" % (sub, inst)) + except LookupError, k: + raise error.Abort("%s, please check your locale settings" % k) + +def colwidth(s): + "Find the column width of a UTF-8 string for display" + d = s.decode(encoding, 'replace') + if hasattr(unicodedata, 'east_asian_width'): + w = unicodedata.east_asian_width + return sum([w(c) in 'WF' and 2 or 1 for c in d]) + return len(d) + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/error.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/error.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,72 @@ +# error.py - Mercurial exceptions +# +# Copyright 2005-2008 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +"""Mercurial exceptions. + +This allows us to catch exceptions at higher levels without forcing +imports. +""" + +# Do not import anything here, please + +class RevlogError(Exception): + pass + +class LookupError(RevlogError, KeyError): + def __init__(self, name, index, message): + self.name = name + if isinstance(name, str) and len(name) == 20: + from node import short + name = short(name) + RevlogError.__init__(self, '%s@%s: %s' % (index, name, message)) + + def __str__(self): + return RevlogError.__str__(self) + +class ParseError(Exception): + """Exception raised on errors in parsing the command line.""" + +class ConfigError(Exception): + 'Exception raised when parsing config files' + +class RepoError(Exception): + pass + +class CapabilityError(RepoError): + pass + +class LockError(IOError): + def __init__(self, errno, strerror, filename, desc): + IOError.__init__(self, errno, strerror, filename) + self.desc = desc + +class LockHeld(LockError): + def __init__(self, errno, filename, desc, locker): + LockError.__init__(self, errno, 'Lock held', filename, desc) + self.locker = locker + +class LockUnavailable(LockError): + pass + +class ResponseError(Exception): + """Raised to print an error with part of output and exit.""" + +class UnknownCommand(Exception): + """Exception raised if command is not in the command table.""" + +class AmbiguousCommand(Exception): + """Exception raised if command shortcut matches more than one command.""" + +# derived from KeyboardInterrupt to simplify some breakout code +class SignalInterrupt(KeyboardInterrupt): + """Exception raised on SIGTERM and SIGHUP.""" + +class SignatureError(Exception): + pass + +class Abort(Exception): + """Raised if a command needs to print an error and exit.""" diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/extensions.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/extensions.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,182 @@ +# extensions.py - extension handling for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import imp, os +import util, cmdutil, help +from i18n import _, gettext + +_extensions = {} +_order = [] + +def extensions(): + for name in _order: + module = _extensions[name] + if module: + yield name, module + +def find(name): + '''return module with given extension name''' + try: + return _extensions[name] + except KeyError: + for k, v in _extensions.iteritems(): + if k.endswith('.' + name) or k.endswith('/' + name): + return v + raise KeyError(name) + +def loadpath(path, module_name): + module_name = module_name.replace('.', '_') + path = os.path.expanduser(path) + if os.path.isdir(path): + # module/__init__.py style + d, f = os.path.split(path.rstrip('/')) + fd, fpath, desc = imp.find_module(f, [d]) + return imp.load_module(module_name, fd, fpath, desc) + else: + return imp.load_source(module_name, path) + +def load(ui, name, path): + if name.startswith('hgext.') or name.startswith('hgext/'): + shortname = name[6:] + else: + shortname = name + if shortname in _extensions: + return + _extensions[shortname] = None + if path: + # the module will be loaded in sys.modules + # choose an unique name so that it doesn't + # conflicts with other modules + mod = loadpath(path, 'hgext.%s' % name) + else: + def importh(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + try: + mod = importh("hgext.%s" % name) + except ImportError: + mod = importh(name) + _extensions[shortname] = mod + _order.append(shortname) + + uisetup = getattr(mod, 'uisetup', None) + if uisetup: + uisetup(ui) + +def loadall(ui): + result = ui.configitems("extensions") + for (name, path) in result: + if path: + if path[0] == '!': + continue + try: + load(ui, name, path) + except KeyboardInterrupt: + raise + except Exception, inst: + if path: + ui.warn(_("*** failed to import extension %s from %s: %s\n") + % (name, path, inst)) + else: + ui.warn(_("*** failed to import extension %s: %s\n") + % (name, inst)) + if ui.traceback(): + return 1 + +def wrapcommand(table, command, wrapper): + aliases, entry = cmdutil.findcmd(command, table) + for alias, e in table.iteritems(): + if e is entry: + key = alias + break + + origfn = entry[0] + def wrap(*args, **kwargs): + return util.checksignature(wrapper)( + util.checksignature(origfn), *args, **kwargs) + + wrap.__doc__ = getattr(origfn, '__doc__') + wrap.__module__ = getattr(origfn, '__module__') + + newentry = list(entry) + newentry[0] = wrap + table[key] = tuple(newentry) + return entry + +def wrapfunction(container, funcname, wrapper): + def wrap(*args, **kwargs): + return wrapper(origfn, *args, **kwargs) + + origfn = getattr(container, funcname) + setattr(container, funcname, wrap) + return origfn + +def disabled(): + '''find disabled extensions from hgext + returns a dict of {name: desc}, and the max name length''' + + import hgext + extpath = os.path.dirname(os.path.abspath(hgext.__file__)) + + try: # might not be a filesystem path + files = os.listdir(extpath) + except OSError: + return None, 0 + + exts = {} + maxlength = 0 + for e in files: + + if e.endswith('.py'): + name = e.rsplit('.', 1)[0] + path = os.path.join(extpath, e) + else: + name = e + path = os.path.join(extpath, e, '__init__.py') + if not os.path.exists(path): + continue + + if name in exts or name in _order or name == '__init__': + continue + + try: + file = open(path) + except IOError: + continue + else: + doc = help.moduledoc(file) + file.close() + + if doc: # extracting localized synopsis + exts[name] = gettext(doc).splitlines()[0] + else: + exts[name] = _('(no help text available)') + + if len(name) > maxlength: + maxlength = len(name) + + return exts, maxlength + +def enabled(): + '''return a dict of {name: desc} of extensions, and the max name length''' + + if not enabled: + return {}, 0 + + exts = {} + maxlength = 0 + exthelps = [] + for ename, ext in extensions(): + doc = (gettext(ext.__doc__) or _('(no help text available)')) + ename = ename.split('.')[-1] + maxlength = max(len(ename), maxlength) + exts[ename] = doc.splitlines(0)[0].strip() + + return exts, maxlength diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/fancyopts.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/fancyopts.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,110 @@ +# fancyopts.py - better command line parsing +# +# Copyright 2005-2009 Matt Mackall and others +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import getopt + +def gnugetopt(args, options, longoptions): + """Parse options mostly like getopt.gnu_getopt. + + This is different from getopt.gnu_getopt in that an argument of - will + become an argument of - instead of vanishing completely. + """ + extraargs = [] + if '--' in args: + stopindex = args.index('--') + extraargs = args[stopindex+1:] + args = args[:stopindex] + opts, parseargs = getopt.getopt(args, options, longoptions) + args = [] + while parseargs: + arg = parseargs.pop(0) + if arg and arg[0] == '-' and len(arg) > 1: + parseargs.insert(0, arg) + topts, newparseargs = getopt.getopt(parseargs, options, longoptions) + opts = opts + topts + parseargs = newparseargs + else: + args.append(arg) + args.extend(extraargs) + return opts, args + + +def fancyopts(args, options, state, gnu=False): + """ + read args, parse options, and store options in state + + each option is a tuple of: + + short option or '' + long option + default value + description + + option types include: + + boolean or none - option sets variable in state to true + string - parameter string is stored in state + list - parameter string is added to a list + integer - parameter strings is stored as int + function - call function with parameter + + non-option args are returned + """ + namelist = [] + shortlist = '' + argmap = {} + defmap = {} + + for short, name, default, comment in options: + # convert opts to getopt format + oname = name + name = name.replace('-', '_') + + argmap['-' + short] = argmap['--' + oname] = name + defmap[name] = default + + # copy defaults to state + if isinstance(default, list): + state[name] = default[:] + elif hasattr(default, '__call__'): + state[name] = None + else: + state[name] = default + + # does it take a parameter? + if not (default is None or default is True or default is False): + if short: short += ':' + if oname: oname += '=' + if short: + shortlist += short + if name: + namelist.append(oname) + + # parse arguments + if gnu: + parse = gnugetopt + else: + parse = getopt.getopt + opts, args = parse(args, shortlist, namelist) + + # transfer result to state + for opt, val in opts: + name = argmap[opt] + t = type(defmap[name]) + if t is type(fancyopts): + state[name] = defmap[name](val) + elif t is type(1): + state[name] = int(val) + elif t is type(''): + state[name] = val + elif t is type([]): + state[name].append(val) + elif t is type(None) or t is type(False): + state[name] = True + + # return unparsed args + return args diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/filelog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/filelog.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,68 @@ +# filelog.py - file history class for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import revlog + +class filelog(revlog.revlog): + def __init__(self, opener, path): + revlog.revlog.__init__(self, opener, + "/".join(("data", path + ".i"))) + + def read(self, node): + t = self.revision(node) + if not t.startswith('\1\n'): + return t + s = t.index('\1\n', 2) + return t[s+2:] + + def _readmeta(self, node): + t = self.revision(node) + if not t.startswith('\1\n'): + return {} + s = t.index('\1\n', 2) + mt = t[2:s] + m = {} + for l in mt.splitlines(): + k, v = l.split(": ", 1) + m[k] = v + return m + + def add(self, text, meta, transaction, link, p1=None, p2=None): + if meta or text.startswith('\1\n'): + mt = "" + if meta: + mt = ["%s: %s\n" % (k, v) for k, v in meta.iteritems()] + text = "\1\n%s\1\n%s" % ("".join(mt), text) + return self.addrevision(text, transaction, link, p1, p2) + + def renamed(self, node): + if self.parents(node)[0] != revlog.nullid: + return False + m = self._readmeta(node) + if m and "copy" in m: + return (m["copy"], revlog.bin(m["copyrev"])) + return False + + def size(self, rev): + """return the size of a given revision""" + + # for revisions with renames, we have to go the slow way + node = self.node(rev) + if self.renamed(node): + return len(self.read(node)) + + return revlog.revlog.size(self, rev) + + def cmp(self, node, text): + """compare text with a given file revision""" + + # for renames, we have to go the slow way + if self.renamed(node): + t2 = self.read(node) + return t2 != text + + return revlog.revlog.cmp(self, node, text) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/filemerge.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/filemerge.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,231 @@ +# filemerge.py - file-level merge handling for Mercurial +# +# Copyright 2006, 2007, 2008 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import short +from i18n import _ +import util, simplemerge, match +import os, tempfile, re, filecmp + +def _toolstr(ui, tool, part, default=""): + return ui.config("merge-tools", tool + "." + part, default) + +def _toolbool(ui, tool, part, default=False): + return ui.configbool("merge-tools", tool + "." + part, default) + +_internal = ['internal:' + s + for s in 'fail local other merge prompt dump'.split()] + +def _findtool(ui, tool): + if tool in _internal: + return tool + k = _toolstr(ui, tool, "regkey") + if k: + p = util.lookup_reg(k, _toolstr(ui, tool, "regname")) + if p: + p = util.find_exe(p + _toolstr(ui, tool, "regappend")) + if p: + return p + return util.find_exe(_toolstr(ui, tool, "executable", tool)) + +def _picktool(repo, ui, path, binary, symlink): + def check(tool, pat, symlink, binary): + tmsg = tool + if pat: + tmsg += " specified for " + pat + if not _findtool(ui, tool): + if pat: # explicitly requested tool deserves a warning + ui.warn(_("couldn't find merge tool %s\n") % tmsg) + else: # configured but non-existing tools are more silent + ui.note(_("couldn't find merge tool %s\n") % tmsg) + elif symlink and not _toolbool(ui, tool, "symlink"): + ui.warn(_("tool %s can't handle symlinks\n") % tmsg) + elif binary and not _toolbool(ui, tool, "binary"): + ui.warn(_("tool %s can't handle binary\n") % tmsg) + elif not util.gui() and _toolbool(ui, tool, "gui"): + ui.warn(_("tool %s requires a GUI\n") % tmsg) + else: + return True + return False + + # HGMERGE takes precedence + hgmerge = os.environ.get("HGMERGE") + if hgmerge: + return (hgmerge, hgmerge) + + # then patterns + for pat, tool in ui.configitems("merge-patterns"): + mf = match.match(repo.root, '', [pat]) + if mf(path) and check(tool, pat, symlink, False): + toolpath = _findtool(ui, tool) + return (tool, '"' + toolpath + '"') + + # then merge tools + tools = {} + for k,v in ui.configitems("merge-tools"): + t = k.split('.')[0] + if t not in tools: + tools[t] = int(_toolstr(ui, t, "priority", "0")) + names = tools.keys() + tools = sorted([(-p,t) for t,p in tools.items()]) + uimerge = ui.config("ui", "merge") + if uimerge: + if uimerge not in names: + return (uimerge, uimerge) + tools.insert(0, (None, uimerge)) # highest priority + tools.append((None, "hgmerge")) # the old default, if found + for p,t in tools: + if check(t, None, symlink, binary): + toolpath = _findtool(ui, t) + return (t, '"' + toolpath + '"') + # internal merge as last resort + return (not (symlink or binary) and "internal:merge" or None, None) + +def _eoltype(data): + "Guess the EOL type of a file" + if '\0' in data: # binary + return None + if '\r\n' in data: # Windows + return '\r\n' + if '\r' in data: # Old Mac + return '\r' + if '\n' in data: # UNIX + return '\n' + return None # unknown + +def _matcheol(file, origfile): + "Convert EOL markers in a file to match origfile" + tostyle = _eoltype(open(origfile, "rb").read()) + if tostyle: + data = open(file, "rb").read() + style = _eoltype(data) + if style: + newdata = data.replace(style, tostyle) + if newdata != data: + open(file, "wb").write(newdata) + +def filemerge(repo, mynode, orig, fcd, fco, fca): + """perform a 3-way merge in the working directory + + mynode = parent node before merge + orig = original local filename before merge + fco = other file context + fca = ancestor file context + fcd = local file context for current/destination file + """ + + def temp(prefix, ctx): + pre = "%s~%s." % (os.path.basename(ctx.path()), prefix) + (fd, name) = tempfile.mkstemp(prefix=pre) + data = repo.wwritedata(ctx.path(), ctx.data()) + f = os.fdopen(fd, "wb") + f.write(data) + f.close() + return name + + def isbin(ctx): + try: + return util.binary(ctx.data()) + except IOError: + return False + + if not fco.cmp(fcd.data()): # files identical? + return None + + ui = repo.ui + fd = fcd.path() + binary = isbin(fcd) or isbin(fco) or isbin(fca) + symlink = 'l' in fcd.flags() + fco.flags() + tool, toolpath = _picktool(repo, ui, fd, binary, symlink) + ui.debug(_("picked tool '%s' for %s (binary %s symlink %s)\n") % + (tool, fd, binary, symlink)) + + if not tool or tool == 'internal:prompt': + tool = "internal:local" + if ui.prompt(_(" no tool found to merge %s\n" + "keep (l)ocal or take (o)ther?") % fd, + (_("&Local"), _("&Other")), _("l")) != _("l"): + tool = "internal:other" + if tool == "internal:local": + return 0 + if tool == "internal:other": + repo.wwrite(fd, fco.data(), fco.flags()) + return 0 + if tool == "internal:fail": + return 1 + + # do the actual merge + a = repo.wjoin(fd) + b = temp("base", fca) + c = temp("other", fco) + out = "" + back = a + ".orig" + util.copyfile(a, back) + + if orig != fco.path(): + ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd)) + else: + ui.status(_("merging %s\n") % fd) + + ui.debug(_("my %s other %s ancestor %s\n") % (fcd, fco, fca)) + + # do we attempt to simplemerge first? + if _toolbool(ui, tool, "premerge", not (binary or symlink)): + r = simplemerge.simplemerge(ui, a, b, c, quiet=True) + if not r: + ui.debug(_(" premerge successful\n")) + os.unlink(back) + os.unlink(b) + os.unlink(c) + return 0 + util.copyfile(back, a) # restore from backup and try again + + env = dict(HG_FILE=fd, + HG_MY_NODE=short(mynode), + HG_OTHER_NODE=str(fco.changectx()), + HG_MY_ISLINK='l' in fcd.flags(), + HG_OTHER_ISLINK='l' in fco.flags(), + HG_BASE_ISLINK='l' in fca.flags()) + + if tool == "internal:merge": + r = simplemerge.simplemerge(ui, a, b, c, label=['local', 'other']) + elif tool == 'internal:dump': + a = repo.wjoin(fd) + util.copyfile(a, a + ".local") + repo.wwrite(fd + ".other", fco.data(), fco.flags()) + repo.wwrite(fd + ".base", fca.data(), fca.flags()) + return 1 # unresolved + else: + args = _toolstr(ui, tool, "args", '$local $base $other') + if "$output" in args: + out, a = a, back # read input from backup, write to original + replace = dict(local=a, base=b, other=c, output=out) + args = re.sub("\$(local|base|other|output)", + lambda x: '"%s"' % replace[x.group()[1:]], args) + r = util.system(toolpath + ' ' + args, cwd=repo.root, environ=env) + + if not r and _toolbool(ui, tool, "checkconflicts"): + if re.match("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data()): + r = 1 + + if not r and _toolbool(ui, tool, "checkchanged"): + if filecmp.cmp(repo.wjoin(fd), back): + if ui.prompt(_(" output file %s appears unchanged\n" + "was merge successful (yn)?") % fd, + (_("&Yes"), _("&No")), _("n")) != _("y"): + r = 1 + + if _toolbool(ui, tool, "fixeol"): + _matcheol(repo.wjoin(fd), back) + + if r: + ui.warn(_("merging %s failed!\n") % fd) + else: + os.unlink(back) + + os.unlink(b) + os.unlink(c) + return r diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/graphmod.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/graphmod.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,119 @@ +# Revision graph generator for Mercurial +# +# Copyright 2008 Dirkjan Ochtman +# Copyright 2007 Joel Rosdahl +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +"""supports walking the history as DAGs suitable for graphical output + +The most basic format we use is that of:: + + (id, type, data, [parentids]) + +The node and parent ids are arbitrary integers which identify a node in the +context of the graph returned. Type is a constant specifying the node type. +Data depends on type. +""" + +from mercurial.node import nullrev + +CHANGESET = 'C' + +def revisions(repo, start, stop): + """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples + + This generator function walks through the revision history from revision + start to revision stop (which must be less than or equal to start). It + returns a tuple for each node. The node and parent ids are arbitrary + integers which identify a node in the context of the graph returned. + """ + cur = start + while cur >= stop: + ctx = repo[cur] + parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev] + yield (cur, CHANGESET, ctx, sorted(parents)) + cur -= 1 + +def filerevs(repo, path, start, stop): + """file cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples + + This generator function walks through the revision history of a single + file from revision start down to revision stop. + """ + filerev = len(repo.file(path)) - 1 + while filerev >= 0: + fctx = repo.filectx(path, fileid=filerev) + parents = [f.linkrev() for f in fctx.parents() if f.path() == path] + rev = fctx.rev() + if rev <= start: + yield (rev, CHANGESET, fctx, sorted(parents)) + if rev <= stop: + break + filerev -= 1 + +def nodes(repo, nodes): + """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples + + This generator function walks the given nodes. It only returns parents + that are in nodes, too. + """ + include = set(nodes) + for node in nodes: + ctx = repo[node] + parents = [p.rev() for p in ctx.parents() if p.node() in include] + yield (ctx.rev(), CHANGESET, ctx, sorted(parents)) + +def colored(dag): + """annotates a DAG with colored edge information + + For each DAG node this function emits tuples:: + + (id, type, data, (col, color), [(col, nextcol, color)]) + + with the following new elements: + + - Tuple (col, color) with column and color index for the current node + - A list of tuples indicating the edges between the current node and its + parents. + """ + seen = [] + colors = {} + newcolor = 1 + for (cur, type, data, parents) in dag: + + # Compute seen and next + if cur not in seen: + seen.append(cur) # new head + colors[cur] = newcolor + newcolor += 1 + + col = seen.index(cur) + color = colors.pop(cur) + next = seen[:] + + # Add parents to next + addparents = [p for p in parents if p not in next] + next[col:col + 1] = addparents + + # Set colors for the parents + for i, p in enumerate(addparents): + if not i: + colors[p] = color + else: + colors[p] = newcolor + newcolor += 1 + + # Add edges to the graph + edges = [] + for ecol, eid in enumerate(seen): + if eid in next: + edges.append((ecol, next.index(eid), colors[eid])) + elif eid == cur: + for p in parents: + edges.append((ecol, next.index(p), colors[p])) + + # Yield and move on + yield (cur, type, data, (col, color), edges) + seen = next diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hbisect.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hbisect.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,145 @@ +# changelog bisection for mercurial +# +# Copyright 2007 Matt Mackall +# Copyright 2005, 2006 Benoit Boissinot +# +# Inspired by git bisect, extension skeleton taken from mq.py. +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os +from i18n import _ +from node import short, hex +import util + +def bisect(changelog, state): + """find the next node (if any) for testing during a bisect search. + returns a (nodes, number, good) tuple. + + 'nodes' is the final result of the bisect if 'number' is 0. + Otherwise 'number' indicates the remaining possible candidates for + the search and 'nodes' contains the next bisect target. + 'good' is True if bisect is searching for a first good changeset, False + if searching for a first bad one. + """ + + clparents = changelog.parentrevs + skip = set([changelog.rev(n) for n in state['skip']]) + + def buildancestors(bad, good): + # only the earliest bad revision matters + badrev = min([changelog.rev(n) for n in bad]) + goodrevs = [changelog.rev(n) for n in good] + # build ancestors array + ancestors = [[]] * (len(changelog) + 1) # an extra for [-1] + + # clear good revs from array + for node in goodrevs: + ancestors[node] = None + for rev in xrange(len(changelog), -1, -1): + if ancestors[rev] is None: + for prev in clparents(rev): + ancestors[prev] = None + + if ancestors[badrev] is None: + return badrev, None + return badrev, ancestors + + good = 0 + badrev, ancestors = buildancestors(state['bad'], state['good']) + if not ancestors: # looking for bad to good transition? + good = 1 + badrev, ancestors = buildancestors(state['good'], state['bad']) + bad = changelog.node(badrev) + if not ancestors: # now we're confused + raise util.Abort(_("Inconsistent state, %s:%s is good and bad") + % (badrev, short(bad))) + + # build children dict + children = {} + visit = [badrev] + candidates = [] + while visit: + rev = visit.pop(0) + if ancestors[rev] == []: + candidates.append(rev) + for prev in clparents(rev): + if prev != -1: + if prev in children: + children[prev].append(rev) + else: + children[prev] = [rev] + visit.append(prev) + + candidates.sort() + # have we narrowed it down to one entry? + # or have all other possible candidates besides 'bad' have been skipped? + tot = len(candidates) + unskipped = [c for c in candidates if (c not in skip) and (c != badrev)] + if tot == 1 or not unskipped: + return ([changelog.node(rev) for rev in candidates], 0, good) + perfect = tot // 2 + + # find the best node to test + best_rev = None + best_len = -1 + poison = set() + for rev in candidates: + if rev in poison: + # poison children + poison.update(children.get(rev, [])) + continue + + a = ancestors[rev] or [rev] + ancestors[rev] = None + + x = len(a) # number of ancestors + y = tot - x # number of non-ancestors + value = min(x, y) # how good is this test? + if value > best_len and rev not in skip: + best_len = value + best_rev = rev + if value == perfect: # found a perfect candidate? quit early + break + + if y < perfect and rev not in skip: # all downhill from here? + # poison children + poison.update(children.get(rev, [])) + continue + + for c in children.get(rev, []): + if ancestors[c]: + ancestors[c] = list(set(ancestors[c] + a)) + else: + ancestors[c] = a + [c] + + assert best_rev is not None + best_node = changelog.node(best_rev) + + return ([best_node], tot, good) + + +def load_state(repo): + state = {'good': [], 'bad': [], 'skip': []} + if os.path.exists(repo.join("bisect.state")): + for l in repo.opener("bisect.state"): + kind, node = l[:-1].split() + node = repo.lookup(node) + if kind not in state: + raise util.Abort(_("unknown bisect kind %s") % kind) + state[kind].append(node) + return state + + +def save_state(repo, state): + f = repo.opener("bisect.state", "w", atomictemp=True) + wlock = repo.wlock() + try: + for kind in state: + for node in state[kind]: + f.write("%s %s\n" % (kind, hex(node))) + f.rename() + finally: + wlock.release() + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/help.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/help.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,511 @@ +# help.py - help data for mercurial +# +# Copyright 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import extensions, util + + +def moduledoc(file): + '''return the top-level python documentation for the given file + + Loosely inspired by pydoc.source_synopsis(), but rewritten to handle \''' + as well as """ and to return the whole text instead of just the synopsis''' + result = [] + + line = file.readline() + while line[:1] == '#' or not line.strip(): + line = file.readline() + if not line: break + + start = line[:3] + if start == '"""' or start == "'''": + line = line[3:] + while line: + if line.rstrip().endswith(start): + line = line.split(start)[0] + if line: + result.append(line) + break + elif not line: + return None # unmatched delimiter + result.append(line) + line = file.readline() + else: + return None + + return ''.join(result) + +def listexts(header, exts, maxlength): + '''return a text listing of the given extensions''' + if not exts: + return '' + result = '\n%s\n\n' % header + for name, desc in sorted(exts.iteritems()): + desc = util.wrap(desc, maxlength + 4) + result += ' %s %s\n' % (name.ljust(maxlength), desc) + return result + +def extshelp(): + doc = _(r''' + Mercurial has the ability to add new features through the use of + extensions. Extensions may add new commands, add options to + existing commands, change the default behavior of commands, or + implement hooks. + + Extensions are not loaded by default for a variety of reasons: + they can increase startup overhead; they may be meant for + advanced usage only; they may provide potentially dangerous + abilities (such as letting you destroy or modify history); they + might not be ready for prime time; or they may alter some + usual behaviors of stock Mercurial. It is thus up to the user to + activate extensions as needed. + + To enable the "foo" extension, either shipped with Mercurial + or in the Python search path, create an entry for it in your + hgrc, like this: + + [extensions] + foo = + + You may also specify the full path to an extension: + + [extensions] + myfeature = ~/.hgext/myfeature.py + + To explicitly disable an extension enabled in an hgrc of broader + scope, prepend its path with !: + + [extensions] + # disabling extension bar residing in /path/to/extension/bar.py + hgext.bar = !/path/to/extension/bar.py + # ditto, but no path was supplied for extension baz + hgext.baz = ! + ''') + + exts, maxlength = extensions.enabled() + doc += listexts(_('enabled extensions:'), exts, maxlength) + + exts, maxlength = extensions.disabled() + doc += listexts(_('disabled extensions:'), exts, maxlength) + + return doc + +helptable = ( + (["dates"], _("Date Formats"), + _(r''' + Some commands allow the user to specify a date, e.g.: + * backout, commit, import, tag: Specify the commit date. + * log, revert, update: Select revision(s) by date. + + Many date formats are valid. Here are some examples: + + "Wed Dec 6 13:18:29 2006" (local timezone assumed) + "Dec 6 13:18 -0600" (year assumed, time offset provided) + "Dec 6 13:18 UTC" (UTC and GMT are aliases for +0000) + "Dec 6" (midnight) + "13:18" (today assumed) + "3:39" (3:39AM assumed) + "3:39pm" (15:39) + "2006-12-06 13:18:29" (ISO 8601 format) + "2006-12-6 13:18" + "2006-12-6" + "12-6" + "12/6" + "12/6/6" (Dec 6 2006) + + Lastly, there is Mercurial's internal format: + + "1165432709 0" (Wed Dec 6 13:18:29 2006 UTC) + + This is the internal representation format for dates. unixtime is + the number of seconds since the epoch (1970-01-01 00:00 UTC). + offset is the offset of the local timezone, in seconds west of UTC + (negative if the timezone is east of UTC). + + The log command also accepts date ranges: + + "<{datetime}" - at or before a given date/time + ">{datetime}" - on or after a given date/time + "{datetime} to {datetime}" - a date range, inclusive + "-{days}" - within a given number of days of today + ''')), + + (["patterns"], _("File Name Patterns"), + _(r''' + Mercurial accepts several notations for identifying one or more + files at a time. + + By default, Mercurial treats filenames as shell-style extended + glob patterns. + + Alternate pattern notations must be specified explicitly. + + To use a plain path name without any pattern matching, start it + with "path:". These path names must completely match starting at + the current repository root. + + To use an extended glob, start a name with "glob:". Globs are + rooted at the current directory; a glob such as "*.c" will only + match files in the current directory ending with ".c". + + The supported glob syntax extensions are "**" to match any string + across path separators and "{a,b}" to mean "a or b". + + To use a Perl/Python regular expression, start a name with "re:". + Regexp pattern matching is anchored at the root of the repository. + + Plain examples: + + path:foo/bar a name bar in a directory named foo in the root of + the repository + path:path:name a file or directory named "path:name" + + Glob examples: + + glob:*.c any name ending in ".c" in the current directory + *.c any name ending in ".c" in the current directory + **.c any name ending in ".c" in any subdirectory of the + current directory including itself. + foo/*.c any name ending in ".c" in the directory foo + foo/**.c any name ending in ".c" in any subdirectory of foo + including itself. + + Regexp examples: + + re:.*\.c$ any name ending in ".c", anywhere in the repository + + ''')), + + (['environment', 'env'], _('Environment Variables'), + _(r''' +HG:: + Path to the 'hg' executable, automatically passed when running + hooks, extensions or external tools. If unset or empty, this is + the hg executable's name if it's frozen, or an executable named + 'hg' (with %PATHEXT% [defaulting to COM/EXE/BAT/CMD] extensions on + Windows) is searched. + +HGEDITOR:: + This is the name of the editor to run when committing. See EDITOR. + + (deprecated, use .hgrc) + +HGENCODING:: + This overrides the default locale setting detected by Mercurial. + This setting is used to convert data including usernames, + changeset descriptions, tag names, and branches. This setting can + be overridden with the --encoding command-line option. + +HGENCODINGMODE:: + This sets Mercurial's behavior for handling unknown characters + while transcoding user input. The default is "strict", which + causes Mercurial to abort if it can't map a character. Other + settings include "replace", which replaces unknown characters, and + "ignore", which drops them. This setting can be overridden with + the --encodingmode command-line option. + +HGMERGE:: + An executable to use for resolving merge conflicts. The program + will be executed with three arguments: local file, remote file, + ancestor file. + + (deprecated, use .hgrc) + +HGRCPATH:: + A list of files or directories to search for hgrc files. Item + separator is ":" on Unix, ";" on Windows. If HGRCPATH is not set, + platform default search path is used. If empty, only the .hg/hgrc + from the current repository is read. + + For each element in HGRCPATH: + * if it's a directory, all files ending with .rc are added + * otherwise, the file itself will be added + +HGUSER:: + This is the string used as the author of a commit. If not set, + available values will be considered in this order: + + * HGUSER (deprecated) + * hgrc files from the HGRCPATH + * EMAIL + * interactive prompt + * LOGNAME (with '@hostname' appended) + + (deprecated, use .hgrc) + +EMAIL:: + May be used as the author of a commit; see HGUSER. + +LOGNAME:: + May be used as the author of a commit; see HGUSER. + +VISUAL:: + This is the name of the editor to use when committing. See EDITOR. + +EDITOR:: + Sometimes Mercurial needs to open a text file in an editor for a + user to modify, for example when writing commit messages. The + editor it uses is determined by looking at the environment + variables HGEDITOR, VISUAL and EDITOR, in that order. The first + non-empty one is chosen. If all of them are empty, the editor + defaults to 'vi'. + +PYTHONPATH:: + This is used by Python to find imported modules and may need to be + set appropriately if this Mercurial is not installed system-wide. + ''')), + + (['revs', 'revisions'], _('Specifying Single Revisions'), + _(r''' + Mercurial supports several ways to specify individual revisions. + + A plain integer is treated as a revision number. Negative integers + are treated as topological offsets from the tip, with -1 denoting + the tip. As such, negative numbers are only useful if you've + memorized your local tree numbers and want to save typing a single + digit. This editor suggests copy and paste. + + A 40-digit hexadecimal string is treated as a unique revision + identifier. + + A hexadecimal string less than 40 characters long is treated as a + unique revision identifier, and referred to as a short-form + identifier. A short-form identifier is only valid if it is the + prefix of exactly one full-length identifier. + + Any other string is treated as a tag name, which is a symbolic + name associated with a revision identifier. Tag names may not + contain the ":" character. + + The reserved name "tip" is a special tag that always identifies + the most recent revision. + + The reserved name "null" indicates the null revision. This is the + revision of an empty repository, and the parent of revision 0. + + The reserved name "." indicates the working directory parent. If + no working directory is checked out, it is equivalent to null. If + an uncommitted merge is in progress, "." is the revision of the + first parent. + ''')), + + (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'), + _(r''' + When Mercurial accepts more than one revision, they may be + specified individually, or provided as a topologically continuous + range, separated by the ":" character. + + The syntax of range notation is [BEGIN]:[END], where BEGIN and END + are revision identifiers. Both BEGIN and END are optional. If + BEGIN is not specified, it defaults to revision number 0. If END + is not specified, it defaults to the tip. The range ":" thus means + "all revisions". + + If BEGIN is greater than END, revisions are treated in reverse + order. + + A range acts as a closed interval. This means that a range of 3:5 + gives 3, 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6. + ''')), + + (['diffs'], _('Diff Formats'), + _(r''' + Mercurial's default format for showing changes between two + versions of a file is compatible with the unified format of GNU + diff, which can be used by GNU patch and many other standard + tools. + + While this standard format is often enough, it does not encode the + following information: + + - executable status and other permission bits + - copy or rename information + - changes in binary files + - creation or deletion of empty files + + Mercurial also supports the extended diff format from the git VCS + which addresses these limitations. The git diff format is not + produced by default because a few widespread tools still do not + understand this format. + + This means that when generating diffs from a Mercurial repository + (e.g. with "hg export"), you should be careful about things like + file copies and renames or other things mentioned above, because + when applying a standard diff to a different repository, this + extra information is lost. Mercurial's internal operations (like + push and pull) are not affected by this, because they use an + internal binary format for communicating changes. + + To make Mercurial produce the git extended diff format, use the + --git option available for many commands, or set 'git = True' in + the [diff] section of your hgrc. You do not need to set this + option when importing diffs in this format or using them in the mq + extension. + ''')), + (['templating'], _('Template Usage'), + _(r''' + Mercurial allows you to customize output of commands through + templates. You can either pass in a template from the command + line, via the --template option, or select an existing + template-style (--style). + + You can customize output for any "log-like" command: log, + outgoing, incoming, tip, parents, heads and glog. + + Three styles are packaged with Mercurial: default (the style used + when no explicit preference is passed), compact and changelog. + Usage: + + $ hg log -r1 --style changelog + + A template is a piece of text, with markup to invoke variable + expansion: + + $ hg log -r1 --template "{node}\n" + b56ce7b07c52de7d5fd79fb89701ea538af65746 + + Strings in curly braces are called keywords. The availability of + keywords depends on the exact context of the templater. These + keywords are usually available for templating a log-like command: + + - author: String. The unmodified author of the changeset. + - branches: String. The name of the branch on which the changeset + was committed. Will be empty if the branch name was default. + - date: Date information. The date when the changeset was committed. + - desc: String. The text of the changeset description. + - diffstat: String. Statistics of changes with the following + format: "modified files: +added/-removed lines" + - files: List of strings. All files modified, added, or removed by + this changeset. + - file_adds: List of strings. Files added by this changeset. + - file_mods: List of strings. Files modified by this changeset. + - file_dels: List of strings. Files removed by this changeset. + - node: String. The changeset identification hash, as a + 40-character hexadecimal string. + - parents: List of strings. The parents of the changeset. + - rev: Integer. The repository-local changeset revision number. + - tags: List of strings. Any tags associated with the changeset. + + The "date" keyword does not produce human-readable output. If you + want to use a date in your output, you can use a filter to process + it. Filters are functions which return a string based on the input + variable. You can also use a chain of filters to get the desired + output: + + $ hg tip --template "{date|isodate}\n" + 2008-08-21 18:22 +0000 + + List of filters: + + - addbreaks: Any text. Add an XHTML "
" tag before the end of + every line except the last. + - age: Date. Returns a human-readable date/time difference between + the given date/time and the current date/time. + - basename: Any text. Treats the text as a path, and returns the + last component of the path after splitting by the path + separator (ignoring trailing separators). For example, + "foo/bar/baz" becomes "baz" and "foo/bar//" becomes "bar". + - stripdir: Treat the text as path and strip a directory level, if + possible. For example, "foo" and "foo/bar" becomes "foo". + - date: Date. Returns a date in a Unix date format, including + the timezone: "Mon Sep 04 15:13:13 2006 0700". + - domain: Any text. Finds the first string that looks like an + email address, and extracts just the domain component. + Example: 'User ' becomes 'example.com'. + - email: Any text. Extracts the first string that looks like an + email address. Example: 'User ' becomes + 'user@example.com'. + - escape: Any text. Replaces the special XML/XHTML characters "&", + "<" and ">" with XML entities. + - fill68: Any text. Wraps the text to fit in 68 columns. + - fill76: Any text. Wraps the text to fit in 76 columns. + - firstline: Any text. Returns the first line of text. + - nonempty: Any text. Returns '(none)' if the string is empty. + - hgdate: Date. Returns the date as a pair of numbers: + "1157407993 25200" (Unix timestamp, timezone offset). + - isodate: Date. Returns the date in ISO 8601 format. + - localdate: Date. Converts a date to local date. + - obfuscate: Any text. Returns the input text rendered as a + sequence of XML entities. + - person: Any text. Returns the text before an email address. + - rfc822date: Date. Returns a date using the same format used + in email headers. + - short: Changeset hash. Returns the short form of a changeset + hash, i.e. a 12-byte hexadecimal string. + - shortdate: Date. Returns a date like "2006-09-18". + - strip: Any text. Strips all leading and trailing whitespace. + - tabindent: Any text. Returns the text, with every line except + the first starting with a tab character. + - urlescape: Any text. Escapes all "special" characters. For + example, "foo bar" becomes "foo%20bar". + - user: Any text. Returns the user portion of an email address. + ''')), + + (['urls'], _('URL Paths'), + _(r''' + Valid URLs are of the form: + + local/filesystem/path[#revision] + file://local/filesystem/path[#revision] + http://[user[:pass]@]host[:port]/[path][#revision] + https://[user[:pass]@]host[:port]/[path][#revision] + ssh://[user[:pass]@]host[:port]/[path][#revision] + + Paths in the local filesystem can either point to Mercurial + repositories or to bundle files (as created by 'hg bundle' or + 'hg incoming --bundle'). + + An optional identifier after # indicates a particular branch, tag, + or changeset to use from the remote repository. See also 'hg help + revisions'. + + Some features, such as pushing to http:// and https:// URLs are + only possible if the feature is explicitly enabled on the remote + Mercurial server. + + Some notes about using SSH with Mercurial: + - SSH requires an accessible shell account on the destination + machine and a copy of hg in the remote path or specified with as + remotecmd. + - path is relative to the remote user's home directory by default. + Use an extra slash at the start of a path to specify an absolute path: + ssh://example.com//tmp/repository + - Mercurial doesn't use its own compression via SSH; the right + thing to do is to configure it in your ~/.ssh/config, e.g.: + Host *.mylocalnetwork.example.com + Compression no + Host * + Compression yes + Alternatively specify "ssh -C" as your ssh command in your hgrc + or with the --ssh command line option. + + These URLs can all be stored in your hgrc with path aliases under + the [paths] section like so: + [paths] + alias1 = URL1 + alias2 = URL2 + ... + + You can then use the alias for any command that uses a URL (for + example 'hg pull alias1' would pull from the 'alias1' path). + + Two path aliases are special because they are used as defaults + when you do not provide the URL to a command: + + default: + When you create a repository with hg clone, the clone command + saves the location of the source repository as the new + repository's 'default' path. This is then used when you omit + path from push- and pull-like commands (including incoming and + outgoing). + + default-push: + The push command will look for a path named 'default-push', and + prefer it over 'default' if both are defined. + ''')), + (["extensions"], _("Using additional features"), extshelp), +) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hg.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hg.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,363 @@ +# hg.py - repository classes for mercurial +# +# Copyright 2005-2007 Matt Mackall +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +from lock import release +import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo +import lock, util, extensions, error +import merge as _merge +import verify as _verify +import errno, os, shutil + +def _local(path): + return (os.path.isfile(util.drop_scheme('file', path)) and + bundlerepo or localrepo) + +def parseurl(url, revs=[]): + '''parse url#branch, returning url, branch + revs''' + + if '#' not in url: + return url, (revs or None), revs and revs[-1] or None + + url, branch = url.split('#', 1) + checkout = revs and revs[-1] or branch + return url, (revs or []) + [branch], checkout + +schemes = { + 'bundle': bundlerepo, + 'file': _local, + 'http': httprepo, + 'https': httprepo, + 'ssh': sshrepo, + 'static-http': statichttprepo, +} + +def _lookup(path): + scheme = 'file' + if path: + c = path.find(':') + if c > 0: + scheme = path[:c] + thing = schemes.get(scheme) or schemes['file'] + try: + return thing(path) + except TypeError: + return thing + +def islocal(repo): + '''return true if repo or path is local''' + if isinstance(repo, str): + try: + return _lookup(repo).islocal(repo) + except AttributeError: + return False + return repo.local() + +def repository(ui, path='', create=False): + """return a repository object for the specified path""" + repo = _lookup(path).instance(ui, path, create) + ui = getattr(repo, "ui", ui) + for name, module in extensions.extensions(): + hook = getattr(module, 'reposetup', None) + if hook: + hook(ui, repo) + return repo + +def defaultdest(source): + '''return default destination of clone if none is given''' + return os.path.basename(os.path.normpath(source)) + +def localpath(path): + if path.startswith('file://localhost/'): + return path[16:] + if path.startswith('file://'): + return path[7:] + if path.startswith('file:'): + return path[5:] + return path + +def share(ui, source, dest=None, update=True): + '''create a shared repository''' + + if not islocal(source): + raise util.Abort(_('can only share local repositories')) + + if not dest: + dest = os.path.basename(source) + + if isinstance(source, str): + origsource = ui.expandpath(source) + source, rev, checkout = parseurl(origsource, '') + srcrepo = repository(ui, source) + else: + srcrepo = source + origsource = source = srcrepo.url() + checkout = None + + sharedpath = srcrepo.sharedpath # if our source is already sharing + + root = os.path.realpath(dest) + roothg = os.path.join(root, '.hg') + + if os.path.exists(roothg): + raise util.Abort(_('destination already exists')) + + if not os.path.isdir(root): + os.mkdir(root) + os.mkdir(roothg) + + requirements = '' + try: + requirements = srcrepo.opener('requires').read() + except IOError, inst: + if inst.errno != errno.ENOENT: + raise + + requirements += 'shared\n' + file(os.path.join(roothg, 'requires'), 'w').write(requirements) + file(os.path.join(roothg, 'sharedpath'), 'w').write(sharedpath) + + default = srcrepo.ui.config('paths', 'default') + if default: + f = file(os.path.join(roothg, 'hgrc'), 'w') + f.write('[paths]\ndefault = %s\n' % default) + f.close() + + r = repository(ui, root) + + if update: + r.ui.status(_("updating working directory\n")) + if update is not True: + checkout = update + for test in (checkout, 'default', 'tip'): + try: + uprev = r.lookup(test) + break + except: + continue + _update(r, uprev) + +def clone(ui, source, dest=None, pull=False, rev=None, update=True, + stream=False): + """Make a copy of an existing repository. + + Create a copy of an existing repository in a new directory. The + source and destination are URLs, as passed to the repository + function. Returns a pair of repository objects, the source and + newly created destination. + + The location of the source is added to the new repository's + .hg/hgrc file, as the default to be used for future pulls and + pushes. + + If an exception is raised, the partly cloned/updated destination + repository will be deleted. + + Arguments: + + source: repository object or URL + + dest: URL of destination repository to create (defaults to base + name of source repository) + + pull: always pull from source repository, even in local case + + stream: stream raw data uncompressed from repository (fast over + LAN, slow over WAN) + + rev: revision to clone up to (implies pull=True) + + update: update working directory after clone completes, if + destination is local repository (True means update to default rev, + anything else is treated as a revision) + """ + + if isinstance(source, str): + origsource = ui.expandpath(source) + source, rev, checkout = parseurl(origsource, rev) + src_repo = repository(ui, source) + else: + src_repo = source + origsource = source = src_repo.url() + checkout = rev and rev[-1] or None + + if dest is None: + dest = defaultdest(source) + ui.status(_("destination directory: %s\n") % dest) + + dest = localpath(dest) + source = localpath(source) + + if os.path.exists(dest): + if not os.path.isdir(dest): + raise util.Abort(_("destination '%s' already exists") % dest) + elif os.listdir(dest): + raise util.Abort(_("destination '%s' is not empty") % dest) + + class DirCleanup(object): + def __init__(self, dir_): + self.rmtree = shutil.rmtree + self.dir_ = dir_ + def close(self): + self.dir_ = None + def cleanup(self): + if self.dir_: + self.rmtree(self.dir_, True) + + src_lock = dest_lock = dir_cleanup = None + try: + if islocal(dest): + dir_cleanup = DirCleanup(dest) + + abspath = origsource + copy = False + if src_repo.cancopy() and islocal(dest): + abspath = os.path.abspath(util.drop_scheme('file', origsource)) + copy = not pull and not rev + + if copy: + try: + # we use a lock here because if we race with commit, we + # can end up with extra data in the cloned revlogs that's + # not pointed to by changesets, thus causing verify to + # fail + src_lock = src_repo.lock(wait=False) + except error.LockError: + copy = False + + if copy: + src_repo.hook('preoutgoing', throw=True, source='clone') + hgdir = os.path.realpath(os.path.join(dest, ".hg")) + if not os.path.exists(dest): + os.mkdir(dest) + else: + # only clean up directories we create ourselves + dir_cleanup.dir_ = hgdir + try: + dest_path = hgdir + os.mkdir(dest_path) + except OSError, inst: + if inst.errno == errno.EEXIST: + dir_cleanup.close() + raise util.Abort(_("destination '%s' already exists") + % dest) + raise + + for f in src_repo.store.copylist(): + src = os.path.join(src_repo.path, f) + dst = os.path.join(dest_path, f) + dstbase = os.path.dirname(dst) + if dstbase and not os.path.exists(dstbase): + os.mkdir(dstbase) + if os.path.exists(src): + if dst.endswith('data'): + # lock to avoid premature writing to the target + dest_lock = lock.lock(os.path.join(dstbase, "lock")) + util.copyfiles(src, dst) + + # we need to re-init the repo after manually copying the data + # into it + dest_repo = repository(ui, dest) + src_repo.hook('outgoing', source='clone', node='0'*40) + else: + try: + dest_repo = repository(ui, dest, create=True) + except OSError, inst: + if inst.errno == errno.EEXIST: + dir_cleanup.close() + raise util.Abort(_("destination '%s' already exists") + % dest) + raise + + revs = None + if rev: + if 'lookup' not in src_repo.capabilities: + raise util.Abort(_("src repository does not support revision " + "lookup and so doesn't support clone by " + "revision")) + revs = [src_repo.lookup(r) for r in rev] + checkout = revs[0] + if dest_repo.local(): + dest_repo.clone(src_repo, heads=revs, stream=stream) + elif src_repo.local(): + src_repo.push(dest_repo, revs=revs) + else: + raise util.Abort(_("clone from remote to remote not supported")) + + if dir_cleanup: + dir_cleanup.close() + + if dest_repo.local(): + fp = dest_repo.opener("hgrc", "w", text=True) + fp.write("[paths]\n") + fp.write("default = %s\n" % abspath) + fp.close() + + dest_repo.ui.setconfig('paths', 'default', abspath) + + if update: + dest_repo.ui.status(_("updating working directory\n")) + if update is not True: + checkout = update + for test in (checkout, 'default', 'tip'): + try: + uprev = dest_repo.lookup(test) + break + except: + continue + _update(dest_repo, uprev) + + return src_repo, dest_repo + finally: + release(src_lock, dest_lock) + if dir_cleanup is not None: + dir_cleanup.cleanup() + +def _showstats(repo, stats): + stats = ((stats[0], _("updated")), + (stats[1], _("merged")), + (stats[2], _("removed")), + (stats[3], _("unresolved"))) + note = ", ".join([_("%d files %s") % s for s in stats]) + repo.ui.status("%s\n" % note) + +def update(repo, node): + """update the working directory to node, merging linear changes""" + stats = _merge.update(repo, node, False, False, None) + _showstats(repo, stats) + if stats[3]: + repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n")) + return stats[3] > 0 + +# naming conflict in clone() +_update = update + +def clean(repo, node, show_stats=True): + """forcibly switch the working directory to node, clobbering changes""" + stats = _merge.update(repo, node, False, True, None) + if show_stats: _showstats(repo, stats) + return stats[3] > 0 + +def merge(repo, node, force=None, remind=True): + """branch merge with node, resolving changes""" + stats = _merge.update(repo, node, True, force, False) + _showstats(repo, stats) + if stats[3]: + repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " + "or 'hg up --clean' to abandon\n")) + elif remind: + repo.ui.status(_("(branch merge, don't forget to commit)\n")) + return stats[3] > 0 + +def revert(repo, node, choose): + """revert changes to revision in node without updating dirstate""" + return _merge.update(repo, node, False, True, choose)[3] > 0 + +def verify(repo): + """verify the consistency of a repository""" + return _verify.verify(repo) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/__init__.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,16 @@ +# hgweb/__init__.py - web interface to a mercurial repository +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import hgweb_mod, hgwebdir_mod + +def hgweb(*args, **kwargs): + return hgweb_mod.hgweb(*args, **kwargs) + +def hgwebdir(*args, **kwargs): + return hgwebdir_mod.hgwebdir(*args, **kwargs) + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/common.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,105 @@ +# hgweb/common.py - Utility functions needed by hgweb_mod and hgwebdir_mod +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import errno, mimetypes, os + +HTTP_OK = 200 +HTTP_BAD_REQUEST = 400 +HTTP_UNAUTHORIZED = 401 +HTTP_FORBIDDEN = 403 +HTTP_NOT_FOUND = 404 +HTTP_METHOD_NOT_ALLOWED = 405 +HTTP_SERVER_ERROR = 500 + +class ErrorResponse(Exception): + def __init__(self, code, message=None, headers=[]): + Exception.__init__(self) + self.code = code + self.headers = headers + if message is not None: + self.message = message + else: + self.message = _statusmessage(code) + +def _statusmessage(code): + from BaseHTTPServer import BaseHTTPRequestHandler + responses = BaseHTTPRequestHandler.responses + return responses.get(code, ('Error', 'Unknown error'))[0] + +def statusmessage(code): + return '%d %s' % (code, _statusmessage(code)) + +def get_mtime(repo_path): + store_path = os.path.join(repo_path, ".hg") + if not os.path.isdir(os.path.join(store_path, "data")): + store_path = os.path.join(store_path, "store") + cl_path = os.path.join(store_path, "00changelog.i") + if os.path.exists(cl_path): + return os.stat(cl_path).st_mtime + else: + return os.stat(store_path).st_mtime + +def staticfile(directory, fname, req): + """return a file inside directory with guessed Content-Type header + + fname always uses '/' as directory separator and isn't allowed to + contain unusual path components. + Content-Type is guessed using the mimetypes module. + Return an empty string if fname is illegal or file not found. + + """ + parts = fname.split('/') + for part in parts: + if (part in ('', os.curdir, os.pardir) or + os.sep in part or os.altsep is not None and os.altsep in part): + return "" + fpath = os.path.join(*parts) + if isinstance(directory, str): + directory = [directory] + for d in directory: + path = os.path.join(d, fpath) + if os.path.exists(path): + break + try: + os.stat(path) + ct = mimetypes.guess_type(path)[0] or "text/plain" + req.respond(HTTP_OK, ct, length = os.path.getsize(path)) + return file(path, 'rb').read() + except TypeError: + raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename') + except OSError, err: + if err.errno == errno.ENOENT: + raise ErrorResponse(HTTP_NOT_FOUND) + else: + raise ErrorResponse(HTTP_SERVER_ERROR, err.strerror) + +def paritygen(stripecount, offset=0): + """count parity of horizontal stripes for easier reading""" + if stripecount and offset: + # account for offset, e.g. due to building the list in reverse + count = (stripecount + offset) % stripecount + parity = (stripecount + offset) / stripecount & 1 + else: + count = 0 + parity = 0 + while True: + yield parity + count += 1 + if stripecount and count >= stripecount: + parity = 1 - parity + count = 0 + +def get_contact(config): + """Return repo contact information or empty string. + + web.contact is the primary source, but if that is not set, try + ui.username or $EMAIL as a fallback to display something useful. + """ + return (config("web", "contact") or + config("ui", "username") or + os.environ.get("EMAIL") or "") diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/hgweb_mod.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/hgweb_mod.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,315 @@ +# hgweb/hgweb_mod.py - Web interface for a repository. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os +from upmana.mercurial import ui, hg, hook, error, encoding, templater +from common import get_mtime, ErrorResponse +from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR +from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED +from request import wsgirequest +import webcommands, protocol, webutil + +perms = { + 'changegroup': 'pull', + 'changegroupsubset': 'pull', + 'unbundle': 'push', + 'stream_out': 'pull', +} + +class hgweb(object): + def __init__(self, repo, name=None): + if isinstance(repo, str): + u = ui.ui() + u.setconfig('ui', 'report_untrusted', 'off') + u.setconfig('ui', 'interactive', 'off') + self.repo = hg.repository(u, repo) + else: + self.repo = repo + + hook.redirect(True) + self.mtime = -1 + self.reponame = name + self.archives = 'zip', 'gz', 'bz2' + self.stripecount = 1 + # a repo owner may set web.templates in .hg/hgrc to get any file + # readable by the user running the CGI script + self.templatepath = self.config('web', 'templates') + + # The CGI scripts are often run by a user different from the repo owner. + # Trust the settings from the .hg/hgrc files by default. + def config(self, section, name, default=None, untrusted=True): + return self.repo.ui.config(section, name, default, + untrusted=untrusted) + + def configbool(self, section, name, default=False, untrusted=True): + return self.repo.ui.configbool(section, name, default, + untrusted=untrusted) + + def configlist(self, section, name, default=None, untrusted=True): + return self.repo.ui.configlist(section, name, default, + untrusted=untrusted) + + def refresh(self): + mtime = get_mtime(self.repo.root) + if mtime != self.mtime: + self.mtime = mtime + self.repo = hg.repository(self.repo.ui, self.repo.root) + self.maxchanges = int(self.config("web", "maxchanges", 10)) + self.stripecount = int(self.config("web", "stripes", 1)) + self.maxshortchanges = int(self.config("web", "maxshortchanges", 60)) + self.maxfiles = int(self.config("web", "maxfiles", 10)) + self.allowpull = self.configbool("web", "allowpull", True) + encoding.encoding = self.config("web", "encoding", + encoding.encoding) + + def run(self): + if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): + raise RuntimeError("This function is only intended to be " + "called while running as a CGI script.") + import mercurial.hgweb.wsgicgi as wsgicgi + wsgicgi.launch(self) + + def __call__(self, env, respond): + req = wsgirequest(env, respond) + return self.run_wsgi(req) + + def run_wsgi(self, req): + + self.refresh() + + # work with CGI variables to create coherent structure + # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME + + req.url = req.env['SCRIPT_NAME'] + if not req.url.endswith('/'): + req.url += '/' + if 'REPO_NAME' in req.env: + req.url += req.env['REPO_NAME'] + '/' + + if 'PATH_INFO' in req.env: + parts = req.env['PATH_INFO'].strip('/').split('/') + repo_parts = req.env.get('REPO_NAME', '').split('/') + if parts[:len(repo_parts)] == repo_parts: + parts = parts[len(repo_parts):] + query = '/'.join(parts) + else: + query = req.env['QUERY_STRING'].split('&', 1)[0] + query = query.split(';', 1)[0] + + # process this if it's a protocol request + # protocol bits don't need to create any URLs + # and the clients always use the old URL structure + + cmd = req.form.get('cmd', [''])[0] + if cmd and cmd in protocol.__all__: + if query: + raise ErrorResponse(HTTP_NOT_FOUND) + try: + if cmd in perms: + try: + self.check_perm(req, perms[cmd]) + except ErrorResponse, inst: + if cmd == 'unbundle': + req.drain() + raise + method = getattr(protocol, cmd) + return method(self.repo, req) + except ErrorResponse, inst: + req.respond(inst, protocol.HGTYPE) + if not inst.message: + return [] + return '0\n%s\n' % inst.message, + + # translate user-visible url structure to internal structure + + args = query.split('/', 2) + if 'cmd' not in req.form and args and args[0]: + + cmd = args.pop(0) + style = cmd.rfind('-') + if style != -1: + req.form['style'] = [cmd[:style]] + cmd = cmd[style+1:] + + # avoid accepting e.g. style parameter as command + if hasattr(webcommands, cmd): + req.form['cmd'] = [cmd] + else: + cmd = '' + + if cmd == 'static': + req.form['file'] = ['/'.join(args)] + else: + if args and args[0]: + node = args.pop(0) + req.form['node'] = [node] + if args: + req.form['file'] = args + + if cmd == 'archive': + fn = req.form['node'][0] + for type_, spec in self.archive_specs.iteritems(): + ext = spec[2] + if fn.endswith(ext): + req.form['node'] = [fn[:-len(ext)]] + req.form['type'] = [type_] + + # process the web interface request + + try: + tmpl = self.templater(req) + ctype = tmpl('mimetype', encoding=encoding.encoding) + ctype = templater.stringify(ctype) + + # check read permissions non-static content + if cmd != 'static': + self.check_perm(req, None) + + if cmd == '': + req.form['cmd'] = [tmpl.cache['default']] + cmd = req.form['cmd'][0] + + if cmd not in webcommands.__all__: + msg = 'no such method: %s' % cmd + raise ErrorResponse(HTTP_BAD_REQUEST, msg) + elif cmd == 'file' and 'raw' in req.form.get('style', []): + self.ctype = ctype + content = webcommands.rawfile(self, req, tmpl) + else: + content = getattr(webcommands, cmd)(self, req, tmpl) + req.respond(HTTP_OK, ctype) + + return content + + except error.LookupError, err: + req.respond(HTTP_NOT_FOUND, ctype) + msg = str(err) + if 'manifest' not in msg: + msg = 'revision not found: %s' % err.name + return tmpl('error', error=msg) + except (error.RepoError, error.RevlogError), inst: + req.respond(HTTP_SERVER_ERROR, ctype) + return tmpl('error', error=str(inst)) + except ErrorResponse, inst: + req.respond(inst, ctype) + return tmpl('error', error=inst.message) + + def templater(self, req): + + # determine scheme, port and server name + # this is needed to create absolute urls + + proto = req.env.get('wsgi.url_scheme') + if proto == 'https': + proto = 'https' + default_port = "443" + else: + proto = 'http' + default_port = "80" + + port = req.env["SERVER_PORT"] + port = port != default_port and (":" + port) or "" + urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port) + staticurl = self.config("web", "staticurl") or req.url + 'static/' + if not staticurl.endswith('/'): + staticurl += '/' + + # some functions for the templater + + def header(**map): + yield tmpl('header', encoding=encoding.encoding, **map) + + def footer(**map): + yield tmpl("footer", **map) + + def motd(**map): + yield self.config("web", "motd", "") + + # figure out which style to use + + vars = {} + style = self.config("web", "style", "paper") + if 'style' in req.form: + style = req.form['style'][0] + vars['style'] = style + + start = req.url[-1] == '?' and '&' or '?' + sessionvars = webutil.sessionvars(vars, start) + mapfile = templater.stylemap(style, self.templatepath) + + if not self.reponame: + self.reponame = (self.config("web", "name") + or req.env.get('REPO_NAME') + or req.url.strip('/') or self.repo.root) + + # create the templater + + tmpl = templater.templater(mapfile, + defaults={"url": req.url, + "staticurl": staticurl, + "urlbase": urlbase, + "repo": self.reponame, + "header": header, + "footer": footer, + "motd": motd, + "sessionvars": sessionvars + }) + return tmpl + + def archivelist(self, nodeid): + allowed = self.configlist("web", "allow_archive") + for i, spec in self.archive_specs.iteritems(): + if i in allowed or self.configbool("web", "allow" + i): + yield {"type" : i, "extension" : spec[2], "node" : nodeid} + + archive_specs = { + 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), + 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), + 'zip': ('application/zip', 'zip', '.zip', None), + } + + def check_perm(self, req, op): + '''Check permission for operation based on request data (including + authentication info). Return if op allowed, else raise an ErrorResponse + exception.''' + + user = req.env.get('REMOTE_USER') + + deny_read = self.configlist('web', 'deny_read') + if deny_read and (not user or deny_read == ['*'] or user in deny_read): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') + + allow_read = self.configlist('web', 'allow_read') + result = (not allow_read) or (allow_read == ['*']) + if not (result or user in allow_read): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized') + + if op == 'pull' and not self.allowpull: + raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized') + elif op == 'pull' or op is None: # op is None for interface requests + return + + # enforce that you can only push using POST requests + if req.env['REQUEST_METHOD'] != 'POST': + msg = 'push requires POST request' + raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) + + # require ssl by default for pushing, auth info cannot be sniffed + # and replayed + scheme = req.env.get('wsgi.url_scheme') + if self.configbool('web', 'push_ssl', True) and scheme != 'https': + raise ErrorResponse(HTTP_OK, 'ssl required') + + deny = self.configlist('web', 'deny_push') + if deny and (not user or deny == ['*'] or user in deny): + raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') + + allow = self.configlist('web', 'allow_push') + result = allow and (allow == ['*'] or user in allow) + if not result: + raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/hgwebdir_mod.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/hgwebdir_mod.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,333 @@ +# hgweb/hgwebdir_mod.py - Web interface for a directory of repositories. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os, re, time +from upmana.mercurial.i18n import _ +from upmana.mercurial import ui, hg, util, templater +from upmana.mercurial import error, encoding +from common import ErrorResponse, get_mtime, staticfile, paritygen,\ + get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR +from hgweb_mod import hgweb +from request import wsgirequest +import webutil + +def cleannames(items): + return [(util.pconvert(name).strip('/'), path) for name, path in items] + +def findrepos(paths): + repos = {} + for prefix, root in cleannames(paths): + roothead, roottail = os.path.split(root) + # "foo = /bar/*" makes every subrepo of /bar/ to be + # mounted as foo/subrepo + # and "foo = /bar/**" also recurses into the subdirectories, + # remember to use it without working dir. + try: + recurse = {'*': False, '**': True}[roottail] + except KeyError: + repos[prefix] = root + continue + roothead = os.path.normpath(roothead) + for path in util.walkrepos(roothead, followsym=True, recurse=recurse): + path = os.path.normpath(path) + name = util.pconvert(path[len(roothead):]).strip('/') + if prefix: + name = prefix + '/' + name + repos[name] = path + return repos.items() + +class hgwebdir(object): + refreshinterval = 20 + + def __init__(self, conf, baseui=None): + self.conf = conf + self.baseui = baseui + self.lastrefresh = 0 + self.refresh() + + def refresh(self): + if self.lastrefresh + self.refreshinterval > time.time(): + return + + if self.baseui: + self.ui = self.baseui.copy() + else: + self.ui = ui.ui() + self.ui.setconfig('ui', 'report_untrusted', 'off') + self.ui.setconfig('ui', 'interactive', 'off') + + if not isinstance(self.conf, (dict, list, tuple)): + map = {'paths': 'hgweb-paths'} + self.ui.readconfig(self.conf, remap=map, trust=True) + paths = self.ui.configitems('hgweb-paths') + elif isinstance(self.conf, (list, tuple)): + paths = self.conf + elif isinstance(self.conf, dict): + paths = self.conf.items() + + encoding.encoding = self.ui.config('web', 'encoding', + encoding.encoding) + self.motd = self.ui.config('web', 'motd') + self.style = self.ui.config('web', 'style', 'paper') + self.stripecount = self.ui.config('web', 'stripes', 1) + if self.stripecount: + self.stripecount = int(self.stripecount) + self._baseurl = self.ui.config('web', 'baseurl') + + self.repos = findrepos(paths) + for prefix, root in self.ui.configitems('collections'): + prefix = util.pconvert(prefix) + for path in util.walkrepos(root, followsym=True): + repo = os.path.normpath(path) + name = util.pconvert(repo) + if name.startswith(prefix): + name = name[len(prefix):] + self.repos.append((name.lstrip('/'), repo)) + + self.repos.sort() + self.lastrefresh = time.time() + + def run(self): + if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): + raise RuntimeError("This function is only intended to be " + "called while running as a CGI script.") + import mercurial.hgweb.wsgicgi as wsgicgi + wsgicgi.launch(self) + + def __call__(self, env, respond): + req = wsgirequest(env, respond) + return self.run_wsgi(req) + + def read_allowed(self, ui, req): + """Check allow_read and deny_read config options of a repo's ui object + to determine user permissions. By default, with neither option set (or + both empty), allow all users to read the repo. There are two ways a + user can be denied read access: (1) deny_read is not empty, and the + user is unauthenticated or deny_read contains user (or *), and (2) + allow_read is not empty and the user is not in allow_read. Return True + if user is allowed to read the repo, else return False.""" + + user = req.env.get('REMOTE_USER') + + deny_read = ui.configlist('web', 'deny_read', untrusted=True) + if deny_read and (not user or deny_read == ['*'] or user in deny_read): + return False + + allow_read = ui.configlist('web', 'allow_read', untrusted=True) + # by default, allow reading if no allow_read option has been set + if (not allow_read) or (allow_read == ['*']) or (user in allow_read): + return True + + return False + + def run_wsgi(self, req): + try: + try: + self.refresh() + + virtual = req.env.get("PATH_INFO", "").strip('/') + tmpl = self.templater(req) + ctype = tmpl('mimetype', encoding=encoding.encoding) + ctype = templater.stringify(ctype) + + # a static file + if virtual.startswith('static/') or 'static' in req.form: + if virtual.startswith('static/'): + fname = virtual[7:] + else: + fname = req.form['static'][0] + static = templater.templatepath('static') + return (staticfile(static, fname, req),) + + # top-level index + elif not virtual: + req.respond(HTTP_OK, ctype) + return self.makeindex(req, tmpl) + + # nested indexes and hgwebs + + repos = dict(self.repos) + while virtual: + real = repos.get(virtual) + if real: + req.env['REPO_NAME'] = virtual + try: + repo = hg.repository(self.ui, real) + return hgweb(repo).run_wsgi(req) + except IOError, inst: + msg = inst.strerror + raise ErrorResponse(HTTP_SERVER_ERROR, msg) + except error.RepoError, inst: + raise ErrorResponse(HTTP_SERVER_ERROR, str(inst)) + + # browse subdirectories + subdir = virtual + '/' + if [r for r in repos if r.startswith(subdir)]: + req.respond(HTTP_OK, ctype) + return self.makeindex(req, tmpl, subdir) + + up = virtual.rfind('/') + if up < 0: + break + virtual = virtual[:up] + + # prefixes not found + req.respond(HTTP_NOT_FOUND, ctype) + return tmpl("notfound", repo=virtual) + + except ErrorResponse, err: + req.respond(err, ctype) + return tmpl('error', error=err.message or '') + finally: + tmpl = None + + def makeindex(self, req, tmpl, subdir=""): + + def archivelist(ui, nodeid, url): + allowed = ui.configlist("web", "allow_archive", untrusted=True) + for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]: + if i[0] in allowed or ui.configbool("web", "allow" + i[0], + untrusted=True): + yield {"type" : i[0], "extension": i[1], + "node": nodeid, "url": url} + + sortdefault = 'name', False + def entries(sortcolumn="", descending=False, subdir="", **map): + rows = [] + parity = paritygen(self.stripecount) + for name, path in self.repos: + if not name.startswith(subdir): + continue + name = name[len(subdir):] + + u = self.ui.copy() + try: + u.readconfig(os.path.join(path, '.hg', 'hgrc')) + except Exception, e: + u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e)) + continue + def get(section, name, default=None): + return u.config(section, name, default, untrusted=True) + + if u.configbool("web", "hidden", untrusted=True): + continue + + if not self.read_allowed(u, req): + continue + + parts = [name] + if 'PATH_INFO' in req.env: + parts.insert(0, req.env['PATH_INFO'].rstrip('/')) + if req.env['SCRIPT_NAME']: + parts.insert(0, req.env['SCRIPT_NAME']) + m = re.match('((?:https?://)?)(.*)', '/'.join(parts)) + # squish repeated slashes out of the path component + url = m.group(1) + re.sub('/+', '/', m.group(2)) + '/' + + # update time with local timezone + try: + d = (get_mtime(path), util.makedate()[1]) + except OSError: + continue + + contact = get_contact(get) + description = get("web", "description", "") + name = get("web", "name", name) + row = dict(contact=contact or "unknown", + contact_sort=contact.upper() or "unknown", + name=name, + name_sort=name, + url=url, + description=description or "unknown", + description_sort=description.upper() or "unknown", + lastchange=d, + lastchange_sort=d[1]-d[0], + archives=archivelist(u, "tip", url)) + if (not sortcolumn or (sortcolumn, descending) == sortdefault): + # fast path for unsorted output + row['parity'] = parity.next() + yield row + else: + rows.append((row["%s_sort" % sortcolumn], row)) + if rows: + rows.sort() + if descending: + rows.reverse() + for key, row in rows: + row['parity'] = parity.next() + yield row + + self.refresh() + sortable = ["name", "description", "contact", "lastchange"] + sortcolumn, descending = sortdefault + if 'sort' in req.form: + sortcolumn = req.form['sort'][0] + descending = sortcolumn.startswith('-') + if descending: + sortcolumn = sortcolumn[1:] + if sortcolumn not in sortable: + sortcolumn = "" + + sort = [("sort_%s" % column, + "%s%s" % ((not descending and column == sortcolumn) + and "-" or "", column)) + for column in sortable] + + self.refresh() + if self._baseurl is not None: + req.env['SCRIPT_NAME'] = self._baseurl + + return tmpl("index", entries=entries, subdir=subdir, + sortcolumn=sortcolumn, descending=descending, + **dict(sort)) + + def templater(self, req): + + def header(**map): + yield tmpl('header', encoding=encoding.encoding, **map) + + def footer(**map): + yield tmpl("footer", **map) + + def motd(**map): + if self.motd is not None: + yield self.motd + else: + yield config('web', 'motd', '') + + def config(section, name, default=None, untrusted=True): + return self.ui.config(section, name, default, untrusted) + + if self._baseurl is not None: + req.env['SCRIPT_NAME'] = self._baseurl + + url = req.env.get('SCRIPT_NAME', '') + if not url.endswith('/'): + url += '/' + + vars = {} + style = self.style + if 'style' in req.form: + vars['style'] = style = req.form['style'][0] + start = url[-1] == '?' and '&' or '?' + sessionvars = webutil.sessionvars(vars, start) + + staticurl = config('web', 'staticurl') or url + 'static/' + if not staticurl.endswith('/'): + staticurl += '/' + + style = 'style' in req.form and req.form['style'][0] or self.style + mapfile = templater.stylemap(style) + tmpl = templater.templater(mapfile, + defaults={"header": header, + "footer": footer, + "motd": motd, + "url": url, + "staticurl": staticurl, + "sessionvars": sessionvars}) + return tmpl diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/protocol.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/protocol.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,206 @@ +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import cStringIO, zlib, tempfile, errno, os, sys, urllib +from upmana.mercurial import util, streamclone +from upmana.mercurial.node import bin, hex +from upmana.mercurial import changegroup as changegroupmod +from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR + +# __all__ is populated with the allowed commands. Be sure to add to it if +# you're adding a new command, or the new command won't work. + +__all__ = [ + 'lookup', 'heads', 'branches', 'between', 'changegroup', + 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out', + 'branchmap', +] + +HGTYPE = 'application/mercurial-0.1' + +def lookup(repo, req): + try: + r = hex(repo.lookup(req.form['key'][0])) + success = 1 + except Exception,inst: + r = str(inst) + success = 0 + resp = "%s %s\n" % (success, r) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) + yield resp + +def heads(repo, req): + resp = " ".join(map(hex, repo.heads())) + "\n" + req.respond(HTTP_OK, HGTYPE, length=len(resp)) + yield resp + +def branchmap(repo, req): + branches = repo.branchmap() + heads = [] + for branch, nodes in branches.iteritems(): + branchname = urllib.quote(branch) + branchnodes = [hex(node) for node in nodes] + heads.append('%s %s' % (branchname, ' '.join(branchnodes))) + resp = '\n'.join(heads) + req.respond(HTTP_OK, HGTYPE, length=len(resp)) + yield resp + +def branches(repo, req): + nodes = [] + if 'nodes' in req.form: + nodes = map(bin, req.form['nodes'][0].split(" ")) + resp = cStringIO.StringIO() + for b in repo.branches(nodes): + resp.write(" ".join(map(hex, b)) + "\n") + resp = resp.getvalue() + req.respond(HTTP_OK, HGTYPE, length=len(resp)) + yield resp + +def between(repo, req): + if 'pairs' in req.form: + pairs = [map(bin, p.split("-")) + for p in req.form['pairs'][0].split(" ")] + resp = cStringIO.StringIO() + for b in repo.between(pairs): + resp.write(" ".join(map(hex, b)) + "\n") + resp = resp.getvalue() + req.respond(HTTP_OK, HGTYPE, length=len(resp)) + yield resp + +def changegroup(repo, req): + req.respond(HTTP_OK, HGTYPE) + nodes = [] + + if 'roots' in req.form: + nodes = map(bin, req.form['roots'][0].split(" ")) + + z = zlib.compressobj() + f = repo.changegroup(nodes, 'serve') + while 1: + chunk = f.read(4096) + if not chunk: + break + yield z.compress(chunk) + + yield z.flush() + +def changegroupsubset(repo, req): + req.respond(HTTP_OK, HGTYPE) + bases = [] + heads = [] + + if 'bases' in req.form: + bases = [bin(x) for x in req.form['bases'][0].split(' ')] + if 'heads' in req.form: + heads = [bin(x) for x in req.form['heads'][0].split(' ')] + + z = zlib.compressobj() + f = repo.changegroupsubset(bases, heads, 'serve') + while 1: + chunk = f.read(4096) + if not chunk: + break + yield z.compress(chunk) + + yield z.flush() + +def capabilities(repo, req): + caps = ['lookup', 'changegroupsubset', 'branchmap'] + if repo.ui.configbool('server', 'uncompressed', untrusted=True): + caps.append('stream=%d' % repo.changelog.version) + if changegroupmod.bundlepriority: + caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) + rsp = ' '.join(caps) + req.respond(HTTP_OK, HGTYPE, length=len(rsp)) + yield rsp + +def unbundle(repo, req): + + proto = req.env.get('wsgi.url_scheme') or 'http' + their_heads = req.form['heads'][0].split(' ') + + def check_heads(): + heads = map(hex, repo.heads()) + return their_heads == [hex('force')] or their_heads == heads + + # fail early if possible + if not check_heads(): + req.drain() + raise ErrorResponse(HTTP_OK, 'unsynced changes') + + # do not lock repo until all changegroup data is + # streamed. save to temporary file. + + fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') + fp = os.fdopen(fd, 'wb+') + try: + length = int(req.env['CONTENT_LENGTH']) + for s in util.filechunkiter(req, limit=length): + fp.write(s) + + try: + lock = repo.lock() + try: + if not check_heads(): + raise ErrorResponse(HTTP_OK, 'unsynced changes') + + fp.seek(0) + header = fp.read(6) + if header.startswith('HG') and not header.startswith('HG10'): + raise ValueError('unknown bundle version') + elif header not in changegroupmod.bundletypes: + raise ValueError('unknown bundle compression type') + gen = changegroupmod.unbundle(header, fp) + + # send addchangegroup output to client + + oldio = sys.stdout, sys.stderr + sys.stderr = sys.stdout = cStringIO.StringIO() + + try: + url = 'remote:%s:%s:%s' % ( + proto, + urllib.quote(req.env.get('REMOTE_HOST', '')), + urllib.quote(req.env.get('REMOTE_USER', ''))) + try: + ret = repo.addchangegroup(gen, 'serve', url) + except util.Abort, inst: + sys.stdout.write("abort: %s\n" % inst) + ret = 0 + finally: + val = sys.stdout.getvalue() + sys.stdout, sys.stderr = oldio + req.respond(HTTP_OK, HGTYPE) + return '%d\n%s' % (ret, val), + finally: + lock.release() + except ValueError, inst: + raise ErrorResponse(HTTP_OK, inst) + except (OSError, IOError), inst: + filename = getattr(inst, 'filename', '') + # Don't send our filesystem layout to the client + if filename.startswith(repo.root): + filename = filename[len(repo.root)+1:] + else: + filename = '' + error = getattr(inst, 'strerror', 'Unknown error') + if inst.errno == errno.ENOENT: + code = HTTP_NOT_FOUND + else: + code = HTTP_SERVER_ERROR + raise ErrorResponse(code, '%s: %s' % (error, filename)) + finally: + fp.close() + os.unlink(tempname) + +def stream_out(repo, req): + req.respond(HTTP_OK, HGTYPE) + try: + for chunk in streamclone.stream_out(repo, untrusted=True): + yield chunk + except streamclone.StreamException, inst: + yield str(inst) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/request.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/request.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,134 @@ +# hgweb/request.py - An http request from either CGI or the standalone server. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import socket, cgi, errno +from upmana.mercurial import util +from common import ErrorResponse, statusmessage + +shortcuts = { + 'cl': [('cmd', ['changelog']), ('rev', None)], + 'sl': [('cmd', ['shortlog']), ('rev', None)], + 'cs': [('cmd', ['changeset']), ('node', None)], + 'f': [('cmd', ['file']), ('filenode', None)], + 'fl': [('cmd', ['filelog']), ('filenode', None)], + 'fd': [('cmd', ['filediff']), ('node', None)], + 'fa': [('cmd', ['annotate']), ('filenode', None)], + 'mf': [('cmd', ['manifest']), ('manifest', None)], + 'ca': [('cmd', ['archive']), ('node', None)], + 'tags': [('cmd', ['tags'])], + 'tip': [('cmd', ['changeset']), ('node', ['tip'])], + 'static': [('cmd', ['static']), ('file', None)] +} + +def expand(form): + for k in shortcuts.iterkeys(): + if k in form: + for name, value in shortcuts[k]: + if value is None: + value = form[k] + form[name] = value + del form[k] + return form + +class wsgirequest(object): + def __init__(self, wsgienv, start_response): + version = wsgienv['wsgi.version'] + if (version < (1, 0)) or (version >= (2, 0)): + raise RuntimeError("Unknown and unsupported WSGI version %d.%d" + % version) + self.inp = wsgienv['wsgi.input'] + self.err = wsgienv['wsgi.errors'] + self.threaded = wsgienv['wsgi.multithread'] + self.multiprocess = wsgienv['wsgi.multiprocess'] + self.run_once = wsgienv['wsgi.run_once'] + self.env = wsgienv + self.form = expand(cgi.parse(self.inp, self.env, keep_blank_values=1)) + self._start_response = start_response + self.server_write = None + self.headers = [] + + def __iter__(self): + return iter([]) + + def read(self, count=-1): + return self.inp.read(count) + + def drain(self): + '''need to read all data from request, httplib is half-duplex''' + length = int(self.env.get('CONTENT_LENGTH', 0)) + for s in util.filechunkiter(self.inp, limit=length): + pass + + def respond(self, status, type=None, filename=None, length=0): + if self._start_response is not None: + + self.httphdr(type, filename, length) + if not self.headers: + raise RuntimeError("request.write called before headers sent") + + for k, v in self.headers: + if not isinstance(v, str): + raise TypeError('header value must be string: %r' % v) + + if isinstance(status, ErrorResponse): + self.header(status.headers) + status = statusmessage(status.code) + elif status == 200: + status = '200 Script output follows' + elif isinstance(status, int): + status = statusmessage(status) + + self.server_write = self._start_response(status, self.headers) + self._start_response = None + self.headers = [] + + def write(self, thing): + if hasattr(thing, "__iter__"): + for part in thing: + self.write(part) + else: + thing = str(thing) + try: + self.server_write(thing) + except socket.error, inst: + if inst[0] != errno.ECONNRESET: + raise + + def writelines(self, lines): + for line in lines: + self.write(line) + + def flush(self): + return None + + def close(self): + return None + + def header(self, headers=[('Content-Type','text/html')]): + self.headers.extend(headers) + + def httphdr(self, type=None, filename=None, length=0, headers={}): + headers = headers.items() + if type is not None: + headers.append(('Content-Type', type)) + if filename: + filename = (filename.split('/')[-1] + .replace('\\', '\\\\').replace('"', '\\"')) + headers.append(('Content-Disposition', + 'inline; filename="%s"' % filename)) + if length: + headers.append(('Content-Length', str(length))) + self.header(headers) + +def wsgiapplication(app_maker): + '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir() + can and should now be used as a WSGI application.''' + application = app_maker() + def run_wsgi(env, respond): + return application(env, respond) + return run_wsgi diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/server.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/server.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,298 @@ +# hgweb/server.py - The standalone hg web server. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback +from upmana.mercurial import hg, util, error +from hgweb_mod import hgweb +from hgwebdir_mod import hgwebdir +from upmana.mercurial.i18n import _ + +def _splitURI(uri): + """ Return path and query splited from uri + + Just like CGI environment, the path is unquoted, the query is + not. + """ + if '?' in uri: + path, query = uri.split('?', 1) + else: + path, query = uri, '' + return urllib.unquote(path), query + +class _error_logger(object): + def __init__(self, handler): + self.handler = handler + def flush(self): + pass + def write(self, str): + self.writelines(str.split('\n')) + def writelines(self, seq): + for msg in seq: + self.handler.log_error("HG error: %s", msg) + +class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler): + + url_scheme = 'http' + + def __init__(self, *args, **kargs): + self.protocol_version = 'HTTP/1.1' + BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs) + + def _log_any(self, fp, format, *args): + fp.write("%s - - [%s] %s\n" % (self.client_address[0], + self.log_date_time_string(), + format % args)) + fp.flush() + + def log_error(self, format, *args): + self._log_any(self.server.errorlog, format, *args) + + def log_message(self, format, *args): + self._log_any(self.server.accesslog, format, *args) + + def do_write(self): + try: + self.do_hgweb() + except socket.error, inst: + if inst[0] != errno.EPIPE: + raise + + def do_POST(self): + try: + self.do_write() + except StandardError: + self._start_response("500 Internal Server Error", []) + self._write("Internal Server Error") + tb = "".join(traceback.format_exception(*sys.exc_info())) + self.log_error("Exception happened during processing " + "request '%s':\n%s", self.path, tb) + + def do_GET(self): + self.do_POST() + + def do_hgweb(self): + path, query = _splitURI(self.path) + + env = {} + env['GATEWAY_INTERFACE'] = 'CGI/1.1' + env['REQUEST_METHOD'] = self.command + env['SERVER_NAME'] = self.server.server_name + env['SERVER_PORT'] = str(self.server.server_port) + env['REQUEST_URI'] = self.path + env['SCRIPT_NAME'] = self.server.prefix + env['PATH_INFO'] = path[len(self.server.prefix):] + env['REMOTE_HOST'] = self.client_address[0] + env['REMOTE_ADDR'] = self.client_address[0] + if query: + env['QUERY_STRING'] = query + + if self.headers.typeheader is None: + env['CONTENT_TYPE'] = self.headers.type + else: + env['CONTENT_TYPE'] = self.headers.typeheader + length = self.headers.getheader('content-length') + if length: + env['CONTENT_LENGTH'] = length + for header in [h for h in self.headers.keys() + if h not in ('content-type', 'content-length')]: + hkey = 'HTTP_' + header.replace('-', '_').upper() + hval = self.headers.getheader(header) + hval = hval.replace('\n', '').strip() + if hval: + env[hkey] = hval + env['SERVER_PROTOCOL'] = self.request_version + env['wsgi.version'] = (1, 0) + env['wsgi.url_scheme'] = self.url_scheme + env['wsgi.input'] = self.rfile + env['wsgi.errors'] = _error_logger(self) + env['wsgi.multithread'] = isinstance(self.server, + SocketServer.ThreadingMixIn) + env['wsgi.multiprocess'] = isinstance(self.server, + SocketServer.ForkingMixIn) + env['wsgi.run_once'] = 0 + + self.close_connection = True + self.saved_status = None + self.saved_headers = [] + self.sent_headers = False + self.length = None + for chunk in self.server.application(env, self._start_response): + self._write(chunk) + + def send_headers(self): + if not self.saved_status: + raise AssertionError("Sending headers before " + "start_response() called") + saved_status = self.saved_status.split(None, 1) + saved_status[0] = int(saved_status[0]) + self.send_response(*saved_status) + should_close = True + for h in self.saved_headers: + self.send_header(*h) + if h[0].lower() == 'content-length': + should_close = False + self.length = int(h[1]) + # The value of the Connection header is a list of case-insensitive + # tokens separated by commas and optional whitespace. + if 'close' in [token.strip().lower() for token in + self.headers.get('connection', '').split(',')]: + should_close = True + if should_close: + self.send_header('Connection', 'close') + self.close_connection = should_close + self.end_headers() + self.sent_headers = True + + def _start_response(self, http_status, headers, exc_info=None): + code, msg = http_status.split(None, 1) + code = int(code) + self.saved_status = http_status + bad_headers = ('connection', 'transfer-encoding') + self.saved_headers = [h for h in headers + if h[0].lower() not in bad_headers] + return self._write + + def _write(self, data): + if not self.saved_status: + raise AssertionError("data written before start_response() called") + elif not self.sent_headers: + self.send_headers() + if self.length is not None: + if len(data) > self.length: + raise AssertionError("Content-length header sent, but more " + "bytes than specified are being written.") + self.length = self.length - len(data) + self.wfile.write(data) + self.wfile.flush() + +class _shgwebhandler(_hgwebhandler): + + url_scheme = 'https' + + def setup(self): + self.connection = self.request + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + + def do_write(self): + from OpenSSL.SSL import SysCallError + try: + super(_shgwebhandler, self).do_write() + except SysCallError, inst: + if inst.args[0] != errno.EPIPE: + raise + + def handle_one_request(self): + from OpenSSL.SSL import SysCallError, ZeroReturnError + try: + super(_shgwebhandler, self).handle_one_request() + except (SysCallError, ZeroReturnError): + self.close_connection = True + pass + +def create_server(ui, repo): + use_threads = True + + def openlog(opt, default): + if opt and opt != '-': + return open(opt, 'a') + return default + + if repo is None: + myui = ui + else: + myui = repo.ui + address = myui.config("web", "address", "") + port = int(myui.config("web", "port", 8000)) + prefix = myui.config("web", "prefix", "") + if prefix: + prefix = "/" + prefix.strip("/") + use_ipv6 = myui.configbool("web", "ipv6") + webdir_conf = myui.config("web", "webdir_conf") + ssl_cert = myui.config("web", "certificate") + accesslog = openlog(myui.config("web", "accesslog", "-"), sys.stdout) + errorlog = openlog(myui.config("web", "errorlog", "-"), sys.stderr) + + if use_threads: + try: + from threading import activeCount + except ImportError: + use_threads = False + + if use_threads: + _mixin = SocketServer.ThreadingMixIn + else: + if hasattr(os, "fork"): + _mixin = SocketServer.ForkingMixIn + else: + class _mixin: + pass + + class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer): + + # SO_REUSEADDR has broken semantics on windows + if os.name == 'nt': + allow_reuse_address = 0 + + def __init__(self, *args, **kargs): + BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs) + self.accesslog = accesslog + self.errorlog = errorlog + self.daemon_threads = True + def make_handler(): + if webdir_conf: + hgwebobj = hgwebdir(webdir_conf, ui) + elif repo is not None: + hgwebobj = hgweb(hg.repository(repo.ui, repo.root)) + else: + raise error.RepoError(_("There is no Mercurial repository" + " here (.hg not found)")) + return hgwebobj + self.application = make_handler() + + if ssl_cert: + try: + from OpenSSL import SSL + ctx = SSL.Context(SSL.SSLv23_METHOD) + except ImportError: + raise util.Abort(_("SSL support is unavailable")) + ctx.use_privatekey_file(ssl_cert) + ctx.use_certificate_file(ssl_cert) + sock = socket.socket(self.address_family, self.socket_type) + self.socket = SSL.Connection(ctx, sock) + self.server_bind() + self.server_activate() + + self.addr, self.port = self.socket.getsockname()[0:2] + self.prefix = prefix + self.fqaddr = socket.getfqdn(address) + + class IPv6HTTPServer(MercurialHTTPServer): + address_family = getattr(socket, 'AF_INET6', None) + + def __init__(self, *args, **kwargs): + if self.address_family is None: + raise error.RepoError(_('IPv6 is not available on this system')) + super(IPv6HTTPServer, self).__init__(*args, **kwargs) + + if ssl_cert: + handler = _shgwebhandler + else: + handler = _hgwebhandler + + # ugly hack due to python issue5853 (for threaded use) + import mimetypes; mimetypes.init() + + try: + if use_ipv6: + return IPv6HTTPServer((address, port), handler) + else: + return MercurialHTTPServer((address, port), handler) + except socket.error, inst: + raise util.Abort(_("cannot start server at '%s:%d': %s") + % (address, port, inst.args[1])) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/webcommands.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/webcommands.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,690 @@ +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os, mimetypes, re, cgi, copy +import webutil +from upmana.mercurial import error, archival, templater, templatefilters +from upmana.mercurial.node import short, hex +from upmana.mercurial.util import binary +from common import paritygen, staticfile, get_contact, ErrorResponse +from common import HTTP_OK, HTTP_FORBIDDEN, HTTP_NOT_FOUND +from upmana.mercurial import graphmod + +# __all__ is populated with the allowed commands. Be sure to add to it if +# you're adding a new command, or the new command won't work. + +__all__ = [ + 'log', 'rawfile', 'file', 'changelog', 'shortlog', 'changeset', 'rev', + 'manifest', 'tags', 'branches', 'summary', 'filediff', 'diff', 'annotate', + 'filelog', 'archive', 'static', 'graph', +] + +def log(web, req, tmpl): + if 'file' in req.form and req.form['file'][0]: + return filelog(web, req, tmpl) + else: + return changelog(web, req, tmpl) + +def rawfile(web, req, tmpl): + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) + if not path: + content = manifest(web, req, tmpl) + req.respond(HTTP_OK, web.ctype) + return content + + try: + fctx = webutil.filectx(web.repo, req) + except error.LookupError, inst: + try: + content = manifest(web, req, tmpl) + req.respond(HTTP_OK, web.ctype) + return content + except ErrorResponse: + raise inst + + path = fctx.path() + text = fctx.data() + mt = mimetypes.guess_type(path)[0] + if mt is None: + mt = binary(text) and 'application/octet-stream' or 'text/plain' + + req.respond(HTTP_OK, mt, path, len(text)) + return [text] + +def _filerevision(web, tmpl, fctx): + f = fctx.path() + text = fctx.data() + parity = paritygen(web.stripecount) + + if binary(text): + mt = mimetypes.guess_type(f)[0] or 'application/octet-stream' + text = '(binary:%s)' % mt + + def lines(): + for lineno, t in enumerate(text.splitlines(1)): + yield {"line": t, + "lineid": "l%d" % (lineno + 1), + "linenumber": "% 6d" % (lineno + 1), + "parity": parity.next()} + + return tmpl("filerevision", + file=f, + path=webutil.up(f), + text=lines(), + rev=fctx.rev(), + node=hex(fctx.node()), + author=fctx.user(), + date=fctx.date(), + desc=fctx.description(), + branch=webutil.nodebranchnodefault(fctx), + parent=webutil.parents(fctx), + child=webutil.children(fctx), + rename=webutil.renamelink(fctx), + permissions=fctx.manifest().flags(f)) + +def file(web, req, tmpl): + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) + if not path: + return manifest(web, req, tmpl) + try: + return _filerevision(web, tmpl, webutil.filectx(web.repo, req)) + except error.LookupError, inst: + try: + return manifest(web, req, tmpl) + except ErrorResponse: + raise inst + +def _search(web, tmpl, query): + + def changelist(**map): + cl = web.repo.changelog + count = 0 + qw = query.lower().split() + + def revgen(): + for i in xrange(len(cl) - 1, 0, -100): + l = [] + for j in xrange(max(0, i - 100), i + 1): + ctx = web.repo[j] + l.append(ctx) + l.reverse() + for e in l: + yield e + + for ctx in revgen(): + miss = 0 + for q in qw: + if not (q in ctx.user().lower() or + q in ctx.description().lower() or + q in " ".join(ctx.files()).lower()): + miss = 1 + break + if miss: + continue + + count += 1 + n = ctx.node() + showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) + files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) + + yield tmpl('searchentry', + parity=parity.next(), + author=ctx.user(), + parent=webutil.parents(ctx), + child=webutil.children(ctx), + changelogtag=showtags, + desc=ctx.description(), + date=ctx.date(), + files=files, + rev=ctx.rev(), + node=hex(n), + tags=webutil.nodetagsdict(web.repo, n), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) + + if count >= web.maxchanges: + break + + cl = web.repo.changelog + parity = paritygen(web.stripecount) + + return tmpl('search', + query=query, + node=hex(cl.tip()), + entries=changelist, + archives=web.archivelist("tip")) + +def changelog(web, req, tmpl, shortlog = False): + if 'node' in req.form: + ctx = webutil.changectx(web.repo, req) + else: + if 'rev' in req.form: + hi = req.form['rev'][0] + else: + hi = len(web.repo) - 1 + try: + ctx = web.repo[hi] + except error.RepoError: + return _search(web, tmpl, hi) # XXX redirect to 404 page? + + def changelist(limit=0, **map): + l = [] # build a list in forward order for efficiency + for i in xrange(start, end): + ctx = web.repo[i] + n = ctx.node() + showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n) + files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles) + + l.insert(0, {"parity": parity.next(), + "author": ctx.user(), + "parent": webutil.parents(ctx, i - 1), + "child": webutil.children(ctx, i + 1), + "changelogtag": showtags, + "desc": ctx.description(), + "date": ctx.date(), + "files": files, + "rev": i, + "node": hex(n), + "tags": webutil.nodetagsdict(web.repo, n), + "inbranch": webutil.nodeinbranch(web.repo, ctx), + "branches": webutil.nodebranchdict(web.repo, ctx) + }) + + if limit > 0: + l = l[:limit] + + for e in l: + yield e + + maxchanges = shortlog and web.maxshortchanges or web.maxchanges + cl = web.repo.changelog + count = len(cl) + pos = ctx.rev() + start = max(0, pos - maxchanges + 1) + end = min(count, start + maxchanges) + pos = end - 1 + parity = paritygen(web.stripecount, offset=start-end) + + changenav = webutil.revnavgen(pos, maxchanges, count, web.repo.changectx) + + return tmpl(shortlog and 'shortlog' or 'changelog', + changenav=changenav, + node=hex(ctx.node()), + rev=pos, changesets=count, + entries=lambda **x: changelist(limit=0,**x), + latestentry=lambda **x: changelist(limit=1,**x), + archives=web.archivelist("tip")) + +def shortlog(web, req, tmpl): + return changelog(web, req, tmpl, shortlog = True) + +def changeset(web, req, tmpl): + ctx = webutil.changectx(web.repo, req) + showtags = webutil.showtag(web.repo, tmpl, 'changesettag', ctx.node()) + showbranch = webutil.nodebranchnodefault(ctx) + + files = [] + parity = paritygen(web.stripecount) + for f in ctx.files(): + template = f in ctx and 'filenodelink' or 'filenolink' + files.append(tmpl(template, + node=ctx.hex(), file=f, + parity=parity.next())) + + parity = paritygen(web.stripecount) + diffs = webutil.diffs(web.repo, tmpl, ctx, None, parity) + return tmpl('changeset', + diff=diffs, + rev=ctx.rev(), + node=ctx.hex(), + parent=webutil.parents(ctx), + child=webutil.children(ctx), + changesettag=showtags, + changesetbranch=showbranch, + author=ctx.user(), + desc=ctx.description(), + date=ctx.date(), + files=files, + archives=web.archivelist(ctx.hex()), + tags=webutil.nodetagsdict(web.repo, ctx.node()), + branch=webutil.nodebranchnodefault(ctx), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) + +rev = changeset + +def manifest(web, req, tmpl): + ctx = webutil.changectx(web.repo, req) + path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0]) + mf = ctx.manifest() + node = ctx.node() + + files = {} + dirs = {} + parity = paritygen(web.stripecount) + + if path and path[-1] != "/": + path += "/" + l = len(path) + abspath = "/" + path + + for f, n in mf.iteritems(): + if f[:l] != path: + continue + remain = f[l:] + elements = remain.split('/') + if len(elements) == 1: + files[remain] = f + else: + h = dirs # need to retain ref to dirs (root) + for elem in elements[0:-1]: + if elem not in h: + h[elem] = {} + h = h[elem] + if len(h) > 1: + break + h[None] = None # denotes files present + + if mf and not files and not dirs: + raise ErrorResponse(HTTP_NOT_FOUND, 'path not found: ' + path) + + def filelist(**map): + for f in sorted(files): + full = files[f] + + fctx = ctx.filectx(full) + yield {"file": full, + "parity": parity.next(), + "basename": f, + "date": fctx.date(), + "size": fctx.size(), + "permissions": mf.flags(full)} + + def dirlist(**map): + for d in sorted(dirs): + + emptydirs = [] + h = dirs[d] + while isinstance(h, dict) and len(h) == 1: + k,v = h.items()[0] + if v: + emptydirs.append(k) + h = v + + path = "%s%s" % (abspath, d) + yield {"parity": parity.next(), + "path": path, + "emptydirs": "/".join(emptydirs), + "basename": d} + + return tmpl("manifest", + rev=ctx.rev(), + node=hex(node), + path=abspath, + up=webutil.up(abspath), + upparity=parity.next(), + fentries=filelist, + dentries=dirlist, + archives=web.archivelist(hex(node)), + tags=webutil.nodetagsdict(web.repo, node), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx)) + +def tags(web, req, tmpl): + i = web.repo.tagslist() + i.reverse() + parity = paritygen(web.stripecount) + + def entries(notip=False,limit=0, **map): + count = 0 + for k, n in i: + if notip and k == "tip": + continue + if limit > 0 and count >= limit: + continue + count = count + 1 + yield {"parity": parity.next(), + "tag": k, + "date": web.repo[n].date(), + "node": hex(n)} + + return tmpl("tags", + node=hex(web.repo.changelog.tip()), + entries=lambda **x: entries(False,0, **x), + entriesnotip=lambda **x: entries(True,0, **x), + latestentry=lambda **x: entries(True,1, **x)) + +def branches(web, req, tmpl): + b = web.repo.branchtags() + tips = (web.repo[n] for t, n in web.repo.branchtags().iteritems()) + heads = web.repo.heads() + parity = paritygen(web.stripecount) + sortkey = lambda ctx: ('close' not in ctx.extra(), ctx.rev()) + + def entries(limit, **map): + count = 0 + for ctx in sorted(tips, key=sortkey, reverse=True): + if limit > 0 and count >= limit: + return + count += 1 + if ctx.node() not in heads: + status = 'inactive' + elif not web.repo.branchheads(ctx.branch()): + status = 'closed' + else: + status = 'open' + yield {'parity': parity.next(), + 'branch': ctx.branch(), + 'status': status, + 'node': ctx.hex(), + 'date': ctx.date()} + + return tmpl('branches', node=hex(web.repo.changelog.tip()), + entries=lambda **x: entries(0, **x), + latestentry=lambda **x: entries(1, **x)) + +def summary(web, req, tmpl): + i = web.repo.tagslist() + i.reverse() + + def tagentries(**map): + parity = paritygen(web.stripecount) + count = 0 + for k, n in i: + if k == "tip": # skip tip + continue + + count += 1 + if count > 10: # limit to 10 tags + break + + yield tmpl("tagentry", + parity=parity.next(), + tag=k, + node=hex(n), + date=web.repo[n].date()) + + def branches(**map): + parity = paritygen(web.stripecount) + + b = web.repo.branchtags() + l = [(-web.repo.changelog.rev(n), n, t) for t, n in b.iteritems()] + for r,n,t in sorted(l): + yield {'parity': parity.next(), + 'branch': t, + 'node': hex(n), + 'date': web.repo[n].date()} + + def changelist(**map): + parity = paritygen(web.stripecount, offset=start-end) + l = [] # build a list in forward order for efficiency + for i in xrange(start, end): + ctx = web.repo[i] + n = ctx.node() + hn = hex(n) + + l.insert(0, tmpl( + 'shortlogentry', + parity=parity.next(), + author=ctx.user(), + desc=ctx.description(), + date=ctx.date(), + rev=i, + node=hn, + tags=webutil.nodetagsdict(web.repo, n), + inbranch=webutil.nodeinbranch(web.repo, ctx), + branches=webutil.nodebranchdict(web.repo, ctx))) + + yield l + + cl = web.repo.changelog + count = len(cl) + start = max(0, count - web.maxchanges) + end = min(count, start + web.maxchanges) + + return tmpl("summary", + desc=web.config("web", "description", "unknown"), + owner=get_contact(web.config) or "unknown", + lastchange=cl.read(cl.tip())[2], + tags=tagentries, + branches=branches, + shortlog=changelist, + node=hex(cl.tip()), + archives=web.archivelist("tip")) + +def filediff(web, req, tmpl): + fctx, ctx = None, None + try: + fctx = webutil.filectx(web.repo, req) + except LookupError: + ctx = webutil.changectx(web.repo, req) + path = webutil.cleanpath(web.repo, req.form['file'][0]) + if path not in ctx.files(): + raise + + if fctx is not None: + n = fctx.node() + path = fctx.path() + else: + n = ctx.node() + # path already defined in except clause + + parity = paritygen(web.stripecount) + diffs = webutil.diffs(web.repo, tmpl, fctx or ctx, [path], parity) + rename = fctx and webutil.renamelink(fctx) or [] + ctx = fctx and fctx or ctx + return tmpl("filediff", + file=path, + node=hex(n), + rev=ctx.rev(), + date=ctx.date(), + desc=ctx.description(), + author=ctx.user(), + rename=rename, + branch=webutil.nodebranchnodefault(ctx), + parent=webutil.parents(ctx), + child=webutil.children(ctx), + diff=diffs) + +diff = filediff + +def annotate(web, req, tmpl): + fctx = webutil.filectx(web.repo, req) + f = fctx.path() + parity = paritygen(web.stripecount) + + def annotate(**map): + last = None + if binary(fctx.data()): + mt = (mimetypes.guess_type(fctx.path())[0] + or 'application/octet-stream') + lines = enumerate([((fctx.filectx(fctx.filerev()), 1), + '(binary:%s)' % mt)]) + else: + lines = enumerate(fctx.annotate(follow=True, linenumber=True)) + for lineno, ((f, targetline), l) in lines: + fnode = f.filenode() + + if last != fnode: + last = fnode + + yield {"parity": parity.next(), + "node": hex(f.node()), + "rev": f.rev(), + "author": f.user(), + "desc": f.description(), + "file": f.path(), + "targetline": targetline, + "line": l, + "lineid": "l%d" % (lineno + 1), + "linenumber": "% 6d" % (lineno + 1)} + + return tmpl("fileannotate", + file=f, + annotate=annotate, + path=webutil.up(f), + rev=fctx.rev(), + node=hex(fctx.node()), + author=fctx.user(), + date=fctx.date(), + desc=fctx.description(), + rename=webutil.renamelink(fctx), + branch=webutil.nodebranchnodefault(fctx), + parent=webutil.parents(fctx), + child=webutil.children(fctx), + permissions=fctx.manifest().flags(f)) + +def filelog(web, req, tmpl): + + try: + fctx = webutil.filectx(web.repo, req) + f = fctx.path() + fl = fctx.filelog() + except error.LookupError: + f = webutil.cleanpath(web.repo, req.form['file'][0]) + fl = web.repo.file(f) + numrevs = len(fl) + if not numrevs: # file doesn't exist at all + raise + rev = webutil.changectx(web.repo, req).rev() + first = fl.linkrev(0) + if rev < first: # current rev is from before file existed + raise + frev = numrevs - 1 + while fl.linkrev(frev) > rev: + frev -= 1 + fctx = web.repo.filectx(f, fl.linkrev(frev)) + + count = fctx.filerev() + 1 + pagelen = web.maxshortchanges + start = max(0, fctx.filerev() - pagelen + 1) # first rev on this page + end = min(count, start + pagelen) # last rev on this page + parity = paritygen(web.stripecount, offset=start-end) + + def entries(limit=0, **map): + l = [] + + repo = web.repo + for i in xrange(start, end): + iterfctx = fctx.filectx(i) + + l.insert(0, {"parity": parity.next(), + "filerev": i, + "file": f, + "node": hex(iterfctx.node()), + "author": iterfctx.user(), + "date": iterfctx.date(), + "rename": webutil.renamelink(iterfctx), + "parent": webutil.parents(iterfctx), + "child": webutil.children(iterfctx), + "desc": iterfctx.description(), + "tags": webutil.nodetagsdict(repo, iterfctx.node()), + "branch": webutil.nodebranchnodefault(iterfctx), + "inbranch": webutil.nodeinbranch(repo, iterfctx), + "branches": webutil.nodebranchdict(repo, iterfctx)}) + + if limit > 0: + l = l[:limit] + + for e in l: + yield e + + nodefunc = lambda x: fctx.filectx(fileid=x) + nav = webutil.revnavgen(end - 1, pagelen, count, nodefunc) + return tmpl("filelog", file=f, node=hex(fctx.node()), nav=nav, + entries=lambda **x: entries(limit=0, **x), + latestentry=lambda **x: entries(limit=1, **x)) + + +def archive(web, req, tmpl): + type_ = req.form.get('type', [None])[0] + allowed = web.configlist("web", "allow_archive") + key = req.form['node'][0] + + if type_ not in web.archives: + msg = 'Unsupported archive type: %s' % type_ + raise ErrorResponse(HTTP_NOT_FOUND, msg) + + if not ((type_ in allowed or + web.configbool("web", "allow" + type_, False))): + msg = 'Archive type not allowed: %s' % type_ + raise ErrorResponse(HTTP_FORBIDDEN, msg) + + reponame = re.sub(r"\W+", "-", os.path.basename(web.reponame)) + cnode = web.repo.lookup(key) + arch_version = key + if cnode == key or key == 'tip': + arch_version = short(cnode) + name = "%s-%s" % (reponame, arch_version) + mimetype, artype, extension, encoding = web.archive_specs[type_] + headers = [ + ('Content-Type', mimetype), + ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) + ] + if encoding: + headers.append(('Content-Encoding', encoding)) + req.header(headers) + req.respond(HTTP_OK) + archival.archive(web.repo, req, cnode, artype, prefix=name) + return [] + + +def static(web, req, tmpl): + fname = req.form['file'][0] + # a repo owner may set web.static in .hg/hgrc to get any file + # readable by the user running the CGI script + static = web.config("web", "static", None, untrusted=False) + if not static: + tp = web.templatepath or templater.templatepath() + if isinstance(tp, str): + tp = [tp] + static = [os.path.join(p, 'static') for p in tp] + return [staticfile(static, fname, req)] + +def graph(web, req, tmpl): + rev = webutil.changectx(web.repo, req).rev() + bg_height = 39 + + revcount = 25 + if 'revcount' in req.form: + revcount = int(req.form.get('revcount', [revcount])[0]) + tmpl.defaults['sessionvars']['revcount'] = revcount + + lessvars = copy.copy(tmpl.defaults['sessionvars']) + lessvars['revcount'] = revcount / 2 + morevars = copy.copy(tmpl.defaults['sessionvars']) + morevars['revcount'] = revcount * 2 + + max_rev = len(web.repo) - 1 + revcount = min(max_rev, revcount) + revnode = web.repo.changelog.node(rev) + revnode_hex = hex(revnode) + uprev = min(max_rev, rev + revcount) + downrev = max(0, rev - revcount) + count = len(web.repo) + changenav = webutil.revnavgen(rev, revcount, count, web.repo.changectx) + + dag = graphmod.revisions(web.repo, rev, downrev) + tree = list(graphmod.colored(dag)) + canvasheight = (len(tree) + 1) * bg_height - 27; + data = [] + for (id, type, ctx, vtx, edges) in tree: + if type != graphmod.CHANGESET: + continue + node = short(ctx.node()) + age = templatefilters.age(ctx.date()) + desc = templatefilters.firstline(ctx.description()) + desc = cgi.escape(templatefilters.nonempty(desc)) + user = cgi.escape(templatefilters.person(ctx.user())) + branch = ctx.branch() + branch = branch, web.repo.branchtags().get(branch) == ctx.node() + data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) + + return tmpl('graph', rev=rev, revcount=revcount, uprev=uprev, + lessvars=lessvars, morevars=morevars, downrev=downrev, + canvasheight=canvasheight, jsdata=data, bg_height=bg_height, + node=revnode_hex, changenav=changenav) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/webutil.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/webutil.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,218 @@ +# hgweb/webutil.py - utility library for the web interface. +# +# Copyright 21 May 2005 - (c) 2005 Jake Edge +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import os, copy +from upmana.mercurial import match, patch, util, error +from upmana.mercurial.node import hex, nullid + +def up(p): + if p[0] != "/": + p = "/" + p + if p[-1] == "/": + p = p[:-1] + up = os.path.dirname(p) + if up == "/": + return "/" + return up + "/" + +def revnavgen(pos, pagelen, limit, nodefunc): + def seq(factor, limit=None): + if limit: + yield limit + if limit >= 20 and limit <= 40: + yield 50 + else: + yield 1 * factor + yield 3 * factor + for f in seq(factor * 10): + yield f + + def nav(**map): + l = [] + last = 0 + for f in seq(1, pagelen): + if f < pagelen or f <= last: + continue + if f > limit: + break + last = f + if pos + f < limit: + l.append(("+%d" % f, hex(nodefunc(pos + f).node()))) + if pos - f >= 0: + l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node()))) + + try: + yield {"label": "(0)", "node": hex(nodefunc('0').node())} + + for label, node in l: + yield {"label": label, "node": node} + + yield {"label": "tip", "node": "tip"} + except error.RepoError: + pass + + return nav + +def _siblings(siblings=[], hiderev=None): + siblings = [s for s in siblings if s.node() != nullid] + if len(siblings) == 1 and siblings[0].rev() == hiderev: + return + for s in siblings: + d = {'node': hex(s.node()), 'rev': s.rev()} + d['user'] = s.user() + d['date'] = s.date() + d['description'] = s.description() + d['branch'] = s.branch() + if hasattr(s, 'path'): + d['file'] = s.path() + yield d + +def parents(ctx, hide=None): + return _siblings(ctx.parents(), hide) + +def children(ctx, hide=None): + return _siblings(ctx.children(), hide) + +def renamelink(fctx): + r = fctx.renamed() + if r: + return [dict(file=r[0], node=hex(r[1]))] + return [] + +def nodetagsdict(repo, node): + return [{"name": i} for i in repo.nodetags(node)] + +def nodebranchdict(repo, ctx): + branches = [] + branch = ctx.branch() + # If this is an empty repo, ctx.node() == nullid, + # ctx.branch() == 'default', but branchtags() is + # an empty dict. Using dict.get avoids a traceback. + if repo.branchtags().get(branch) == ctx.node(): + branches.append({"name": branch}) + return branches + +def nodeinbranch(repo, ctx): + branches = [] + branch = ctx.branch() + if branch != 'default' and repo.branchtags().get(branch) != ctx.node(): + branches.append({"name": branch}) + return branches + +def nodebranchnodefault(ctx): + branches = [] + branch = ctx.branch() + if branch != 'default': + branches.append({"name": branch}) + return branches + +def showtag(repo, tmpl, t1, node=nullid, **args): + for t in repo.nodetags(node): + yield tmpl(t1, tag=t, **args) + +def cleanpath(repo, path): + path = path.lstrip('/') + return util.canonpath(repo.root, '', path) + +def changectx(repo, req): + changeid = "tip" + if 'node' in req.form: + changeid = req.form['node'][0] + elif 'manifest' in req.form: + changeid = req.form['manifest'][0] + + try: + ctx = repo[changeid] + except error.RepoError: + man = repo.manifest + ctx = repo[man.linkrev(man.rev(man.lookup(changeid)))] + + return ctx + +def filectx(repo, req): + path = cleanpath(repo, req.form['file'][0]) + if 'node' in req.form: + changeid = req.form['node'][0] + else: + changeid = req.form['filenode'][0] + try: + fctx = repo[changeid][path] + except error.RepoError: + fctx = repo.filectx(path, fileid=changeid) + + return fctx + +def listfilediffs(tmpl, files, node, max): + for f in files[:max]: + yield tmpl('filedifflink', node=hex(node), file=f) + if len(files) > max: + yield tmpl('fileellipses') + +def diffs(repo, tmpl, ctx, files, parity): + + def countgen(): + start = 1 + while True: + yield start + start += 1 + + blockcount = countgen() + def prettyprintlines(diff): + blockno = blockcount.next() + for lineno, l in enumerate(diff.splitlines(True)): + lineno = "%d.%d" % (blockno, lineno + 1) + if l.startswith('+'): + ltype = "difflineplus" + elif l.startswith('-'): + ltype = "difflineminus" + elif l.startswith('@'): + ltype = "difflineat" + else: + ltype = "diffline" + yield tmpl(ltype, + line=l, + lineid="l%s" % lineno, + linenumber="% 8s" % lineno) + + if files: + m = match.exact(repo.root, repo.getcwd(), files) + else: + m = match.always(repo.root, repo.getcwd()) + + diffopts = patch.diffopts(repo.ui, untrusted=True) + parents = ctx.parents() + node1 = parents and parents[0].node() or nullid + node2 = ctx.node() + + block = [] + for chunk in patch.diff(repo, node1, node2, m, opts=diffopts): + if chunk.startswith('diff') and block: + yield tmpl('diffblock', parity=parity.next(), + lines=prettyprintlines(''.join(block))) + block = [] + if chunk.startswith('diff'): + chunk = ''.join(chunk.splitlines(True)[1:]) + block.append(chunk) + yield tmpl('diffblock', parity=parity.next(), + lines=prettyprintlines(''.join(block))) + +class sessionvars(object): + def __init__(self, vars, start='?'): + self.start = start + self.vars = vars + def __getitem__(self, key): + return self.vars[key] + def __setitem__(self, key, value): + self.vars[key] = value + def __copy__(self): + return sessionvars(copy.copy(self.vars), self.start) + def __iter__(self): + separator = self.start + for key, value in self.vars.iteritems(): + yield {'name': key, 'value': str(value), 'separator': separator} + separator = '&' diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hgweb/wsgicgi.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hgweb/wsgicgi.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,70 @@ +# hgweb/wsgicgi.py - CGI->WSGI translator +# +# Copyright 2006 Eric Hopper +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. +# +# This was originally copied from the public domain code at +# http://www.python.org/dev/peps/pep-0333/#the-server-gateway-side + +import os, sys +from upmana.mercurial import util + +def launch(application): + util.set_binary(sys.stdin) + util.set_binary(sys.stdout) + + environ = dict(os.environ.iteritems()) + environ.setdefault('PATH_INFO', '') + if '.cgi' in environ['PATH_INFO']: + environ['PATH_INFO'] = environ['PATH_INFO'].split('.cgi', 1)[1] + + environ['wsgi.input'] = sys.stdin + environ['wsgi.errors'] = sys.stderr + environ['wsgi.version'] = (1, 0) + environ['wsgi.multithread'] = False + environ['wsgi.multiprocess'] = True + environ['wsgi.run_once'] = True + + if environ.get('HTTPS','off').lower() in ('on','1','yes'): + environ['wsgi.url_scheme'] = 'https' + else: + environ['wsgi.url_scheme'] = 'http' + + headers_set = [] + headers_sent = [] + out = sys.stdout + + def write(data): + if not headers_set: + raise AssertionError("write() before start_response()") + + elif not headers_sent: + # Before the first output, send the stored headers + status, response_headers = headers_sent[:] = headers_set + out.write('Status: %s\r\n' % status) + for header in response_headers: + out.write('%s: %s\r\n' % header) + out.write('\r\n') + + out.write(data) + out.flush() + + def start_response(status, response_headers, exc_info=None): + if exc_info: + try: + if headers_sent: + # Re-raise original exception if headers sent + raise exc_info[0](exc_info[1], exc_info[2]) + finally: + exc_info = None # avoid dangling circular ref + elif headers_set: + raise AssertionError("Headers already set!") + + headers_set[:] = [status, response_headers] + return write + + content = application(environ, start_response) + for chunk in content: + write(chunk) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/hook.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/hook.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,127 @@ +# hook.py - hook support for mercurial +# +# Copyright 2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import os, sys +import extensions, util + +def _pythonhook(ui, repo, name, hname, funcname, args, throw): + '''call python hook. hook is callable object, looked up as + name in python module. if callable returns "true", hook + fails, else passes. if hook raises exception, treated as + hook failure. exception propagates if throw is "true". + + reason for "true" meaning "hook failed" is so that + unmodified commands (e.g. mercurial.commands.update) can + be run as hooks without wrappers to convert return values.''' + + ui.note(_("calling hook %s: %s\n") % (hname, funcname)) + obj = funcname + if not hasattr(obj, '__call__'): + d = funcname.rfind('.') + if d == -1: + raise util.Abort(_('%s hook is invalid ("%s" not in ' + 'a module)') % (hname, funcname)) + modname = funcname[:d] + try: + obj = __import__(modname) + except ImportError: + try: + # extensions are loaded with hgext_ prefix + obj = __import__("hgext_%s" % modname) + except ImportError: + raise util.Abort(_('%s hook is invalid ' + '(import of "%s" failed)') % + (hname, modname)) + try: + for p in funcname.split('.')[1:]: + obj = getattr(obj, p) + except AttributeError: + raise util.Abort(_('%s hook is invalid ' + '("%s" is not defined)') % + (hname, funcname)) + if not hasattr(obj, '__call__'): + raise util.Abort(_('%s hook is invalid ' + '("%s" is not callable)') % + (hname, funcname)) + try: + r = obj(ui=ui, repo=repo, hooktype=name, **args) + except KeyboardInterrupt: + raise + except Exception, exc: + if isinstance(exc, util.Abort): + ui.warn(_('error: %s hook failed: %s\n') % + (hname, exc.args[0])) + else: + ui.warn(_('error: %s hook raised an exception: ' + '%s\n') % (hname, exc)) + if throw: + raise + ui.traceback() + return True + if r: + if throw: + raise util.Abort(_('%s hook failed') % hname) + ui.warn(_('warning: %s hook failed\n') % hname) + return r + +def _exthook(ui, repo, name, cmd, args, throw): + ui.note(_("running hook %s: %s\n") % (name, cmd)) + + env = {} + for k, v in args.iteritems(): + if hasattr(v, '__call__'): + v = v() + env['HG_' + k.upper()] = v + + if repo: + cwd = repo.root + else: + cwd = os.getcwd() + r = util.system(cmd, environ=env, cwd=cwd) + if r: + desc, r = util.explain_exit(r) + if throw: + raise util.Abort(_('%s hook %s') % (name, desc)) + ui.warn(_('warning: %s hook %s\n') % (name, desc)) + return r + +_redirect = False +def redirect(state): + global _redirect + _redirect = state + +def hook(ui, repo, name, throw=False, **args): + r = False + + if _redirect: + # temporarily redirect stdout to stderr + oldstdout = os.dup(sys.__stdout__.fileno()) + os.dup2(sys.__stderr__.fileno(), sys.__stdout__.fileno()) + + try: + for hname, cmd in ui.configitems('hooks'): + if hname.split('.')[0] != name or not cmd: + continue + if hasattr(cmd, '__call__'): + r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r + elif cmd.startswith('python:'): + if cmd.count(':') == 2: + path, cmd = cmd[7:].split(':') + mod = extensions.loadpath(path, 'hgkook.%s' % hname) + hookfn = getattr(mod, cmd) + else: + hookfn = cmd[7:].strip() + r = _pythonhook(ui, repo, name, hname, hookfn, args, throw) or r + else: + r = _exthook(ui, repo, hname, cmd, args, throw) or r + finally: + if _redirect: + os.dup2(oldstdout, sys.__stdout__.fileno()) + os.close(oldstdout) + + return r diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/httprepo.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/httprepo.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,258 @@ +# httprepo.py - HTTP repository proxy classes for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# Copyright 2006 Vadim Gelfer +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import bin, hex, nullid +from i18n import _ +import repo, changegroup, statichttprepo, error, url, util +import os, urllib, urllib2, urlparse, zlib, httplib +import errno, socket + +def zgenerator(f): + zd = zlib.decompressobj() + try: + for chunk in util.filechunkiter(f): + yield zd.decompress(chunk) + except httplib.HTTPException: + raise IOError(None, _('connection ended unexpectedly')) + yield zd.flush() + +class httprepository(repo.repository): + def __init__(self, ui, path): + self.path = path + self.caps = None + self.handler = None + scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path) + if query or frag: + raise util.Abort(_('unsupported URL component: "%s"') % + (query or frag)) + + # urllib cannot handle URLs with embedded user or passwd + self._url, authinfo = url.getauthinfo(path) + + self.ui = ui + self.ui.debug(_('using %s\n') % self._url) + + self.urlopener = url.opener(ui, authinfo) + + def __del__(self): + for h in self.urlopener.handlers: + h.close() + if hasattr(h, "close_all"): + h.close_all() + + def url(self): + return self.path + + # look up capabilities only when needed + + def get_caps(self): + if self.caps is None: + try: + self.caps = set(self.do_read('capabilities').split()) + except error.RepoError: + self.caps = set() + self.ui.debug(_('capabilities: %s\n') % + (' '.join(self.caps or ['none']))) + return self.caps + + capabilities = property(get_caps) + + def lock(self): + raise util.Abort(_('operation not supported over http')) + + def do_cmd(self, cmd, **args): + data = args.pop('data', None) + headers = args.pop('headers', {}) + self.ui.debug(_("sending %s command\n") % cmd) + q = {"cmd": cmd} + q.update(args) + qs = '?%s' % urllib.urlencode(q) + cu = "%s%s" % (self._url, qs) + try: + if data: + self.ui.debug(_("sending %s bytes\n") % len(data)) + resp = self.urlopener.open(urllib2.Request(cu, data, headers)) + except urllib2.HTTPError, inst: + if inst.code == 401: + raise util.Abort(_('authorization failed')) + raise + except httplib.HTTPException, inst: + self.ui.debug(_('http error while sending %s command\n') % cmd) + self.ui.traceback() + raise IOError(None, inst) + except IndexError: + # this only happens with Python 2.3, later versions raise URLError + raise util.Abort(_('http error, possibly caused by proxy setting')) + # record the url we got redirected to + resp_url = resp.geturl() + if resp_url.endswith(qs): + resp_url = resp_url[:-len(qs)] + if self._url != resp_url: + self.ui.status(_('real URL is %s\n') % resp_url) + self._url = resp_url + try: + proto = resp.getheader('content-type') + except AttributeError: + proto = resp.headers['content-type'] + + safeurl = url.hidepassword(self._url) + # accept old "text/plain" and "application/hg-changegroup" for now + if not (proto.startswith('application/mercurial-') or + proto.startswith('text/plain') or + proto.startswith('application/hg-changegroup')): + self.ui.debug(_("requested URL: '%s'\n") % url.hidepassword(cu)) + raise error.RepoError(_("'%s' does not appear to be an hg repository") + % safeurl) + + if proto.startswith('application/mercurial-'): + try: + version = proto.split('-', 1)[1] + version_info = tuple([int(n) for n in version.split('.')]) + except ValueError: + raise error.RepoError(_("'%s' sent a broken Content-Type " + "header (%s)") % (safeurl, proto)) + if version_info > (0, 1): + raise error.RepoError(_("'%s' uses newer protocol %s") % + (safeurl, version)) + + return resp + + def do_read(self, cmd, **args): + fp = self.do_cmd(cmd, **args) + try: + return fp.read() + finally: + # if using keepalive, allow connection to be reused + fp.close() + + def lookup(self, key): + self.requirecap('lookup', _('look up remote revision')) + d = self.do_cmd("lookup", key = key).read() + success, data = d[:-1].split(' ', 1) + if int(success): + return bin(data) + raise error.RepoError(data) + + def heads(self): + d = self.do_read("heads") + try: + return map(bin, d[:-1].split(" ")) + except: + raise error.ResponseError(_("unexpected response:"), d) + + def branchmap(self): + d = self.do_read("branchmap") + try: + branchmap = {} + for branchpart in d.splitlines(): + branchheads = branchpart.split(' ') + branchname = urllib.unquote(branchheads[0]) + branchheads = [bin(x) for x in branchheads[1:]] + branchmap[branchname] = branchheads + return branchmap + except: + raise error.ResponseError(_("unexpected response:"), d) + + def branches(self, nodes): + n = " ".join(map(hex, nodes)) + d = self.do_read("branches", nodes=n) + try: + br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ] + return br + except: + raise error.ResponseError(_("unexpected response:"), d) + + def between(self, pairs): + batch = 8 # avoid giant requests + r = [] + for i in xrange(0, len(pairs), batch): + n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]]) + d = self.do_read("between", pairs=n) + try: + r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ] + except: + raise error.ResponseError(_("unexpected response:"), d) + return r + + def changegroup(self, nodes, kind): + n = " ".join(map(hex, nodes)) + f = self.do_cmd("changegroup", roots=n) + return util.chunkbuffer(zgenerator(f)) + + def changegroupsubset(self, bases, heads, source): + self.requirecap('changegroupsubset', _('look up remote changes')) + baselst = " ".join([hex(n) for n in bases]) + headlst = " ".join([hex(n) for n in heads]) + f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst) + return util.chunkbuffer(zgenerator(f)) + + def unbundle(self, cg, heads, source): + # have to stream bundle to a temp file because we do not have + # http 1.1 chunked transfer. + + type = "" + types = self.capable('unbundle') + # servers older than d1b16a746db6 will send 'unbundle' as a + # boolean capability + try: + types = types.split(',') + except AttributeError: + types = [""] + if types: + for x in types: + if x in changegroup.bundletypes: + type = x + break + + tempname = changegroup.writebundle(cg, None, type) + fp = url.httpsendfile(tempname, "rb") + try: + try: + resp = self.do_read( + 'unbundle', data=fp, + headers={'Content-Type': 'application/octet-stream'}, + heads=' '.join(map(hex, heads))) + resp_code, output = resp.split('\n', 1) + try: + ret = int(resp_code) + except ValueError, err: + raise error.ResponseError( + _('push failed (unexpected response):'), resp) + self.ui.write(output) + return ret + except socket.error, err: + if err[0] in (errno.ECONNRESET, errno.EPIPE): + raise util.Abort(_('push failed: %s') % err[1]) + raise util.Abort(err[1]) + finally: + fp.close() + os.unlink(tempname) + + def stream_out(self): + return self.do_cmd('stream_out') + +class httpsrepository(httprepository): + def __init__(self, ui, path): + if not url.has_https: + raise util.Abort(_('Python support for SSL and HTTPS ' + 'is not installed')) + httprepository.__init__(self, ui, path) + +def instance(ui, path, create): + if create: + raise util.Abort(_('cannot create new http repository')) + try: + if path.startswith('https:'): + inst = httpsrepository(ui, path) + else: + inst = httprepository(ui, path) + inst.between([(nullid, nullid)]) + return inst + except error.RepoError: + ui.note('(falling back to static-http)\n') + return statichttprepo.instance(ui, "static-" + path, create) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/i18n.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/i18n.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,48 @@ +# i18n.py - internationalization support for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import encoding +import gettext, sys, os + +# modelled after templater.templatepath: +if hasattr(sys, 'frozen'): + module = sys.executable +else: + module = __file__ + +base = os.path.dirname(module) +for dir in ('.', '..'): + localedir = os.path.normpath(os.path.join(base, dir, 'locale')) + if os.path.isdir(localedir): + break + +t = gettext.translation('hg', localedir, fallback=True) + +def gettext(message): + """Translate message. + + The message is looked up in the catalog to get a Unicode string, + which is encoded in the local encoding before being returned. + + Important: message is restricted to characters in the encoding + given by sys.getdefaultencoding() which is most likely 'ascii'. + """ + # If message is None, t.ugettext will return u'None' as the + # translation whereas our callers expect us to return None. + if message is None: + return message + + # We cannot just run the text through encoding.tolocal since that + # leads to infinite recursion when encoding._encoding is invalid. + try: + u = t.ugettext(message) + return u.encode(encoding.encoding, "replace") + except LookupError: + return message + +_ = gettext + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/ignore.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/ignore.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,92 @@ +# ignore.py - ignored file handling for mercurial +# +# Copyright 2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from i18n import _ +import util, match +import re + +_commentre = None + +def _parselines(fp): + for line in fp: + if "#" in line: + global _commentre + if not _commentre: + _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*') + # remove comments prefixed by an even number of escapes + line = _commentre.sub(r'\1', line) + # fixup properly escaped comments that survived the above + line = line.replace("\\#", "#") + line = line.rstrip() + if line: + yield line + +def ignore(root, files, warn): + '''return the contents of .hgignore files as a list of patterns. + + the files parsed for patterns include: + .hgignore in the repository root + any additional files specified in the [ui] section of ~/.hgrc + + trailing white space is dropped. + the escape character is backslash. + comments start with #. + empty lines are skipped. + + lines can be of the following formats: + + syntax: regexp # defaults following lines to non-rooted regexps + syntax: glob # defaults following lines to non-rooted globs + re:pattern # non-rooted regular expression + glob:pattern # non-rooted glob + pattern # pattern of the current default type''' + + syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'} + pats = {} + for f in files: + try: + pats[f] = [] + fp = open(f) + syntax = 'relre:' + for line in _parselines(fp): + if line.startswith('syntax:'): + s = line[7:].strip() + try: + syntax = syntaxes[s] + except KeyError: + warn(_("%s: ignoring invalid syntax '%s'\n") % (f, s)) + continue + pat = syntax + line + for s, rels in syntaxes.iteritems(): + if line.startswith(rels): + pat = line + break + elif line.startswith(s+':'): + pat = rels + line[len(s)+1:] + break + pats[f].append(pat) + except IOError, inst: + if f != files[0]: + warn(_("skipping unreadable ignore file '%s': %s\n") % + (f, inst.strerror)) + + allpats = [] + [allpats.extend(patlist) for patlist in pats.values()] + if not allpats: + return util.never + + try: + ignorefunc = match.match(root, '', [], allpats) + except util.Abort: + # Re-raise an exception where the src is the right file + for f, patlist in pats.iteritems(): + try: + match.match(root, '', [], patlist) + except util.Abort, inst: + raise util.Abort('%s: %s' % (f, inst[0])) + + return ignorefunc diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/keepalive.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/keepalive.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,671 @@ +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +# This file is part of urlgrabber, a high-level cross-protocol url-grabber +# Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko + +# Modified by Benoit Boissinot: +# - fix for digest auth (inspired from urllib2.py @ Python v2.4) +# Modified by Dirkjan Ochtman: +# - import md5 function from a local util module +# Modified by Martin Geisler: +# - moved md5 function from local util module to this module + +"""An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive. + +>>> import urllib2 +>>> from keepalive import HTTPHandler +>>> keepalive_handler = HTTPHandler() +>>> opener = urllib2.build_opener(keepalive_handler) +>>> urllib2.install_opener(opener) +>>> +>>> fo = urllib2.urlopen('http://www.python.org') + +If a connection to a given host is requested, and all of the existing +connections are still in use, another connection will be opened. If +the handler tries to use an existing connection but it fails in some +way, it will be closed and removed from the pool. + +To remove the handler, simply re-run build_opener with no arguments, and +install that opener. + +You can explicitly close connections by using the close_connection() +method of the returned file-like object (described below) or you can +use the handler methods: + + close_connection(host) + close_all() + open_connections() + +NOTE: using the close_connection and close_all methods of the handler +should be done with care when using multiple threads. + * there is nothing that prevents another thread from creating new + connections immediately after connections are closed + * no checks are done to prevent in-use connections from being closed + +>>> keepalive_handler.close_all() + +EXTRA ATTRIBUTES AND METHODS + + Upon a status of 200, the object returned has a few additional + attributes and methods, which should not be used if you want to + remain consistent with the normal urllib2-returned objects: + + close_connection() - close the connection to the host + readlines() - you know, readlines() + status - the return status (ie 404) + reason - english translation of status (ie 'File not found') + + If you want the best of both worlds, use this inside an + AttributeError-catching try: + + >>> try: status = fo.status + >>> except AttributeError: status = None + + Unfortunately, these are ONLY there if status == 200, so it's not + easy to distinguish between non-200 responses. The reason is that + urllib2 tries to do clever things with error codes 301, 302, 401, + and 407, and it wraps the object upon return. + + For python versions earlier than 2.4, you can avoid this fancy error + handling by setting the module-level global HANDLE_ERRORS to zero. + You see, prior to 2.4, it's the HTTP Handler's job to determine what + to handle specially, and what to just pass up. HANDLE_ERRORS == 0 + means "pass everything up". In python 2.4, however, this job no + longer belongs to the HTTP Handler and is now done by a NEW handler, + HTTPErrorProcessor. Here's the bottom line: + + python version < 2.4 + HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as + errors + HANDLE_ERRORS == 0 pass everything up, error processing is + left to the calling code + python version >= 2.4 + HANDLE_ERRORS == 1 pass up 200, treat the rest as errors + HANDLE_ERRORS == 0 (default) pass everything up, let the + other handlers (specifically, + HTTPErrorProcessor) decide what to do + + In practice, setting the variable either way makes little difference + in python 2.4, so for the most consistent behavior across versions, + you probably just want to use the defaults, which will give you + exceptions on errors. + +""" + +# $Id: keepalive.py,v 1.14 2006/04/04 21:00:32 mstenner Exp $ + +import urllib2 +import httplib +import socket +import thread + +DEBUG = None + +import sys +if sys.version_info < (2, 4): HANDLE_ERRORS = 1 +else: HANDLE_ERRORS = 0 + +class ConnectionManager: + """ + The connection manager must be able to: + * keep track of all existing + """ + def __init__(self): + self._lock = thread.allocate_lock() + self._hostmap = {} # map hosts to a list of connections + self._connmap = {} # map connections to host + self._readymap = {} # map connection to ready state + + def add(self, host, connection, ready): + self._lock.acquire() + try: + if not host in self._hostmap: self._hostmap[host] = [] + self._hostmap[host].append(connection) + self._connmap[connection] = host + self._readymap[connection] = ready + finally: + self._lock.release() + + def remove(self, connection): + self._lock.acquire() + try: + try: + host = self._connmap[connection] + except KeyError: + pass + else: + del self._connmap[connection] + del self._readymap[connection] + self._hostmap[host].remove(connection) + if not self._hostmap[host]: del self._hostmap[host] + finally: + self._lock.release() + + def set_ready(self, connection, ready): + try: self._readymap[connection] = ready + except KeyError: pass + + def get_ready_conn(self, host): + conn = None + self._lock.acquire() + try: + if host in self._hostmap: + for c in self._hostmap[host]: + if self._readymap[c]: + self._readymap[c] = 0 + conn = c + break + finally: + self._lock.release() + return conn + + def get_all(self, host=None): + if host: + return list(self._hostmap.get(host, [])) + else: + return dict(self._hostmap) + +class KeepAliveHandler: + def __init__(self): + self._cm = ConnectionManager() + + #### Connection Management + def open_connections(self): + """return a list of connected hosts and the number of connections + to each. [('foo.com:80', 2), ('bar.org', 1)]""" + return [(host, len(li)) for (host, li) in self._cm.get_all().items()] + + def close_connection(self, host): + """close connection(s) to + host is the host:port spec, as in 'www.cnn.com:8080' as passed in. + no error occurs if there is no connection to that host.""" + for h in self._cm.get_all(host): + self._cm.remove(h) + h.close() + + def close_all(self): + """close all open connections""" + for host, conns in self._cm.get_all().iteritems(): + for h in conns: + self._cm.remove(h) + h.close() + + def _request_closed(self, request, host, connection): + """tells us that this request is now closed and the the + connection is ready for another request""" + self._cm.set_ready(connection, 1) + + def _remove_connection(self, host, connection, close=0): + if close: connection.close() + self._cm.remove(connection) + + #### Transaction Execution + def http_open(self, req): + return self.do_open(HTTPConnection, req) + + def do_open(self, http_class, req): + host = req.get_host() + if not host: + raise urllib2.URLError('no host given') + + try: + h = self._cm.get_ready_conn(host) + while h: + r = self._reuse_connection(h, req, host) + + # if this response is non-None, then it worked and we're + # done. Break out, skipping the else block. + if r: break + + # connection is bad - possibly closed by server + # discard it and ask for the next free connection + h.close() + self._cm.remove(h) + h = self._cm.get_ready_conn(host) + else: + # no (working) free connections were found. Create a new one. + h = http_class(host) + if DEBUG: DEBUG.info("creating new connection to %s (%d)", + host, id(h)) + self._cm.add(host, h, 0) + self._start_transaction(h, req) + r = h.getresponse() + except (socket.error, httplib.HTTPException), err: + raise urllib2.URLError(err) + + # if not a persistent connection, don't try to reuse it + if r.will_close: self._cm.remove(h) + + if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason) + r._handler = self + r._host = host + r._url = req.get_full_url() + r._connection = h + r.code = r.status + r.headers = r.msg + r.msg = r.reason + + if r.status == 200 or not HANDLE_ERRORS: + return r + else: + return self.parent.error('http', req, r, + r.status, r.msg, r.headers) + + def _reuse_connection(self, h, req, host): + """start the transaction with a re-used connection + return a response object (r) upon success or None on failure. + This DOES not close or remove bad connections in cases where + it returns. However, if an unexpected exception occurs, it + will close and remove the connection before re-raising. + """ + try: + self._start_transaction(h, req) + r = h.getresponse() + # note: just because we got something back doesn't mean it + # worked. We'll check the version below, too. + except (socket.error, httplib.HTTPException): + r = None + except: + # adding this block just in case we've missed + # something we will still raise the exception, but + # lets try and close the connection and remove it + # first. We previously got into a nasty loop + # where an exception was uncaught, and so the + # connection stayed open. On the next try, the + # same exception was raised, etc. The tradeoff is + # that it's now possible this call will raise + # a DIFFERENT exception + if DEBUG: DEBUG.error("unexpected exception - closing " + \ + "connection to %s (%d)", host, id(h)) + self._cm.remove(h) + h.close() + raise + + if r is None or r.version == 9: + # httplib falls back to assuming HTTP 0.9 if it gets a + # bad header back. This is most likely to happen if + # the socket has been closed by the server since we + # last used the connection. + if DEBUG: DEBUG.info("failed to re-use connection to %s (%d)", + host, id(h)) + r = None + else: + if DEBUG: DEBUG.info("re-using connection to %s (%d)", host, id(h)) + + return r + + def _start_transaction(self, h, req): + # What follows mostly reimplements HTTPConnection.request() + # except it adds self.parent.addheaders in the mix. + headers = req.headers.copy() + if sys.version_info >= (2, 4): + headers.update(req.unredirected_hdrs) + headers.update(self.parent.addheaders) + headers = dict((n.lower(), v) for n,v in headers.items()) + skipheaders = {} + for n in ('host', 'accept-encoding'): + if n in headers: + skipheaders['skip_' + n.replace('-', '_')] = 1 + try: + if req.has_data(): + data = req.get_data() + h.putrequest('POST', req.get_selector(), **skipheaders) + if 'content-type' not in headers: + h.putheader('Content-type', + 'application/x-www-form-urlencoded') + if 'content-length' not in headers: + h.putheader('Content-length', '%d' % len(data)) + else: + h.putrequest('GET', req.get_selector(), **skipheaders) + except (socket.error), err: + raise urllib2.URLError(err) + for k, v in headers.items(): + h.putheader(k, v) + h.endheaders() + if req.has_data(): + h.send(data) + +class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler): + pass + +class HTTPResponse(httplib.HTTPResponse): + # we need to subclass HTTPResponse in order to + # 1) add readline() and readlines() methods + # 2) add close_connection() methods + # 3) add info() and geturl() methods + + # in order to add readline(), read must be modified to deal with a + # buffer. example: readline must read a buffer and then spit back + # one line at a time. The only real alternative is to read one + # BYTE at a time (ick). Once something has been read, it can't be + # put back (ok, maybe it can, but that's even uglier than this), + # so if you THEN do a normal read, you must first take stuff from + # the buffer. + + # the read method wraps the original to accomodate buffering, + # although read() never adds to the buffer. + # Both readline and readlines have been stolen with almost no + # modification from socket.py + + + def __init__(self, sock, debuglevel=0, strict=0, method=None): + if method: # the httplib in python 2.3 uses the method arg + httplib.HTTPResponse.__init__(self, sock, debuglevel, method) + else: # 2.2 doesn't + httplib.HTTPResponse.__init__(self, sock, debuglevel) + self.fileno = sock.fileno + self.code = None + self._rbuf = '' + self._rbufsize = 8096 + self._handler = None # inserted by the handler later + self._host = None # (same) + self._url = None # (same) + self._connection = None # (same) + + _raw_read = httplib.HTTPResponse.read + + def close(self): + if self.fp: + self.fp.close() + self.fp = None + if self._handler: + self._handler._request_closed(self, self._host, + self._connection) + + def close_connection(self): + self._handler._remove_connection(self._host, self._connection, close=1) + self.close() + + def info(self): + return self.headers + + def geturl(self): + return self._url + + def read(self, amt=None): + # the _rbuf test is only in this first if for speed. It's not + # logically necessary + if self._rbuf and not amt is None: + L = len(self._rbuf) + if amt > L: + amt -= L + else: + s = self._rbuf[:amt] + self._rbuf = self._rbuf[amt:] + return s + + s = self._rbuf + self._raw_read(amt) + self._rbuf = '' + return s + + # stolen from Python SVN #68532 to fix issue1088 + def _read_chunked(self, amt): + chunk_left = self.chunk_left + value = '' + + # XXX This accumulates chunks by repeated string concatenation, + # which is not efficient as the number or size of chunks gets big. + while True: + if chunk_left is None: + line = self.fp.readline() + i = line.find(';') + if i >= 0: + line = line[:i] # strip chunk-extensions + try: + chunk_left = int(line, 16) + except ValueError: + # close the connection as protocol synchronisation is + # probably lost + self.close() + raise httplib.IncompleteRead(value) + if chunk_left == 0: + break + if amt is None: + value += self._safe_read(chunk_left) + elif amt < chunk_left: + value += self._safe_read(amt) + self.chunk_left = chunk_left - amt + return value + elif amt == chunk_left: + value += self._safe_read(amt) + self._safe_read(2) # toss the CRLF at the end of the chunk + self.chunk_left = None + return value + else: + value += self._safe_read(chunk_left) + amt -= chunk_left + + # we read the whole chunk, get another + self._safe_read(2) # toss the CRLF at the end of the chunk + chunk_left = None + + # read and discard trailer up to the CRLF terminator + ### note: we shouldn't have any trailers! + while True: + line = self.fp.readline() + if not line: + # a vanishingly small number of sites EOF without + # sending the trailer + break + if line == '\r\n': + break + + # we read everything; close the "file" + self.close() + + return value + + def readline(self, limit=-1): + i = self._rbuf.find('\n') + while i < 0 and not (0 < limit <= len(self._rbuf)): + new = self._raw_read(self._rbufsize) + if not new: break + i = new.find('\n') + if i >= 0: i = i + len(self._rbuf) + self._rbuf = self._rbuf + new + if i < 0: i = len(self._rbuf) + else: i = i+1 + if 0 <= limit < len(self._rbuf): i = limit + data, self._rbuf = self._rbuf[:i], self._rbuf[i:] + return data + + def readlines(self, sizehint = 0): + total = 0 + list = [] + while 1: + line = self.readline() + if not line: break + list.append(line) + total += len(line) + if sizehint and total >= sizehint: + break + return list + + +class HTTPConnection(httplib.HTTPConnection): + # use the modified response class + response_class = HTTPResponse + +######################################################################### +##### TEST FUNCTIONS +######################################################################### + +def error_handler(url): + global HANDLE_ERRORS + orig = HANDLE_ERRORS + keepalive_handler = HTTPHandler() + opener = urllib2.build_opener(keepalive_handler) + urllib2.install_opener(opener) + pos = {0: 'off', 1: 'on'} + for i in (0, 1): + print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i) + HANDLE_ERRORS = i + try: + fo = urllib2.urlopen(url) + fo.read() + fo.close() + try: status, reason = fo.status, fo.reason + except AttributeError: status, reason = None, None + except IOError, e: + print " EXCEPTION: %s" % e + raise + else: + print " status = %s, reason = %s" % (status, reason) + HANDLE_ERRORS = orig + hosts = keepalive_handler.open_connections() + print "open connections:", hosts + keepalive_handler.close_all() + +def md5(s): + try: + from hashlib import md5 as _md5 + except ImportError: + from md5 import md5 as _md5 + global md5 + md5 = _md5 + return _md5(s) + +def continuity(url): + format = '%25s: %s' + + # first fetch the file with the normal http handler + opener = urllib2.build_opener() + urllib2.install_opener(opener) + fo = urllib2.urlopen(url) + foo = fo.read() + fo.close() + m = md5.new(foo) + print format % ('normal urllib', m.hexdigest()) + + # now install the keepalive handler and try again + opener = urllib2.build_opener(HTTPHandler()) + urllib2.install_opener(opener) + + fo = urllib2.urlopen(url) + foo = fo.read() + fo.close() + m = md5.new(foo) + print format % ('keepalive read', m.hexdigest()) + + fo = urllib2.urlopen(url) + foo = '' + while 1: + f = fo.readline() + if f: foo = foo + f + else: break + fo.close() + m = md5.new(foo) + print format % ('keepalive readline', m.hexdigest()) + +def comp(N, url): + print ' making %i connections to:\n %s' % (N, url) + + sys.stdout.write(' first using the normal urllib handlers') + # first use normal opener + opener = urllib2.build_opener() + urllib2.install_opener(opener) + t1 = fetch(N, url) + print ' TIME: %.3f s' % t1 + + sys.stdout.write(' now using the keepalive handler ') + # now install the keepalive handler and try again + opener = urllib2.build_opener(HTTPHandler()) + urllib2.install_opener(opener) + t2 = fetch(N, url) + print ' TIME: %.3f s' % t2 + print ' improvement factor: %.2f' % (t1/t2, ) + +def fetch(N, url, delay=0): + import time + lens = [] + starttime = time.time() + for i in range(N): + if delay and i > 0: time.sleep(delay) + fo = urllib2.urlopen(url) + foo = fo.read() + fo.close() + lens.append(len(foo)) + diff = time.time() - starttime + + j = 0 + for i in lens[1:]: + j = j + 1 + if not i == lens[0]: + print "WARNING: inconsistent length on read %i: %i" % (j, i) + + return diff + +def test_timeout(url): + global DEBUG + dbbackup = DEBUG + class FakeLogger: + def debug(self, msg, *args): print msg % args + info = warning = error = debug + DEBUG = FakeLogger() + print " fetching the file to establish a connection" + fo = urllib2.urlopen(url) + data1 = fo.read() + fo.close() + + i = 20 + print " waiting %i seconds for the server to close the connection" % i + while i > 0: + sys.stdout.write('\r %2i' % i) + sys.stdout.flush() + time.sleep(1) + i -= 1 + sys.stderr.write('\r') + + print " fetching the file a second time" + fo = urllib2.urlopen(url) + data2 = fo.read() + fo.close() + + if data1 == data2: + print ' data are identical' + else: + print ' ERROR: DATA DIFFER' + + DEBUG = dbbackup + + +def test(url, N=10): + print "checking error hander (do this on a non-200)" + try: error_handler(url) + except IOError: + print "exiting - exception will prevent further tests" + sys.exit() + print + print "performing continuity test (making sure stuff isn't corrupted)" + continuity(url) + print + print "performing speed comparison" + comp(N, url) + print + print "performing dropped-connection check" + test_timeout(url) + +if __name__ == '__main__': + import time + import sys + try: + N = int(sys.argv[1]) + url = sys.argv[2] + except: + print "%s " % sys.argv[0] + else: + test(url, N) diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/localrepo.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/localrepo.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,2191 @@ +# localrepo.py - read/write repository class for mercurial +# +# Copyright 2005-2007 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +from node import bin, hex, nullid, nullrev, short +from i18n import _ +import repo, changegroup, subrepo +import changelog, dirstate, filelog, manifest, context +import lock, transaction, store, encoding +import util, extensions, hook, error +import match as match_ +import merge as merge_ +from lock import release +import weakref, stat, errno, os, time, inspect +propertycache = util.propertycache + +class localrepository(repo.repository): + capabilities = set(('lookup', 'changegroupsubset', 'branchmap')) + supported = set('revlogv1 store fncache shared'.split()) + + def __init__(self, baseui, path=None, create=0): + repo.repository.__init__(self) + self.root = os.path.realpath(path) + self.path = os.path.join(self.root, ".hg") + self.origroot = path + self.opener = util.opener(self.path) + self.wopener = util.opener(self.root) + self.baseui = baseui + self.ui = baseui.copy() + + try: + self.ui.readconfig(self.join("hgrc"), self.root) + extensions.loadall(self.ui) + except IOError: + pass + + if not os.path.isdir(self.path): + if create: + if not os.path.exists(path): + os.mkdir(path) + os.mkdir(self.path) + requirements = ["revlogv1"] + if self.ui.configbool('format', 'usestore', True): + os.mkdir(os.path.join(self.path, "store")) + requirements.append("store") + if self.ui.configbool('format', 'usefncache', True): + requirements.append("fncache") + # create an invalid changelog + self.opener("00changelog.i", "a").write( + '\0\0\0\2' # represents revlogv2 + ' dummy changelog to prevent using the old repo layout' + ) + reqfile = self.opener("requires", "w") + for r in requirements: + reqfile.write("%s\n" % r) + reqfile.close() + else: + raise error.RepoError(_("repository %s not found") % path) + elif create: + raise error.RepoError(_("repository %s already exists") % path) + else: + # find requirements + requirements = set() + try: + requirements = set(self.opener("requires").read().splitlines()) + except IOError, inst: + if inst.errno != errno.ENOENT: + raise + for r in requirements - self.supported: + raise error.RepoError(_("requirement '%s' not supported") % r) + + self.sharedpath = self.path + try: + s = os.path.realpath(self.opener("sharedpath").read()) + if not os.path.exists(s): + raise error.RepoError( + _('.hg/sharedpath points to nonexistent directory %s') % s) + self.sharedpath = s + except IOError, inst: + if inst.errno != errno.ENOENT: + raise + + self.store = store.store(requirements, self.sharedpath, util.opener) + self.spath = self.store.path + self.sopener = self.store.opener + self.sjoin = self.store.join + self.opener.createmode = self.store.createmode + + self.tagscache = None + self._tagstypecache = None + self.branchcache = None + self._ubranchcache = None # UTF-8 version of branchcache + self._branchcachetip = None + self.nodetagscache = None + self.filterpats = {} + self._datafilters = {} + self._transref = self._lockref = self._wlockref = None + + @propertycache + def changelog(self): + c = changelog.changelog(self.sopener) + if 'HG_PENDING' in os.environ: + p = os.environ['HG_PENDING'] + if p.startswith(self.root): + c.readpending('00changelog.i.a') + self.sopener.defversion = c.version + return c + + @propertycache + def manifest(self): + return manifest.manifest(self.sopener) + + @propertycache + def dirstate(self): + return dirstate.dirstate(self.opener, self.ui, self.root) + + def __getitem__(self, changeid): + if changeid is None: + return context.workingctx(self) + return context.changectx(self, changeid) + + def __nonzero__(self): + return True + + def __len__(self): + return len(self.changelog) + + def __iter__(self): + for i in xrange(len(self)): + yield i + + def url(self): + return 'file:' + self.root + + def hook(self, name, throw=False, **args): + return hook.hook(self.ui, self, name, throw, **args) + + tag_disallowed = ':\r\n' + + def _tag(self, names, node, message, local, user, date, extra={}): + if isinstance(names, str): + allchars = names + names = (names,) + else: + allchars = ''.join(names) + for c in self.tag_disallowed: + if c in allchars: + raise util.Abort(_('%r cannot be used in a tag name') % c) + + for name in names: + self.hook('pretag', throw=True, node=hex(node), tag=name, + local=local) + + def writetags(fp, names, munge, prevtags): + fp.seek(0, 2) + if prevtags and prevtags[-1] != '\n': + fp.write('\n') + for name in names: + m = munge and munge(name) or name + if self._tagstypecache and name in self._tagstypecache: + old = self.tagscache.get(name, nullid) + fp.write('%s %s\n' % (hex(old), m)) + fp.write('%s %s\n' % (hex(node), m)) + fp.close() + + prevtags = '' + if local: + try: + fp = self.opener('localtags', 'r+') + except IOError: + fp = self.opener('localtags', 'a') + else: + prevtags = fp.read() + + # local tags are stored in the current charset + writetags(fp, names, None, prevtags) + for name in names: + self.hook('tag', node=hex(node), tag=name, local=local) + return + + try: + fp = self.wfile('.hgtags', 'rb+') + except IOError: + fp = self.wfile('.hgtags', 'ab') + else: + prevtags = fp.read() + + # committed tags are stored in UTF-8 + writetags(fp, names, encoding.fromlocal, prevtags) + + if '.hgtags' not in self.dirstate: + self.add(['.hgtags']) + + m = match_.exact(self.root, '', ['.hgtags']) + tagnode = self.commit(message, user, date, extra=extra, match=m) + + for name in names: + self.hook('tag', node=hex(node), tag=name, local=local) + + return tagnode + + def tag(self, names, node, message, local, user, date): + '''tag a revision with one or more symbolic names. + + names is a list of strings or, when adding a single tag, names may be a + string. + + if local is True, the tags are stored in a per-repository file. + otherwise, they are stored in the .hgtags file, and a new + changeset is committed with the change. + + keyword arguments: + + local: whether to store tags in non-version-controlled file + (default False) + + message: commit message to use if committing + + user: name of user to use if committing + + date: date tuple to use if committing''' + + for x in self.status()[:5]: + if '.hgtags' in x: + raise util.Abort(_('working copy of .hgtags is changed ' + '(please commit .hgtags manually)')) + + self.tags() # instantiate the cache + self._tag(names, node, message, local, user, date) + + def tags(self): + '''return a mapping of tag to node''' + if self.tagscache: + return self.tagscache + + globaltags = {} + tagtypes = {} + + def readtags(lines, fn, tagtype): + filetags = {} + count = 0 + + def warn(msg): + self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg)) + + for l in lines: + count += 1 + if not l: + continue + s = l.split(" ", 1) + if len(s) != 2: + warn(_("cannot parse entry")) + continue + node, key = s + key = encoding.tolocal(key.strip()) # stored in UTF-8 + try: + bin_n = bin(node) + except TypeError: + warn(_("node '%s' is not well formed") % node) + continue + if bin_n not in self.changelog.nodemap: + # silently ignore as pull -r might cause this + continue + + h = [] + if key in filetags: + n, h = filetags[key] + h.append(n) + filetags[key] = (bin_n, h) + + for k, nh in filetags.iteritems(): + if k not in globaltags: + globaltags[k] = nh + tagtypes[k] = tagtype + continue + + # we prefer the global tag if: + # it supercedes us OR + # mutual supercedes and it has a higher rank + # otherwise we win because we're tip-most + an, ah = nh + bn, bh = globaltags[k] + if (bn != an and an in bh and + (bn not in ah or len(bh) > len(ah))): + an = bn + ah.extend([n for n in bh if n not in ah]) + globaltags[k] = an, ah + tagtypes[k] = tagtype + + seen = set() + f = None + ctxs = [] + for node in self.heads(): + try: + fnode = self[node].filenode('.hgtags') + except error.LookupError: + continue + if fnode not in seen: + seen.add(fnode) + if not f: + f = self.filectx('.hgtags', fileid=fnode) + else: + f = f.filectx(fnode) + ctxs.append(f) + + # read the tags file from each head, ending with the tip + for f in reversed(ctxs): + readtags(f.data().splitlines(), f, "global") + + try: + data = encoding.fromlocal(self.opener("localtags").read()) + # localtags are stored in the local character set + # while the internal tag table is stored in UTF-8 + readtags(data.splitlines(), "localtags", "local") + except IOError: + pass + + self.tagscache = {} + self._tagstypecache = {} + for k, nh in globaltags.iteritems(): + n = nh[0] + if n != nullid: + self.tagscache[k] = n + self._tagstypecache[k] = tagtypes[k] + self.tagscache['tip'] = self.changelog.tip() + return self.tagscache + + def tagtype(self, tagname): + ''' + return the type of the given tag. result can be: + + 'local' : a local tag + 'global' : a global tag + None : tag does not exist + ''' + + self.tags() + + return self._tagstypecache.get(tagname) + + def tagslist(self): + '''return a list of tags ordered by revision''' + l = [] + for t, n in self.tags().iteritems(): + try: + r = self.changelog.rev(n) + except: + r = -2 # sort to the beginning of the list if unknown + l.append((r, t, n)) + return [(t, n) for r, t, n in sorted(l)] + + def nodetags(self, node): + '''return the tags associated with a node''' + if not self.nodetagscache: + self.nodetagscache = {} + for t, n in self.tags().iteritems(): + self.nodetagscache.setdefault(n, []).append(t) + return self.nodetagscache.get(node, []) + + def _branchtags(self, partial, lrev): + # TODO: rename this function? + tiprev = len(self) - 1 + if lrev != tiprev: + self._updatebranchcache(partial, lrev+1, tiprev+1) + self._writebranchcache(partial, self.changelog.tip(), tiprev) + + return partial + + def branchmap(self): + tip = self.changelog.tip() + if self.branchcache is not None and self._branchcachetip == tip: + return self.branchcache + + oldtip = self._branchcachetip + self._branchcachetip = tip + if self.branchcache is None: + self.branchcache = {} # avoid recursion in changectx + else: + self.branchcache.clear() # keep using the same dict + if oldtip is None or oldtip not in self.changelog.nodemap: + partial, last, lrev = self._readbranchcache() + else: + lrev = self.changelog.rev(oldtip) + partial = self._ubranchcache + + self._branchtags(partial, lrev) + # this private cache holds all heads (not just tips) + self._ubranchcache = partial + + # the branch cache is stored on disk as UTF-8, but in the local + # charset internally + for k, v in partial.iteritems(): + self.branchcache[encoding.tolocal(k)] = v + return self.branchcache + + + def branchtags(self): + '''return a dict where branch names map to the tipmost head of + the branch, open heads come before closed''' + bt = {} + for bn, heads in self.branchmap().iteritems(): + head = None + for i in range(len(heads)-1, -1, -1): + h = heads[i] + if 'close' not in self.changelog.read(h)[5]: + head = h + break + # no open heads were found + if head is None: + head = heads[-1] + bt[bn] = head + return bt + + + def _readbranchcache(self): + partial = {} + try: + f = self.opener("branchheads.cache") + lines = f.read().split('\n') + f.close() + except (IOError, OSError): + return {}, nullid, nullrev + + try: + last, lrev = lines.pop(0).split(" ", 1) + last, lrev = bin(last), int(lrev) + if lrev >= len(self) or self[lrev].node() != last: + # invalidate the cache + raise ValueError('invalidating branch cache (tip differs)') + for l in lines: + if not l: continue + node, label = l.split(" ", 1) + partial.setdefault(label.strip(), []).append(bin(node)) + except KeyboardInterrupt: + raise + except Exception, inst: + if self.ui.debugflag: + self.ui.warn(str(inst), '\n') + partial, last, lrev = {}, nullid, nullrev + return partial, last, lrev + + def _writebranchcache(self, branches, tip, tiprev): + try: + f = self.opener("branchheads.cache", "w", atomictemp=True) + f.write("%s %s\n" % (hex(tip), tiprev)) + for label, nodes in branches.iteritems(): + for node in nodes: + f.write("%s %s\n" % (hex(node), label)) + f.rename() + except (IOError, OSError): + pass + + def _updatebranchcache(self, partial, start, end): + # collect new branch entries + newbranches = {} + for r in xrange(start, end): + c = self[r] + newbranches.setdefault(c.branch(), []).append(c.node()) + # if older branchheads are reachable from new ones, they aren't + # really branchheads. Note checking parents is insufficient: + # 1 (branch a) -> 2 (branch b) -> 3 (branch a) + for branch, newnodes in newbranches.iteritems(): + bheads = partial.setdefault(branch, []) + bheads.extend(newnodes) + if len(bheads) < 2: + continue + newbheads = [] + # starting from tip means fewer passes over reachable + while newnodes: + latest = newnodes.pop() + if latest not in bheads: + continue + minbhrev = self[min([self[bh].rev() for bh in bheads])].node() + reachable = self.changelog.reachable(latest, minbhrev) + bheads = [b for b in bheads if b not in reachable] + newbheads.insert(0, latest) + bheads.extend(newbheads) + partial[branch] = bheads + + def lookup(self, key): + if isinstance(key, int): + return self.changelog.node(key) + elif key == '.': + return self.dirstate.parents()[0] + elif key == 'null': + return nullid + elif key == 'tip': + return self.changelog.tip() + n = self.changelog._match(key) + if n: + return n + if key in self.tags(): + return self.tags()[key] + if key in self.branchtags(): + return self.branchtags()[key] + n = self.changelog._partialmatch(key) + if n: + return n + + # can't find key, check if it might have come from damaged dirstate + if key in self.dirstate.parents(): + raise error.Abort(_("working directory has unknown parent '%s'!") + % short(key)) + try: + if len(key) == 20: + key = hex(key) + except: + pass + raise error.RepoError(_("unknown revision '%s'") % key) + + def local(self): + return True + + def join(self, f): + return os.path.join(self.path, f) + + def wjoin(self, f): + return os.path.join(self.root, f) + + def rjoin(self, f): + return os.path.join(self.root, util.pconvert(f)) + + def file(self, f): + if f[0] == '/': + f = f[1:] + return filelog.filelog(self.sopener, f) + + def changectx(self, changeid): + return self[changeid] + + def parents(self, changeid=None): + '''get list of changectxs for parents of changeid''' + return self[changeid].parents() + + def filectx(self, path, changeid=None, fileid=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + return context.filectx(self, path, changeid, fileid) + + def getcwd(self): + return self.dirstate.getcwd() + + def pathto(self, f, cwd=None): + return self.dirstate.pathto(f, cwd) + + def wfile(self, f, mode='r'): + return self.wopener(f, mode) + + def _link(self, f): + return os.path.islink(self.wjoin(f)) + + def _filter(self, filter, filename, data): + if filter not in self.filterpats: + l = [] + for pat, cmd in self.ui.configitems(filter): + if cmd == '!': + continue + mf = match_.match(self.root, '', [pat]) + fn = None + params = cmd + for name, filterfn in self._datafilters.iteritems(): + if cmd.startswith(name): + fn = filterfn + params = cmd[len(name):].lstrip() + break + if not fn: + fn = lambda s, c, **kwargs: util.filter(s, c) + # Wrap old filters not supporting keyword arguments + if not inspect.getargspec(fn)[2]: + oldfn = fn + fn = lambda s, c, **kwargs: oldfn(s, c) + l.append((mf, fn, params)) + self.filterpats[filter] = l + + for mf, fn, cmd in self.filterpats[filter]: + if mf(filename): + self.ui.debug(_("filtering %s through %s\n") % (filename, cmd)) + data = fn(data, cmd, ui=self.ui, repo=self, filename=filename) + break + + return data + + def adddatafilter(self, name, filter): + self._datafilters[name] = filter + + def wread(self, filename): + if self._link(filename): + data = os.readlink(self.wjoin(filename)) + else: + data = self.wopener(filename, 'r').read() + return self._filter("encode", filename, data) + + def wwrite(self, filename, data, flags): + data = self._filter("decode", filename, data) + try: + os.unlink(self.wjoin(filename)) + except OSError: + pass + if 'l' in flags: + self.wopener.symlink(data, filename) + else: + self.wopener(filename, 'w').write(data) + if 'x' in flags: + util.set_flags(self.wjoin(filename), False, True) + + def wwritedata(self, filename, data): + return self._filter("decode", filename, data) + + def transaction(self): + tr = self._transref and self._transref() or None + if tr and tr.running(): + return tr.nest() + + # abort here if the journal already exists + if os.path.exists(self.sjoin("journal")): + raise error.RepoError(_("journal already exists - run hg recover")) + + # save dirstate for rollback + try: + ds = self.opener("dirstate").read() + except IOError: + ds = "" + self.opener("journal.dirstate", "w").write(ds) + self.opener("journal.branch", "w").write(self.dirstate.branch()) + + renames = [(self.sjoin("journal"), self.sjoin("undo")), + (self.join("journal.dirstate"), self.join("undo.dirstate")), + (self.join("journal.branch"), self.join("undo.branch"))] + tr = transaction.transaction(self.ui.warn, self.sopener, + self.sjoin("journal"), + aftertrans(renames), + self.store.createmode) + self._transref = weakref.ref(tr) + return tr + + def recover(self): + lock = self.lock() + try: + if os.path.exists(self.sjoin("journal")): + self.ui.status(_("rolling back interrupted transaction\n")) + transaction.rollback(self.sopener, self.sjoin("journal"), self.ui.warn) + self.invalidate() + return True + else: + self.ui.warn(_("no interrupted transaction available\n")) + return False + finally: + lock.release() + + def rollback(self): + wlock = lock = None + try: + wlock = self.wlock() + lock = self.lock() + if os.path.exists(self.sjoin("undo")): + self.ui.status(_("rolling back last transaction\n")) + transaction.rollback(self.sopener, self.sjoin("undo"), self.ui.warn) + util.rename(self.join("undo.dirstate"), self.join("dirstate")) + try: + branch = self.opener("undo.branch").read() + self.dirstate.setbranch(branch) + except IOError: + self.ui.warn(_("Named branch could not be reset, " + "current branch still is: %s\n") + % encoding.tolocal(self.dirstate.branch())) + self.invalidate() + self.dirstate.invalidate() + else: + self.ui.warn(_("no rollback information available\n")) + finally: + release(lock, wlock) + + def invalidate(self): + for a in "changelog manifest".split(): + if a in self.__dict__: + delattr(self, a) + self.tagscache = None + self._tagstypecache = None + self.nodetagscache = None + self.branchcache = None + self._ubranchcache = None + self._branchcachetip = None + + def _lock(self, lockname, wait, releasefn, acquirefn, desc): + try: + l = lock.lock(lockname, 0, releasefn, desc=desc) + except error.LockHeld, inst: + if not wait: + raise + self.ui.warn(_("waiting for lock on %s held by %r\n") % + (desc, inst.locker)) + # default to 600 seconds timeout + l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")), + releasefn, desc=desc) + if acquirefn: + acquirefn() + return l + + def lock(self, wait=True): + l = self._lockref and self._lockref() + if l is not None and l.held: + l.lock() + return l + + l = self._lock(self.sjoin("lock"), wait, None, self.invalidate, + _('repository %s') % self.origroot) + self._lockref = weakref.ref(l) + return l + + def wlock(self, wait=True): + l = self._wlockref and self._wlockref() + if l is not None and l.held: + l.lock() + return l + + l = self._lock(self.join("wlock"), wait, self.dirstate.write, + self.dirstate.invalidate, _('working directory of %s') % + self.origroot) + self._wlockref = weakref.ref(l) + return l + + def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist): + """ + commit an individual file as part of a larger transaction + """ + + fname = fctx.path() + text = fctx.data() + flog = self.file(fname) + fparent1 = manifest1.get(fname, nullid) + fparent2 = fparent2o = manifest2.get(fname, nullid) + + meta = {} + copy = fctx.renamed() + if copy and copy[0] != fname: + # Mark the new revision of this file as a copy of another + # file. This copy data will effectively act as a parent + # of this new revision. If this is a merge, the first + # parent will be the nullid (meaning "look up the copy data") + # and the second one will be the other parent. For example: + # + # 0 --- 1 --- 3 rev1 changes file foo + # \ / rev2 renames foo to bar and changes it + # \- 2 -/ rev3 should have bar with all changes and + # should record that bar descends from + # bar in rev2 and foo in rev1 + # + # this allows this merge to succeed: + # + # 0 --- 1 --- 3 rev4 reverts the content change from rev2 + # \ / merging rev3 and rev4 should use bar@rev2 + # \- 2 --- 4 as the merge base + # + + cfname = copy[0] + crev = manifest1.get(cfname) + newfparent = fparent2 + + if manifest2: # branch merge + if fparent2 == nullid or crev is None: # copied on remote side + if cfname in manifest2: + crev = manifest2[cfname] + newfparent = fparent1 + + # find source in nearest ancestor if we've lost track + if not crev: + self.ui.debug(_(" %s: searching for copy revision for %s\n") % + (fname, cfname)) + for ancestor in self['.'].ancestors(): + if cfname in ancestor: + crev = ancestor[cfname].filenode() + break + + self.ui.debug(_(" %s: copy %s:%s\n") % (fname, cfname, hex(crev))) + meta["copy"] = cfname + meta["copyrev"] = hex(crev) + fparent1, fparent2 = nullid, newfparent + elif fparent2 != nullid: + # is one parent an ancestor of the other? + fparentancestor = flog.ancestor(fparent1, fparent2) + if fparentancestor == fparent1: + fparent1, fparent2 = fparent2, nullid + elif fparentancestor == fparent2: + fparent2 = nullid + + # is the file changed? + if fparent2 != nullid or flog.cmp(fparent1, text) or meta: + changelist.append(fname) + return flog.add(text, meta, tr, linkrev, fparent1, fparent2) + + # are just the flags changed during merge? + if fparent1 != fparent2o and manifest1.flags(fname) != fctx.flags(): + changelist.append(fname) + + return fparent1 + + def commit(self, text="", user=None, date=None, match=None, force=False, + editor=False, extra={}): + """Add a new revision to current repository. + + Revision information is gathered from the working directory, + match can be used to filter the committed files. If editor is + supplied, it is called to get a commit message. + """ + + def fail(f, msg): + raise util.Abort('%s: %s' % (f, msg)) + + if not match: + match = match_.always(self.root, '') + + if not force: + vdirs = [] + match.dir = vdirs.append + match.bad = fail + + wlock = self.wlock() + try: + p1, p2 = self.dirstate.parents() + wctx = self[None] + + if (not force and p2 != nullid and match and + (match.files() or match.anypats())): + raise util.Abort(_('cannot partially commit a merge ' + '(do not specify files or patterns)')) + + changes = self.status(match=match, clean=force) + if force: + changes[0].extend(changes[6]) # mq may commit unchanged files + + # check subrepos + subs = [] + for s in wctx.substate: + if match(s) and wctx.sub(s).dirty(): + subs.append(s) + if subs and '.hgsubstate' not in changes[0]: + changes[0].insert(0, '.hgsubstate') + + # make sure all explicit patterns are matched + if not force and match.files(): + matched = set(changes[0] + changes[1] + changes[2]) + + for f in match.files(): + if f == '.' or f in matched or f in wctx.substate: + continue + if f in changes[3]: # missing + fail(f, _('file not found!')) + if f in vdirs: # visited directory + d = f + '/' + for mf in matched: + if mf.startswith(d): + break + else: + fail(f, _("no match under directory!")) + elif f not in self.dirstate: + fail(f, _("file not tracked!")) + + if (not force and not extra.get("close") and p2 == nullid + and not (changes[0] or changes[1] or changes[2]) + and self[None].branch() == self['.'].branch()): + return None + + ms = merge_.mergestate(self) + for f in changes[0]: + if f in ms and ms[f] == 'u': + raise util.Abort(_("unresolved merge conflicts " + "(see hg resolve)")) + + cctx = context.workingctx(self, (p1, p2), text, user, date, + extra, changes) + if editor: + cctx._text = editor(self, cctx, subs) + + # commit subs + if subs: + state = wctx.substate.copy() + for s in subs: + self.ui.status(_('committing subrepository %s\n') % s) + sr = wctx.sub(s).commit(cctx._text, user, date) + state[s] = (state[s][0], sr) + subrepo.writestate(self, state) + + ret = self.commitctx(cctx, True) + + # update dirstate and mergestate + for f in changes[0] + changes[1]: + self.dirstate.normal(f) + for f in changes[2]: + self.dirstate.forget(f) + self.dirstate.setparents(ret) + ms.reset() + + return ret + + finally: + wlock.release() + + def commitctx(self, ctx, error=False): + """Add a new revision to current repository. + + Revision information is passed via the context argument. + """ + + tr = lock = None + removed = ctx.removed() + p1, p2 = ctx.p1(), ctx.p2() + m1 = p1.manifest().copy() + m2 = p2.manifest() + user = ctx.user() + + xp1, xp2 = p1.hex(), p2 and p2.hex() or '' + self.hook("precommit", throw=True, parent1=xp1, parent2=xp2) + + lock = self.lock() + try: + tr = self.transaction() + trp = weakref.proxy(tr) + + # check in files + new = {} + changed = [] + linkrev = len(self) + for f in sorted(ctx.modified() + ctx.added()): + self.ui.note(f + "\n") + try: + fctx = ctx[f] + new[f] = self._filecommit(fctx, m1, m2, linkrev, trp, + changed) + m1.set(f, fctx.flags()) + except (OSError, IOError): + if error: + self.ui.warn(_("trouble committing %s!\n") % f) + raise + else: + removed.append(f) + + # update manifest + m1.update(new) + removed = [f for f in sorted(removed) if f in m1 or f in m2] + drop = [f for f in removed if f in m1] + for f in drop: + del m1[f] + mn = self.manifest.add(m1, trp, linkrev, p1.manifestnode(), + p2.manifestnode(), (new, drop)) + + # update changelog + self.changelog.delayupdate() + n = self.changelog.add(mn, changed + removed, ctx.description(), + trp, p1.node(), p2.node(), + user, ctx.date(), ctx.extra().copy()) + p = lambda: self.changelog.writepending() and self.root or "" + self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, + parent2=xp2, pending=p) + self.changelog.finalize(trp) + tr.close() + + if self.branchcache: + self.branchtags() + + self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2) + return n + finally: + del tr + lock.release() + + def walk(self, match, node=None): + ''' + walk recursively through the directory tree or a given + changeset, finding all files matched by the match + function + ''' + return self[node].walk(match) + + def status(self, node1='.', node2=None, match=None, + ignored=False, clean=False, unknown=False): + """return status of files between two nodes or node and working directory + + If node1 is None, use the first dirstate parent instead. + If node2 is None, compare node1 with working directory. + """ + + def mfmatches(ctx): + mf = ctx.manifest().copy() + for fn in mf.keys(): + if not match(fn): + del mf[fn] + return mf + + if isinstance(node1, context.changectx): + ctx1 = node1 + else: + ctx1 = self[node1] + if isinstance(node2, context.changectx): + ctx2 = node2 + else: + ctx2 = self[node2] + + working = ctx2.rev() is None + parentworking = working and ctx1 == self['.'] + match = match or match_.always(self.root, self.getcwd()) + listignored, listclean, listunknown = ignored, clean, unknown + + # load earliest manifest first for caching reasons + if not working and ctx2.rev() < ctx1.rev(): + ctx2.manifest() + + if not parentworking: + def bad(f, msg): + if f not in ctx1: + self.ui.warn('%s: %s\n' % (self.dirstate.pathto(f), msg)) + match.bad = bad + + if working: # we need to scan the working dir + s = self.dirstate.status(match, listignored, listclean, listunknown) + cmp, modified, added, removed, deleted, unknown, ignored, clean = s + + # check for any possibly clean files + if parentworking and cmp: + fixup = [] + # do a full compare of any files that might have changed + for f in sorted(cmp): + if (f not in ctx1 or ctx2.flags(f) != ctx1.flags(f) + or ctx1[f].cmp(ctx2[f].data())): + modified.append(f) + else: + fixup.append(f) + + if listclean: + clean += fixup + + # update dirstate for files that are actually clean + if fixup: + try: + # updating the dirstate is optional + # so we don't wait on the lock + wlock = self.wlock(False) + try: + for f in fixup: + self.dirstate.normal(f) + finally: + wlock.release() + except error.LockError: + pass + + if not parentworking: + mf1 = mfmatches(ctx1) + if working: + # we are comparing working dir against non-parent + # generate a pseudo-manifest for the working dir + mf2 = mfmatches(self['.']) + for f in cmp + modified + added: + mf2[f] = None + mf2.set(f, ctx2.flags(f)) + for f in removed: + if f in mf2: + del mf2[f] + else: + # we are comparing two revisions + deleted, unknown, ignored = [], [], [] + mf2 = mfmatches(ctx2) + + modified, added, clean = [], [], [] + for fn in mf2: + if fn in mf1: + if (mf1.flags(fn) != mf2.flags(fn) or + (mf1[fn] != mf2[fn] and + (mf2[fn] or ctx1[fn].cmp(ctx2[fn].data())))): + modified.append(fn) + elif listclean: + clean.append(fn) + del mf1[fn] + else: + added.append(fn) + removed = mf1.keys() + + r = modified, added, removed, deleted, unknown, ignored, clean + [l.sort() for l in r] + return r + + def add(self, list): + wlock = self.wlock() + try: + rejected = [] + for f in list: + p = self.wjoin(f) + try: + st = os.lstat(p) + except: + self.ui.warn(_("%s does not exist!\n") % f) + rejected.append(f) + continue + if st.st_size > 10000000: + self.ui.warn(_("%s: files over 10MB may cause memory and" + " performance problems\n" + "(use 'hg revert %s' to unadd the file)\n") + % (f, f)) + if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)): + self.ui.warn(_("%s not added: only files and symlinks " + "supported currently\n") % f) + rejected.append(p) + elif self.dirstate[f] in 'amn': + self.ui.warn(_("%s already tracked!\n") % f) + elif self.dirstate[f] == 'r': + self.dirstate.normallookup(f) + else: + self.dirstate.add(f) + return rejected + finally: + wlock.release() + + def forget(self, list): + wlock = self.wlock() + try: + for f in list: + if self.dirstate[f] != 'a': + self.ui.warn(_("%s not added!\n") % f) + else: + self.dirstate.forget(f) + finally: + wlock.release() + + def remove(self, list, unlink=False): + if unlink: + for f in list: + try: + util.unlink(self.wjoin(f)) + except OSError, inst: + if inst.errno != errno.ENOENT: + raise + wlock = self.wlock() + try: + for f in list: + if unlink and os.path.exists(self.wjoin(f)): + self.ui.warn(_("%s still exists!\n") % f) + elif self.dirstate[f] == 'a': + self.dirstate.forget(f) + elif f not in self.dirstate: + self.ui.warn(_("%s not tracked!\n") % f) + else: + self.dirstate.remove(f) + finally: + wlock.release() + + def undelete(self, list): + manifests = [self.manifest.read(self.changelog.read(p)[0]) + for p in self.dirstate.parents() if p != nullid] + wlock = self.wlock() + try: + for f in list: + if self.dirstate[f] != 'r': + self.ui.warn(_("%s not removed!\n") % f) + else: + m = f in manifests[0] and manifests[0] or manifests[1] + t = self.file(f).read(m[f]) + self.wwrite(f, t, m.flags(f)) + self.dirstate.normal(f) + finally: + wlock.release() + + def copy(self, source, dest): + p = self.wjoin(dest) + if not (os.path.exists(p) or os.path.islink(p)): + self.ui.warn(_("%s does not exist!\n") % dest) + elif not (os.path.isfile(p) or os.path.islink(p)): + self.ui.warn(_("copy failed: %s is not a file or a " + "symbolic link\n") % dest) + else: + wlock = self.wlock() + try: + if self.dirstate[dest] in '?r': + self.dirstate.add(dest) + self.dirstate.copy(source, dest) + finally: + wlock.release() + + def heads(self, start=None): + heads = self.changelog.heads(start) + # sort the output in rev descending order + heads = [(-self.changelog.rev(h), h) for h in heads] + return [n for (r, n) in sorted(heads)] + + def branchheads(self, branch=None, start=None, closed=False): + if branch is None: + branch = self[None].branch() + branches = self.branchmap() + if branch not in branches: + return [] + bheads = branches[branch] + # the cache returns heads ordered lowest to highest + bheads.reverse() + if start is not None: + # filter out the heads that cannot be reached from startrev + bheads = self.changelog.nodesbetween([start], bheads)[2] + if not closed: + bheads = [h for h in bheads if + ('close' not in self.changelog.read(h)[5])] + return bheads + + def branches(self, nodes): + if not nodes: + nodes = [self.changelog.tip()] + b = [] + for n in nodes: + t = n + while 1: + p = self.changelog.parents(n) + if p[1] != nullid or p[0] == nullid: + b.append((t, n, p[0], p[1])) + break + n = p[0] + return b + + def between(self, pairs): + r = [] + + for top, bottom in pairs: + n, l, i = top, [], 0 + f = 1 + + while n != bottom and n != nullid: + p = self.changelog.parents(n)[0] + if i == f: + l.append(n) + f = f * 2 + n = p + i += 1 + + r.append(l) + + return r + + def findincoming(self, remote, base=None, heads=None, force=False): + """Return list of roots of the subsets of missing nodes from remote + + If base dict is specified, assume that these nodes and their parents + exist on the remote side and that no child of a node of base exists + in both remote and self. + Furthermore base will be updated to include the nodes that exists + in self and remote but no children exists in self and remote. + If a list of heads is specified, return only nodes which are heads + or ancestors of these heads. + + All the ancestors of base are in self and in remote. + All the descendants of the list returned are missing in self. + (and so we know that the rest of the nodes are missing in remote, see + outgoing) + """ + return self.findcommonincoming(remote, base, heads, force)[1] + + def findcommonincoming(self, remote, base=None, heads=None, force=False): + """Return a tuple (common, missing roots, heads) used to identify + missing nodes from remote. + + If base dict is specified, assume that these nodes and their parents + exist on the remote side and that no child of a node of base exists + in both remote and self. + Furthermore base will be updated to include the nodes that exists + in self and remote but no children exists in self and remote. + If a list of heads is specified, return only nodes which are heads + or ancestors of these heads. + + All the ancestors of base are in self and in remote. + """ + m = self.changelog.nodemap + search = [] + fetch = set() + seen = set() + seenbranch = set() + if base is None: + base = {} + + if not heads: + heads = remote.heads() + + if self.changelog.tip() == nullid: + base[nullid] = 1 + if heads != [nullid]: + return [nullid], [nullid], list(heads) + return [nullid], [], [] + + # assume we're closer to the tip than the root + # and start by examining the heads + self.ui.status(_("searching for changes\n")) + + unknown = [] + for h in heads: + if h not in m: + unknown.append(h) + else: + base[h] = 1 + + heads = unknown + if not unknown: + return base.keys(), [], [] + + req = set(unknown) + reqcnt = 0 + + # search through remote branches + # a 'branch' here is a linear segment of history, with four parts: + # head, root, first parent, second parent + # (a branch always has two parents (or none) by definition) + unknown = remote.branches(unknown) + while unknown: + r = [] + while unknown: + n = unknown.pop(0) + if n[0] in seen: + continue + + self.ui.debug(_("examining %s:%s\n") + % (short(n[0]), short(n[1]))) + if n[0] == nullid: # found the end of the branch + pass + elif n in seenbranch: + self.ui.debug(_("branch already found\n")) + continue + elif n[1] and n[1] in m: # do we know the base? + self.ui.debug(_("found incomplete branch %s:%s\n") + % (short(n[0]), short(n[1]))) + search.append(n[0:2]) # schedule branch range for scanning + seenbranch.add(n) + else: + if n[1] not in seen and n[1] not in fetch: + if n[2] in m and n[3] in m: + self.ui.debug(_("found new changeset %s\n") % + short(n[1])) + fetch.add(n[1]) # earliest unknown + for p in n[2:4]: + if p in m: + base[p] = 1 # latest known + + for p in n[2:4]: + if p not in req and p not in m: + r.append(p) + req.add(p) + seen.add(n[0]) + + if r: + reqcnt += 1 + self.ui.debug(_("request %d: %s\n") % + (reqcnt, " ".join(map(short, r)))) + for p in xrange(0, len(r), 10): + for b in remote.branches(r[p:p+10]): + self.ui.debug(_("received %s:%s\n") % + (short(b[0]), short(b[1]))) + unknown.append(b) + + # do binary search on the branches we found + while search: + newsearch = [] + reqcnt += 1 + for n, l in zip(search, remote.between(search)): + l.append(n[1]) + p = n[0] + f = 1 + for i in l: + self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i))) + if i in m: + if f <= 2: + self.ui.debug(_("found new branch changeset %s\n") % + short(p)) + fetch.add(p) + base[i] = 1 + else: + self.ui.debug(_("narrowed branch search to %s:%s\n") + % (short(p), short(i))) + newsearch.append((p, i)) + break + p, f = i, f * 2 + search = newsearch + + # sanity check our fetch list + for f in fetch: + if f in m: + raise error.RepoError(_("already have changeset ") + + short(f[:4])) + + if base.keys() == [nullid]: + if force: + self.ui.warn(_("warning: repository is unrelated\n")) + else: + raise util.Abort(_("repository is unrelated")) + + self.ui.debug(_("found new changesets starting at ") + + " ".join([short(f) for f in fetch]) + "\n") + + self.ui.debug(_("%d total queries\n") % reqcnt) + + return base.keys(), list(fetch), heads + + def findoutgoing(self, remote, base=None, heads=None, force=False): + """Return list of nodes that are roots of subsets not in remote + + If base dict is specified, assume that these nodes and their parents + exist on the remote side. + If a list of heads is specified, return only nodes which are heads + or ancestors of these heads, and return a second element which + contains all remote heads which get new children. + """ + if base is None: + base = {} + self.findincoming(remote, base, heads, force=force) + + self.ui.debug(_("common changesets up to ") + + " ".join(map(short, base.keys())) + "\n") + + remain = set(self.changelog.nodemap) + + # prune everything remote has from the tree + remain.remove(nullid) + remove = base.keys() + while remove: + n = remove.pop(0) + if n in remain: + remain.remove(n) + for p in self.changelog.parents(n): + remove.append(p) + + # find every node whose parents have been pruned + subset = [] + # find every remote head that will get new children + updated_heads = set() + for n in remain: + p1, p2 = self.changelog.parents(n) + if p1 not in remain and p2 not in remain: + subset.append(n) + if heads: + if p1 in heads: + updated_heads.add(p1) + if p2 in heads: + updated_heads.add(p2) + + # this is the set of all roots we have to push + if heads: + return subset, list(updated_heads) + else: + return subset + + def pull(self, remote, heads=None, force=False): + lock = self.lock() + try: + common, fetch, rheads = self.findcommonincoming(remote, heads=heads, + force=force) + if fetch == [nullid]: + self.ui.status(_("requesting all changes\n")) + + if not fetch: + self.ui.status(_("no changes found\n")) + return 0 + + if heads is None and remote.capable('changegroupsubset'): + heads = rheads + + if heads is None: + cg = remote.changegroup(fetch, 'pull') + else: + if not remote.capable('changegroupsubset'): + raise util.Abort(_("Partial pull cannot be done because " + "other repository doesn't support " + "changegroupsubset.")) + cg = remote.changegroupsubset(fetch, heads, 'pull') + return self.addchangegroup(cg, 'pull', remote.url()) + finally: + lock.release() + + def push(self, remote, force=False, revs=None): + # there are two ways to push to remote repo: + # + # addchangegroup assumes local user can lock remote + # repo (local filesystem, old ssh servers). + # + # unbundle assumes local user cannot lock remote repo (new ssh + # servers, http servers). + + if remote.capable('unbundle'): + return self.push_unbundle(remote, force, revs) + return self.push_addchangegroup(remote, force, revs) + + def prepush(self, remote, force, revs): + common = {} + remote_heads = remote.heads() + inc = self.findincoming(remote, common, remote_heads, force=force) + + update, updated_heads = self.findoutgoing(remote, common, remote_heads) + if revs is not None: + msng_cl, bases, heads = self.changelog.nodesbetween(update, revs) + else: + bases, heads = update, self.changelog.heads() + + def checkbranch(lheads, rheads, updatelh): + ''' + check whether there are more local heads than remote heads on + a specific branch. + + lheads: local branch heads + rheads: remote branch heads + updatelh: outgoing local branch heads + ''' + + warn = 0 + + if not revs and len(lheads) > len(rheads): + warn = 1 + else: + updatelheads = [self.changelog.heads(x, lheads) + for x in updatelh] + newheads = set(sum(updatelheads, [])) & set(lheads) + + if not newheads: + return True + + for r in rheads: + if r in self.changelog.nodemap: + desc = self.changelog.heads(r, heads) + l = [h for h in heads if h in desc] + if not l: + newheads.add(r) + else: + newheads.add(r) + if len(newheads) > len(rheads): + warn = 1 + + if warn: + if not rheads: # new branch requires --force + self.ui.warn(_("abort: push creates new" + " remote branch '%s'!\n") % + self[updatelh[0]].branch()) + else: + self.ui.warn(_("abort: push creates new remote heads!\n")) + + self.ui.status(_("(did you forget to merge?" + " use push -f to force)\n")) + return False + return True + + if not bases: + self.ui.status(_("no changes found\n")) + return None, 1 + elif not force: + # Check for each named branch if we're creating new remote heads. + # To be a remote head after push, node must be either: + # - unknown locally + # - a local outgoing head descended from update + # - a remote head that's known locally and not + # ancestral to an outgoing head + # + # New named branches cannot be created without --force. + + if remote_heads != [nullid]: + if remote.capable('branchmap'): + localhds = {} + if not revs: + localhds = self.branchmap() + else: + for n in heads: + branch = self[n].branch() + if branch in localhds: + localhds[branch].append(n) + else: + localhds[branch] = [n] + + remotehds = remote.branchmap() + + for lh in localhds: + if lh in remotehds: + rheads = remotehds[lh] + else: + rheads = [] + lheads = localhds[lh] + updatelh = [upd for upd in update + if self[upd].branch() == lh] + if not updatelh: + continue + if not checkbranch(lheads, rheads, updatelh): + return None, 0 + else: + if not checkbranch(heads, remote_heads, update): + return None, 0 + + if inc: + self.ui.warn(_("note: unsynced remote changes!\n")) + + + if revs is None: + # use the fast path, no race possible on push + cg = self._changegroup(common.keys(), 'push') + else: + cg = self.changegroupsubset(update, revs, 'push') + return cg, remote_heads + + def push_addchangegroup(self, remote, force, revs): + lock = remote.lock() + try: + ret = self.prepush(remote, force, revs) + if ret[0] is not None: + cg, remote_heads = ret + return remote.addchangegroup(cg, 'push', self.url()) + return ret[1] + finally: + lock.release() + + def push_unbundle(self, remote, force, revs): + # local repo finds heads on server, finds out what revs it + # must push. once revs transferred, if server finds it has + # different heads (someone else won commit/push race), server + # aborts. + + ret = self.prepush(remote, force, revs) + if ret[0] is not None: + cg, remote_heads = ret + if force: remote_heads = ['force'] + return remote.unbundle(cg, remote_heads, 'push') + return ret[1] + + def changegroupinfo(self, nodes, source): + if self.ui.verbose or source == 'bundle': + self.ui.status(_("%d changesets found\n") % len(nodes)) + if self.ui.debugflag: + self.ui.debug(_("list of changesets:\n")) + for node in nodes: + self.ui.debug("%s\n" % hex(node)) + + def changegroupsubset(self, bases, heads, source, extranodes=None): + """This function generates a changegroup consisting of all the nodes + that are descendents of any of the bases, and ancestors of any of + the heads. + + It is fairly complex as determining which filenodes and which + manifest nodes need to be included for the changeset to be complete + is non-trivial. + + Another wrinkle is doing the reverse, figuring out which changeset in + the changegroup a particular filenode or manifestnode belongs to. + + The caller can specify some nodes that must be included in the + changegroup using the extranodes argument. It should be a dict + where the keys are the filenames (or 1 for the manifest), and the + values are lists of (node, linknode) tuples, where node is a wanted + node and linknode is the changelog node that should be transmitted as + the linkrev. + """ + + if extranodes is None: + # can we go through the fast path ? + heads.sort() + allheads = self.heads() + allheads.sort() + if heads == allheads: + common = [] + # parents of bases are known from both sides + for n in bases: + for p in self.changelog.parents(n): + if p != nullid: + common.append(p) + return self._changegroup(common, source) + + self.hook('preoutgoing', throw=True, source=source) + + # Set up some initial variables + # Make it easy to refer to self.changelog + cl = self.changelog + # msng is short for missing - compute the list of changesets in this + # changegroup. + msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads) + self.changegroupinfo(msng_cl_lst, source) + # Some bases may turn out to be superfluous, and some heads may be + # too. nodesbetween will return the minimal set of bases and heads + # necessary to re-create the changegroup. + + # Known heads are the list of heads that it is assumed the recipient + # of this changegroup will know about. + knownheads = set() + # We assume that all parents of bases are known heads. + for n in bases: + knownheads.update(cl.parents(n)) + knownheads.discard(nullid) + knownheads = list(knownheads) + if knownheads: + # Now that we know what heads are known, we can compute which + # changesets are known. The recipient must know about all + # changesets required to reach the known heads from the null + # changeset. + has_cl_set, junk, junk = cl.nodesbetween(None, knownheads) + junk = None + # Transform the list into a set. + has_cl_set = set(has_cl_set) + else: + # If there were no known heads, the recipient cannot be assumed to + # know about any changesets. + has_cl_set = set() + + # Make it easy to refer to self.manifest + mnfst = self.manifest + # We don't know which manifests are missing yet + msng_mnfst_set = {} + # Nor do we know which filenodes are missing. + msng_filenode_set = {} + + junk = mnfst.index[len(mnfst) - 1] # Get around a bug in lazyindex + junk = None + + # A changeset always belongs to itself, so the changenode lookup + # function for a changenode is identity. + def identity(x): + return x + + # A function generating function. Sets up an environment for the + # inner function. + def cmp_by_rev_func(revlog): + # Compare two nodes by their revision number in the environment's + # revision history. Since the revision number both represents the + # most efficient order to read the nodes in, and represents a + # topological sorting of the nodes, this function is often useful. + def cmp_by_rev(a, b): + return cmp(revlog.rev(a), revlog.rev(b)) + return cmp_by_rev + + # If we determine that a particular file or manifest node must be a + # node that the recipient of the changegroup will already have, we can + # also assume the recipient will have all the parents. This function + # prunes them from the set of missing nodes. + def prune_parents(revlog, hasset, msngset): + haslst = list(hasset) + haslst.sort(cmp_by_rev_func(revlog)) + for node in haslst: + parentlst = [p for p in revlog.parents(node) if p != nullid] + while parentlst: + n = parentlst.pop() + if n not in hasset: + hasset.add(n) + p = [p for p in revlog.parents(n) if p != nullid] + parentlst.extend(p) + for n in hasset: + msngset.pop(n, None) + + # This is a function generating function used to set up an environment + # for the inner function to execute in. + def manifest_and_file_collector(changedfileset): + # This is an information gathering function that gathers + # information from each changeset node that goes out as part of + # the changegroup. The information gathered is a list of which + # manifest nodes are potentially required (the recipient may + # already have them) and total list of all files which were + # changed in any changeset in the changegroup. + # + # We also remember the first changenode we saw any manifest + # referenced by so we can later determine which changenode 'owns' + # the manifest. + def collect_manifests_and_files(clnode): + c = cl.read(clnode) + for f in c[3]: + # This is to make sure we only have one instance of each + # filename string for each filename. + changedfileset.setdefault(f, f) + msng_mnfst_set.setdefault(c[0], clnode) + return collect_manifests_and_files + + # Figure out which manifest nodes (of the ones we think might be part + # of the changegroup) the recipient must know about and remove them + # from the changegroup. + def prune_manifests(): + has_mnfst_set = set() + for n in msng_mnfst_set: + # If a 'missing' manifest thinks it belongs to a changenode + # the recipient is assumed to have, obviously the recipient + # must have that manifest. + linknode = cl.node(mnfst.linkrev(mnfst.rev(n))) + if linknode in has_cl_set: + has_mnfst_set.add(n) + prune_parents(mnfst, has_mnfst_set, msng_mnfst_set) + + # Use the information collected in collect_manifests_and_files to say + # which changenode any manifestnode belongs to. + def lookup_manifest_link(mnfstnode): + return msng_mnfst_set[mnfstnode] + + # A function generating function that sets up the initial environment + # the inner function. + def filenode_collector(changedfiles): + next_rev = [0] + # This gathers information from each manifestnode included in the + # changegroup about which filenodes the manifest node references + # so we can include those in the changegroup too. + # + # It also remembers which changenode each filenode belongs to. It + # does this by assuming the a filenode belongs to the changenode + # the first manifest that references it belongs to. + def collect_msng_filenodes(mnfstnode): + r = mnfst.rev(mnfstnode) + if r == next_rev[0]: + # If the last rev we looked at was the one just previous, + # we only need to see a diff. + deltamf = mnfst.readdelta(mnfstnode) + # For each line in the delta + for f, fnode in deltamf.iteritems(): + f = changedfiles.get(f, None) + # And if the file is in the list of files we care + # about. + if f is not None: + # Get the changenode this manifest belongs to + clnode = msng_mnfst_set[mnfstnode] + # Create the set of filenodes for the file if + # there isn't one already. + ndset = msng_filenode_set.setdefault(f, {}) + # And set the filenode's changelog node to the + # manifest's if it hasn't been set already. + ndset.setdefault(fnode, clnode) + else: + # Otherwise we need a full manifest. + m = mnfst.read(mnfstnode) + # For every file in we care about. + for f in changedfiles: + fnode = m.get(f, None) + # If it's in the manifest + if fnode is not None: + # See comments above. + clnode = msng_mnfst_set[mnfstnode] + ndset = msng_filenode_set.setdefault(f, {}) + ndset.setdefault(fnode, clnode) + # Remember the revision we hope to see next. + next_rev[0] = r + 1 + return collect_msng_filenodes + + # We have a list of filenodes we think we need for a file, lets remove + # all those we know the recipient must have. + def prune_filenodes(f, filerevlog): + msngset = msng_filenode_set[f] + hasset = set() + # If a 'missing' filenode thinks it belongs to a changenode we + # assume the recipient must have, then the recipient must have + # that filenode. + for n in msngset: + clnode = cl.node(filerevlog.linkrev(filerevlog.rev(n))) + if clnode in has_cl_set: + hasset.add(n) + prune_parents(filerevlog, hasset, msngset) + + # A function generator function that sets up the a context for the + # inner function. + def lookup_filenode_link_func(fname): + msngset = msng_filenode_set[fname] + # Lookup the changenode the filenode belongs to. + def lookup_filenode_link(fnode): + return msngset[fnode] + return lookup_filenode_link + + # Add the nodes that were explicitly requested. + def add_extra_nodes(name, nodes): + if not extranodes or name not in extranodes: + return + + for node, linknode in extranodes[name]: + if node not in nodes: + nodes[node] = linknode + + # Now that we have all theses utility functions to help out and + # logically divide up the task, generate the group. + def gengroup(): + # The set of changed files starts empty. + changedfiles = {} + # Create a changenode group generator that will call our functions + # back to lookup the owning changenode and collect information. + group = cl.group(msng_cl_lst, identity, + manifest_and_file_collector(changedfiles)) + for chnk in group: + yield chnk + + # The list of manifests has been collected by the generator + # calling our functions back. + prune_manifests() + add_extra_nodes(1, msng_mnfst_set) + msng_mnfst_lst = msng_mnfst_set.keys() + # Sort the manifestnodes by revision number. + msng_mnfst_lst.sort(cmp_by_rev_func(mnfst)) + # Create a generator for the manifestnodes that calls our lookup + # and data collection functions back. + group = mnfst.group(msng_mnfst_lst, lookup_manifest_link, + filenode_collector(changedfiles)) + for chnk in group: + yield chnk + + # These are no longer needed, dereference and toss the memory for + # them. + msng_mnfst_lst = None + msng_mnfst_set.clear() + + if extranodes: + for fname in extranodes: + if isinstance(fname, int): + continue + msng_filenode_set.setdefault(fname, {}) + changedfiles[fname] = 1 + # Go through all our files in order sorted by name. + for fname in sorted(changedfiles): + filerevlog = self.file(fname) + if not len(filerevlog): + raise util.Abort(_("empty or missing revlog for %s") % fname) + # Toss out the filenodes that the recipient isn't really + # missing. + if fname in msng_filenode_set: + prune_filenodes(fname, filerevlog) + add_extra_nodes(fname, msng_filenode_set[fname]) + msng_filenode_lst = msng_filenode_set[fname].keys() + else: + msng_filenode_lst = [] + # If any filenodes are left, generate the group for them, + # otherwise don't bother. + if len(msng_filenode_lst) > 0: + yield changegroup.chunkheader(len(fname)) + yield fname + # Sort the filenodes by their revision # + msng_filenode_lst.sort(cmp_by_rev_func(filerevlog)) + # Create a group generator and only pass in a changenode + # lookup function as we need to collect no information + # from filenodes. + group = filerevlog.group(msng_filenode_lst, + lookup_filenode_link_func(fname)) + for chnk in group: + yield chnk + if fname in msng_filenode_set: + # Don't need this anymore, toss it to free memory. + del msng_filenode_set[fname] + # Signal that no more groups are left. + yield changegroup.closechunk() + + if msng_cl_lst: + self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source) + + return util.chunkbuffer(gengroup()) + + def changegroup(self, basenodes, source): + # to avoid a race we use changegroupsubset() (issue1320) + return self.changegroupsubset(basenodes, self.heads(), source) + + def _changegroup(self, common, source): + """Generate a changegroup of all nodes that we have that a recipient + doesn't. + + This is much easier than the previous function as we can assume that + the recipient has any changenode we aren't sending them. + + common is the set of common nodes between remote and self""" + + self.hook('preoutgoing', throw=True, source=source) + + cl = self.changelog + nodes = cl.findmissing(common) + revset = set([cl.rev(n) for n in nodes]) + self.changegroupinfo(nodes, source) + + def identity(x): + return x + + def gennodelst(log): + for r in log: + if log.linkrev(r) in revset: + yield log.node(r) + + def changed_file_collector(changedfileset): + def collect_changed_files(clnode): + c = cl.read(clnode) + changedfileset.update(c[3]) + return collect_changed_files + + def lookuprevlink_func(revlog): + def lookuprevlink(n): + return cl.node(revlog.linkrev(revlog.rev(n))) + return lookuprevlink + + def gengroup(): + # construct a list of all changed files + changedfiles = set() + + for chnk in cl.group(nodes, identity, + changed_file_collector(changedfiles)): + yield chnk + + mnfst = self.manifest + nodeiter = gennodelst(mnfst) + for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)): + yield chnk + + for fname in sorted(changedfiles): + filerevlog = self.file(fname) + if not len(filerevlog): + raise util.Abort(_("empty or missing revlog for %s") % fname) + nodeiter = gennodelst(filerevlog) + nodeiter = list(nodeiter) + if nodeiter: + yield changegroup.chunkheader(len(fname)) + yield fname + lookup = lookuprevlink_func(filerevlog) + for chnk in filerevlog.group(nodeiter, lookup): + yield chnk + + yield changegroup.closechunk() + + if nodes: + self.hook('outgoing', node=hex(nodes[0]), source=source) + + return util.chunkbuffer(gengroup()) + + def addchangegroup(self, source, srctype, url, emptyok=False): + """add changegroup to repo. + + return values: + - nothing changed or no source: 0 + - more heads than before: 1+added heads (2..n) + - less heads than before: -1-removed heads (-2..-n) + - number of heads stays the same: 1 + """ + def csmap(x): + self.ui.debug(_("add changeset %s\n") % short(x)) + return len(cl) + + def revmap(x): + return cl.rev(x) + + if not source: + return 0 + + self.hook('prechangegroup', throw=True, source=srctype, url=url) + + changesets = files = revisions = 0 + + # write changelog data to temp files so concurrent readers will not see + # inconsistent view + cl = self.changelog + cl.delayupdate() + oldheads = len(cl.heads()) + + tr = self.transaction() + try: + trp = weakref.proxy(tr) + # pull off the changeset group + self.ui.status(_("adding changesets\n")) + clstart = len(cl) + chunkiter = changegroup.chunkiter(source) + if cl.addgroup(chunkiter, csmap, trp) is None and not emptyok: + raise util.Abort(_("received changelog group is empty")) + clend = len(cl) + changesets = clend - clstart + + # pull off the manifest group + self.ui.status(_("adding manifests\n")) + chunkiter = changegroup.chunkiter(source) + # no need to check for empty manifest group here: + # if the result of the merge of 1 and 2 is the same in 3 and 4, + # no new manifest will be created and the manifest group will + # be empty during the pull + self.manifest.addgroup(chunkiter, revmap, trp) + + # process the files + self.ui.status(_("adding file changes\n")) + while 1: + f = changegroup.getchunk(source) + if not f: + break + self.ui.debug(_("adding %s revisions\n") % f) + fl = self.file(f) + o = len(fl) + chunkiter = changegroup.chunkiter(source) + if fl.addgroup(chunkiter, revmap, trp) is None: + raise util.Abort(_("received file revlog group is empty")) + revisions += len(fl) - o + files += 1 + + newheads = len(cl.heads()) + heads = "" + if oldheads and newheads != oldheads: + heads = _(" (%+d heads)") % (newheads - oldheads) + + self.ui.status(_("added %d changesets" + " with %d changes to %d files%s\n") + % (changesets, revisions, files, heads)) + + if changesets > 0: + p = lambda: cl.writepending() and self.root or "" + self.hook('pretxnchangegroup', throw=True, + node=hex(cl.node(clstart)), source=srctype, + url=url, pending=p) + + # make changelog see real files again + cl.finalize(trp) + + tr.close() + finally: + del tr + + if changesets > 0: + # forcefully update the on-disk branch cache + self.ui.debug(_("updating the branch cache\n")) + self.branchtags() + self.hook("changegroup", node=hex(cl.node(clstart)), + source=srctype, url=url) + + for i in xrange(clstart, clend): + self.hook("incoming", node=hex(cl.node(i)), + source=srctype, url=url) + + # never return 0 here: + if newheads < oldheads: + return newheads - oldheads - 1 + else: + return newheads - oldheads + 1 + + + def stream_in(self, remote): + fp = remote.stream_out() + l = fp.readline() + try: + resp = int(l) + except ValueError: + raise error.ResponseError( + _('Unexpected response from remote server:'), l) + if resp == 1: + raise util.Abort(_('operation forbidden by server')) + elif resp == 2: + raise util.Abort(_('locking the remote repository failed')) + elif resp != 0: + raise util.Abort(_('the server sent an unknown error code')) + self.ui.status(_('streaming all changes\n')) + l = fp.readline() + try: + total_files, total_bytes = map(int, l.split(' ', 1)) + except (ValueError, TypeError): + raise error.ResponseError( + _('Unexpected response from remote server:'), l) + self.ui.status(_('%d files to transfer, %s of data\n') % + (total_files, util.bytecount(total_bytes))) + start = time.time() + for i in xrange(total_files): + # XXX doesn't support '\n' or '\r' in filenames + l = fp.readline() + try: + name, size = l.split('\0', 1) + size = int(size) + except (ValueError, TypeError): + raise error.ResponseError( + _('Unexpected response from remote server:'), l) + self.ui.debug(_('adding %s (%s)\n') % (name, util.bytecount(size))) + # for backwards compat, name was partially encoded + ofp = self.sopener(store.decodedir(name), 'w') + for chunk in util.filechunkiter(fp, limit=size): + ofp.write(chunk) + ofp.close() + elapsed = time.time() - start + if elapsed <= 0: + elapsed = 0.001 + self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') % + (util.bytecount(total_bytes), elapsed, + util.bytecount(total_bytes / elapsed))) + self.invalidate() + return len(self.heads()) + 1 + + def clone(self, remote, heads=[], stream=False): + '''clone remote repository. + + keyword arguments: + heads: list of revs to clone (forces use of pull) + stream: use streaming clone if possible''' + + # now, all clients that can request uncompressed clones can + # read repo formats supported by all servers that can serve + # them. + + # if revlog format changes, client will have to check version + # and format flags on "stream" capability, and use + # uncompressed only if compatible. + + if stream and not heads and remote.capable('stream'): + return self.stream_in(remote) + return self.pull(remote, heads) + +# used to avoid circular references so destructors work +def aftertrans(files): + renamefiles = [tuple(t) for t in files] + def a(): + for src, dest in renamefiles: + util.rename(src, dest) + return a + +def instance(ui, path, create): + return localrepository(ui, util.drop_scheme('file', path), create) + +def islocal(path): + return True diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/lock.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/lock.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,128 @@ +# lock.py - simple locking scheme for mercurial +# +# Copyright 2005, 2006 Matt Mackall +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2, incorporated herein by reference. + +import util, error +import errno, os, socket, time +import warnings + +class lock(object): + # lock is symlink on platforms that support it, file on others. + + # symlink is used because create of directory entry and contents + # are atomic even over nfs. + + # old-style lock: symlink to pid + # new-style lock: symlink to hostname:pid + + _host = None + + def __init__(self, file, timeout=-1, releasefn=None, desc=None): + self.f = file + self.held = 0 + self.timeout = timeout + self.releasefn = releasefn + self.desc = desc + self.lock() + + def __del__(self): + if self.held: + warnings.warn("use lock.release instead of del lock", + category=DeprecationWarning, + stacklevel=2) + + # ensure the lock will be removed + # even if recursive locking did occur + self.held = 1 + + self.release() + + def lock(self): + timeout = self.timeout + while 1: + try: + self.trylock() + return 1 + except error.LockHeld, inst: + if timeout != 0: + time.sleep(1) + if timeout > 0: + timeout -= 1 + continue + raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc, + inst.locker) + + def trylock(self): + if self.held: + self.held += 1 + return + if lock._host is None: + lock._host = socket.gethostname() + lockname = '%s:%s' % (lock._host, os.getpid()) + while not self.held: + try: + util.makelock(lockname, self.f) + self.held = 1 + except (OSError, IOError), why: + if why.errno == errno.EEXIST: + locker = self.testlock() + if locker is not None: + raise error.LockHeld(errno.EAGAIN, self.f, self.desc, + locker) + else: + raise error.LockUnavailable(why.errno, why.strerror, + why.filename, self.desc) + + def testlock(self): + """return id of locker if lock is valid, else None. + + If old-style lock, we cannot tell what machine locker is on. + with new-style lock, if locker is on this machine, we can + see if locker is alive. If locker is on this machine but + not alive, we can safely break lock. + + The lock file is only deleted when None is returned. + + """ + locker = util.readlock(self.f) + try: + host, pid = locker.split(":", 1) + except ValueError: + return locker + if host != lock._host: + return locker + try: + pid = int(pid) + except: + return locker + if util.testpid(pid): + return locker + # if locker dead, break lock. must do this with another lock + # held, or can race and break valid lock. + try: + l = lock(self.f + '.break') + l.trylock() + os.unlink(self.f) + l.release() + except error.LockError: + return locker + + def release(self): + if self.held > 1: + self.held -= 1 + elif self.held is 1: + self.held = 0 + if self.releasefn: + self.releasefn() + try: + os.unlink(self.f) + except: pass + +def release(*locks): + for lock in locks: + if lock is not None: + lock.release() + diff -r 394ebb3b6a0f -r dcf4fbe09b70 upmana/mercurial/lsprof.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upmana/mercurial/lsprof.py Tue Nov 10 14:11:28 2009 -0600 @@ -0,0 +1,115 @@ +#! /usr/bin/env python + +import sys +from _lsprof import Profiler, profiler_entry + +__all__ = ['profile', 'Stats'] + +def profile(f, *args, **kwds): + """XXX docstring""" + p = Profiler() + p.enable(subcalls=True, builtins=True) + try: + f(*args, **kwds) + finally: + p.disable() + return Stats(p.getstats()) + + +class Stats(object): + """XXX docstring""" + + def __init__(self, data): + self.data = data + + def sort(self, crit="inlinetime"): + """XXX docstring""" + if crit not in profiler_entry.__dict__: + raise ValueError("Can't sort by %s" % crit) + self.data.sort(lambda b, a: cmp(getattr(a, crit), + getattr(b, crit))) + for e in self.data: + if e.calls: + e.calls.sort(lambda b, a: cmp(getattr(a, crit), + getattr(b, crit))) + + def pprint(self, top=None, file=None, limit=None, climit=None): + """XXX docstring""" + if file is None: + file = sys.stdout + d = self.data + if top is not None: + d = d[:top] + cols = "% 12s %12s %11.4f %11.4f %s\n" + hcols = "% 12s %12s %12s %12s %s\n" + file.write(hcols % ("CallCount", "Recursive", "Total(ms)", + "Inline(ms)", "module:lineno(function)")) + count = 0 + for e in d: + file.write(cols % (e.callcount, e.reccallcount, e.totaltime, + e.inlinetime, label(e.code))) + count += 1 + if limit is not None and count == limit: + return + ccount = 0 + if e.calls: + for se in e.calls: + file.write(cols % ("+%s" % se.callcount, se.reccallcount, + se.totaltime, se.inlinetime, + "+%s" % label(se.code))) + count += 1 + ccount += 1 + if limit is not None and count == limit: + return + if climit is not None and ccount == climit: + break + + def freeze(self): + """Replace all references to code objects with string + descriptions; this makes it possible to pickle the instance.""" + + # this code is probably rather ickier than it needs to be! + for i in range(len(self.data)): + e = self.data[i] + if not isinstance(e.code, str): + self.data[i] = type(e)((label(e.code),) + e[1:]) + if e.calls: + for j in range(len(e.calls)): + se = e.calls[j] + if not isinstance(se.code, str): + e.calls[j] = type(se)((label(se.code),) + se[1:]) + +_fn2mod = {} + +def label(code): + if isinstance(code, str): + return code + try: + mname = _fn2mod[code.co_filename] + except KeyError: + for k, v in sys.modules.iteritems(): + if v is None: + continue + if not hasattr(v, '__file__'): + continue + if not isinstance(v.__file__, str): + continue + if v.__file__.startswith(code.co_filename): + mname = _fn2mod[code.co_filename] = k + break + else: + mname = _fn2mod[code.co_filename] = '<%s>'%code.co_filename + + return '%s:%d(%s)' % (mname, code.co_firstlineno, code.co_name) + + +if __name__ == '__main__': + import os + sys.argv = sys.argv[1:] + if not sys.argv: + print >> sys.stderr, "usage: lsprof.py
" + pl += "GROUP & '' PLAYER LIST
" + debug((self.groups, self.groups[k], self.groups[k].name)) + groupstring = "
" groutstring += "Group " + str(k) + ": " + self.groups[k].name + "" - groupstring += " Password: \"" + self.groups[k].pwd + "\" Boot: \"" + self.groups[k].boot_pwd + "\"
Password: " + self.groups[k].pwd + " Boot: " + self.groups[k].boot_pwd + "