| 1 | # Name: xrced.py |
| 2 | # Purpose: XRC editor, main module |
| 3 | # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be> |
| 4 | # Created: 20.08.2001 |
| 5 | # RCS-ID: $Id$ |
| 6 | |
| 7 | """ |
| 8 | |
| 9 | xrced -- Simple resource editor for XRC format used by wxWindows/wxPython |
| 10 | GUI toolkit. |
| 11 | |
| 12 | Usage: |
| 13 | |
| 14 | xrced [ -h ] [ -v ] [ XRC-file ] |
| 15 | |
| 16 | Options: |
| 17 | |
| 18 | -h output short usage info and exit |
| 19 | |
| 20 | -v output version info and exit |
| 21 | """ |
| 22 | |
| 23 | |
| 24 | from globals import * |
| 25 | import os, sys, getopt, re, traceback |
| 26 | |
| 27 | # Local modules |
| 28 | from tree import * # imports xxx which imports params |
| 29 | from panel import * |
| 30 | from tools import * |
| 31 | # Cleanup recursive import sideeffects, otherwise we can't create undoMan |
| 32 | import undo |
| 33 | undo.ParamPage = ParamPage |
| 34 | undoMan = g.undoMan = UndoManager() |
| 35 | |
| 36 | # Set application path for loading resources |
| 37 | if __name__ == '__main__': |
| 38 | basePath = os.path.dirname(sys.argv[0]) |
| 39 | else: |
| 40 | basePath = os.path.dirname(__file__) |
| 41 | |
| 42 | # 1 adds CMD command to Help menu |
| 43 | debug = 0 |
| 44 | |
| 45 | g.helpText = """\ |
| 46 | <HTML><H2>Welcome to XRC<font color="blue">ed</font></H2><H3><font color="green">DON'T PANIC :)</font></H3> |
| 47 | Read this note before clicking on anything!<P> |
| 48 | To start select tree root, then popup menu with your right mouse button, |
| 49 | select "Append Child", and then any command.<P> |
| 50 | Or just press one of the buttons on the tools palette.<P> |
| 51 | Enter XML ID, change properties, create children.<P> |
| 52 | To test your interface select Test command (View menu).<P> |
| 53 | Consult README file for the details.</HTML> |
| 54 | """ |
| 55 | |
| 56 | defaultIDs = {xxxPanel:'PANEL', xxxDialog:'DIALOG', xxxFrame:'FRAME', |
| 57 | xxxMenuBar:'MENUBAR', xxxMenu:'MENU', xxxToolBar:'TOOLBAR'} |
| 58 | |
| 59 | ################################################################################ |
| 60 | |
| 61 | # ScrolledMessageDialog - modified from wxPython lib to set fixed-width font |
| 62 | class ScrolledMessageDialog(wxDialog): |
| 63 | def __init__(self, parent, msg, caption, pos = wxDefaultPosition, size = (500,300)): |
| 64 | from wxPython.lib.layoutf import Layoutf |
| 65 | wxDialog.__init__(self, parent, -1, caption, pos, size) |
| 66 | text = wxTextCtrl(self, -1, msg, wxDefaultPosition, |
| 67 | wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY) |
| 68 | text.SetFont(g.modernFont()) |
| 69 | dc = wxWindowDC(text) |
| 70 | # !!! possible bug - GetTextExtent without font returns sysfont dims |
| 71 | w, h = dc.GetFullTextExtent(' ', g.modernFont())[:2] |
| 72 | ok = wxButton(self, wxID_OK, "OK") |
| 73 | text.SetConstraints(Layoutf('t=t5#1;b=t5#2;l=l5#1;r=r5#1', (self,ok))) |
| 74 | text.SetSize((w * 80 + 30, h * 40)) |
| 75 | ok.SetConstraints(Layoutf('b=b5#1;x%w50#1;w!80;h!25', (self,))) |
| 76 | self.SetAutoLayout(True) |
| 77 | self.Fit() |
| 78 | self.CenterOnScreen(wxBOTH) |
| 79 | |
| 80 | ################################################################################ |
| 81 | |
| 82 | class Frame(wxFrame): |
| 83 | def __init__(self, pos, size): |
| 84 | wxFrame.__init__(self, None, -1, '', pos, size) |
| 85 | global frame |
| 86 | frame = g.frame = self |
| 87 | bar = self.CreateStatusBar(2) |
| 88 | bar.SetStatusWidths([-1, 40]) |
| 89 | self.SetIcon(images.getIconIcon()) |
| 90 | |
| 91 | # Idle flag |
| 92 | self.inIdle = False |
| 93 | |
| 94 | # Make menus |
| 95 | menuBar = wxMenuBar() |
| 96 | |
| 97 | menu = wxMenu() |
| 98 | menu.Append(wxID_NEW, '&New\tCtrl-N', 'New file') |
| 99 | menu.AppendSeparator() |
| 100 | menu.Append(wxID_OPEN, '&Open...\tCtrl-O', 'Open XRC file') |
| 101 | self.recentMenu = wxMenu() |
| 102 | self.AppendRecent(self.recentMenu) |
| 103 | menu.AppendMenu(-1, 'Open Recent', self.recentMenu, 'Open a recent file') |
| 104 | menu.AppendSeparator() |
| 105 | menu.Append(wxID_SAVE, '&Save\tCtrl-S', 'Save XRC file') |
| 106 | menu.Append(wxID_SAVEAS, 'Save &As...', 'Save XRC file under different name') |
| 107 | menu.AppendSeparator() |
| 108 | menu.Append(wxID_EXIT, '&Quit\tCtrl-Q', 'Exit application') |
| 109 | |
| 110 | menuBar.Append(menu, '&File') |
| 111 | |
| 112 | menu = wxMenu() |
| 113 | menu.Append(wxID_UNDO, '&Undo\tCtrl-Z', 'Undo') |
| 114 | menu.Append(wxID_REDO, '&Redo\tCtrl-Y', 'Redo') |
| 115 | menu.AppendSeparator() |
| 116 | menu.Append(wxID_CUT, 'Cut\tCtrl-X', 'Cut to the clipboard') |
| 117 | menu.Append(wxID_COPY, '&Copy\tCtrl-C', 'Copy to the clipboard') |
| 118 | menu.Append(wxID_PASTE, '&Paste\tCtrl-V', 'Paste from the clipboard') |
| 119 | self.ID_DELETE = wxNewId() |
| 120 | menu.Append(self.ID_DELETE, '&Delete\tCtrl-D', 'Delete object') |
| 121 | # menu.AppendSeparator() |
| 122 | ID_SELECT = wxNewId() |
| 123 | # menu.Append(ID_SELECT, '&Select', 'Select object') |
| 124 | menuBar.Append(menu, '&Edit') |
| 125 | |
| 126 | menu = wxMenu() |
| 127 | self.ID_EMBED_PANEL = wxNewId() |
| 128 | menu.Append(self.ID_EMBED_PANEL, '&Embed Panel', |
| 129 | 'Toggle embedding properties panel in the main window', True) |
| 130 | menu.Check(self.ID_EMBED_PANEL, conf.embedPanel) |
| 131 | self.ID_SHOW_TOOLS = wxNewId() |
| 132 | menu.Append(self.ID_SHOW_TOOLS, 'Show &Tools', 'Toggle tools', True) |
| 133 | menu.Check(self.ID_SHOW_TOOLS, conf.showTools) |
| 134 | menu.AppendSeparator() |
| 135 | self.ID_TEST = wxNewId() |
| 136 | menu.Append(self.ID_TEST, '&Test\tF5', 'Test window') |
| 137 | self.ID_REFRESH = wxNewId() |
| 138 | menu.Append(self.ID_REFRESH, '&Refresh\tCtrl-R', 'Refresh test window') |
| 139 | self.ID_AUTO_REFRESH = wxNewId() |
| 140 | menu.Append(self.ID_AUTO_REFRESH, '&Auto-refresh\tCtrl-A', |
| 141 | 'Toggle auto-refresh mode', True) |
| 142 | menu.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) |
| 143 | menuBar.Append(menu, '&View') |
| 144 | |
| 145 | menu = wxMenu() |
| 146 | menu.Append(wxID_ABOUT, '&About...', 'About XCRed') |
| 147 | self.ID_README = wxNewId() |
| 148 | menu.Append(self.ID_README, '&Readme...', 'View the README file') |
| 149 | if debug: |
| 150 | self.ID_DEBUG_CMD = wxNewId() |
| 151 | menu.Append(self.ID_DEBUG_CMD, 'CMD', 'Python command line') |
| 152 | EVT_MENU(self, self.ID_DEBUG_CMD, self.OnDebugCMD) |
| 153 | menuBar.Append(menu, '&Help') |
| 154 | |
| 155 | self.menuBar = menuBar |
| 156 | self.SetMenuBar(menuBar) |
| 157 | |
| 158 | # Create toolbar |
| 159 | tb = self.CreateToolBar(wxTB_HORIZONTAL | wxNO_BORDER | wxTB_FLAT) |
| 160 | tb.SetToolBitmapSize((24, 23)) |
| 161 | tb.AddSimpleTool(wxID_NEW, images.getNewBitmap(), 'New', 'New file') |
| 162 | tb.AddSimpleTool(wxID_OPEN, images.getOpenBitmap(), 'Open', 'Open file') |
| 163 | tb.AddSimpleTool(wxID_SAVE, images.getSaveBitmap(), 'Save', 'Save file') |
| 164 | tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) |
| 165 | tb.AddSimpleTool(wxID_UNDO, images.getUndoBitmap(), 'Undo', 'Undo') |
| 166 | tb.AddSimpleTool(wxID_REDO, images.getRedoBitmap(), 'Redo', 'Redo') |
| 167 | tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) |
| 168 | tb.AddSimpleTool(wxID_CUT, images.getCutBitmap(), 'Cut', 'Cut') |
| 169 | tb.AddSimpleTool(wxID_COPY, images.getCopyBitmap(), 'Copy', 'Copy') |
| 170 | tb.AddSimpleTool(wxID_PASTE, images.getPasteBitmap(), 'Paste', 'Paste') |
| 171 | tb.AddControl(wxStaticLine(tb, -1, size=(-1,23), style=wxLI_VERTICAL)) |
| 172 | tb.AddSimpleTool(self.ID_TEST, images.getTestBitmap(), 'Test', 'Test window') |
| 173 | tb.AddSimpleTool(self.ID_REFRESH, images.getRefreshBitmap(), |
| 174 | 'Refresh', 'Refresh view') |
| 175 | tb.AddSimpleTool(self.ID_AUTO_REFRESH, images.getAutoRefreshBitmap(), |
| 176 | 'Auto-refresh', 'Toggle auto-refresh mode', True) |
| 177 | if wxPlatform == '__WXGTK__': |
| 178 | tb.AddSeparator() # otherwise auto-refresh sticks in status line |
| 179 | tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) |
| 180 | tb.Realize() |
| 181 | self.tb = tb |
| 182 | self.minWidth = tb.GetSize()[0] # minimal width is the size of toolbar |
| 183 | |
| 184 | # File |
| 185 | EVT_MENU(self, wxID_NEW, self.OnNew) |
| 186 | EVT_MENU(self, wxID_OPEN, self.OnOpen) |
| 187 | EVT_MENU(self, wxID_SAVE, self.OnSaveOrSaveAs) |
| 188 | EVT_MENU(self, wxID_SAVEAS, self.OnSaveOrSaveAs) |
| 189 | EVT_MENU(self, wxID_EXIT, self.OnExit) |
| 190 | # Edit |
| 191 | EVT_MENU(self, wxID_UNDO, self.OnUndo) |
| 192 | EVT_MENU(self, wxID_REDO, self.OnRedo) |
| 193 | EVT_MENU(self, wxID_CUT, self.OnCutDelete) |
| 194 | EVT_MENU(self, wxID_COPY, self.OnCopy) |
| 195 | EVT_MENU(self, wxID_PASTE, self.OnPaste) |
| 196 | EVT_MENU(self, self.ID_DELETE, self.OnCutDelete) |
| 197 | EVT_MENU(self, ID_SELECT, self.OnSelect) |
| 198 | # View |
| 199 | EVT_MENU(self, self.ID_EMBED_PANEL, self.OnEmbedPanel) |
| 200 | EVT_MENU(self, self.ID_SHOW_TOOLS, self.OnShowTools) |
| 201 | EVT_MENU(self, self.ID_TEST, self.OnTest) |
| 202 | EVT_MENU(self, self.ID_REFRESH, self.OnRefresh) |
| 203 | EVT_MENU(self, self.ID_AUTO_REFRESH, self.OnAutoRefresh) |
| 204 | # Help |
| 205 | EVT_MENU(self, wxID_ABOUT, self.OnAbout) |
| 206 | EVT_MENU(self, self.ID_README, self.OnReadme) |
| 207 | |
| 208 | # Update events |
| 209 | EVT_UPDATE_UI(self, wxID_CUT, self.OnUpdateUI) |
| 210 | EVT_UPDATE_UI(self, wxID_COPY, self.OnUpdateUI) |
| 211 | EVT_UPDATE_UI(self, wxID_PASTE, self.OnUpdateUI) |
| 212 | EVT_UPDATE_UI(self, wxID_UNDO, self.OnUpdateUI) |
| 213 | EVT_UPDATE_UI(self, wxID_REDO, self.OnUpdateUI) |
| 214 | EVT_UPDATE_UI(self, self.ID_DELETE, self.OnUpdateUI) |
| 215 | EVT_UPDATE_UI(self, self.ID_TEST, self.OnUpdateUI) |
| 216 | EVT_UPDATE_UI(self, self.ID_REFRESH, self.OnUpdateUI) |
| 217 | |
| 218 | # Build interface |
| 219 | sizer = wxBoxSizer(wxVERTICAL) |
| 220 | sizer.Add(wxStaticLine(self, -1), 0, wxEXPAND) |
| 221 | # Horizontal sizer for toolbar and splitter |
| 222 | self.toolsSizer = sizer1 = wxBoxSizer() |
| 223 | splitter = wxSplitterWindow(self, -1, style=wxSP_3DSASH) |
| 224 | self.splitter = splitter |
| 225 | splitter.SetMinimumPaneSize(100) |
| 226 | # Create tree |
| 227 | global tree |
| 228 | g.tree = tree = XML_Tree(splitter, -1) |
| 229 | |
| 230 | # Init pull-down menu data |
| 231 | global pullDownMenu |
| 232 | g.pullDownMenu = pullDownMenu = PullDownMenu(self) |
| 233 | |
| 234 | # Vertical toolbar for GUI buttons |
| 235 | g.tools = tools = Tools(self) |
| 236 | tools.Show(conf.showTools) |
| 237 | if conf.showTools: sizer1.Add(tools, 0, wxEXPAND) |
| 238 | |
| 239 | tree.RegisterKeyEvents() |
| 240 | |
| 241 | # !!! frame styles are broken |
| 242 | # Miniframe for not embedded mode |
| 243 | miniFrame = wxFrame(self, -1, 'Properties Panel', |
| 244 | (conf.panelX, conf.panelY), |
| 245 | (conf.panelWidth, conf.panelHeight)) |
| 246 | self.miniFrame = miniFrame |
| 247 | sizer2 = wxBoxSizer() |
| 248 | miniFrame.SetAutoLayout(True) |
| 249 | miniFrame.SetSizer(sizer2) |
| 250 | EVT_CLOSE(self.miniFrame, self.OnCloseMiniFrame) |
| 251 | # Create panel for parameters |
| 252 | global panel |
| 253 | if conf.embedPanel: |
| 254 | panel = Panel(splitter) |
| 255 | # Set plitter windows |
| 256 | splitter.SplitVertically(tree, panel, conf.sashPos) |
| 257 | else: |
| 258 | panel = Panel(miniFrame) |
| 259 | sizer2.Add(panel, 1, wxEXPAND) |
| 260 | miniFrame.Show(True) |
| 261 | splitter.Initialize(tree) |
| 262 | sizer1.Add(splitter, 1, wxEXPAND) |
| 263 | sizer.Add(sizer1, 1, wxEXPAND) |
| 264 | self.SetAutoLayout(True) |
| 265 | self.SetSizer(sizer) |
| 266 | |
| 267 | # Initialize |
| 268 | self.clipboard = None |
| 269 | self.Clear() |
| 270 | |
| 271 | # Other events |
| 272 | EVT_IDLE(self, self.OnIdle) |
| 273 | EVT_CLOSE(self, self.OnCloseWindow) |
| 274 | EVT_LEFT_DOWN(self, self.OnLeftDown) |
| 275 | EVT_KEY_DOWN(self, tools.OnKeyDown) |
| 276 | EVT_KEY_UP(self, tools.OnKeyUp) |
| 277 | |
| 278 | def AppendRecent(self, menu): |
| 279 | # add recently used files to the menu |
| 280 | for id,name in conf.recentfiles.iteritems(): |
| 281 | menu.Append(id,name) |
| 282 | EVT_MENU(self,id,self.OnRecentFile) |
| 283 | return |
| 284 | |
| 285 | def OnRecentFile(self,evt): |
| 286 | # open recently used file |
| 287 | if not self.AskSave(): return |
| 288 | wxBeginBusyCursor() |
| 289 | try: |
| 290 | path=conf.recentfiles[evt.GetId()] |
| 291 | if self.Open(path): |
| 292 | self.SetStatusText('Data loaded') |
| 293 | else: |
| 294 | self.SetStatusText('Failed') |
| 295 | except KeyError: |
| 296 | self.SetStatusText('No such file') |
| 297 | wxEndBusyCursor() |
| 298 | |
| 299 | def OnNew(self, evt): |
| 300 | if not self.AskSave(): return |
| 301 | self.Clear() |
| 302 | |
| 303 | def OnOpen(self, evt): |
| 304 | if not self.AskSave(): return |
| 305 | dlg = wxFileDialog(self, 'Open', os.path.dirname(self.dataFile), |
| 306 | '', '*.xrc', wxOPEN | wxCHANGE_DIR) |
| 307 | if dlg.ShowModal() == wxID_OK: |
| 308 | path = dlg.GetPath() |
| 309 | self.SetStatusText('Loading...') |
| 310 | wxYield() |
| 311 | wxBeginBusyCursor() |
| 312 | if self.Open(path): |
| 313 | self.SetStatusText('Data loaded') |
| 314 | else: |
| 315 | self.SetStatusText('Failed') |
| 316 | self.SaveRecent(path) |
| 317 | wxEndBusyCursor() |
| 318 | dlg.Destroy() |
| 319 | |
| 320 | def OnSaveOrSaveAs(self, evt): |
| 321 | if evt.GetId() == wxID_SAVEAS or not self.dataFile: |
| 322 | if self.dataFile: defaultName = '' |
| 323 | else: defaultName = 'UNTITLED.xrc' |
| 324 | dlg = wxFileDialog(self, 'Save As', os.path.dirname(self.dataFile), |
| 325 | defaultName, '*.xrc', |
| 326 | wxSAVE | wxOVERWRITE_PROMPT | wxCHANGE_DIR) |
| 327 | if dlg.ShowModal() == wxID_OK: |
| 328 | path = dlg.GetPath() |
| 329 | dlg.Destroy() |
| 330 | else: |
| 331 | dlg.Destroy() |
| 332 | return |
| 333 | else: |
| 334 | path = self.dataFile |
| 335 | self.SetStatusText('Saving...') |
| 336 | wxYield() |
| 337 | wxBeginBusyCursor() |
| 338 | try: |
| 339 | self.Save(path) |
| 340 | self.dataFile = path |
| 341 | self.SetStatusText('Data saved') |
| 342 | self.SaveRecent(path) |
| 343 | except IOError: |
| 344 | self.SetStatusText('Failed') |
| 345 | wxEndBusyCursor() |
| 346 | |
| 347 | def SaveRecent(self,path): |
| 348 | # append to recently used files |
| 349 | if path not in conf.recentfiles.values(): |
| 350 | newid = wxNewId() |
| 351 | self.recentMenu.Append(newid, path) |
| 352 | EVT_MENU(self, newid, self.OnRecentFile) |
| 353 | conf.recentfiles[newid] = path |
| 354 | |
| 355 | def OnExit(self, evt): |
| 356 | self.Close() |
| 357 | |
| 358 | def OnUndo(self, evt): |
| 359 | # Extra check to not mess with idle updating |
| 360 | if undoMan.CanUndo(): |
| 361 | undoMan.Undo() |
| 362 | |
| 363 | def OnRedo(self, evt): |
| 364 | if undoMan.CanRedo(): |
| 365 | undoMan.Redo() |
| 366 | |
| 367 | def OnCopy(self, evt): |
| 368 | selected = tree.selection |
| 369 | if not selected: return # key pressed event |
| 370 | xxx = tree.GetPyData(selected) |
| 371 | self.clipboard = xxx.element.cloneNode(True) |
| 372 | self.SetStatusText('Copied') |
| 373 | |
| 374 | def OnPaste(self, evt): |
| 375 | selected = tree.selection |
| 376 | if not selected: return # key pressed event |
| 377 | # For pasting with Ctrl pressed |
| 378 | if evt.GetId() == pullDownMenu.ID_PASTE_SIBLING: appendChild = False |
| 379 | else: appendChild = not tree.NeedInsert(selected) |
| 380 | xxx = tree.GetPyData(selected) |
| 381 | if not appendChild: |
| 382 | # If has next item, insert, else append to parent |
| 383 | nextItem = tree.GetNextSibling(selected) |
| 384 | parentLeaf = tree.GetItemParent(selected) |
| 385 | # Expanded container (must have children) |
| 386 | elif tree.IsExpanded(selected) and tree.GetChildrenCount(selected, False): |
| 387 | # Insert as first child |
| 388 | nextItem = tree.GetFirstChild(selected)[0] |
| 389 | parentLeaf = selected |
| 390 | else: |
| 391 | # No children or unexpanded item - appendChild stays True |
| 392 | nextItem = wxTreeItemId() # no next item |
| 393 | parentLeaf = selected |
| 394 | parent = tree.GetPyData(parentLeaf).treeObject() |
| 395 | |
| 396 | # Create a copy of clipboard element |
| 397 | elem = self.clipboard.cloneNode(True) |
| 398 | # Tempopary xxx object to test things |
| 399 | xxx = MakeXXXFromDOM(parent, elem) |
| 400 | |
| 401 | # Check compatibility |
| 402 | error = False |
| 403 | # Top-level |
| 404 | x = xxx.treeObject() |
| 405 | if x.__class__ in [xxxDialog, xxxFrame, xxxMenuBar]: |
| 406 | # Top-level classes |
| 407 | if parent.__class__ != xxxMainNode: error = True |
| 408 | elif x.__class__ == xxxToolBar: |
| 409 | # Toolbar can be top-level of child of panel or frame |
| 410 | if parent.__class__ not in [xxxMainNode, xxxPanel, xxxFrame]: error = True |
| 411 | elif x.__class__ == xxxPanel and parent.__class__ == xxxMainNode: |
| 412 | pass |
| 413 | elif x.__class__ == xxxSpacer: |
| 414 | if not parent.isSizer: error = True |
| 415 | elif x.__class__ == xxxSeparator: |
| 416 | if not parent.__class__ in [xxxMenu, xxxToolBar]: error = True |
| 417 | elif x.__class__ == xxxTool: |
| 418 | if parent.__class__ != xxxToolBar: error = True |
| 419 | elif x.__class__ == xxxMenu: |
| 420 | if not parent.__class__ in [xxxMainNode, xxxMenuBar, xxxMenu]: error = True |
| 421 | elif x.__class__ == xxxMenuItem: |
| 422 | if not parent.__class__ in [xxxMenuBar, xxxMenu]: error = True |
| 423 | elif x.isSizer and parent.__class__ == xxxNotebook: error = True |
| 424 | else: # normal controls can be almost anywhere |
| 425 | if parent.__class__ == xxxMainNode or \ |
| 426 | parent.__class__ in [xxxMenuBar, xxxMenu]: error = True |
| 427 | if error: |
| 428 | if parent.__class__ == xxxMainNode: parentClass = 'root' |
| 429 | else: parentClass = parent.className |
| 430 | wxLogError('Incompatible parent/child: parent is %s, child is %s!' % |
| 431 | (parentClass, x.className)) |
| 432 | return |
| 433 | |
| 434 | # Check parent and child relationships. |
| 435 | # If parent is sizer or notebook, child is of wrong class or |
| 436 | # parent is normal window, child is child container then detach child. |
| 437 | isChildContainer = isinstance(xxx, xxxChildContainer) |
| 438 | if isChildContainer and \ |
| 439 | ((parent.isSizer and not isinstance(xxx, xxxSizerItem)) or \ |
| 440 | (isinstance(parent, xxxNotebook) and not isinstance(xxx, xxxNotebookPage)) or \ |
| 441 | not (parent.isSizer or isinstance(parent, xxxNotebook))): |
| 442 | elem.removeChild(xxx.child.element) # detach child |
| 443 | elem.unlink() # delete child container |
| 444 | elem = xxx.child.element # replace |
| 445 | # This may help garbage collection |
| 446 | xxx.child.parent = None |
| 447 | isChildContainer = False |
| 448 | # Parent is sizer or notebook, child is not child container |
| 449 | if parent.isSizer and not isChildContainer and not isinstance(xxx, xxxSpacer): |
| 450 | # Create sizer item element |
| 451 | sizerItemElem = MakeEmptyDOM('sizeritem') |
| 452 | sizerItemElem.appendChild(elem) |
| 453 | elem = sizerItemElem |
| 454 | elif isinstance(parent, xxxNotebook) and not isChildContainer: |
| 455 | pageElem = MakeEmptyDOM('notebookpage') |
| 456 | pageElem.appendChild(elem) |
| 457 | elem = pageElem |
| 458 | # Insert new node, register undo |
| 459 | newItem = tree.InsertNode(parentLeaf, parent, elem, nextItem) |
| 460 | undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) |
| 461 | # Scroll to show new item (!!! redundant?) |
| 462 | tree.EnsureVisible(newItem) |
| 463 | tree.SelectItem(newItem) |
| 464 | if not tree.IsVisible(newItem): |
| 465 | tree.ScrollTo(newItem) |
| 466 | tree.Refresh() |
| 467 | # Update view? |
| 468 | if g.testWin and tree.IsHighlatable(newItem): |
| 469 | if conf.autoRefresh: |
| 470 | tree.needUpdate = True |
| 471 | tree.pendingHighLight = newItem |
| 472 | else: |
| 473 | tree.pendingHighLight = None |
| 474 | self.modified = True |
| 475 | self.SetStatusText('Pasted') |
| 476 | |
| 477 | def OnCutDelete(self, evt): |
| 478 | selected = tree.selection |
| 479 | if not selected: return # key pressed event |
| 480 | # Undo info |
| 481 | if evt.GetId() == wxID_CUT: |
| 482 | self.lastOp = 'CUT' |
| 483 | status = 'Removed to clipboard' |
| 484 | else: |
| 485 | self.lastOp = 'DELETE' |
| 486 | status = 'Deleted' |
| 487 | # Delete testWin? |
| 488 | if g.testWin: |
| 489 | # If deleting top-level item, delete testWin |
| 490 | if selected == g.testWin.item: |
| 491 | g.testWin.Destroy() |
| 492 | g.testWin = None |
| 493 | else: |
| 494 | # Remove highlight, update testWin |
| 495 | if g.testWin.highLight: |
| 496 | g.testWin.highLight.Remove() |
| 497 | tree.needUpdate = True |
| 498 | # Prepare undo data |
| 499 | panel.Apply() |
| 500 | index = tree.ItemFullIndex(selected) |
| 501 | parent = tree.GetPyData(tree.GetItemParent(selected)).treeObject() |
| 502 | elem = tree.RemoveLeaf(selected) |
| 503 | undoMan.RegisterUndo(UndoCutDelete(index, parent, elem)) |
| 504 | if evt.GetId() == wxID_CUT: |
| 505 | if self.clipboard: self.clipboard.unlink() |
| 506 | self.clipboard = elem.cloneNode(True) |
| 507 | tree.pendingHighLight = None |
| 508 | tree.Unselect() |
| 509 | panel.Clear() |
| 510 | self.modified = True |
| 511 | self.SetStatusText(status) |
| 512 | |
| 513 | def OnSubclass(self, evt): |
| 514 | selected = tree.selection |
| 515 | xxx = tree.GetPyData(selected).treeObject() |
| 516 | elem = xxx.element |
| 517 | subclass = xxx.subclass |
| 518 | dlg = wxTextEntryDialog(self, 'Subclass:', defaultValue=subclass) |
| 519 | if dlg.ShowModal() == wxID_OK: |
| 520 | subclass = dlg.GetValue() |
| 521 | if subclass: |
| 522 | elem.setAttribute('subclass', subclass) |
| 523 | self.modified = True |
| 524 | elif elem.hasAttribute('subclass'): |
| 525 | elem.removeAttribute('subclass') |
| 526 | self.modified = True |
| 527 | xxx.subclass = elem.getAttribute('subclass') |
| 528 | tree.SetItemText(selected, xxx.treeName()) |
| 529 | panel.pages[0].box.SetLabel(xxx.panelName()) |
| 530 | dlg.Destroy() |
| 531 | |
| 532 | def OnSelect(self, evt): |
| 533 | print >> sys.stderr, 'Xperimental function!' |
| 534 | wxYield() |
| 535 | self.SetCursor(wxCROSS_CURSOR) |
| 536 | self.CaptureMouse() |
| 537 | |
| 538 | def OnLeftDown(self, evt): |
| 539 | pos = evt.GetPosition() |
| 540 | self.SetCursor(wxNullCursor) |
| 541 | self.ReleaseMouse() |
| 542 | |
| 543 | def OnEmbedPanel(self, evt): |
| 544 | conf.embedPanel = evt.IsChecked() |
| 545 | if conf.embedPanel: |
| 546 | # Remember last dimentions |
| 547 | conf.panelX, conf.panelY = self.miniFrame.GetPosition() |
| 548 | conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() |
| 549 | size = self.GetSize() |
| 550 | pos = self.GetPosition() |
| 551 | sizePanel = panel.GetSize() |
| 552 | panel.Reparent(self.splitter) |
| 553 | self.miniFrame.GetSizer().Remove(panel) |
| 554 | wxYield() |
| 555 | # Widen |
| 556 | self.SetDimensions(pos.x, pos.y, size.width + sizePanel.width, size.height) |
| 557 | self.splitter.SplitVertically(tree, panel, conf.sashPos) |
| 558 | self.miniFrame.Show(False) |
| 559 | else: |
| 560 | conf.sashPos = self.splitter.GetSashPosition() |
| 561 | pos = self.GetPosition() |
| 562 | size = self.GetSize() |
| 563 | sizePanel = panel.GetSize() |
| 564 | self.splitter.Unsplit(panel) |
| 565 | sizer = self.miniFrame.GetSizer() |
| 566 | panel.Reparent(self.miniFrame) |
| 567 | panel.Show(True) |
| 568 | sizer.Add(panel, 1, wxEXPAND) |
| 569 | self.miniFrame.Show(True) |
| 570 | self.miniFrame.SetDimensions(conf.panelX, conf.panelY, |
| 571 | conf.panelWidth, conf.panelHeight) |
| 572 | wxYield() |
| 573 | # Reduce width |
| 574 | self.SetDimensions(pos.x, pos.y, |
| 575 | max(size.width - sizePanel.width, self.minWidth), size.height) |
| 576 | |
| 577 | def OnShowTools(self, evt): |
| 578 | conf.showTools = evt.IsChecked() |
| 579 | g.tools.Show(conf.showTools) |
| 580 | if conf.showTools: |
| 581 | self.toolsSizer.Prepend(g.tools, 0, wxEXPAND) |
| 582 | else: |
| 583 | self.toolsSizer.Remove(g.tools) |
| 584 | self.toolsSizer.Layout() |
| 585 | |
| 586 | def OnTest(self, evt): |
| 587 | if not tree.selection: return # key pressed event |
| 588 | tree.ShowTestWindow(tree.selection) |
| 589 | |
| 590 | def OnRefresh(self, evt): |
| 591 | # If modified, apply first |
| 592 | selection = tree.selection |
| 593 | if selection: |
| 594 | xxx = tree.GetPyData(selection) |
| 595 | if xxx and panel.IsModified(): |
| 596 | tree.Apply(xxx, selection) |
| 597 | if g.testWin: |
| 598 | # (re)create |
| 599 | tree.CreateTestWin(g.testWin.item) |
| 600 | panel.modified = False |
| 601 | tree.needUpdate = False |
| 602 | |
| 603 | def OnAutoRefresh(self, evt): |
| 604 | conf.autoRefresh = evt.IsChecked() |
| 605 | self.menuBar.Check(self.ID_AUTO_REFRESH, conf.autoRefresh) |
| 606 | self.tb.ToggleTool(self.ID_AUTO_REFRESH, conf.autoRefresh) |
| 607 | |
| 608 | def OnAbout(self, evt): |
| 609 | str = '''\ |
| 610 | XRCed version %s |
| 611 | |
| 612 | (c) Roman Rolinsky <rollrom@users.sourceforge.net> |
| 613 | Homepage: http://xrced.sourceforge.net\ |
| 614 | ''' % version |
| 615 | dlg = wxMessageDialog(self, str, 'About XRCed', wxOK | wxCENTRE) |
| 616 | dlg.ShowModal() |
| 617 | dlg.Destroy() |
| 618 | |
| 619 | def OnReadme(self, evt): |
| 620 | text = open(os.path.join(basePath, 'README.txt'), 'r').read() |
| 621 | dlg = ScrolledMessageDialog(self, text, "XRCed README") |
| 622 | dlg.ShowModal() |
| 623 | dlg.Destroy() |
| 624 | |
| 625 | # Simple emulation of python command line |
| 626 | def OnDebugCMD(self, evt): |
| 627 | import traceback |
| 628 | while 1: |
| 629 | try: |
| 630 | exec raw_input('C:\> ') |
| 631 | except EOFError: |
| 632 | print '^D' |
| 633 | break |
| 634 | except: |
| 635 | (etype, value, tb) =sys.exc_info() |
| 636 | tblist =traceback.extract_tb(tb)[1:] |
| 637 | msg =' '.join(traceback.format_exception_only(etype, value) |
| 638 | +traceback.format_list(tblist)) |
| 639 | print msg |
| 640 | |
| 641 | def OnCreate(self, evt): |
| 642 | selected = tree.selection |
| 643 | if tree.ctrl: appendChild = False |
| 644 | else: appendChild = not tree.NeedInsert(selected) |
| 645 | xxx = tree.GetPyData(selected) |
| 646 | if not appendChild: |
| 647 | # If insert before |
| 648 | if tree.shift: |
| 649 | # If has previous item, insert after it, else append to parent |
| 650 | nextItem = selected |
| 651 | parentLeaf = tree.GetItemParent(selected) |
| 652 | else: |
| 653 | # If has next item, insert, else append to parent |
| 654 | nextItem = tree.GetNextSibling(selected) |
| 655 | parentLeaf = tree.GetItemParent(selected) |
| 656 | # Expanded container (must have children) |
| 657 | elif tree.shift and tree.IsExpanded(selected) \ |
| 658 | and tree.GetChildrenCount(selected, False): |
| 659 | nextItem = tree.GetFirstChild(selected)[0] |
| 660 | parentLeaf = selected |
| 661 | else: |
| 662 | nextItem = wxTreeItemId() |
| 663 | parentLeaf = selected |
| 664 | parent = tree.GetPyData(parentLeaf) |
| 665 | if parent.hasChild: parent = parent.child |
| 666 | |
| 667 | # Create element |
| 668 | className = pullDownMenu.createMap[evt.GetId()] |
| 669 | xxx = MakeEmptyXXX(parent, className) |
| 670 | |
| 671 | # Set default name for top-level windows |
| 672 | if parent.__class__ == xxxMainNode: |
| 673 | cl = xxx.treeObject().__class__ |
| 674 | frame.maxIDs[cl] += 1 |
| 675 | xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) |
| 676 | xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) |
| 677 | |
| 678 | # Insert new node, register undo |
| 679 | elem = xxx.element |
| 680 | newItem = tree.InsertNode(parentLeaf, parent, elem, nextItem) |
| 681 | undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) |
| 682 | tree.EnsureVisible(newItem) |
| 683 | tree.SelectItem(newItem) |
| 684 | if not tree.IsVisible(newItem): |
| 685 | tree.ScrollTo(newItem) |
| 686 | tree.Refresh() |
| 687 | # Update view? |
| 688 | if g.testWin and tree.IsHighlatable(newItem): |
| 689 | if conf.autoRefresh: |
| 690 | tree.needUpdate = True |
| 691 | tree.pendingHighLight = newItem |
| 692 | else: |
| 693 | tree.pendingHighLight = None |
| 694 | tree.SetFocus() |
| 695 | self.modified = True |
| 696 | |
| 697 | # Replace one object with another |
| 698 | def OnReplace(self, evt): |
| 699 | selected = tree.selection |
| 700 | xxx = tree.GetPyData(selected).treeObject() |
| 701 | elem = xxx.element |
| 702 | parent = elem.parentNode |
| 703 | parentXXX = xxx.parent |
| 704 | # New class |
| 705 | className = pullDownMenu.createMap[evt.GetId() - 1000] |
| 706 | # Create temporary empty node (with default values) |
| 707 | dummy = MakeEmptyDOM(className) |
| 708 | xxxClass = xxxDict[className] |
| 709 | # Remove non-compatible children |
| 710 | if tree.ItemHasChildren(selected) and not xxxClass.hasChildren: |
| 711 | tree.DeleteChildren(selected) |
| 712 | nodes = elem.childNodes[:] |
| 713 | tags = [] |
| 714 | for node in nodes: |
| 715 | remove = False |
| 716 | tag = node.tagName |
| 717 | if tag == 'object': |
| 718 | if not xxxClass.hasChildren: |
| 719 | remove = True |
| 720 | elif tag not in xxxClass.allParams and \ |
| 721 | (not xxxClass.hasStyle or tag not in xxxClass.styles): |
| 722 | remove = True |
| 723 | else: |
| 724 | tags.append(tag) |
| 725 | if remove: |
| 726 | elem.removeChild(node) |
| 727 | node.unlink() |
| 728 | |
| 729 | # Copy parameters present in dummy but not in elem |
| 730 | for node in dummy.childNodes: |
| 731 | tag = node.tagName |
| 732 | if tag not in tags: |
| 733 | elem.appendChild(node.cloneNode(True)) |
| 734 | dummy.unlink() |
| 735 | # Change class name |
| 736 | elem.setAttribute('class', className) |
| 737 | # Re-create xxx element |
| 738 | xxx = MakeXXXFromDOM(parentXXX, elem) |
| 739 | # Update parent in child objects |
| 740 | if tree.ItemHasChildren(selected): |
| 741 | i, cookie = tree.GetFirstChild(selected) |
| 742 | while i.IsOk(): |
| 743 | x = tree.GetPyData(i) |
| 744 | x.parent = xxx |
| 745 | if x.hasChild: x.child.parent = xxx |
| 746 | i, cookie = tree.GetNextChild(selected, cookie) |
| 747 | |
| 748 | # Update tree |
| 749 | if tree.GetPyData(selected).hasChild: # child container |
| 750 | container = tree.GetPyData(selected) |
| 751 | container.child = xxx |
| 752 | container.hasChildren = xxx.hasChildren |
| 753 | container.isSizer = xxx.isSizer |
| 754 | else: |
| 755 | tree.SetPyData(selected, xxx) |
| 756 | tree.SetItemText(selected, xxx.treeName()) |
| 757 | tree.SetItemImage(selected, xxx.treeImage()) |
| 758 | |
| 759 | # Set default name for top-level windows |
| 760 | if parent.__class__ == xxxMainNode: |
| 761 | cl = xxx.treeObject().__class__ |
| 762 | frame.maxIDs[cl] += 1 |
| 763 | xxx.treeObject().name = '%s%d' % (defaultIDs[cl], frame.maxIDs[cl]) |
| 764 | xxx.treeObject().element.setAttribute('name', xxx.treeObject().name) |
| 765 | |
| 766 | # Update panel |
| 767 | g.panel.SetData(xxx) |
| 768 | # Update tools |
| 769 | g.tools.UpdateUI() |
| 770 | |
| 771 | #undoMan.RegisterUndo(UndoPasteCreate(parentLeaf, parent, newItem, selected)) |
| 772 | # Update view? |
| 773 | if g.testWin and tree.IsHighlatable(selected): |
| 774 | if conf.autoRefresh: |
| 775 | tree.needUpdate = True |
| 776 | tree.pendingHighLight = selected |
| 777 | else: |
| 778 | tree.pendingHighLight = None |
| 779 | tree.SetFocus() |
| 780 | self.modified = True |
| 781 | |
| 782 | # Expand/collapse subtree |
| 783 | def OnExpand(self, evt): |
| 784 | if tree.selection: tree.ExpandAll(tree.selection) |
| 785 | else: tree.ExpandAll(tree.root) |
| 786 | def OnCollapse(self, evt): |
| 787 | if tree.selection: tree.CollapseAll(tree.selection) |
| 788 | else: tree.CollapseAll(tree.root) |
| 789 | |
| 790 | def OnPullDownHighlight(self, evt): |
| 791 | menuId = evt.GetMenuId() |
| 792 | if menuId != -1: |
| 793 | menu = evt.GetEventObject() |
| 794 | help = menu.GetHelpString(menuId) |
| 795 | self.SetStatusText(help) |
| 796 | else: |
| 797 | self.SetStatusText('') |
| 798 | |
| 799 | def OnUpdateUI(self, evt): |
| 800 | if evt.GetId() in [wxID_CUT, wxID_COPY, self.ID_DELETE]: |
| 801 | evt.Enable(tree.selection is not None and tree.selection != tree.root) |
| 802 | elif evt.GetId() == wxID_PASTE: |
| 803 | evt.Enable((self.clipboard and tree.selection) != None) |
| 804 | elif evt.GetId() == self.ID_TEST: |
| 805 | evt.Enable(tree.selection is not None and tree.selection != tree.root) |
| 806 | elif evt.GetId() == wxID_UNDO: evt.Enable(undoMan.CanUndo()) |
| 807 | elif evt.GetId() == wxID_REDO: evt.Enable(undoMan.CanRedo()) |
| 808 | |
| 809 | def OnIdle(self, evt): |
| 810 | if self.inIdle: return # Recursive call protection |
| 811 | self.inIdle = True |
| 812 | if tree.needUpdate: |
| 813 | if conf.autoRefresh: |
| 814 | if g.testWin: |
| 815 | self.SetStatusText('Refreshing test window...') |
| 816 | # (re)create |
| 817 | tree.CreateTestWin(g.testWin.item) |
| 818 | wxYield() |
| 819 | self.SetStatusText('') |
| 820 | tree.needUpdate = False |
| 821 | elif tree.pendingHighLight: |
| 822 | tree.HighLight(tree.pendingHighLight) |
| 823 | else: |
| 824 | evt.Skip() |
| 825 | self.inIdle = False |
| 826 | |
| 827 | # We don't let close panel window |
| 828 | def OnCloseMiniFrame(self, evt): |
| 829 | return |
| 830 | |
| 831 | def OnCloseWindow(self, evt): |
| 832 | if not self.AskSave(): return |
| 833 | if g.testWin: g.testWin.Destroy() |
| 834 | if not panel.GetPageCount() == 2: |
| 835 | panel.page2.Destroy() |
| 836 | else: |
| 837 | # If we don't do this, page does not get destroyed (a bug?) |
| 838 | panel.RemovePage(1) |
| 839 | if not self.IsIconized(): |
| 840 | conf.x, conf.y = self.GetPosition() |
| 841 | conf.width, conf.height = self.GetSize() |
| 842 | if conf.embedPanel: |
| 843 | conf.sashPos = self.splitter.GetSashPosition() |
| 844 | else: |
| 845 | conf.panelX, conf.panelY = self.miniFrame.GetPosition() |
| 846 | conf.panelWidth, conf.panelHeight = self.miniFrame.GetSize() |
| 847 | evt.Skip() |
| 848 | |
| 849 | def Clear(self): |
| 850 | self.dataFile = '' |
| 851 | if self.clipboard: |
| 852 | self.clipboard.unlink() |
| 853 | self.clipboard = None |
| 854 | undoMan.Clear() |
| 855 | self.modified = False |
| 856 | tree.Clear() |
| 857 | panel.Clear() |
| 858 | if g.testWin: |
| 859 | g.testWin.Destroy() |
| 860 | g.testWin = None |
| 861 | self.SetTitle(progname) |
| 862 | # Numbers for new controls |
| 863 | self.maxIDs = {} |
| 864 | self.maxIDs[xxxPanel] = self.maxIDs[xxxDialog] = self.maxIDs[xxxFrame] = \ |
| 865 | self.maxIDs[xxxMenuBar] = self.maxIDs[xxxMenu] = self.maxIDs[xxxToolBar] = 0 |
| 866 | |
| 867 | def Open(self, path): |
| 868 | if not os.path.exists(path): |
| 869 | wxLogError('File does not exists: %s' % path) |
| 870 | return False |
| 871 | # Try to read the file |
| 872 | try: |
| 873 | f = open(path) |
| 874 | self.Clear() |
| 875 | dom = minidom.parse(f) |
| 876 | f.close() |
| 877 | # Set encoding global variable |
| 878 | if dom.encoding: g.currentEncoding = dom.encoding |
| 879 | # Change dir |
| 880 | dir = os.path.dirname(path) |
| 881 | if dir: os.chdir(dir) |
| 882 | tree.SetData(dom) |
| 883 | self.dataFile = path |
| 884 | self.SetTitle(progname + ': ' + os.path.basename(path)) |
| 885 | except: |
| 886 | # Nice exception printing |
| 887 | inf = sys.exc_info() |
| 888 | wxLogError(traceback.format_exception(inf[0], inf[1], None)[-1]) |
| 889 | wxLogError('Error reading file: %s' % path) |
| 890 | return False |
| 891 | return True |
| 892 | |
| 893 | def Indent(self, node, indent = 0): |
| 894 | # Copy child list because it will change soon |
| 895 | children = node.childNodes[:] |
| 896 | # Main node doesn't need to be indented |
| 897 | if indent: |
| 898 | text = self.domCopy.createTextNode('\n' + ' ' * indent) |
| 899 | node.parentNode.insertBefore(text, node) |
| 900 | if children: |
| 901 | # Append newline after last child, except for text nodes |
| 902 | if children[-1].nodeType == minidom.Node.ELEMENT_NODE: |
| 903 | text = self.domCopy.createTextNode('\n' + ' ' * indent) |
| 904 | node.appendChild(text) |
| 905 | # Indent children which are elements |
| 906 | for n in children: |
| 907 | if n.nodeType == minidom.Node.ELEMENT_NODE: |
| 908 | self.Indent(n, indent + 2) |
| 909 | |
| 910 | def Save(self, path): |
| 911 | try: |
| 912 | import codecs |
| 913 | # Apply changes |
| 914 | if tree.selection and panel.IsModified(): |
| 915 | self.OnRefresh(wxCommandEvent()) |
| 916 | f = codecs.open(path, 'w', g.currentEncoding) |
| 917 | # Make temporary copy for formatting it |
| 918 | # !!! We can't clone dom node, it works only once |
| 919 | #self.domCopy = tree.dom.cloneNode(True) |
| 920 | self.domCopy = MyDocument() |
| 921 | mainNode = self.domCopy.appendChild(tree.mainNode.cloneNode(True)) |
| 922 | self.Indent(mainNode) |
| 923 | self.domCopy.writexml(f, encoding = g.currentEncoding) |
| 924 | f.close() |
| 925 | self.domCopy.unlink() |
| 926 | self.domCopy = None |
| 927 | self.modified = False |
| 928 | panel.SetModified(False) |
| 929 | except: |
| 930 | wxLogError('Error writing file: %s' % path) |
| 931 | raise |
| 932 | |
| 933 | def AskSave(self): |
| 934 | if not (self.modified or panel.IsModified()): return True |
| 935 | flags = wxICON_EXCLAMATION | wxYES_NO | wxCANCEL | wxCENTRE |
| 936 | dlg = wxMessageDialog( self, 'File is modified. Save before exit?', |
| 937 | 'Save before too late?', flags ) |
| 938 | say = dlg.ShowModal() |
| 939 | dlg.Destroy() |
| 940 | if say == wxID_YES: |
| 941 | self.OnSaveOrSaveAs(wxCommandEvent(wxID_SAVE)) |
| 942 | # If save was successful, modified flag is unset |
| 943 | if not self.modified: return True |
| 944 | elif say == wxID_NO: |
| 945 | self.modified = False |
| 946 | panel.SetModified(False) |
| 947 | return True |
| 948 | return False |
| 949 | |
| 950 | def SaveUndo(self): |
| 951 | pass # !!! |
| 952 | |
| 953 | ################################################################################ |
| 954 | |
| 955 | def usage(): |
| 956 | print >> sys.stderr, 'usage: xrced [-dhiv] [file]' |
| 957 | |
| 958 | class App(wxApp): |
| 959 | def OnInit(self): |
| 960 | global debug |
| 961 | # Process comand-line |
| 962 | try: |
| 963 | opts = args = None |
| 964 | opts, args = getopt.getopt(sys.argv[1:], 'dhiv') |
| 965 | for o,a in opts: |
| 966 | if o == '-h': |
| 967 | usage() |
| 968 | sys.exit(0) |
| 969 | elif o == '-d': |
| 970 | debug = True |
| 971 | elif o == '-v': |
| 972 | print 'XRCed version', version |
| 973 | sys.exit(0) |
| 974 | |
| 975 | except getopt.GetoptError: |
| 976 | if wxPlatform != '__WXMAC__': # macs have some extra parameters |
| 977 | print >> sys.stderr, 'Unknown option' |
| 978 | usage() |
| 979 | sys.exit(1) |
| 980 | |
| 981 | self.SetAppName('xrced') |
| 982 | # Settings |
| 983 | global conf |
| 984 | conf = g.conf = wxConfig(style = wxCONFIG_USE_LOCAL_FILE) |
| 985 | conf.autoRefresh = conf.ReadInt('autorefresh', True) |
| 986 | pos = conf.ReadInt('x', -1), conf.ReadInt('y', -1) |
| 987 | size = conf.ReadInt('width', 800), conf.ReadInt('height', 600) |
| 988 | conf.embedPanel = conf.ReadInt('embedPanel', True) |
| 989 | conf.showTools = conf.ReadInt('showTools', True) |
| 990 | conf.sashPos = conf.ReadInt('sashPos', 200) |
| 991 | # read recently used files |
| 992 | recentfiles=conf.Read('recentFiles','') |
| 993 | conf.recentfiles={} |
| 994 | if recentfiles: |
| 995 | for fil in recentfiles.split('|'): |
| 996 | conf.recentfiles[wxNewId()]=fil |
| 997 | if not conf.embedPanel: |
| 998 | conf.panelX = conf.ReadInt('panelX', -1) |
| 999 | conf.panelY = conf.ReadInt('panelY', -1) |
| 1000 | else: |
| 1001 | conf.panelX = conf.panelY = -1 |
| 1002 | conf.panelWidth = conf.ReadInt('panelWidth', 200) |
| 1003 | conf.panelHeight = conf.ReadInt('panelHeight', 200) |
| 1004 | conf.panic = not conf.HasEntry('nopanic') |
| 1005 | # Add handlers |
| 1006 | wxFileSystem_AddHandler(wxMemoryFSHandler()) |
| 1007 | wxInitAllImageHandlers() |
| 1008 | # Create main frame |
| 1009 | frame = Frame(pos, size) |
| 1010 | frame.Show(True) |
| 1011 | # Load resources from XRC file (!!! should be transformed to .py later?) |
| 1012 | frame.res = wxXmlResource('') |
| 1013 | # !!! Temporary blocking of assert failure occuring in unicode build |
| 1014 | try: |
| 1015 | frame.res.Load(os.path.join(basePath, 'xrced.xrc')) |
| 1016 | except wx._core.PyAssertionError: |
| 1017 | pass |
| 1018 | |
| 1019 | # Load file after showing |
| 1020 | if args: |
| 1021 | conf.panic = False |
| 1022 | frame.open = frame.Open(args[0]) |
| 1023 | |
| 1024 | return True |
| 1025 | |
| 1026 | def OnExit(self): |
| 1027 | # Write config |
| 1028 | global conf |
| 1029 | wc = wxConfigBase_Get() |
| 1030 | wc.WriteInt('autorefresh', conf.autoRefresh) |
| 1031 | wc.WriteInt('x', conf.x) |
| 1032 | wc.WriteInt('y', conf.y) |
| 1033 | wc.WriteInt('width', conf.width) |
| 1034 | wc.WriteInt('height', conf.height) |
| 1035 | wc.WriteInt('embedPanel', conf.embedPanel) |
| 1036 | wc.WriteInt('showTools', conf.showTools) |
| 1037 | if not conf.embedPanel: |
| 1038 | wc.WriteInt('panelX', conf.panelX) |
| 1039 | wc.WriteInt('panelY', conf.panelY) |
| 1040 | wc.WriteInt('sashPos', conf.sashPos) |
| 1041 | wc.WriteInt('panelWidth', conf.panelWidth) |
| 1042 | wc.WriteInt('panelHeight', conf.panelHeight) |
| 1043 | wc.WriteInt('nopanic', True) |
| 1044 | wc.Write('recentFiles', '|'.join(conf.recentfiles.values()[-5:])) |
| 1045 | wc.Flush() |
| 1046 | |
| 1047 | def main(): |
| 1048 | app = App(0, useBestVisual=False) |
| 1049 | #app.SetAssertMode(wxPYAPP_ASSERT_LOG) |
| 1050 | app.MainLoop() |
| 1051 | app.OnExit() |
| 1052 | global conf |
| 1053 | del conf |
| 1054 | |
| 1055 | if __name__ == '__main__': |
| 1056 | main() |