]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/ProjectEditor.py
implement {Create,Clone}RefData for gtk wxBitmap
[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: Morgan Hua, Peter Yared
6 #
7 # Created: 8/15/03
8 # CVS-ID: $Id$
9 # Copyright: (c) 2003-2006 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13 import wx
14 import wx.lib.docview
15 import wx.lib.pydocview
16 import wx.lib.buttons
17 import Service
18 import copy
19 import os
20 import os.path
21 import sets
22 import sys
23 import time
24 import types
25 import activegrid.util.appdirs as appdirs
26 import activegrid.util.fileutils as fileutils
27 import activegrid.util.aglogging as aglogging
28 import UICommon
29 import Wizard
30 import SVNService
31 import project as projectlib
32 import ExtensionService
33
34 from IDE import ACTIVEGRID_BASE_IDE
35 if not ACTIVEGRID_BASE_IDE:
36 import activegrid.server.deployment as deploymentlib
37 import ProcessModelEditor
38 import DataModelEditor
39 import DeploymentGeneration
40 import WsdlAgEditor
41 import WsdlAgModel
42 APP_LAST_LANGUAGE = "LastLanguage"
43 import activegrid.model.basedocmgr as basedocmgr
44 import activegrid.model.basemodel as basemodel
45 import activegrid.model.projectmodel as projectmodel
46 import PropertyService
47 from activegrid.server.toolsupport import GetTemplate
48 import activegrid.util.xmlutils as xmlutils
49 import activegrid.util.sysutils as sysutils
50 DataServiceExistenceException = DeploymentGeneration.DataServiceExistenceException
51 import WebBrowserService
52
53 from SVNService import SVN_INSTALLED
54
55 _ = wx.GetTranslation
56
57 if wx.Platform == '__WXMSW__':
58 _WINDOWS = True
59 else:
60 _WINDOWS = False
61
62 #----------------------------------------------------------------------------
63 # Constants
64 #----------------------------------------------------------------------------
65 SPACE = 10
66 HALF_SPACE = 5
67 PROJECT_EXTENSION = ".agp"
68
69 if not ACTIVEGRID_BASE_IDE:
70 PRE_17_TMP_DPL_NAME = "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION
71 _17_TMP_DPL_NAME = ".tmp" + deploymentlib.DEPLOYMENT_EXTENSION
72
73 # wxBug: the wxTextCtrl and wxChoice controls on Mac do not correctly size
74 # themselves with sizers, so we need to add a right border to the sizer to
75 # get the control to shrink itself to fit in the sizer.
76 MAC_RIGHT_BORDER = 0
77 if wx.Platform == "__WXMAC__":
78 MAC_RIGHT_BORDER = 5
79
80
81 PROJECT_KEY = "/AG_Projects"
82 PROJECT_DIRECTORY_KEY = "NewProjectDirectory"
83
84 NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.getSystemDir()
85
86 #----------------------------------------------------------------------------
87 # Methods
88 #----------------------------------------------------------------------------
89
90 def AddProjectMapping(doc, projectDoc=None, hint=None):
91 projectService = wx.GetApp().GetService(ProjectService)
92 if projectService:
93 if not projectDoc:
94 if not hint:
95 hint = doc.GetFilename()
96 projectDocs = projectService.FindProjectByFile(hint)
97 if projectDocs:
98 projectDoc = projectDocs[0]
99
100 projectService.AddProjectMapping(doc, projectDoc)
101 if hasattr(doc, "GetModel"):
102 projectService.AddProjectMapping(doc.GetModel(), projectDoc)
103
104
105 def getProjectKeyName(projectName, mode=None):
106 if mode:
107 return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode)
108 else:
109 return "%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'))
110
111
112 def GetDocCallback(filepath):
113 """ Get the Document used by the IDE and the in-memory document model used by runtime engine """
114 docMgr = wx.GetApp().GetDocumentManager()
115
116 try:
117 doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
118 if doc:
119 AddProjectMapping(doc)
120 else: # already open
121 for d in docMgr.GetDocuments():
122 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
123 doc = d
124 break
125 except Exception,e:
126 doc = None
127 aglogging.reportException(e, stacktrace=True)
128
129 if doc and doc.GetDocumentTemplate().GetDocumentType() == WsdlAgEditor.WsdlAgDocument:
130 # get referenced wsdl doc instead
131 if doc.GetModel().filePath:
132 if os.path.isabs(doc.GetModel().filePath): # if absolute path, leave it alone
133 filepath = doc.GetModel().filePath
134 else:
135 filepath = doc.GetAppDocMgr().fullPath(doc.GetModel().filePath) # check relative to project homeDir
136
137 if not os.path.isfile(filepath):
138 filepath = os.path.normpath(os.path.join(os.path.dirname(doc.GetFilename()), doc.GetModel().filePath)) # check relative to wsdlag file
139
140 if not os.path.isfile(filepath):
141 filename = os.sep + os.path.basename(doc.GetModel().filePath) # check to see if in project file
142 filePaths = findDocumentMgr(doc).filePaths
143 for fp in filePaths:
144 if fp.endswith(filename):
145 filepath = fp
146 break
147
148 try:
149 doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
150 except Exception,e:
151 doc = None
152 aglogging.reportException(e, stacktrace=True)
153
154 if doc:
155 AddProjectMapping(doc)
156 else: # already open
157 for d in docMgr.GetDocuments():
158 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
159 doc = d
160 break
161 else:
162 doc = None
163
164 if doc:
165 docModel = doc.GetModel()
166 else:
167 docModel = None
168
169 return doc, docModel
170
171
172 def findDocumentMgr(root):
173 projectService = wx.GetApp().GetService(ProjectService)
174 if projectService:
175 projectDoc = projectService.FindProjectFromMapping(root)
176 if projectDoc:
177 return projectDoc.GetModel()
178
179 projectDoc = projectService.GetCurrentProject()
180 if not projectDoc:
181 return None
182
183 if isinstance(root, wx.lib.docview.Document):
184 filepath = root.GetFilename()
185 elif hasattr(root, "fileName") and root.fileName:
186 filepath = root.fileName
187 else:
188 filepath = None
189
190 if filepath:
191 if projectDoc.IsFileInProject(filepath):
192 return projectDoc.GetModel()
193
194 projects = []
195 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
196 for openDoc in openDocs:
197 if openDoc == projectDoc:
198 continue
199 if(isinstance(openDoc, ProjectDocument)):
200 if openDoc.IsFileInProject(filepath):
201 projects.append(openDoc)
202
203 if projects:
204 if len(projects) == 1:
205 return projects[0].GetModel()
206 else:
207 choices = [os.path.basename(project.GetFilename()) for project in projects]
208 dlg = wx.SingleChoiceDialog(wx.GetApp().GetTopWindow(), _("'%s' found in more than one project.\nWhich project should be used for this operation?") % os.path.basename(filepath), _("Select Project"), choices, wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
209 dlg.CenterOnParent()
210 projectDoc = None
211 if dlg.ShowModal() == wx.ID_OK:
212 i = dlg.GetSelection()
213 projectDoc = projects[i]
214 dlg.Destroy()
215 return projectDoc.GetModel()
216 return projectDoc.GetModel()
217
218 return None
219
220
221 if not ACTIVEGRID_BASE_IDE:
222 basemodel.findGlobalDocumentMgr = findDocumentMgr
223
224
225 #----------------------------------------------------------------------------
226 # Classes
227 #----------------------------------------------------------------------------
228
229 if not ACTIVEGRID_BASE_IDE:
230 class IDEResourceFactory(DeploymentGeneration.DeploymentResourceFactory):
231
232 def __init__(self, openDocs, dataSourceService, projectDir,
233 preview=False, deployFilepath=None):
234
235 self.openDocs = openDocs
236 self.dataSourceService = dataSourceService
237 self.projectDir = projectDir
238 self.preview = preview
239 self.deployFilepath = deployFilepath
240
241 self.defaultFlagsNoView = (
242 wx.GetApp().GetDocumentManager().GetFlags()|
243 wx.lib.docview.DOC_SILENT|
244 wx.lib.docview.DOC_OPEN_ONCE|
245 wx.lib.docview.DOC_NO_VIEW)
246
247 def getModel(self, projectFile):
248 doc = wx.GetApp().GetDocumentManager().CreateDocument(
249 projectFile.filePath, flags=self.defaultFlagsNoView)
250 if (doc == None): # already open
251 doc = self._findOpenDoc(projectFile.filePath)
252 else:
253 AddProjectMapping(doc)
254 if (doc != None):
255 return doc.GetModel()
256
257 def getDataSource(self, dataSourceName):
258 # in preview mode, runtime needs the generated Deployment
259 # to contain the requried data source. But runtime doesn't
260 # actually need to communicate to db. So here is the logic to
261 # make preview works if the required data soruce has not
262 # yet been defined.
263 dataSource = self.dataSourceService.getDataSource(dataSourceName)
264 if (dataSource != None):
265 return dataSource
266 elif not self.preview:
267 raise DataServiceExistenceException(dataSourceName)
268 else:
269 # first to see if an existing dpl file is there, if so,
270 # use the data source in dpl file
271 if (self.deployFilepath != None):
272 tempDply = None
273 try:
274 tempDply = xmlutils.load(deployFilepath)
275 except:
276 pass
277 if (tempDply != None):
278 for tempDataSource in tempDply.dataSources:
279 if (tempDataSource.name == dataSourceName):
280 return tempDataSource
281
282 # if unable to use dpl file, then create a dummy data source
283 import activegrid.data.dataservice as dataservice
284 return dataservice.DataSource(
285 name=dataSourceName, dbtype=dataservice.DB_TYPE_SQLITE)
286
287 def initDocumentRef(self, projectFile, documentRef, dpl):
288 doc = self._findOpenDoc(projectFile.filePath)
289 if (doc and hasattr(doc, 'GetModel')):
290 documentRef.document = doc.GetModel()
291 if isinstance(documentRef, deploymentlib.XFormRef):
292 doc.GetModel().linkDeployment(dpl, dpl.loader)
293
294 def _findOpenDoc(self, filePath):
295 for openDoc in self.openDocs:
296 if openDoc.GetFilename() == filePath:
297 return openDoc
298 return None
299
300 def getProjectDir(self):
301 return self.projectDir
302
303
304 class ProjectDocument(wx.lib.docview.Document):
305
306 def __init__(self, model=None):
307 wx.lib.docview.Document.__init__(self)
308 if model:
309 self.SetModel(model)
310 else:
311 self.SetModel(projectlib.Project()) # initial model used by "File | New... | Project"
312 self.GetModel().SetDocCallback(GetDocCallback)
313
314 self._stageProjectFile = False
315
316
317 def __copy__(self):
318 model = copy.copy(self.GetModel())
319 clone = ProjectDocument(model)
320 clone.SetFilename(self.GetFilename())
321 return clone
322
323
324 def GetFirstView(self):
325 """ Bug: workaround. If user tries to open an already open project with main menu "File | Open...", docview.DocManager.OnFileOpen() silently returns None if project is already open.
326 And to the user, it appears as if nothing has happened. The user expects to see the open project.
327 This forces the project view to show the correct project.
328 """
329 view = wx.lib.docview.Document.GetFirstView(self)
330 view.SetProject(self.GetFilename()) # ensure project is displayed in view
331 return view
332
333
334 def GetModel(self):
335 return self._projectModel
336
337
338 def SetModel(self, model):
339 self._projectModel = model
340
341
342 def OnCreate(self, path, flags):
343 projectService = wx.GetApp().GetService(ProjectService)
344 view = projectService.GetView()
345 if view: # view already exists, reuse
346 # All project documents share the same view.
347 self.AddView(view)
348
349 if view.GetDocument():
350 # All project documents need to share the same command processor,
351 # to enable redo/undo of cross project document commands
352 cmdProcessor = view.GetDocument().GetCommandProcessor()
353 if cmdProcessor:
354 self.SetCommandProcessor(cmdProcessor)
355 else: # generate view
356 view = self.GetDocumentTemplate().CreateView(self, flags)
357 projectService.SetView(view)
358
359 return view
360
361
362 def LoadObject(self, fileObject):
363 self.SetModel(projectlib.load(fileObject))
364 self.GetModel().SetDocCallback(GetDocCallback)
365 return True
366
367
368 def SaveObject(self, fileObject):
369 projectlib.save(fileObject, self.GetModel())
370 return True
371
372
373 def OnOpenDocument(self, filePath):
374 projectService = wx.GetApp().GetService(ProjectService)
375 view = projectService.GetView()
376
377 if not os.path.exists(filePath):
378 wx.GetApp().CloseSplash()
379 msgTitle = wx.GetApp().GetAppName()
380 if not msgTitle:
381 msgTitle = _("File Error")
382 wx.MessageBox(_("Could not find '%s'.") % filePath,
383 msgTitle,
384 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
385 wx.GetApp().GetTopWindow())
386 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
387
388 fileObject = file(filePath, 'r')
389 try:
390 self.LoadObject(fileObject)
391 except:
392 wx.GetApp().CloseSplash()
393 msgTitle = wx.GetApp().GetAppName()
394 if not msgTitle:
395 msgTitle = _("File Error")
396 wx.MessageBox(_("Could not open '%s'. %s") % (wx.lib.docview.FileNameFromPath(filePath), sys.exc_value),
397 msgTitle,
398 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
399 wx.GetApp().GetTopWindow())
400 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
401
402 self.Modify(False)
403 self.SetFilename(filePath, True)
404 view.AddProjectToView(self)
405 self.SetDocumentModificationDate()
406 self.UpdateAllViews()
407 self._savedYet = True
408 view.Activate()
409 return True
410
411
412 def AddFile(self, filePath, folderPath=None, type=None, name=None):
413 if type:
414 types = [type]
415 else:
416 types = None
417 if name:
418 names = [name]
419 else:
420 names = None
421
422 return self.AddFiles([filePath], folderPath, types, names)
423
424
425 def AddFiles(self, filePaths=None, folderPath=None, types=None, names=None, files=None):
426 # Filter out files that are not already in the project
427 if filePaths:
428 newFilePaths = []
429 oldFilePaths = []
430 for filePath in filePaths:
431 if self.GetModel().FindFile(filePath):
432 oldFilePaths.append(filePath)
433 else:
434 newFilePaths.append(filePath)
435
436 projectService = wx.GetApp().GetService(ProjectService)
437 for i, filePath in enumerate(newFilePaths):
438 if types:
439 type = types[i]
440 else:
441 type = None
442
443 if names:
444 name = names[i]
445 else:
446 name = projectService.FindNameDefault(filePath)
447
448 if not folderPath:
449 folder = projectService.FindLogicalViewFolderDefault(filePath)
450 else:
451 folder = folderPath
452
453 self.GetModel().AddFile(filePath, folder, type, name)
454 elif files:
455 newFilePaths = []
456 oldFilePaths = []
457 for file in files:
458 if self.GetModel().FindFile(file.filePath):
459 oldFilePaths.append(file.filePath)
460 else:
461 newFilePaths.append(file.filePath)
462 self.GetModel().AddFile(file=file)
463 else:
464 return False
465
466 self.AddNameSpaces(newFilePaths)
467
468 self.UpdateAllViews(hint = ("add", self, newFilePaths, oldFilePaths))
469 if len(newFilePaths):
470 self.Modify(True)
471 return True
472 else:
473 return False
474
475
476 def RemoveFile(self, filePath):
477 return self.RemoveFiles([filePath])
478
479
480 def RemoveFiles(self, filePaths=None, files=None):
481 removedFiles = []
482
483 if files:
484 filePaths = []
485 for file in files:
486 filePaths.append(file.filePath)
487
488 for filePath in filePaths:
489 file = self.GetModel().FindFile(filePath)
490 if file:
491 self.GetModel().RemoveFile(file)
492 removedFiles.append(file.filePath)
493
494 self.UpdateAllViews(hint = ("remove", self, removedFiles))
495 if len(removedFiles):
496 self.Modify(True)
497 return True
498 else:
499 return False
500
501
502 def RenameFile(self, oldFilePath, newFilePath, isProject = False):
503 try:
504 if oldFilePath == newFilePath:
505 return False
506
507 # projects don't have to exist yet, so not required to rename old file,
508 # but files must exist, so we'll try to rename and allow exceptions to occur if can't.
509 if not isProject or (isProject and os.path.exists(oldFilePath)):
510 os.rename(oldFilePath, newFilePath)
511
512 if isProject:
513 documents = self.GetDocumentManager().GetDocuments()
514 for document in documents:
515 if os.path.normcase(document.GetFilename()) == os.path.normcase(oldFilePath): # If the renamed document is open, update it
516 document.SetFilename(newFilePath)
517 document.SetTitle(wx.lib.docview.FileNameFromPath(newFilePath))
518 document.UpdateAllViews(hint = ("rename", self, oldFilePath, newFilePath))
519 else:
520 self.UpdateFilePath(oldFilePath, newFilePath)
521 documents = self.GetDocumentManager().GetDocuments()
522 for document in documents:
523 if os.path.normcase(document.GetFilename()) == os.path.normcase(oldFilePath): # If the renamed document is open, update it
524 document.SetFilename(newFilePath, notifyViews = True)
525 document.UpdateAllViews(hint = ("rename", self, oldFilePath, newFilePath))
526 return True
527 except OSError, (code, message):
528 msgTitle = wx.GetApp().GetAppName()
529 if not msgTitle:
530 msgTitle = _("File Error")
531 wx.MessageBox("Could not rename '%s'. '%s'" % (wx.lib.docview.FileNameFromPath(oldFilePath), message),
532 msgTitle,
533 wx.OK | wx.ICON_EXCLAMATION,
534 wx.GetApp().GetTopWindow())
535 return False
536
537
538 def MoveFile(self, file, newFolderPath):
539 return self.MoveFiles([file], newFolderPath)
540
541
542 def MoveFiles(self, files, newFolderPath):
543 filePaths = []
544 isArray = isinstance(newFolderPath, type([]))
545 for i in range(len(files)):
546 if isArray:
547 files[i].logicalFolder = newFolderPath[i]
548 else:
549 files[i].logicalFolder = newFolderPath
550 filePaths.append(files[i].filePath)
551
552 self.UpdateAllViews(hint = ("remove", self, filePaths))
553 self.UpdateAllViews(hint = ("add", self, filePaths, []))
554 self.Modify(True)
555 return True
556
557
558 def UpdateFilePath(self, oldFilePath, newFilePath):
559 file = self.GetModel().FindFile(oldFilePath)
560 self.RemoveFile(oldFilePath)
561 if file:
562 self.AddFile(newFilePath, file.logicalFolder, file.type, file.name)
563 else:
564 self.AddFile(newFilePath)
565
566
567 def RemoveInvalidPaths(self):
568 """Makes sure all paths project knows about are valid and point to existing files. Removes and returns list of invalid paths."""
569
570 invalidFileRefs = []
571
572 fileRefs = self.GetFileRefs()
573
574 for fileRef in fileRefs:
575 if not os.path.exists(fileRef.filePath):
576 invalidFileRefs.append(fileRef)
577
578 for fileRef in invalidFileRefs:
579 fileRefs.remove(fileRef)
580
581 return [fileRef.filePath for fileRef in invalidFileRefs]
582
583
584 def SetStageProjectFile(self):
585 self._stageProjectFile = True
586
587
588 def ArchiveProject(self, zipdest, stagedir):
589 """Zips stagedir, creates a zipfile that has as name the projectname, in zipdest. Returns path to zipfile."""
590 if os.path.exists(zipdest):
591 raise AssertionError("Cannot archive project, %s already exists" % zipdest)
592 fileutils.zip(zipdest, stagedir)
593
594 return zipdest
595
596
597 def StageProject(self, tmpdir, targetDataSourceMapping={}):
598 """ Copies all files this project knows about into staging location. Files that live outside of the project dir are copied into the root of the stage dir, and their recorded file path is updated. Files that live inside of the project dir keep their relative path. Generates .dpl file into staging dir. Returns path to staging dir."""
599
600 projname = self.GetProjectName()
601 stagedir = os.path.join(tmpdir, projname)
602 fileutils.remove(stagedir)
603 os.makedirs(stagedir)
604
605 # remove invalid files from project
606 self.RemoveInvalidPaths()
607
608 # required so relative paths are written correctly when .dpl file is
609 # generated below.
610 self.SetFilename(os.path.join(stagedir,
611 os.path.basename(self.GetFilename())))
612 projectdir = self.GetModel().homeDir
613
614 # Validate paths before actually copying, and populate a dict
615 # with src->dest so copying is easy.
616 # (fileDict: ProjectFile instance -> dest path (string))
617 fileDict = self._ValidateFilePaths(projectdir, stagedir)
618
619 # copy files to staging dir
620 self._StageFiles(fileDict)
621
622 # set target data source for schemas
623 self._SetSchemaTargetDataSource(fileDict, targetDataSourceMapping)
624
625 # it is unfortunate we require this. it would be nice if filepaths
626 # were only in the project
627 self._FixWsdlAgFiles(stagedir)
628
629 # generate .dpl file
630 dplfilename = projname + deploymentlib.DEPLOYMENT_EXTENSION
631 dplfilepath = os.path.join(stagedir, dplfilename)
632 self.GenerateDeployment(dplfilepath)
633
634 if self._stageProjectFile:
635 # save project so we get the .agp file. not required for deployment
636 # but convenient if user wants to open the deployment in the IDE
637 agpfilename = projname + PROJECT_EXTENSION
638 agpfilepath = os.path.join(stagedir, agpfilename)
639
640 # if this project has deployment data sources configured, remove
641 # them. changing the project is fine, since this is a clone of
642 # the project the IDE has.
643 self.GetModel().GetAppInfo().ResetDeploymentDataSources()
644
645 f = None
646 try:
647 f = open(agpfilepath, "w")
648
649 # setting homeDir correctly is required for the "figuring out
650 # relative paths" logic when saving the project
651 self.GetModel().homeDir = stagedir
652
653 projectlib.save(f, self.GetModel(), productionDeployment=True)
654 finally:
655 try:
656 f.close()
657 except: pass
658
659 return stagedir
660
661 def _FixWsdlAgFiles(self, stagedir):
662 """For each wsdlag file in the stagedir: if referenced artifact (wsdl or code file) is a known product file (such as securityservice.wsdl), make sure patch to it is parameterized with special env var. We do not want to copy those files. For user artifacts, ensure the file lives in root of stagedir. This should be the case if it is part of project (since staging has run). If it is not at root of stagedir, copy it. Then update path in wsdlag."""
663 files = os.listdir(stagedir)
664 for f in files:
665 if (f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT)):
666 wsdlagpath = os.path.join(stagedir, f)
667 fileObject = None
668 modified = False
669 try:
670 fileObject = open(wsdlagpath)
671 serviceref = WsdlAgEditor.load(fileObject)
672
673 # referenced wsdl
674 if (hasattr(serviceref, WsdlAgModel.WSDL_FILE_ATTR)):
675 modified = (modified |
676 self._UpdateServiceRefPathAttr(
677 stagedir, serviceref,
678 WsdlAgModel.WSDL_FILE_ATTR))
679
680 # referenced code file
681 if (hasattr(serviceref, WsdlAgModel.LOCAL_SERVICE_ELEMENT)):
682 lse = getattr(serviceref,
683 WsdlAgModel.LOCAL_SERVICE_ELEMENT)
684 if (hasattr(lse, WsdlAgModel.LOCAL_SERVICE_FILE_ATTR)):
685 modified = (modified |
686 self._UpdateServiceRefPathAttr(
687 stagedir, lse,
688 WsdlAgModel.LOCAL_SERVICE_FILE_ATTR))
689
690
691 finally:
692 try:
693 fileObject.close()
694 except:
695 pass
696
697 # no need to save the file if we did not change anything
698 if not modified: continue
699
700 # write the wsdlag file
701 fileObject = open(wsdlagpath)
702 try:
703 serviceref = WsdlAgEditor.save(fileObject, serviceref)
704 finally:
705 try:
706 fileObject.close()
707 except:
708 pass
709
710
711 def _UpdateServiceRefPathAttr(self, stagedir, serviceref, attrName):
712 """Returns True if serviceref path has been updated, False otherwise."""
713
714 filePath = getattr(serviceref, attrName)
715
716 if (filePath == None):
717 return False
718
719 filePath = filePath.strip()
720
721 if (len(filePath) == 0):
722 return False
723
724
725 # if filePath starts with one of the AG systems vars, we don't
726 # have to do anything
727 if (fileutils.startsWithAgSystemVar(filePath)):
728 return False
729
730 # remove any known env var refs (we'll put them back a little below)
731 # we remove them here so that paths that do not have env vars also
732 # get parameterized correctly below
733 filePath = fileutils.expandKnownAGVars(filePath)
734
735 # make sure we have forward slashes. this is a workaround, which
736 # would not be necessary if we only write paths with forward slashes
737 # into our files
738 filePath = filePath.replace("\\", "/")
739
740 filePath = os.path.abspath(filePath)
741
742 if (not os.path.exists(filePath)):
743 # Wrong place to validate that referenced file exists, so just
744 # give up
745 return False
746
747 # If the referenced file is in stagedir already, there's nothing to do
748 if (fileutils.hasAncestorDir(filePath, stagedir)):
749 return False
750
751 # The path points outside of stagedir.
752
753 # Check if we already have the referenced wsdl file at root, should be
754 # the case if the referenced wsdl is part of project.
755 # Copy it if we don't have it, unless it lives in one of the known
756 # product directories - in which case we parameterize the known path
757 # with one of our AG system vars
758 relPath = os.path.basename(filePath)
759 stagePath = os.path.join(stagedir, relPath)
760
761 if (not os.path.exists(stagePath)):
762 pFilePath = fileutils.parameterizePathWithAGSystemVar(filePath)
763 if pFilePath == filePath: # no parameterization happened, copy
764 fileutils.copyFile(filePath, stagePath)
765 setattr(serviceref, attrName, relPath)
766 else:
767 setattr(serviceref, attrName, pFilePath.replace("\\", "/"))
768 else:
769 setattr(serviceref, attrName, relPath)
770
771 return True
772
773
774 def _SetSchemaTargetDataSource(self, projectFiles, dsmapping):
775 """Update schema's default data source, if necessary."""
776
777 for projectFile in projectFiles:
778 if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
779 name = os.path.basename(projectFile.filePath)
780 if (dsmapping.has_key(name)):
781 schema = xmlutils.load(projectFile.filePath)
782 defaultName = schema.getDefaultDataSourceName()
783 if (defaultName != dsmapping[name]):
784 schema.setDefaultDataSourceName(dsmapping[name])
785 xmlutils.save(projectFile.filePath, schema)
786
787
788 def _StageFiles(self, fileDict):
789 """Copy files to staging directory, update filePath attr of project's ProjectFile instances."""
790
791 # fileDict: ProjectFile instance -> dest path (string)
792
793 for fileRef, fileDest in fileDict.items():
794 fileutils.copyFile(fileRef.filePath, fileDest)
795 fileRef.filePath = fileDest
796
797 def _ValidateFilePaths(self, projectdir, stagedir):
798 """If paths validate, returns a dict mapping ProjectFile to destination path. Destination path is the path the file needs to be copied to for staging. If paths don't validate, throws an IOError.
799 With our current slightly simplistic staging algorithm, staging will not work iff the project has files outside of the projectdir with names (filename without path) that:
800 - match filenames of files living at the root of the project.
801 - are same as those of any other file that lives outside of the projectdir.
802
803 We have this limitation because we move any file that lives outside of the project dir into the root of the stagedir (== copied project dir). We could make this smarter by either giving files unique names if we detect a collistion, or by creating some directory structure instead of putting all files from outside of the projectdir into the root of the stagedir (== copied projectdir)."""
804
805 # ProjectFile instance -> dest path (string)
806 rtn = {}
807
808 projectRootFiles = sets.Set() # live at project root
809 foreignFiles = sets.Set() # live outside of project
810
811 fileRefsToDeploy = self.GetFileRefs()
812
813 for fileRef in fileRefsToDeploy:
814 relPath = fileutils.getRelativePath(fileRef.filePath, projectdir)
815 filename = os.path.basename(fileRef.filePath)
816 if not relPath: # file lives outside of project dir...
817
818 # do we have another file with the same name already?
819 if filename in foreignFiles:
820 raise IOError("More than one file with name \"%s\" lives outside of the project. These files need to have unique names" % filename)
821 foreignFiles.add(filename)
822 fileDest = os.path.join(stagedir, filename)
823 else:
824 # file lives somewhere within the project dir
825 fileDest = os.path.join(stagedir, relPath)
826 if not os.path.dirname(relPath):
827 projectRootFiles.add(filename)
828
829 rtn[fileRef] = fileDest
830
831 # make sure we won't collide with a file that lives at root of
832 # projectdir when moving files into project
833 for filename in foreignFiles:
834 if filename in projectRootFiles:
835 raise IOError("File outside of project, \"%s\", cannot have same name as file at project root" % filename)
836 return rtn
837
838
839 def RenameFolder(self, oldFolderPath, newFolderPath):
840 for file in self.GetModel()._files:
841 if file.logicalFolder == oldFolderPath:
842 file.logicalFolder = newFolderPath
843 self.UpdateAllViews(hint = ("rename folder", self, oldFolderPath, newFolderPath))
844 self.Modify(True)
845 return True
846
847 def GetSchemas(self):
848 """Returns list of schema models (activegrid.model.schema.schema) for all schemas in this project."""
849
850 rtn = []
851 resourceFactory = self._GetResourceFactory()
852 for projectFile in self.GetModel().projectFiles:
853 if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
854 schema = resourceFactory.getModel(projectFile)
855 if (schema != None):
856 rtn.append(schema)
857
858 return rtn
859
860 def GetFiles(self):
861 return self.GetModel().filePaths
862
863
864 def GetFileRefs(self):
865 return self.GetModel().findAllRefs()
866
867
868 def SetFileRefs(self, fileRefs):
869 return self.GetModel().setRefs(fileRefs)
870
871
872 def IsFileInProject(self, filename):
873 return self.GetModel().FindFile(filename)
874
875
876 def GetAppInfo(self):
877 return self.GetModel().GetAppInfo()
878
879
880 def GetAppDocMgr(self):
881 return self.GetModel()
882
883
884 def GetProjectName(self):
885 return os.path.splitext(os.path.basename(self.GetFilename()))[0]
886
887
888 def GetDeploymentFilepath(self, pre17=False):
889 if (pre17):
890 name = self.GetProjectName() + PRE_17_TMP_DPL_NAME
891 else:
892 name = self.GetProjectName() + _17_TMP_DPL_NAME
893 return os.path.join(self.GetModel().homeDir, name)
894
895
896 def _GetResourceFactory(self, preview=False, deployFilepath=None):
897 return IDEResourceFactory(
898 openDocs=wx.GetApp().GetDocumentManager().GetDocuments(),
899 dataSourceService=wx.GetApp().GetService(DataModelEditor.DataSourceService),
900 projectDir=os.path.dirname(self.GetFilename()),
901 preview=preview,
902 deployFilepath=deployFilepath)
903
904 def GenerateDeployment(self, deployFilepath=None, preview=False):
905
906 if ACTIVEGRID_BASE_IDE:
907 return
908
909 if not deployFilepath:
910 deployFilepath = self.GetDeploymentFilepath()
911
912 d = DeploymentGeneration.DeploymentGenerator(
913 self.GetModel(), self._GetResourceFactory(preview,
914 deployFilepath))
915
916 dpl = d.getDeployment(deployFilepath)
917
918 if preview:
919 dpl.initialize() # used in preview only
920
921 # REVIEW 07-Apr-06 stoens@activegrid.com -- Check if there's a
922 # tmp dpl file with pre 17 name, if so, delete it, so user doesn't end
923 # up with unused file in project dir. We should probably remove this
924 # check after 1.7 goes out.
925 fileutils.remove(self.GetDeploymentFilepath(pre17=True))
926
927 deploymentlib.saveThroughCache(dpl.fileName, dpl)
928 return deployFilepath
929
930 def AddNameSpaces(self, filePaths):
931 """ Add any new wsdl and schema namespaces to bpel files """
932 """ Add any new schema namespaces to wsdl files """
933 if ACTIVEGRID_BASE_IDE:
934 return
935
936 processRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_PROCESS) # bpel
937 schemaRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_SCHEMA) # xsd
938 serviceRefs = self.GetAppDocMgr().allServiceRefs # wsdl
939
940 # update bpel files
941 if processRefs and (serviceRefs or schemaRefs):
942 for processRef in processRefs:
943 processDoc = processRef.ideDocument
944 process = processDoc.GetModel()
945
946 if processDoc and process:
947 modified = False
948
949 # add wsdl namespaces to bpel file
950 for serviceRef in serviceRefs:
951 wsdl = serviceRef.document
952 if (wsdl
953 and (wsdl.fileName in filePaths
954 or serviceRef.filePath in filePaths)):
955 wsdlLongNS = wsdl.targetNamespace
956 wsdlShortNS = self.GetAppDocMgr().findShortNS(wsdlLongNS)
957 if not wsdlShortNS:
958 wsdlShortNS = xmlutils.genShortNS(process, wsdlLongNS)
959 xmlutils.addNSAttribute(process, wsdlShortNS, wsdlLongNS)
960 modified = True
961
962 # add schema namespaces to bpel file
963 for schemaRef in schemaRefs:
964 schema = schemaRef.document
965 if schema and schema.fileName in filePaths:
966 schemaLongNS = schema.targetNamespace
967 schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
968 if not schemaShortNS:
969 schemaShortNS = xmlutils.genShortNS(process, schemaLongNS)
970 xmlutils.addNSAttribute(process, schemaShortNS, schemaLongNS)
971 modified = True
972
973 if modified:
974 processDoc.OnSaveDocument(processDoc.GetFilename())
975
976
977 # update wsdl files
978 if serviceRefs and schemaRefs:
979 for serviceRef in serviceRefs:
980 wsdl = serviceRef.document
981 wsdlDoc = serviceRef.ideDocument
982
983 if wsdl and wsdlDoc:
984 modified = False
985
986 # add schema namespace to wsdl file
987 for schemaRef in schemaRefs:
988 schema = schemaRef.document
989 if schema and schema.fileName in filePaths:
990 schemaLongNS = schema.targetNamespace
991 schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
992 if not schemaShortNS:
993 schemaShortNS = xmlutils.genShortNS(wsdl, schemaLongNS)
994 xmlutils.addNSAttribute(wsdl, schemaShortNS, schemaLongNS)
995 modified = True
996
997 if modified:
998 wsdlDoc.OnSaveDocument(wsdlDoc.GetFilename())
999
1000
1001 class NewProjectWizard(Wizard.BaseWizard):
1002
1003 WIZTITLE = _("New Project Wizard")
1004
1005
1006 def __init__(self, parent):
1007 self._parent = parent
1008 self._fullProjectPath = None
1009 Wizard.BaseWizard.__init__(self, parent, self.WIZTITLE)
1010 self._projectLocationPage = self.CreateProjectLocation(self)
1011 wx.wizard.EVT_WIZARD_PAGE_CHANGING(self, self.GetId(), self.OnWizPageChanging)
1012
1013
1014 def CreateProjectLocation(self,wizard):
1015 page = Wizard.TitledWizardPage(wizard, _("Name and Location"))
1016
1017 page.GetSizer().Add(wx.StaticText(page, -1, _("\nEnter the name and location for the project.\n")))
1018 self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, fileExtension="agp", appDirDefaultStartDir=True, fileLabel=_("Name:"), dirLabel=_("Location:"))
1019 page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
1020
1021 wizard.Layout()
1022 wizard.FitToPage(page)
1023 return page
1024
1025
1026 def RunWizard(self, existingTables = None, existingRelationships = None):
1027 status = Wizard.BaseWizard.RunWizard(self, self._projectLocationPage)
1028 if status:
1029 wx.ConfigBase_Get().Write(PROJECT_DIRECTORY_KEY, self._dirCtrl.GetValue())
1030 docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
1031 if os.path.exists(self._fullProjectPath):
1032 # What if the document is already open and we're overwriting it?
1033 documents = docManager.GetDocuments()
1034 for document in documents:
1035 if os.path.normcase(document.GetFilename()) == os.path.normcase(self._fullProjectPath): # If the renamed document is open, update it
1036 document.DeleteAllViews()
1037 break
1038 os.remove(self._fullProjectPath)
1039
1040 for template in docManager.GetTemplates():
1041 if template.GetDocumentType() == ProjectDocument:
1042 doc = template.CreateDocument(self._fullProjectPath, flags = wx.lib.docview.DOC_NEW)
1043 doc.OnSaveDocument(self._fullProjectPath)
1044 projectService = wx.GetApp().GetService(ProjectService)
1045 view = projectService.GetView()
1046 view.AddProjectToView(doc)
1047 break
1048
1049 self.Destroy()
1050 return status
1051
1052
1053 def OnWizPageChanging(self, event):
1054 if event.GetDirection(): # It's going forwards
1055 if event.GetPage() == self._projectLocationPage:
1056 if not self._fileValidation(validClassName=True):
1057 event.Veto()
1058 return
1059 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(), PROJECT_EXTENSION))
1060
1061
1062 def OnShowCreatePages(self):
1063 self.Hide()
1064 import DataModelEditor
1065 requestedPos = self.GetPositionTuple()
1066 projectService = wx.GetApp().GetService(ProjectService)
1067 projectView = projectService.GetView()
1068
1069 wiz = DataModelEditor.ImportExportWizard(projectView.GetFrame(), pos=requestedPos)
1070 if wiz.RunWizard(dontDestroy=True):
1071 self._schemaName.SetValue(wiz.GetSchemaFileName())
1072 wiz.Destroy()
1073 self.Show(True)
1074
1075
1076 class ProjectTemplate(wx.lib.docview.DocTemplate):
1077
1078
1079 def CreateDocument(self, path, flags):
1080 if path:
1081 doc = wx.lib.docview.DocTemplate.CreateDocument(self, path, flags)
1082 if path:
1083 doc.GetModel()._projectDir = os.path.dirname(path)
1084 return doc
1085 else:
1086 wiz = NewProjectWizard(wx.GetApp().GetTopWindow())
1087 wiz.RunWizard()
1088 wiz.Destroy()
1089 return None # never return the doc, otherwise docview will think it is a new file and rename it
1090
1091
1092 class ProjectAddFilesCommand(wx.lib.docview.Command):
1093
1094
1095 def __init__(self, projectDoc, filePaths, folderPath=None, types=None, names=None):
1096 wx.lib.docview.Command.__init__(self, canUndo = True)
1097 self._projectDoc = projectDoc
1098 self._allFilePaths = filePaths
1099 self._folderPath = folderPath
1100 self._types = types
1101 self._names = names
1102
1103 if not self._types:
1104 self._types = []
1105 projectService = wx.GetApp().GetService(ProjectService)
1106 for filePath in self._allFilePaths:
1107 self._types.append(projectService.FindFileTypeDefault(filePath))
1108
1109 # list of files that will really be added
1110 self._newFiles = []
1111 for filePath in self._allFilePaths:
1112 if not projectDoc.GetModel().FindFile(filePath):
1113 self._newFiles.append(filePath)
1114
1115
1116 def GetName(self):
1117 if len(self._allFilePaths) == 1:
1118 return _("Add File %s") % os.path.basename(self._allFilePaths[0])
1119 else:
1120 return _("Add Files")
1121
1122
1123 def Do(self):
1124 return self._projectDoc.AddFiles(self._allFilePaths, self._folderPath, self._types, self._names)
1125
1126
1127 def Undo(self):
1128 return self._projectDoc.RemoveFiles(self._newFiles)
1129
1130
1131 class ProjectRemoveFilesCommand(wx.lib.docview.Command):
1132
1133
1134 def __init__(self, projectDoc, files):
1135 wx.lib.docview.Command.__init__(self, canUndo = True)
1136 self._projectDoc = projectDoc
1137 self._files = files
1138
1139
1140 def GetName(self):
1141 if len(self._files) == 1:
1142 return _("Remove File %s") % os.path.basename(self._files[0].filePath)
1143 else:
1144 return _("Remove Files")
1145
1146
1147 def Do(self):
1148 return self._projectDoc.RemoveFiles(files=self._files)
1149
1150
1151 def Undo(self):
1152 return self._projectDoc.AddFiles(files=self._files)
1153
1154
1155
1156 class ProjectRenameFileCommand(wx.lib.docview.Command):
1157
1158
1159 def __init__(self, projectDoc, oldFilePath, newFilePath, isProject = False):
1160 wx.lib.docview.Command.__init__(self, canUndo = True)
1161 self._projectDoc = projectDoc
1162 self._oldFilePath = oldFilePath
1163 self._newFilePath = newFilePath
1164 self._isProject = isProject
1165
1166
1167 def GetName(self):
1168 return _("Rename File %s to %s") % (os.path.basename(self._oldFilePath), os.path.basename(self._newFilePath))
1169
1170
1171 def Do(self):
1172 return self._projectDoc.RenameFile(self._oldFilePath, self._newFilePath, self._isProject)
1173
1174
1175 def Undo(self):
1176 return self._projectDoc.RenameFile(self._newFilePath, self._oldFilePath, self._isProject)
1177
1178
1179 class ProjectRenameFolderCommand(wx.lib.docview.Command):
1180 def __init__(self, doc, oldFolderPath, newFolderPath):
1181 wx.lib.docview.Command.__init__(self, canUndo = True)
1182 self._doc = doc
1183 self._oldFolderPath = oldFolderPath
1184 self._newFolderPath = newFolderPath
1185
1186
1187 def GetName(self):
1188 return _("Rename Folder %s to %s") % (os.path.basename(self._oldFolderPath), os.path.basename(self._newFolderPath))
1189
1190
1191 def Do(self):
1192 return self._doc.RenameFolder(self._oldFolderPath, self._newFolderPath)
1193
1194
1195 def Undo(self):
1196 return self._doc.RenameFolder(self._newFolderPath, self._oldFolderPath)
1197
1198
1199 class ProjectAddFolderCommand(wx.lib.docview.Command):
1200 def __init__(self, view, doc, folderpath):
1201 wx.lib.docview.Command.__init__(self, canUndo = True)
1202 self._doc = doc
1203 self._view = view
1204 self._folderpath = folderpath
1205
1206
1207 def GetName(self):
1208 return _("Add Folder %s") % (os.path.basename(self._folderpath))
1209
1210
1211 def Do(self):
1212 if self._view.GetDocument() != self._doc:
1213 return True
1214 status = self._view.AddFolder(self._folderpath)
1215 if status:
1216 self._view._treeCtrl.UnselectAll()
1217 item = self._view._treeCtrl.FindFolder(self._folderpath)
1218 self._view._treeCtrl.SelectItem(item)
1219 return status
1220
1221
1222 def Undo(self):
1223 if self._view.GetDocument() != self._doc:
1224 return True
1225 return self._view.DeleteFolder(self._folderpath)
1226
1227
1228 class ProjectRemoveFolderCommand(wx.lib.docview.Command):
1229 def __init__(self, view, doc, folderpath):
1230 wx.lib.docview.Command.__init__(self, canUndo = True)
1231 self._doc = doc
1232 self._view = view
1233 self._folderpath = folderpath
1234
1235
1236 def GetName(self):
1237 return _("Remove Folder %s") % (os.path.basename(self._folderpath))
1238
1239
1240 def Do(self):
1241 if self._view.GetDocument() != self._doc:
1242 return True
1243 return self._view.DeleteFolder(self._folderpath)
1244
1245
1246 def Undo(self):
1247 if self._view.GetDocument() != self._doc:
1248 return True
1249 status = self._view.AddFolder(self._folderpath)
1250 if status:
1251 self._view._treeCtrl.UnselectAll()
1252 item = self._view._treeCtrl.FindFolder(self._folderpath)
1253 self._view._treeCtrl.SelectItem(item)
1254 return status
1255
1256
1257 class ProjectMoveFilesCommand(wx.lib.docview.Command):
1258
1259 def __init__(self, doc, files, folderPath):
1260 wx.lib.docview.Command.__init__(self, canUndo = True)
1261 self._doc = doc
1262 self._files = files
1263 self._newFolderPath = folderPath
1264
1265 self._oldFolderPaths = []
1266 for file in self._files:
1267 self._oldFolderPaths.append(file.logicalFolder)
1268
1269
1270 def GetName(self):
1271 if len(self._files) == 1:
1272 return _("Move File %s") % os.path.basename(self._files[0].filePath)
1273 else:
1274 return _("Move Files")
1275
1276
1277 def Do(self):
1278 return self._doc.MoveFiles(self._files, self._newFolderPath)
1279
1280
1281 def Undo(self):
1282 return self._doc.MoveFiles(self._files, self._oldFolderPaths)
1283
1284
1285 class ProjectTreeCtrl(wx.TreeCtrl):
1286
1287 #----------------------------------------------------------------------------
1288 # Overridden Methods
1289 #----------------------------------------------------------------------------
1290
1291 def __init__(self, parent, id, style):
1292 wx.TreeCtrl.__init__(self, parent, id, style = style)
1293
1294 templates = wx.GetApp().GetDocumentManager().GetTemplates()
1295 iconList = wx.ImageList(16, 16, initialCount = len(templates))
1296 self._iconIndexLookup = []
1297 for template in templates:
1298 icon = template.GetIcon()
1299 if icon:
1300 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
1301 icon.SetHeight(16)
1302 icon.SetWidth(16)
1303 if wx.GetApp().GetDebug():
1304 print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
1305 iconIndex = iconList.AddIcon(icon)
1306 self._iconIndexLookup.append((template, iconIndex))
1307
1308 icon = getBlankIcon()
1309 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
1310 icon.SetHeight(16)
1311 icon.SetWidth(16)
1312 if wx.GetApp().GetDebug():
1313 print "Warning: getBlankIcon isn't 16x16, not crossplatform"
1314 self._blankIconIndex = iconList.AddIcon(icon)
1315
1316 icon = getFolderClosedIcon()
1317 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
1318 icon.SetHeight(16)
1319 icon.SetWidth(16)
1320 if wx.GetApp().GetDebug():
1321 print "Warning: getFolderIcon isn't 16x16, not crossplatform"
1322 self._folderClosedIconIndex = iconList.AddIcon(icon)
1323
1324 icon = getFolderOpenIcon()
1325 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
1326 icon.SetHeight(16)
1327 icon.SetWidth(16)
1328 if wx.GetApp().GetDebug():
1329 print "Warning: getFolderIcon isn't 16x16, not crossplatform"
1330 self._folderOpenIconIndex = iconList.AddIcon(icon)
1331
1332 self.AssignImageList(iconList)
1333
1334
1335 def OnCompareItems(self, item1, item2):
1336 item1IsFolder = (self.GetPyData(item1) == None)
1337 item2IsFolder = (self.GetPyData(item2) == None)
1338 if (item1IsFolder == item2IsFolder): # if both are folders or both not
1339 return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower())
1340 elif item1IsFolder and not item2IsFolder: # folders sort above non-folders
1341 return -1
1342 elif not item1IsFolder and item2IsFolder: # folders sort above non-folders
1343 return 1
1344
1345
1346 def AppendFolder(self, parent, folderName):
1347 item = wx.TreeCtrl.AppendItem(self, parent, folderName)
1348 self.SetItemImage(item, self._folderClosedIconIndex, wx.TreeItemIcon_Normal)
1349 self.SetItemImage(item, self._folderOpenIconIndex, wx.TreeItemIcon_Expanded)
1350 self.SetPyData(item, None)
1351 return item
1352
1353
1354 def AppendItem(self, parent, filename, file):
1355 item = wx.TreeCtrl.AppendItem(self, parent, filename)
1356
1357 found = False
1358 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
1359 if template:
1360 for t, iconIndex in self._iconIndexLookup:
1361 if t is template:
1362 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Normal)
1363 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Expanded)
1364 ## self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Selected)
1365 found = True
1366 break
1367
1368 if not found:
1369 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Normal)
1370 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Expanded)
1371 ## self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Selected)
1372
1373 self.SetPyData(item, file)
1374
1375 return item
1376
1377
1378 def AddFolder(self, folderPath):
1379 folderItems = []
1380
1381 if folderPath != None:
1382 folderTree = folderPath.split('/')
1383
1384 item = self.GetRootItem()
1385 for folderName in folderTree:
1386 found = False
1387
1388 (child, cookie) = self.GetFirstChild(item)
1389 while child.IsOk():
1390 file = self.GetPyData(child)
1391 if file:
1392 pass
1393 else: # folder
1394 if self.GetItemText(child) == folderName:
1395 item = child
1396 found = True
1397 break
1398 (child, cookie) = self.GetNextChild(item, cookie)
1399
1400 if not found:
1401 item = self.AppendFolder(item, folderName)
1402 folderItems.append(item)
1403
1404 return folderItems
1405
1406
1407 def FindItem(self, filePath, parentItem=None):
1408 if not parentItem:
1409 parentItem = self.GetRootItem()
1410
1411 (child, cookie) = self.GetFirstChild(parentItem)
1412 while child.IsOk():
1413 file = self.GetPyData(child)
1414 if file:
1415 if file.filePath == filePath:
1416 return child
1417 else: # folder
1418 result = self.FindItem(filePath, child) # do recursive call
1419 if result:
1420 return result
1421 (child, cookie) = self.GetNextChild(parentItem, cookie)
1422
1423 return None
1424
1425
1426 def FindFolder(self, folderPath):
1427 if folderPath != None:
1428 folderTree = folderPath.split('/')
1429
1430 item = self.GetRootItem()
1431 for folderName in folderTree:
1432 found = False
1433
1434 (child, cookie) = self.GetFirstChild(item)
1435 while child.IsOk():
1436 file = self.GetPyData(child)
1437 if file:
1438 pass
1439 else: # folder
1440 if self.GetItemText(child) == folderName:
1441 item = child
1442 found = True
1443 break
1444 (child, cookie) = self.GetNextChild(item, cookie)
1445
1446 if found:
1447 return item
1448
1449 return None
1450
1451
1452 def FindClosestFolder(self, x, y):
1453 item, flags = self.HitTest((x,y))
1454 if item:
1455 file = self.GetPyData(item)
1456 if file:
1457 item = self.GetItemParent(item)
1458 return item
1459 return item
1460 return None
1461
1462
1463 class ProjectView(wx.lib.docview.View):
1464 LOGICAL_MODE = "logical"
1465 PHYSICAL_MODE = "physical"
1466
1467 #----------------------------------------------------------------------------
1468 # Overridden methods
1469 #----------------------------------------------------------------------------
1470
1471 def __init__(self, service = None):
1472 wx.lib.docview.View.__init__(self)
1473 # self._service = service # not used, but kept to match other Services
1474 self._projectChoice = None
1475 self._logicalBtn = None
1476 self._physicalBtn = None
1477 self._treeCtrl = None
1478 self._editingSoDontKillFocus = False
1479 self._checkEditMenu = True
1480 self._loading = False # flag to not to try to saving state of folders while it is loading
1481
1482
1483 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
1484 return wx.GetApp().GetDocumentManager()
1485
1486
1487 def Destroy(self):
1488 projectService = wx.GetApp().GetService(ProjectService)
1489 if projectService:
1490 projectService.SetView(None)
1491 wx.lib.docview.View.Destroy(self)
1492
1493
1494 def GetDocument(self):
1495 if not self._projectChoice:
1496 return None
1497
1498 selItem = self._projectChoice.GetSelection()
1499 if selItem == wx.NOT_FOUND:
1500 return None
1501
1502 document = self._projectChoice.GetClientData(selItem)
1503 return document
1504
1505
1506 def Activate(self, activate = True):
1507 if not wx.GetApp().IsMDI():
1508 if activate and not self.IsShown():
1509 self.Show()
1510
1511 if self.IsShown():
1512 wx.lib.docview.View.Activate(self, activate = activate)
1513 if activate and self._treeCtrl:
1514 self._treeCtrl.SetFocus()
1515
1516
1517 def OnCreate(self, doc, flags):
1518 config = wx.ConfigBase_Get()
1519 if wx.GetApp().IsMDI():
1520 self._embeddedWindow = wx.GetApp().GetTopWindow().GetEmbeddedWindow(wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT)
1521 self.SetFrame(self._embeddedWindow)
1522 frame = self._embeddedWindow
1523 wx.EVT_SIZE(frame, self.OnSize)
1524 else:
1525 self._embeddedWindow = None
1526 pos = config.ReadInt("ProjectFrameXLoc", -1), config.ReadInt("ProjectFrameYLoc", -1)
1527 # make sure frame is visible
1528 screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)
1529 screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
1530 if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight:
1531 pos = wx.DefaultPosition
1532
1533 size = wx.Size(config.ReadInt("ProjectFrameXSize", -1), config.ReadInt("ProjectFrameYSize", -1))
1534
1535 title = _("Projects")
1536 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
1537 title = title + " - " + wx.GetApp().GetAppName()
1538
1539 frame = wx.GetApp().CreateDocumentFrame(self, doc, 0, title = title, pos = pos, size = size)
1540 if config.ReadInt("ProjectFrameMaximized", False):
1541 frame.Maximize(True)
1542
1543 panel = wx.Panel(frame, -1)
1544
1545 sizer = wx.BoxSizer(wx.VERTICAL)
1546
1547 butSizer = wx.BoxSizer(wx.HORIZONTAL)
1548
1549 self._projectChoice = wx.Choice(panel, -1)
1550 panel.Bind(wx.EVT_CHOICE, self.OnProjectSelect, self._projectChoice)
1551 w, h = self._projectChoice.GetSize()
1552
1553 self._logicalBtn = wx.lib.buttons.GenBitmapToggleButton(panel, -1, getLogicalModeOffBitmap(), size=(h,h))
1554 self._logicalBtn.SetBitmapSelected(getLogicalModeOnBitmap())
1555 self._logicalBtn.SetToggle(True)
1556 self._logicalBtn.SetToolTipString(_("View Files by Logical Groups"))
1557 panel.Bind(wx.EVT_BUTTON, self.OnSelectMode, self._logicalBtn)
1558 self._physicalBtn = wx.lib.buttons.GenBitmapToggleButton(panel, -1, getPhysicalModeOffBitmap(), size=(h,h))
1559 self._physicalBtn.SetBitmapSelected(getPhysicalModeOnBitmap())
1560 self._physicalBtn.SetToolTipString(_("View Files by Physical Disk Layout"))
1561 panel.Bind(wx.EVT_BUTTON, self.OnSelectMode, self._physicalBtn)
1562
1563 butSizer.Add(self._projectChoice, 1, wx.EXPAND)
1564 butSizer.Add(self._logicalBtn, 0)
1565 butSizer.Add(self._physicalBtn, 0)
1566 sizer.Add(butSizer, 0, wx.EXPAND)
1567
1568 self._treeCtrl = ProjectTreeCtrl(panel, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE | wx.TR_EXTENDED)
1569 self._treeCtrl.AddRoot(_("Projects"))
1570 if self._embeddedWindow:
1571 sizer.Add(self._treeCtrl, 1, wx.EXPAND|wx.BOTTOM, HALF_SPACE) # allow space for embedded window resize-sash
1572 else:
1573 sizer.Add(self._treeCtrl, 1, wx.EXPAND)
1574 panel.SetSizer(sizer)
1575
1576 sizer = wx.BoxSizer(wx.VERTICAL)
1577
1578 if wx.GetApp().IsMDI():
1579 sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 3) # wxBug: without bottom margin, can't resize embedded window
1580 else:
1581 sizer.Add(panel, 1, wx.EXPAND)
1582
1583 frame.SetSizer(sizer)
1584 frame.Layout()
1585 self.Activate()
1586
1587 if wx.GetApp().IsMDI():
1588 wx.EVT_SET_FOCUS(self._treeCtrl, self.OnFocus)
1589 wx.EVT_KILL_FOCUS(self._treeCtrl, self.OnKillFocus)
1590
1591 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
1592 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelectionSDI)
1593 else:
1594 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelection)
1595 wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
1596 wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
1597 wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
1598 wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
1599 wx.EVT_TREE_ITEM_COLLAPSED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
1600 wx.EVT_TREE_ITEM_EXPANDED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
1601 wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
1602 wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
1603 wx.EVT_LEFT_DOWN(self._treeCtrl, self.OnLeftClick)
1604
1605 # drag-and-drop support
1606 dt = ProjectFileDropTarget(self)
1607 self._treeCtrl.SetDropTarget(dt)
1608
1609 return True
1610
1611
1612 def OnSelectMode(self, event):
1613 btn = event.GetEventObject()
1614 down = event.GetIsDown()
1615 if btn == self._logicalBtn:
1616 self._physicalBtn.SetToggle(not down)
1617 else: # btn == self._physicalBtn:
1618 self._logicalBtn.SetToggle(not down)
1619 self.LoadProject(self.GetDocument())
1620
1621
1622 def GetMode(self):
1623 if not self._physicalBtn.up:
1624 return ProjectView.PHYSICAL_MODE
1625 else: # elif self._logicalBtn.GetValue():
1626 return ProjectView.LOGICAL_MODE
1627
1628
1629 def OnProjectSelect(self, event=None):
1630 self.LoadProject(self.GetDocument())
1631 if self.GetDocument():
1632 filename = self.GetDocument().GetFilename()
1633 else:
1634 filename = ''
1635 self._projectChoice.SetToolTipString(filename)
1636
1637
1638 def OnSize(self, event):
1639 event.Skip()
1640 wx.CallAfter(self.GetFrame().Layout)
1641
1642
1643 def OnBeginDrag(self, event):
1644 if self.GetMode() == ProjectView.PHYSICAL_MODE:
1645 return
1646
1647 item = event.GetItem()
1648 if item.IsOk():
1649 self._draggingItems = []
1650 for item in self._treeCtrl.GetSelections():
1651 if self._IsItemFile(item):
1652 self._draggingItems.append(item)
1653 if len(self._draggingItems):
1654 event.Allow()
1655
1656
1657 def OnEndDrag(self, event):
1658 item = event.GetItem()
1659 if item.IsOk():
1660 files = []
1661 for ditem in self._draggingItems:
1662 file = self._GetItemFile(ditem)
1663 if file not in files:
1664 files.append(file)
1665
1666 folderPath = self._GetItemFolderPath(item)
1667
1668 self.GetDocument().GetCommandProcessor().Submit(ProjectMoveFilesCommand(self.GetDocument(), files, folderPath))
1669
1670
1671 def WriteProjectConfig(self):
1672 frame = self.GetFrame()
1673 config = wx.ConfigBase_Get()
1674 if frame and not self._embeddedWindow:
1675 if not frame.IsMaximized():
1676 config.WriteInt("ProjectFrameXLoc", frame.GetPositionTuple()[0])
1677 config.WriteInt("ProjectFrameYLoc", frame.GetPositionTuple()[1])
1678 config.WriteInt("ProjectFrameXSize", frame.GetSizeTuple()[0])
1679 config.WriteInt("ProjectFrameYSize", frame.GetSizeTuple()[1])
1680 config.WriteInt("ProjectFrameMaximized", frame.IsMaximized())
1681
1682 if config.ReadInt("ProjectSaveDocs", True):
1683 projectFileNames = []
1684 curProject = None
1685
1686 if self._projectChoice:
1687 for i in range(self._projectChoice.GetCount()):
1688 project = self._projectChoice.GetClientData(i)
1689 if not project.OnSaveModified():
1690 return
1691 if project.GetDocumentSaved(): # Might be a new document and "No" selected to save it
1692 projectFileNames.append(str(project.GetFilename()))
1693 config.Write("ProjectSavedDocs", projectFileNames.__repr__())
1694
1695 document = None
1696 if self._projectChoice.GetCount():
1697 i = self._projectChoice.GetSelection()
1698 if i != wx.NOT_FOUND:
1699 document = self._projectChoice.GetClientData(i)
1700 if document:
1701 config.Write("ProjectCurrent", document.GetFilename())
1702 else:
1703 config.DeleteEntry("ProjectCurrent")
1704
1705
1706 def OnClose(self, deleteWindow = True):
1707 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
1708 self.WriteProjectConfig()
1709
1710 project = self.GetDocument()
1711 if not project:
1712 return True
1713 if not project.Close():
1714 return True
1715
1716 if not deleteWindow:
1717 self.RemoveCurrentDocumentUpdate()
1718 else:
1719 # need this to accelerate closing down app if treeCtrl has lots of items
1720 self._treeCtrl.Freeze()
1721 try:
1722 rootItem = self._treeCtrl.GetRootItem()
1723 self._treeCtrl.DeleteChildren(rootItem)
1724 finally:
1725 self._treeCtrl.Thaw()
1726
1727 # We don't need to delete the window since it is a floater/embedded
1728 return True
1729
1730
1731 def _GetParentFrame(self):
1732 return wx.GetTopLevelParent(self.GetFrame())
1733
1734
1735 def OnUpdate(self, sender = None, hint = None):
1736 if wx.lib.docview.View.OnUpdate(self, sender, hint):
1737 return
1738
1739 if hint:
1740 if hint[0] == "add":
1741 projectDoc = hint[1]
1742 if self.GetDocument() != projectDoc: # project being updated isn't currently viewed project
1743 return
1744
1745 self._treeCtrl.Freeze()
1746
1747 try:
1748 newFilePaths = hint[2] # need to be added and selected, and sorted
1749 oldFilePaths = hint[3] # need to be selected
1750 self._treeCtrl.UnselectAll()
1751
1752 mode = self.GetMode()
1753
1754 project = projectDoc.GetModel()
1755 projectDir = project.homeDir
1756 rootItem = self._treeCtrl.GetRootItem()
1757
1758 # add new folders and new items
1759 addList = []
1760 for filePath in newFilePaths:
1761 file = project.FindFile(filePath)
1762 if file:
1763 if mode == ProjectView.LOGICAL_MODE:
1764 folderPath = file.logicalFolder
1765 else: # ProjectView.PHYSICAL_MODE
1766 folderPath = file.physicalFolder
1767 if folderPath:
1768 self._treeCtrl.AddFolder(folderPath)
1769 folder = self._treeCtrl.FindFolder(folderPath)
1770 else:
1771 folder = rootItem
1772 item = self._treeCtrl.AppendItem(folder, os.path.basename(file.filePath), file)
1773 addList.append(item)
1774
1775 # sort folders with new items
1776 parentList = []
1777 for item in addList:
1778 parentItem = self._treeCtrl.GetItemParent(item)
1779 if parentItem not in parentList:
1780 parentList.append(parentItem)
1781 for parentItem in parentList:
1782 self._treeCtrl.SortChildren(parentItem)
1783
1784 # select all the items user wanted to add
1785 lastItem = None
1786 for filePath in (oldFilePaths + newFilePaths):
1787 item = self._treeCtrl.FindItem(filePath)
1788 if item:
1789 self._treeCtrl.SelectItem(item)
1790 lastItem = item
1791
1792 if lastItem:
1793 self._treeCtrl.EnsureVisible(lastItem)
1794
1795 finally:
1796 self._treeCtrl.Thaw()
1797 return
1798
1799 elif hint[0] == "remove":
1800 projectDoc = hint[1]
1801 if self.GetDocument() != projectDoc: # project being updated isn't currently viewed project
1802 return
1803
1804 self._treeCtrl.Freeze()
1805
1806 try:
1807 filePaths = hint[2]
1808 self._treeCtrl.UnselectAll()
1809
1810 for filePath in filePaths:
1811 item = self._treeCtrl.FindItem(filePath)
1812 if item:
1813 self._treeCtrl.Delete(item)
1814
1815 self._treeCtrl.UnselectAll() # wxBug: even though we unselected earlier, an item still gets selected after the delete
1816
1817 finally:
1818 self._treeCtrl.Thaw()
1819 return
1820
1821 elif hint[0] == "rename":
1822 projectDoc = hint[1]
1823 if self.GetDocument() != projectDoc: # project being updated isn't currently viewed project
1824 return
1825
1826 self._treeCtrl.Freeze()
1827 try:
1828 item = self._treeCtrl.FindItem(hint[2])
1829 self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
1830 self._treeCtrl.EnsureVisible(item)
1831 finally:
1832 self._treeCtrl.Thaw()
1833 return
1834
1835 elif hint[0] == "rename folder":
1836 projectDoc = hint[1]
1837 if self.GetDocument() != projectDoc: # project being updated isn't currently viewed project
1838 return
1839
1840 self._treeCtrl.Freeze()
1841 try:
1842 item = self._treeCtrl.FindFolder(hint[2])
1843 if item:
1844 self._treeCtrl.UnselectAll()
1845 self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
1846 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
1847 self._treeCtrl.SelectItem(item)
1848 self._treeCtrl.EnsureVisible(item)
1849 finally:
1850 self._treeCtrl.Thaw()
1851 return
1852
1853
1854 def RemoveProjectUpdate(self, projectDoc):
1855 """ Called by service after deleting a project, need to remove from project choices """
1856 i = self._projectChoice.FindString(self._MakeProjectName(projectDoc))
1857 self._projectChoice.Delete(i)
1858
1859 numProj = self._projectChoice.GetCount()
1860 if i >= numProj:
1861 i = numProj - 1
1862 if i >= 0:
1863 self._projectChoice.SetSelection(i)
1864 self.OnProjectSelect()
1865
1866
1867 def RemoveCurrentDocumentUpdate(self, i=-1):
1868 """ Called by service after deleting a project, need to remove from project choices """
1869 i = self._projectChoice.GetSelection()
1870 self._projectChoice.Delete(i)
1871
1872 numProj = self._projectChoice.GetCount()
1873 if i >= numProj:
1874 i = numProj - 1
1875 if i >= 0:
1876 self._projectChoice.SetSelection(i)
1877 self.OnProjectSelect()
1878
1879
1880 def ProcessEvent(self, event):
1881 id = event.GetId()
1882 if id == ProjectService.CLOSE_PROJECT_ID:
1883 projectDoc = self.GetDocument()
1884 if projectDoc:
1885 projectService = wx.GetApp().GetService(ProjectService)
1886 if projectService:
1887 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
1888 for openDoc in openDocs[:]: # need to make a copy, as each file closes we're off by one
1889 if projectDoc == openDoc: # close project last
1890 continue
1891
1892 if projectDoc == projectService.FindProjectFromMapping(openDoc):
1893 self.GetDocumentManager().CloseDocument(openDoc, False)
1894
1895 projectService.RemoveProjectMapping(openDoc)
1896 if hasattr(openDoc, "GetModel"):
1897 projectService.RemoveProjectMapping(openDoc.GetModel())
1898
1899 if self.GetDocumentManager().CloseDocument(projectDoc, False):
1900 self.RemoveCurrentDocumentUpdate()
1901 return True
1902 elif id == ProjectService.ADD_FILES_TO_PROJECT_ID:
1903 self.OnAddFileToProject(event)
1904 return True
1905 elif id == ProjectService.ADD_DIR_FILES_TO_PROJECT_ID:
1906 self.OnAddDirToProject(event)
1907 return True
1908 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1909 return False # Implement this one in the service
1910 elif id == ProjectService.ADD_FOLDER_ID:
1911 self.OnAddFolder(event)
1912 return True
1913 elif id == ProjectService.RENAME_ID:
1914 self.OnRename(event)
1915 return True
1916 elif id == ProjectService.DELETE_FILE_ID:
1917 self.OnDeleteFile(event)
1918 return True
1919 elif id == ProjectService.DELETE_PROJECT_ID:
1920 self.OnDeleteProject(event)
1921 return True
1922 elif id == wx.ID_CUT:
1923 self.OnCut(event)
1924 return True
1925 elif id == wx.ID_COPY:
1926 self.OnCopy(event)
1927 return True
1928 elif id == wx.ID_PASTE:
1929 self.OnPaste(event)
1930 return True
1931 elif (id == wx.ID_CLEAR
1932 or id == ProjectService.REMOVE_FROM_PROJECT):
1933 self.OnClear(event)
1934 return True
1935 elif id == wx.ID_SELECTALL:
1936 self.OnSelectAll(event)
1937 return True
1938 elif id == ProjectService.OPEN_SELECTION_ID:
1939 self.OnOpenSelection(event)
1940 return True
1941 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1942 self.OnProperties(event)
1943 return True
1944 elif id == ProjectService.PROJECT_PROPERTIES_ID:
1945 self.OnProjectProperties(event)
1946 return True
1947 else:
1948 return False
1949
1950
1951 def ProcessUpdateUIEvent(self, event):
1952 # Hack: The edit menu is not being set for projects that are preloaded at startup, so make sure it is OK here
1953 if self._checkEditMenu:
1954 doc = self.GetDocument()
1955 if doc and not doc.GetCommandProcessor().GetEditMenu():
1956 doc.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
1957 self._checkEditMenu = False
1958
1959 id = event.GetId()
1960 if id == wx.ID_CLOSE:
1961 # Too confusing, so disable closing from "File | Close" menu, must close from "Project | Close Current Project" menu
1962 if self.ProjectHasFocus() or self.FilesHasFocus():
1963 event.Enable(False)
1964 return True
1965 else:
1966 return False
1967 elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
1968 or id == ProjectService.ADD_DIR_FILES_TO_PROJECT_ID
1969 or id == ProjectService.CLOSE_PROJECT_ID
1970 or id == ProjectService.DELETE_PROJECT_ID):
1971 event.Enable(self.GetDocument() != None)
1972 return True
1973 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1974 event.Enable(False) # Implement this one in the service
1975 return True
1976 elif id == ProjectService.ADD_FOLDER_ID:
1977 event.Enable((self.GetDocument() != None) and (self.GetMode() == ProjectView.LOGICAL_MODE))
1978 return True
1979 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1980 status = False
1981 if self.ProjectHasFocus():
1982 if self.GetDocument():
1983 status = True
1984 elif self.FilesHasFocus():
1985 items = self._treeCtrl.GetSelections()
1986 if items:
1987 item = items[0]
1988 if self._IsItemFile(item):
1989 status = True
1990
1991 event.Enable(status)
1992 return True
1993 elif (id == wx.ID_CUT
1994 or id == wx.ID_COPY
1995 or id == ProjectService.DELETE_FILE_ID
1996 or id == ProjectService.REMOVE_FROM_PROJECT
1997 or id == ProjectService.OPEN_SELECTION_ID):
1998 event.Enable(self._HasFilesSelected())
1999 return True
2000 elif (id == wx.ID_CLEAR
2001 or id == ProjectService.RENAME_ID):
2002 items = self._treeCtrl.GetSelections()
2003 if items:
2004 hasViewSelected = False
2005 for item in items:
2006 if self._IsItemFile(item):
2007 file = self._GetItemFile(item)
2008 if file.type == 'xform':
2009 hasViewSelected = True
2010 break
2011 if hasViewSelected:
2012 event.Enable(False)
2013 return True
2014
2015 event.Enable(self._HasFilesSelected() or (self.GetDocument() != None and self.GetMode() == ProjectView.LOGICAL_MODE and self._HasFoldersSelected()))
2016 return True
2017 elif id == wx.ID_PASTE:
2018 event.Enable(self.CanPaste())
2019 return True
2020 elif id == wx.ID_SELECTALL:
2021 event.Enable(self._HasFiles())
2022 return True
2023 elif (id == wx.ID_PREVIEW
2024 or id == wx.ID_PRINT):
2025 event.Enable(False)
2026 return True
2027 else:
2028 return False
2029
2030 #----------------------------------------------------------------------------
2031 # Display Methods
2032 #----------------------------------------------------------------------------
2033
2034 def IsShown(self):
2035 if not self.GetFrame():
2036 return False
2037 return self.GetFrame().IsShown()
2038
2039
2040 def Hide(self):
2041 self.Show(False)
2042
2043
2044 def Show(self, show = True):
2045 self.GetFrame().Show(show)
2046 if wx.GetApp().IsMDI():
2047 mdiParentFrame = wx.GetApp().GetTopWindow()
2048 mdiParentFrame.ShowEmbeddedWindow(self.GetFrame(), show)
2049
2050
2051 #----------------------------------------------------------------------------
2052 # Methods for ProjectDocument and ProjectService to call
2053 #----------------------------------------------------------------------------
2054
2055 def SetProject(self, projectPath):
2056 curSel = self._projectChoice.GetSelection()
2057 for i in range(self._projectChoice.GetCount()):
2058 document = self._projectChoice.GetClientData(i)
2059 if document.GetFilename() == projectPath:
2060 if curSel != i: # don't reload if already loaded
2061 self._projectChoice.SetSelection(i)
2062 self.LoadProject(document)
2063 break
2064
2065
2066 def GetSelectedFile(self):
2067 for item in self._treeCtrl.GetSelections():
2068 filePath = self._GetItemFilePath(item)
2069 if filePath:
2070 return filePath
2071 return None
2072
2073
2074 def GetSelectedFiles(self):
2075 filePaths = []
2076 for item in self._treeCtrl.GetSelections():
2077 filePath = self._GetItemFilePath(item)
2078 if filePath and filePath not in filePaths:
2079 filePaths.append(filePath)
2080 return filePaths
2081
2082
2083 def GetSelectedPhysicalFolder(self):
2084 if self.GetMode() == ProjectView.LOGICAL_MODE:
2085 return None
2086 else:
2087 for item in self._treeCtrl.GetSelections():
2088 if not self._IsItemFile(item):
2089 filePath = self._GetItemFolderPath(item)
2090 if filePath:
2091 return filePath
2092 return None
2093
2094
2095 def GetSelectedProject(self):
2096 document = self.GetDocument()
2097 if document:
2098 return document.GetFilename()
2099 else:
2100 return None
2101
2102
2103 def AddProjectToView(self, document):
2104 i = self._projectChoice.Append(self._MakeProjectName(document), document)
2105 self._projectChoice.SetSelection(i)
2106 self.OnProjectSelect()
2107
2108
2109 def LoadProject(self, document):
2110 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
2111 self._treeCtrl.Freeze()
2112
2113 try:
2114 rootItem = self._treeCtrl.GetRootItem()
2115 self._treeCtrl.DeleteChildren(rootItem)
2116
2117 if document:
2118 mode = self.GetMode()
2119 docFilePath = document.GetFilename()
2120
2121 if mode == ProjectView.LOGICAL_MODE:
2122 folders = document.GetModel().logicalFolders
2123 else:
2124 folders = document.GetModel().physicalFolders
2125
2126 folders.sort()
2127 folderItems = []
2128 for folderPath in folders:
2129 folderItems = folderItems + self._treeCtrl.AddFolder(folderPath)
2130
2131 for file in document.GetModel()._files:
2132 if mode == ProjectView.LOGICAL_MODE:
2133 folder = file.logicalFolder
2134 else:
2135 folder = file.physicalFolder
2136 if folder:
2137 folderTree = folder.split('/')
2138
2139 item = rootItem
2140 for folderName in folderTree:
2141 found = False
2142 (child, cookie) = self._treeCtrl.GetFirstChild(item)
2143 while child.IsOk():
2144 if self._treeCtrl.GetItemText(child) == folderName:
2145 item = child
2146 found = True
2147 break
2148 (child, cookie) = self._treeCtrl.GetNextChild(item, cookie)
2149
2150 if not found:
2151 print "error folder '%s' not found for %s" % (folder, file.filePath)
2152 break
2153 else:
2154 item = rootItem
2155
2156 fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
2157
2158 self._treeCtrl.SortChildren(rootItem)
2159 for item in folderItems:
2160 self._treeCtrl.SortChildren(item)
2161
2162 self.LoadFolderState()
2163
2164 self._treeCtrl.SetFocus()
2165 (child, cookie) = self._treeCtrl.GetFirstChild(self._treeCtrl.GetRootItem())
2166 if child.IsOk():
2167 self._treeCtrl.UnselectAll()
2168 self._treeCtrl.SelectItem(child)
2169 self._treeCtrl.ScrollTo(child)
2170
2171 if self._embeddedWindow:
2172 document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
2173
2174 finally:
2175 self._treeCtrl.Thaw()
2176 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
2177
2178
2179 def ProjectHasFocus(self):
2180 """ Does Project Choice have focus """
2181 return (wx.Window.FindFocus() == self._projectChoice)
2182
2183
2184 def FilesHasFocus(self):
2185 """ Does Project Tree have focus """
2186 winWithFocus = wx.Window.FindFocus()
2187 if not winWithFocus:
2188 return False
2189 while winWithFocus:
2190 if winWithFocus == self._treeCtrl:
2191 return True
2192 winWithFocus = winWithFocus.GetParent()
2193 return False
2194
2195
2196 def ClearFolderState(self):
2197 config = wx.ConfigBase_Get()
2198 config.DeleteGroup(getProjectKeyName(self.GetDocument().GetFilename()))
2199
2200
2201 def SaveFolderState(self, event=None):
2202 """ Save the open/close state of folders """
2203
2204 if self._loading:
2205 return
2206
2207 folderList = []
2208 folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
2209 for item in folderItemList:
2210 if self._treeCtrl.IsExpanded(item):
2211 folderList.append(self._GetItemFolderPath(item))
2212
2213 config = wx.ConfigBase_Get()
2214 config.Write(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()), repr(folderList))
2215
2216
2217 def LoadFolderState(self):
2218 """ Load the open/close state of folders. """
2219 self._loading = True
2220
2221 config = wx.ConfigBase_Get()
2222 openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()), "")
2223 if openFolderData:
2224 folderList = eval(openFolderData)
2225
2226 folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
2227 for item in folderItemList:
2228 folderPath = self._GetItemFolderPath(item)
2229 if folderPath in folderList:
2230 self._treeCtrl.Expand(item)
2231 else:
2232 self._treeCtrl.Collapse(item)
2233
2234 else:
2235 projectService = wx.GetApp().GetService(ProjectService)
2236
2237 folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
2238 for item in folderItemList:
2239 folderPath = self._GetItemFolderPath(item)
2240 if projectService.FindLogicalViewFolderCollapsedDefault(folderPath): # get default initial state
2241 self._treeCtrl.Collapse(item)
2242 else:
2243 self._treeCtrl.Expand(item)
2244
2245 self._loading = False
2246
2247
2248 #----------------------------------------------------------------------------
2249 # Control events
2250 #----------------------------------------------------------------------------
2251
2252 def OnProperties(self, event):
2253 if self.ProjectHasFocus():
2254 self.OnProjectProperties(event)
2255 elif self.FilesHasFocus():
2256 items = self._treeCtrl.GetSelections()
2257 if not items:
2258 return
2259 item = items[0]
2260 filePath = self._GetItemFilePath(item)
2261 if filePath:
2262 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
2263 filePropertiesService.ShowPropertiesDialog(filePath)
2264
2265
2266 def OnProjectProperties(self, event):
2267 if self.GetDocument():
2268 dlg = ProjectPropertiesDialog(wx.GetApp().GetTopWindow(), self.GetDocument())
2269 dlg.CenterOnParent()
2270 finished = False
2271 while not finished:
2272 if dlg.ShowModal() == wx.ID_OK:
2273 if hasattr(dlg, "_appInfoCtrl") and dlg._appInfoCtrl._grid.IsCellEditControlShown(): # for Linux
2274 dlg._appInfoCtrl._grid.DisableCellEditControl() # If editor is still active, force it to finish the edit before setting the new model.
2275
2276 homeDir = dlg._homeDirCtrl.GetValue()
2277 if homeDir:
2278 if homeDir == ProjectPropertiesDialog.RELATIVE_TO_PROJECT_FILE:
2279 homeDir = None
2280 if homeDir and not os.path.isdir(homeDir):
2281 wx.MessageBox(_("Home Dir '%s' does not exist. Please specify a valid directory.") % homeDir,
2282 _("Project Properties"),
2283 wx.OK | wx.ICON_EXCLAMATION)
2284 else:
2285 if self.GetDocument().GetModel()._homeDir != homeDir: # don't set it if it hasn't changed
2286 self.GetDocument().GetModel().homeDir = homeDir
2287 self.GetDocument().Modify(True)
2288 finished = True
2289 else:
2290 wx.MessageBox(_("Blank Home Dir. Please specify a valid directory."),
2291 _("Project Properties"),
2292 wx.OK | wx.ICON_EXCLAMATION)
2293 else: # ID_CANCEL
2294 finished = True
2295 dlg.Destroy()
2296
2297
2298 def OnAddFolder(self, event):
2299 if self.GetDocument():
2300 items = self._treeCtrl.GetSelections()
2301 if items:
2302 item = items[0]
2303 if self._IsItemFile(item):
2304 item = self._treeCtrl.GetItemParent(item)
2305
2306 folderDir = self._GetItemFolderPath(item)
2307 else:
2308 folderDir = ""
2309
2310 if folderDir:
2311 folderDir += "/"
2312 folderPath = _("%sUntitled") % folderDir
2313 i = 1
2314 while self._treeCtrl.FindFolder(folderPath):
2315 i += 1
2316 folderPath = _("%sUntitled%s") % (folderDir, i)
2317 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFolderCommand(self, self.GetDocument(), folderPath))
2318
2319 self._treeCtrl.UnselectAll()
2320 item = self._treeCtrl.FindFolder(folderPath)
2321 self._treeCtrl.SelectItem(item)
2322 self._treeCtrl.EnsureVisible(item)
2323 self.OnRename()
2324
2325
2326 def AddFolder(self, folderPath):
2327 self._treeCtrl.AddFolder(folderPath)
2328 return True
2329
2330
2331 def DeleteFolder(self, folderPath):
2332 item = self._treeCtrl.FindFolder(folderPath)
2333 self._treeCtrl.Delete(item)
2334 return True
2335
2336
2337 def OnAddFileToProject(self, event):
2338 if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
2339 descr = ''
2340 for temp in self.GetDocumentManager()._templates:
2341 if temp.IsVisible():
2342 if len(descr) > 0:
2343 descr = descr + _('|')
2344 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
2345 descr = _("All|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
2346 else:
2347 descr = _("*.*")
2348
2349 dialog = wx.FileDialog(self.GetFrame(), _("Add Files"), wildcard=descr, style=wx.OPEN|wx.HIDE_READONLY|wx.MULTIPLE|wx.CHANGE_DIR)
2350 # dialog.CenterOnParent() # wxBug: caused crash with wx.FileDialog
2351 if dialog.ShowModal() != wx.ID_OK:
2352 dialog.Destroy()
2353 return
2354 paths = dialog.GetPaths()
2355 dialog.Destroy()
2356 if len(paths):
2357
2358 folderPath = None
2359 if self.GetMode() == ProjectView.LOGICAL_MODE:
2360 selections = self._treeCtrl.GetSelections()
2361 if selections:
2362 item = selections[0]
2363 if not self._IsItemFile(item):
2364 folderPath = self._GetItemFolderPath(item)
2365
2366 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), paths, folderPath=folderPath))
2367 self.Activate() # after add, should put focus on project editor
2368
2369
2370 def OnAddDirToProject(self, event):
2371 frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Add Directory Files to Project"), size= (320,200))
2372 contentSizer = wx.BoxSizer(wx.VERTICAL)
2373
2374 flexGridSizer = wx.FlexGridSizer(cols = 2, vgap=HALF_SPACE, hgap=HALF_SPACE)
2375 flexGridSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER_VERTICAL, 0)
2376 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
2377 dirCtrl = wx.TextCtrl(frame, -1, os.path.dirname(self.GetDocument().GetFilename()), size=(250,-1))
2378 dirCtrl.SetToolTipString(dirCtrl.GetValue())
2379 lineSizer.Add(dirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
2380 findDirButton = wx.Button(frame, -1, _("Browse..."))
2381 lineSizer.Add(findDirButton, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, HALF_SPACE)
2382 flexGridSizer.Add(lineSizer, 1, wx.EXPAND)
2383
2384 def OnBrowseButton(event):
2385 dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
2386 dir = dirCtrl.GetValue()
2387 if len(dir):
2388 dlg.SetPath(dir)
2389 dlg.CenterOnParent()
2390 if dlg.ShowModal() == wx.ID_OK:
2391 dirCtrl.SetValue(dlg.GetPath())
2392 dirCtrl.SetToolTipString(dirCtrl.GetValue())
2393 dirCtrl.SetInsertionPointEnd()
2394 dlg.Destroy()
2395 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
2396
2397 visibleTemplates = []
2398 for template in self.GetDocumentManager()._templates:
2399 if template.IsVisible():
2400 visibleTemplates.append(template)
2401
2402 choices = []
2403 descr = ''
2404 for template in visibleTemplates:
2405 if len(descr) > 0:
2406 descr = descr + _('|')
2407 descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
2408 choices.append(descr)
2409 choices.insert(0, _("All")) # first item
2410 filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
2411 filterChoice.SetSelection(0)
2412 filterChoice.SetToolTipString(_("Select file type filter."))
2413 flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL)
2414 flexGridSizer.Add(filterChoice, 1, wx.EXPAND)
2415
2416 contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE)
2417
2418 subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories"))
2419 subfolderCtrl.SetValue(True)
2420 contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE)
2421
2422 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
2423 findBtn = wx.Button(frame, wx.ID_OK, _("Add"))
2424 findBtn.SetDefault()
2425 buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE)
2426 buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
2427 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
2428
2429 frame.SetSizer(contentSizer)
2430 frame.Fit()
2431
2432 frame.CenterOnParent()
2433 status = frame.ShowModal()
2434
2435 passedCheck = False
2436 while status == wx.ID_OK and not passedCheck:
2437 if not os.path.exists(dirCtrl.GetValue()):
2438 dlg = wx.MessageDialog(frame,
2439 _("'%s' does not exist.") % dirCtrl.GetValue(),
2440 _("Find in Directory"),
2441 wx.OK | wx.ICON_EXCLAMATION
2442 )
2443 dlg.CenterOnParent()
2444 dlg.ShowModal()
2445 dlg.Destroy()
2446
2447 status = frame.ShowModal()
2448 else:
2449 passedCheck = True
2450
2451 frame.Destroy()
2452
2453 if status == wx.ID_OK:
2454 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
2455
2456 try:
2457 doc = self.GetDocument()
2458 searchSubfolders = subfolderCtrl.IsChecked()
2459 dirString = dirCtrl.GetValue()
2460
2461 if os.path.isfile(dirString):
2462 # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
2463 # We'll assume they know what they're doing.
2464 paths = [dirString]
2465 else:
2466 paths = []
2467
2468 index = filterChoice.GetSelection()
2469 lastIndex = filterChoice.GetCount()-1
2470 if index and index != lastIndex: # if not All or Any
2471 template = visibleTemplates[index-1]
2472
2473 # do search in files on disk
2474 for root, dirs, files in os.walk(dirString):
2475 if not searchSubfolders and root != dirString:
2476 break
2477
2478 for name in files:
2479 if index == 0: # All
2480 filename = os.path.join(root, name)
2481 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
2482 if not doc.IsFileInProject(filename):
2483 paths.append(filename)
2484 else: # use selected filter
2485 if template.FileMatchesTemplate(name):
2486 filename = os.path.join(root, name)
2487 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
2488 if not doc.IsFileInProject(filename):
2489 paths.append(filename)
2490
2491 folderPath = None
2492 if self.GetMode() == ProjectView.LOGICAL_MODE:
2493 selections = self._treeCtrl.GetSelections()
2494 if selections:
2495 item = selections[0]
2496 if not self._IsItemFile(item):
2497 folderPath = self._GetItemFolderPath(item)
2498
2499 doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths, folderPath=folderPath))
2500 self.Activate() # after add, should put focus on project editor
2501
2502 finally:
2503 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
2504
2505
2506 def DoAddFilesToProject(self, filePaths, folderPath):
2507 # method used by Drag-n-Drop to add files to current Project
2508 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filePaths, folderPath))
2509
2510
2511 def OnFocus(self, event):
2512 self.GetDocumentManager().ActivateView(self)
2513 event.Skip()
2514
2515
2516 def OnKillFocus(self, event):
2517 # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame
2518 # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame
2519 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
2520 topWindow = wx.GetApp().GetTopWindow()
2521 # wxBug: On Mac, this event can fire during shutdown, even after GetTopWindow()
2522 # is set to NULL. So make sure we have a TLW before getting the active child.
2523 if topWindow:
2524 childFrame = topWindow.GetActiveChild()
2525 if childFrame:
2526 childFrame.Activate()
2527 event.Skip()
2528
2529
2530 def OnLeftClick(self, event):
2531 """
2532 wxBug: We also spurious drag events on a single click of on item that is already selected,
2533 so the solution was to consume the left click event. But his broke the single click expand/collapse
2534 of a folder, so if it is a folder, we do an event.Skip() to allow the expand/collapse,
2535 otherwise we consume the event.
2536 """
2537 # if folder let it collapse/expand
2538 if wx.Platform == '__WXMSW__':
2539 item, flags = self._treeCtrl.HitTest(event.GetPosition())
2540 if item.IsOk() and self._treeCtrl.GetChildrenCount(item, False):
2541 event.Skip()
2542 else:
2543 event.Skip()
2544
2545 def OnRightClick(self, event):
2546 self.Activate()
2547 if not self.GetSelectedProject():
2548 return
2549 menu = wx.Menu()
2550 if self._HasFilesSelected(): # Files context
2551 menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection"))
2552 menu.Enable(ProjectService.OPEN_SELECTION_ID, True)
2553 wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection)
2554
2555 extService = wx.GetApp().GetService(ExtensionService.ExtensionService)
2556 if extService and extService.GetExtensions():
2557 firstItem = True
2558 for ext in extService.GetExtensions():
2559 if not ext.opOnSelectedFile:
2560 continue
2561 if firstItem:
2562 menu.AppendSeparator()
2563 firstItem = False
2564 menu.Append(ext.id, ext.menuItemName)
2565 wx.EVT_MENU(self._GetParentFrame(), ext.id, extService.ProcessEvent)
2566 wx.EVT_UPDATE_UI(self._GetParentFrame(), ext.id, extService.ProcessUpdateUIEvent)
2567
2568 itemIDs = [None]
2569 for item in self._treeCtrl.GetSelections():
2570 if self._IsItemProcessModelFile(item):
2571 itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None]
2572 break
2573 else: # Project context
2574 itemIDs = []
2575 menuBar = self._GetParentFrame().GetMenuBar()
2576 itemIDs = itemIDs + [ProjectService.ADD_FILES_TO_PROJECT_ID, ProjectService.ADD_DIR_FILES_TO_PROJECT_ID, ProjectService.ADD_FOLDER_ID, ProjectService.REMOVE_FROM_PROJECT, None, ProjectService.CLOSE_PROJECT_ID, ProjectService.DELETE_PROJECT_ID, None, ProjectService.PROJECT_PROPERTIES_ID]
2577 svnIDs = [SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
2578 if SVN_INSTALLED:
2579 itemIDs = itemIDs + [None, SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
2580 globalIDs = [wx.ID_UNDO, wx.ID_REDO, wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS]
2581 itemIDs = itemIDs + [None, wx.ID_UNDO, wx.ID_REDO, None, wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE, wx.ID_CLEAR, None, wx.ID_SELECTALL, ProjectService.RENAME_ID, ProjectService.DELETE_FILE_ID, None, wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID]
2582 for itemID in itemIDs:
2583 if not itemID:
2584 menu.AppendSeparator()
2585 else:
2586 if itemID == ProjectService.RUN_SELECTED_PM_ID and not ACTIVEGRID_BASE_IDE:
2587 webBrowserService = wx.GetApp().GetService(WebBrowserService.WebBrowserService)
2588 if webBrowserService:
2589 if wx.Platform == '__WXMSW__':
2590 menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
2591 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.ProjectServiceProcessEvent)
2592
2593 if wx.Platform == '__WXMSW__':
2594 menuLabel = _("Run Process in External Browser")
2595 else:
2596 menuLabel = _("Run Process")
2597 menu.Append(ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, menuLabel)
2598 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, self.ProjectServiceProcessEvent)
2599
2600 if wx.Platform == '__WXMSW__':
2601
2602 if wx.GetApp().GetUseTabbedMDI():
2603 menuLabel = _("Run Process in new Tab")
2604 else:
2605 menuLabel = _("Run Process in new Window")
2606 menu.Append(ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, menuLabel)
2607 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, self.ProjectServiceProcessEvent)
2608
2609 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
2610 menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
2611 wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
2612 wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
2613 else:
2614 item = menuBar.FindItemById(itemID)
2615 if item:
2616 if SVN_INSTALLED:
2617 svnService = wx.GetApp().GetService(SVNService.SVNService)
2618
2619 if itemID in svnIDs:
2620 if SVN_INSTALLED and svnService:
2621 wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent)
2622 elif itemID in globalIDs:
2623 pass
2624 else:
2625 wx.EVT_MENU(self._treeCtrl, itemID, self.ProcessEvent)
2626 menu.Append(itemID, item.GetLabel())
2627 self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY()))
2628 menu.Destroy()
2629
2630
2631 def ProjectServiceProcessEvent(self, event):
2632 projectService = wx.GetApp().GetService(ProjectService)
2633 if projectService:
2634 projectService.ProcessEvent(event)
2635
2636
2637 def OnRename(self, event=None):
2638 items = self._treeCtrl.GetSelections()
2639 if not items:
2640 return
2641 item = items[0]
2642 if wx.Platform == "__WXGTK__":
2643 dlg = wx.TextEntryDialog(self.GetFrame(), _("Enter New Name"), _("Enter New Name"))
2644 dlg.CenterOnParent()
2645 if dlg.ShowModal() == wx.ID_OK:
2646 text = dlg.GetValue()
2647 self.ChangeLabel(item, text)
2648 else:
2649 if items:
2650 self._treeCtrl.EditLabel(item)
2651
2652
2653 def OnBeginLabelEdit(self, event):
2654 self._editingSoDontKillFocus = True
2655 item = event.GetItem()
2656 if self._IsItemFile(item):
2657 file = self._GetItemFile(item)
2658 if file.type == 'xform':
2659 event.Veto()
2660 if (self.GetMode() == ProjectView.PHYSICAL_MODE) and not self._IsItemFile(item):
2661 event.Veto()
2662
2663
2664 def OnEndLabelEdit(self, event):
2665 self._editingSoDontKillFocus = False
2666 item = event.GetItem()
2667 newName = event.GetLabel()
2668 if not self.ChangeLabel(item, newName):
2669 event.Veto()
2670
2671
2672 def ChangeLabel(self, item, newName):
2673 if not newName:
2674 return False
2675 if self._IsItemFile(item):
2676 oldFilePath = self._GetItemFilePath(item)
2677 newFilePath = os.path.join(os.path.dirname(oldFilePath), newName)
2678 doc = self.GetDocument()
2679 if not doc.GetCommandProcessor().Submit(ProjectRenameFileCommand(doc, oldFilePath, newFilePath)):
2680 return False
2681 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
2682 else:
2683 oldFolderPath = self._GetItemFolderPath(item)
2684 newFolderPath = os.path.dirname(oldFolderPath)
2685 if newFolderPath:
2686 newFolderPath += "/"
2687 newFolderPath += newName
2688 if self._treeCtrl.FindFolder(newFolderPath):
2689 wx.MessageBox(_("Folder '%s' already exists.") % newName,
2690 "Rename Folder",
2691 wx.OK | wx.ICON_EXCLAMATION,
2692 self.GetFrame())
2693 return False
2694 doc = self.GetDocument()
2695 if not doc.GetCommandProcessor().Submit(ProjectRenameFolderCommand(doc, oldFolderPath, newFolderPath)):
2696 return False
2697 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
2698
2699 return True
2700
2701
2702 def CanPaste(self):
2703 # wxBug: Should be able to use IsSupported/IsSupportedFormat here
2704 #fileDataObject = wx.FileDataObject()
2705 #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
2706 hasFilesInClipboard = False
2707 if not wx.TheClipboard.IsOpened():
2708 if wx.TheClipboard.Open():
2709 fileDataObject = wx.FileDataObject()
2710 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
2711 wx.TheClipboard.Close()
2712 return hasFilesInClipboard
2713
2714
2715 def OnCut(self, event):
2716 self.OnCopy(event)
2717 self.OnClear(event)
2718
2719
2720 def OnCopy(self, event):
2721 fileDataObject = wx.FileDataObject()
2722 items = self._treeCtrl.GetSelections()
2723 for item in items:
2724 filePath = self._GetItemFilePath(item)
2725 if filePath:
2726 fileDataObject.AddFile(filePath)
2727 if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open():
2728 wx.TheClipboard.SetData(fileDataObject)
2729 wx.TheClipboard.Close()
2730
2731
2732 def OnPaste(self, event):
2733 if wx.TheClipboard.Open():
2734 fileDataObject = wx.FileDataObject()
2735 if wx.TheClipboard.GetData(fileDataObject):
2736 folderPath = None
2737 if self.GetMode() == ProjectView.LOGICAL_MODE:
2738 items = self._treeCtrl.GetSelections()
2739 if items:
2740 item = items[0]
2741 if item:
2742 folderPath = self._GetItemFolderPath(item)
2743 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames(), folderPath))
2744 wx.TheClipboard.Close()
2745
2746
2747 def OnClear(self, event):
2748 if self._HasFilesSelected():
2749 items = self._treeCtrl.GetSelections()
2750 files = []
2751 for item in items:
2752 file = self._GetItemFile(item)
2753 if file:
2754 files.append(file)
2755 self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(self.GetDocument(), files))
2756
2757 elif self._HasFoldersSelected():
2758 items = self._treeCtrl.GetSelections()
2759 item = items[0]
2760 if self._treeCtrl.GetChildrenCount(item, False):
2761 wx.MessageBox(_("Cannot remove folder '%s'. Folder is not empty.") % self._treeCtrl.GetItemText(item),
2762 _("Remove Folder"),
2763 wx.OK | wx.ICON_EXCLAMATION,
2764 self.GetFrame())
2765 return
2766
2767 folderPath = self._GetItemFolderPath(item)
2768 self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFolderCommand(self, self.GetDocument(), folderPath))
2769
2770
2771 def OnDeleteFile(self, event):
2772 yesNoMsg = wx.MessageDialog(self.GetFrame(),
2773 _("Delete cannot be reversed.\n\nRemove the selected files from the\nproject and file system permanently?"),
2774 _("Delete File"),
2775 wx.YES_NO|wx.ICON_QUESTION)
2776 yesNoMsg.CenterOnParent()
2777 status = yesNoMsg.ShowModal()
2778 yesNoMsg.Destroy()
2779 if status == wx.ID_NO:
2780 return
2781
2782 items = self._treeCtrl.GetSelections()
2783 delFiles = []
2784 for item in items:
2785 filePath = self._GetItemFilePath(item)
2786 if filePath and filePath not in delFiles:
2787 delFiles.append(filePath)
2788
2789 # remove selected files from project
2790 self.GetDocument().RemoveFiles(delFiles)
2791
2792 # remove selected files from file system
2793 for filePath in delFiles:
2794 if os.path.exists(filePath):
2795 try:
2796 os.remove(filePath)
2797 except:
2798 wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filePath), sys.exc_value),
2799 _("Delete File"),
2800 wx.OK | wx.ICON_EXCLAMATION,
2801 self.GetFrame())
2802
2803 def OnDeleteProject(self, event=None, noPrompt=False, closeFiles=True, delFiles=True):
2804
2805 class DeleteProjectDialog(wx.Dialog):
2806
2807 def __init__(self, parent, doc):
2808 wx.Dialog.__init__(self, parent, -1, _("Delete Project"), size = (310, 330))
2809
2810 sizer = wx.BoxSizer(wx.VERTICAL)
2811 sizer.Add(wx.StaticText(self, -1, _("Delete cannot be reversed.\nDeleted files are removed from the file system permanently.\n\nThe project file '%s' will be closed and deleted.") % os.path.basename(doc.GetFilename())), 0, wx.ALL, SPACE)
2812 self._delFilesCtrl = wx.CheckBox(self, -1, _("Delete all files in project"))
2813 self._delFilesCtrl.SetValue(True)
2814 self._delFilesCtrl.SetToolTipString(_("Deletes files from disk, whether open or closed"))
2815 sizer.Add(self._delFilesCtrl, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM, SPACE)
2816 self._closeDeletedCtrl = wx.CheckBox(self, -1, _("Close open files belonging to project"))
2817 self._closeDeletedCtrl.SetValue(True)
2818 self._closeDeletedCtrl.SetToolTipString(_("Closes open editors for files belonging to project"))
2819 sizer.Add(self._closeDeletedCtrl, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM, SPACE)
2820
2821 sizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.LEFT|wx.BOTTOM, SPACE)
2822
2823 self.SetSizer(sizer)
2824 sizer.Fit(self)
2825 self.Layout()
2826
2827 doc = self.GetDocument()
2828 if not noPrompt:
2829 dlg = DeleteProjectDialog(self.GetFrame(), doc)
2830 dlg.CenterOnParent()
2831 status = dlg.ShowModal()
2832 delFiles = dlg._delFilesCtrl.GetValue()
2833 closeFiles = dlg._closeDeletedCtrl.GetValue()
2834 dlg.Destroy()
2835 if status == wx.ID_CANCEL:
2836 return
2837
2838 if closeFiles or delFiles:
2839 filesInProject = doc.GetFiles()
2840 deploymentFilePath = self.GetDocument().GetDeploymentFilepath()
2841 if deploymentFilePath:
2842 filesInProject.append(deploymentFilePath) # remove deployment file also.
2843 import activegrid.server.secutils as secutils
2844 keystoreFilePath = os.path.join(os.path.dirname(deploymentFilePath), secutils.AGKEYSTORE_FILENAME)
2845 filesInProject.append(keystoreFilePath) # remove keystore file also.
2846
2847 # don't remove self prematurely
2848 filePath = doc.GetFilename()
2849 if filePath in filesInProject:
2850 filesInProject.remove(filePath)
2851
2852 # don't close/delete files outside of project's directory
2853 homeDir = doc.GetModel().homeDir + os.sep
2854 for filePath in filesInProject[:]:
2855 fileDir = os.path.dirname(filePath) + os.sep
2856 if not fileDir.startswith(homeDir):
2857 filesInProject.remove(filePath)
2858
2859 if closeFiles:
2860 # close any open views of documents in the project
2861 openDocs = self.GetDocumentManager().GetDocuments()[:] # need copy or docs shift when closed
2862 for d in openDocs:
2863 if d.GetFilename() in filesInProject:
2864 d.Modify(False) # make sure it doesn't ask to save the file
2865 if isinstance(d.GetDocumentTemplate(), ProjectTemplate): # if project, remove from project list drop down
2866 if self.GetDocumentManager().CloseDocument(d, True):
2867 self.RemoveProjectUpdate(d)
2868 else: # regular file
2869 self.GetDocumentManager().CloseDocument(d, True)
2870
2871 # remove files in project from file system
2872 if delFiles:
2873 dirPaths = []
2874 for filePath in filesInProject:
2875 if os.path.isfile(filePath):
2876 try:
2877 dirPath = os.path.dirname(filePath)
2878 if dirPath not in dirPaths:
2879 dirPaths.append(dirPath)
2880
2881 os.remove(filePath)
2882 except:
2883 wx.MessageBox("Could not delete file '%s'.\n%s" % (filePath, sys.exc_value),
2884 _("Delete Project"),
2885 wx.OK | wx.ICON_EXCLAMATION,
2886 self.GetFrame())
2887
2888 filePath = doc.GetFilename()
2889
2890 self.ClearFolderState() # remove from registry folder settings
2891
2892 # close project
2893 if doc:
2894 doc.Modify(False) # make sure it doesn't ask to save the project
2895 if self.GetDocumentManager().CloseDocument(doc, True):
2896 self.RemoveCurrentDocumentUpdate()
2897
2898 # remove project file
2899 if delFiles:
2900 dirPath = os.path.dirname(filePath)
2901 if dirPath not in dirPaths:
2902 dirPaths.append(dirPath)
2903 if os.path.isfile(filePath):
2904 try:
2905 os.remove(filePath)
2906 except:
2907 wx.MessageBox("Could not delete project file '%s'.\n%s" % (filePath, sys.exc_value),
2908 _("Delete Prjoect"),
2909 wx.OK | wx.ICON_EXCLAMATION,
2910 self.GetFrame())
2911
2912 # remove empty directories from file system
2913 if delFiles:
2914 dirPaths.sort() # sorting puts parent directories ahead of child directories
2915 dirPaths.reverse() # remove child directories first
2916
2917 for dirPath in dirPaths:
2918 if os.path.isdir(dirPath):
2919 files = os.listdir(dirPath)
2920 if not files:
2921 try:
2922 os.rmdir(dirPath)
2923 except:
2924 wx.MessageBox("Could not delete empty directory '%s'.\n%s" % (dirPath, sys.exc_value),
2925 _("Delete Project"),
2926 wx.OK | wx.ICON_EXCLAMATION,
2927 self.GetFrame())
2928
2929
2930 def OnKeyPressed(self, event):
2931 key = event.KeyCode()
2932 if key == wx.WXK_DELETE:
2933 self.OnClear(event)
2934 else:
2935 event.Skip()
2936
2937
2938 def OnSelectAll(self, event):
2939 project = self.GetDocument()
2940 if project:
2941 self.DoSelectAll(self._treeCtrl.GetRootItem())
2942
2943
2944 def DoSelectAll(self, parentItem):
2945 (child, cookie) = self._treeCtrl.GetFirstChild(parentItem)
2946 while child.IsOk():
2947 if self._IsItemFile(child):
2948 self._treeCtrl.SelectItem(child)
2949 else:
2950 self.DoSelectAll(child)
2951 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
2952
2953
2954 def OnOpenSelectionSDI(self, event):
2955 # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window
2956 wx.CallAfter(self.OnOpenSelection, None)
2957
2958
2959 def OnOpenSelection(self, event):
2960 doc = None
2961 try:
2962 items = self._treeCtrl.GetSelections()[:]
2963 for item in items:
2964 filepath = self._GetItemFilePath(item)
2965 if filepath:
2966 if not os.path.exists(filepath):
2967 msgTitle = wx.GetApp().GetAppName()
2968 if not msgTitle:
2969 msgTitle = _("File Not Found")
2970 yesNoMsg = wx.MessageDialog(self.GetFrame(),
2971 _("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)),
2972 msgTitle,
2973 wx.YES_NO|wx.ICON_QUESTION
2974 )
2975 yesNoMsg.CenterOnParent()
2976 status = yesNoMsg.ShowModal()
2977 yesNoMsg.Destroy()
2978 if status == wx.ID_NO:
2979 continue
2980 findFileDlg = wx.FileDialog(self.GetFrame(),
2981 _("Choose a file"),
2982 defaultFile=wx.lib.docview.FileNameFromPath(filepath),
2983 style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR
2984 )
2985 # findFileDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
2986 if findFileDlg.ShowModal() == wx.ID_OK:
2987 newpath = findFileDlg.GetPath()
2988 else:
2989 newpath = None
2990 findFileDlg.Destroy()
2991 if newpath:
2992 # update Project Model with new location
2993 self.GetDocument().UpdateFilePath(filepath, newpath)
2994 filepath = newpath
2995
2996 doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
2997 if not doc and filepath.endswith(PROJECT_EXTENSION): # project already open
2998 self.SetProject(filepath)
2999 elif doc:
3000 AddProjectMapping(doc)
3001
3002
3003 except IOError, (code, message):
3004 msgTitle = wx.GetApp().GetAppName()
3005 if not msgTitle:
3006 msgTitle = _("File Error")
3007 wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath),
3008 msgTitle,
3009 wx.OK | wx.ICON_EXCLAMATION,
3010 self.GetFrame())
3011
3012
3013 #----------------------------------------------------------------------------
3014 # Convenience methods
3015 #----------------------------------------------------------------------------
3016
3017 def _HasFiles(self):
3018 if not self._treeCtrl:
3019 return False
3020 return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files
3021
3022
3023 def _HasFilesSelected(self):
3024 if not self._treeCtrl:
3025 return False
3026 items = self._treeCtrl.GetSelections()
3027 if not items:
3028 return False
3029 for item in items:
3030 if self._IsItemFile(item):
3031 return True
3032 return False
3033
3034
3035 def _HasFoldersSelected(self):
3036 if not self._treeCtrl:
3037 return False
3038 items = self._treeCtrl.GetSelections()
3039 if not items:
3040 return False
3041 for item in items:
3042 if self._IsItemFile(item):
3043 return False
3044 return True
3045
3046
3047 def _MakeProjectName(self, project):
3048 return project.GetPrintableName()
3049
3050
3051 def _GetItemFilePath(self, item):
3052 file = self._GetItemFile(item)
3053 if file:
3054 return file.filePath
3055 else:
3056 return None
3057
3058
3059 def _GetItemFolderPath(self, item):
3060 rootItem = self._treeCtrl.GetRootItem()
3061 if item == rootItem:
3062 return ""
3063
3064 if self._IsItemFile(item):
3065 item = self._treeCtrl.GetItemParent(item)
3066
3067 folderPath = ""
3068 while item != rootItem:
3069 if folderPath:
3070 folderPath = self._treeCtrl.GetItemText(item) + "/" + folderPath
3071 else:
3072 folderPath = self._treeCtrl.GetItemText(item)
3073 item = self._treeCtrl.GetItemParent(item)
3074
3075 return folderPath
3076
3077
3078 def _GetItemFile(self, item):
3079 return self._treeCtrl.GetPyData(item)
3080
3081
3082 def _IsItemFile(self, item):
3083 return self._GetItemFile(item) != None
3084
3085
3086 def _IsItemProcessModelFile(self, item):
3087 if ACTIVEGRID_BASE_IDE:
3088 return False
3089
3090 if self._IsItemFile(item):
3091 filepath = self._GetItemFilePath(item)
3092 ext = None
3093 for template in self.GetDocumentManager().GetTemplates():
3094 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
3095 ext = template.GetDefaultExtension()
3096 break;
3097 if not ext:
3098 return False
3099
3100 if filepath.endswith(ext):
3101 return True
3102
3103 return False
3104
3105
3106 def _GetChildItems(self, parentItem):
3107 children = []
3108 (child, cookie) = self._treeCtrl.GetFirstChild(parentItem)
3109 while child.IsOk():
3110 children.append(child)
3111 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
3112 return children
3113
3114
3115 def _GetFolderItems(self, parentItem):
3116 folderItems = []
3117 childrenItems = self._GetChildItems(parentItem)
3118 for childItem in childrenItems:
3119 if not self._IsItemFile(childItem):
3120 folderItems.append(childItem)
3121 folderItems += self._GetFolderItems(childItem)
3122 return folderItems
3123
3124
3125 class ProjectFileDropTarget(wx.FileDropTarget):
3126
3127 def __init__(self, view):
3128 wx.FileDropTarget.__init__(self)
3129 self._view = view
3130
3131
3132 def OnDropFiles(self, x, y, filePaths):
3133 """ Do actual work of dropping files into project """
3134 if self._view.GetDocument():
3135 folderPath = None
3136 if self._view.GetMode() == ProjectView.LOGICAL_MODE:
3137 folderItem = self._view._treeCtrl.FindClosestFolder(x,y)
3138 if folderItem:
3139 folderPath = self._view._GetItemFolderPath(folderItem)
3140 self._view.DoAddFilesToProject(filePaths, folderPath)
3141 return True
3142 return False
3143
3144
3145 def OnDragOver(self, x, y, default):
3146 """ Feedback to show copy cursor if copy is allowed """
3147 if self._view.GetDocument(): # only allow drop if project exists
3148 return wx.DragCopy
3149 return wx.DragNone
3150
3151
3152 class ProjectPropertiesDialog(wx.Dialog):
3153 RELATIVE_TO_PROJECT_FILE = _("relative to project file")
3154
3155 def __init__(self, parent, document):
3156 wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330))
3157
3158 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
3159
3160 notebook = wx.Notebook(self, -1)
3161
3162 tab = wx.Panel(notebook, -1)
3163 gridSizer = wx.FlexGridSizer(cols = 2, vgap = SPACE, hgap = SPACE)
3164 gridSizer.AddGrowableCol(1)
3165 gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")))
3166 filename = document.GetFilename()
3167 if os.path.isfile(filename):
3168 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]))
3169
3170 gridSizer.Add(wx.StaticText(tab, -1, _("Location:")))
3171 gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.dirname(filename), length=50)))
3172
3173 gridSizer.Add(wx.StaticText(tab, -1, _("Size:")))
3174 gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")))
3175
3176 lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
3177 lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND)
3178 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP)
3179
3180 lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
3181 lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND)
3182 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP)
3183
3184 gridSizer.Add(wx.StaticText(tab, -1, _("Created:")))
3185 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))))
3186
3187 gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")))
3188 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))))
3189
3190 gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")))
3191 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))))
3192 else:
3193 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")))
3194 spacerGrid = wx.BoxSizer(wx.HORIZONTAL) # add a border around the inside of the tab
3195 spacerGrid.Add(gridSizer, 1, wx.ALL|wx.EXPAND, SPACE);
3196 tab.SetSizer(spacerGrid)
3197 notebook.AddPage(tab, _("General"))
3198
3199 tab = wx.Panel(notebook, -1)
3200 spacerGrid = wx.BoxSizer(wx.VERTICAL) # add a border around the inside of the tab
3201 homePathLabel = wx.StaticText(tab, -1, _("Home Dir:"))
3202 if document.GetModel().isDefaultHomeDir:
3203 defaultHomeDir = ProjectPropertiesDialog.RELATIVE_TO_PROJECT_FILE
3204 else:
3205 defaultHomeDir = document.GetModel().homeDir
3206 self._homeDirCtrl = wx.ComboBox(tab, -1, defaultHomeDir, size=(125,-1), choices=[ProjectPropertiesDialog.RELATIVE_TO_PROJECT_FILE, document.GetModel().homeDir])
3207 self._homeDirCtrl.SetToolTipString(self._homeDirCtrl.GetValue())
3208 if not document.GetModel().isDefaultHomeDir:
3209 self._homeDirCtrl.SetInsertionPointEnd()
3210 def OnDirChanged(event):
3211 self._homeDirCtrl.SetToolTip(wx.ToolTip(self._homeDirCtrl.GetValue())) # wx.Bug: SetToolTipString only sets it for the dropdown control, not for the text edit control, so need to replace it completely
3212 wx.EVT_COMBOBOX(self._homeDirCtrl, -1, OnDirChanged)
3213 wx.EVT_TEXT(self._homeDirCtrl, -1, OnDirChanged)
3214 choosePathButton = wx.Button(tab, -1, _("Browse..."))
3215 def OnBrowseButton(event):
3216 if self._homeDirCtrl.GetValue() == ProjectPropertiesDialog.RELATIVE_TO_PROJECT_FILE:
3217 defaultHomeDir = document.GetModel().homeDir
3218 else:
3219 defaultHomeDir = self._homeDirCtrl.GetValue()
3220
3221 dlg = wx.DirDialog(self, "Choose a directory:", defaultHomeDir,
3222 style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
3223 if dlg.ShowModal() == wx.ID_OK:
3224 self._homeDirCtrl.SetValue(dlg.GetPath())
3225 self._homeDirCtrl.SetInsertionPointEnd()
3226 self._homeDirCtrl.SetToolTip(wx.ToolTip(dlg.GetPath())) # wx.Bug: SetToolTipString only sets it for the dropdown control, not for the text edit control, so need to replace it completely
3227 dlg.Destroy()
3228 wx.EVT_BUTTON(choosePathButton, -1, OnBrowseButton)
3229 pathSizer = wx.BoxSizer(wx.HORIZONTAL)
3230 pathSizer.Add(homePathLabel, 0, wx.ALIGN_CENTER_VERTICAL)
3231 pathSizer.Add(self._homeDirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.LEFT, HALF_SPACE)
3232 pathSizer.Add(choosePathButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, SPACE)
3233 spacerGrid.Add(pathSizer, 0, wx.ALL|wx.EXPAND, SPACE);
3234 instructionText = wx.StaticText(tab, -1, _("The physical view shows files relative to Home Dir.\nThe Home Dir default is the project file's directory.\nSetting the Home Dir overrides the default directory."))
3235 spacerGrid.Add(instructionText, 0, wx.ALL, SPACE);
3236 tab.SetSizer(spacerGrid)
3237 notebook.AddPage(tab, _("Physical View"))
3238
3239 if wx.Platform == "__WXMSW__":
3240 notebook.SetPageSize((310,300))
3241
3242 if not ACTIVEGRID_BASE_IDE:
3243 tab = wx.Panel(notebook, -1)
3244 self._appInfoCtrl = PropertyService.PropertyCtrl(tab, header=False)
3245 self._appInfoCtrl.SetDocument(document)
3246 self._appInfoCtrl.SetModel(document.GetAppInfo())
3247 sizer = wx.BoxSizer(wx.HORIZONTAL)
3248 sizer.Add(self._appInfoCtrl, 1, wx.EXPAND|wx.ALL, PropertyService.LEAVE_MARGIN)
3249 tab.SetSizer(sizer)
3250 notebook.AddPage(tab, _("App Info"))
3251 self._appInfoCtrl._grid.AutoSizeColumns()
3252
3253
3254 sizer = wx.BoxSizer(wx.VERTICAL)
3255 sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
3256 sizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
3257
3258 self.SetSizer(sizer)
3259 sizer.Fit(self)
3260 self.Layout()
3261
3262
3263 class ProjectOptionsPanel(wx.Panel):
3264
3265
3266 def __init__(self, parent, id):
3267 wx.Panel.__init__(self, parent, id)
3268 self._useSashMessageShown = False
3269 config = wx.ConfigBase_Get()
3270 self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects"))
3271 self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True))
3272 projectBorderSizer = wx.BoxSizer(wx.VERTICAL)
3273 projectSizer = wx.BoxSizer(wx.VERTICAL)
3274 projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
3275 if not ACTIVEGRID_BASE_IDE:
3276 self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
3277 self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog2", True))
3278 projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
3279
3280 sizer = wx.BoxSizer(wx.HORIZONTAL)
3281 sizer.Add(wx.StaticText(self, -1, _("Default language for projects:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, HALF_SPACE)
3282 self._langCtrl = wx.Choice(self, -1, choices=projectmodel.LANGUAGE_LIST)
3283 self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT))
3284 self._langCtrl.SetToolTipString(_("Programming language to be used throughout the project."))
3285 sizer.Add(self._langCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, MAC_RIGHT_BORDER)
3286 projectSizer.Add(sizer, 0, wx.ALL, HALF_SPACE)
3287
3288 projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE)
3289 self.SetSizer(projectBorderSizer)
3290 self.Layout()
3291 parent.AddPage(self, _("Project"))
3292
3293
3294 def OnUseSashSelect(self, event):
3295 if not self._useSashMessageShown:
3296 msgTitle = wx.GetApp().GetAppName()
3297 if not msgTitle:
3298 msgTitle = _("Document Options")
3299 wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.",
3300 msgTitle,
3301 wx.OK | wx.ICON_INFORMATION,
3302 self.GetParent())
3303 self._useSashMessageShown = True
3304
3305
3306 def OnOK(self, optionsDialog):
3307 config = wx.ConfigBase_Get()
3308 config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
3309 if not ACTIVEGRID_BASE_IDE:
3310 config.WriteInt("RunWelcomeDialog2", self._projShowWelcomeCheckBox.GetValue())
3311 config.Write(APP_LAST_LANGUAGE, self._langCtrl.GetStringSelection())
3312
3313
3314 def GetIcon(self):
3315 return getProjectIcon()
3316
3317
3318 class ProjectService(Service.Service):
3319
3320 #----------------------------------------------------------------------------
3321 # Constants
3322 #----------------------------------------------------------------------------
3323 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
3324 RUN_SELECTED_PM_ID = wx.NewId()
3325 RUN_SELECTED_PM_INTERNAL_WINDOW_ID = wx.NewId()
3326 RUN_SELECTED_PM_EXTERNAL_BROWSER_ID = wx.NewId()
3327 RUN_CURRENT_PM_ID = wx.NewId()
3328 RUN_CURRENT_PM_INTERNAL_WINDOW_ID = wx.NewId()
3329 RUN_CURRENT_PM_EXTERNAL_BROWSER_ID = wx.NewId()
3330 RENAME_ID = wx.NewId()
3331 OPEN_SELECTION_ID = wx.NewId()
3332 REMOVE_FROM_PROJECT = wx.NewId()
3333 DELETE_FILE_ID = wx.NewId()
3334 ADD_FILES_TO_PROJECT_ID = wx.NewId()
3335 ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId()
3336 ADD_DIR_FILES_TO_PROJECT_ID = wx.NewId()
3337 CLOSE_PROJECT_ID = wx.NewId()
3338 PROJECT_PROPERTIES_ID = wx.NewId()
3339 ADD_FOLDER_ID = wx.NewId()
3340 DELETE_PROJECT_ID = wx.NewId()
3341
3342
3343 #----------------------------------------------------------------------------
3344 # Overridden methods
3345 #----------------------------------------------------------------------------
3346
3347 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT):
3348 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
3349 self._runHandlers = []
3350 self._suppressOpenProjectMessages = False
3351 self._logicalViewDefaults = []
3352 self._logicalViewOpenDefaults = []
3353 self._fileTypeDefaults = []
3354 self._nameDefaults = []
3355 self._mapToProject = dict()
3356
3357
3358 def _CreateView(self):
3359 return ProjectView(self)
3360
3361
3362 def ShowWindow(self, show = True):
3363 """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """
3364 Service.Service.ShowWindow(self, show)
3365
3366 if show:
3367 project = self.GetView().GetDocument()
3368 if not project:
3369 self.OpenSavedProjects()
3370
3371
3372 #----------------------------------------------------------------------------
3373 # Service specific methods
3374 #----------------------------------------------------------------------------
3375
3376 def GetSuppressOpenProjectMessages(self):
3377 return self._suppressOpenProjectMessages
3378
3379
3380 def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages):
3381 self._suppressOpenProjectMessages = suppressOpenProjectMessages
3382
3383
3384 def GetRunHandlers(self):
3385 return self._runHandlers
3386
3387
3388 def AddRunHandler(self, runHandler):
3389 self._runHandlers.append(runHandler)
3390
3391
3392 def RemoveRunHandler(self, runHandler):
3393 self._runHandlers.remove(runHandler)
3394
3395
3396 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
3397 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
3398
3399 projectMenu = wx.Menu()
3400
3401 ## accelTable = wx.AcceleratorTable([
3402 ## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID"))
3403 ## ])
3404 ## frame.SetAcceleratorTable(accelTable)
3405 isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument
3406 if wx.GetApp().IsMDI() or isProjectDocument:
3407 if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID):
3408 projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project"))
3409 wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent)
3410 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
3411 if not menuBar.FindItemById(ProjectService.ADD_DIR_FILES_TO_PROJECT_ID):
3412 projectMenu.Append(ProjectService.ADD_DIR_FILES_TO_PROJECT_ID, _("Add Directory Files to Project..."), _("Adds a directory's documents to the current project"))
3413 wx.EVT_MENU(frame, ProjectService.ADD_DIR_FILES_TO_PROJECT_ID, frame.ProcessEvent)
3414 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_DIR_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
3415 if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID):
3416 projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project"))
3417 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
3418 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
3419 if not menuBar.FindItemById(ProjectService.ADD_FOLDER_ID):
3420 projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("New Folder"), _("Creates a new folder"))
3421 wx.EVT_MENU(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessEvent)
3422 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessUpdateUIEvent)
3423 if not menuBar.FindItemById(ProjectService.CLOSE_PROJECT_ID):
3424 projectMenu.AppendSeparator()
3425 projectMenu.Append(ProjectService.CLOSE_PROJECT_ID, _("Close Project"), _("Closes currently open project"))
3426 wx.EVT_MENU(frame, ProjectService.CLOSE_PROJECT_ID, frame.ProcessEvent)
3427 wx.EVT_UPDATE_UI(frame, ProjectService.CLOSE_PROJECT_ID, frame.ProcessUpdateUIEvent)
3428 if not menuBar.FindItemById(ProjectService.DELETE_PROJECT_ID):
3429 projectMenu.Append(ProjectService.DELETE_PROJECT_ID, _("Delete Project..."), _("Delete currently open project and its files."))
3430 wx.EVT_MENU(frame, ProjectService.DELETE_PROJECT_ID, frame.ProcessEvent)
3431 wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_PROJECT_ID, frame.ProcessUpdateUIEvent)
3432 if not menuBar.FindItemById(ProjectService.PROJECT_PROPERTIES_ID):
3433 projectMenu.AppendSeparator()
3434 projectMenu.Append(ProjectService.PROJECT_PROPERTIES_ID, _("Project Properties"), _("Project Properties"))
3435 wx.EVT_MENU(frame, ProjectService.PROJECT_PROPERTIES_ID, frame.ProcessEvent)
3436 wx.EVT_UPDATE_UI(frame, ProjectService.PROJECT_PROPERTIES_ID, frame.ProcessUpdateUIEvent)
3437 index = menuBar.FindMenu(_("&Format"))
3438 if index == -1:
3439 index = menuBar.FindMenu(_("&View"))
3440 menuBar.Insert(index + 1, projectMenu, _("&Project"))
3441 editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
3442 if not menuBar.FindItemById(ProjectService.RENAME_ID):
3443 editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item"))
3444 wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent)
3445 wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent)
3446 if not menuBar.FindItemById(ProjectService.DELETE_FILE_ID):
3447 editMenu.Append(ProjectService.DELETE_FILE_ID, _("Delete File"), _("Delete the file from the project and file system."))
3448 wx.EVT_MENU(frame, ProjectService.DELETE_FILE_ID, frame.ProcessEvent)
3449 wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_FILE_ID, frame.ProcessUpdateUIEvent)
3450
3451 return True
3452
3453
3454 def OnCloseFrame(self, event):
3455 if not self.GetView():
3456 return True
3457
3458 if wx.GetApp().IsMDI():
3459 # close all non-project documents first
3460 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
3461 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
3462 if not self.GetDocumentManager().CloseDocument(document, False):
3463 return False
3464
3465 # write project config afterwards because user may change filenames on closing of new documents
3466 self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere
3467
3468 # close all project documents after closing other documents
3469 # because user may save a new document with a new name or cancel closing a document
3470 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
3471 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3472 if not document.OnSaveModified():
3473 return False
3474
3475 # This is called when any SDI frame is closed, so need to check if message window is closing or some other window
3476 elif self.GetView() == event.GetEventObject().GetView():
3477 self.SetView(None)
3478 return True
3479
3480
3481 #----------------------------------------------------------------------------
3482 # Document Manager Methods
3483 #----------------------------------------------------------------------------
3484
3485 def FindProjectFromMapping(self, key):
3486 """ Find which project a model or document belongs to """
3487 return self._mapToProject.get(key)
3488
3489
3490 def AddProjectMapping(self, key, projectDoc=None):
3491 """ Generate a mapping from model or document to project. If no project given, use current project.
3492 e.g. Which project does this model or document belong to (when it was opened)?
3493 """
3494 if not projectDoc:
3495 projectDoc = self.GetCurrentProject()
3496 self._mapToProject[key] = projectDoc
3497
3498
3499 def RemoveProjectMapping(self, key):
3500 """ Remove mapping from model or document to project. """
3501 if self._mapToProject.has_key(key):
3502 del self._mapToProject[key]
3503
3504
3505 #----------------------------------------------------------------------------
3506 # Default Logical View Folder Methods
3507 #----------------------------------------------------------------------------
3508
3509 def AddLogicalViewFolderDefault(self, pattern, folder):
3510 self._logicalViewDefaults.append((pattern, folder))
3511
3512
3513 def FindLogicalViewFolderDefault(self, filename):
3514 for (pattern, folder) in self._logicalViewDefaults:
3515 if filename.endswith(pattern):
3516 return folder
3517 return None
3518
3519
3520 def AddLogicalViewFolderCollapsedDefault(self, folderName, collapsed=True):
3521 # default is collapsed, don't add to list if collapse is True
3522 if not collapsed:
3523 self._logicalViewOpenDefaults.append(folderName)
3524
3525
3526 def FindLogicalViewFolderCollapsedDefault(self, folderName):
3527 if folderName in self._logicalViewOpenDefaults:
3528 return False
3529 return True
3530
3531
3532 #----------------------------------------------------------------------------
3533 # Default File Type Methods
3534 #----------------------------------------------------------------------------
3535
3536 def AddFileTypeDefault(self, pattern, type):
3537 self._fileTypeDefaults.append((pattern, type))
3538
3539
3540 def FindFileTypeDefault(self, filename):
3541 for (pattern, type) in self._fileTypeDefaults:
3542 if filename.endswith(pattern):
3543 return type
3544 return None
3545
3546
3547 #----------------------------------------------------------------------------
3548 # Default Name Methods
3549 #----------------------------------------------------------------------------
3550
3551 def AddNameDefault(self, pattern, method):
3552 self._nameDefaults.append((pattern, method))
3553
3554
3555 def FindNameDefault(self, filename):
3556 for (pattern, method) in self._nameDefaults:
3557 if filename.endswith(pattern):
3558 return method(filename)
3559 return None
3560
3561
3562 def GetDefaultNameCallback(self, filename):
3563 """ A method for generating name from filepath for Project Service """
3564 return os.path.splitext(os.path.basename(filename))[0]
3565
3566
3567 #----------------------------------------------------------------------------
3568 # Event Processing Methods
3569 #----------------------------------------------------------------------------
3570
3571 def ProcessEventBeforeWindows(self, event):
3572 id = event.GetId()
3573
3574 if id == wx.ID_CLOSE_ALL:
3575 self.OnFileCloseAll(event)
3576 return True
3577 return False
3578
3579
3580 def ProcessUpdateUIEventBeforeWindows(self, event):
3581 id = event.GetId()
3582
3583 if id == wx.ID_CLOSE_ALL:
3584 for document in self.GetDocumentManager().GetDocuments():
3585 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
3586 event.Enable(True)
3587 return True
3588
3589 event.Enable(False)
3590 return True
3591
3592 elif id == wx.ID_CLOSE:
3593 # "File | Close" is too confusing and hard to determine whether user wants to close a viewed file or the current project.
3594 # Disallow "File | Close" if project is current document or active in project view.
3595 # User must explicitly close project via "Project | Close Current Project".
3596 document = self.GetDocumentManager().GetCurrentDocument()
3597 if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3598 event.Enable(False)
3599 return True
3600 if self.GetView().ProcessUpdateUIEvent(event):
3601 return True
3602
3603 return False
3604
3605
3606 def ProcessEvent(self, event):
3607 if Service.Service.ProcessEvent(self, event):
3608 return True
3609
3610 id = event.GetId()
3611 if id == ProjectService.RUN_SELECTED_PM_ID:
3612 self.OnRunProcessModel(event, runSelected=True)
3613 return True
3614 elif id == ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID:
3615 self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceInternal=True)
3616 return True
3617 elif id == ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID:
3618 self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceExternal=True)
3619 return True
3620 elif id == ProjectService.RUN_CURRENT_PM_ID:
3621 self.OnRunProcessModel(event, runCurrentFile=True)
3622 return True
3623 elif id == ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID:
3624 self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceInternal=True)
3625 return True
3626 elif id == ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID:
3627 self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceExternal=True)
3628 return True
3629 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
3630 self.OnAddCurrentFileToProject(event)
3631 return True
3632 elif (id == ProjectService.PROJECT_PROPERTIES_ID
3633 or id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID
3634 or id == ProjectService.ADD_FOLDER_ID
3635 or id == ProjectService.DELETE_PROJECT_ID
3636 or id == ProjectService.CLOSE_PROJECT_ID):
3637 if self.GetView():
3638 return self.GetView().ProcessEvent(event)
3639 else:
3640 return False
3641 else:
3642 return False
3643
3644
3645 def ProcessUpdateUIEvent(self, event):
3646 if Service.Service.ProcessUpdateUIEvent(self, event):
3647 return True
3648
3649 id = event.GetId()
3650 if id in [ProjectService.RUN_SELECTED_PM_ID,
3651 ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID,
3652 ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID,
3653 ProjectService.RUN_CURRENT_PM_ID,
3654 ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID,
3655 ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID]:
3656 event.Enable(True)
3657 return True
3658 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
3659 event.Enable(self._CanAddCurrentFileToProject())
3660 return True
3661 elif id in [ProjectService.ADD_FILES_TO_PROJECT_ID,
3662 ProjectService.ADD_DIR_FILES_TO_PROJECT_ID,
3663 ProjectService.RENAME_ID,
3664 ProjectService.OPEN_SELECTION_ID,
3665 ProjectService.DELETE_FILE_ID]:
3666 event.Enable(False)
3667 return True
3668 elif id == ProjectService.PROJECT_PROPERTIES_ID:
3669 event.Enable(self._HasOpenedProjects())
3670 return True
3671 elif id in [wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID,
3672 ProjectService.ADD_FOLDER_ID,
3673 ProjectService.DELETE_PROJECT_ID,
3674 ProjectService.CLOSE_PROJECT_ID]:
3675 if self.GetView():
3676 return self.GetView().ProcessUpdateUIEvent(event)
3677 else:
3678 return False
3679 else:
3680 return False
3681
3682
3683 def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False, newWindow=False, forceExternal=False, forceInternal=False):
3684 project = self.GetCurrentProject()
3685
3686 if runCurrentFile:
3687 doc = self.GetDocumentManager().GetCurrentDocument()
3688 if not doc or not hasattr(doc, "GetFilename"):
3689 return
3690 fileToRun = doc.GetFilename()
3691 projects = self.FindProjectByFile(fileToRun)
3692 if not projects:
3693 return
3694 elif project in projects:
3695 # use current project
3696 pass
3697 elif len(projects) == 1:
3698 # only one project, display it
3699 project = projects[0]
3700 self.GetView().SetProject(project.GetFilename())
3701 elif len(projects) > 1:
3702 strings = map(lambda file: os.path.basename(file.GetFilename()), projects)
3703 res = wx.GetSingleChoiceIndex(_("More than one project uses '%s'. Select project to run:") % os.path.basename(fileToRun),
3704 _("Select Project"),
3705 strings,
3706 self.GetView()._GetParentFrame())
3707 if res == -1:
3708 return
3709 project = projects[res]
3710 self.GetView().SetProject(project.GetFilename())
3711
3712 if project:
3713 ext = None
3714 for template in self.GetDocumentManager().GetTemplates():
3715 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
3716 ext = template.GetDefaultExtension()
3717 break;
3718 if not ext:
3719 return
3720
3721 files = filter(lambda f: f.endswith(ext), project.GetFiles())
3722 if not files:
3723 return
3724
3725 docs = wx.GetApp().GetDocumentManager().GetDocuments()
3726
3727 filesModified = False
3728 for doc in docs:
3729 if doc.IsModified():
3730 filesModified = True
3731 break
3732 if filesModified:
3733 frame = self.GetView().GetFrame()
3734 yesNoMsg = wx.MessageDialog(frame,
3735 _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"),
3736 _("Run Process"),
3737 wx.YES_NO|wx.ICON_QUESTION
3738 )
3739 yesNoMsg.CenterOnParent()
3740 status = yesNoMsg.ShowModal()
3741 yesNoMsg.Destroy()
3742 if status == wx.ID_YES:
3743 wx.GetTopLevelParent(frame).OnFileSaveAll(None)
3744
3745 if runCurrentFile:
3746 fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename()
3747 elif runSelected:
3748 fileToRun = self.GetView().GetSelectedFile()
3749 elif len(files) > 1:
3750 files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower()))
3751 strings = map(lambda file: os.path.basename(file), files)
3752 res = wx.GetSingleChoiceIndex(_("Select a process to run:"),
3753 _("Run"),
3754 strings,
3755 self.GetView()._GetParentFrame())
3756 if res == -1:
3757 return
3758 fileToRun = files[res]
3759 else:
3760 fileToRun = files[0]
3761
3762 try:
3763 deployFilePath = project.GenerateDeployment()
3764 except DataServiceExistenceException, e:
3765 dataSourceName = str(e)
3766 self.PromptForMissingDataSource(dataSourceName)
3767 return
3768 self.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath, newWindow, forceExternal, forceInternal)
3769
3770
3771 def RunProcessModel(self, fileToRun, language, deployFilePath, newWindow=True, forceExternal=False, forceInternal=False):
3772 for runHandler in self.GetRunHandlers():
3773 if runHandler.RunProjectFile(fileToRun, language, deployFilePath, newWindow, forceExternal, forceInternal):
3774 return
3775 os.system('"' + fileToRun + '"')
3776
3777
3778 def _HasProcessModel(self):
3779 project = self.GetView().GetDocument()
3780
3781 if project:
3782 ext = None
3783 for template in self.GetDocumentManager().GetTemplates():
3784 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
3785 ext = template.GetDefaultExtension()
3786 break;
3787 if not ext:
3788 return False
3789
3790 files = filter(lambda f: f.endswith(ext), project.GetFiles())
3791 if not files:
3792 return False
3793
3794 if len(files):
3795 return True
3796
3797 return False
3798
3799
3800 def _HasOpenedProjects(self):
3801 for document in self.GetDocumentManager().GetDocuments():
3802 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3803 return True
3804 return False
3805
3806
3807 def _CanAddCurrentFileToProject(self):
3808 currentDoc = self.GetDocumentManager().GetCurrentDocument()
3809 if not currentDoc:
3810 return False
3811 if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3812 return False
3813 if not currentDoc._savedYet:
3814 return False
3815 if self.GetView().GetDocument(): # a project is open
3816 return True
3817 return False # There are no documents open
3818
3819
3820 def GetFilesFromCurrentProject(self):
3821 view = self.GetView()
3822 if view:
3823 project = view.GetDocument()
3824 if project:
3825 return project.GetFiles()
3826 return None
3827
3828
3829 def GetCurrentProject(self):
3830 view = self.GetView()
3831 if view:
3832 return view.GetDocument()
3833 return None
3834
3835
3836 def GetOpenProjects(self):
3837 retval = []
3838 for document in self.GetDocumentManager().GetDocuments():
3839 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3840 retval.append(document)
3841 return retval
3842
3843
3844 def FindProjectByFile(self, filename):
3845 retval = []
3846 for document in self.GetDocumentManager().GetDocuments():
3847 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3848 if document.GetFilename() == filename:
3849 retval.append(document)
3850 elif document.IsFileInProject(filename):
3851 retval.append(document)
3852
3853 # make sure current project is first in list
3854 currProject = self.GetCurrentProject()
3855 if currProject and currProject in retval:
3856 retval.remove(currProject)
3857 retval.insert(0, currProject)
3858
3859 return retval
3860
3861
3862 def OnAddCurrentFileToProject(self, event):
3863 doc = self.GetDocumentManager().GetCurrentDocument()
3864 file = doc.GetFilename()
3865 projectDoc = self.GetView().GetDocument()
3866 projectDoc.GetCommandProcessor().Submit(ProjectAddFilesCommand(projectDoc, [file]))
3867
3868 AddProjectMapping(doc, projectDoc)
3869
3870 self.GetView().Activate() # after add, should put focus on project editor
3871
3872
3873 def OnFileCloseAll(self, event):
3874 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
3875 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
3876 if not self.GetDocumentManager().CloseDocument(document, False):
3877 return
3878 # document.DeleteAllViews() # Implicitly delete the document when the last view is removed
3879
3880
3881 def OpenSavedProjects(self):
3882 config = wx.ConfigBase_Get()
3883 openedDocs = False
3884 if config.ReadInt("ProjectSaveDocs", True):
3885 docString = config.Read("ProjectSavedDocs")
3886 if docString:
3887 doc = None
3888 docList = eval(docString)
3889 self.GetView()._treeCtrl.Freeze()
3890
3891 for fileName in docList:
3892 if isinstance(fileName, types.StringTypes):
3893 if os.path.exists(fileName):
3894 doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
3895 self.GetView()._treeCtrl.Thaw()
3896
3897 if doc:
3898 openedDocs = True
3899
3900 currProject = config.Read("ProjectCurrent")
3901 if currProject in docList:
3902 self.GetView().SetProject(currProject)
3903
3904 return openedDocs
3905
3906
3907 def PromptForMissingDataSource(self, dataSourceName):
3908 prompt = "A required Data Source '%s' was not found. The process cannot be run without this Data Source.\n\nWould you like to configure this Data Source now?" % dataSourceName
3909 msgTitle = "Unknown Data Source"
3910 dataSourceMissingDlg = wx.MessageDialog(self.GetView().GetFrame(), prompt, msgTitle, wx.YES_NO|wx.ICON_QUESTION)
3911 dataSourceMissingDlg.CenterOnParent()
3912 if dataSourceMissingDlg.ShowModal() == wx.ID_YES:
3913 dataSourceMissingDlg.Destroy()
3914 self._AddDataSource(dataSourceName)
3915 else:
3916 dataSourceMissingDlg.Destroy()
3917
3918
3919 def _AddDataSource(self, defaultDataSourceName=None):
3920 dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
3921 dsChoices = dataSourceService.getDataSourceNames()
3922 dlg = DataModelEditor.AddDataSourceDialog(self.GetView().GetFrame(), 'Add Data Source', dsChoices, defaultDataSourceName)
3923 dlg.CenterOnParent()
3924 if dlg.ShowModal() == wx.ID_OK:
3925 dataSource = dlg.GetDataSource()
3926 dlg.Destroy()
3927 else:
3928 dlg.Destroy()
3929 return False
3930 if (dataSource == None):
3931 wx.MessageBox(_("Error getting data source."), self._title)
3932 dataSourceService.updateDataSource(dataSource)
3933 if ((dsChoices == None) or (len(dsChoices) <= 0)):
3934 wx.ConfigBase_Get().Write(DataModelEditor.SchemaOptionsPanel.DEFAULT_DATASOURCE_KEY, dataSource.name)
3935 dataSourceService.save()
3936 return True
3937
3938
3939 #----------------------------------------------------------------------------
3940 # Icon Bitmaps - generated by encode_bitmaps.py
3941 #----------------------------------------------------------------------------
3942 from wx import ImageFromStream, BitmapFromImage
3943 import cStringIO
3944
3945
3946 def getProjectData():
3947 return \
3948 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
3949 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
3950 \x00\x01\x89IDAT8\x8d\xa5\x92\xcdJ\x02Q\x18\x86\x9fq&-+H\xd40\x033Bha\x05\
3951 \xfd\xac*[\xb7l\xd1\xae\xfbhQ7\x10\x04A]\x86\xd0&\xba\x01CW\n!B\xa2\x882\x8b\
3952 )R+"\x7fft\x9a\x16\x91\xcd4\xd3\x0f\xf4\xee\xce\xf9\xde\xf7\xe1\xfd\x0eG\x10\
3953 \\"\x9arb\xe8\xcf\x1a\x9d\x9e\n\x80\xd6\xad\x03\x10Z;\x13\xf8ER\xa7xd\x88\
3954 \xbe-D\x1f\xb8\xbf\x0c\xaf\xcf\x15C\xd2k\xf4\xc5(\x92^\x03 \xbe\x9b\xb3@\x85\
3955 n\xe9\xd8h\xde\xe6\x1d\xe9\xfe\xa9E\xc7\xfb\x91\xf9\xfd\x01D\xfa\xc9\xd8\xf7\
3956 \xcdPI\'\x01X\xd8>@p\xf7\x00($W\x8c\x8f&R\xa7\xa7\xa2u\xebL.\xef\xd9\x00\x97\
3957 \xa7\x87D\\er\x15\x95\xb9\xf5\x12\xa3\x81Y\x9bG\xfax0\xb3Z\x8d*\x95t\x92z\
3958 \xb5\x80yjhC\x83\x16\x96\x15\xdc\xc3AZ\x8d\xea{XN#g.,\xa6\xe0l\x9c\xde}\x89\
3959 \xb6\xc3\x9aR\xff\xe5\x01\x801}\x1c\x80\x9b\xcc\x05\xde\xb0\x9f\xd0t\x04oX\
3960 \xa6\xad4\xc9U\n\xc0&\x1e\xfd\xd6\x0e\x18\xd4Se\x00\xbca?m\xa5\xc9\x1d\xd0V\
3961 \x9a\x03\xa3\xd6\xadc\xa8\x8fv\xc0S\xa3H\xc8\x13\x01\xa2\x00\xc4V\x13\x94\
3962 \xb3)\xae\xae\x14\x8b\xd1\x17\x90laK\x03\xb3b\xab\t&\x02\xf7(\xf94\xf2k\x8c\
3963 \x8d\x8dy\xc7\xf0\xb7\x00\x80`t\x92`t\x87%\xa0\x9cM\xd1\xa8}\xce\xcc\xbf\xd1\
3964 \x11P\xce\xa6,\xe7\xaf\xdf\xd7,Ap\x89\x14\x92+\xc6_\x03\x8e\x80\xff\xc8\xf5\
3965 \xaf4\xf0\x06=\xf3\x8fJr]C\xd9\x00\x00\x00\x00IEND\xaeB`\x82'
3966
3967 def getProjectBitmap():
3968 return BitmapFromImage(getProjectImage())
3969
3970 def getProjectImage():
3971 stream = cStringIO.StringIO(getProjectData())
3972 return ImageFromStream(stream)
3973
3974 def getProjectIcon():
3975 return wx.IconFromBitmap(getProjectBitmap())
3976
3977
3978 #----------------------------------------------------------------------------
3979
3980 def getBlankData():
3981 return \
3982 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
3983 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
3984 \x00\x01\x04IDAT8\x8d\xa5\x93\xbdj\x02A\x10\xc7\x7f{gme\xe5c\xe4\t\x82\x85\
3985 \x85\x85oa\xe5+\xd8Z\xd8'e\xfa\x80\xd8\xd8X\x19R\xc4\x07\x90\x04\xd1J\x08\
3986 \x17\x0cr\\V\xe1\xe4\xfc\x80\xb58\xf7\xd8\xbd\x0f\xa280\xec\xec2\xbf\xff\xce\
3987 \xcc\xb2B8.\xf7X\xc9\xdc|L\x97J\xc7\xbe\x0c\x01\xf0\xd6\x01\x00RFtZu\x91Q\
3988 \x10\x8e\x9b\xf8\xe4\xf3[-w*\xf1\xafm\xec\xcf\x83\x89\x1a\xad\x94\xea\xbe\
3989 \x8c\x95\x99/\x1c\x17\xe7\xdaR\xcb%xh\xd4hw_\x95yn\xb5\xe0\xcb\x90\xea%\x0eO\
3990 \xf1\xba\xd9\xc7\xe5\xbf\x0f\xdfX]\xda)\x140A\r\x03<6klO\xf0w\x84~\xef\xc9\
3991 \xca/lA\xc3@\x02\xe7\x99U\x81\xb7\x0e\xa8\xec\xed\x04\x13\xde\x1c\xfe\x11\
3992 \x902\xb2@\xc8\xc2\x8b\xd9\xbcX\xc0\x045\xac\xc1 Jg\xe6\x08\xe8)\xa7o\xd5\
3993 \xb0\xbf\xcb\nd\x86x\x0b\x9c+p\x0b\x0c\xa9\x16~\xbc_\xeb\x9d\xd3\x03\xcb3q\
3994 \xefo\xbc\xfa/\x14\xd9\x19\x1f\xfb\x8aa\x87\xf2\xf7\x16\x00\x00\x00\x00IEND\
3995 \xaeB`\x82"
3996
3997
3998 def getBlankBitmap():
3999 return BitmapFromImage(getBlankImage())
4000
4001 def getBlankImage():
4002 stream = cStringIO.StringIO(getBlankData())
4003 return ImageFromStream(stream)
4004
4005 def getBlankIcon():
4006 return wx.IconFromBitmap(getBlankBitmap())
4007
4008
4009 #----------------------------------------------------------------------
4010 def getFolderClosedData():
4011 return \
4012 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4013 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4014 \x00\x00\xffIDAT8\x8d\xa5\x93?N\x02A\x14\x87\xbf\x19&\x10B\xb4A\x0b*iL0!$\
4015 \x06\x0f\xe0\x05lH\x88G\xe1\x0c\xdbx\x11ZN`,\xa5\x01\x8aM\xa4\x80\x84\xc4Fc\
4016 \xd0\xe8\xb0\xae\xbbc\x01\x0b,\x19\x16X~\xd5\x9b\xf7\xe7\x9by3o\x84\x90\x19\
4017 \x8e\x91\x8a\x0c\xed:\x06\xc0\xf7g\x00x\xde\x14\x80\xf3\x9b\x07\xb1\x13\xa0]\
4018 \xc7d\xcbw\x00d\x17\x81\x82\xff\x01\xc0\xb0\xd3\x9f\x83\x7f\xf5\xb2\xe8\xaa\
4019 \xf1\xb4\x84\n!3h\xd71\xef\xaf=\xeb\x0e\xc5R\xcd\xea\xcfWZ"\xd6\xc2\xb6\xc4\
4020 \xdc\xe5\xad\xd5?h\xd7M\xb5\xd9\x15\n\xe6}{\xde\x94\xe2\xf5\xbd59I\x12V\x17\
4021 \x96F\n \xfc\xfbD\xaaS\xc2\x9fI:@\x041\xdf\xa3\x8d\xb0Y\xb3\xed\xaf\xa9\x00\
4022 \xbe\xde\xc6\x9c\x9c]\x10\xea\xc3O #\xc3\xd7:)/\x19\xb0>$\x87J\x01\x04\xc1n\
4023 \xc0\xcb\xf3cl]mv\xe3\x83\xb4o\xc1\xa6D\xf4\x1b\x07\xed\xba\xd9\xa7`+ \xad\
4024 \xfe\x01\xd1\x03SV!\xfbHa\x00\x00\x00\x00IEND\xaeB`\x82'
4025
4026 def getFolderClosedBitmap():
4027 return BitmapFromImage(getFolderClosedImage())
4028
4029 def getFolderClosedImage():
4030 stream = cStringIO.StringIO(getFolderClosedData())
4031 return ImageFromStream(stream)
4032
4033 def getFolderClosedIcon():
4034 return wx.IconFromBitmap(getFolderClosedBitmap())
4035
4036
4037 #----------------------------------------------------------------------
4038 def getFolderOpenData():
4039 return \
4040 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4041 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4042 \x00\x01>IDAT8\x8d\xa5\x93\xbdJ\x03A\x14\x85\xbfY\x03i\xac\x14\x92\xc2F\xad$\
4043 Z\xa4\x10\x11|\x01\xc1J\xdbt\xbe\x86\x9d\x85\x0f\xa0\xe0\x1b\x04,l\xc4J\x0b\
4044 \x0bA;a\x11\x13\xb1H\xc2\xc2\xca\x84@\x88n\xb2\xd9?\xcd\xd8d6.\x9b\x104\xa7\
4045 \xbas\xef=g\xce\x9d\xe1\na\xcc1\x0b\x8c\x99\xd8@F\x07_\xd6\xb9\n\xdd\x8f\xb8\
4046 \xd0s\x9a\x00\xe4\xb6O\xc5T\x81~\xf5D\x89\xdc\x0e\xd9_\x85,\xa0\xa2\x06\xefw\
4047 R\x01\x04\x9e\x03\xc0\xea\xde\x8dH\th\xa8\xa81:\xf8\x1e\x00\xf9\x8d\x03\x00\
4048 \xa4U\x07\xc0,\xdb\xaaX\xaa\xc4"\x99\x04\xd9\xf7\xe0\xfbs$\x12\x0e\x90\xad\
4049 \x0e\x00]\xeb*N\x9b\xe5u\x05P,UD\xc2\x81&K\xbb\r@\xd4\xba\x1f\x9a\xe9\xb0\
4050 \xb6\x7f\x96h}\xbe8\x1c9\xe89M\x16\xfc\x15\xa4\xdd\xc6\xe8\x9a\x18\xc3\x99\
4051 \x97w\x8f\x99\x86\xd8\x81\xb4\xea\x18]\x93\xfcf).\x0e\\9\x96\xf4r}\x84~\x87\
4052 \xc4\x08\x81\xe7\xa0\xfa\xb5\xa9\xb7\xa6\x1c\xf4\xdao\xcc/B\x04\x0c<\xfb\xef\
4053 \x02Zd\xa9P\x98\xd8\xf8\xfax\x1b\xc7\xa9o\xf4\xbdN\x8aP{z \x0c\xdc\xb1\xa4\
4054 \xdf\x10z\x99\xaa\x97[J\'\xc3\xc0\x9dH\x98(\xf0_\xcc\xbc\x8d?\xf2)\x7f\x8e|f\
4055 \xe54\x00\x00\x00\x00IEND\xaeB`\x82'
4056
4057 def getFolderOpenBitmap():
4058 return BitmapFromImage(getFolderOpenImage())
4059
4060 def getFolderOpenImage():
4061 stream = cStringIO.StringIO(getFolderOpenData())
4062 return ImageFromStream(stream)
4063
4064 def getFolderOpenIcon():
4065 return wx.IconFromBitmap(getFolderOpenBitmap())
4066
4067
4068 #----------------------------------------------------------------------
4069 def getLogicalModeOnData():
4070 return \
4071 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4072 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4073 \x00\x01\x83IDAT8\x8d\xa5\x93\xcbJ\xc3@\x14\x86\xbfI\x83buS\xabE+TE\x04\x17\
4074 \xde\xf0\x02\x82\xa0k\x17\n.\xdc\xf9\x1e.\xf4\x05\\\t\xfa\x18\x057\xe2\x0b\
4075 \x08ue@\xa4`\xb0\x84J\xd0(M\xa3"\xb65\x8d5.jcbS\x14\xfdW3\xe7\xfc\xe7\x9b9\
4076 \xc3\x19!\xa4\x08\xff\x91\xdcXT\x8d=\xb7\xf6\\\xa5\xe2\xd8\xf5\xfd\xab\t@\
4077 \xdf\xfc\x81\xf8\x11PQw\xddHl\x99H\x0c\xda\xbe\x19\xce\x0f\r\x17@\xae]{\xb1\
4078 \xf1\r\xc5\x83\n!E\xa8\xa8\xbb\xaeuw\x11zB\xbc\x7f24\xde1\xb6%\x02-\xb42\xbe\
4079 \xc5\x06\xd12i\x00&V\xb6\x11m\x0e\x00\xd9\xf4\xac;\xbe\xa1\x88z\x0b\x8eM\xf5\
4080 \xd5$1\xb3\xd9\x048\xde\xdf!%\xe5P4\x9b\x91\xc5+:{\x86\x03y\x19\xbe\x1e\xcc\
4081 \xafR1\x8f\x96Ic\xe6\xb34g\xbf\x01\xfcE\x00%=\x83~z\xd4dv\nW\x94\xc2\x00o/\
4082 \x0f\xc8]\xdd\xb4\xd7\xee\x00\xb8<="\x9a\x8c\xd37\x90"\x9a\xd4Qo\xba1\xf3Y\
4083 \x00\xcf\x13z\x03\xd7\xd6\x01\x88&\xe3\x00\xdc\xdf\xea\x94\r\x8b\x94da~\xb6\
4084 \xea\xda\x8f\x01\x80\x04\xf0TT\x91\x9d\x1b/8:\xb7D\xd9\xb0(\x1b\x16\x8af\xa3\
4085 h\xf5\xe1\x8a\xf5\x04\xcek\xbe\x81_Sk\xeb\x98\xd7\x05\xf4\xf7\x02\x00\x0b\
4086 \xd3\x89P_K\x00@\xefP\x82\xd5\xa1za\xee\xec\x84\xa7\xa2\xea\xe5\x1a\xd3\xd8\
4087 \x12\x90;;\t\xec\xfd\xe3\xeb\x97h\xfc\xc6lz\xd6\xfdMAK\xc0_\xf5\x01\xf4\x01\
4088 \x91\xdc\xfe\x86\x9e^\x00\x00\x00\x00IEND\xaeB`\x82'
4089
4090 def getLogicalModeOnBitmap():
4091 return BitmapFromImage(getLogicalModeOnImage())
4092
4093 def getLogicalModeOnImage():
4094 stream = cStringIO.StringIO(getLogicalModeOnData())
4095 return ImageFromStream(stream)
4096
4097 #----------------------------------------------------------------------
4098 def getLogicalModeOffData():
4099 return \
4100 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4101 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4102 \x00\x01\x83IDAT8\x8d\xa5\x93\xcbJ\xc3@\x14\x86\xbfI\x83buS\xabE+TE\x04\x17\
4103 \xde\xf0\x02\x82\xa0k\x17\n.\xdc\xf9\x1e.\xf4\x05\\\t\xfa\x18\x057\xe2\x0b\
4104 \x08ue@\xa4`\xb0\x84J\xd0(M\xa3"\xb65\x8d5.jcbS\x14\xfdW3\xe7\xfc\xe7\x9b9\
4105 \xc3\x19!\xa4\x08\xff\x91\xdcXT\x8d=\xb7\xf6\\\xa5\xe2\xd8\xf5\xfd\xab\t@\
4106 \xdf\xfc\x81\xf8\x11PQw\xddHl\x99H\x0c\xda\xbe\x19\xce\x0f\r\x17@\xae]{\xb1\
4107 \xf1\r\xc5\x83\n!E\xa8\xa8\xbb\xaeuw\x11zB\xbc\x7f24\xde1\xb6%\x02-\xb42\xbe\
4108 \xc5\x06\xd12i\x00&V\xb6\x11m\x0e\x00\xd9\xf4\xac;\xbe\xa1\x88z\x0b\x8eM\xf5\
4109 \xd5$1\xb3\xd9\x048\xde\xdf!%\xe5P4\x9b\x91\xc5+:{\x86\x03y\x19\xbe\x1e\xcc\
4110 \xafR1\x8f\x96Ic\xe6\xb34g\xbf\x01\xfcE\x00%=\x83~z\xd4dv\nW\x94\xc2\x00o/\
4111 \x0f\xc8]\xdd\xb4\xd7\xee\x00\xb8<="\x9a\x8c\xd37\x90"\x9a\xd4Qo\xba1\xf3Y\
4112 \x00\xcf\x13z\x03\xd7\xd6\x01\x88&\xe3\x00\xdc\xdf\xea\x94\r\x8b\x94da~\xb6\
4113 \xea\xda\x8f\x01\x80\x04\xf0TT\x91\x9d\x1b/8:\xb7D\xd9\xb0(\x1b\x16\x8af\xa3\
4114 h\xf5\xe1\x8a\xf5\x04\xcek\xbe\x81_Sk\xeb\x98\xd7\x05\xf4\xf7\x02\x00\x0b\
4115 \xd3\x89P_K\x00@\xefP\x82\xd5\xa1za\xee\xec\x84\xa7\xa2\xea\xe5\x1a\xd3\xd8\
4116 \x12\x90;;\t\xec\xfd\xe3\xeb\x97h\xfc\xc6lz\xd6\xfdMAK\xc0_\xf5\x01\xf4\x01\
4117 \x91\xdc\xfe\x86\x9e^\x00\x00\x00\x00IEND\xaeB`\x82'
4118
4119 def getLogicalModeOffBitmap():
4120 return BitmapFromImage(getLogicalModeOffImage())
4121
4122 def getLogicalModeOffImage():
4123 stream = cStringIO.StringIO(getLogicalModeOffData())
4124 return ImageFromStream(stream)
4125
4126 #----------------------------------------------------------------------
4127 def getPhysicalModeOnData():
4128 return \
4129 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4130 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4131 \x00\x01\xabIDAT8\x8d}\x931k\xdb@\x18\x86\x9f\xb3=\x98R\xb0\x06\xc7X\x01\x1d\
4132 \x14\x1c\xeaA4?\xa0\xa1\x8b\x9d\x04C\xe6N\xed\xd8\xad\xbf\xc0\xbf!c\xb6@\x9d\
4133 \xa1\xf4\'\xd4m\xd2l\x9dJ(\xb8R\x87\x90\x84\x80\xaeD\x8e\xad\xc1\xeePBIQ\x87\
4134 \x8b.:+\xc9\x0b\x82\xef\xee\xd3\xf3\xde{\x1f\x9c\x10\xa52\xf7)\x99N\xd2q\x1c\
4135 [{\xfe\xb3U\x91_\x8bE\x83E\xa8\xe9\xba\xa6\x1e\xc71*Rx\xd2\xa3\xe9\xba\xd4\
4136 \x97\x1a\xa2\x92L\'i\xd6\xbc\x0bZ\xecy\xd2CE\n\x15)\x00*Y\xf3!hQ\x9e\xf4\xf8\
4137 vt\xa4\r\xf2\xf0}\x90L|\xae\x93\xdb\xf5E;4uEE\xca\x184]\xd72\x91\x89\x0f\xc0\
4138 \xe3\xf6\xaee\xf8\xe7\x83\xcf\x06\x00e\xc4`o/\r\x83\x80\x96\xf4x\xf9\xea\xb5\
4139 I"\x13\xbf\x00ZJF\\\xec\xef >}\x1c\xa6\x00\x07\x87_hI\x8f\x17\x9d.*R<\x7f\
4140 \xd43\xffZF7\xa0\xb9\xc2\xf9\xc91OV\x9e\xb2\xde\xe9Z\x07\\\'\xe0\xacip\xf6\
4141 \xf5\xcdm\xfc\x08\x967\xde\xeaY\xec\xef\xe8!\x9e\x9f\x1c\x03\xf0[\xfe\x85\
4142 \xa8\x98\xd6Y\xdb\x85d\xa4\xeb60>\x03\xe0\xe7!\x94N#E\xb5\xe6P\xad9\x06\x88\
4143 \'\x97\x85\xfb\xea\xe1\x9c\x198Si\xbd\xd3%\x0c\x02\xae\xe63\x1a\xf3\x86\x15\
4144 \xd5\x82\xf3\x9a^\xea\x0f(\xf5\xb6\xb6D\xbf\xdf\xa7Zs\x08\x83\x00\x80\xab\
4145 \xf9\xac\x08g\'O\xedt\x15\x80\xfaRC\x00\x84?F\xe9\xbb\xc1\x80\x96\xf4t\xb7\
4146 \xbezw\x82\x9c\n\x8f)\xaf_\xdb\xffR\xb8\x99z.\xc1\xc1\xfb\xef\x00l\x0e\xcb\
4147 \xe2A\x83L\x9f{\xda(\xd3\xe6\xb0l\x9e\xf4\x7f\x85\x1d\xb2s\xbf\x8c\xaeh\x00\
4148 \x00\x00\x00IEND\xaeB`\x82'
4149
4150 def getPhysicalModeOnBitmap():
4151 return BitmapFromImage(getPhysicalModeOnImage())
4152
4153 def getPhysicalModeOnImage():
4154 stream = cStringIO.StringIO(getPhysicalModeOnData())
4155 return ImageFromStream(stream)
4156
4157 #----------------------------------------------------------------------
4158 def getPhysicalModeOffData():
4159 return \
4160 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
4161 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
4162 \x00\x01\xabIDAT8\x8d}\x931k\xdb@\x18\x86\x9f\xb3=\x98R\xb0\x06\xc7X\x01\x1d\
4163 \x14\x1c\xeaA4?\xa0\xa1\x8b\x9d\x04C\xe6N\xed\xd8\xad\xbf\xc0\xbf!c\xb6@\x9d\
4164 \xa1\xf4\'\xd4m\xd2l\x9dJ(\xb8R\x87\x90\x84\x80\xaeD\x8e\xad\xc1\xeePBIQ\x87\
4165 \x8b.:+\xc9\x0b\x82\xef\xee\xd3\xf3\xde{\x1f\x9c\x10\xa52\xf7)\x99N\xd2q\x1c\
4166 [{\xfe\xb3U\x91_\x8bE\x83E\xa8\xe9\xba\xa6\x1e\xc71*Rx\xd2\xa3\xe9\xba\xd4\
4167 \x97\x1a\xa2\x92L\'i\xd6\xbc\x0bZ\xecy\xd2CE\n\x15)\x00*Y\xf3!hQ\x9e\xf4\xf8\
4168 vt\xa4\r\xf2\xf0}\x90L|\xae\x93\xdb\xf5E;4uEE\xca\x184]\xd72\x91\x89\x0f\xc0\
4169 \xe3\xf6\xaee\xf8\xe7\x83\xcf\x06\x00e\xc4`o/\r\x83\x80\x96\xf4x\xf9\xea\xb5\
4170 I"\x13\xbf\x00ZJF\\\xec\xef >}\x1c\xa6\x00\x07\x87_hI\x8f\x17\x9d.*R<\x7f\
4171 \xd43\xffZF7\xa0\xb9\xc2\xf9\xc91OV\x9e\xb2\xde\xe9Z\x07\\\'\xe0\xacip\xf6\
4172 \xf5\xcdm\xfc\x08\x967\xde\xeaY\xec\xef\xe8!\x9e\x9f\x1c\x03\xf0[\xfe\x85\
4173 \xa8\x98\xd6Y\xdb\x85d\xa4\xeb60>\x03\xe0\xe7!\x94N#E\xb5\xe6P\xad9\x06\x88\
4174 \'\x97\x85\xfb\xea\xe1\x9c\x198Si\xbd\xd3%\x0c\x02\xae\xe63\x1a\xf3\x86\x15\
4175 \xd5\x82\xf3\x9a^\xea\x0f(\xf5\xb6\xb6D\xbf\xdf\xa7Zs\x08\x83\x00\x80\xab\
4176 \xf9\xac\x08g\'O\xedt\x15\x80\xfaRC\x00\x84?F\xe9\xbb\xc1\x80\x96\xf4t\xb7\
4177 \xbezw\x82\x9c\n\x8f)\xaf_\xdb\xffR\xb8\x99z.\xc1\xc1\xfb\xef\x00l\x0e\xcb\
4178 \xe2A\x83L\x9f{\xda(\xd3\xe6\xb0l\x9e\xf4\x7f\x85\x1d\xb2s\xbf\x8c\xaeh\x00\
4179 \x00\x00\x00IEND\xaeB`\x82'
4180
4181 def getPhysicalModeOffBitmap():
4182 return BitmapFromImage(getPhysicalModeOffImage())
4183
4184 def getPhysicalModeOffImage():
4185 stream = cStringIO.StringIO(getPhysicalModeOffData())
4186 return ImageFromStream(stream)
4187