X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/510bb7480c5138dd5127ed3d8b1d9cbab39983c9..1f780e48af479e7bf9a07eaaa1ab6b41f1ffb17b:/wxPython/samples/ide/activegrid/tool/ProjectEditor.py diff --git a/wxPython/samples/ide/activegrid/tool/ProjectEditor.py b/wxPython/samples/ide/activegrid/tool/ProjectEditor.py new file mode 100644 index 0000000000..763e74bc0a --- /dev/null +++ b/wxPython/samples/ide/activegrid/tool/ProjectEditor.py @@ -0,0 +1,1847 @@ +#---------------------------------------------------------------------------- +# Name: ProjectEditor.py +# Purpose: IDE-style Project Editor for wx.lib.pydocview +# +# Author: Peter Yared, Morgan Hua +# +# Created: 8/15/03 +# CVS-ID: $Id$ +# Copyright: (c) 2003, 2004, 2005 ActiveGrid, Inc. +# License: wxWindows License +#---------------------------------------------------------------------------- + +import wx.lib.docview +import wx.lib.pydocview +import types +import os +import os.path +import wx +from wxPython.lib.rcsizer import RowColSizer +import time +import Service +import MessageService +import DebuggerService +import sys +import activegrid.util.xmlmarshaller +import UICommon +from IDE import ACTIVEGRID_BASE_IDE +if not ACTIVEGRID_BASE_IDE: + import ProcessModelEditor + +_ = wx.GetTranslation + +if wx.Platform == '__WXMSW__': + _WINDOWS = True +else: + _WINDOWS = False + + +#---------------------------------------------------------------------------- +# XML Marshalling Methods +#---------------------------------------------------------------------------- + +def load(fileObject): + xml = fileObject.read() + projectModel = activegrid.util.xmlmarshaller.unmarshal(xml) + return projectModel + + +def save(fileObject, projectModel): + xml = activegrid.util.xmlmarshaller.marshal(projectModel, prettyPrint=True) + fileObject.write(xml) + + +#---------------------------------------------------------------------------- +# Classes +#---------------------------------------------------------------------------- + +class ProjectModel: + __xmlname__ = "projectmodel" + __xmlrename__ = { "_files":"files", "_homepath":"homepath" } + + def __init__(self): + self._homepath = None + self._files = [] + + +class ProjectDocument(wx.lib.docview.Document): + + def __init__(self): + wx.lib.docview.Document.__init__(self) + self._projectModel = ProjectModel() + + + def GetModel(self): + return self._projectModel + + + def OnCreate(self, path, flags): + projectService = wx.GetApp().GetService(ProjectService) + if projectService.GetView(): + view = projectService.GetView() + self.AddView(view) + else: + view = self.GetDocumentTemplate().CreateView(self, flags) + projectService.SetView(view) + return view + + + def LoadObject(self, fileObject): + self._projectModel = activegrid.tool.ProjectEditor.load(fileObject) + return True + + + def SaveObject(self, fileObject): + activegrid.tool.ProjectEditor.save(fileObject, self._projectModel) + return True + + + def OnSaveDocument(self, filename): + self._projectModel._homepath = wx.lib.docview.PathOnly(filename) + return wx.lib.docview.Document.OnSaveDocument(self, filename) + + + def OnOpenDocument(self, filename): + view = self.GetFirstView() + frame = view.GetFrame() + + if not os.path.exists(filename): + wx.GetApp().CloseSplash() + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("File Error") + wx.MessageBox(_("Could not find '%s'.") % filename, + msgTitle, + wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP, + frame) + return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed + + fileObject = file(filename, 'r') + try: + self.LoadObject(fileObject) + except: + wx.GetApp().CloseSplash() + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("File Error") + wx.MessageBox(_("Could not open '%s'. %s") % (wx.lib.docview.FileNameFromPath(filename), sys.exc_value), + msgTitle, + wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP, + frame) + return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed + + self.Modify(False) + + # if the project file has moved, then ask the user if we should readjust the paths of all the files in the project + newHomepath = wx.lib.docview.PathOnly(filename) + if newHomepath != self._projectModel._homepath: + wx.GetApp().CloseSplash() + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("Project Moved") + projectService = wx.GetApp().GetService(activegrid.tool.ProjectEditor.ProjectService) + yesNoMsg = wx.MessageDialog(frame, + _("The project file '%s' was moved from:\n '%s'\nto:\n '%s'.\n\nWould you like to automatically adjust the project contents accordingly?") % (wx.lib.docview.FileNameFromPath(filename), self._projectModel._homepath, wx.lib.docview.PathOnly(filename)), + msgTitle, + wx.YES_NO | wx.STAY_ON_TOP + ) + if projectService.GetSuppressOpenProjectMessages() or yesNoMsg.ShowModal() == wx.ID_YES: + if not projectService.GetSuppressOpenProjectMessages(): + messageService = wx.GetApp().GetService(MessageService.MessageService) + messageService.ShowWindow() + messageView = messageService.GetView() + messageView.ClearLines() + messageView.AddLines(_("The project file '%s' was moved from:\n '%s'\nto:\n '%s'\n") % (wx.lib.docview.FileNameFromPath(filename), self._projectModel._homepath, wx.lib.docview.PathOnly(filename))) + messageView.AddLines(_("Updating file references:\n")) + + for index, filepath in enumerate(self._projectModel._files): + if filepath.startswith(self._projectModel._homepath + os.sep): + newfile = newHomepath + filepath[len(self._projectModel._homepath):len(filepath)] + if os.path.exists(newfile): + self._projectModel._files[index] = newfile + if not projectService.GetSuppressOpenProjectMessages(): + messageView.AddLines(_(" Success: '%s' location changed from '%s' to '%s'\n") % (wx.lib.docview.FileNameFromPath(filepath), wx.lib.docview.PathOnly(filepath), newHomepath)) + self.Modify(True) + else: + if not projectService.GetSuppressOpenProjectMessages(): + messageView.AddLines(_(" Failure: Couldn't find '%s', file wasn't located at '%s'\n") % (wx.lib.docview.FileNameFromPath(filepath), newHomepath)) + else: + if not projectService.GetSuppressOpenProjectMessages(): + messageView.AddLines(_( " Unmodified: '%s' location wasn't relative to '%s'\n") % (filepath, self._projectModel._homepath)) + self._projectModel._homepath = newHomepath + if not projectService.GetSuppressOpenProjectMessages(): + messageView.AddLines(_("Project file updated.")) + + self.SetFilename(filename, True) + view.AddProjectToView(self) + self.UpdateAllViews() + self._savedYet = True + view.Activate(True) + return True + + + def AddFile(self, file): + return self.AddFiles([file]) + + + def AddFiles(self, files): + notAlreadyThereFiles = filter(lambda x: x not in self._projectModel._files, files) # Filter to the files that are not already in the project + if len(notAlreadyThereFiles) == 0: + self.UpdateAllViews(hint = ("select", self, files)) + return False + else: + self._projectModel._files = self._projectModel._files + notAlreadyThereFiles + self.UpdateAllViews(hint = ("add", self, notAlreadyThereFiles)) + self.Modify(True) + return True + + + def RemoveFile(self, file): + return self.RemoveFiles([file]) + + + def RemoveFiles(self, files): + for file in files: + self._projectModel._files.remove(file) + self.UpdateAllViews(hint = ("remove", self, files)) + self.Modify(True) + return True + + + def RenameFile(self, oldFile, newFile, isProject = False): + try: + if oldFile == newFile: + return False + + # projects don't have to exist yet, so not required to rename old file, + # but files must exist, so we'll try to rename and allow exceptions to occur if can't. + if not isProject or (isProject and os.path.exists(oldFile)): + os.rename(oldFile, newFile) + + if isProject: + documents = self.GetDocumentManager().GetDocuments() + for document in documents: + if document.GetFilename() == oldFile: # If the renamed document is open, update it + document.SetFilename(newFile) + document.SetTitle(wx.lib.docview.FileNameFromPath(newFile)) + document.UpdateAllViews(hint = ("rename", document, newFile)) + else: + self.RemoveFile(oldFile) + self.AddFile(newFile) + documents = self.GetDocumentManager().GetDocuments() + for document in documents: + if document.GetFilename() == oldFile: # If the renamed document is open, update it + document.SetFilename(newFile, notifyViews = True) + document.UpdateAllViews(hint = ("rename", document, newFile)) + return True + except OSError, (code, message): + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("File Error") + wx.MessageBox("Could not rename '%s'. '%s'" % (wx.lib.docview.FileNameFromPath(oldFile), message), + msgTitle, + wx.OK | wx.ICON_EXCLAMATION, + self.GetFirstView().GetFrame()) + return False + + + def GetFiles(self): + return self._projectModel._files + + + def IsFileInProject(self, filename): + return filename in self.GetFiles() + + +import Wizard + +class NewProjectWizard(Wizard.BaseWizard): + + WIZTITLE = _("New Project Wizard") + + def __init__(self, parent): + self._parent = parent + self._fullProjectPath = None + Wizard.BaseWizard.__init__(self, parent, self.WIZTITLE) + self._projectLocationPage = self.CreateProjectLocation(self) + wx.wizard.EVT_WIZARD_PAGE_CHANGING(self, self.GetId(), self.OnWizPageChanging) + + def CreateProjectLocation(self,wizard): + page = Wizard.TitledWizardPage(wizard, _("Project File Location")) + + page.GetSizer().Add(wx.StaticText(page, -1, _("\nSelect the directory and filename for the project.\n\n"))) + self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, _("File Name:"), _("Directory:"), _("agp"), startingDirectory=os.getcwd()) + page.GetSizer().Add(sizer, 1, flag=wx.EXPAND) + + wizard.Layout() + wizard.FitToPage(page) + return page + + def RunWizard(self, existingTables = None, existingRelationships = None): + status = wx.wizard.Wizard.RunWizard(self, self._projectLocationPage) + if status: + docManager = wx.GetApp().GetTopWindow().GetDocumentManager() + if os.path.exists(self._fullProjectPath): + # What if the document is already open and we're overwriting it? + documents = docManager.GetDocuments() + for document in documents: + if document.GetFilename() == self._fullProjectPath: # If the renamed document is open, update it + document.DeleteAllViews() + break + os.remove(self._fullProjectPath) + + for template in docManager.GetTemplates(): + if template.GetDocumentType() == ProjectDocument: + doc = template.CreateDocument(self._fullProjectPath, flags = wx.lib.docview.DOC_NEW) + doc.OnSaveDocument(self._fullProjectPath) + view = doc.GetFirstView() + view.AddProjectToView(doc) + break + + self.Destroy() + return status + + + def OnWizPageChanging(self, event): + if event.GetDirection(): # It's going forwards + if event.GetPage() == self._projectLocationPage: + if not self._fileValidation(): + event.Veto() + return + self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(),'.agp')) + + + + def OnShowCreatePages(self): + self.Hide() + import DataModelEditor + requestedPos = self.GetPositionTuple() + projectService = wx.GetApp().GetService(ProjectService) + projectView = projectService.GetView() + + wiz = DataModelEditor.ImportExportWizard(projectView.GetFrame(), pos=requestedPos) + if wiz.RunWizard(dontDestroy=True): + self._schemaName.SetValue(wiz.GetSchemaFileName()) + wiz.Destroy() + self.Show(True) + +class ProjectTemplate(wx.lib.docview.DocTemplate): + + def CreateDocument(self, path, flags): + if path: + return wx.lib.docview.DocTemplate.CreateDocument(self, path, flags) + else: + wiz = NewProjectWizard(wx.GetApp().GetTopWindow()) + wiz.RunWizard() + wiz.Destroy() + return None # never return the doc, otherwise docview will think it is a new file and rename it + +class ProjectAddFilesCommand(wx.lib.docview.Command): + + def __init__(self, projectDoc, files): + wx.lib.docview.Command.__init__(self, canUndo = True) + self._projectDoc = projectDoc + self._files = files + + + def GetName(self): + if len(self._files) == 1: + return _("Add File") + else: + return _("Add Files") + + + def Do(self): + return self._projectDoc.AddFiles(self._files) + + + def Undo(self): + return self._projectDoc.RemoveFiles(self._files) + + +class ProjectRemoveFilesCommand(wx.lib.docview.Command): + + def __init__(self, projectDoc, files): + wx.lib.docview.Command.__init__(self, canUndo = True) + self._projectDoc = projectDoc + self._files = files + + + def GetName(self): + if len(self._files) == 1: + return _("Remove File") + else: + return _("Remove Files") + + + def Do(self): + return self._projectDoc.RemoveFiles(self._files) + + + def Undo(self): + return self._projectDoc.AddFiles(self._files) + + +class ProjectRenameFileCommand(wx.lib.docview.Command): + + def __init__(self, projectDoc, oldFile, newFile, isProject = False): + wx.lib.docview.Command.__init__(self, canUndo = True) + self._projectDoc = projectDoc + self._oldFile = oldFile + self._newFile = newFile + self._isProject = isProject + + + def GetName(self): + return _("Rename File") + + + def Do(self): + return self._projectDoc.RenameFile(self._oldFile, self._newFile, self._isProject) + + + def Undo(self): + return self._projectDoc.RenameFile(self._newFile, self._oldFile, self._isProject) + + +class ProjectTreeCtrl(wx.TreeCtrl): + + def __init__(self, parent, id, style): + wx.TreeCtrl.__init__(self, parent, id, style = style) + + templates = wx.GetApp().GetDocumentManager().GetTemplates() + iconList = wx.ImageList(16, 16, initialCount = len(templates)) + self._iconIndexLookup = [] + for template in templates: + icon = template.GetIcon() + if icon: + if icon.GetHeight() != 16: + icon.SetHeight(16) # wxBug: img2py.py uses EmptyIcon which is 32x32 + if icon.GetWidth() != 16: + icon.SetWidth(16) # wxBug: img2py.py uses EmptyIcon which is 32x32 + iconIndex = iconList.AddIcon(icon) + self._iconIndexLookup.append((template, iconIndex)) + + icon = getBlankIcon() + if icon.GetHeight() != 16: + icon.SetHeight(16) # wxBug: img2py.py uses EmptyIcon which is 32x32 + if icon.GetWidth() != 16: + icon.SetWidth(16) # wxBug: img2py.py uses EmptyIcon which is 32x32 + self._blankIconIndex = iconList.AddIcon(icon) + self.AssignImageList(iconList) + + + def OnCompareItems(self, item1, item2): + return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower()) + + + def AppendItem(self, parent, filepath): + item = wx.TreeCtrl.AppendItem(self, parent, filepath) + + found = False + template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filepath) + if not template and parent == self.GetRootItem(): # If the parent is a root it's a new project + template = wx.GetApp().GetDocumentManager().FindTemplateForPath('.agp') + if template: + for t, iconIndex in self._iconIndexLookup: + if t is template: + self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Normal) + self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Expanded) + self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Selected) + found = True + break + + if not found: + self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Normal) + self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Expanded) + self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Selected) + + return item + + +class ProjectView(wx.lib.docview.View): + + + #---------------------------------------------------------------------------- + # Overridden methods + #---------------------------------------------------------------------------- + + def __init__(self, service = None): + wx.lib.docview.View.__init__(self) + self._service = service # not used, but kept to match other Services + self._lastDirectory = "" + self._treeCtrl = None + self._editingSoDontKillFocus = False + self._checkEditMenu = True + + + def Destroy(self): + projectService = wx.GetApp().GetService(ProjectService) + if projectService: + projectService.SetView(None) + wx.lib.docview.View.Destroy(self) + + + def GetDocument(self): + if not self._treeCtrl: + return None + + items = self._treeCtrl.GetSelections() + if not items: # No selection, so just return first project + item = self._treeCtrl.GetFirstVisibleItem() + if item.IsOk(): + return self._GetItemProject(item) + else: + return None + + for item in items: + project = self._GetItemProject(item) + if project: + return project + + return None + + + 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 + return wx.GetApp().GetDocumentManager() + + + def OnChangeFilename(self): + if self.GetFrame(): + title = _("Projects") + if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName(): + title = title + " - " + wx.GetApp().GetAppName() + self.GetFrame().SetTitle(title) + project = self.GetDocument() + if project: + projectItem = self._GetProjectItem(project) + name = self._treeCtrl.GetItemText(self._GetProjectItem(project)) + name2 = self._MakeProjectName(project) + if name != name2: + self._treeCtrl.SetItemText(projectItem, name2) + self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem()) + + + def Activate(self, activate = True): + if not wx.GetApp().IsMDI(): + if activate and not self.IsShown(): + self.Show() + + if self.IsShown(): + wx.lib.docview.View.Activate(self, activate = activate) + if activate and self._treeCtrl: + self._treeCtrl.SetFocus() + + + def OnCreate(self, doc, flags): + config = wx.ConfigBase_Get() + if wx.GetApp().IsMDI(): + self._embeddedWindow = wx.GetApp().GetTopWindow().GetEmbeddedWindow(wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT) + self.SetFrame(self._embeddedWindow) + frame = self._embeddedWindow + else: + self._embeddedWindow = None + pos = config.ReadInt("ProjectFrameXLoc", -1), config.ReadInt("ProjectFrameYLoc", -1) + # make sure frame is visible + screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X) + screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) + if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight: + pos = wx.DefaultPosition + + size = wx.Size(config.ReadInt("ProjectFrameXSize", -1), config.ReadInt("ProjectFrameYSize", -1)) + + title = _("Projects") + if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName(): + title = title + " - " + wx.GetApp().GetAppName() + + frame = wx.GetApp().CreateDocumentFrame(self, doc, 0, title = title, pos = pos, size = size) + if config.ReadInt("ProjectFrameMaximized", False): + frame.Maximize(True) + + sizer = wx.BoxSizer() + 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) + self._treeCtrl.AddRoot(_("Projects")) + + if self._embeddedWindow: + sizer.Add(self._treeCtrl) + sizer.Fit(frame) + else: + sizer.Add(self._treeCtrl, 1, wx.EXPAND, 0) + frame.SetSizer(sizer) + frame.Layout() + self.Activate() + + if wx.GetApp().IsMDI(): + wx.EVT_SET_FOCUS(self._treeCtrl, self.OnFocus) + wx.EVT_KILL_FOCUS(self._treeCtrl, self.OnKillFocus) + + if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: + wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelectionSDI) + else: + wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelection) + wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit) + wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit) + wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick) + wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed) + # wx.EVT_COMMAND_RIGHT_CLICK(self._treeCtrl, self._treeCtrl.GetId(), self.OnRightClick) # wxBug: This isn't working for some reason + + # drag-and-drop support + dt = ProjectFileDropTarget(self) + self._treeCtrl.SetDropTarget(dt) + + return True + + + def WriteProjectConfig(self): + frame = self.GetFrame() + config = wx.ConfigBase_Get() + if frame and not self._embeddedWindow: + if not frame.IsMaximized(): + config.WriteInt("ProjectFrameXLoc", frame.GetPositionTuple()[0]) + config.WriteInt("ProjectFrameYLoc", frame.GetPositionTuple()[1]) + config.WriteInt("ProjectFrameXSize", frame.GetSizeTuple()[0]) + config.WriteInt("ProjectFrameYSize", frame.GetSizeTuple()[1]) + config.WriteInt("ProjectFrameMaximized", frame.IsMaximized()) + + if config.ReadInt("ProjectSaveDocs", True): + projectFileNames = [] + projectExpanded = [] + if self._treeCtrl: + for projectItem in self._GetChildItems(self._treeCtrl.GetRootItem()): + project = self._GetItemProject(projectItem) + if not project.OnSaveModified(): + return + if project.GetDocumentSaved(): # Might be a new document and "No" selected to save it + projectFileNames.append(str(project.GetFilename())) + projectExpanded.append(self._treeCtrl.IsExpanded(projectItem)) + config.Write("ProjectSavedDocs", projectFileNames.__repr__()) + config.Write("ProjectExpandedSavedDocs", projectExpanded.__repr__()) + + + def OnClose(self, deleteWindow = True): + if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: + self.WriteProjectConfig() + project = self.GetDocument() + if not project: + return True + if not self.GetDocument().Close(): + return True + self.Activate(False) + if project: + projectItem = self._GetProjectItem(project) + if projectItem: + self._treeCtrl.Delete(projectItem) + # We don't need to delete the window since it is a floater/embedded + return True + + + def _GetParentFrame(self): + return wx.GetTopLevelParent(self.GetFrame()) + + + def OnUpdate(self, sender = None, hint = None): + wx.lib.docview.View.OnUpdate(self, sender, hint) + if hint: + if hint[0] == "add": + projectItem = self._GetProjectItem(hint[1]) + files = hint[2] + self._treeCtrl.UnselectAll() + self._treeCtrl.Expand(projectItem) + for file in files: + item = self._treeCtrl.AppendItem(projectItem, os.path.basename(file)) + self._treeCtrl.SetPyData(item, file) + self._treeCtrl.SelectItem(item) + self._treeCtrl.EnsureVisible(item) # wxBug: Doesn't work + self._treeCtrl.SortChildren(projectItem) + elif hint[0] == "remove": + projectItem = self._GetProjectItem(hint[1]) + files = hint[2] + children = self._GetChildItems(projectItem) + for child in children: + if self._GetItemFile(child) in files: + self._treeCtrl.Delete(child) + elif hint[0] == "select": + projectItem = self._GetProjectItem(hint[1]) + files = hint[2] + self._treeCtrl.UnselectAll() + children = self._GetChildItems(projectItem) + for child in children: + if self._GetItemFile(child) in files: + self._treeCtrl.SelectItem(child) + self._treeCtrl.EnsureVisible(child) # wxBug: Doesn't work + elif hint[0] == "rename": + projectItem = self._GetProjectItem(hint[1]) + self._treeCtrl.SetItemText(projectItem, os.path.basename(hint[2])) + + + def ProcessEvent(self, event): + id = event.GetId() + if id == ProjectService.ADD_FILES_TO_PROJECT_ID: + self.OnAddFileToProject(event) + return True + elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: + return False # Implement this one in the service + elif id == ProjectService.RENAME_ID: + self.OnRename(event) + return True + elif id == wx.ID_CUT: + self.OnCut(event) + return True + elif id == wx.ID_COPY: + self.OnCopy(event) + return True + elif id == wx.ID_PASTE: + self.OnPaste(event) + return True + elif id == wx.ID_CLEAR or id == ProjectService.REMOVE_FROM_PROJECT: + self.OnClear(event) + return True + elif id == wx.ID_SELECTALL: + self.OnSelectAll(event) + return True + elif id == ProjectService.OPEN_SELECTION_ID: + self.OnOpenSelection(event) + return True + elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: + self.OnProperties(event) + return True + else: + return False + + def ProcessUpdateUIEvent(self, event): + # Hack: The edit menu is not being set for projects that are preloaded at startup, so make sure it is OK here + if self._checkEditMenu: + doc = self.GetDocument() + if doc and not doc.GetCommandProcessor().GetEditMenu(): + doc.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame())) + self._checkEditMenu = False + id = event.GetId() + if id == ProjectService.ADD_FILES_TO_PROJECT_ID: + event.Enable(self._HasProjectsSelected() or self._HasFilesSelected()) + return True + elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: + event.Enable(False) + return True + elif id == ProjectService.RENAME_ID: + event.Enable(self._HasFilesSelected() or self._HasProjectsSelected()) + return True + elif id == wx.ID_CUT: + event.Enable(self._AreSelectedItemsFromSameProject()) + return True + elif id == wx.ID_COPY: + event.Enable(self._HasFilesSelected()) + return True + elif id == wx.ID_PASTE: + event.Enable(self.CanPaste()) + return True + elif id == wx.ID_CLEAR or id == ProjectService.REMOVE_FROM_PROJECT: + event.Enable(self._AreSelectedItemsFromSameProject()) + return True + elif id == wx.ID_SELECTALL: + event.Enable(self._HasFiles()) + return True + elif id == ProjectService.OPEN_SELECTION_ID: + event.Enable(self._HasFilesSelected()) + return True + elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: + event.Enable(self._HasProjectsSelected() or self._HasFilesSelected()) + return True + else: + return False + + #---------------------------------------------------------------------------- + # Display Methods + #---------------------------------------------------------------------------- + + def IsShown(self): + if not self.GetFrame(): + return False + return self.GetFrame().IsShown() + + + def Hide(self): + self.Show(False) + + + def Show(self, show = True): + self.GetFrame().Show(show) + if wx.GetApp().IsMDI(): + mdiParentFrame = wx.GetApp().GetTopWindow() + mdiParentFrame.ShowEmbeddedWindow(self.GetFrame(), show) + + + #---------------------------------------------------------------------------- + # Methods for ProjectDocument and ProjectService to call + #---------------------------------------------------------------------------- + + def SetExpandedProjects(self, expandedProjects): + self._treeCtrl.UnselectAll() + firstItem = None + for i, item in enumerate(self._GetChildItems(self._treeCtrl.GetRootItem())): + if i == 0: + firstItem = item + if expandedProjects[i]: + self._treeCtrl.Expand(item) + else: + self._treeCtrl.Collapse(item) + # wxBug: This causes a crash, tried using ScrollTo which crashed as well. Then tried calling it with wx.CallAfter and that crashed as well, with both EnsureVisible and ScrollTo + # self._treeCtrl.EnsureVisible(self._treeCtrl.GetRootItem()) + # So doing the following massive hack which forces the treectrl to scroll up to the top item + if firstItem: + if expandedProjects[i]: + self._treeCtrl.Collapse(firstItem) + self._treeCtrl.Expand(firstItem) + else: + self._treeCtrl.Expand(firstItem) + self._treeCtrl.Collapse(firstItem) + + def GetSelectedFile(self): + for item in self._treeCtrl.GetSelections(): + return self._GetItemFile(item) + + def AddProjectToView(self, document): + rootItem = self._treeCtrl.GetRootItem() + projectItem = self._treeCtrl.AppendItem(rootItem, self._MakeProjectName(document)) + self._treeCtrl.SetPyData(projectItem, document) + for file in document.GetFiles(): + fileItem = self._treeCtrl.AppendItem(projectItem, os.path.basename(file)) + self._treeCtrl.SetPyData(fileItem, file) + self._treeCtrl.SortChildren(rootItem) + self._treeCtrl.SortChildren(projectItem) + self._treeCtrl.UnselectAll() + self._treeCtrl.Expand(projectItem) + self._treeCtrl.SelectItem(projectItem) + if self._embeddedWindow: + document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame())) + + #---------------------------------------------------------------------------- + # Methods for OutlineService + #---------------------------------------------------------------------------- + def DoLoadOutlineCallback(self, force=False): + """ Project Editor is a special case for the Outline Service. + You need to be able to be active in the Project Manager without clearing + the Outline View. So we make the Project Editor a client of the Outline + Service, but we don't load anything in the Outline View, leaving the + contents of the Outline View alone (e.g. last document's outline view). + """ + pass + + #---------------------------------------------------------------------------- + # Control events + #---------------------------------------------------------------------------- + + def OnProperties(self, event): + items = self._treeCtrl.GetSelections() + if not items: + return + item = items[0] + if self._IsItemProject(item): + projectPropertiesDialog = ProjectPropertiesDialog(wx.GetApp().GetTopWindow(), self._GetItemProject(item).GetFilename()) + if projectPropertiesDialog.ShowModal() == wx.ID_OK: + pass # Handle OK + projectPropertiesDialog.Destroy() + elif self._IsItemFile(item): + filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService) + filePropertiesService.ShowPropertiesDialog(self._GetItemFile(item)) + + + def OnAddFileToProject(self, event): + if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__": + allfilter = '' + descr = '' + for temp in self.GetDocumentManager()._templates: + if temp.IsVisible(): + if len(descr) > 0: + descr = descr + _('|') + allfilter = allfilter + _(';') + 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 + allfilter = allfilter + temp.GetFileFilter() + descr = _("All") + _(" (") + allfilter + _(") |") + allfilter + _('|') + descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk + descr = descr + _("|") + _("Any (*.*) | *.*") + else: + descr = _("*.*") + if True or _WINDOWS: + dialog = wx.FileDialog(self.GetFrame(), _("Add Files"), self._lastDirectory, "", descr, wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE) + if dialog.ShowModal() != wx.ID_OK: + return + paths = dialog.GetPaths() + dialog.Destroy() + else: + paths = wx.FileSelector(_("Add Files"), self._lastDirectory, "", wildcard = descr, flags = wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE, parent=self.GetFrame()) + if type(paths) == types.StringType: + paths = [paths] + if len(paths): + self._lastDirectory = wx.lib.docview.PathOnly(paths[0]) + self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), paths)) + self.Activate(True) # after add, should put focus on project editor + + + def DoAddFilesToProject(self, filenames): + # method used by Drag-n-Drop to add files to current Project + self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filenames)) + + + def DoSelectFiles(self, filenames): + # method used by Drag-n-Drop to select files in current Project + for selection in self._treeCtrl.GetSelections(): + self._treeCtrl.SelectItem(selection, False) + for file in filenames: + item = self._GetFileItem(longFileName=file) + if item: + self._treeCtrl.SelectItem(item, True) + self._treeCtrl.EnsureVisible(item) + + + def DoSelectProject(self, x, y): + # method used by Drag-n-Drop to set current Project based on cursor position + item, flag = self._treeCtrl.HitTest((x,y)) + if not item: + return False + + project = self._GetItemProject(item) + if not project: + return False + + projectItem = self._GetProjectItem(project) + self._treeCtrl.UnselectAll() + self._treeCtrl.SelectItem(projectItem) + return True + + + def OnFocus(self, event): + wx.GetApp().GetDocumentManager().ActivateView(self) + event.Skip() + + + def OnKillFocus(self, event): + # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame + # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame + 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 + childFrame = wx.GetApp().GetTopWindow().GetActiveChild() + if childFrame: + childFrame.Activate() + event.Skip() + + + def OnRightClick(self, event): + self.Activate(True) + if not self._treeCtrl.GetSelections(): + return + if len(self._treeCtrl.GetSelections()) == 1 and self._IsItemRoot(self._treeCtrl.GetSelections()[0]): + return # Don't do a menu if it's just the root item selected + menu = wx.Menu() + if self._HasFilesSelected(): # Files context + menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection")) + menu.Enable(ProjectService.OPEN_SELECTION_ID, True) + wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection) + itemIDs = [None] + for item in self._treeCtrl.GetSelections(): + if self._IsItemProcessModelFile(item): + itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None] + break + else: # Project context + itemIDs = [wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS, None] + menuBar = self._GetParentFrame().GetMenuBar() + itemIDs = itemIDs + [wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID, None, ProjectService.ADD_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, None, ProjectService.RENAME_ID] + for itemID in itemIDs: + if not itemID: + menu.AppendSeparator() + else: + if itemID == ProjectService.RUN_SELECTED_PM_ID: + menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process")) + wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM) + elif itemID == ProjectService.REMOVE_FROM_PROJECT: + menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project")) + wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear) + wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent) + else: + item = menuBar.FindItemById(itemID) + if item: + menu.Append(itemID, item.GetLabel()) + self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY())) + menu.Destroy() + + def OnRunSelectedPM(self, event): + projectService = wx.GetApp().GetService(ProjectService) + projectService.OnRunProcessModel(event, runSelected=True) + + def OnRename(self, event): + if self._treeCtrl.GetSelections(): + self._treeCtrl.EditLabel(self._treeCtrl.GetSelections()[0]) + + + def OnBeginLabelEdit(self, event): + self._editingSoDontKillFocus = True + item = event.GetItem() + if not self._IsItemFile(item) and not self._IsItemProject(item): + event.Veto() + + + def OnEndLabelEdit(self, event): + self._editingSoDontKillFocus = False + item = event.GetItem() + newName = event.GetLabel() + if not newName or (not self._IsItemFile(item) and not self._IsItemProject(item)): + event.Veto() + return + if self._IsItemFile(item): + oldFile = self._GetItemFile(item) + newFile = os.path.join(os.path.split(oldFile)[0], newName) + if not self._GetItemProject(item).GetCommandProcessor().Submit(ProjectRenameFileCommand(self.GetDocument(), oldFile, newFile)): + event.Veto() + return + self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(self._treeCtrl.GetSelections()[0])) + elif self._IsItemProject(item): + oldFile = self._GetItemProject(item).GetFilename() + newFile = os.path.join(os.path.split(oldFile)[0], newName) + if not self._GetItemProject(item).GetCommandProcessor().Submit(ProjectRenameFileCommand(self.GetDocument(), oldFile, newFile, True)): + event.Veto() + return + self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem()) + + + def CanPaste(self): + # wxBug: Should be able to use IsSupported/IsSupportedFormat here + #fileDataObject = wx.FileDataObject() + #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject) + if not wx.TheClipboard.IsOpened(): + if wx.TheClipboard.Open(): + fileDataObject = wx.FileDataObject() + hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject) + wx.TheClipboard.Close() + else: + hasFilesInClipboard = False + return hasFilesInClipboard + + + def OnCut(self, event): + if self._AreSelectedItemsFromSameProject(): + self.OnCopy(event) + self.OnClear(event) + + + def OnCopy(self, event): + fileDataObject = wx.FileDataObject() + items = self._treeCtrl.GetSelections() + for item in items: + if self._IsItemFile(item): + file = self._treeCtrl.GetPyData(item) + fileDataObject.AddFile(file) + if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open(): + wx.TheClipboard.SetData(fileDataObject) + wx.TheClipboard.Close() + + + def OnPaste(self, event): + if wx.TheClipboard.Open(): + fileDataObject = wx.FileDataObject() + if wx.TheClipboard.GetData(fileDataObject): + self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames())) + wx.TheClipboard.Close() + + + def OnClear(self, event): + if self._AreSelectedItemsFromSameProject(): + items = self._treeCtrl.GetSelections() + files = [] + for item in items: + if self._IsItemFile(item): + files.append(self._GetItemFile(item)) + self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(self._GetItemProject(items[0]), files)) + + + def OnKeyPressed(self, event): + key = event.KeyCode() + if key == wx.WXK_DELETE: + self.OnClear(event) + else: + event.Skip() + + + def OnSelectAll(self, event): + project = self.GetDocument() + if project: + self._treeCtrl.UnselectAll() + for child in self._GetChildItems(self._GetProjectItem(project)): + self._treeCtrl.SelectItem(child) + + + def OnOpenSelectionSDI(self, event): + # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window + wx.CallAfter(self.OnOpenSelection, None) + + + def OnOpenSelection(self, event): + doc = None + try: + items = self._treeCtrl.GetSelections() + for item in items: + if self._IsItemFile(item): + filepath = self._GetItemFile(item) + if not os.path.exists(filepath): + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("File Not Found") + yesNoMsg = wx.MessageDialog(self.GetFrame(), + _("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)), + msgTitle, + wx.YES_NO + ) + if yesNoMsg.ShowModal() == wx.ID_NO: + continue + findFile = wx.FileDialog(self.GetFrame(), + _("Choose a file"), + wx.lib.docview.PathOnly(filepath), + wx.lib.docview.FileNameFromPath(filepath), + style = wx.OPEN + ) + if findFile.ShowModal() == wx.ID_OK and findFile.GetPath(): + newpath = findFile.GetPath() + else: + newpath = None + findFile.Destroy() + if newpath: + # update Project Model with new location + self.GetDocument().RemoveFile(filepath) + self.GetDocument().AddFile(newpath) + filepath = newpath + + doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT) + + except IOError, (code, message): + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("File Error") + wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath), + msgTitle, + wx.OK | wx.ICON_EXCLAMATION, + self.GetFrame()) + + + #---------------------------------------------------------------------------- + # Convenience methods + #---------------------------------------------------------------------------- + + def _HasFiles(self): + if not self._treeCtrl: + return False + return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files + + + def _HasProjectsSelected(self): + if not self._treeCtrl: + return False + items = self._treeCtrl.GetSelections() + if not items: + return False + for item in items: + if self._IsItemProject(item): + return True + return False + + + def _HasFilesSelected(self): + if not self._treeCtrl: + return False + items = self._treeCtrl.GetSelections() + if not items: + return False + for item in items: + if not self._IsItemFile(item): + return False + return True + + + def _MakeProjectName(self, project): + return project.GetPrintableName() + + + # Return the tree item for a project + def _GetProjectItem(self, project): + children = self._GetChildItems(self._treeCtrl.GetRootItem()) + for child in children: + if self._treeCtrl.GetPyData(child) == project: + return child + return None + + + # Returns the project for an item, either for a project item or a file that is part of a project + def _GetItemProject(self, item): + if self._IsItemRoot(item): + return None + if self._IsItemProject(item): + return self._treeCtrl.GetPyData(item) + if self._IsItemFile(item): + return self._treeCtrl.GetPyData(self._treeCtrl.GetItemParent(item)) + return None + + + def _GetItemFile(self, item): + if self._IsItemFile(item): + return self._treeCtrl.GetPyData(item) + else: + return None + + + def _GetFileItem(self, shortFileName = None, longFileName = None): + """ Returns the tree item for a file given the short (display) or long (fullpath) file name. """ + + if shortFileName: + project_children = self._GetChildItems(self._treeCtrl.GetRootItem()) + for child in project_children: + file_children = self._GetChildItems(child) + for file_child in file_children: + if self._treeCtrl.GetItemText(file_child) == shortFileName: + return file_child + return None + else: + project_children = self._GetChildItems(self._treeCtrl.GetRootItem()) + for child in project_children: + file_children = self._GetChildItems(child) + for file_child in file_children: + if self._treeCtrl.GetPyData(file_child) == longFileName: + return file_child + return None + + + def GetFilePathFromTreeName(self, shortFileName): + """ + Returns the data object given a short (display) file name for a file. The data + object should be the full path. + """ + return self._GetItemFile(self._GetFileItem(shortFileName)) + + + def SelectFileInTree(self, shortFileName): + item = self._GetFileItem(shortFileName) + if item: + for selection in self._treeCtrl.GetSelections(): + self._treeCtrl.SelectItem(selection, False) + self._treeCtrl.SelectItem(item, True) + self._treeCtrl.EnsureVisible(item) + + + def _IsItemRoot(self, item): + return item == self._treeCtrl.GetRootItem() + + + def _IsItemProject(self, item): + return isinstance(self._treeCtrl.GetPyData(item), ProjectDocument) + + + def _IsItemFile(self, item): + return isinstance(self._treeCtrl.GetPyData(item), types.StringTypes) + + + def _IsItemProcessModelFile(self, item): + if ACTIVEGRID_BASE_IDE: + return False + + if isinstance(self._treeCtrl.GetPyData(item), types.StringTypes): + filename = self._treeCtrl.GetPyData(item) + ext = None + for template in self.GetDocumentManager().GetTemplates(): + if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: + ext = template.GetDefaultExtension() + break; + if not ext: + return False + + if filename.endswith(ext): + return True + + return False + + + def _AreSelectedItemsFromSameProject(self): + if not self._treeCtrl: + return False + items = self._treeCtrl.GetSelections() + if not items: + return False + project = self._GetItemProject(items[0]) + if project == None: + return False + for item in items: + if not self._IsItemFile(item): + return False + if self._GetItemProject(item) != project: + return False + return True + + + def _GetChildItems(self, parentItem): + children = [] + (child, cookie) = self._treeCtrl.GetFirstChild(parentItem) + while child.IsOk(): + children.append(child) + (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie) + return children + + + +class ProjectFileDropTarget(wx.FileDropTarget): + + def __init__(self, view): + wx.FileDropTarget.__init__(self) + self._view = view + + + def OnDropFiles(self, x, y, filenames): + if self._view.DoSelectProject(x, y): + self._view.DoAddFilesToProject(filenames) + self._view.DoSelectFiles(filenames) + return True + return False + + + def OnDragOver(self, x, y, default): + if self._view.DoSelectProject(x,y): + return wx.DragCopy + return wx.DragNone + + +class ProjectPropertiesDialog(wx.Dialog): + + + def __init__(self, parent, filename): + wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330)) + + HALF_SPACE = 5 + SPACE = 10 + + filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService) + + notebook = wx.Notebook(self, -1) + tab = wx.Panel(notebook, -1) + + gridSizer = RowColSizer() + + gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")), flag=wx.RIGHT, border=HALF_SPACE, row=0, col=0) + if os.path.isfile(filename): + gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]), row=0, col=1) + + gridSizer.Add(wx.StaticText(tab, -1, _("Location:")), flag=wx.RIGHT, border=HALF_SPACE, row=1, col=0) + gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.split(filename)[0])), flag=wx.BOTTOM, border=SPACE, row=1, col=1) + + gridSizer.Add(wx.StaticText(tab, -1, _("Size:")), flag=wx.RIGHT, border=HALF_SPACE, row=2, col=0) + gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")), row=2, col=1) + + lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion + lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND) + gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=HALF_SPACE, row=3, col=0, colspan=2) + + gridSizer.Add(wx.StaticText(tab, -1, _("Created:")), flag=wx.RIGHT, border=HALF_SPACE, row=4, col=0) + gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))), row=4, col=1) + + gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")), flag=wx.RIGHT, border=HALF_SPACE, row=5, col=0) + gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))), row=5, col=1) + + gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")), flag=wx.RIGHT, border=HALF_SPACE, row=6, col=0) + gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))), row=6, col=1) + + else: + gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")), row=0, col=1) + + # add a border around the inside of the tab + spacerGrid = wx.BoxSizer(wx.VERTICAL) + spacerGrid.Add(gridSizer, 0, wx.ALL, SPACE); + tab.SetSizer(spacerGrid) + notebook.AddPage(tab, _("General")) + if wx.Platform == "__WXMSW__": + notebook.SetPageSize((310,200)) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE) + sizer.Add(self.CreateButtonSizer(wx.OK), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE) + + sizer.Fit(self) + self.SetDimensions(-1, -1, 310, -1, wx.SIZE_USE_EXISTING) + self.SetSizer(sizer) + self.Layout() + + +class ProjectOptionsPanel(wx.Panel): + + + def __init__(self, parent, id): + wx.Panel.__init__(self, parent, id) + self._useSashMessageShown = False + SPACE = 10 + HALF_SPACE = 5 + config = wx.ConfigBase_Get() + self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects")) + self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True)) + projectBorderSizer = wx.BoxSizer(wx.VERTICAL) + projectSizer = wx.BoxSizer(wx.VERTICAL) + projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE) + self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog")) + self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True)) + projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE) + projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE) + self.SetSizer(projectBorderSizer) + self.Layout() + parent.AddPage(self, _("Project")) + + def OnUseSashSelect(self, event): + if not self._useSashMessageShown: + msgTitle = wx.GetApp().GetAppName() + if not msgTitle: + msgTitle = _("Document Options") + wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.", + msgTitle, + wx.OK | wx.ICON_INFORMATION, + self.GetParent()) + self._useSashMessageShown = True + + + def OnOK(self, optionsDialog): + config = wx.ConfigBase_Get() + config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue()) + config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue()) + + +class ProjectService(Service.Service): + + #---------------------------------------------------------------------------- + # Constants + #---------------------------------------------------------------------------- + SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service + RUNPM_ID = wx.NewId() + RUN_SELECTED_PM_ID = wx.NewId() + RUN_CURRENT_PM_ID = wx.NewId() + ADD_FILES_TO_PROJECT_ID = wx.NewId() + ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId() + RENAME_ID = wx.NewId() + OPEN_SELECTION_ID = wx.NewId() + REMOVE_FROM_PROJECT = wx.NewId() + + + #---------------------------------------------------------------------------- + # Overridden methods + #---------------------------------------------------------------------------- + + def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT): + Service.Service.__init__(self, serviceName, embeddedWindowLocation) + self._runHandlers = [] + self._suppressOpenProjectMessages = False + + + def _CreateView(self): + return ProjectView(self) + + + def ShowWindow(self, show = True): + """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """ + Service.Service.ShowWindow(self, show) + + if show: + project = self.GetView().GetDocument() + if not project: + self.OpenSavedProjects() + + + #---------------------------------------------------------------------------- + # Service specific methods + #---------------------------------------------------------------------------- + + def GetSuppressOpenProjectMessages(self): + return self._suppressOpenProjectMessages + + + def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages): + self._suppressOpenProjectMessages = suppressOpenProjectMessages + + + def GetRunHandlers(self): + return self._runHandlers + + + def AddRunHandler(self, runHandler): + self._runHandlers.append(runHandler) + + + def RemoveRunHandler(self, runHandler): + self._runHandlers.remove(runHandler) + + + def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): + Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document) + + config = wx.ConfigBase_Get() + + projectMenu = wx.Menu() + +## accelTable = wx.AcceleratorTable([ +## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID")) +## ]) +## frame.SetAcceleratorTable(accelTable) + isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument + if wx.GetApp().IsMDI() or isProjectDocument: + if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID): + projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("&Add Files to Project..."), _("Adds a document to the current project")) + wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent) + if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID): + projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project")) + wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent) + viewMenuIndex = menuBar.FindMenu(_("&View")) + menuBar.Insert(viewMenuIndex + 1, projectMenu, _("&Project")) + editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit"))) + if not menuBar.FindItemById(ProjectService.RENAME_ID): + editMenu.AppendSeparator() + editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item")) + wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent) + wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent) + + return True + + + def OnCloseFrame(self, event): + if not self.GetView(): + return True + + if wx.GetApp().IsMDI(): + # close all non-project documents first + for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted + if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument: + if not self.GetDocumentManager().CloseDocument(document, False): + return False + + # write project config afterwards because user may change filenames on closing of new documents + self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere + + # close all project documents after closing other documents + # because user may save a new document with a new name or cancel closing a document + for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + if not document.OnSaveModified(): + return False + + # This is called when any SDI frame is closed, so need to check if message window is closing or some other window + elif self.GetView() == event.GetEventObject().GetView(): + self.SetView(None) + return True + + + #---------------------------------------------------------------------------- + # Event Processing Methods + #---------------------------------------------------------------------------- + + def ProcessEventBeforeWindows(self, event): + id = event.GetId() + if id == wx.ID_CLOSE_ALL: + self.OnFileCloseAll(event) + return True + return False + + + def ProcessEvent(self, event): + if Service.Service.ProcessEvent(self, event): + return True + + id = event.GetId() + if id == ProjectService.RUN_SELECTED_PM_ID: + self.OnRunProcessModel(event, runSelected=True) + return True + elif id == ProjectService.RUN_CURRENT_PM_ID: + self.OnRunProcessModel(event, runCurrentFile=True) + return True + elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: + self.OnAddCurrentFileToProject(event) + return True + elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: + if self.GetView(): + return self.GetView().ProcessEvent(event) + else: + return False + else: + return False + + + def ProcessUpdateUIEvent(self, event): + if Service.Service.ProcessUpdateUIEvent(self, event): + return True + + id = event.GetId() + if id == ProjectService.RUNPM_ID or id == ProjectService.RUN_SELECTED_PM_ID or id == ProjectService.RUN_CURRENT_PM_ID: + event.Enable(self._HasOpenedProjects() and self._HasProcessModel()) + return True + elif id == ProjectService.ADD_FILES_TO_PROJECT_ID: + event.Enable(False) + return True + elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: + event.Enable(self._CanAddCurrentFileToProject()) + return True + elif id == ProjectService.RENAME_ID: + event.Enable(False) + return True + elif id == ProjectService.OPEN_SELECTION_ID: + event.Enable(False) + return True + elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: + if self.GetView(): + return self.GetView().ProcessUpdateUIEvent(event) + else: + return False + else: + return False + + + def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False): + project = self.GetView().GetDocument() + + if project: + ext = None + for template in self.GetDocumentManager().GetTemplates(): + if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: + ext = template.GetDefaultExtension() + break; + if not ext: + return + + files = filter(lambda f: f.endswith(ext), project.GetFiles()) + if not files: + return + + docs = wx.GetApp().GetDocumentManager().GetDocuments() + for doc in docs: + if doc.GetFilename() in files and doc.GetDocumentTemplate().GetDocumentType() == ProcessModelEditor.ProcessModelDocument: + if not doc.GetProcessModel().beginProcess: + wx.MessageBox(_("Cannot run process. No begin action found."), _("Run Process")) + return + + filesModified = False + for doc in docs: + if doc.IsModified(): + filesModified = True + break + if filesModified: + frame = self.GetView().GetFrame() + yesNoMsg = wx.MessageDialog(frame, + _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"), + _("Run Process"), + wx.YES_NO + ) + if yesNoMsg.ShowModal() == wx.ID_YES: + wx.GetTopLevelParent(frame).OnFileSaveAll(None) + + if runCurrentFile: + fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename() + elif runSelected: + fileToRun = self.GetView().GetSelectedFile() + elif len(files) > 1: + files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower())) + strings = map(lambda file: os.path.basename(file), files) + res = wx.GetSingleChoiceIndex(_("Select a process to run:"), + _("Run"), + strings, + project.GetFirstView()._GetParentFrame()) + if res == -1: + return + fileToRun = files[res] + else: + fileToRun = files[0] + + self.RunProcessModel(fileToRun) + + + def RunProcessModel(self, fileToRun): + for runHandler in self.GetRunHandlers(): + if runHandler.RunProjectFile(fileToRun): + return + os.system('"' + fileToRun + '"') + + + def _HasProcessModel(self): + project = self.GetView().GetDocument() + + if project: + ext = None + for template in self.GetDocumentManager().GetTemplates(): + if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: + ext = template.GetDefaultExtension() + break; + if not ext: + return False + + files = filter(lambda f: f.endswith(ext), project.GetFiles()) + if not files: + return False + + if len(files): + return True + + return False + + + def _HasOpenedProjects(self): + for document in self.GetDocumentManager().GetDocuments(): + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + return True + return False + + + def _HasCurrentFile(self): + currentDoc = self.GetDocumentManager().GetCurrentDocument() + return currentDoc + + + def _CanAddCurrentFileToProject(self): + currentDoc = self.GetDocumentManager().GetCurrentDocument() + if not currentDoc: + return False + if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + return False + if not currentDoc._savedYet: + return False + for document in self.GetDocumentManager().GetDocuments(): + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + return True + return False # There are no documents open + + + def GetFilesFromCurrentProject(self): + view = self.GetView() + if view: + project = view.GetDocument() + if project: + return project.GetFiles() + return None + + + def GetCurrentProject(self): + view = self.GetView() + if view: + return view.GetDocument() + return None + + + def FindProjectByFile(self, filename): + for document in self.GetDocumentManager().GetDocuments(): + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + if document.GetFilename() == filename: + return document + elif document.IsFileInProject(filename): + return document + return None + + + def GetCurrentProjectNames(self): + projects = [] + for document in self.GetDocumentManager().GetDocuments(): + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + projects.append(document) + if not projects: + return + projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower())) + strings = map(lambda project: project.GetPrintableName(), projects) + return strings + + def OnAddCurrentFileToProject(self, event): + if not self._CanAddCurrentFileToProject(): + return + projects = [] + for document in self.GetDocumentManager().GetDocuments(): + if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: + projects.append(document) + if not projects: + return + projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower())) + strings = map(lambda project: project.GetPrintableName(), projects) + res = wx.GetSingleChoiceIndex(_("Select a project to add the file to:"), + _("Add to Project"), + strings, + self.GetDocumentManager().FindSuitableParent()) + if res == -1: + return + file = self.GetDocumentManager().GetCurrentDocument().GetFilename() + projects[res].GetCommandProcessor().Submit(ProjectAddFilesCommand(projects[res], [file])) + self.GetView().Activate(True) # after add, should put focus on project editor + + + def OnFileCloseAll(self, event): + for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted + if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument: + if not self.GetDocumentManager().CloseDocument(document, False): + return + # document.DeleteAllViews() # Implicitly delete the document when the last view is removed + + + def OpenSavedProjects(self): + config = wx.ConfigBase_Get() + openedDocs = False + if config.ReadInt("ProjectSaveDocs", True): + docString = config.Read("ProjectSavedDocs") + if docString: + doc = None + for fileName in eval(docString): + if isinstance(fileName, types.StringTypes): + if os.path.exists(fileName): + doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT) + + if doc: + openedDocs = True + expandedString = config.Read("ProjectExpandedSavedDocs") + if expandedString: + view = doc.GetFirstView() + view.SetExpandedProjects(eval(expandedString)) + return openedDocs + +#---------------------------------------------------------------------------- +# Icon Bitmaps - generated by encode_bitmaps.py +#---------------------------------------------------------------------------- +from wx import ImageFromStream, BitmapFromImage +from wx import EmptyIcon +import cStringIO + + +def getProjectData(): + return \ +'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ +\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ +\x00\x00[IDAT8\x8d\xc5\x93\xc1\n\xc00\x08C\x8d\xf6\xff\xffX\xb3Sa-\xf6`;:O\n\ +\x12\x1fj\x0059\t\xed\t\xc3\xc9pn\x0b\x88\x88@\rU\x81\xf6.\x18N\xa8aE\x92\rh\ +YC\x85\xa4D\x90\x91\xdc%\xf8w\x07+\xd1\xfbW\x98\xc5\x8f\t\x86W\xee\x93+\xbe\ +\xc0gn\xdc\x8d\x07\xab"\xf9(3zH\x1e\xfb\x00\x00\x00\x00IEND\xaeB`\x82" + + +def getBlankBitmap(): + return BitmapFromImage(getBlankImage()) + +def getBlankImage(): + stream = cStringIO.StringIO(getBlankData()) + return ImageFromStream(stream) + +def getBlankIcon(): + icon = EmptyIcon() + icon.CopyFromBitmap(getBlankBitmap()) + return icon +