X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9389458452c806130c8c014509897768e45ae155..bdba6fdc0db2329dbe2d4fe45262631b1b506caa:/wxPython/wx/tools/XRCed/xrced.py diff --git a/wxPython/wx/tools/XRCed/xrced.py b/wxPython/wx/tools/XRCed/xrced.py index f4ad0cfcfa..c41e9592cc 100644 --- a/wxPython/wx/tools/XRCed/xrced.py +++ b/wxPython/wx/tools/XRCed/xrced.py @@ -28,6 +28,7 @@ from xml.parsers import expat 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 @@ -39,6 +40,9 @@ if __name__ == '__main__': else: basePath = os.path.dirname(__file__) +# Remember system path +sys_path = sys.path + # 1 adds CMD command to Help menu debug = 0 @@ -50,7 +54,7 @@ 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. +Consult README.txt file for the details. """ defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', @@ -70,13 +74,13 @@ class ScrolledMessageDialog(wx.Dialog): wx.DefaultSize, wx.TE_MULTILINE | wx.TE_READONLY) text.SetFont(g.modernFont()) dc = wx.WindowDC(text) - # !!! possible bug - GetTextExtent without font returns sysfont dims 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) - ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) + 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) @@ -126,6 +130,9 @@ class Frame(wx.Frame): 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') @@ -160,17 +167,28 @@ class Frame(wx.Frame): 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\tCtrl-A', + 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\tCtrl-H', 'Close test window') + 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...', 'View the README file') + 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') @@ -212,6 +230,15 @@ class Frame(wx.Frame): '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) @@ -226,6 +253,7 @@ class Frame(wx.Frame): 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) @@ -244,6 +272,11 @@ class Frame(wx.Frame): 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) @@ -285,11 +318,10 @@ class Frame(wx.Frame): tree.RegisterKeyEvents() - # !!! frame styles are broken - # Miniframe for not embedded mode - miniFrame = wx.Frame(self, -1, 'Properties & Style', - (conf.panelX, conf.panelY), - (conf.panelWidth, conf.panelHeight)) + # 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) @@ -311,9 +343,6 @@ class Frame(wx.Frame): self.SetAutoLayout(True) self.SetSizer(sizer) - # Initialize - self.Clear() - # Other events wx.EVT_IDLE(self, self.OnIdle) wx.EVT_CLOSE(self, self.OnCloseWindow) @@ -373,6 +402,8 @@ class Frame(wx.Frame): 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() @@ -443,7 +474,17 @@ class Frame(wx.Frame): 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] + dlg.Destroy() def OnExit(self, evt): self.Close() @@ -452,20 +493,28 @@ class Frame(wx.Frame): # 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(): - data = wx.CustomDataObject('XRCED') - # Set encoding in header - # (False,True) - s = xxx.element.toxml(encoding=expat.native_encoding) + 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() @@ -500,60 +549,41 @@ class Frame(wx.Frame): parent = tree.GetPyData(parentLeaf).treeObject() # Create a copy of clipboard pickled element - success = False + success = success_node = False if wx.TheClipboard.Open(): - data = wx.CustomDataObject('XRCED') - if wx.TheClipboard.IsSupported(data.GetFormat()): - success = wx.TheClipboard.GetData(data) - wx.TheClipboard.Close() + 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: + 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 - elem = minidom.parseString(xml).childNodes[0] + 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 - error = False - # Top-level - x = xxx.treeObject() - if x.__class__ in [xxxDialog, xxxFrame, xxxWizard]: - # Top-level classes - if parent.__class__ != xxxMainNode: error = True - elif x.__class__ == xxxMenuBar: - # Menubar can be put in frame or dialog - if parent.__class__ not in [xxxMainNode, xxxFrame, xxxDialog]: 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] and \ - not parent.isSizer: 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__ 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, x.className)) - return + 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 @@ -564,9 +594,9 @@ class Frame(wx.Frame): ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ (parentIsBook and not isinstance(xxx, xxxPage)) or \ not (parent.isSizer or parentIsBook)): - elem.removeChild(xxx.child.element) # detach child + elem.removeChild(xxx.child.node) # detach child elem.unlink() # delete child container - elem = xxx.child.element # replace + elem = xxx.child.node # replace # This may help garbage collection xxx.child.parent = None isChildContainer = False @@ -607,6 +637,224 @@ class Frame(wx.Frame): 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 + + # Undo info + self.lastOp = 'MOVEUP' + status = 'Moved before previous sibling' + + # Prepare undo data + panel.Apply() + + 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 + + # Undo info + self.lastOp = 'MOVEDOWN' + status = 'Moved after next sibling' + + # Prepare undo data + panel.Apply() + + 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 + + # 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.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 + + # 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.SelectItem(selected) + + undoMan.RegisterUndo(UndoMove(oldParent, oldIndex, newParent, newIndex)) + + self.modified = True + self.SetStatusText(status) + def OnCutDelete(self, evt): selected = tree.selection @@ -632,14 +880,19 @@ class Frame(wx.Frame): # 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(): - data = wx.CustomDataObject('XRCED') - # (False, True) - s = elem.toxml(encoding=expat.native_encoding) + 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() @@ -657,7 +910,7 @@ class Frame(wx.Frame): def OnSubclass(self, evt): selected = tree.selection xxx = tree.GetPyData(selected).treeObject() - elem = xxx.element + elem = xxx.node subclass = xxx.subclass dlg = wx.TextEntryDialog(self, 'Subclass:', defaultValue=subclass) if dlg.ShowModal() == wx.ID_OK: @@ -700,6 +953,7 @@ class Frame(wx.Frame): 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) @@ -853,29 +1107,36 @@ Homepage: http://xrced.sourceforge.net\ ref = wx.GetTextFromUser('Create reference to:', 'Create reference') if not ref: return xxx = MakeEmptyRefXXX(parent, ref) + elif evt.GetId() == ID_NEW.COMMENT: + xxx = MakeEmptyCommentXXX(parent) else: # Create empty element - className = pullDownMenu.createMap[evt.GetId()] + if evt.GetId() >= ID_NEW.CUSTOM: + className = pullDownMenu.customMap[evt.GetId()] + else: + 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.setTreeName('%s%d' % (defaultIDs[cl], frame.maxIDs[cl])) - # And for some other standard controls - elif parent.__class__ == xxxStdDialogButtonSizer: - xxx.setTreeName(pullDownMenu.stdButtonIDs[evt.GetId()][0]) - # We can even set label - obj = xxx.treeObject() - elem = g.tree.dom.createElement('label') - elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[evt.GetId()][1])) - obj.params['label'] = xxxParam(elem) - xxx.treeObject().element.appendChild(elem) - # Insert new node, register undo - elem = xxx.element - newItem = tree.InsertNode(parentLeaf, parent, elem, nextItem) + 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: + xxx.setTreeName(pullDownMenu.stdButtonIDs[evt.GetId()][0]) + # We can even set label + obj = xxx.treeObject() + elem = g.tree.dom.createElement('label') + elem.appendChild(g.tree.dom.createTextNode(pullDownMenu.stdButtonIDs[evt.GetId()][1])) + obj.params['label'] = xxxParam(elem) + xxx.treeObject().node.appendChild(elem) + + newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem) + else: # comment node + newItem = tree.InsertNode(parentLeaf, parent, xxx.node, nextItem) undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) tree.EnsureVisible(newItem) tree.SelectItem(newItem) @@ -883,24 +1144,27 @@ Homepage: http://xrced.sourceforge.net\ tree.ScrollTo(newItem) tree.Refresh() # Update view? - if g.testWin and tree.IsHighlatable(newItem): + 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() # Replace one object with another def OnReplace(self, evt): selected = tree.selection xxx = tree.GetPyData(selected).treeObject() - elem = xxx.element + 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': @@ -936,7 +1200,7 @@ Homepage: http://xrced.sourceforge.net\ sizeritem.removeChild(elem) elem.unlink() elem = sizeritem - tree.GetPyData(selected).hasChild = false + tree.GetPyData(selected).hasChild = False elif xxx.className == 'spacer' and className != 'spacer': # Create sizeritem element assert xxx.parent.isSizer @@ -960,6 +1224,17 @@ Homepage: http://xrced.sourceforge.net\ 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) @@ -972,9 +1247,7 @@ Homepage: http://xrced.sourceforge.net\ # Update tree if tree.GetPyData(selected).hasChild: # child container container = tree.GetPyData(selected) - container.child = xxx - container.hasChildren = xxx.hasChildren - container.isSizer = xxx.isSizer + container.resetChild(xxx) xxx = container else: tree.SetPyData(selected, xxx) @@ -1065,14 +1338,18 @@ Homepage: http://xrced.sourceforge.net\ return def OnIconize(self, evt): - conf.x, conf.y = self.GetPosition() - conf.width, conf.height = self.GetSize() - if conf.embedPanel: - conf.sashPos = self.splitter.GetSashPosition() + 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: - conf.panelX, conf.panelY = self.miniFrame.GetPosition() - conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() - self.miniFrame.Iconize() + if not conf.embedPanel: + self.miniFrame.Iconize(False) evt.Skip() def OnCloseWindow(self, evt): @@ -1093,13 +1370,11 @@ Homepage: http://xrced.sourceforge.net\ 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 @@ -1116,6 +1391,11 @@ Homepage: http://xrced.sourceforge.net\ 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]) def SetModified(self, state=True): self.modified = state @@ -1146,6 +1426,8 @@ Homepage: http://xrced.sourceforge.net\ 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) @@ -1159,6 +1441,10 @@ Homepage: http://xrced.sourceforge.net\ 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 @@ -1172,7 +1458,8 @@ Homepage: http://xrced.sourceforge.net\ node.appendChild(text) # Indent children which are elements for n in children: - if n.nodeType == minidom.Node.ELEMENT_NODE: + if n.nodeType == minidom.Node.ELEMENT_NODE or \ + n.nodeType == minidom.Node.COMMENT_NODE: self.Indent(n, indent + 2) def Save(self, path): @@ -1295,9 +1582,74 @@ class PythonOptions(wx.Dialog): 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'] + 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) + + 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]' @@ -1354,11 +1706,45 @@ Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion) conf.panelWidth = conf.ReadInt('panelWidth', 200) conf.panelHeight = conf.ReadInt('panelHeight', 200) conf.panic = not conf.HasEntry('nopanic') + # Preferences + p = 'Prefs/sizeritem_defaults_panel' + if conf.HasEntry(p): + sys.modules['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)) + # 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 = set(sys.modules.keys()) + + # Initialize + frame.Clear() # Load file after showing if args: @@ -1386,6 +1772,12 @@ Please upgrade wxWidgets to %d.%d.%d or higher.''' % MinWxVersion) wc.WriteInt('panelHeight', conf.panelHeight) wc.WriteInt('nopanic', True) wc.Write('recentFiles', '|'.join(conf.recentfiles.values()[-5:])) + # Preferences + wc.DeleteGroup('Prefs') + v = sys.modules['xxx'].xxxSizerItem.defaults_panel + if v: wc.Write('Prefs/sizeritem_defaults_panel', DictToString(v)) + v = sys.modules['xxx'].xxxSizerItem.defaults_control + if v: wc.Write('Prefs/sizeritem_defaults_control', DictToString(v)) wc.Flush() def main():