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