X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/8b9a4190f70909de9568f45389e7aa3ecbc66b8a..b86ad1d632e9d7c677177631d3a5484cb3195f0e:/wxPython/wx/tools/XRCed/xrced.py diff --git a/wxPython/wx/tools/XRCed/xrced.py b/wxPython/wx/tools/XRCed/xrced.py index d5a3be7b2c..26d6bc6f30 100644 --- a/wxPython/wx/tools/XRCed/xrced.py +++ b/wxPython/wx/tools/XRCed/xrced.py @@ -1,8 +1,1845 @@ +# Name: xrced.py +# Purpose: XRC editor, main module +# Author: Roman Rolinsky +# Created: 20.08.2001 +# RCS-ID: $Id$ -"""Renamer stub: provides a way to drop the wx prefix from wxPython objects.""" +""" -from wx import _rename -from wxPython.tools.XRCed import xrced -_rename(globals(), xrced.__dict__, modulename='tools.XRCed.xrced') -del xrced -del _rename +xrced -- Simple resource editor for XRC format used by wxWidgets/wxPython + GUI toolkit. + +Usage: + + xrced [ -h ] [ -v ] [ XRC-file ] + +Options: + + -h output short usage info and exit + + -v output version info and exit +""" + +from globals import * +import os, sys, getopt, re, traceback, tempfile, shutil, cPickle +from xml.parsers import expat + +# Local modules +from tree import * # imports xxx which imports params +from panel import * +from tools import * +from params import genericStyles +# Cleanup recursive import sideeffects, otherwise we can't create undoMan +import undo +undo.ParamPage = ParamPage +undoMan = g.undoMan = UndoManager() + +# Set application path for loading resources +if __name__ == '__main__': + basePath = os.path.dirname(sys.argv[0]) +else: + basePath = os.path.dirname(__file__) + +# Remember system path +sys_path = sys.path + +# 1 adds CMD command to Help menu +debug = 0 + +g.helpText = """\ +

Welcome to XRCed

DON'T PANIC :)

+Read this note before clicking on anything!

+To start select tree root, then popup menu with your right mouse button, +select "Append Child", and then any command.

+Or just press one of the buttons on the tools palette.

+Enter XML ID, change properties, create children.

+To test your interface select Test command (View menu).

+Consult README.txt file for the details. +""" + +defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', + xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR', + xxxWizard:'WIZARD', xxxBitmap:'BITMAP', xxxIcon:'ICON'} + +defaultName = 'UNTITLED.xrc' + +################################################################################ + +# ScrolledMessageDialog - modified from wxPython lib to set fixed-width font +class ScrolledMessageDialog(wx.Dialog): + def __init__(self, parent, msg, caption, pos = wx.DefaultPosition, size = (500,300)): + from wx.lib.layoutf import Layoutf + wx.Dialog.__init__(self, parent, -1, caption, pos, size) + text = wx.TextCtrl(self, -1, msg, wx.DefaultPosition, + wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY) + text.SetFont(g.modernFont()) + dc = wx.WindowDC(text) + w, h = dc.GetFullTextExtent(' ', g.modernFont())[:2] + ok = wx.Button(self, wx.ID_OK, "OK") + ok.SetDefault() + text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) + text.SetSize((w * 80 + 30, h * 40)) + text.ShowPosition(1) # scroll to the first line + ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!35', (self,))) + self.SetAutoLayout(True) + self.Fit() + self.CenterOnScreen(wx.BOTH) + +################################################################################ + +# Event handler for using during location +class Locator(wx.EvtHandler): + def ProcessEvent(self, evt): + print evt + +class Frame(wx.Frame): + def __init__(self, pos, size): + wx.Frame.__init__(self, None, -1, '', pos, size) + global frame + frame = g.frame = self + bar = self.CreateStatusBar(2) + bar.SetStatusWidths([-1, 40]) + self.SetIcon(images.getIconIcon()) + + # Idle flag + self.inIdle = False + + # Load our own resources + self.res = xrc.EmptyXmlResource() + # !!! Blocking of assert failure occurring in older unicode builds + try: + quietlog = wx.LogNull() + self.res.Load(os.path.join(basePath, 'xrced.xrc')) + except wx._core.PyAssertionError: + print 'PyAssertionError was ignored' + + # Make menus + menuBar = wx.MenuBar() + + menu = wx.Menu() + menu.Append(wx.ID_NEW, '&New\tCtrl-N', 'New file') + menu.AppendSeparator() + menu.Append(wx.ID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') + + self.recentMenu = wx.Menu() + g.fileHistory.UseMenu(self.recentMenu) + g.fileHistory.AddFilesToMenu() + self.Bind(wx.EVT_MENU, self.OnRecentFile, id=wx.ID_FILE1, id2=wx.ID_FILE9) + menu.AppendMenu(-1, 'Open &Recent', self.recentMenu, 'Open a recent file') + + menu.AppendSeparator() + menu.Append(wx.ID_SAVE, '&Save\tCtrl-S', 'Save XRC file') + menu.Append(wx.ID_SAVEAS, 'Save &As...', 'Save XRC file under different name') + self.ID_GENERATE_PYTHON = wx.NewId() + menu.Append(self.ID_GENERATE_PYTHON, '&Generate Python...', + 'Generate a Python module that uses this XRC') + menu.AppendSeparator() + self.ID_PREFS = wx.NewId() + menu.Append(self.ID_PREFS, 'Preferences...', 'Change XRCed settings') + menu.AppendSeparator() + menu.Append(wx.ID_EXIT, '&Quit\tCtrl-Q', 'Exit application') + + menuBar.Append(menu, '&File') + + menu = wx.Menu() + menu.Append(wx.ID_UNDO, '&Undo\tCtrl-Z', 'Undo') + menu.Append(wx.ID_REDO, '&Redo\tCtrl-Y', 'Redo') + menu.AppendSeparator() + menu.Append(wx.ID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') + menu.Append(wx.ID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') + menu.Append(wx.ID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') + self.ID_DELETE = wx.NewId() + menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') + menu.AppendSeparator() + self.ID_LOCATE = wx.NewId() + self.ID_TOOL_LOCATE = wx.NewId() + self.ID_TOOL_PASTE = wx.NewId() + menu.Append(self.ID_LOCATE, '&Locate\tCtrl-L', 'Locate control in test window and select it') + menuBar.Append(menu, '&Edit') + + menu = wx.Menu() + self.ID_EMBED_PANEL = wx.NewId() + menu.Append(self.ID_EMBED_PANEL, '&Embed Panel', + 'Toggle embedding properties panel in the main window', True) + menu.Check(self.ID_EMBED_PANEL, conf.embedPanel) + self.ID_SHOW_TOOLS = wx.NewId() + menu.Append(self.ID_SHOW_TOOLS, 'Show &Tools', 'Toggle tools', True) + menu.Check(self.ID_SHOW_TOOLS, conf.showTools) + menu.AppendSeparator() + self.ID_TEST = wx.NewId() + menu.Append(self.ID_TEST, '&Test\tF5', 'Show test window') + self.ID_REFRESH = wx.NewId() + menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') + self.ID_AUTO_REFRESH = wx.NewId() + menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tAlt-A', + 'Toggle auto-refresh mode', True) + menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) + self.ID_TEST_HIDE = wx.NewId() + menu.Append(self.ID_TEST_HIDE, '&Hide\tF6', 'Close test window') + menuBar.Append(menu, '&View') + + menu = wx.Menu() + self.ID_MOVEUP = wx.NewId() + menu.Append(self.ID_MOVEUP, '&Up', 'Move before previous sibling') + self.ID_MOVEDOWN = wx.NewId() + menu.Append(self.ID_MOVEDOWN, '&Down', 'Move after next sibling') + self.ID_MOVELEFT = wx.NewId() + menu.Append(self.ID_MOVELEFT, '&Make sibling', 'Make sibling of parent') + self.ID_MOVERIGHT = wx.NewId() + menu.Append(self.ID_MOVERIGHT, '&Make child', 'Make child of previous sibling') + menuBar.Append(menu, '&Move') + + menu = wx.Menu() + menu.Append(wx.ID_ABOUT, '&About...', 'About XCRed') + self.ID_README = wx.NewId() + menu.Append(self.ID_README, '&Readme...\tF1', 'View the README file') + if debug: + self.ID_DEBUG_CMD = wx.NewId() + menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') + wx.EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) + menuBar.Append(menu, '&Help') + + self.menuBar = menuBar + self.SetMenuBar(menuBar) + + # Create toolbar + tb = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT) + tb.SetToolBitmapSize((24,24)) + new_bmp = wx.ArtProvider.GetBitmap(wx.ART_NORMAL_FILE, wx.ART_TOOLBAR) + open_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR) + save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR) + undo_bmp = wx.ArtProvider.GetBitmap(wx.ART_UNDO, wx.ART_TOOLBAR) + redo_bmp = wx.ArtProvider.GetBitmap(wx.ART_REDO, wx.ART_TOOLBAR) + cut_bmp = wx.ArtProvider.GetBitmap(wx.ART_CUT, wx.ART_TOOLBAR) + copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR) + paste_bmp= wx.ArtProvider.GetBitmap(wx.ART_PASTE, wx.ART_TOOLBAR) + + tb.AddSimpleTool(wx.ID_NEW, new_bmp, 'New', 'New file') + tb.AddSimpleTool(wx.ID_OPEN, open_bmp, 'Open', 'Open file') + tb.AddSimpleTool(wx.ID_SAVE, save_bmp, 'Save', 'Save file') + tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL)) + tb.AddSimpleTool(wx.ID_UNDO, undo_bmp, 'Undo', 'Undo') + tb.AddSimpleTool(wx.ID_REDO, redo_bmp, 'Redo', 'Redo') + tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL)) + tb.AddSimpleTool(wx.ID_CUT, cut_bmp, 'Cut', 'Cut') + tb.AddSimpleTool(wx.ID_COPY, copy_bmp, 'Copy', 'Copy') + tb.AddSimpleTool(self.ID_TOOL_PASTE, paste_bmp, 'Paste', 'Paste') + tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL)) + tb.AddSimpleTool(self.ID_TOOL_LOCATE, + images.getLocateBitmap(), #images.getLocateArmedBitmap(), + 'Locate', 'Locate control in test window and select it', True) + tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL)) + tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window') + tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), + 'Refresh', 'Refresh view') + tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), + 'Auto-refresh', 'Toggle auto-refresh mode', True) + tb.AddControl(wx.StaticLine(tb, -1, size=(-1,23), style=wx.LI_VERTICAL)) + tb.AddSimpleTool(self.ID_MOVEUP, images.getToolMoveUpBitmap(), + 'Up', 'Move before previous sibling') + tb.AddSimpleTool(self.ID_MOVEDOWN, images.getToolMoveDownBitmap(), + 'Down', 'Move after next sibling') + tb.AddSimpleTool(self.ID_MOVELEFT, images.getToolMoveLeftBitmap(), + 'Make Sibling', 'Make sibling of parent') + tb.AddSimpleTool(self.ID_MOVERIGHT, images.getToolMoveRightBitmap(), + 'Make Child', 'Make child of previous sibling') +# if wx.Platform == '__WXGTK__': +# tb.AddSeparator() # otherwise auto-refresh sticks in status line + tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) + tb.Realize() + + self.tb = tb + self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar + + # File + wx.EVT_MENU(self, wx.ID_NEW, self.OnNew) + wx.EVT_MENU(self, wx.ID_OPEN, self.OnOpen) + wx.EVT_MENU(self, wx.ID_SAVE, self.OnSaveOrSaveAs) + wx.EVT_MENU(self, wx.ID_SAVEAS, self.OnSaveOrSaveAs) + wx.EVT_MENU(self, self.ID_GENERATE_PYTHON, self.OnGeneratePython) + wx.EVT_MENU(self, self.ID_PREFS, self.OnPrefs) + wx.EVT_MENU(self, wx.ID_EXIT, self.OnExit) + # Edit + wx.EVT_MENU(self, wx.ID_UNDO, self.OnUndo) + wx.EVT_MENU(self, wx.ID_REDO, self.OnRedo) + wx.EVT_MENU(self, wx.ID_CUT, self.OnCutDelete) + wx.EVT_MENU(self, wx.ID_COPY, self.OnCopy) + wx.EVT_MENU(self, wx.ID_PASTE, self.OnPaste) + wx.EVT_MENU(self, self.ID_TOOL_PASTE, self.OnPaste) + wx.EVT_MENU(self, self.ID_DELETE, self.OnCutDelete) + wx.EVT_MENU(self, self.ID_LOCATE, self.OnLocate) + wx.EVT_MENU(self, self.ID_TOOL_LOCATE, self.OnLocate) + # View + wx.EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) + wx.EVT_MENU(self, self.ID_SHOW_TOOLS, self.OnShowTools) + wx.EVT_MENU(self, self.ID_TEST, self.OnTest) + wx.EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) + wx.EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) + wx.EVT_MENU(self, self.ID_TEST_HIDE, self.OnTestHide) + # Move + wx.EVT_MENU(self, self.ID_MOVEUP, self.OnMoveUp) + wx.EVT_MENU(self, self.ID_MOVEDOWN, self.OnMoveDown) + wx.EVT_MENU(self, self.ID_MOVELEFT, self.OnMoveLeft) + wx.EVT_MENU(self, self.ID_MOVERIGHT, self.OnMoveRight) + # Help + wx.EVT_MENU(self, wx.ID_ABOUT, self.OnAbout) + wx.EVT_MENU(self, self.ID_README, self.OnReadme) + + # Update events + wx.EVT_UPDATE_UI(self, wx.ID_SAVE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, wx.ID_CUT, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, wx.ID_COPY, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, wx.ID_PASTE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_LOCATE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_TOOL_LOCATE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_TOOL_PASTE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, wx.ID_UNDO, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, wx.ID_REDO, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) + wx.EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) + + # Build interface + sizer = wx.BoxSizer(wx.VERTICAL) + #sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND) + # Horizontal sizer for toolbar and splitter + self.toolsSizer = sizer1 = wx.BoxSizer() + splitter = wx.SplitterWindow(self, -1, style=wx.SP_3DSASH) + self.splitter = splitter + splitter.SetMinimumPaneSize(100) + # Create tree + global tree + g.tree = tree = XML_Tree(splitter, -1) + + # Init pull-down menu data + global pullDownMenu + g.pullDownMenu = pullDownMenu = PullDownMenu(self) + + # Vertical toolbar for GUI buttons + g.tools = tools = Tools(self) + tools.Show(conf.showTools) + if conf.showTools: sizer1.Add(tools, 0, wx.EXPAND) + + tree.RegisterKeyEvents() + + # Miniframe for split mode + miniFrame = wx.MiniFrame(self, -1, 'Properties & Style', + (conf.panelX, conf.panelY), + (conf.panelWidth, conf.panelHeight)) + self.miniFrame = miniFrame + sizer2 = wx.BoxSizer() + miniFrame.SetAutoLayout(True) + miniFrame.SetSizer(sizer2) + wx.EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame) + # Create panel for parameters + global panel + if conf.embedPanel: + panel = Panel(splitter) + # Set plitter windows + splitter.SplitVertically(tree, panel, conf.sashPos) + else: + panel = Panel(miniFrame) + sizer2.Add(panel, 1, wx.EXPAND) + miniFrame.Show(True) + splitter.Initialize(tree) + sizer1.Add(splitter, 1, wx.EXPAND) + sizer.Add(sizer1, 1, wx.EXPAND) + self.SetAutoLayout(True) + self.SetSizer(sizer) + + # Other events + wx.EVT_IDLE(self, self.OnIdle) + wx.EVT_CLOSE(self, self.OnCloseWindow) + wx.EVT_KEY_DOWN(self, tools.OnKeyDown) + wx.EVT_KEY_UP(self, tools.OnKeyUp) + wx.EVT_ICONIZE(self, self.OnIconize) + + def OnRecentFile(self,evt): + # open recently used file + if not self.AskSave(): return + wx.BeginBusyCursor() + + # get the pathname based on the menu ID + fileNum = evt.GetId() - wx.ID_FILE1 + path = g.fileHistory.GetHistoryFile(fileNum) + + if self.Open(path): + self.SetStatusText('Data loaded') + # add it back to the history so it will be moved up the list + self.SaveRecent(path) + else: + self.SetStatusText('Failed') + + wx.EndBusyCursor() + + def OnNew(self, evt): + if not self.AskSave(): return + self.Clear() + + def OnOpen(self, evt): + if not self.AskSave(): return + dlg = wx.FileDialog(self, 'Open', os.path.dirname(self.dataFile), + '', '*.xrc', wx.OPEN | wx.CHANGE_DIR) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + self.SetStatusText('Loading...') + wx.BeginBusyCursor() + try: + if self.Open(path): + self.SetStatusText('Data loaded') + self.SaveRecent(path) + else: + self.SetStatusText('Failed') + finally: + wx.EndBusyCursor() + dlg.Destroy() + + def OnSaveOrSaveAs(self, evt): + if evt.GetId() == wx.ID_SAVEAS or not self.dataFile: + if self.dataFile: name = '' + else: name = defaultName + dirname = os.path.abspath(os.path.dirname(self.dataFile)) + dlg = wx.FileDialog(self, 'Save As', dirname, name, '*.xrc', + wx.SAVE | wx.OVERWRITE_PROMPT | wx.CHANGE_DIR) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + if isinstance(path, unicode): + path = path.encode(sys.getfilesystemencoding()) + dlg.Destroy() + else: + dlg.Destroy() + return + + if conf.localconf: + # if we already have a localconf then it needs to be + # copied to a new config with the new name + lc = conf.localconf + nc = self.CreateLocalConf(path) + flag, key, idx = lc.GetFirstEntry() + while flag: + nc.Write(key, lc.Read(key)) + flag, key, idx = lc.GetNextEntry(idx) + conf.localconf = nc + else: + # otherwise create a new one + conf.localconf = self.CreateLocalConf(path) + else: + path = self.dataFile + self.SetStatusText('Saving...') + wx.BeginBusyCursor() + try: + try: + tmpFile,tmpName = tempfile.mkstemp(prefix='xrced-') + os.close(tmpFile) + self.Save(tmpName) # save temporary file first + shutil.move(tmpName, path) + self.dataFile = path + self.SetModified(False) + if conf.localconf.ReadBool("autogenerate", False): + pypath = conf.localconf.Read("filename") + embed = conf.localconf.ReadBool("embedResource", False) + genGettext = conf.localconf.ReadBool("genGettext", False) + self.GeneratePython(self.dataFile, pypath, embed, genGettext) + + self.SetStatusText('Data saved') + self.SaveRecent(path) + except IOError: + self.SetStatusText('Failed') + finally: + wx.EndBusyCursor() + + def SaveRecent(self,path): + # append to recently used files + g.fileHistory.AddFileToHistory(path) + + def GeneratePython(self, dataFile, pypath, embed, genGettext): + try: + import wx.tools.pywxrc + rescomp = wx.tools.pywxrc.XmlResourceCompiler() + rescomp.MakePythonModule([dataFile], pypath, embed, genGettext) + except: + inf = sys.exc_info() + wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) + wx.LogError('Error generating python code : %s' % pypath) + raise + + + def OnGeneratePython(self, evt): + if self.modified or not conf.localconf: + wx.MessageBox("Save the XRC file first!", "Error") + return + + dlg = PythonOptions(self, conf.localconf, self.dataFile) + dlg.ShowModal() + dlg.Destroy() + + def OnPrefs(self, evt): + dlg = PrefsDialog(self) + if dlg.ShowModal() == wx.ID_OK: + # Fetch new preferences + for id,cdp in dlg.checkControls.items(): + c,d,p = cdp + if dlg.FindWindowById(id).IsChecked(): + d[p] = str(c.GetValue()) + elif p in d: del d[p] + g.conf.allowExec = ('ask', 'yes', 'no')[dlg.radio_allow_exec.GetSelection()] + dlg.Destroy() + + def OnExit(self, evt): + self.Close() + + def OnUndo(self, evt): + # Extra check to not mess with idle updating + if undoMan.CanUndo(): + undoMan.Undo() + g.panel.SetModified(False) + if not undoMan.CanUndo(): + self.SetModified(False) + + def OnRedo(self, evt): + if undoMan.CanRedo(): + undoMan.Redo() + self.SetModified(True) + + def OnCopy(self, evt): + selected = tree.selection + if not selected: return # key pressed event + xxx = tree.GetPyData(selected) + if wx.TheClipboard.Open(): + if xxx.isElement: + data = wx.CustomDataObject('XRCED') + # Set encoding in header + # (False,True) + s = xxx.node.toxml(encoding=expat.native_encoding) + else: + data = wx.CustomDataObject('XRCED_node') + s = xxx.node.data + data.SetData(cPickle.dumps(s)) + wx.TheClipboard.SetData(data) + wx.TheClipboard.Close() + self.SetStatusText('Copied') + else: + wx.MessageBox("Unable to open the clipboard", "Error") + + def OnPaste(self, evt): + selected = tree.selection + if not selected: return # key pressed event + # For pasting with Ctrl pressed + appendChild = True + if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = False + elif evt.GetId() == self.ID_TOOL_PASTE: + if g.tree.ctrl: appendChild = False + else: appendChild = not tree.NeedInsert(selected) + else: appendChild = not tree.NeedInsert(selected) + xxx = tree.GetPyData(selected) + if not appendChild: + # If has next item, insert, else append to parent + nextItem = tree.GetNextSibling(selected) + parentLeaf = tree.GetItemParent(selected) + # Expanded container (must have children) + elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, False): + # Insert as first child + nextItem = tree.GetFirstChild(selected)[0] + parentLeaf = selected + else: + # No children or unexpanded item - appendChild stays True + nextItem = wx.TreeItemId() # no next item + parentLeaf = selected + parent = tree.GetPyData(parentLeaf).treeObject() + + # Create a copy of clipboard pickled element + success = success_node = False + if wx.TheClipboard.Open(): + try: + data = wx.CustomDataObject('XRCED') + if wx.TheClipboard.IsSupported(data.GetFormat()): + try: + success = wx.TheClipboard.GetData(data) + except: + # there is a problem if XRCED_node is in clipboard + # but previous SetData was for XRCED + pass + if not success: # try other format + data = wx.CustomDataObject('XRCED_node') + if wx.TheClipboard.IsSupported(data.GetFormat()): + success_node = wx.TheClipboard.GetData(data) + finally: + wx.TheClipboard.Close() + + if not success and not success_node: + wx.MessageBox( + "There is no data in the clipboard in the required format", + "Error") + return + + xml = cPickle.loads(data.GetData()) # xml representation of element + if success: + elem = minidom.parseString(xml).childNodes[0] + else: + elem = g.tree.dom.createComment(xml) + + # Tempopary xxx object to test things + xxx = MakeXXXFromDOM(parent, elem) + + # Check compatibility + if not self.ItemsAreCompatible(parent, xxx.treeObject()): return + + # Check parent and child relationships. + # If parent is sizer or notebook, child is of wrong class or + # parent is normal window, child is child container then detach child. + isChildContainer = isinstance(xxx, xxxChildContainer) + parentIsBook = parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook] + if isChildContainer and \ + ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ + (parentIsBook and not isinstance(xxx, xxxPage)) or \ + not (parent.isSizer or parentIsBook)): + elem.removeChild(xxx.child.node) # detach child + elem.unlink() # delete child container + elem = xxx.child.node # replace + # This may help garbage collection + xxx.child.parent = None + isChildContainer = False + # Parent is sizer or notebook, child is not child container + if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): + # Create sizer item element + sizerItemElem = MakeEmptyDOM(parent.itemTag) + sizerItemElem.appendChild(elem) + elem = sizerItemElem + elif isinstance(parent, xxxNotebook) and not isChildContainer: + pageElem = MakeEmptyDOM('notebookpage') + pageElem.appendChild(elem) + elem = pageElem + elif isinstance(parent, xxxChoicebook) and not isChildContainer: + pageElem = MakeEmptyDOM('choicebookpage') + pageElem.appendChild(elem) + elem = pageElem + elif isinstance(parent, xxxListbook) and not isChildContainer: + pageElem = MakeEmptyDOM('listbookpage') + pageElem.appendChild(elem) + elem = pageElem + # Insert new node, register undo + newItem = tree.InsertNode(parentLeaf, parent, elem, nextItem) + undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) + # Scroll to show new item (!!! redundant?) + tree.EnsureVisible(newItem) + tree.SelectItem(newItem) + if not tree.IsVisible(newItem): + tree.ScrollTo(newItem) + tree.Refresh() + # Update view? + if g.testWin and tree.IsHighlatable(newItem): + if conf.autoRefresh: + tree.needUpdate = True + tree.pendingHighLight = newItem + else: + tree.pendingHighLight = None + self.SetModified() + self.SetStatusText('Pasted') + + + def ItemsAreCompatible(self, parent, child): + # Check compatibility + error = False + # Comments are always compatible + if child.__class__ == xxxComment: + return True + # Top-level + if child.__class__ in [xxxDialog, xxxFrame, xxxWizard]: + # Top-level classes + if parent.__class__ != xxxMainNode: error = True + elif child.__class__ == xxxMenuBar: + # Menubar can be put in frame or dialog + if parent.__class__ not in [xxxMainNode, xxxFrame, xxxDialog]: error = True + elif child.__class__ == xxxToolBar: + # Toolbar can be top-level of child of panel or frame + if parent.__class__ not in [xxxMainNode, xxxPanel, xxxFrame] and \ + not parent.isSizer: error = True + elif child.__class__ == xxxPanel and parent.__class__ == xxxMainNode: + pass + elif child.__class__ == xxxSpacer: + if not parent.isSizer: error = True + elif child.__class__ == xxxSeparator: + if not parent.__class__ in [xxxMenu, xxxToolBar]: error = True + elif child.__class__ == xxxTool: + if parent.__class__ != xxxToolBar: error = True + elif child.__class__ == xxxMenu: + if not parent.__class__ in [xxxMainNode, xxxMenuBar, xxxMenu]: error = True + elif child.__class__ == xxxMenuItem: + if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = True + elif child.isSizer and parent.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook]: + error = True + else: # normal controls can be almost anywhere + if parent.__class__ == xxxMainNode or \ + parent.__class__ in [xxxMenuBar, xxxMenu]: error = True + if error: + if parent.__class__ == xxxMainNode: parentClass = 'root' + else: parentClass = parent.className + wx.LogError('Incompatible parent/child: parent is %s, child is %s!' % + (parentClass, child.className)) + return False + return True + + def OnMoveUp(self, evt): + selected = tree.selection + if not selected: return + + index = tree.ItemIndex(selected) + if index == 0: return # No previous sibling found + + # Remove highlight, update testWin + if g.testWin and g.testWin.highLight: + g.testWin.highLight.Remove() + tree.needUpdate = True + + # Undo info + self.lastOp = 'MOVEUP' + status = 'Moved before previous sibling' + + # Prepare undo data + panel.Apply() + tree.UnselectAll() + + parent = tree.GetItemParent(selected) + elem = tree.RemoveLeaf(selected) + nextItem = tree.GetFirstChild(parent)[0] + for i in range(index - 1): nextItem = tree.GetNextSibling(nextItem) + selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem) + newIndex = tree.ItemIndex(selected) + tree.SelectItem(selected) + + undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex)) + + self.modified = True + self.SetStatusText(status) + + return + + def OnMoveDown(self, evt): + selected = tree.selection + if not selected: return + + index = tree.ItemIndex(selected) + next = tree.GetNextSibling(selected) + if not next: return + + # Remove highlight, update testWin + if g.testWin and g.testWin.highLight: + g.testWin.highLight.Remove() + tree.needUpdate = True + + # Undo info + self.lastOp = 'MOVEDOWN' + status = 'Moved after next sibling' + + # Prepare undo data + panel.Apply() + tree.UnselectAll() + + parent = tree.GetItemParent(selected) + elem = tree.RemoveLeaf(selected) + nextItem = tree.GetFirstChild(parent)[0] + for i in range(index + 1): nextItem = tree.GetNextSibling(nextItem) + selected = tree.InsertNode(parent, tree.GetPyData(parent).treeObject(), elem, nextItem) + newIndex = tree.ItemIndex(selected) + tree.SelectItem(selected) + + undoMan.RegisterUndo(UndoMove(parent, index, parent, newIndex)) + + self.modified = True + self.SetStatusText(status) + + return + + def OnMoveLeft(self, evt): + selected = tree.selection + if not selected: return + + oldParent = tree.GetItemParent(selected) + if not oldParent: return + pparent = tree.GetItemParent(oldParent) + if not pparent: return + + # Check compatibility + if not self.ItemsAreCompatible(tree.GetPyData(pparent).treeObject(), tree.GetPyData(selected).treeObject()): return + + # Remove highlight, update testWin + if g.testWin and g.testWin.highLight: + g.testWin.highLight.Remove() + tree.needUpdate = True + + # Undo info + self.lastOp = 'MOVELEFT' + status = 'Made next sibling of parent' + + oldIndex = tree.ItemIndex(selected) + elem = tree.RemoveLeaf(selected) + nextItem = tree.GetFirstChild(pparent)[0] + parentIndex = tree.ItemIndex(oldParent) + for i in range(parentIndex + 1): nextItem = tree.GetNextSibling(nextItem) + + # Check parent and child relationships. + # If parent is sizer or notebook, child is of wrong class or + # parent is normal window, child is child container then detach child. + parent = tree.GetPyData(pparent).treeObject() + xxx = MakeXXXFromDOM(parent, elem) + isChildContainer = isinstance(xxx, xxxChildContainer) + if isChildContainer and \ + ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ + (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ + not (parent.isSizer or isinstance(parent, xxxNotebook))): + elem.removeChild(xxx.child.node) # detach child + elem.unlink() # delete child container + elem = xxx.child.node # replace + # This may help garbage collection + xxx.child.parent = None + isChildContainer = False + # Parent is sizer or notebook, child is not child container + if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): + # Create sizer item element + sizerItemElem = MakeEmptyDOM('sizeritem') + sizerItemElem.appendChild(elem) + elem = sizerItemElem + elif isinstance(parent, xxxNotebook) and not isChildContainer: + pageElem = MakeEmptyDOM('notebookpage') + pageElem.appendChild(elem) + elem = pageElem + + selected = tree.InsertNode(pparent, tree.GetPyData(pparent).treeObject(), elem, nextItem) + newIndex = tree.ItemIndex(selected) + tree.oldItem = None + tree.SelectItem(selected) + + undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, pparent, newIndex)) + + self.modified = True + self.SetStatusText(status) + + def OnMoveRight(self, evt): + selected = tree.selection + if not selected: return + + oldParent = tree.GetItemParent(selected) + if not oldParent: return + + newParent = tree.GetPrevSibling(selected) + if not newParent: return + + parent = tree.GetPyData(newParent).treeObject() + + # Check compatibility + if not self.ItemsAreCompatible(parent, tree.GetPyData(selected).treeObject()): return + + # Remove highlight, update testWin + if g.testWin and g.testWin.highLight: + g.testWin.highLight.Remove() + tree.needUpdate = True + + # Undo info + self.lastOp = 'MOVERIGHT' + status = 'Made last child of previous sibling' + + oldIndex = tree.ItemIndex(selected) + elem = tree.RemoveLeaf(selected) + + # Check parent and child relationships. + # If parent is sizer or notebook, child is of wrong class or + # parent is normal window, child is child container then detach child. + xxx = MakeXXXFromDOM(parent, elem) + isChildContainer = isinstance(xxx, xxxChildContainer) + if isChildContainer and \ + ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ + (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ + not (parent.isSizer or isinstance(parent, xxxNotebook))): + elem.removeChild(xxx.child.node) # detach child + elem.unlink() # delete child container + elem = xxx.child.node # replace + # This may help garbage collection + xxx.child.parent = None + isChildContainer = False + # Parent is sizer or notebook, child is not child container + if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): + # Create sizer item element + sizerItemElem = MakeEmptyDOM('sizeritem') + sizerItemElem.appendChild(elem) + elem = sizerItemElem + elif isinstance(parent, xxxNotebook) and not isChildContainer: + pageElem = MakeEmptyDOM('notebookpage') + pageElem.appendChild(elem) + elem = pageElem + + selected = tree.InsertNode(newParent, tree.GetPyData(newParent).treeObject(), elem, wx.TreeItemId()) + + newIndex = tree.ItemIndex(selected) + tree.oldItem = None + tree.SelectItem(selected) + + undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, newParent, newIndex)) + + self.modified = True + self.SetStatusText(status) + + + def OnCutDelete(self, evt): + selected = tree.selection + if not selected: return # key pressed event + # Undo info + if evt.GetId() == wx.ID_CUT: + self.lastOp = 'CUT' + status = 'Removed to clipboard' + else: + self.lastOp = 'DELETE' + status = 'Deleted' + # Delete testWin? + if g.testWin: + # If deleting top-level item, delete testWin + if selected == g.testWin.item: + g.testWin.Destroy() + g.testWin = None + else: + # Remove highlight, update testWin + if g.testWin.highLight: + g.testWin.highLight.Remove() + tree.needUpdate = True + # Prepare undo data + panel.Apply() + index = tree.ItemFullIndex(selected) + xxx = tree.GetPyData(selected) + parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject() + elem = tree.RemoveLeaf(selected) + undoMan.RegisterUndo(UndoCutDelete(index, parent, elem)) + if evt.GetId() == wx.ID_CUT: + if wx.TheClipboard.Open(): + if xxx.isElement: + data = wx.CustomDataObject('XRCED') + # (False, True) + s = elem.toxml(encoding=expat.native_encoding) + else: + data = wx.CustomDataObject('XRCED_node') + s = xxx.node.data + data.SetData(cPickle.dumps(s)) + wx.TheClipboard.SetData(data) + wx.TheClipboard.Close() + else: + wx.MessageBox("Unable to open the clipboard", "Error") + tree.pendingHighLight = None + tree.UnselectAll() + tree.selection = None + # Update tools + g.tools.UpdateUI() + panel.Clear() + self.SetModified() + self.SetStatusText(status) + + def OnSubclass(self, evt): + selected = tree.selection + xxx = tree.GetPyData(selected).treeObject() + elem = xxx.node + subclass = xxx.subclass + dlg = wx.TextEntryDialog(self, 'Subclass:', defaultValue=subclass) + if dlg.ShowModal() == wx.ID_OK: + subclass = dlg.GetValue() + if subclass: + elem.setAttribute('subclass', subclass) + elif elem.hasAttribute('subclass'): + elem.removeAttribute('subclass') + self.SetModified() + xxx.subclass = elem.getAttribute('subclass') + tree.SetItemText(selected, xxx.treeName()) + panel.pages[0].box.SetLabel(xxx.panelName()) + dlg.Destroy() + + def OnEmbedPanel(self, evt): + conf.embedPanel = evt.IsChecked() + if conf.embedPanel: + # Remember last dimentions + conf.panelX, conf.panelY = self.miniFrame.GetPosition() + conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() + size = self.GetSize() + pos = self.GetPosition() + sizePanel = panel.GetSize() + panel.Reparent(self.splitter) + self.miniFrame.GetSizer().Remove(panel) + # Widen + self.SetDimensions(pos.x, pos.y, size.width + sizePanel.width, size.height) + self.splitter.SplitVertically(tree, panel, conf.sashPos) + self.miniFrame.Show(False) + else: + conf.sashPos = self.splitter.GetSashPosition() + pos = self.GetPosition() + size = self.GetSize() + sizePanel = panel.GetSize() + self.splitter.Unsplit(panel) + sizer = self.miniFrame.GetSizer() + panel.Reparent(self.miniFrame) + panel.Show(True) + sizer.Add(panel, 1, wx.EXPAND) + self.miniFrame.Show(True) + self.miniFrame.SetDimensions(conf.panelX, conf.panelY, + conf.panelWidth, conf.panelHeight) + self.miniFrame.Layout() + # Reduce width + self.SetDimensions(pos.x, pos.y, + max(size.width - sizePanel.width, self.minWidth), size.height) + + def OnShowTools(self, evt): + conf.showTools = evt.IsChecked() + g.tools.Show(conf.showTools) + if conf.showTools: + self.toolsSizer.Prepend(g.tools, 0, wx.EXPAND) + else: + self.toolsSizer.Remove(g.tools) + self.toolsSizer.Layout() + + def OnTest(self, evt): + if not tree.selection: return # key pressed event + tree.ShowTestWindow(tree.selection) + + def OnTestHide(self, evt): + tree.CloseTestWindow() + + # Find object by relative position + def FindObject(self, item, obj): + # We simply perform depth-first traversal, sinse it's too much + # hassle to deal with all sizer/window combinations + w = tree.FindNodeObject(item) + if w == obj or isinstance(w, wx.GBSizerItem) and w.GetWindow() == obj: + return item + if tree.ItemHasChildren(item): + child = tree.GetFirstChild(item)[0] + while child: + found = self.FindObject(child, obj) + if found: return found + child = tree.GetNextSibling(child) + return None + + # Click event after locate activated + def OnTestWinLeftDown(self, evt): + # Restore normal event processing + self.SetHandler(g.testWin) + g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN) + item = self.FindObject(g.testWin.item, evt.GetEventObject()) + if item: + tree.EnsureVisible(item) + tree.SelectItem(item) + self.tb.ToggleTool(self.ID_TOOL_LOCATE, False) + if item: + self.SetStatusText('Selected %s' % tree.GetItemText(item)) + else: + self.SetStatusText('Locate failed!') + + def SetHandler(self, w, h=None): + if h: + w.SetEventHandler(h) + w.SetCursor(wx.CROSS_CURSOR) + else: + w.SetEventHandler(w) + w.SetCursor(wx.NullCursor) + for ch in w.GetChildren(): + self.SetHandler(ch, h) + + def OnLocate(self, evt): + if g.testWin: + if evt.GetId() == self.ID_LOCATE or \ + evt.GetId() == self.ID_TOOL_LOCATE and evt.IsChecked(): + self.SetHandler(g.testWin, g.testWin) + g.testWin.Connect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN, self.OnTestWinLeftDown) + if evt.GetId() == self.ID_LOCATE: + self.tb.ToggleTool(self.ID_TOOL_LOCATE, True) + elif evt.GetId() == self.ID_TOOL_LOCATE and not evt.IsChecked(): + self.SetHandler(g.testWin, None) + g.testWin.Disconnect(wx.ID_ANY, wx.ID_ANY, wx.wxEVT_LEFT_DOWN) + self.SetStatusText('Click somewhere in your test window now') + + def OnRefresh(self, evt): + # If modified, apply first + selection = tree.selection + if selection: + xxx = tree.GetPyData(selection) + if xxx and panel.IsModified(): + tree.Apply(xxx, selection) + if g.testWin: + # (re)create + tree.CreateTestWin(g.testWin.item) + panel.modified = False + tree.needUpdate = False + + def OnAutoRefresh(self, evt): + conf.autoRefresh = evt.IsChecked() + self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) + self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) + + def OnAbout(self, evt): + str = '''\ +XRCed version %s + +(c) Roman Rolinsky +Homepage: http://xrced.sourceforge.net\ +''' % version + dlg = wx.MessageDialog(self, str, 'About XRCed', wx.OK | wx.CENTRE) + dlg.ShowModal() + dlg.Destroy() + + def OnReadme(self, evt): + text = open(os.path.join(basePath, 'README.txt'), 'r').read() + dlg = ScrolledMessageDialog(self, text, "XRCed README") + dlg.ShowModal() + dlg.Destroy() + + # Simple emulation of python command line + def OnDebugCMD(self, evt): + while 1: + try: + exec raw_input('C:\> ') + except EOFError: + print '^D' + break + except: + (etype, value, tb) =sys.exc_info() + tblist =traceback.extract_tb(tb)[1:] + msg =' '.join(traceback.format_exception_only(etype, value) + +traceback.format_list(tblist)) + print msg + + def OnCreate(self, evt): + # Ignore fake events generated while dragging + if g.tools.drag: + g.tools.drag = False + return + selected = tree.selection + if tree.ctrl: appendChild = False + else: appendChild = not tree.NeedInsert(selected) + xxx = tree.GetPyData(selected) + if not appendChild: + # If insert before + if tree.shift: + # If has previous item, insert after it, else append to parent + nextItem = selected + parentLeaf = tree.GetItemParent(selected) + else: + # If has next item, insert, else append to parent + nextItem = tree.GetNextSibling(selected) + parentLeaf = tree.GetItemParent(selected) + # Expanded container (must have children) + elif tree.shift and tree.IsExpanded(selected) \ + and tree.GetChildrenCount(selected, False): + nextItem = tree.GetFirstChild(selected)[0] + parentLeaf = selected + else: + nextItem = wx.TreeItemId() + parentLeaf = selected + parent = tree.GetPyData(parentLeaf) + if parent.hasChild: parent = parent.child + + self.CreateXXX(parent, parentLeaf, nextItem, evt.GetId()) + + # Actual method to create object and add to XML and wx trees + def CreateXXX(self, parent, parentLeaf, nextItem, id): + selected = tree.selection + # Create object_ref? + if id == ID_NEW.REF: + ref = wx.GetTextFromUser('Create reference to:', 'Create reference') + if not ref: return + xxx = MakeEmptyRefXXX(parent, ref) + elif id == ID_NEW.COMMENT: + xxx = MakeEmptyCommentXXX(parent) + else: + # Create empty element + if id >= ID_NEW.CUSTOM: + className = pullDownMenu.customMap[id] + else: + className = pullDownMenu.createMap[id] + xxx = MakeEmptyXXX(parent, className) + + # Insert new node, register undo + if xxx.isElement: # true object + # Set default name for top-level windows + if parent.__class__ == xxxMainNode: + cl = xxx.treeObject().__class__ + frame.maxIDs[cl] += 1 + xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl])) + # And for some other standard controls + elif parent.__class__ == xxxStdDialogButtonSizer: + # ... we can even set automatically tree name + xxx.setTreeName(pullDownMenu.stdButtonIDs[id][0]) + obj = xxx.treeObject() + # ... and label + elem = g.tree.dom.createElement('label') + elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[id][1])) + obj.params['label'] = xxxParam(elem) + xxx.treeObject().node.appendChild(elem) + # Else, set label if exists to class name + elif 'label' in xxx.treeObject().allParams: + label = className + if label[:2] == 'wx': label = label[2:] + xxx.treeObject().set('label', label.upper()) + # For comment nodes, simply add node + newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem) + undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) + tree.EnsureVisible(newItem) + tree.SelectItem(newItem) + if not tree.IsVisible(newItem): + tree.ScrollTo(newItem) + tree.Refresh() + # Update view? + if xxx.isElement and g.testWin and tree.IsHighlatable(newItem): + if conf.autoRefresh: + tree.needUpdate = True + tree.pendingHighLight = newItem + else: + tree.pendingHighLight = None + tree.SetFocus() + if not xxx.isElement: + tree.EditLabel(newItem) + self.SetModified() + return xxx + + # Replace one object with another + def OnReplace(self, evt): + selected = tree.selection + xxx = tree.GetPyData(selected).treeObject() + elem = xxx.node + parent = elem.parentNode + undoMan.RegisterUndo(UndoReplace(selected)) + # New class + className = pullDownMenu.createMap[evt.GetId() - 1000] + + # Create temporary empty node (with default values) + dummy = MakeEmptyDOM(className) + if className == 'spacer' and xxx.className != 'spacer': + klass = xxxSpacer + elif xxx.className == 'spacer' and className != 'spacer': + klass = xxxSizerItem + else: + klass = xxxDict[className] + # Remove non-compatible children + if tree.ItemHasChildren(selected) and not klass.hasChildren: + tree.DeleteChildren(selected) + nodes = elem.childNodes[:] + tags = [] + for node in nodes: + if node.nodeType != minidom.Node.ELEMENT_NODE: continue + remove = False + tag = node.tagName + if tag == 'object': + if not klass.hasChildren: remove = True + elif tag not in klass.allParams and \ + (not klass.hasStyle or tag not in klass.styles): + remove = True + else: + tags.append(tag) + if remove: + elem.removeChild(node) + node.unlink() + + # Remove sizeritem child if spacer + if className == 'spacer' and xxx.className != 'spacer': + sizeritem = elem.parentNode + assert sizeritem.getAttribute('class') == 'sizeritem' + sizeritem.removeChild(elem) + elem.unlink() + elem = sizeritem + tree.GetPyData(selected).hasChild = False + elif xxx.className == 'spacer' and className != 'spacer': + # Create sizeritem element + assert xxx.parent.isSizer + elem.setAttribute('class', 'sizeritem') + node = MakeEmptyDOM(className) + elem.appendChild(node) + # Replace to point to new object + xxx = xxxSizerItem(xxx.parent, elem) + elem = node + tree.SetPyData(selected, xxx) + xxx = xxx.child + else: + # Copy parameters present in dummy but not in elem + for node in dummy.childNodes: + if node.tagName not in tags: elem.appendChild(node.cloneNode(True)) + dummy.unlink() + + # Change class name + elem.setAttribute('class', className) + if elem.hasAttribute('subclass'): + elem.removeAttribute('subclass') # clear subclassing + # Re-create xxx element + xxx = MakeXXXFromDOM(xxx.parent, elem) + # Remove incompatible style flags + if 'style' in xxx.params: + styles = map(string.strip, xxx.params['style'].value().split('|')) + newStyles = [s for s in styles if s in klass.winStyles or s in genericStyles] + if newStyles != styles: + if newStyles: + value = reduce(lambda a,b: a+'|'+b, newStyles) + else: + value = '' + xxx.params['style'].update(value) + + # Update parent in child objects + if tree.ItemHasChildren(selected): + i, cookie = tree.GetFirstChild(selected) + while i.IsOk(): + x = tree.GetPyData(i) + x.parent = xxx + if x.hasChild: x.child.parent = xxx + i, cookie = tree.GetNextChild(selected, cookie) + + # Update tree + if tree.GetPyData(selected).hasChild: # child container + container = tree.GetPyData(selected) + container.resetChild(xxx) + xxx = container + else: + tree.SetPyData(selected, xxx) + tree.SetItemText(selected, xxx.treeName()) + tree.SetItemImage(selected, xxx.treeImage()) + + # Set default name for top-level windows + if parent.__class__ == xxxMainNode: + cl = xxx.treeObject().__class__ + frame.maxIDs[cl] += 1 + xxx.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl])) + + # Update panel + g.panel.SetData(xxx) + # Update tools + g.tools.UpdateUI() + + #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) + # Update view? + if g.testWin and tree.IsHighlatable(selected): + if conf.autoRefresh: + tree.needUpdate = True + tree.pendingHighLight = selected + else: + tree.pendingHighLight = None + tree.SetFocus() + self.SetModified() + + # Expand/collapse subtree + def OnExpand(self, evt): + if tree.selection: tree.ExpandAll(tree.selection) + else: tree.ExpandAll(tree.root) + def OnCollapse(self, evt): + if tree.selection: tree.CollapseAll(tree.selection) + else: tree.CollapseAll(tree.root) + + def OnPullDownHighlight(self, evt): + menuId = evt.GetMenuId() + if menuId != -1: + menu = evt.GetEventObject() + help = menu.GetHelpString(menuId) + self.SetStatusText(help) + else: + self.SetStatusText('') + + def OnUpdateUI(self, evt): + if evt.GetId() in [wx.ID_CUT, wx.ID_COPY, self.ID_DELETE]: + evt.Enable(tree.selection is not None and tree.selection != tree.root) + elif evt.GetId() == wx.ID_SAVE: + evt.Enable(self.modified) + elif evt.GetId() in [wx.ID_PASTE, self.ID_TOOL_PASTE]: + evt.Enable(tree.selection is not None) + elif evt.GetId() == self.ID_TEST: + evt.Enable(tree.selection is not None and tree.selection != tree.root) + elif evt.GetId() in [self.ID_LOCATE, self.ID_TOOL_LOCATE]: + evt.Enable(g.testWin is not None) + elif evt.GetId() == wx.ID_UNDO: evt.Enable(undoMan.CanUndo()) + elif evt.GetId() == wx.ID_REDO: evt.Enable(undoMan.CanRedo()) + + def OnIdle(self, evt): + if self.inIdle: return # Recursive call protection + self.inIdle = True + try: + if tree.needUpdate: + if conf.autoRefresh: + if g.testWin: + #self.SetStatusText('Refreshing test window...') + # (re)create + tree.CreateTestWin(g.testWin.item) + #self.SetStatusText('') + tree.needUpdate = False + elif tree.pendingHighLight: + try: + tree.HighLight(tree.pendingHighLight) + except: + # Remove highlight if any problem + if g.testWin and g.testWin.highLight: + g.testWin.highLight.Remove() + tree.pendingHighLight = None + raise + else: + evt.Skip() + finally: + self.inIdle = False + + # We don't let close panel window + def OnCloseMiniFrame(self, evt): + return + + def OnIconize(self, evt): + if evt.Iconized(): + conf.x, conf.y = self.GetPosition() + conf.width, conf.height = self.GetSize() + if conf.embedPanel: + conf.sashPos = self.splitter.GetSashPosition() + else: + conf.panelX, conf.panelY = self.miniFrame.GetPosition() + conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() + self.miniFrame.Iconize() + else: + if not conf.embedPanel: + self.miniFrame.Iconize(False) + evt.Skip() + + def OnCloseWindow(self, evt): + if not self.AskSave(): return + if g.testWin: g.testWin.Destroy() + if not panel.GetPageCount() == 2: + panel.page2.Destroy() + else: + # If we don't do this, page does not get destroyed (a bug?) + panel.RemovePage(1) + if not self.IsIconized(): + conf.x, conf.y = self.GetPosition() + conf.width, conf.height = self.GetSize() + if conf.embedPanel: + conf.sashPos = self.splitter.GetSashPosition() + else: + conf.panelX, conf.panelY = self.miniFrame.GetPosition() + conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() + evt.Skip() + + def CreateLocalConf(self, path): + name = os.path.splitext(path)[0] + name += '.xcfg' + return wx.FileConfig(localFilename=name) + + def Clear(self): + self.dataFile = '' + conf.localconf = None + undoMan.Clear() + self.SetModified(False) + tree.Clear() + panel.Clear() + if g.testWin: + g.testWin.Destroy() + g.testWin = None + # Numbers for new controls + self.maxIDs = {} + for cl in [xxxPanel, xxxDialog, xxxFrame, + xxxMenuBar, xxxMenu, xxxToolBar, + xxxWizard, xxxBitmap, xxxIcon]: + self.maxIDs[cl] = 0 + # Restore handlers, menu, etc. to initial + setHandlers(self.handlers[:]) + g.pullDownMenu.custom = self.custom[:] + # Remove modules imported from comment directives + map(sys.modules.pop, [m for m in sys.modules if m not in self.modules]) + xxxParamComment.locals = {} # clear local namespace + xxxParamComment.allow = None # clear execution state + + def SetModified(self, state=True): + self.modified = state + name = os.path.basename(self.dataFile) + if not name: name = defaultName + if state: + self.SetTitle(progname + ': ' + name + ' *') + else: + self.SetTitle(progname + ': ' + name) + + def Open(self, path): + if not os.path.exists(path): + wx.LogError('File does not exists: %s' % path) + return False + # Try to read the file + try: + f = open(path) + self.Clear() + dom = minidom.parse(f) + f.close() + # Set encoding global variable and default encoding + if dom.encoding: + g.currentEncoding = dom.encoding + wx.SetDefaultPyEncoding(g.currentEncoding.encode()) + else: + g.currentEncoding = '' + # Change dir + self.dataFile = path = os.path.abspath(path) + dir = os.path.dirname(path) + if dir: os.chdir(dir) + # Allow importing modules from the same directory + sys.path = sys_path + [dir] + tree.SetData(dom) + self.SetTitle(progname + ': ' + os.path.basename(path)) + conf.localconf = self.CreateLocalConf(self.dataFile) + except: + # Nice exception printing + inf = sys.exc_info() + wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) + wx.LogError('Error reading file: %s' % path) + if debug: raise + return False + return True + + def Indent(self, node, indent = 0): + if node.nodeType == minidom.Node.COMMENT_NODE: + text = self.domCopy.createTextNode('\n' + ' ' * indent) + node.parentNode.insertBefore(text, node) + return # no children + # Copy child list because it will change soon + children = node.childNodes[:] + # Main node doesn't need to be indented + if indent: + text = self.domCopy.createTextNode('\n' + ' ' * indent) + node.parentNode.insertBefore(text, node) + if children: + # Append newline after last child, except for text nodes + if children[-1].nodeType == minidom.Node.ELEMENT_NODE: + text = self.domCopy.createTextNode('\n' + ' ' * indent) + node.appendChild(text) + # Indent children which are elements + for n in children: + if n.nodeType == minidom.Node.ELEMENT_NODE or \ + n.nodeType == minidom.Node.COMMENT_NODE: + self.Indent(n, indent + 2) + + def Save(self, path): + try: + import codecs + # Apply changes + if tree.selection and panel.IsModified(): + self.OnRefresh(wx.CommandEvent()) + if g.currentEncoding: + f = codecs.open(path, 'wt', g.currentEncoding) + else: + f = codecs.open(path, 'wt') + # Make temporary copy for formatting it + # !!! We can't clone dom node, it works only once + #self.domCopy = tree.dom.cloneNode(True) + self.domCopy = MyDocument() + mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(True)) + # Remove first child (test element) + testElem = mainNode.firstChild + mainNode.removeChild(testElem) + testElem.unlink() + self.Indent(mainNode) + self.domCopy.writexml(f, encoding = g.currentEncoding) + f.close() + self.domCopy.unlink() + self.domCopy = None + self.SetModified(False) + panel.SetModified(False) + conf.localconf.Flush() + except: + inf = sys.exc_info() + wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1]) + wx.LogError('Error writing file: %s' % path) + raise + + def AskSave(self): + if not (self.modified or panel.IsModified()): return True + flags = wx.ICON_EXCLAMATION | wx.YES_NO | wx.CANCEL | wx.CENTRE + dlg = wx.MessageDialog( self, 'File is modified. Save before exit?', + 'Save before too late?', flags ) + say = dlg.ShowModal() + dlg.Destroy() + wx.Yield() + if say == wx.ID_YES: + self.OnSaveOrSaveAs(wx.CommandEvent(wx.ID_SAVE)) + # If save was successful, modified flag is unset + if not self.modified: return True + elif say == wx.ID_NO: + self.SetModified(False) + panel.SetModified(False) + return True + return False + +################################################################################ + +class PythonOptions(wx.Dialog): + + def __init__(self, parent, cfg, dataFile): + pre = wx.PreDialog() + g.frame.res.LoadOnDialog(pre, parent, "PYTHON_OPTIONS") + self.PostCreate(pre) + + self.cfg = cfg + self.dataFile = dataFile + + self.AutoGenerateCB = xrc.XRCCTRL(self, "AutoGenerateCB") + self.EmbedCB = xrc.XRCCTRL(self, "EmbedCB") + self.GettextCB = xrc.XRCCTRL(self, "GettextCB") + self.MakeXRSFileCB = xrc.XRCCTRL(self, "MakeXRSFileCB") + self.FileNameTC = xrc.XRCCTRL(self, "FileNameTC") + self.BrowseBtn = xrc.XRCCTRL(self, "BrowseBtn") + self.GenerateBtn = xrc.XRCCTRL(self, "GenerateBtn") + self.SaveOptsBtn = xrc.XRCCTRL(self, "SaveOptsBtn") + + self.Bind(wx.EVT_BUTTON, self.OnBrowse, self.BrowseBtn) + self.Bind(wx.EVT_BUTTON, self.OnGenerate, self.GenerateBtn) + self.Bind(wx.EVT_BUTTON, self.OnSaveOpts, self.SaveOptsBtn) + + if self.cfg.Read("filename", "") != "": + self.FileNameTC.SetValue(self.cfg.Read("filename")) + else: + name = os.path.splitext(os.path.split(dataFile)[1])[0] + name += '_xrc.py' + self.FileNameTC.SetValue(name) + self.AutoGenerateCB.SetValue(self.cfg.ReadBool("autogenerate", False)) + self.EmbedCB.SetValue(self.cfg.ReadBool("embedResource", False)) + self.MakeXRSFileCB.SetValue(self.cfg.ReadBool("makeXRS", False)) + self.GettextCB.SetValue(self.cfg.ReadBool("genGettext", False)) + + + def OnBrowse(self, evt): + path = self.FileNameTC.GetValue() + dirname = os.path.abspath(os.path.dirname(path)) + name = os.path.split(path)[1] + dlg = wx.FileDialog(self, 'Save As', dirname, name, '*.py', + wx.SAVE | wx.OVERWRITE_PROMPT) + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + self.FileNameTC.SetValue(path) + dlg.Destroy() + + + def OnGenerate(self, evt): + pypath = self.FileNameTC.GetValue() + embed = self.EmbedCB.GetValue() + genGettext = self.GettextCB.GetValue() + frame.GeneratePython(self.dataFile, pypath, embed, genGettext) + self.OnSaveOpts() + + + def OnSaveOpts(self, evt=None): + self.cfg.Write("filename", self.FileNameTC.GetValue()) + self.cfg.WriteBool("autogenerate", self.AutoGenerateCB.GetValue()) + self.cfg.WriteBool("embedResource", self.EmbedCB.GetValue()) + self.cfg.WriteBool("makeXRS", self.MakeXRSFileCB.GetValue()) + self.cfg.WriteBool("genGettext", self.GettextCB.GetValue()) + + self.EndModal(wx.ID_OK) + +################################################################################ + +class PrefsDialog(wx.Dialog): + + def __init__(self, parent): + pre = wx.PreDialog() + g.frame.res.LoadOnDialog(pre, parent, "DIALOG_PREFS") + self.PostCreate(pre) + self.checkControls = {} # map of check IDs to (control,dict,param) + + ##xxx = sys.modules['xxx'] + import xxx + d = xxx.xxxSizerItem.defaults_panel + + self.check_proportion_panel = xrc.XRCCTRL(self, 'check_proportion_panel') + id = self.check_proportion_panel.GetId() + wx.EVT_CHECKBOX(self, id, self.OnCheck) + self.checkControls[id] = (xrc.XRCCTRL(self, 'spin_proportion_panel'), + d, 'option') + + self.check_flag_panel = xrc.XRCCTRL(self, 'check_flag_panel') + id = self.check_flag_panel.GetId() + wx.EVT_CHECKBOX(self, id, self.OnCheck) + self.checkControls[id] = (xrc.XRCCTRL(self, 'text_flag_panel'), + d, 'flag') + + d = xxx.xxxSizerItem.defaults_control + + self.check_proportion_panel = xrc.XRCCTRL(self, 'check_proportion_control') + id = self.check_proportion_panel.GetId() + wx.EVT_CHECKBOX(self, id, self.OnCheck) + self.checkControls[id] = (xrc.XRCCTRL(self, 'spin_proportion_control'), + d, 'option') + + self.check_flag_panel = xrc.XRCCTRL(self, 'check_flag_control') + id = self.check_flag_panel.GetId() + wx.EVT_CHECKBOX(self, id, self.OnCheck) + self.checkControls[id] = (xrc.XRCCTRL(self, 'text_flag_control'), + d, 'flag') + + for id,cdp in self.checkControls.items(): + c,d,p = cdp + try: + if isinstance(c, wx.SpinCtrl): + c.SetValue(int(d[p])) + else: + c.SetValue(d[p]) + self.FindWindowById(id).SetValue(True) + except KeyError: + c.Enable(False) + + self.radio_allow_exec = xrc.XRCCTRL(self, 'radio_allow_exec') + try: + radio = {'ask': 0, 'yes':1, 'no':2}[g.conf.allowExec] + except KeyError: + radio = 0 + self.radio_allow_exec.SetSelection(radio) + + def OnCheck(self, evt): + self.checkControls[evt.GetId()][0].Enable(evt.IsChecked()) + evt.Skip() + +################################################################################ + +# Parse string in form var1=val1[,var2=val2]* as dictionary +def ReadDictFromString(s): + d = {} + for vv in s.split(','): + var,val = vv.split(':') + d[var.strip()] = val + return d + +# Transform dictionary with strings into one string +def DictToString(d): + return ','.join(map(':'.join, d.items())) + +def usage(): + print >> sys.stderr, 'usage: xrced [-dhiv] [file]' + +class App(wx.App): + def OnInit(self): + # Check version + if wx.VERSION[:3] < MinWxVersion: + wx.LogWarning('''\ +This version of XRCed may not work correctly on your version of wxWidgets. \ +Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion) + global debug + # Process comand-line + opts = args = None + try: + opts, args = getopt.getopt(sys.argv[1:], 'dhiv') + for o,a in opts: + if o == '-h': + usage() + sys.exit(0) + elif o == '-d': + debug = True + elif o == '-v': + print 'XRCed version', version + sys.exit(0) + + except getopt.GetoptError: + if wx.Platform != '__WXMAC__': # macs have some extra parameters + print >> sys.stderr, 'Unknown option' + usage() + sys.exit(1) + + self.SetAppName('xrced') + # Settings + global conf + conf = g.conf = wx.Config(style = wx.CONFIG_USE_LOCAL_FILE) + conf.localconf = None + conf.autoRefresh = conf.ReadInt('autorefresh', True) + pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) + size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) + conf.embedPanel = conf.ReadInt('embedPanel', True) + conf.showTools = conf.ReadInt('showTools', True) + conf.sashPos = conf.ReadInt('sashPos', 200) + + # read recently used files + g.fileHistory = wx.FileHistory() + g.fileHistory.Load(conf) + + if not conf.embedPanel: + conf.panelX = conf.ReadInt('panelX', -1) + conf.panelY = conf.ReadInt('panelY', -1) + else: + conf.panelX = conf.panelY = -1 + conf.panelWidth = conf.ReadInt('panelWidth', 200) + conf.panelHeight = conf.ReadInt('panelHeight', 200) + conf.panic = not conf.HasEntry('nopanic') + # Preferences + conf.allowExec = conf.Read('Prefs/allowExec', 'ask') + p = 'Prefs/sizeritem_defaults_panel' + import xxx + if conf.HasEntry(p): + ##sys.modules['xxx'].xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p)) + xxx.xxxSizerItem.defaults_panel = ReadDictFromString(conf.Read(p)) + p = 'Prefs/sizeritem_defaults_control' + if conf.HasEntry(p): + ##sys.modules['xxx'].xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p)) + xxx.xxxSizerItem.defaults_control = ReadDictFromString(conf.Read(p)) + + # Add handlers + wx.FileSystem.AddHandler(wx.MemoryFSHandler()) + # Create main frame + frame = Frame(pos, size) + frame.Show(True) + + # Load plugins + plugins = os.getenv('XRCEDPATH') + if plugins: + cwd = os.getcwd() + try: + for dir in plugins.split(':'): + if os.path.isdir(dir) and \ + os.path.isfile(os.path.join(dir, '__init__.py')): + # Normalize + dir = os.path.abspath(os.path.normpath(dir)) + sys.path = sys_path + [os.path.dirname(dir)] + try: + os.chdir(dir) + __import__(os.path.basename(dir), globals(), locals(), ['*']) + except: + print traceback.print_exc() + finally: + os.chdir(cwd) + # Store important data + frame.handlers = getHandlers()[:] + frame.custom = g.pullDownMenu.custom[:] + frame.modules = sys.modules.copy() + + # Initialize + frame.Clear() + + # Load file after showing + if args: + conf.panic = False + frame.open = frame.Open(args[0]) + + return True + + def OnExit(self): + # Write config + global conf + wc = conf + wc.WriteInt('autorefresh', conf.autoRefresh) + wc.WriteInt('x', conf.x) + wc.WriteInt('y', conf.y) + wc.WriteInt('width', conf.width) + wc.WriteInt('height', conf.height) + wc.WriteInt('embedPanel', conf.embedPanel) + wc.WriteInt('showTools', conf.showTools) + if not conf.embedPanel: + wc.WriteInt('panelX', conf.panelX) + wc.WriteInt('panelY', conf.panelY) + wc.WriteInt('sashPos', conf.sashPos) + wc.WriteInt('panelWidth', conf.panelWidth) + wc.WriteInt('panelHeight', conf.panelHeight) + wc.WriteInt('nopanic', 1) + g.fileHistory.Save(wc) + # Preferences + wc.DeleteGroup('Prefs') + wc.Write('Prefs/allowExec', conf.allowExec) + import xxx + ##v = sys.modules['xxx'].xxxSizerItem.defaults_panel + v = xxx.xxxSizerItem.defaults_panel + if v: wc.Write('Prefs/sizeritem_defaults_panel', DictToString(v)) + ###v = sys.modules['xxx'].xxxSizerItem.defaults_control + v = xxx.xxxSizerItem.defaults_control + if v: wc.Write('Prefs/sizeritem_defaults_control', DictToString(v)) + + wc.Flush() + +def main(): + app = App(0, useBestVisual=False) + #app.SetAssertMode(wx.PYAPP_ASSERT_LOG) + app.MainLoop() + app.OnExit() + global conf + del conf + +if __name__ == '__main__': + main()