]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/ProjectEditor.py
Olly Betts' typo patch
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / ProjectEditor.py
1 #----------------------------------------------------------------------------
2 # Name: ProjectEditor.py
3 # Purpose: IDE-style Project Editor for wx.lib.pydocview
4 #
5 # Author: Peter Yared, Morgan Hua
6 #
7 # Created: 8/15/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003, 2004, 2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx.lib.docview
14 import wx.lib.pydocview
15 import types
16 import os
17 import os.path
18 import wx
19 from wxPython.lib.rcsizer import RowColSizer
20 import time
21 import Service
22 import sys
23 import activegrid.util.xmlmarshaller
24 import UICommon
25 import Wizard
26 import SVNService
27 from IDE import ACTIVEGRID_BASE_IDE
28 if not ACTIVEGRID_BASE_IDE:
29 import ProcessModelEditor
30 from SVNService import SVN_INSTALLED
31
32 _ = wx.GetTranslation
33
34 if wx.Platform == '__WXMSW__':
35 _WINDOWS = True
36 else:
37 _WINDOWS = False
38
39
40 #----------------------------------------------------------------------------
41 # Constants
42 #----------------------------------------------------------------------------
43 SPACE = 10
44 HALF_SPACE = 5
45
46
47 #----------------------------------------------------------------------------
48 # XML Marshalling Methods
49 #----------------------------------------------------------------------------
50 LOCAL_TYPE_MAPPING = { "projectmodel" : "activegrid.tool.ProjectEditor.ProjectModel", }
51
52
53 def load(fileObject):
54 xml = fileObject.read()
55 projectModel = activegrid.util.xmlmarshaller.unmarshal(xml, knownTypes=LOCAL_TYPE_MAPPING)
56 return projectModel
57
58
59 def save(fileObject, projectModel):
60 xml = activegrid.util.xmlmarshaller.marshal(projectModel, prettyPrint=True, knownTypes=LOCAL_TYPE_MAPPING)
61 fileObject.write(xml)
62
63
64 #----------------------------------------------------------------------------
65 # Classes
66 #----------------------------------------------------------------------------
67
68 class ProjectModel:
69 __xmlname__ = "projectmodel"
70 __xmlrename__ = { "_files":"files"}
71
72
73 def __init__(self):
74 self._files = []
75
76
77 class ProjectDocument(wx.lib.docview.Document):
78
79
80 def __init__(self):
81 wx.lib.docview.Document.__init__(self)
82 self._projectModel = ProjectModel()
83
84
85 def GetModel(self):
86 return self._projectModel
87
88
89 def OnCreate(self, path, flags):
90 projectService = wx.GetApp().GetService(ProjectService)
91 if projectService.GetView():
92 # All project documents share the same view.
93 view = projectService.GetView()
94 self.AddView(view)
95
96 if view.GetDocument():
97 # All project documents need to share the same command processor,
98 # to enable redo/undo of cross project document commands
99 cmdProcessor = view.GetDocument().GetCommandProcessor()
100 if cmdProcessor:
101 self.SetCommandProcessor(cmdProcessor)
102 else:
103 view = self.GetDocumentTemplate().CreateView(self, flags)
104 projectService.SetView(view)
105
106
107 return view
108
109
110 def LoadObject(self, fileObject):
111 self._projectModel = activegrid.tool.ProjectEditor.load(fileObject)
112 self._projectModel._files = self.RelativeToAbsPath(self._projectModel._files)
113 return True
114
115
116 def SaveObject(self, fileObject):
117 absPath = self._projectModel._files
118 self._projectModel._files = self.AbsToRelativePath(absPath) # temporarily change it to relative paths for saving
119 activegrid.tool.ProjectEditor.save(fileObject, self._projectModel)
120 self._projectModel._files = absPath # swap it back to absolute path
121 return True
122
123
124 def AbsToRelativePath(self, paths):
125 curPath = os.path.dirname(self.GetFilename())
126 curPathLen = len(curPath)
127
128 newFilePaths = []
129 for path in paths:
130 if path.startswith(curPath):
131 path = "." + path[curPathLen:] # use relative path
132 if os.sep != '/':
133 path = path.replace(os.sep, '/', -1) # always save out with '/' as path separator for cross-platform compatibility.
134 else:
135 pass # use absolute path
136 newFilePaths.append(path)
137 return newFilePaths
138
139
140 def RelativeToAbsPath(self, paths):
141 newFilePaths = []
142 for path in paths:
143 if path.startswith("."): # relative to project file
144 curPath = os.path.dirname(self.GetFilename())
145 path = os.path.normpath(os.path.join(curPath, path))
146 elif not ACTIVEGRID_BASE_IDE:
147 print "Warning: absolute path '%s' found in project file, this may affect deployment" % path
148 newFilePaths.append(path)
149 return newFilePaths
150
151
152 def OnOpenDocument(self, filename):
153 view = self.GetFirstView()
154 frame = view.GetFrame()
155
156 if not os.path.exists(filename):
157 wx.GetApp().CloseSplash()
158 msgTitle = wx.GetApp().GetAppName()
159 if not msgTitle:
160 msgTitle = _("File Error")
161 wx.MessageBox(_("Could not find '%s'.") % filename,
162 msgTitle,
163 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
164 frame)
165 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
166
167 fileObject = file(filename, 'r')
168 try:
169 self.LoadObject(fileObject)
170 except:
171 wx.GetApp().CloseSplash()
172 msgTitle = wx.GetApp().GetAppName()
173 if not msgTitle:
174 msgTitle = _("File Error")
175 wx.MessageBox(_("Could not open '%s'. %s") % (wx.lib.docview.FileNameFromPath(filename), sys.exc_value),
176 msgTitle,
177 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
178 frame)
179 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
180
181 self.Modify(False)
182 self.SetFilename(filename, True)
183 view.AddProjectToView(self)
184 self.SetDocumentModificationDate()
185 self.UpdateAllViews()
186 self._savedYet = True
187 view.Activate(True)
188 return True
189
190
191 def AddFile(self, file):
192 return self.AddFiles([file])
193
194
195 def AddFiles(self, files):
196 notAlreadyThereFiles = filter(lambda x: x not in self._projectModel._files, files) # Filter to the files that are not already in the project
197 if len(notAlreadyThereFiles) == 0:
198 self.UpdateAllViews(hint = ("select", self, files))
199 return False
200 else:
201 self._projectModel._files = self._projectModel._files + notAlreadyThereFiles
202 self.UpdateAllViews(hint = ("add", self, notAlreadyThereFiles))
203 self.Modify(True)
204 return True
205
206
207 def RemoveFile(self, file):
208 return self.RemoveFiles([file])
209
210
211 def RemoveFiles(self, files):
212 for file in files:
213 self._projectModel._files.remove(file)
214 self.UpdateAllViews(hint = ("remove", self, files))
215 self.Modify(True)
216 return True
217
218
219 def RenameFile(self, oldFile, newFile, isProject = False):
220 try:
221 if oldFile == newFile:
222 return False
223
224 # projects don't have to exist yet, so not required to rename old file,
225 # but files must exist, so we'll try to rename and allow exceptions to occur if can't.
226 if not isProject or (isProject and os.path.exists(oldFile)):
227 os.rename(oldFile, newFile)
228
229 if isProject:
230 documents = self.GetDocumentManager().GetDocuments()
231 for document in documents:
232 if document.GetFilename() == oldFile: # If the renamed document is open, update it
233 document.SetFilename(newFile)
234 document.SetTitle(wx.lib.docview.FileNameFromPath(newFile))
235 document.UpdateAllViews(hint = ("rename", document, newFile))
236 else:
237 self.RemoveFile(oldFile)
238 self.AddFile(newFile)
239 documents = self.GetDocumentManager().GetDocuments()
240 for document in documents:
241 if document.GetFilename() == oldFile: # If the renamed document is open, update it
242 document.SetFilename(newFile, notifyViews = True)
243 document.UpdateAllViews(hint = ("rename", document, newFile))
244 return True
245 except OSError, (code, message):
246 msgTitle = wx.GetApp().GetAppName()
247 if not msgTitle:
248 msgTitle = _("File Error")
249 wx.MessageBox("Could not rename '%s'. '%s'" % (wx.lib.docview.FileNameFromPath(oldFile), message),
250 msgTitle,
251 wx.OK | wx.ICON_EXCLAMATION,
252 self.GetFirstView().GetFrame())
253 return False
254
255
256 def GetFiles(self):
257 return self._projectModel._files
258
259
260 def IsFileInProject(self, filename):
261 return filename in self.GetFiles()
262
263
264 class NewProjectWizard(Wizard.BaseWizard):
265
266 WIZTITLE = _("New Project Wizard")
267
268 def __init__(self, parent):
269 self._parent = parent
270 self._fullProjectPath = None
271 Wizard.BaseWizard.__init__(self, parent, self.WIZTITLE)
272 self._projectLocationPage = self.CreateProjectLocation(self)
273 wx.wizard.EVT_WIZARD_PAGE_CHANGING(self, self.GetId(), self.OnWizPageChanging)
274
275
276 def CreateProjectLocation(self,wizard):
277 page = Wizard.TitledWizardPage(wizard, _("Project File Location"))
278
279 page.GetSizer().Add(wx.StaticText(page, -1, _("\nSelect the directory and filename for the project.\n\n")))
280 self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, _("File Name:"), _("Directory:"), _("agp"), startingDirectory=os.getcwd())
281 page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
282
283 wizard.Layout()
284 wizard.FitToPage(page)
285 return page
286
287
288 def RunWizard(self, existingTables = None, existingRelationships = None):
289 status = wx.wizard.Wizard.RunWizard(self, self._projectLocationPage)
290 if status:
291 docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
292 if os.path.exists(self._fullProjectPath):
293 # What if the document is already open and we're overwriting it?
294 documents = docManager.GetDocuments()
295 for document in documents:
296 if document.GetFilename() == self._fullProjectPath: # If the renamed document is open, update it
297 document.DeleteAllViews()
298 break
299 os.remove(self._fullProjectPath)
300
301 for template in docManager.GetTemplates():
302 if template.GetDocumentType() == ProjectDocument:
303 doc = template.CreateDocument(self._fullProjectPath, flags = wx.lib.docview.DOC_NEW)
304 doc.OnSaveDocument(self._fullProjectPath)
305 view = doc.GetFirstView()
306 view.AddProjectToView(doc)
307 break
308
309 self.Destroy()
310 return status
311
312
313 def OnWizPageChanging(self, event):
314 if event.GetDirection(): # It's going forwards
315 if event.GetPage() == self._projectLocationPage:
316 if not self._fileValidation():
317 event.Veto()
318 return
319 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(),'.agp'))
320
321
322
323 def OnShowCreatePages(self):
324 self.Hide()
325 import DataModelEditor
326 requestedPos = self.GetPositionTuple()
327 projectService = wx.GetApp().GetService(ProjectService)
328 projectView = projectService.GetView()
329
330 wiz = DataModelEditor.ImportExportWizard(projectView.GetFrame(), pos=requestedPos)
331 if wiz.RunWizard(dontDestroy=True):
332 self._schemaName.SetValue(wiz.GetSchemaFileName())
333 wiz.Destroy()
334 self.Show(True)
335
336
337 class ProjectTemplate(wx.lib.docview.DocTemplate):
338
339
340 def CreateDocument(self, path, flags):
341 if path:
342 return wx.lib.docview.DocTemplate.CreateDocument(self, path, flags)
343 else:
344 wiz = NewProjectWizard(wx.GetApp().GetTopWindow())
345 wiz.RunWizard()
346 wiz.Destroy()
347 return None # never return the doc, otherwise docview will think it is a new file and rename it
348
349
350 class ProjectAddFilesCommand(wx.lib.docview.Command):
351
352
353 def __init__(self, projectDoc, files):
354 wx.lib.docview.Command.__init__(self, canUndo = True)
355 self._projectDoc = projectDoc
356 self._files = files
357
358
359 def GetName(self):
360 if len(self._files) == 1:
361 return _("Add File %s") % os.path.basename(self._files[0])
362 else:
363 return _("Add Files")
364
365
366 def Do(self):
367 return self._projectDoc.AddFiles(self._files)
368
369
370 def Undo(self):
371 return self._projectDoc.RemoveFiles(self._files)
372
373
374 class ProjectRemoveFilesCommand(wx.lib.docview.Command):
375
376
377 def __init__(self, files):
378 wx.lib.docview.Command.__init__(self, canUndo = True)
379 self._files = files
380
381
382 def GetName(self):
383 if len(self._files) == 1:
384 return _("Remove File %s") % os.path.basename((self._files[0])[1])
385 else:
386 return _("Remove Files")
387
388
389 def Do(self):
390 status = False
391 projects = []
392 for data in self._files:
393 proj, filename = data
394 if proj not in projects:
395 projects.append(proj)
396 for project in projects:
397 files = []
398 for data in self._files:
399 proj, filename = data
400 if project == proj:
401 files.append(filename)
402 status = project.RemoveFiles(files) or status
403 return status
404
405
406 def Undo(self):
407 status = False
408 projects = []
409 for data in self._files:
410 proj, filename = data
411 if proj not in projects:
412 projects.append(proj)
413 for project in projects:
414 files = []
415 for data in self._files:
416 proj, filename = data
417 if project == proj:
418 files.append(filename)
419 status = project.AddFiles(files) or status
420 return status
421
422
423 class ProjectRenameFileCommand(wx.lib.docview.Command):
424
425
426 def __init__(self, projectDoc, oldFile, newFile, isProject = False):
427 wx.lib.docview.Command.__init__(self, canUndo = True)
428 self._projectDoc = projectDoc
429 self._oldFile = oldFile
430 self._newFile = newFile
431 self._isProject = isProject
432
433
434 def GetName(self):
435 return _("Rename File %s to %s") % (os.path.basename(self._oldFile), os.path.basename(self._newFile))
436
437
438 def Do(self):
439 return self._projectDoc.RenameFile(self._oldFile, self._newFile, self._isProject)
440
441
442 def Undo(self):
443 return self._projectDoc.RenameFile(self._newFile, self._oldFile, self._isProject)
444
445
446 class ProjectTreeCtrl(wx.TreeCtrl):
447
448
449 #----------------------------------------------------------------------------
450 # Overridden Methods
451 #----------------------------------------------------------------------------
452
453 def __init__(self, parent, id, style):
454 wx.TreeCtrl.__init__(self, parent, id, style = style)
455
456 templates = wx.GetApp().GetDocumentManager().GetTemplates()
457 iconList = wx.ImageList(16, 16, initialCount = len(templates))
458 self._iconIndexLookup = []
459 for template in templates:
460 icon = template.GetIcon()
461 if icon:
462 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
463 icon.SetHeight(16)
464 icon.SetWidth(16)
465 if wx.GetApp().GetDebug():
466 print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
467 iconIndex = iconList.AddIcon(icon)
468 self._iconIndexLookup.append((template, iconIndex))
469
470 icon = getBlankIcon()
471 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
472 icon.SetHeight(16)
473 icon.SetWidth(16)
474 if wx.GetApp().GetDebug():
475 print "Warning: getBlankIcon isn't 16x16, not crossplatform"
476 self._blankIconIndex = iconList.AddIcon(icon)
477 self.AssignImageList(iconList)
478
479
480 def OnCompareItems(self, item1, item2):
481 return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower())
482
483
484 def AppendItem(self, parent, filepath):
485 item = wx.TreeCtrl.AppendItem(self, parent, filepath)
486
487 found = False
488 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filepath)
489 if not template and parent == self.GetRootItem(): # If the parent is a root it's a new project
490 template = wx.GetApp().GetDocumentManager().FindTemplateForPath('.agp')
491 if template:
492 for t, iconIndex in self._iconIndexLookup:
493 if t is template:
494 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Normal)
495 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Expanded)
496 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Selected)
497 found = True
498 break
499
500 if not found:
501 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Normal)
502 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Expanded)
503 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Selected)
504
505 return item
506
507
508 #----------------------------------------------------------------------------
509 # Client Data
510 #----------------------------------------------------------------------------
511
512 def SetData(self, item, longFilename, projectDoc=None):
513 self.SetPyData(item, (longFilename, projectDoc))
514
515
516 def GetData(self, item):
517 """ returns longFilename and optional
518 """
519 data = self.GetPyData(item)
520 if not data:
521 return (None, None)
522 return data
523
524
525 def GetLongFilename(self, item):
526 return self.GetData(item)[0]
527
528
529 def GetProjectDoc(self, item):
530 return self.GetData(item)[1]
531
532
533 class ProjectView(wx.lib.docview.View):
534
535
536 #----------------------------------------------------------------------------
537 # Overridden methods
538 #----------------------------------------------------------------------------
539
540 def __init__(self, service = None):
541 wx.lib.docview.View.__init__(self)
542 self._service = service # not used, but kept to match other Services
543 self._treeCtrl = None
544 self._editingSoDontKillFocus = False
545 self._checkEditMenu = True
546
547
548 def Destroy(self):
549 projectService = wx.GetApp().GetService(ProjectService)
550 if projectService:
551 projectService.SetView(None)
552 wx.lib.docview.View.Destroy(self)
553
554
555 def GetDocument(self):
556 if not self._treeCtrl:
557 return None
558
559 items = self._treeCtrl.GetSelections()
560 if not items: # No selection, so just return first project
561 item = self._treeCtrl.GetFirstVisibleItem()
562 if item.IsOk():
563 return self._GetItemProject(item)
564 else:
565 return None
566
567 for item in items:
568 project = self._GetItemProject(item)
569 if project:
570 return project
571
572 return None
573
574
575 def GetDocumentManager(self): # Overshadow this since the superclass uses the view._viewDocument attribute directly, which the project editor doesn't use since it hosts multiple docs
576 return wx.GetApp().GetDocumentManager()
577
578
579 def OnChangeFilename(self):
580 if self.GetFrame():
581 title = _("Projects")
582 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
583 title = title + " - " + wx.GetApp().GetAppName()
584 self.GetFrame().SetTitle(title)
585 project = self.GetDocument()
586 if project:
587 projectItem = self._GetProjectItem(project)
588 name = self._treeCtrl.GetItemText(self._GetProjectItem(project))
589 name2 = self._MakeProjectName(project)
590 if name != name2:
591 self._treeCtrl.SetItemText(projectItem, name2)
592 self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem())
593
594
595 def Activate(self, activate = True):
596 if not wx.GetApp().IsMDI():
597 if activate and not self.IsShown():
598 self.Show()
599
600 if self.IsShown():
601 wx.lib.docview.View.Activate(self, activate = activate)
602 if activate and self._treeCtrl:
603 self._treeCtrl.SetFocus()
604
605
606 def OnCreate(self, doc, flags):
607 config = wx.ConfigBase_Get()
608 if wx.GetApp().IsMDI():
609 self._embeddedWindow = wx.GetApp().GetTopWindow().GetEmbeddedWindow(wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT)
610 self.SetFrame(self._embeddedWindow)
611 frame = self._embeddedWindow
612 else:
613 self._embeddedWindow = None
614 pos = config.ReadInt("ProjectFrameXLoc", -1), config.ReadInt("ProjectFrameYLoc", -1)
615 # make sure frame is visible
616 screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)
617 screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
618 if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight:
619 pos = wx.DefaultPosition
620
621 size = wx.Size(config.ReadInt("ProjectFrameXSize", -1), config.ReadInt("ProjectFrameYSize", -1))
622
623 title = _("Projects")
624 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
625 title = title + " - " + wx.GetApp().GetAppName()
626
627 frame = wx.GetApp().CreateDocumentFrame(self, doc, 0, title = title, pos = pos, size = size)
628 if config.ReadInt("ProjectFrameMaximized", False):
629 frame.Maximize(True)
630
631 sizer = wx.BoxSizer()
632 self._treeCtrl = ProjectTreeCtrl(frame, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE)
633 self._treeCtrl.AddRoot(_("Projects"))
634 wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
635 wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
636
637 if self._embeddedWindow:
638 sizer.Add(self._treeCtrl)
639 sizer.Fit(frame)
640 else:
641 sizer.Add(self._treeCtrl, 1, wx.EXPAND, 0)
642 frame.SetSizer(sizer)
643 frame.Layout()
644 self.Activate()
645
646 if wx.GetApp().IsMDI():
647 wx.EVT_SET_FOCUS(self._treeCtrl, self.OnFocus)
648 wx.EVT_KILL_FOCUS(self._treeCtrl, self.OnKillFocus)
649
650 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
651 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelectionSDI)
652 else:
653 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelection)
654 wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
655 wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
656 wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
657 wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
658 # wx.EVT_COMMAND_RIGHT_CLICK(self._treeCtrl, self._treeCtrl.GetId(), self.OnRightClick) # wxBug: This isn't working for some reason
659
660 # drag-and-drop support
661 dt = ProjectFileDropTarget(self)
662 self._treeCtrl.SetDropTarget(dt)
663
664 return True
665
666
667 def OnBeginDrag(self, event):
668 item = event.GetItem()
669 if item.IsOk():
670 if item == self._treeCtrl.GetRootItem():
671 return
672 self._draggingItem = item
673 event.Allow()
674
675
676 def OnEndDrag(self, event):
677 item = event.GetItem()
678 if item.IsOk():
679 # don't allow object to be dragged to itself
680 if item == self._draggingItem:
681 return
682
683 rootItem = self._treeCtrl.GetRootItem()
684
685 # don't let object replace root view
686 if item == rootItem:
687 wx.MessageBox(_("Cannot replace root view with item."))
688 return
689
690 # don't allow object to be dragged to a direct descendant
691 ancestor = self._treeCtrl.GetItemParent(item)
692 while ancestor != rootItem:
693 if ancestor == self._draggingItem:
694 wx.MessageBox(_("Cannot make item direct descendant of self."))
695 return
696 else:
697 ancestor = self._treeCtrl.GetItemParent(ancestor)
698
699 if self._treeCtrl.GetItemParent(item) == self._treeCtrl.GetItemParent(self._draggingItem):
700 # put it in same folder as it was originally, no-op.
701 return
702 if item == self._treeCtrl.GetItemParent(self._draggingItem):
703 # put it in same folder as it was originally, no-op.
704 return
705
706 self.GetDocument().GetCommandProcessor().Submit(ProjectEditorMoveCommand(self, item, self._draggingItem))
707
708
709 def WriteProjectConfig(self):
710 frame = self.GetFrame()
711 config = wx.ConfigBase_Get()
712 if frame and not self._embeddedWindow:
713 if not frame.IsMaximized():
714 config.WriteInt("ProjectFrameXLoc", frame.GetPositionTuple()[0])
715 config.WriteInt("ProjectFrameYLoc", frame.GetPositionTuple()[1])
716 config.WriteInt("ProjectFrameXSize", frame.GetSizeTuple()[0])
717 config.WriteInt("ProjectFrameYSize", frame.GetSizeTuple()[1])
718 config.WriteInt("ProjectFrameMaximized", frame.IsMaximized())
719
720 if config.ReadInt("ProjectSaveDocs", True):
721 projectFileNames = []
722 projectExpanded = []
723 if self._treeCtrl:
724 for projectItem in self._GetChildItems(self._treeCtrl.GetRootItem()):
725 project = self._GetItemProject(projectItem)
726 if not project.OnSaveModified():
727 return
728 if project.GetDocumentSaved(): # Might be a new document and "No" selected to save it
729 projectFileNames.append(str(project.GetFilename()))
730 projectExpanded.append(self._treeCtrl.IsExpanded(projectItem))
731 config.Write("ProjectSavedDocs", projectFileNames.__repr__())
732 config.Write("ProjectExpandedSavedDocs", projectExpanded.__repr__())
733
734
735 def OnClose(self, deleteWindow = True):
736 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
737 self.WriteProjectConfig()
738 project = self.GetDocument()
739 if not project:
740 return True
741 if not self.GetDocument().Close():
742 return True
743 self.Activate(False)
744 if project:
745 projectItem = self._GetProjectItem(project)
746 if projectItem:
747 self._treeCtrl.Delete(projectItem)
748 # We don't need to delete the window since it is a floater/embedded
749 return True
750
751
752 def _GetParentFrame(self):
753 return wx.GetTopLevelParent(self.GetFrame())
754
755
756 def OnUpdate(self, sender = None, hint = None):
757 wx.lib.docview.View.OnUpdate(self, sender, hint)
758 if hint:
759 if hint[0] == "add":
760 projectItem = self._GetProjectItem(hint[1])
761 files = hint[2]
762 self._treeCtrl.UnselectAll()
763 self._treeCtrl.Expand(projectItem)
764 item = None
765 for file in files:
766 item = self._treeCtrl.AppendItem(projectItem, os.path.basename(file))
767 self._treeCtrl.SetData(item, file)
768 self._treeCtrl.SelectItem(item)
769 self._treeCtrl.EnsureVisible(item)
770 self._treeCtrl.SortChildren(projectItem)
771 if item:
772 self._treeCtrl.EnsureVisible(item) # need to be called after SortChildren
773 elif hint[0] == "remove":
774 projectItem = self._GetProjectItem(hint[1])
775 files = hint[2]
776 self._treeCtrl.UnselectAll()
777 children = self._GetChildItems(projectItem)
778 for child in children:
779 if self._GetItemFile(child) in files:
780 self._treeCtrl.Delete(child)
781 elif hint[0] == "select":
782 projectItem = self._GetProjectItem(hint[1])
783 files = hint[2]
784 self._treeCtrl.UnselectAll()
785 children = self._GetChildItems(projectItem)
786 for child in children:
787 if self._GetItemFile(child) in files:
788 self._treeCtrl.SelectItem(child)
789 self._treeCtrl.EnsureVisible(child)
790 elif hint[0] == "rename":
791 projectItem = self._GetProjectItem(hint[1])
792 self._treeCtrl.SetItemText(projectItem, os.path.basename(hint[2]))
793
794
795 def ProcessEvent(self, event):
796 id = event.GetId()
797 if id == ProjectService.ADD_FILES_TO_PROJECT_ID:
798 self.OnAddFileToProject(event)
799 return True
800 elif id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID:
801 self.OnAddDirToProject(event)
802 return True
803 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
804 return False # Implement this one in the service
805 elif id == ProjectService.RENAME_ID:
806 self.OnRename(event)
807 return True
808 elif id == ProjectService.DELETE_FILE_ID:
809 self.OnDeleteFile(event)
810 return True
811 elif id == wx.ID_CUT:
812 self.OnCut(event)
813 return True
814 elif id == wx.ID_COPY:
815 self.OnCopy(event)
816 return True
817 elif id == wx.ID_PASTE:
818 self.OnPaste(event)
819 return True
820 elif (id == wx.ID_CLEAR
821 or id == ProjectService.REMOVE_FROM_PROJECT):
822 self.OnClear(event)
823 return True
824 elif id == wx.ID_SELECTALL:
825 self.OnSelectAll(event)
826 return True
827 elif id == ProjectService.OPEN_SELECTION_ID:
828 self.OnOpenSelection(event)
829 return True
830 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
831 self.OnProperties(event)
832 return True
833 else:
834 return False
835
836
837 def ProcessUpdateUIEvent(self, event):
838 # Hack: The edit menu is not being set for projects that are preloaded at startup, so make sure it is OK here
839 if self._checkEditMenu:
840 doc = self.GetDocument()
841 if doc and not doc.GetCommandProcessor().GetEditMenu():
842 doc.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
843 self._checkEditMenu = False
844
845 id = event.GetId()
846 if (id == wx.ID_CUT
847 or id == wx.ID_COPY
848 or id == ProjectService.RENAME_ID
849 or id == ProjectService.ADD_FILES_TO_PROJECT_ID
850 or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID
851 or id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID
852 or id == ProjectService.DELETE_FILE_ID):
853 event.Enable(self._HasSelection())
854 return True
855 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
856 event.Enable(False)
857 return True
858 elif id == wx.ID_PASTE:
859 event.Enable(self.CanPaste())
860 return True
861 elif id == wx.ID_SELECTALL:
862 event.Enable(self._HasFiles())
863 return True
864 elif (id == wx.ID_CLEAR
865 or id == ProjectService.REMOVE_FROM_PROJECT
866 or id == ProjectService.OPEN_SELECTION_ID):
867 event.Enable(self._HasFilesSelected())
868 return True
869 else:
870 return False
871
872 #----------------------------------------------------------------------------
873 # Display Methods
874 #----------------------------------------------------------------------------
875
876 def IsShown(self):
877 if not self.GetFrame():
878 return False
879 return self.GetFrame().IsShown()
880
881
882 def Hide(self):
883 self.Show(False)
884
885
886 def Show(self, show = True):
887 self.GetFrame().Show(show)
888 if wx.GetApp().IsMDI():
889 mdiParentFrame = wx.GetApp().GetTopWindow()
890 mdiParentFrame.ShowEmbeddedWindow(self.GetFrame(), show)
891
892
893 #----------------------------------------------------------------------------
894 # Methods for ProjectDocument and ProjectService to call
895 #----------------------------------------------------------------------------
896
897 def SetExpandedProjects(self, expandedProjects):
898 self._treeCtrl.UnselectAll()
899 firstItem = None
900 for i, item in enumerate(self._GetChildItems(self._treeCtrl.GetRootItem())):
901 if i == 0:
902 firstItem = item
903
904 if expandedProjects[i]:
905 self._treeCtrl.Expand(item)
906 else:
907 self._treeCtrl.Collapse(item)
908
909 if firstItem:
910 self._treeCtrl.EnsureVisible(firstItem)
911
912
913 def GetSelectedFile(self):
914 for item in self._treeCtrl.GetSelections():
915 return self._GetItemFile(item)
916
917
918 def GetSelectedFiles(self):
919 filenames = []
920 for item in self._treeCtrl.GetSelections():
921 filename = self._GetItemFile(item)
922 if filename and filename not in filenames:
923 filenames.append(filename)
924 return filenames
925
926
927 def AddProjectToView(self, document):
928 rootItem = self._treeCtrl.GetRootItem()
929 projectItem = self._treeCtrl.AppendItem(rootItem, self._MakeProjectName(document))
930 self._treeCtrl.SetData(projectItem, document.GetFilename(), document)
931 for file in document.GetFiles():
932 fileItem = self._treeCtrl.AppendItem(projectItem, os.path.basename(file))
933 self._treeCtrl.SetData(fileItem, file)
934 self._treeCtrl.SortChildren(rootItem)
935 self._treeCtrl.SortChildren(projectItem)
936 self._treeCtrl.UnselectAll()
937 self._treeCtrl.Expand(projectItem)
938 self._treeCtrl.SelectItem(projectItem)
939 if self._embeddedWindow:
940 document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
941
942
943 def HasFocus(self):
944 winWithFocus = wx.Window.FindFocus()
945 if not winWithFocus:
946 return False
947 while winWithFocus:
948 if winWithFocus == self._treeCtrl:
949 return True
950 winWithFocus = winWithFocus.GetParent()
951 return False
952
953
954 #----------------------------------------------------------------------------
955 # Control events
956 #----------------------------------------------------------------------------
957
958 def OnProperties(self, event):
959 items = self._treeCtrl.GetSelections()
960 if not items:
961 return
962 item = items[0]
963 if self._IsItemProject(item):
964 projectPropertiesDialog = ProjectPropertiesDialog(wx.GetApp().GetTopWindow(), self._GetItemProject(item).GetFilename())
965 if projectPropertiesDialog.ShowModal() == wx.ID_OK:
966 pass # Handle OK
967 projectPropertiesDialog.Destroy()
968 elif self._IsItemFile(item):
969 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
970 filePropertiesService.ShowPropertiesDialog(self._GetItemFile(item))
971
972
973 def OnAddFileToProject(self, event):
974 if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
975 allfilter = ''
976 descr = ''
977 for temp in self.GetDocumentManager()._templates:
978 if temp.IsVisible():
979 if len(descr) > 0:
980 descr = descr + _('|')
981 allfilter = allfilter + _(';')
982 descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter() # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
983 allfilter = allfilter + temp.GetFileFilter()
984 descr = _("All") + _(" (") + allfilter + _(") |") + allfilter + _('|') + descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
985 descr = descr + _("|") + _("Any (*.*) | *.*")
986 else:
987 descr = _("*.*")
988
989 startDirectory = os.path.dirname(self.GetDocument().GetFilename())
990
991 if True or _WINDOWS:
992 dialog = wx.FileDialog(self.GetFrame(), _("Add Files"), startDirectory, "", descr, wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE)
993 if dialog.ShowModal() != wx.ID_OK:
994 return
995 paths = dialog.GetPaths()
996 dialog.Destroy()
997 else:
998 paths = wx.FileSelector(_("Add Files"), startDirectory, "", wildcard = descr, flags = wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE, parent=self.GetFrame())
999 if type(paths) == types.StringType:
1000 paths = [paths]
1001 if len(paths):
1002 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), paths))
1003 self.Activate(True) # after add, should put focus on project editor
1004
1005
1006 def OnAddDirToProject(self, event):
1007 frame = wx.Dialog(None, -1, _("Add Directory Files to Project"), size= (320,200))
1008 contentSizer = wx.BoxSizer(wx.VERTICAL)
1009
1010 flexGridSizer = wx.FlexGridSizer(cols = 2, vgap=HALF_SPACE, hgap=HALF_SPACE)
1011 flexGridSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER_VERTICAL, 0)
1012 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
1013 dirCtrl = wx.TextCtrl(frame, -1, os.path.dirname(self.GetDocument().GetFilename()), size=(250,-1))
1014 dirCtrl.SetToolTipString(dirCtrl.GetValue())
1015 lineSizer.Add(dirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
1016 findDirButton = wx.Button(frame, -1, _("Browse..."))
1017 lineSizer.Add(findDirButton, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, HALF_SPACE)
1018 flexGridSizer.Add(lineSizer, 1, wx.EXPAND)
1019
1020 def OnBrowseButton(event):
1021 dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
1022 dir = dirCtrl.GetValue()
1023 if len(dir):
1024 dlg.SetPath(dir)
1025 if dlg.ShowModal() == wx.ID_OK:
1026 dirCtrl.SetValue(dlg.GetPath())
1027 dirCtrl.SetToolTipString(dirCtrl.GetValue())
1028 dirCtrl.SetInsertionPointEnd()
1029
1030 dlg.Destroy()
1031 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
1032
1033 visibleTemplates = []
1034 for template in self.GetDocumentManager()._templates:
1035 if template.IsVisible():
1036 visibleTemplates.append(template)
1037
1038 choices = []
1039 allfilter = ''
1040 descr = ''
1041 for template in visibleTemplates:
1042 if len(descr) > 0:
1043 descr = descr + _('|')
1044 allfilter = allfilter + _(';')
1045 descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
1046 choices.append(descr)
1047 allfilter = allfilter + template.GetFileFilter()
1048 choices.insert(0, _("All (%s)") % allfilter)
1049 filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
1050 filterChoice.SetSelection(0)
1051 filterChoice.SetToolTipString(_("Select file type filter."))
1052 flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL)
1053 flexGridSizer.Add(filterChoice, 1, wx.EXPAND)
1054
1055 contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE)
1056
1057 subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories"))
1058 subfolderCtrl.SetValue(True)
1059 contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE)
1060
1061 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
1062 findBtn = wx.Button(frame, wx.ID_OK, _("Add"))
1063 findBtn.SetDefault()
1064 buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE)
1065 buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
1066 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
1067
1068 frame.SetSizer(contentSizer)
1069 frame.Fit()
1070
1071 status = frame.ShowModal()
1072
1073 passedCheck = False
1074 while status == wx.ID_OK and not passedCheck:
1075 if not os.path.exists(dirCtrl.GetValue()):
1076 dlg = wx.MessageDialog(frame,
1077 _("'%s' does not exist.") % dirCtrl.GetValue(),
1078 _("Find in Directory"),
1079 wx.OK | wx.ICON_EXCLAMATION
1080 )
1081 dlg.ShowModal()
1082 dlg.Destroy()
1083
1084 status = frame.ShowModal()
1085 else:
1086 passedCheck = True
1087
1088 if status == wx.ID_OK:
1089 frame.Destroy()
1090
1091 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
1092
1093 doc = self.GetDocument()
1094 searchSubfolders = subfolderCtrl.IsChecked()
1095 dirString = dirCtrl.GetValue()
1096
1097 if os.path.isfile(dirString):
1098 # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
1099 # We'll assume they know what they're doing.
1100 paths = [dirString]
1101 else:
1102 paths = []
1103
1104 index = filterChoice.GetSelection()
1105 if index:
1106 template = visibleTemplates[index-1]
1107
1108 # do search in files on disk
1109 for root, dirs, files in os.walk(dirString):
1110 if not searchSubfolders and root != dirString:
1111 break
1112
1113 for name in files:
1114 if index == 0: # all
1115 for template in visibleTemplates:
1116 if template.FileMatchesTemplate(name):
1117 filename = os.path.join(root, name)
1118
1119 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1120 if doc.IsFileInProject(filename):
1121 break
1122
1123 paths.append(filename)
1124 break
1125 else: # use selected filter
1126 if template.FileMatchesTemplate(name):
1127 filename = os.path.join(root, name)
1128 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1129 if not doc.IsFileInProject(filename):
1130 paths.append(filename)
1131
1132 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
1133
1134 doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths))
1135 self.Activate(True) # after add, should put focus on project editor
1136 else:
1137 frame.Destroy()
1138
1139
1140 def DoAddFilesToProject(self, filenames):
1141 # method used by Drag-n-Drop to add files to current Project
1142 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filenames))
1143
1144
1145 def DoSelectFiles(self, filenames):
1146 # method used by Drag-n-Drop to select files in current Project
1147 self._treeCtrl.UnselectAll()
1148 for file in filenames:
1149 item = self._GetFileItem(longFileName=file)
1150 if item:
1151 self._treeCtrl.SelectItem(item, True)
1152 self._treeCtrl.EnsureVisible(item)
1153
1154
1155 def DoSelectProject(self, x, y):
1156 # method used by Drag-n-Drop to set current Project based on cursor position
1157 item, flag = self._treeCtrl.HitTest((x,y))
1158 if not item:
1159 return False
1160
1161 project = self._GetItemProject(item)
1162 if not project:
1163 return False
1164
1165 projectItem = self._GetProjectItem(project)
1166 self._treeCtrl.UnselectAll()
1167 self._treeCtrl.SelectItem(projectItem)
1168 return True
1169
1170
1171 def OnFocus(self, event):
1172 wx.GetApp().GetDocumentManager().ActivateView(self)
1173 event.Skip()
1174
1175
1176 def OnKillFocus(self, event):
1177 # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame
1178 # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame
1179 if not self._editingSoDontKillFocus: # wxBug: This didn't used to happen, but now when you start to edit an item in a wxTreeCtrl it puts out a KILL_FOCUS event, so we need to detect it
1180 childFrame = wx.GetApp().GetTopWindow().GetActiveChild()
1181 if childFrame:
1182 childFrame.Activate()
1183 event.Skip()
1184
1185
1186 def OnRightClick(self, event):
1187 self.Activate(True)
1188 if not self._treeCtrl.GetSelections():
1189 return
1190 if len(self._treeCtrl.GetSelections()) == 1 and self._IsItemRoot(self._treeCtrl.GetSelections()[0]):
1191 return # Don't do a menu if it's just the root item selected
1192 menu = wx.Menu()
1193 if self._HasFilesSelected(): # Files context
1194 menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection"))
1195 menu.Enable(ProjectService.OPEN_SELECTION_ID, True)
1196 wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection)
1197 itemIDs = [None]
1198 for item in self._treeCtrl.GetSelections():
1199 if self._IsItemProcessModelFile(item):
1200 itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None]
1201 break
1202 else: # Project context
1203 itemIDs = [wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS, None]
1204 menuBar = self._GetParentFrame().GetMenuBar()
1205 itemIDs = itemIDs + [ProjectService.ADD_FILES_TO_PROJECT_ID, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, ProjectService.REMOVE_FROM_PROJECT]
1206 if SVN_INSTALLED:
1207 itemIDs = itemIDs + [None, SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
1208 itemIDs = itemIDs + [None, wx.ID_UNDO, wx.ID_REDO, None, wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE, wx.ID_CLEAR, None, wx.ID_SELECTALL, ProjectService.RENAME_ID, ProjectService.DELETE_FILE_ID, None, wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID]
1209 for itemID in itemIDs:
1210 if not itemID:
1211 menu.AppendSeparator()
1212 else:
1213 if itemID == ProjectService.RUN_SELECTED_PM_ID:
1214 menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
1215 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM)
1216 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
1217 menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
1218 wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
1219 wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
1220 else:
1221 item = menuBar.FindItemById(itemID)
1222 if item:
1223 menu.Append(itemID, item.GetLabel())
1224 self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY()))
1225 menu.Destroy()
1226
1227
1228 def OnRunSelectedPM(self, event):
1229 projectService = wx.GetApp().GetService(ProjectService)
1230 if projectService:
1231 projectService.OnRunProcessModel(event, runSelected=True)
1232
1233
1234 def OnRename(self, event):
1235 if self._treeCtrl.GetSelections():
1236 self._treeCtrl.EditLabel(self._treeCtrl.GetSelections()[0])
1237
1238
1239 def OnBeginLabelEdit(self, event):
1240 self._editingSoDontKillFocus = True
1241 item = event.GetItem()
1242 if not self._IsItemFile(item) and not self._IsItemProject(item):
1243 event.Veto()
1244
1245
1246 def OnEndLabelEdit(self, event):
1247 self._editingSoDontKillFocus = False
1248 item = event.GetItem()
1249 newName = event.GetLabel()
1250 if not newName or (not self._IsItemFile(item) and not self._IsItemProject(item)):
1251 event.Veto()
1252 return
1253 if self._IsItemFile(item):
1254 oldFile = self._GetItemFile(item)
1255 newFile = os.path.join(os.path.split(oldFile)[0], newName)
1256 project = self._GetItemProject(item)
1257 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile)):
1258 event.Veto()
1259 return
1260 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(self._treeCtrl.GetSelections()[0]))
1261 elif self._IsItemProject(item):
1262 oldFile = self._GetItemProject(item).GetFilename()
1263 newFile = os.path.join(os.path.split(oldFile)[0], newName)
1264 project = self._GetItemProject(item)
1265 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile, True)):
1266 event.Veto()
1267 return
1268 self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem())
1269
1270
1271 def CanPaste(self):
1272 # wxBug: Should be able to use IsSupported/IsSupportedFormat here
1273 #fileDataObject = wx.FileDataObject()
1274 #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
1275 if not wx.TheClipboard.IsOpened():
1276 if wx.TheClipboard.Open():
1277 fileDataObject = wx.FileDataObject()
1278 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
1279 wx.TheClipboard.Close()
1280 else:
1281 hasFilesInClipboard = False
1282 return hasFilesInClipboard
1283
1284
1285 def OnCut(self, event):
1286 self.OnCopy(event)
1287 self.OnClear(event)
1288
1289
1290 def OnCopy(self, event):
1291 fileDataObject = wx.FileDataObject()
1292 items = self._treeCtrl.GetSelections()
1293 for item in items:
1294 if self._IsItemFile(item):
1295 file = self._treeCtrl.GetLongFilename(item)
1296 fileDataObject.AddFile(file)
1297 if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open():
1298 wx.TheClipboard.SetData(fileDataObject)
1299 wx.TheClipboard.Close()
1300
1301
1302 def OnPaste(self, event):
1303 if wx.TheClipboard.Open():
1304 fileDataObject = wx.FileDataObject()
1305 if wx.TheClipboard.GetData(fileDataObject):
1306 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames()))
1307 wx.TheClipboard.Close()
1308
1309
1310 def OnClear(self, event):
1311 items = self._treeCtrl.GetSelections()
1312 files = []
1313 for item in items:
1314 if self._IsItemFile(item):
1315 files.append((self._GetItemProject(item), self._GetItemFile(item)))
1316 self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(files))
1317
1318
1319 def OnDeleteFile(self, event):
1320 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1321 _("Delete cannot be reversed.\n\nRemove the selected files from the\nprojects and file system permanently?"),
1322 _("Delete File"),
1323 wx.YES_NO|wx.ICON_QUESTION)
1324 if yesNoMsg.ShowModal() == wx.ID_NO:
1325 return
1326
1327 items = self._treeCtrl.GetSelections()
1328 files = []
1329 delFiles = []
1330 for item in items:
1331 if self._IsItemFile(item):
1332 filename = self._GetItemFile(item)
1333 files.append((self._GetItemProject(item), filename))
1334 if filename not in delFiles:
1335 delFiles.append(filename)
1336
1337 # remove selected files from projects
1338 projects = []
1339 for data in files:
1340 proj, filename = data
1341 if proj not in projects:
1342 projects.append(proj)
1343 for project in projects:
1344 filenames = []
1345 for data in files:
1346 proj, filename = data
1347 if project == proj:
1348 filenames.append(filename)
1349 project.RemoveFiles(filenames)
1350
1351 # remove selected files from file system
1352 for filename in delFiles:
1353 if os.path.exists(filename):
1354 try:
1355 os.remove(filename)
1356 except:
1357 wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filename), sys.exc_value),
1358 _("Delete File"),
1359 wx.OK | wx.ICON_EXCLAMATION)
1360
1361
1362 def OnKeyPressed(self, event):
1363 key = event.KeyCode()
1364 if key == wx.WXK_DELETE:
1365 self.OnClear(event)
1366 else:
1367 event.Skip()
1368
1369
1370 def OnSelectAll(self, event):
1371 project = self.GetDocument()
1372 if project:
1373 self._treeCtrl.UnselectAll()
1374 for child in self._GetChildItems(self._GetProjectItem(project)):
1375 self._treeCtrl.SelectItem(child)
1376
1377
1378 def OnOpenSelectionSDI(self, event):
1379 # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window
1380 wx.CallAfter(self.OnOpenSelection, None)
1381
1382
1383 def OnOpenSelection(self, event):
1384 doc = None
1385 try:
1386 items = self._treeCtrl.GetSelections()
1387 for item in items:
1388 if self._IsItemFile(item):
1389 filepath = self._GetItemFile(item)
1390 if not os.path.exists(filepath):
1391 msgTitle = wx.GetApp().GetAppName()
1392 if not msgTitle:
1393 msgTitle = _("File Not Found")
1394 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1395 _("The file '%s' was not found in '%s'.\n\nWould you like to browse for the file?") % (wx.lib.docview.FileNameFromPath(filepath), wx.lib.docview.PathOnly(filepath)),
1396 msgTitle,
1397 wx.YES_NO|wx.ICON_QUESTION
1398 )
1399 if yesNoMsg.ShowModal() == wx.ID_NO:
1400 continue
1401 findFile = wx.FileDialog(self.GetFrame(),
1402 _("Choose a file"),
1403 wx.lib.docview.PathOnly(filepath),
1404 wx.lib.docview.FileNameFromPath(filepath),
1405 style = wx.OPEN
1406 )
1407 if findFile.ShowModal() == wx.ID_OK and findFile.GetPath():
1408 newpath = findFile.GetPath()
1409 else:
1410 newpath = None
1411 findFile.Destroy()
1412 if newpath:
1413 # update Project Model with new location
1414 project = self._GetItemProject(item)
1415 project.RemoveFile(filepath)
1416 project.AddFile(newpath)
1417 filepath = newpath
1418
1419 doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT)
1420 if not doc:
1421 shortFilename = self._treeCtrl.GetItemText(item)
1422 if shortFilename.endswith(".agp"):
1423 projItem = self._GetProjectItem(shortFilename=shortFilename)
1424 self._treeCtrl.UnselectAll()
1425 if not self._treeCtrl.IsExpanded(projItem):
1426 self._treeCtrl.Expand(projItem)
1427 if not self._treeCtrl.IsVisible(projItem):
1428 self._treeCtrl.EnsureVisible(projItem)
1429 if not self._treeCtrl.IsSelected(projItem):
1430 self._treeCtrl.SelectItem(projItem)
1431
1432 except IOError, (code, message):
1433 msgTitle = wx.GetApp().GetAppName()
1434 if not msgTitle:
1435 msgTitle = _("File Error")
1436 wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath),
1437 msgTitle,
1438 wx.OK | wx.ICON_EXCLAMATION,
1439 self.GetFrame())
1440
1441
1442 #----------------------------------------------------------------------------
1443 # Convenience methods
1444 #----------------------------------------------------------------------------
1445
1446 def _HasFiles(self):
1447 if not self._treeCtrl:
1448 return False
1449 return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files
1450
1451
1452 def _HasSelection(self):
1453 if not self._treeCtrl:
1454 return False
1455
1456 items = self._treeCtrl.GetSelections()
1457 if items:
1458 return True
1459
1460 return False
1461
1462
1463 def _HasFilesSelected(self):
1464 if not self._treeCtrl:
1465 return False
1466 items = self._treeCtrl.GetSelections()
1467 if not items:
1468 return False
1469 for item in items:
1470 if not self._IsItemFile(item):
1471 return False
1472 return True
1473
1474
1475 def _MakeProjectName(self, project):
1476 return project.GetPrintableName()
1477
1478
1479 # Return the tree item for a project
1480 def _GetProjectItem(self, project=None, shortFilename=None):
1481 rootItem = self._treeCtrl.GetRootItem()
1482 (child, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1483 while child.IsOk():
1484 if project:
1485 if self._treeCtrl.GetProjectDoc(child) == project:
1486 return child
1487 elif shortFilename:
1488 if self._treeCtrl.GetItemText(child) == shortFilename:
1489 return child
1490 (child, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1491 return None
1492
1493
1494 # Returns the project for an item, either for a project item or a file that is part of a project
1495 def _GetItemProject(self, item):
1496 if self._IsItemRoot(item):
1497 return None
1498 if self._IsItemProject(item):
1499 return self._treeCtrl.GetProjectDoc(item)
1500 if self._IsItemFile(item):
1501 return self._treeCtrl.GetProjectDoc(self._treeCtrl.GetItemParent(item))
1502 return None
1503
1504
1505 def _GetItemFile(self, item):
1506 if self._IsItemFile(item):
1507 return self._treeCtrl.GetLongFilename(item)
1508 else:
1509 return None
1510
1511
1512 def _GetFileItem(self, shortFileName = None, longFileName = None):
1513 """ Returns the tree item for a file given the short (display) or long (fullpath) file name. """
1514
1515 rootItem = self._treeCtrl.GetRootItem()
1516 (project, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1517 while project.IsOk():
1518 (child, cookie2) = self._treeCtrl.GetFirstChild(project)
1519 while child.IsOk():
1520 if shortFileName:
1521 if self._treeCtrl.GetItemText(child) == shortFileName:
1522 return child
1523 else:
1524 if self._treeCtrl.GetLongFilename(child) == longFileName:
1525 return child
1526 (child, cookie2) = self._treeCtrl.GetNextChild(project, cookie2)
1527 (project, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1528 return None
1529
1530
1531 def _IsItemRoot(self, item):
1532 return item == self._treeCtrl.GetRootItem()
1533
1534
1535 def _IsItemProject(self, item):
1536 return self._treeCtrl.GetProjectDoc(item) != None
1537
1538
1539 def _IsItemFile(self, item):
1540 return self._treeCtrl.GetProjectDoc(item) == None
1541
1542
1543 def _IsItemProcessModelFile(self, item):
1544 if ACTIVEGRID_BASE_IDE:
1545 return False
1546
1547 if self._IsItemFile(item):
1548 filename = self._treeCtrl.GetLongFilename(item)
1549 ext = None
1550 for template in self.GetDocumentManager().GetTemplates():
1551 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1552 ext = template.GetDefaultExtension()
1553 break;
1554 if not ext:
1555 return False
1556
1557 if filename.endswith(ext):
1558 return True
1559
1560 return False
1561
1562
1563 def _GetChildItems(self, parentItem):
1564 children = []
1565 (child, cookie) = self._treeCtrl.GetFirstChild(parentItem)
1566 while child.IsOk():
1567 children.append(child)
1568 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
1569 return children
1570
1571
1572
1573 class ProjectFileDropTarget(wx.FileDropTarget):
1574
1575 def __init__(self, view):
1576 wx.FileDropTarget.__init__(self)
1577 self._view = view
1578
1579
1580 def OnDropFiles(self, x, y, filenames):
1581 if self._view.DoSelectProject(x, y):
1582 self._view.DoAddFilesToProject(filenames)
1583 self._view.DoSelectFiles(filenames)
1584 return True
1585 return False
1586
1587
1588 def OnDragOver(self, x, y, default):
1589 if self._view.DoSelectProject(x,y):
1590 return wx.DragCopy
1591 return wx.DragNone
1592
1593
1594 class ProjectPropertiesDialog(wx.Dialog):
1595
1596
1597 def __init__(self, parent, filename):
1598 wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330))
1599
1600 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
1601
1602 notebook = wx.Notebook(self, -1)
1603 tab = wx.Panel(notebook, -1)
1604
1605 gridSizer = RowColSizer()
1606
1607 gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")), flag=wx.RIGHT, border=HALF_SPACE, row=0, col=0)
1608 if os.path.isfile(filename):
1609 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]), row=0, col=1)
1610
1611 gridSizer.Add(wx.StaticText(tab, -1, _("Location:")), flag=wx.RIGHT, border=HALF_SPACE, row=1, col=0)
1612 gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.split(filename)[0])), flag=wx.BOTTOM, border=SPACE, row=1, col=1)
1613
1614 gridSizer.Add(wx.StaticText(tab, -1, _("Size:")), flag=wx.RIGHT, border=HALF_SPACE, row=2, col=0)
1615 gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")), row=2, col=1)
1616
1617 lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
1618 lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND)
1619 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=HALF_SPACE, row=3, col=0, colspan=2)
1620
1621 gridSizer.Add(wx.StaticText(tab, -1, _("Created:")), flag=wx.RIGHT, border=HALF_SPACE, row=4, col=0)
1622 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))), row=4, col=1)
1623
1624 gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")), flag=wx.RIGHT, border=HALF_SPACE, row=5, col=0)
1625 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))), row=5, col=1)
1626
1627 gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")), flag=wx.RIGHT, border=HALF_SPACE, row=6, col=0)
1628 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))), row=6, col=1)
1629
1630 else:
1631 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")), row=0, col=1)
1632
1633 # add a border around the inside of the tab
1634 spacerGrid = wx.BoxSizer(wx.VERTICAL)
1635 spacerGrid.Add(gridSizer, 0, wx.ALL, SPACE);
1636 tab.SetSizer(spacerGrid)
1637 notebook.AddPage(tab, _("General"))
1638 if wx.Platform == "__WXMSW__":
1639 notebook.SetPageSize((310,200))
1640
1641 sizer = wx.BoxSizer(wx.VERTICAL)
1642 sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
1643 sizer.Add(self.CreateButtonSizer(wx.OK), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
1644
1645 self.SetSizer(sizer)
1646 sizer.Fit(self)
1647 self.Layout()
1648
1649
1650 class ProjectOptionsPanel(wx.Panel):
1651
1652
1653 def __init__(self, parent, id):
1654 wx.Panel.__init__(self, parent, id)
1655 self._useSashMessageShown = False
1656 config = wx.ConfigBase_Get()
1657 self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects"))
1658 self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True))
1659 projectBorderSizer = wx.BoxSizer(wx.VERTICAL)
1660 projectSizer = wx.BoxSizer(wx.VERTICAL)
1661 projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
1662 if not ACTIVEGRID_BASE_IDE:
1663 self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
1664 self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True))
1665 projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
1666 projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE)
1667 self.SetSizer(projectBorderSizer)
1668 self.Layout()
1669 parent.AddPage(self, _("Project"))
1670
1671 def OnUseSashSelect(self, event):
1672 if not self._useSashMessageShown:
1673 msgTitle = wx.GetApp().GetAppName()
1674 if not msgTitle:
1675 msgTitle = _("Document Options")
1676 wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.",
1677 msgTitle,
1678 wx.OK | wx.ICON_INFORMATION,
1679 self.GetParent())
1680 self._useSashMessageShown = True
1681
1682
1683 def OnOK(self, optionsDialog):
1684 config = wx.ConfigBase_Get()
1685 config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
1686 if not ACTIVEGRID_BASE_IDE:
1687 config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue())
1688
1689
1690 class ProjectService(Service.Service):
1691
1692 #----------------------------------------------------------------------------
1693 # Constants
1694 #----------------------------------------------------------------------------
1695 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
1696 RUNPM_ID = wx.NewId()
1697 RUN_SELECTED_PM_ID = wx.NewId()
1698 RUN_CURRENT_PM_ID = wx.NewId()
1699 ADD_FILES_TO_PROJECT_ID = wx.NewId()
1700 ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId()
1701 RENAME_ID = wx.NewId()
1702 OPEN_SELECTION_ID = wx.NewId()
1703 REMOVE_FROM_PROJECT = wx.NewId()
1704 DELETE_FILE_ID = wx.NewId()
1705 ADD_ALL_FILES_TO_PROJECT_ID = wx.NewId()
1706
1707
1708 #----------------------------------------------------------------------------
1709 # Overridden methods
1710 #----------------------------------------------------------------------------
1711
1712 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT):
1713 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
1714 self._runHandlers = []
1715 self._suppressOpenProjectMessages = False
1716
1717
1718 def _CreateView(self):
1719 return ProjectView(self)
1720
1721
1722 def ShowWindow(self, show = True):
1723 """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """
1724 Service.Service.ShowWindow(self, show)
1725
1726 if show:
1727 project = self.GetView().GetDocument()
1728 if not project:
1729 self.OpenSavedProjects()
1730
1731
1732 #----------------------------------------------------------------------------
1733 # Service specific methods
1734 #----------------------------------------------------------------------------
1735
1736 def GetSuppressOpenProjectMessages(self):
1737 return self._suppressOpenProjectMessages
1738
1739
1740 def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages):
1741 self._suppressOpenProjectMessages = suppressOpenProjectMessages
1742
1743
1744 def GetRunHandlers(self):
1745 return self._runHandlers
1746
1747
1748 def AddRunHandler(self, runHandler):
1749 self._runHandlers.append(runHandler)
1750
1751
1752 def RemoveRunHandler(self, runHandler):
1753 self._runHandlers.remove(runHandler)
1754
1755
1756 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
1757 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
1758
1759 projectMenu = wx.Menu()
1760
1761 ## accelTable = wx.AcceleratorTable([
1762 ## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID"))
1763 ## ])
1764 ## frame.SetAcceleratorTable(accelTable)
1765 isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument
1766 if wx.GetApp().IsMDI() or isProjectDocument:
1767 if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID):
1768 projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project"))
1769 wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1770 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1771 if not menuBar.FindItemById(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID):
1772 projectMenu.Append(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, _("Add Directory Files to Project..."), _("Adds a directory's documents to the current project"))
1773 wx.EVT_MENU(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1774 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1775 if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID):
1776 projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project"))
1777 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
1778 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1779 viewMenuIndex = menuBar.FindMenu(_("&View"))
1780 menuBar.Insert(viewMenuIndex + 1, projectMenu, _("&Project"))
1781 editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
1782 if not menuBar.FindItemById(ProjectService.RENAME_ID):
1783 editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item"))
1784 wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent)
1785 wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent)
1786 if not menuBar.FindItemById(ProjectService.DELETE_FILE_ID):
1787 editMenu.Append(ProjectService.DELETE_FILE_ID, _("Delete File"), _("Delete the file from the project and file system."))
1788 wx.EVT_MENU(frame, ProjectService.DELETE_FILE_ID, frame.ProcessEvent)
1789 wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_FILE_ID, frame.ProcessUpdateUIEvent)
1790
1791 return True
1792
1793
1794 def OnCloseFrame(self, event):
1795 if not self.GetView():
1796 return True
1797
1798 if wx.GetApp().IsMDI():
1799 # close all non-project documents first
1800 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1801 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
1802 if not self.GetDocumentManager().CloseDocument(document, False):
1803 return False
1804
1805 # write project config afterwards because user may change filenames on closing of new documents
1806 self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere
1807
1808 # close all project documents after closing other documents
1809 # because user may save a new document with a new name or cancel closing a document
1810 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1811 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1812 if not document.OnSaveModified():
1813 return False
1814
1815 # This is called when any SDI frame is closed, so need to check if message window is closing or some other window
1816 elif self.GetView() == event.GetEventObject().GetView():
1817 self.SetView(None)
1818 return True
1819
1820
1821 #----------------------------------------------------------------------------
1822 # Event Processing Methods
1823 #----------------------------------------------------------------------------
1824
1825 def ProcessEventBeforeWindows(self, event):
1826 id = event.GetId()
1827 if id == wx.ID_CLOSE_ALL:
1828 self.OnFileCloseAll(event)
1829 return True
1830 return False
1831
1832
1833 def ProcessEvent(self, event):
1834 if Service.Service.ProcessEvent(self, event):
1835 return True
1836
1837 id = event.GetId()
1838 if id == ProjectService.RUN_SELECTED_PM_ID:
1839 self.OnRunProcessModel(event, runSelected=True)
1840 return True
1841 elif id == ProjectService.RUN_CURRENT_PM_ID:
1842 self.OnRunProcessModel(event, runCurrentFile=True)
1843 return True
1844 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1845 self.OnAddCurrentFileToProject(event)
1846 return True
1847 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1848 if self.GetView():
1849 return self.GetView().ProcessEvent(event)
1850 else:
1851 return False
1852 else:
1853 return False
1854
1855
1856 def ProcessUpdateUIEvent(self, event):
1857 if Service.Service.ProcessUpdateUIEvent(self, event):
1858 return True
1859
1860 id = event.GetId()
1861 if (id == ProjectService.RUNPM_ID
1862 or id == ProjectService.RUN_SELECTED_PM_ID
1863 or id == ProjectService.RUN_CURRENT_PM_ID):
1864 event.Enable(self._HasOpenedProjects() and self._HasProcessModel())
1865 return True
1866 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1867 event.Enable(self._CanAddCurrentFileToProject())
1868 return True
1869 elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
1870 or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID
1871 or id == ProjectService.RENAME_ID
1872 or id == ProjectService.OPEN_SELECTION_ID
1873 or id == ProjectService.DELETE_FILE_ID):
1874 event.Enable(False)
1875 return True
1876 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1877 if self.GetView():
1878 return self.GetView().ProcessUpdateUIEvent(event)
1879 else:
1880 return False
1881 else:
1882 return False
1883
1884
1885 def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False):
1886 project = self.GetView().GetDocument()
1887
1888 if project:
1889 ext = None
1890 for template in self.GetDocumentManager().GetTemplates():
1891 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1892 ext = template.GetDefaultExtension()
1893 break;
1894 if not ext:
1895 return
1896
1897 files = filter(lambda f: f.endswith(ext), project.GetFiles())
1898 if not files:
1899 return
1900
1901 docs = wx.GetApp().GetDocumentManager().GetDocuments()
1902 for doc in docs:
1903 if doc.GetFilename() in files and doc.GetDocumentTemplate().GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1904 if not doc.GetProcessModel().beginProcess:
1905 wx.MessageBox(_("Cannot run process. No begin action found."), _("Run Process"))
1906 return
1907
1908 filesModified = False
1909 for doc in docs:
1910 if doc.IsModified():
1911 filesModified = True
1912 break
1913 if filesModified:
1914 frame = self.GetView().GetFrame()
1915 yesNoMsg = wx.MessageDialog(frame,
1916 _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"),
1917 _("Run Process"),
1918 wx.YES_NO|wx.ICON_QUESTION
1919 )
1920 if yesNoMsg.ShowModal() == wx.ID_YES:
1921 wx.GetTopLevelParent(frame).OnFileSaveAll(None)
1922
1923 if runCurrentFile:
1924 fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename()
1925 elif runSelected:
1926 fileToRun = self.GetView().GetSelectedFile()
1927 elif len(files) > 1:
1928 files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower()))
1929 strings = map(lambda file: os.path.basename(file), files)
1930 res = wx.GetSingleChoiceIndex(_("Select a process to run:"),
1931 _("Run"),
1932 strings,
1933 project.GetFirstView()._GetParentFrame())
1934 if res == -1:
1935 return
1936 fileToRun = files[res]
1937 else:
1938 fileToRun = files[0]
1939
1940 self.RunProcessModel(fileToRun)
1941
1942
1943 def RunProcessModel(self, fileToRun):
1944 for runHandler in self.GetRunHandlers():
1945 if runHandler.RunProjectFile(fileToRun):
1946 return
1947 os.system('"' + fileToRun + '"')
1948
1949
1950 def _HasProcessModel(self):
1951 project = self.GetView().GetDocument()
1952
1953 if project:
1954 ext = None
1955 for template in self.GetDocumentManager().GetTemplates():
1956 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1957 ext = template.GetDefaultExtension()
1958 break;
1959 if not ext:
1960 return False
1961
1962 files = filter(lambda f: f.endswith(ext), project.GetFiles())
1963 if not files:
1964 return False
1965
1966 if len(files):
1967 return True
1968
1969 return False
1970
1971
1972 def _HasOpenedProjects(self):
1973 for document in self.GetDocumentManager().GetDocuments():
1974 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1975 return True
1976 return False
1977
1978
1979 def _HasCurrentFile(self):
1980 currentDoc = self.GetDocumentManager().GetCurrentDocument()
1981 return currentDoc
1982
1983
1984 def _CanAddCurrentFileToProject(self):
1985 currentDoc = self.GetDocumentManager().GetCurrentDocument()
1986 if not currentDoc:
1987 return False
1988 if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1989 return False
1990 if not currentDoc._savedYet:
1991 return False
1992 for document in self.GetDocumentManager().GetDocuments():
1993 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1994 return True
1995 return False # There are no documents open
1996
1997
1998 def GetFilesFromCurrentProject(self):
1999 view = self.GetView()
2000 if view:
2001 project = view.GetDocument()
2002 if project:
2003 return project.GetFiles()
2004 return None
2005
2006
2007 def GetCurrentProject(self):
2008 view = self.GetView()
2009 if view:
2010 return view.GetDocument()
2011 return None
2012
2013
2014 def FindProjectByFile(self, filename):
2015 for document in self.GetDocumentManager().GetDocuments():
2016 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2017 if document.GetFilename() == filename:
2018 return document
2019 elif document.IsFileInProject(filename):
2020 return document
2021 return None
2022
2023
2024 def GetCurrentProjectNames(self):
2025 projects = []
2026 for document in self.GetDocumentManager().GetDocuments():
2027 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2028 projects.append(document)
2029 if not projects:
2030 return
2031 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2032 strings = map(lambda project: project.GetPrintableName(), projects)
2033 return strings
2034
2035
2036 def OnAddCurrentFileToProject(self, event):
2037 if not self._CanAddCurrentFileToProject():
2038 return
2039 projects = []
2040 for document in self.GetDocumentManager().GetDocuments():
2041 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2042 projects.append(document)
2043 if not projects:
2044 return
2045 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2046 strings = map(lambda project: project.GetPrintableName(), projects)
2047 res = wx.GetSingleChoiceIndex(_("Select a project to add the file to:"),
2048 _("Add to Project"),
2049 strings,
2050 self.GetDocumentManager().FindSuitableParent())
2051 if res == -1:
2052 return
2053 file = self.GetDocumentManager().GetCurrentDocument().GetFilename()
2054 projects[res].GetCommandProcessor().Submit(ProjectAddFilesCommand(projects[res], [file]))
2055 self.GetView().Activate(True) # after add, should put focus on project editor
2056
2057
2058 def OnFileCloseAll(self, event):
2059 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
2060 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
2061 if not self.GetDocumentManager().CloseDocument(document, False):
2062 return
2063 # document.DeleteAllViews() # Implicitly delete the document when the last view is removed
2064
2065
2066 def OpenSavedProjects(self):
2067 config = wx.ConfigBase_Get()
2068 openedDocs = False
2069 if config.ReadInt("ProjectSaveDocs", True):
2070 docString = config.Read("ProjectSavedDocs")
2071 if docString:
2072 doc = None
2073 for fileName in eval(docString):
2074 if isinstance(fileName, types.StringTypes):
2075 if os.path.exists(fileName):
2076 doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT)
2077
2078 if doc:
2079 openedDocs = True
2080 expandedString = config.Read("ProjectExpandedSavedDocs")
2081 if expandedString:
2082 view = doc.GetFirstView()
2083 view.SetExpandedProjects(eval(expandedString))
2084 return openedDocs
2085
2086
2087 class ProjectEditorMoveCommand(wx.lib.docview.Command):
2088
2089 def __init__(self, view, newPositionItem, item):
2090 wx.lib.docview.Command.__init__(self, canUndo = True)
2091 self._view = view
2092 self._item = item
2093 self._file = view._treeCtrl.GetLongFilename(item)
2094 if view._IsItemFile(item):
2095 self._projectOld = view._GetItemProject(item)
2096 else: # view._IsItemProject(item):
2097 self._projectOld = None
2098 self._projectNew = view._GetItemProject(newPositionItem)
2099
2100
2101 def GetName(self):
2102 return _("Move File %s") % os.path.basename(self._file)
2103
2104
2105 def Do(self):
2106 if self._projectOld:
2107 self._projectOld.RemoveFile(self._file)
2108 if self._projectNew:
2109 self._projectNew.AddFile(self._file)
2110 return True
2111
2112
2113 def Undo(self):
2114 if self._projectNew:
2115 self._projectNew.RemoveFile(self._file)
2116 if self._projectOld:
2117 self._projectOld.AddFile(self._file)
2118 return True
2119
2120
2121 #----------------------------------------------------------------------------
2122 # Icon Bitmaps - generated by encode_bitmaps.py
2123 #----------------------------------------------------------------------------
2124 from wx import ImageFromStream, BitmapFromImage
2125 import cStringIO
2126
2127
2128 def getProjectData():
2129 return \
2130 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2131 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2132 \x00\x00[IDAT8\x8d\xc5\x93\xc1\n\xc00\x08C\x8d\xf6\xff\xffX\xb3Sa-\xf6`;:O\n\
2133 \x12\x1fj\x0059\t\xed\t\xc3\xc9pn\x0b\x88\x88@\rU\x81\xf6.\x18N\xa8aE\x92\rh\
2134 YC\x85\xa4D\x90\x91\xdc%\xf8w\x07+\xd1\xfbW\x98\xc5\x8f\t\x86W\xee\x93+\xbe\
2135 \xc0gn\xdc\x8d\x07\xab"<iG\x8e\xa9\r\x00\x00\x00\x00IEND\xaeB`\x82'
2136
2137 def getProjectBitmap():
2138 return BitmapFromImage(getProjectImage())
2139
2140 def getProjectImage():
2141 stream = cStringIO.StringIO(getProjectData())
2142 return ImageFromStream(stream)
2143
2144 def getProjectIcon():
2145 return wx.IconFromBitmap(getProjectBitmap())
2146
2147
2148 #----------------------------------------------------------------------------
2149
2150 def getBlankData():
2151 return \
2152 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2153 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2154 \x00\x00]IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\x87\xb6C\x11\x89\
2155 \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
2156 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
2157 \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
2158 \x00\x00IEND\xaeB`\x82'
2159
2160
2161 def getBlankBitmap():
2162 return BitmapFromImage(getBlankImage())
2163
2164 def getBlankImage():
2165 stream = cStringIO.StringIO(getBlankData())
2166 return ImageFromStream(stream)
2167
2168 def getBlankIcon():
2169 return wx.IconFromBitmap(getBlankBitmap())
2170