]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/ProjectEditor.py
Themed backgrounds for flat generic buttons
[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 contentSizer = wx.BoxSizer(wx.VERTICAL)
985
986 flexGridSizer = wx.FlexGridSizer(cols = 2, vgap=HALF_SPACE, hgap=HALF_SPACE)
987 flexGridSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER_VERTICAL, 0)
988 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
989 dirCtrl = wx.TextCtrl(frame, -1, os.path.dirname(self.GetDocument().GetFilename()), size=(250,-1))
990 dirCtrl.SetToolTipString(dirCtrl.GetValue())
991 lineSizer.Add(dirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
992 findDirButton = wx.Button(frame, -1, "Browse...")
993 lineSizer.Add(findDirButton, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, HALF_SPACE)
994 flexGridSizer.Add(lineSizer, 1, wx.EXPAND)
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=(250, -1), choices=choices)
1026 filterChoice.SetSelection(0)
1027 filterChoice.SetToolTipString(_("Select file type filter."))
1028 flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL)
1029 flexGridSizer.Add(filterChoice, 1, wx.EXPAND)
1030
1031 contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE)
1032
1033 subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories"))
1034 subfolderCtrl.SetValue(True)
1035 contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE)
1036
1037 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
1038 findBtn = wx.Button(frame, wx.ID_OK, _("Add"))
1039 findBtn.SetDefault()
1040 buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE)
1041 buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
1042 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
1043
1044 frame.SetSizer(contentSizer)
1045 frame.Fit()
1046
1047 status = frame.ShowModal()
1048
1049 passedCheck = False
1050 while status == wx.ID_OK and not passedCheck:
1051 if not os.path.exists(dirCtrl.GetValue()):
1052 dlg = wx.MessageDialog(frame,
1053 _("'%s' does not exist.") % dirCtrl.GetValue(),
1054 _("Find in Directory"),
1055 wx.OK | wx.ICON_EXCLAMATION
1056 )
1057 dlg.ShowModal()
1058 dlg.Destroy()
1059
1060 status = frame.ShowModal()
1061 else:
1062 passedCheck = True
1063
1064 if status == wx.ID_OK:
1065 frame.Destroy()
1066
1067 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
1068
1069 doc = self.GetDocument()
1070 searchSubfolders = subfolderCtrl.IsChecked()
1071 dirString = dirCtrl.GetValue()
1072
1073 if os.path.isfile(dirString):
1074 # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
1075 # We'll assume they know what they're doing.
1076 paths = [dirString]
1077 else:
1078 paths = []
1079
1080 index = filterChoice.GetSelection()
1081 if index:
1082 template = visibleTemplates[index-1]
1083
1084 # do search in files on disk
1085 for root, dirs, files in os.walk(dirString):
1086 if not searchSubfolders and root != dirString:
1087 break
1088
1089 for name in files:
1090 if index == 0: # all
1091 for template in visibleTemplates:
1092 if template.FileMatchesTemplate(name):
1093 filename = os.path.join(root, name)
1094
1095 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1096 if doc.IsFileInProject(filename):
1097 break
1098
1099 paths.append(filename)
1100 break
1101 else: # use selected filter
1102 if template.FileMatchesTemplate(name):
1103 filename = os.path.join(root, name)
1104 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1105 if not doc.IsFileInProject(filename):
1106 paths.append(filename)
1107
1108 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
1109
1110 doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths))
1111 self.Activate(True) # after add, should put focus on project editor
1112 else:
1113 frame.Destroy()
1114
1115
1116 def DoAddFilesToProject(self, filenames):
1117 # method used by Drag-n-Drop to add files to current Project
1118 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filenames))
1119
1120
1121 def DoSelectFiles(self, filenames):
1122 # method used by Drag-n-Drop to select files in current Project
1123 self._treeCtrl.UnselectAll()
1124 for file in filenames:
1125 item = self._GetFileItem(longFileName=file)
1126 if item:
1127 self._treeCtrl.SelectItem(item, True)
1128 self._treeCtrl.EnsureVisible(item)
1129
1130
1131 def DoSelectProject(self, x, y):
1132 # method used by Drag-n-Drop to set current Project based on cursor position
1133 item, flag = self._treeCtrl.HitTest((x,y))
1134 if not item:
1135 return False
1136
1137 project = self._GetItemProject(item)
1138 if not project:
1139 return False
1140
1141 projectItem = self._GetProjectItem(project)
1142 self._treeCtrl.UnselectAll()
1143 self._treeCtrl.SelectItem(projectItem)
1144 return True
1145
1146
1147 def OnFocus(self, event):
1148 wx.GetApp().GetDocumentManager().ActivateView(self)
1149 event.Skip()
1150
1151
1152 def OnKillFocus(self, event):
1153 # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame
1154 # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame
1155 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
1156 childFrame = wx.GetApp().GetTopWindow().GetActiveChild()
1157 if childFrame:
1158 childFrame.Activate()
1159 event.Skip()
1160
1161
1162 def OnRightClick(self, event):
1163 self.Activate(True)
1164 if not self._treeCtrl.GetSelections():
1165 return
1166 if len(self._treeCtrl.GetSelections()) == 1 and self._IsItemRoot(self._treeCtrl.GetSelections()[0]):
1167 return # Don't do a menu if it's just the root item selected
1168 menu = wx.Menu()
1169 if self._HasFilesSelected(): # Files context
1170 menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection"))
1171 menu.Enable(ProjectService.OPEN_SELECTION_ID, True)
1172 wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection)
1173 itemIDs = [None]
1174 for item in self._treeCtrl.GetSelections():
1175 if self._IsItemProcessModelFile(item):
1176 itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None]
1177 break
1178 else: # Project context
1179 itemIDs = [wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS, None]
1180 menuBar = self._GetParentFrame().GetMenuBar()
1181 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]
1182 for itemID in itemIDs:
1183 if not itemID:
1184 menu.AppendSeparator()
1185 else:
1186 if itemID == ProjectService.RUN_SELECTED_PM_ID:
1187 menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
1188 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM)
1189 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
1190 menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
1191 wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
1192 wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
1193 else:
1194 item = menuBar.FindItemById(itemID)
1195 if item:
1196 menu.Append(itemID, item.GetLabel())
1197 self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY()))
1198 menu.Destroy()
1199
1200
1201 def OnRunSelectedPM(self, event):
1202 projectService = wx.GetApp().GetService(ProjectService)
1203 if projectService:
1204 projectService.OnRunProcessModel(event, runSelected=True)
1205
1206
1207 def OnRename(self, event):
1208 if self._treeCtrl.GetSelections():
1209 self._treeCtrl.EditLabel(self._treeCtrl.GetSelections()[0])
1210
1211
1212 def OnBeginLabelEdit(self, event):
1213 self._editingSoDontKillFocus = True
1214 item = event.GetItem()
1215 if not self._IsItemFile(item) and not self._IsItemProject(item):
1216 event.Veto()
1217
1218
1219 def OnEndLabelEdit(self, event):
1220 self._editingSoDontKillFocus = False
1221 item = event.GetItem()
1222 newName = event.GetLabel()
1223 if not newName or (not self._IsItemFile(item) and not self._IsItemProject(item)):
1224 event.Veto()
1225 return
1226 if self._IsItemFile(item):
1227 oldFile = self._GetItemFile(item)
1228 newFile = os.path.join(os.path.split(oldFile)[0], newName)
1229 project = self._GetItemProject(item)
1230 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile)):
1231 event.Veto()
1232 return
1233 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(self._treeCtrl.GetSelections()[0]))
1234 elif self._IsItemProject(item):
1235 oldFile = self._GetItemProject(item).GetFilename()
1236 newFile = os.path.join(os.path.split(oldFile)[0], newName)
1237 project = self._GetItemProject(item)
1238 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile, True)):
1239 event.Veto()
1240 return
1241 self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem())
1242
1243
1244 def CanPaste(self):
1245 # wxBug: Should be able to use IsSupported/IsSupportedFormat here
1246 #fileDataObject = wx.FileDataObject()
1247 #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
1248 if not wx.TheClipboard.IsOpened():
1249 if wx.TheClipboard.Open():
1250 fileDataObject = wx.FileDataObject()
1251 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
1252 wx.TheClipboard.Close()
1253 else:
1254 hasFilesInClipboard = False
1255 return hasFilesInClipboard
1256
1257
1258 def OnCut(self, event):
1259 self.OnCopy(event)
1260 self.OnClear(event)
1261
1262
1263 def OnCopy(self, event):
1264 fileDataObject = wx.FileDataObject()
1265 items = self._treeCtrl.GetSelections()
1266 for item in items:
1267 if self._IsItemFile(item):
1268 file = self._treeCtrl.GetLongFilename(item)
1269 fileDataObject.AddFile(file)
1270 if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open():
1271 wx.TheClipboard.SetData(fileDataObject)
1272 wx.TheClipboard.Close()
1273
1274
1275 def OnPaste(self, event):
1276 if wx.TheClipboard.Open():
1277 fileDataObject = wx.FileDataObject()
1278 if wx.TheClipboard.GetData(fileDataObject):
1279 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames()))
1280 wx.TheClipboard.Close()
1281
1282
1283 def OnClear(self, event):
1284 items = self._treeCtrl.GetSelections()
1285 files = []
1286 for item in items:
1287 if self._IsItemFile(item):
1288 files.append((self._GetItemProject(item), self._GetItemFile(item)))
1289 self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(files))
1290
1291
1292 def OnDeleteFile(self, event):
1293 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1294 _("Delete cannot be reversed.\n\nRemove the selected files from the\nprojects and file system permanently?"),
1295 _("Delete File"),
1296 wx.YES_NO|wx.ICON_QUESTION)
1297 if yesNoMsg.ShowModal() == wx.ID_NO:
1298 return
1299
1300 items = self._treeCtrl.GetSelections()
1301 files = []
1302 delFiles = []
1303 for item in items:
1304 if self._IsItemFile(item):
1305 filename = self._GetItemFile(item)
1306 files.append((self._GetItemProject(item), filename))
1307 if filename not in delFiles:
1308 delFiles.append(filename)
1309
1310 # remove selected files from projects
1311 projects = []
1312 for data in files:
1313 proj, filename = data
1314 if proj not in projects:
1315 projects.append(proj)
1316 for project in projects:
1317 filenames = []
1318 for data in files:
1319 proj, filename = data
1320 if project == proj:
1321 filenames.append(filename)
1322 project.RemoveFiles(filenames)
1323
1324 # remove selected files from file system
1325 for filename in delFiles:
1326 if os.path.exists(filename):
1327 try:
1328 os.remove(filename)
1329 except:
1330 wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filename), sys.exc_value),
1331 _("Delete File"),
1332 wx.OK | wx.ICON_EXCLAMATION)
1333
1334
1335 def OnKeyPressed(self, event):
1336 key = event.KeyCode()
1337 if key == wx.WXK_DELETE:
1338 self.OnClear(event)
1339 else:
1340 event.Skip()
1341
1342
1343 def OnSelectAll(self, event):
1344 project = self.GetDocument()
1345 if project:
1346 self._treeCtrl.UnselectAll()
1347 for child in self._GetChildItems(self._GetProjectItem(project)):
1348 self._treeCtrl.SelectItem(child)
1349
1350
1351 def OnOpenSelectionSDI(self, event):
1352 # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window
1353 wx.CallAfter(self.OnOpenSelection, None)
1354
1355
1356 def OnOpenSelection(self, event):
1357 doc = None
1358 try:
1359 items = self._treeCtrl.GetSelections()
1360 for item in items:
1361 if self._IsItemFile(item):
1362 filepath = self._GetItemFile(item)
1363 if not os.path.exists(filepath):
1364 msgTitle = wx.GetApp().GetAppName()
1365 if not msgTitle:
1366 msgTitle = _("File Not Found")
1367 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1368 _("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)),
1369 msgTitle,
1370 wx.YES_NO|wx.ICON_QUESTION
1371 )
1372 if yesNoMsg.ShowModal() == wx.ID_NO:
1373 continue
1374 findFile = wx.FileDialog(self.GetFrame(),
1375 _("Choose a file"),
1376 wx.lib.docview.PathOnly(filepath),
1377 wx.lib.docview.FileNameFromPath(filepath),
1378 style = wx.OPEN
1379 )
1380 if findFile.ShowModal() == wx.ID_OK and findFile.GetPath():
1381 newpath = findFile.GetPath()
1382 else:
1383 newpath = None
1384 findFile.Destroy()
1385 if newpath:
1386 # update Project Model with new location
1387 self.GetDocument().RemoveFile(filepath)
1388 self.GetDocument().AddFile(newpath)
1389 filepath = newpath
1390
1391 doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT)
1392 if not doc:
1393 shortFilename = self._treeCtrl.GetItemText(item)
1394 if shortFilename.endswith(".agp"):
1395 projItem = self._GetProjectItem(shortFilename=shortFilename)
1396 self._treeCtrl.UnselectAll()
1397 if not self._treeCtrl.IsExpanded(projItem):
1398 self._treeCtrl.Expand(projItem)
1399 if not self._treeCtrl.IsVisible(projItem):
1400 self._treeCtrl.EnsureVisible(projItem)
1401 if not self._treeCtrl.IsSelected(projItem):
1402 self._treeCtrl.SelectItem(projItem)
1403
1404 except IOError, (code, message):
1405 msgTitle = wx.GetApp().GetAppName()
1406 if not msgTitle:
1407 msgTitle = _("File Error")
1408 wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath),
1409 msgTitle,
1410 wx.OK | wx.ICON_EXCLAMATION,
1411 self.GetFrame())
1412
1413
1414 #----------------------------------------------------------------------------
1415 # Convenience methods
1416 #----------------------------------------------------------------------------
1417
1418 def _HasFiles(self):
1419 if not self._treeCtrl:
1420 return False
1421 return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files
1422
1423
1424 def _HasSelection(self):
1425 if not self._treeCtrl:
1426 return False
1427
1428 items = self._treeCtrl.GetSelections()
1429 if items:
1430 return True
1431
1432 return False
1433
1434
1435 def _HasFilesSelected(self):
1436 if not self._treeCtrl:
1437 return False
1438 items = self._treeCtrl.GetSelections()
1439 if not items:
1440 return False
1441 for item in items:
1442 if not self._IsItemFile(item):
1443 return False
1444 return True
1445
1446
1447 def _MakeProjectName(self, project):
1448 return project.GetPrintableName()
1449
1450
1451 # Return the tree item for a project
1452 def _GetProjectItem(self, project=None, shortFilename=None):
1453 rootItem = self._treeCtrl.GetRootItem()
1454 (child, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1455 while child.IsOk():
1456 if project:
1457 if self._treeCtrl.GetProjectDoc(child) == project:
1458 return child
1459 elif shortFilename:
1460 if self._treeCtrl.GetItemText(child) == shortFilename:
1461 return child
1462 (child, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1463 return None
1464
1465
1466 # Returns the project for an item, either for a project item or a file that is part of a project
1467 def _GetItemProject(self, item):
1468 if self._IsItemRoot(item):
1469 return None
1470 if self._IsItemProject(item):
1471 return self._treeCtrl.GetProjectDoc(item)
1472 if self._IsItemFile(item):
1473 return self._treeCtrl.GetProjectDoc(self._treeCtrl.GetItemParent(item))
1474 return None
1475
1476
1477 def _GetItemFile(self, item):
1478 if self._IsItemFile(item):
1479 return self._treeCtrl.GetLongFilename(item)
1480 else:
1481 return None
1482
1483
1484 def _GetFileItem(self, shortFileName = None, longFileName = None):
1485 """ Returns the tree item for a file given the short (display) or long (fullpath) file name. """
1486
1487 rootItem = self._treeCtrl.GetRootItem()
1488 (project, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1489 while project.IsOk():
1490 (child, cookie2) = self._treeCtrl.GetFirstChild(project)
1491 while child.IsOk():
1492 if shortFileName:
1493 if self._treeCtrl.GetItemText(child) == shortFileName:
1494 return child
1495 else:
1496 if self._treeCtrl.GetLongFilename(child) == longFileName:
1497 return child
1498 (child, cookie2) = self._treeCtrl.GetNextChild(project, cookie2)
1499 (project, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1500 return None
1501
1502
1503 def _IsItemRoot(self, item):
1504 return item == self._treeCtrl.GetRootItem()
1505
1506
1507 def _IsItemProject(self, item):
1508 return self._treeCtrl.GetProjectDoc(item) != None
1509
1510
1511 def _IsItemFile(self, item):
1512 return self._treeCtrl.GetProjectDoc(item) == None
1513
1514
1515 def _IsItemProcessModelFile(self, item):
1516 if ACTIVEGRID_BASE_IDE:
1517 return False
1518
1519 if self._IsItemFile(item):
1520 filename = self._treeCtrl.GetLongFilename(item)
1521 ext = None
1522 for template in self.GetDocumentManager().GetTemplates():
1523 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1524 ext = template.GetDefaultExtension()
1525 break;
1526 if not ext:
1527 return False
1528
1529 if filename.endswith(ext):
1530 return True
1531
1532 return False
1533
1534
1535 def _GetChildItems(self, parentItem):
1536 children = []
1537 (child, cookie) = self._treeCtrl.GetFirstChild(parentItem)
1538 while child.IsOk():
1539 children.append(child)
1540 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
1541 return children
1542
1543
1544
1545 class ProjectFileDropTarget(wx.FileDropTarget):
1546
1547 def __init__(self, view):
1548 wx.FileDropTarget.__init__(self)
1549 self._view = view
1550
1551
1552 def OnDropFiles(self, x, y, filenames):
1553 if self._view.DoSelectProject(x, y):
1554 self._view.DoAddFilesToProject(filenames)
1555 self._view.DoSelectFiles(filenames)
1556 return True
1557 return False
1558
1559
1560 def OnDragOver(self, x, y, default):
1561 if self._view.DoSelectProject(x,y):
1562 return wx.DragCopy
1563 return wx.DragNone
1564
1565
1566 class ProjectPropertiesDialog(wx.Dialog):
1567
1568
1569 def __init__(self, parent, filename):
1570 wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330))
1571
1572 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
1573
1574 notebook = wx.Notebook(self, -1)
1575 tab = wx.Panel(notebook, -1)
1576
1577 gridSizer = RowColSizer()
1578
1579 gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")), flag=wx.RIGHT, border=HALF_SPACE, row=0, col=0)
1580 if os.path.isfile(filename):
1581 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]), row=0, col=1)
1582
1583 gridSizer.Add(wx.StaticText(tab, -1, _("Location:")), flag=wx.RIGHT, border=HALF_SPACE, row=1, col=0)
1584 gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.split(filename)[0])), flag=wx.BOTTOM, border=SPACE, row=1, col=1)
1585
1586 gridSizer.Add(wx.StaticText(tab, -1, _("Size:")), flag=wx.RIGHT, border=HALF_SPACE, row=2, col=0)
1587 gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")), row=2, col=1)
1588
1589 lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
1590 lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND)
1591 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=HALF_SPACE, row=3, col=0, colspan=2)
1592
1593 gridSizer.Add(wx.StaticText(tab, -1, _("Created:")), flag=wx.RIGHT, border=HALF_SPACE, row=4, col=0)
1594 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))), row=4, col=1)
1595
1596 gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")), flag=wx.RIGHT, border=HALF_SPACE, row=5, col=0)
1597 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))), row=5, col=1)
1598
1599 gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")), flag=wx.RIGHT, border=HALF_SPACE, row=6, col=0)
1600 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))), row=6, col=1)
1601
1602 else:
1603 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")), row=0, col=1)
1604
1605 # add a border around the inside of the tab
1606 spacerGrid = wx.BoxSizer(wx.VERTICAL)
1607 spacerGrid.Add(gridSizer, 0, wx.ALL, SPACE);
1608 tab.SetSizer(spacerGrid)
1609 notebook.AddPage(tab, _("General"))
1610 if wx.Platform == "__WXMSW__":
1611 notebook.SetPageSize((310,200))
1612
1613 sizer = wx.BoxSizer(wx.VERTICAL)
1614 sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
1615 sizer.Add(self.CreateButtonSizer(wx.OK), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
1616
1617 sizer.Fit(self)
1618 self.SetDimensions(-1, -1, 310, -1, wx.SIZE_USE_EXISTING)
1619 self.SetSizer(sizer)
1620 self.Layout()
1621
1622
1623 class ProjectOptionsPanel(wx.Panel):
1624
1625
1626 def __init__(self, parent, id):
1627 wx.Panel.__init__(self, parent, id)
1628 self._useSashMessageShown = False
1629 config = wx.ConfigBase_Get()
1630 self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects"))
1631 self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True))
1632 projectBorderSizer = wx.BoxSizer(wx.VERTICAL)
1633 projectSizer = wx.BoxSizer(wx.VERTICAL)
1634 projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
1635 if not ACTIVEGRID_BASE_IDE:
1636 self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
1637 self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True))
1638 projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
1639 projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE)
1640 self.SetSizer(projectBorderSizer)
1641 self.Layout()
1642 parent.AddPage(self, _("Project"))
1643
1644 def OnUseSashSelect(self, event):
1645 if not self._useSashMessageShown:
1646 msgTitle = wx.GetApp().GetAppName()
1647 if not msgTitle:
1648 msgTitle = _("Document Options")
1649 wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.",
1650 msgTitle,
1651 wx.OK | wx.ICON_INFORMATION,
1652 self.GetParent())
1653 self._useSashMessageShown = True
1654
1655
1656 def OnOK(self, optionsDialog):
1657 config = wx.ConfigBase_Get()
1658 config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
1659 if not ACTIVEGRID_BASE_IDE:
1660 config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue())
1661
1662
1663 class ProjectService(Service.Service):
1664
1665 #----------------------------------------------------------------------------
1666 # Constants
1667 #----------------------------------------------------------------------------
1668 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
1669 RUNPM_ID = wx.NewId()
1670 RUN_SELECTED_PM_ID = wx.NewId()
1671 RUN_CURRENT_PM_ID = wx.NewId()
1672 ADD_FILES_TO_PROJECT_ID = wx.NewId()
1673 ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId()
1674 RENAME_ID = wx.NewId()
1675 OPEN_SELECTION_ID = wx.NewId()
1676 REMOVE_FROM_PROJECT = wx.NewId()
1677 DELETE_FILE_ID = wx.NewId()
1678 ADD_ALL_FILES_TO_PROJECT_ID = wx.NewId()
1679
1680
1681 #----------------------------------------------------------------------------
1682 # Overridden methods
1683 #----------------------------------------------------------------------------
1684
1685 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT):
1686 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
1687 self._runHandlers = []
1688 self._suppressOpenProjectMessages = False
1689
1690
1691 def _CreateView(self):
1692 return ProjectView(self)
1693
1694
1695 def ShowWindow(self, show = True):
1696 """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """
1697 Service.Service.ShowWindow(self, show)
1698
1699 if show:
1700 project = self.GetView().GetDocument()
1701 if not project:
1702 self.OpenSavedProjects()
1703
1704
1705 #----------------------------------------------------------------------------
1706 # Service specific methods
1707 #----------------------------------------------------------------------------
1708
1709 def GetSuppressOpenProjectMessages(self):
1710 return self._suppressOpenProjectMessages
1711
1712
1713 def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages):
1714 self._suppressOpenProjectMessages = suppressOpenProjectMessages
1715
1716
1717 def GetRunHandlers(self):
1718 return self._runHandlers
1719
1720
1721 def AddRunHandler(self, runHandler):
1722 self._runHandlers.append(runHandler)
1723
1724
1725 def RemoveRunHandler(self, runHandler):
1726 self._runHandlers.remove(runHandler)
1727
1728
1729 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
1730 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
1731
1732 projectMenu = wx.Menu()
1733
1734 ## accelTable = wx.AcceleratorTable([
1735 ## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID"))
1736 ## ])
1737 ## frame.SetAcceleratorTable(accelTable)
1738 isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument
1739 if wx.GetApp().IsMDI() or isProjectDocument:
1740 if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID):
1741 projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project"))
1742 wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1743 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1744 if not menuBar.FindItemById(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID):
1745 projectMenu.Append(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, _("Add All Files to Project..."), _("Adds a directory's documents to the current project"))
1746 wx.EVT_MENU(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1747 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1748 if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID):
1749 projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project"))
1750 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
1751 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1752 viewMenuIndex = menuBar.FindMenu(_("&View"))
1753 menuBar.Insert(viewMenuIndex + 1, projectMenu, _("&Project"))
1754 editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
1755 if not menuBar.FindItemById(ProjectService.RENAME_ID):
1756 editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item"))
1757 wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent)
1758 wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent)
1759 if not menuBar.FindItemById(ProjectService.DELETE_FILE_ID):
1760 editMenu.Append(ProjectService.DELETE_FILE_ID, _("Delete File"), _("Delete the file from the project and file system."))
1761 wx.EVT_MENU(frame, ProjectService.DELETE_FILE_ID, frame.ProcessEvent)
1762 wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_FILE_ID, frame.ProcessUpdateUIEvent)
1763
1764 return True
1765
1766
1767 def OnCloseFrame(self, event):
1768 if not self.GetView():
1769 return True
1770
1771 if wx.GetApp().IsMDI():
1772 # close all non-project documents first
1773 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1774 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
1775 if not self.GetDocumentManager().CloseDocument(document, False):
1776 return False
1777
1778 # write project config afterwards because user may change filenames on closing of new documents
1779 self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere
1780
1781 # close all project documents after closing other documents
1782 # because user may save a new document with a new name or cancel closing a document
1783 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1784 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1785 if not document.OnSaveModified():
1786 return False
1787
1788 # This is called when any SDI frame is closed, so need to check if message window is closing or some other window
1789 elif self.GetView() == event.GetEventObject().GetView():
1790 self.SetView(None)
1791 return True
1792
1793
1794 #----------------------------------------------------------------------------
1795 # Event Processing Methods
1796 #----------------------------------------------------------------------------
1797
1798 def ProcessEventBeforeWindows(self, event):
1799 id = event.GetId()
1800 if id == wx.ID_CLOSE_ALL:
1801 self.OnFileCloseAll(event)
1802 return True
1803 return False
1804
1805
1806 def ProcessEvent(self, event):
1807 if Service.Service.ProcessEvent(self, event):
1808 return True
1809
1810 id = event.GetId()
1811 if id == ProjectService.RUN_SELECTED_PM_ID:
1812 self.OnRunProcessModel(event, runSelected=True)
1813 return True
1814 elif id == ProjectService.RUN_CURRENT_PM_ID:
1815 self.OnRunProcessModel(event, runCurrentFile=True)
1816 return True
1817 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1818 self.OnAddCurrentFileToProject(event)
1819 return True
1820 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1821 if self.GetView():
1822 return self.GetView().ProcessEvent(event)
1823 else:
1824 return False
1825 else:
1826 return False
1827
1828
1829 def ProcessUpdateUIEvent(self, event):
1830 if Service.Service.ProcessUpdateUIEvent(self, event):
1831 return True
1832
1833 id = event.GetId()
1834 if (id == ProjectService.RUNPM_ID
1835 or id == ProjectService.RUN_SELECTED_PM_ID
1836 or id == ProjectService.RUN_CURRENT_PM_ID):
1837 event.Enable(self._HasOpenedProjects() and self._HasProcessModel())
1838 return True
1839 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1840 event.Enable(self._CanAddCurrentFileToProject())
1841 return True
1842 elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
1843 or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID
1844 or id == ProjectService.RENAME_ID
1845 or id == ProjectService.OPEN_SELECTION_ID
1846 or id == ProjectService.DELETE_FILE_ID):
1847 event.Enable(False)
1848 return True
1849 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1850 if self.GetView():
1851 return self.GetView().ProcessUpdateUIEvent(event)
1852 else:
1853 return False
1854 else:
1855 return False
1856
1857
1858 def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False):
1859 project = self.GetView().GetDocument()
1860
1861 if project:
1862 ext = None
1863 for template in self.GetDocumentManager().GetTemplates():
1864 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1865 ext = template.GetDefaultExtension()
1866 break;
1867 if not ext:
1868 return
1869
1870 files = filter(lambda f: f.endswith(ext), project.GetFiles())
1871 if not files:
1872 return
1873
1874 docs = wx.GetApp().GetDocumentManager().GetDocuments()
1875 for doc in docs:
1876 if doc.GetFilename() in files and doc.GetDocumentTemplate().GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1877 if not doc.GetProcessModel().beginProcess:
1878 wx.MessageBox(_("Cannot run process. No begin action found."), _("Run Process"))
1879 return
1880
1881 filesModified = False
1882 for doc in docs:
1883 if doc.IsModified():
1884 filesModified = True
1885 break
1886 if filesModified:
1887 frame = self.GetView().GetFrame()
1888 yesNoMsg = wx.MessageDialog(frame,
1889 _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"),
1890 _("Run Process"),
1891 wx.YES_NO|wx.ICON_QUESTION
1892 )
1893 if yesNoMsg.ShowModal() == wx.ID_YES:
1894 wx.GetTopLevelParent(frame).OnFileSaveAll(None)
1895
1896 if runCurrentFile:
1897 fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename()
1898 elif runSelected:
1899 fileToRun = self.GetView().GetSelectedFile()
1900 elif len(files) > 1:
1901 files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower()))
1902 strings = map(lambda file: os.path.basename(file), files)
1903 res = wx.GetSingleChoiceIndex(_("Select a process to run:"),
1904 _("Run"),
1905 strings,
1906 project.GetFirstView()._GetParentFrame())
1907 if res == -1:
1908 return
1909 fileToRun = files[res]
1910 else:
1911 fileToRun = files[0]
1912
1913 self.RunProcessModel(fileToRun)
1914
1915
1916 def RunProcessModel(self, fileToRun):
1917 for runHandler in self.GetRunHandlers():
1918 if runHandler.RunProjectFile(fileToRun):
1919 return
1920 os.system('"' + fileToRun + '"')
1921
1922
1923 def _HasProcessModel(self):
1924 project = self.GetView().GetDocument()
1925
1926 if project:
1927 ext = None
1928 for template in self.GetDocumentManager().GetTemplates():
1929 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1930 ext = template.GetDefaultExtension()
1931 break;
1932 if not ext:
1933 return False
1934
1935 files = filter(lambda f: f.endswith(ext), project.GetFiles())
1936 if not files:
1937 return False
1938
1939 if len(files):
1940 return True
1941
1942 return False
1943
1944
1945 def _HasOpenedProjects(self):
1946 for document in self.GetDocumentManager().GetDocuments():
1947 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1948 return True
1949 return False
1950
1951
1952 def _HasCurrentFile(self):
1953 currentDoc = self.GetDocumentManager().GetCurrentDocument()
1954 return currentDoc
1955
1956
1957 def _CanAddCurrentFileToProject(self):
1958 currentDoc = self.GetDocumentManager().GetCurrentDocument()
1959 if not currentDoc:
1960 return False
1961 if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1962 return False
1963 if not currentDoc._savedYet:
1964 return False
1965 for document in self.GetDocumentManager().GetDocuments():
1966 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1967 return True
1968 return False # There are no documents open
1969
1970
1971 def GetFilesFromCurrentProject(self):
1972 view = self.GetView()
1973 if view:
1974 project = view.GetDocument()
1975 if project:
1976 return project.GetFiles()
1977 return None
1978
1979
1980 def GetCurrentProject(self):
1981 view = self.GetView()
1982 if view:
1983 return view.GetDocument()
1984 return None
1985
1986
1987 def FindProjectByFile(self, filename):
1988 for document in self.GetDocumentManager().GetDocuments():
1989 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1990 if document.GetFilename() == filename:
1991 return document
1992 elif document.IsFileInProject(filename):
1993 return document
1994 return None
1995
1996
1997 def GetCurrentProjectNames(self):
1998 projects = []
1999 for document in self.GetDocumentManager().GetDocuments():
2000 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2001 projects.append(document)
2002 if not projects:
2003 return
2004 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2005 strings = map(lambda project: project.GetPrintableName(), projects)
2006 return strings
2007
2008
2009 def OnAddCurrentFileToProject(self, event):
2010 if not self._CanAddCurrentFileToProject():
2011 return
2012 projects = []
2013 for document in self.GetDocumentManager().GetDocuments():
2014 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2015 projects.append(document)
2016 if not projects:
2017 return
2018 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2019 strings = map(lambda project: project.GetPrintableName(), projects)
2020 res = wx.GetSingleChoiceIndex(_("Select a project to add the file to:"),
2021 _("Add to Project"),
2022 strings,
2023 self.GetDocumentManager().FindSuitableParent())
2024 if res == -1:
2025 return
2026 file = self.GetDocumentManager().GetCurrentDocument().GetFilename()
2027 projects[res].GetCommandProcessor().Submit(ProjectAddFilesCommand(projects[res], [file]))
2028 self.GetView().Activate(True) # after add, should put focus on project editor
2029
2030
2031 def OnFileCloseAll(self, event):
2032 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
2033 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
2034 if not self.GetDocumentManager().CloseDocument(document, False):
2035 return
2036 # document.DeleteAllViews() # Implicitly delete the document when the last view is removed
2037
2038
2039 def OpenSavedProjects(self):
2040 config = wx.ConfigBase_Get()
2041 openedDocs = False
2042 if config.ReadInt("ProjectSaveDocs", True):
2043 docString = config.Read("ProjectSavedDocs")
2044 if docString:
2045 doc = None
2046 for fileName in eval(docString):
2047 if isinstance(fileName, types.StringTypes):
2048 if os.path.exists(fileName):
2049 doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT)
2050
2051 if doc:
2052 openedDocs = True
2053 expandedString = config.Read("ProjectExpandedSavedDocs")
2054 if expandedString:
2055 view = doc.GetFirstView()
2056 view.SetExpandedProjects(eval(expandedString))
2057 return openedDocs
2058
2059
2060 class ProjectEditorMoveCommand(wx.lib.docview.Command):
2061
2062 def __init__(self, view, newPositionItem, item):
2063 wx.lib.docview.Command.__init__(self, canUndo = True)
2064 self._view = view
2065 self._item = item
2066 self._file = view._treeCtrl.GetLongFilename(item)
2067 if view._IsItemFile(item):
2068 self._projectOld = view._GetItemProject(item)
2069 else: # view._IsItemProject(item):
2070 self._projectOld = None
2071 self._projectNew = view._GetItemProject(newPositionItem)
2072
2073
2074 def GetName(self):
2075 return _("Move File %s") % os.path.basename(self._file)
2076
2077
2078 def Do(self):
2079 if self._projectOld:
2080 self._projectOld.RemoveFile(self._file)
2081 if self._projectNew:
2082 self._projectNew.AddFile(self._file)
2083 return True
2084
2085
2086 def Undo(self):
2087 if self._projectNew:
2088 self._projectNew.RemoveFile(self._file)
2089 if self._projectOld:
2090 self._projectOld.AddFile(self._file)
2091 return True
2092
2093
2094 #----------------------------------------------------------------------------
2095 # Icon Bitmaps - generated by encode_bitmaps.py
2096 #----------------------------------------------------------------------------
2097 from wx import ImageFromStream, BitmapFromImage
2098 import cStringIO
2099
2100
2101 def getProjectData():
2102 return \
2103 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2104 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2105 \x00\x00[IDAT8\x8d\xc5\x93\xc1\n\xc00\x08C\x8d\xf6\xff\xffX\xb3Sa-\xf6`;:O\n\
2106 \x12\x1fj\x0059\t\xed\t\xc3\xc9pn\x0b\x88\x88@\rU\x81\xf6.\x18N\xa8aE\x92\rh\
2107 YC\x85\xa4D\x90\x91\xdc%\xf8w\x07+\xd1\xfbW\x98\xc5\x8f\t\x86W\xee\x93+\xbe\
2108 \xc0gn\xdc\x8d\x07\xab"<iG\x8e\xa9\r\x00\x00\x00\x00IEND\xaeB`\x82'
2109
2110 def getProjectBitmap():
2111 return BitmapFromImage(getProjectImage())
2112
2113 def getProjectImage():
2114 stream = cStringIO.StringIO(getProjectData())
2115 return ImageFromStream(stream)
2116
2117 def getProjectIcon():
2118 return wx.IconFromBitmap(getProjectBitmap())
2119
2120
2121 #----------------------------------------------------------------------------
2122
2123 def getBlankData():
2124 return \
2125 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2126 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2127 \x00\x00]IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\x87\xb6C\x11\x89\
2128 \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
2129 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
2130 \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
2131 \x00\x00IEND\xaeB`\x82'
2132
2133
2134 def getBlankBitmap():
2135 return BitmapFromImage(getBlankImage())
2136
2137 def getBlankImage():
2138 stream = cStringIO.StringIO(getBlankData())
2139 return ImageFromStream(stream)
2140
2141 def getBlankIcon():
2142 return wx.IconFromBitmap(getBlankBitmap())
2143