X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1fded56b375bf7a4687af1cdb182899614c1b2a8..0bdcd2f50750d235bff2766f3d03d7228aa88dff:/wxPython/wx/tools/XRCed/xrced.py diff --git a/wxPython/wx/tools/XRCed/xrced.py b/wxPython/wx/tools/XRCed/xrced.py index 47b754b3ba..033ad45ff3 100644 --- a/wxPython/wx/tools/XRCed/xrced.py +++ b/wxPython/wx/tools/XRCed/xrced.py @@ -1,11 +1,1017 @@ +# 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.""" +""" -__cvsid__ = "$Id$" -__revision__ = "$Revision$"[11:-2] +xrced -- Simple resource editor for XRC format used by wxWindows/wxPython + GUI toolkit. -from wx import _rename -from wxPython.tools.XRCed import xrced -_rename(globals(), xrced.__dict__, modulename='tools.XRCed.xrced') -del xrced -del _rename +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 + +# Local modules +from tree import * # imports xxx which imports params +from panel import * +from tools import * +# 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__) + +# 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 file for the details. +""" + +defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', + xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'} + +################################################################################ + +# ScrolledMessageDialog - modified from wxPython lib to set fixed-width font +class ScrolledMessageDialog(wxDialog): + def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = (500,300)): + from wxPython.lib.layoutf import Layoutf + wxDialog.__init__(self, parent, -1, caption, pos, size) + text = wxTextCtrl(self, -1, msg, wxDefaultPosition, + wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY) + text.SetFont(g.modernFont()) + dc = wxWindowDC(text) + # !!! possible bug - GetTextExtent without font returns sysfont dims + w, h = dc.GetFullTextExtent(' ', g.modernFont())[:2] + ok = wxButton(self, wxID_OK, "OK") + text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) + text.SetSize((w * 80 + 30, h * 40)) + ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) + self.SetAutoLayout(True) + self.Fit() + self.CenterOnScreen(wxBOTH) + +################################################################################ + +class Frame(wxFrame): + def __init__(self, pos, size): + wxFrame.__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 + + # Make menus + menuBar = wxMenuBar() + + menu = wxMenu() + menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file') + menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') + menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') + menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') + menu.AppendSeparator() + menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') + menuBar.Append(menu, '&File') + + menu = wxMenu() + menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') + menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo') + menu.AppendSeparator() + menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') + menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') + menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') + self.ID_DELETE = wxNewId() + menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') +# menu.AppendSeparator() + ID_SELECT = wxNewId() +# menu.Append(ID_SELECT, '&Select', 'Select object') + menuBar.Append(menu, '&Edit') + + menu = wxMenu() + self.ID_EMBED_PANEL = wxNewId() + 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 = wxNewId() + menu.Append(self.ID_SHOW_TOOLS, 'Show &Tools', 'Toggle tools', True) + menu.Check(self.ID_SHOW_TOOLS, conf.showTools) + menu.AppendSeparator() + self.ID_TEST = wxNewId() + menu.Append(self.ID_TEST, '&Test\tF5', 'Test window') + self.ID_REFRESH = wxNewId() + menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') + self.ID_AUTO_REFRESH = wxNewId() + menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', + 'Toggle auto-refresh mode', True) + menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) + menuBar.Append(menu, '&View') + + menu = wxMenu() + menu.Append(wxID_ABOUT, '&About...', 'About XCRed') + self.ID_README = wxNewId() + menu.Append(self.ID_README, '&Readme...', 'View the README file') + if debug: + self.ID_DEBUG_CMD = wxNewId() + menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') + EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) + menuBar.Append(menu, '&Help') + + self.menuBar = menuBar + self.SetMenuBar(menuBar) + + # Create toolbar + tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT) + tb.SetToolBitmapSize((24, 23)) + tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') + tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') + tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') + tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) + tb.AddSimpleTool(wxID_UNDO, images.getUndoBitmap(), 'Undo', 'Undo') + tb.AddSimpleTool(wxID_REDO, images.getRedoBitmap(), 'Redo', 'Redo') + tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) + tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') + tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') + tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') + tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_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) + if wxPlatform == '__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 + EVT_MENU(self, wxID_NEW, self.OnNew) + EVT_MENU(self, wxID_OPEN, self.OnOpen) + EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) + EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) + EVT_MENU(self, wxID_EXIT, self.OnExit) + # Edit + EVT_MENU(self, wxID_UNDO, self.OnUndo) + EVT_MENU(self, wxID_REDO, self.OnRedo) + EVT_MENU(self, wxID_CUT, self.OnCutDelete) + EVT_MENU(self, wxID_COPY, self.OnCopy) + EVT_MENU(self, wxID_PASTE, self.OnPaste) + EVT_MENU(self, self.ID_DELETE, self.OnCutDelete) + EVT_MENU(self, ID_SELECT, self.OnSelect) + # View + EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) + EVT_MENU(self, self.ID_SHOW_TOOLS, self.OnShowTools) + EVT_MENU(self, self.ID_TEST, self.OnTest) + EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) + EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) + # Help + EVT_MENU(self, wxID_ABOUT, self.OnAbout) + EVT_MENU(self, self.ID_README, self.OnReadme) + + # Update events + EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) + EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) + EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) + EVT_UPDATE_UI(self, wxID_UNDO, self.OnUpdateUI) + EVT_UPDATE_UI(self, wxID_REDO, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) + EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) + + # Build interface + sizer = wxBoxSizer(wxVERTICAL) + sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND) + # Horizontal sizer for toolbar and splitter + self.toolsSizer = sizer1 = wxBoxSizer() + splitter = wxSplitterWindow(self, -1, style=wxSP_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, wxEXPAND) + + tree.RegisterKeyEvents() + + # !!! frame styles are broken + # Miniframe for not embedded mode + miniFrame = wxFrame(self, -1, 'Properties Panel', + (conf.panelX, conf.panelY), + (conf.panelWidth, conf.panelHeight)) + self.miniFrame = miniFrame + sizer2 = wxBoxSizer() + miniFrame.SetAutoLayout(True) + miniFrame.SetSizer(sizer2) + 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, wxEXPAND) + miniFrame.Show(True) + splitter.Initialize(tree) + sizer1.Add(splitter, 1, wxEXPAND) + sizer.Add(sizer1, 1, wxEXPAND) + self.SetAutoLayout(True) + self.SetSizer(sizer) + + # Initialize + self.clipboard = None + self.Clear() + + # Other events + EVT_IDLE(self, self.OnIdle) + EVT_CLOSE(self, self.OnCloseWindow) + EVT_LEFT_DOWN(self, self.OnLeftDown) + EVT_KEY_DOWN(self, tools.OnKeyDown) + EVT_KEY_UP(self, tools.OnKeyUp) + + def OnNew(self, evt): + if not self.AskSave(): return + self.Clear() + + def OnOpen(self, evt): + if not self.AskSave(): return + dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile), + '', '*.xrc', wxOPEN | wxCHANGE_DIR) + if dlg.ShowModal() == wxID_OK: + path = dlg.GetPath() + self.SetStatusText('Loading...') + wxYield() + wxBeginBusyCursor() + if self.Open(path): + self.SetStatusText('Data loaded') + else: + self.SetStatusText('Failed') + wxEndBusyCursor() + dlg.Destroy() + + def OnSaveOrSaveAs(self, evt): + if evt.GetId() == wxID_SAVEAS or not self.dataFile: + if self.dataFile: defaultName = '' + else: defaultName = 'UNTITLED.xrc' + dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), + defaultName, '*.xrc', + wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) + if dlg.ShowModal() == wxID_OK: + path = dlg.GetPath() + dlg.Destroy() + else: + dlg.Destroy() + return + else: + path = self.dataFile + self.SetStatusText('Saving...') + wxYield() + wxBeginBusyCursor() + try: + self.Save(path) + self.dataFile = path + self.SetStatusText('Data saved') + except IOError: + self.SetStatusText('Failed') + wxEndBusyCursor() + + def OnExit(self, evt): + self.Close() + + def OnUndo(self, evt): + # Extra check to not mess with idle updating + if undoMan.CanUndo(): + undoMan.Undo() + + def OnRedo(self, evt): + if undoMan.CanRedo(): + undoMan.Redo() + + def OnCopy(self, evt): + selected = tree.selection + if not selected: return # key pressed event + xxx = tree.GetPyData(selected) + self.clipboard = xxx.element.cloneNode(True) + self.SetStatusText('Copied') + + def OnPaste(self, evt): + selected = tree.selection + if not selected: return # key pressed event + # For pasting with Ctrl pressed + if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = False + 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 = wxTreeItemId() # no next item + parentLeaf = selected + parent = tree.GetPyData(parentLeaf).treeObject() + + # Create a copy of clipboard element + elem = self.clipboard.cloneNode(True) + # Tempopary xxx object to test things + xxx = MakeXXXFromDOM(parent, elem) + + # Check compatibility + error = False + # Top-level + x = xxx.treeObject() + if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar]: + # Top-level classes + if parent.__class__ != xxxMainNode: error = True + elif x.__class__ == xxxToolBar: + # Toolbar can be top-level of child of panel or frame + if parent.__class__ not in [xxxMainNode, xxxPanel, xxxFrame]: error = True + elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode: + pass + elif x.__class__ == xxxSpacer: + if not parent.isSizer: error = True + elif x.__class__ == xxxSeparator: + if not parent.__class__ in [xxxMenu, xxxToolBar]: error = True + elif x.__class__ == xxxTool: + if parent.__class__ != xxxToolBar: error = True + elif x.__class__ == xxxMenu: + if not parent.__class__ in [xxxMainNode, xxxMenuBar, xxxMenu]: error = True + elif x.__class__ == xxxMenuItem: + if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = True + elif x.isSizer and parent.__class__ == xxxNotebook: 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 + wxLogError('Incompatible parent/child: parent is %s, child is %s!' % + (parentClass, x.className)) + 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) + 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.element) # detach child + elem.unlink() # delete child container + elem = xxx.child.element # 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 + # 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.modified = True + self.SetStatusText('Pasted') + + def OnCutDelete(self, evt): + selected = tree.selection + if not selected: return # key pressed event + # Undo info + if evt.GetId() == wxID_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) + parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject() + elem = tree.RemoveLeaf(selected) + undoMan.RegisterUndo(UndoCutDelete(index, parent, elem)) + if evt.GetId() == wxID_CUT: + if self.clipboard: self.clipboard.unlink() + self.clipboard = elem.cloneNode(True) + tree.pendingHighLight = None + tree.Unselect() + panel.Clear() + self.modified = True + self.SetStatusText(status) + + def OnSubclass(self, evt): + selected = tree.selection + xxx = tree.GetPyData(selected).treeObject() + elem = xxx.element + subclass = xxx.subclass + dlg = wxTextEntryDialog(self, 'Subclass:', defaultValue=subclass) + if dlg.ShowModal() == wxID_OK: + subclass = dlg.GetValue() + if subclass: + elem.setAttribute('subclass', subclass) + self.modified = True + elif elem.hasAttribute('subclass'): + elem.removeAttribute('subclass') + self.modified = True + xxx.subclass = elem.getAttribute('subclass') + tree.SetItemText(selected, xxx.treeName()) + panel.pages[0].box.SetLabel(xxx.panelName()) + dlg.Destroy() + + def OnSelect(self, evt): + print >> sys.stderr, 'Xperimental function!' + wxYield() + self.SetCursor(wxCROSS_CURSOR) + self.CaptureMouse() + + def OnLeftDown(self, evt): + pos = evt.GetPosition() + self.SetCursor(wxNullCursor) + self.ReleaseMouse() + + 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().RemoveWindow(panel) + wxYield() + # 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, wxEXPAND) + self.miniFrame.Show(True) + self.miniFrame.SetDimensions(conf.panelX, conf.panelY, + conf.panelWidth, conf.panelHeight) + wxYield() + # 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, wxEXPAND) + 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 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 = wxMessageDialog(self, str, 'About XRCed', wxOK | wxCENTRE) + 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): + import traceback + 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): + 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 = wxTreeItemId() + parentLeaf = selected + parent = tree.GetPyData(parentLeaf) + if parent.hasChild: parent = parent.child + + # Create element + className = pullDownMenu.createMap[evt.GetId()] + xxx = MakeEmptyXXX(parent, className) + + # Set default name for top-level windows + if parent.__class__ == xxxMainNode: + cl = xxx.treeObject().__class__ + frame.maxIDs[cl] += 1 + xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) + xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) + + # Insert new node, register undo + elem = xxx.element + newItem = tree.InsertNode(parentLeaf, parent, elem, 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 g.testWin and tree.IsHighlatable(newItem): + if conf.autoRefresh: + tree.needUpdate = True + tree.pendingHighLight = newItem + else: + tree.pendingHighLight = None + tree.SetFocus() + self.modified = True + + # Replace one object with another + def OnReplace(self, evt): + selected = tree.selection + xxx = tree.GetPyData(selected).treeObject() + elem = xxx.element + parent = elem.parentNode + parentXXX = xxx.parent + # New class + className = pullDownMenu.createMap[evt.GetId() - 1000] + # Create temporary empty node (with default values) + dummy = MakeEmptyDOM(className) + xxxClass = xxxDict[className] + # Remove non-compatible children + if tree.ItemHasChildren(selected) and not xxxClass.hasChildren: + tree.DeleteChildren(selected) + nodes = elem.childNodes[:] + tags = [] + for node in nodes: + remove = False + tag = node.tagName + if tag == 'object': + if not xxxClass.hasChildren: + remove = True + elif tag not in xxxClass.allParams and \ + (not xxxClass.hasStyle or tag not in xxxClass.styles): + remove = True + else: + tags.append(tag) + if remove: + elem.removeChild(node) + node.unlink() + + # Copy parameters present in dummy but not in elem + for node in dummy.childNodes: + tag = node.tagName + if tag not in tags: + elem.appendChild(node.cloneNode(True)) + dummy.unlink() + # Change class name + elem.setAttribute('class', className) + # Re-create xxx element + xxx = MakeXXXFromDOM(parentXXX, elem) + # 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.child = xxx + container.hasChildren = xxx.hasChildren + container.isSizer = xxx.isSizer + 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.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) + xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) + + # 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.modified = True + + # 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 [wxID_CUT, wxID_COPY, self.ID_DELETE]: + evt.Enable(tree.selection is not None and tree.selection != tree.root) + elif evt.GetId() == wxID_PASTE: + evt.Enable((self.clipboard and tree.selection) != None) + elif evt.GetId() == self.ID_TEST: + evt.Enable(tree.selection is not None and tree.selection != tree.root) + elif evt.GetId() == wxID_UNDO: evt.Enable(undoMan.CanUndo()) + elif evt.GetId() == wxID_REDO: evt.Enable(undoMan.CanRedo()) + + def OnIdle(self, evt): + if self.inIdle: return # Recursive call protection + self.inIdle = True + if tree.needUpdate: + if conf.autoRefresh: + if g.testWin: + self.SetStatusText('Refreshing test window...') + # (re)create + tree.CreateTestWin(g.testWin.item) + wxYield() + self.SetStatusText('') + tree.needUpdate = False + elif tree.pendingHighLight: + tree.HighLight(tree.pendingHighLight) + else: + evt.Skip() + self.inIdle = False + + # We don't let close panel window + def OnCloseMiniFrame(self, evt): + return + + def OnCloseWindow(self, evt): + if not self.AskSave(): return + if g.testWin: g.testWin.Destroy() + # Destroy cached windows + panel.cacheParent.Destroy() + if not panel.GetPageCount() == 2: + panel.page2.Destroy() + 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 Clear(self): + self.dataFile = '' + if self.clipboard: + self.clipboard.unlink() + self.clipboard = None + undoMan.Clear() + self.modified = False + tree.Clear() + panel.Clear() + if g.testWin: + g.testWin.Destroy() + g.testWin = None + self.SetTitle(progname) + # Numbers for new controls + self.maxIDs = {} + self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \ + self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0 + + def Open(self, path): + if not os.path.exists(path): + wxLogError('File does not exists: %s' % path) + return False + # Try to read the file + try: + f = open(path) + self.Clear() + # Parse first line to get encoding (!! hack, I don't know a better way) + line = f.readline() + mo = re.match(r'^<\?xml ([^<>]* )?encoding="(?P[^<>].*)"\?>', line) + # Build wx tree + f.seek(0) + dom = minidom.parse(f) + # Set encoding global variable and document encoding property + if mo: + dom.encoding = g.currentEncoding = mo.group('encd') + if dom.encoding not in ['ascii', sys.getdefaultencoding()]: + wxLogWarning('Encoding is different from system default') + else: + g.currentEncoding = 'ascii' + dom.encoding = '' + f.close() + # Change dir + dir = os.path.dirname(path) + if dir: os.chdir(dir) + tree.SetData(dom) + self.dataFile = path + self.SetTitle(progname + ': ' + os.path.basename(path)) + except: + # Nice exception printing + inf = sys.exc_info() + wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1]) + wxLogError('Error reading file: %s' % path) + return False + return True + + def Indent(self, node, indent = 0): + # 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: + self.Indent(n, indent + 2) + + def Save(self, path): + try: + # Apply changes + if tree.selection and panel.IsModified(): + self.OnRefresh(wxCommandEvent()) + f = open(path, 'w') + # 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)) + self.Indent(mainNode) + self.domCopy.writexml(f, encoding=tree.rootObj.params['encoding'].value()) + f.close() + self.domCopy.unlink() + self.domCopy = None + self.modified = False + panel.SetModified(False) + except: + wxLogError('Error writing file: %s' % path) + raise + + def AskSave(self): + if not (self.modified or panel.IsModified()): return True + flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE + dlg = wxMessageDialog( self, 'File is modified. Save before exit?', + 'Save before too late?', flags ) + say = dlg.ShowModal() + dlg.Destroy() + if say == wxID_YES: + self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE)) + # If save was successful, modified flag is unset + if not self.modified: return True + elif say == wxID_NO: + self.modified = False + panel.SetModified(False) + return True + return False + + def SaveUndo(self): + pass # !!! + +################################################################################ + +def usage(): + print >> sys.stderr, 'usage: xrced [-dhiv] [file]' + +class App(wxApp): + def OnInit(self): + global debug + # Process comand-line + try: + opts = args = None + 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 wxPlatform != '__WXMAC__': # macs have some extra parameters + print >> sys.stderr, 'Unknown option' + usage() + sys.exit(1) + + self.SetAppName('xrced') + # Settings + global conf + conf = g.conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) + 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) + 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') + # Add handlers + wxFileSystem_AddHandler(wxMemoryFSHandler()) + wxInitAllImageHandlers() + # Create main frame + frame = Frame(pos, size) + frame.Show(True) + # Load resources from XRC file (!!! should be transformed to .py later?) + frame.res = wxXmlResource('') + frame.res.Load(os.path.join(basePath, 'xrced.xrc')) + + # 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 = wxConfigBase_Get() + 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', True) + wc.Flush() + +def main(): + app = App(0, useBestVisual=False) + #app.SetAssertMode(wxPYAPP_ASSERT_LOG) + app.MainLoop() + app.OnExit() + global conf + del conf + +if __name__ == '__main__': + main()