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