]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/ide/activegrid/tool/ProjectEditor.py
removed code inside USE_SIZABLE_CALENDAR, we should allow making the main calendar...
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / ProjectEditor.py
CommitLineData
1f780e48
RD
1#----------------------------------------------------------------------------
2# Name: ProjectEditor.py
3# Purpose: IDE-style Project Editor for wx.lib.pydocview
4#
02b800ce 5# Author: Morgan Hua, Peter Yared
1f780e48
RD
6#
7# Created: 8/15/03
8# CVS-ID: $Id$
aca310e5 9# Copyright: (c) 2003-2006 ActiveGrid, Inc.
1f780e48
RD
10# License: wxWindows License
11#----------------------------------------------------------------------------
12
02b800ce 13import wx
1f780e48
RD
14import wx.lib.docview
15import wx.lib.pydocview
02b800ce 16import wx.lib.buttons
1f780e48 17import Service
02b800ce
RD
18import copy
19import os
20import os.path
21import sets
1f780e48 22import sys
02b800ce
RD
23import time
24import types
25import activegrid.util.appdirs as appdirs
26import activegrid.util.fileutils as fileutils
aca310e5 27import activegrid.util.aglogging as aglogging
1f780e48 28import UICommon
b792147d 29import Wizard
6f1a3f9c 30import SVNService
02b800ce
RD
31import project as projectlib
32import ExtensionService
33
1f780e48
RD
34from IDE import ACTIVEGRID_BASE_IDE
35if not ACTIVEGRID_BASE_IDE:
02b800ce 36 import activegrid.server.deployment as deploymentlib
1f780e48 37 import ProcessModelEditor
02b800ce 38 import DataModelEditor
aca310e5 39 import DeploymentGeneration
02b800ce 40 import WsdlAgEditor
aca310e5 41 import WsdlAgModel
02b800ce
RD
42 APP_LAST_LANGUAGE = "LastLanguage"
43 import activegrid.model.basedocmgr as basedocmgr
44 import activegrid.model.basemodel as basemodel
aca310e5 45 import activegrid.model.projectmodel as projectmodel
02b800ce
RD
46 import PropertyService
47 from activegrid.server.toolsupport import GetTemplate
48 import activegrid.util.xmlutils as xmlutils
49 import activegrid.util.sysutils as sysutils
aca310e5
RD
50 DataServiceExistenceException = DeploymentGeneration.DataServiceExistenceException
51 import WebBrowserService
02b800ce 52
6f1a3f9c 53from SVNService import SVN_INSTALLED
1f780e48
RD
54
55_ = wx.GetTranslation
56
57if wx.Platform == '__WXMSW__':
58 _WINDOWS = True
59else:
60 _WINDOWS = False
02b800ce 61
b792147d
RD
62#----------------------------------------------------------------------------
63# Constants
64#----------------------------------------------------------------------------
65SPACE = 10
66HALF_SPACE = 5
02b800ce
RD
67PROJECT_EXTENSION = ".agp"
68
aca310e5
RD
69if 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
02b800ce
RD
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.
76MAC_RIGHT_BORDER = 0
77if wx.Platform == "__WXMAC__":
78 MAC_RIGHT_BORDER = 5
79
80
81PROJECT_KEY = "/AG_Projects"
82PROJECT_DIRECTORY_KEY = "NewProjectDirectory"
b792147d 83
aca310e5 84NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.getSystemDir()
b792147d 85
1f780e48 86#----------------------------------------------------------------------------
02b800ce 87# Methods
1f780e48 88#----------------------------------------------------------------------------
b792147d 89
aca310e5
RD
90def 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
105def 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, '|'))
1f780e48 110
02b800ce
RD
111
112def 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
aca310e5 116 try:
02b800ce 117 doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
aca310e5
RD
118 if doc:
119 AddProjectMapping(doc)
120 else: # already open
02b800ce
RD
121 for d in docMgr.GetDocuments():
122 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
123 doc = d
124 break
aca310e5
RD
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
02b800ce 161 else:
aca310e5 162 doc = None
02b800ce
RD
163
164 if doc:
165 docModel = doc.GetModel()
166 else:
167 docModel = None
168
169 return doc, docModel
170
171
172def 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
221if not ACTIVEGRID_BASE_IDE:
222 basemodel.findGlobalDocumentMgr = findDocumentMgr
1f780e48
RD
223
224
225#----------------------------------------------------------------------------
226# Classes
227#----------------------------------------------------------------------------
228
aca310e5
RD
229if 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
1f780e48 303
aca310e5 304class ProjectDocument(wx.lib.docview.Document):
b792147d 305
02b800ce 306 def __init__(self, model=None):
1f780e48 307 wx.lib.docview.Document.__init__(self)
02b800ce
RD
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
1f780e48
RD
333
334 def GetModel(self):
335 return self._projectModel
02b800ce
RD
336
337
338 def SetModel(self, model):
339 self._projectModel = model
1f780e48
RD
340
341
342 def OnCreate(self, path, flags):
343 projectService = wx.GetApp().GetService(ProjectService)
02b800ce
RD
344 view = projectService.GetView()
345 if view: # view already exists, reuse
b792147d 346 # All project documents share the same view.
1f780e48 347 self.AddView(view)
b792147d 348
02b800ce 349 if view.GetDocument():
b792147d
RD
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)
02b800ce 355 else: # generate view
1f780e48
RD
356 view = self.GetDocumentTemplate().CreateView(self, flags)
357 projectService.SetView(view)
b792147d 358
1f780e48
RD
359 return view
360
361
362 def LoadObject(self, fileObject):
02b800ce
RD
363 self.SetModel(projectlib.load(fileObject))
364 self.GetModel().SetDocCallback(GetDocCallback)
1f780e48
RD
365 return True
366
367
368 def SaveObject(self, fileObject):
02b800ce 369 projectlib.save(fileObject, self.GetModel())
1f780e48
RD
370 return True
371
372
02b800ce
RD
373 def OnOpenDocument(self, filePath):
374 projectService = wx.GetApp().GetService(ProjectService)
375 view = projectService.GetView()
1f780e48 376
02b800ce 377 if not os.path.exists(filePath):
1f780e48
RD
378 wx.GetApp().CloseSplash()
379 msgTitle = wx.GetApp().GetAppName()
380 if not msgTitle:
381 msgTitle = _("File Error")
02b800ce 382 wx.MessageBox(_("Could not find '%s'.") % filePath,
1f780e48
RD
383 msgTitle,
384 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
02b800ce 385 wx.GetApp().GetTopWindow())
1f780e48
RD
386 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
387
02b800ce 388 fileObject = file(filePath, 'r')
1f780e48
RD
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")
02b800ce 396 wx.MessageBox(_("Could not open '%s'. %s") % (wx.lib.docview.FileNameFromPath(filePath), sys.exc_value),
1f780e48
RD
397 msgTitle,
398 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
02b800ce 399 wx.GetApp().GetTopWindow())
1f780e48
RD
400 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
401
402 self.Modify(False)
02b800ce 403 self.SetFilename(filePath, True)
1f780e48 404 view.AddProjectToView(self)
bbf7159c 405 self.SetDocumentModificationDate()
1f780e48
RD
406 self.UpdateAllViews()
407 self._savedYet = True
02b800ce 408 view.Activate()
1f780e48
RD
409 return True
410
1f780e48 411
02b800ce
RD
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)
1f780e48 423
02b800ce
RD
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)
1f780e48 463 else:
02b800ce
RD
464 return False
465
466 self.AddNameSpaces(newFilePaths)
467
468 self.UpdateAllViews(hint = ("add", self, newFilePaths, oldFilePaths))
469 if len(newFilePaths):
1f780e48
RD
470 self.Modify(True)
471 return True
02b800ce
RD
472 else:
473 return False
1f780e48
RD
474
475
02b800ce
RD
476 def RemoveFile(self, filePath):
477 return self.RemoveFiles([filePath])
1f780e48
RD
478
479
02b800ce
RD
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
1f780e48
RD
500
501
02b800ce 502 def RenameFile(self, oldFilePath, newFilePath, isProject = False):
1f780e48 503 try:
02b800ce 504 if oldFilePath == newFilePath:
1f780e48
RD
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.
02b800ce
RD
509 if not isProject or (isProject and os.path.exists(oldFilePath)):
510 os.rename(oldFilePath, newFilePath)
1f780e48
RD
511
512 if isProject:
513 documents = self.GetDocumentManager().GetDocuments()
514 for document in documents:
02b800ce
RD
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))
1f780e48 519 else:
02b800ce 520 self.UpdateFilePath(oldFilePath, newFilePath)
1f780e48
RD
521 documents = self.GetDocumentManager().GetDocuments()
522 for document in documents:
02b800ce
RD
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))
1f780e48
RD
526 return True
527 except OSError, (code, message):
528 msgTitle = wx.GetApp().GetAppName()
529 if not msgTitle:
530 msgTitle = _("File Error")
02b800ce 531 wx.MessageBox("Could not rename '%s'. '%s'" % (wx.lib.docview.FileNameFromPath(oldFilePath), message),
1f780e48
RD
532 msgTitle,
533 wx.OK | wx.ICON_EXCLAMATION,
02b800ce
RD
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
aca310e5 583
02b800ce
RD
584 def SetStageProjectFile(self):
585 self._stageProjectFile = True
586
aca310e5
RD
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."""
02b800ce
RD
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
aca310e5
RD
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."""
02b800ce
RD
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
aca310e5
RD
622 # set target data source for schemas
623 self._SetSchemaTargetDataSource(fileDict, targetDataSourceMapping)
624
02b800ce
RD
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)
aca310e5 632 self.GenerateDeployment(dplfilepath)
02b800ce
RD
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
aca310e5
RD
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
02b800ce
RD
645 f = None
646 try:
647 f = open(agpfilepath, "w")
aca310e5 648
02b800ce
RD
649 # setting homeDir correctly is required for the "figuring out
650 # relative paths" logic when saving the project
651 self.GetModel().homeDir = stagedir
aca310e5 652
02b800ce
RD
653 projectlib.save(f, self.GetModel(), productionDeployment=True)
654 finally:
655 try:
656 f.close()
657 except: pass
658
659 return stagedir
660
02b800ce 661 def _FixWsdlAgFiles(self, stagedir):
aca310e5 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."""
02b800ce
RD
663 files = os.listdir(stagedir)
664 for f in files:
aca310e5 665 if (f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT)):
02b800ce
RD
666 wsdlagpath = os.path.join(stagedir, f)
667 fileObject = None
aca310e5 668 modified = False
02b800ce
RD
669 try:
670 fileObject = open(wsdlagpath)
671 serviceref = WsdlAgEditor.load(fileObject)
aca310e5
RD
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
02b800ce
RD
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
aca310e5 698 if not modified: continue
02b800ce
RD
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
aca310e5
RD
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
02b800ce
RD
745 return False
746
747 # If the referenced file is in stagedir already, there's nothing to do
aca310e5 748 if (fileutils.hasAncestorDir(filePath, stagedir)):
1f780e48
RD
749 return False
750
02b800ce
RD
751 # The path points outside of stagedir.
752
753 # Check if we already have the referenced wsdl file at root, should be
aca310e5
RD
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)
02b800ce
RD
770
771 return True
aca310e5
RD
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)
02b800ce
RD
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)
02b800ce
RD
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
aca310e5
RD
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)
1f780e48 857
aca310e5
RD
858 return rtn
859
1f780e48 860 def GetFiles(self):
02b800ce
RD
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)
1f780e48
RD
870
871
872 def IsFileInProject(self, filename):
02b800ce
RD
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
aca310e5
RD
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
02b800ce 895
aca310e5
RD
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
02b800ce
RD
906 if ACTIVEGRID_BASE_IDE:
907 return
02b800ce
RD
908
909 if not deployFilepath:
910 deployFilepath = self.GetDeploymentFilepath()
02b800ce 911
aca310e5
RD
912 d = DeploymentGeneration.DeploymentGenerator(
913 self.GetModel(), self._GetResourceFactory(preview,
914 deployFilepath))
915
916 dpl = d.getDeployment(deployFilepath)
02b800ce 917
02b800ce 918 if preview:
aca310e5 919 dpl.initialize() # used in preview only
02b800ce 920
aca310e5
RD
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))
02b800ce 926
aca310e5
RD
927 deploymentlib.saveThroughCache(dpl.fileName, dpl)
928 return deployFilepath
929
02b800ce 930 def AddNameSpaces(self, filePaths):
aca310e5 931 """ Add any new wsdl and schema namespaces to bpel files """
02b800ce
RD
932 """ Add any new schema namespaces to wsdl files """
933 if ACTIVEGRID_BASE_IDE:
934 return
935
02b800ce 936 processRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_PROCESS) # bpel
aca310e5
RD
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):
02b800ce 942 for processRef in processRefs:
aca310e5 943 processDoc = processRef.ideDocument
02b800ce
RD
944 process = processDoc.GetModel()
945
aca310e5
RD
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:
02b800ce
RD
979 for serviceRef in serviceRefs:
980 wsdl = serviceRef.document
981 wsdlDoc = serviceRef.ideDocument
982
aca310e5
RD
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())
1f780e48
RD
999
1000
1f780e48
RD
1001class NewProjectWizard(Wizard.BaseWizard):
1002
1003 WIZTITLE = _("New Project Wizard")
1004
02b800ce 1005
1f780e48
RD
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)
02b800ce
RD
1012
1013
1014 def CreateProjectLocation(self,wizard):
aca310e5 1015 page = Wizard.TitledWizardPage(wizard, _("Name and Location"))
1f780e48 1016
aca310e5
RD
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:"))
1f780e48
RD
1019 page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
1020
1021 wizard.Layout()
1022 wizard.FitToPage(page)
02b800ce 1023 return page
1f780e48 1024
b792147d 1025
1f780e48 1026 def RunWizard(self, existingTables = None, existingRelationships = None):
aca310e5 1027 status = Wizard.BaseWizard.RunWizard(self, self._projectLocationPage)
1f780e48 1028 if status:
02b800ce 1029 wx.ConfigBase_Get().Write(PROJECT_DIRECTORY_KEY, self._dirCtrl.GetValue())
1f780e48
RD
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:
26ee3a06 1035 if os.path.normcase(document.GetFilename()) == os.path.normcase(self._fullProjectPath): # If the renamed document is open, update it
1f780e48
RD
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)
02b800ce
RD
1043 doc.OnSaveDocument(self._fullProjectPath)
1044 projectService = wx.GetApp().GetService(ProjectService)
1045 view = projectService.GetView()
1f780e48
RD
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:
aca310e5 1056 if not self._fileValidation(validClassName=True):
1f780e48 1057 event.Veto()
02b800ce
RD
1058 return
1059 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(), PROJECT_EXTENSION))
1060
1061
1062 def OnShowCreatePages(self):
1f780e48
RD
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)
02b800ce 1074
b792147d 1075
1f780e48
RD
1076class ProjectTemplate(wx.lib.docview.DocTemplate):
1077
b792147d 1078
1f780e48
RD
1079 def CreateDocument(self, path, flags):
1080 if path:
02b800ce
RD
1081 doc = wx.lib.docview.DocTemplate.CreateDocument(self, path, flags)
1082 if path:
1083 doc.GetModel()._projectDir = os.path.dirname(path)
1084 return doc
1f780e48
RD
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
b792147d 1091
1f780e48
RD
1092class ProjectAddFilesCommand(wx.lib.docview.Command):
1093
b792147d 1094
02b800ce 1095 def __init__(self, projectDoc, filePaths, folderPath=None, types=None, names=None):
1f780e48
RD
1096 wx.lib.docview.Command.__init__(self, canUndo = True)
1097 self._projectDoc = projectDoc
02b800ce
RD
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)
1f780e48
RD
1114
1115
1116 def GetName(self):
02b800ce
RD
1117 if len(self._allFilePaths) == 1:
1118 return _("Add File %s") % os.path.basename(self._allFilePaths[0])
1f780e48
RD
1119 else:
1120 return _("Add Files")
1121
1122
1123 def Do(self):
02b800ce 1124 return self._projectDoc.AddFiles(self._allFilePaths, self._folderPath, self._types, self._names)
1f780e48
RD
1125
1126
1127 def Undo(self):
02b800ce 1128 return self._projectDoc.RemoveFiles(self._newFiles)
1f780e48
RD
1129
1130
1131class ProjectRemoveFilesCommand(wx.lib.docview.Command):
1132
b792147d 1133
02b800ce 1134 def __init__(self, projectDoc, files):
1f780e48 1135 wx.lib.docview.Command.__init__(self, canUndo = True)
02b800ce 1136 self._projectDoc = projectDoc
1f780e48
RD
1137 self._files = files
1138
1139
1140 def GetName(self):
1141 if len(self._files) == 1:
02b800ce 1142 return _("Remove File %s") % os.path.basename(self._files[0].filePath)
1f780e48
RD
1143 else:
1144 return _("Remove Files")
1145
1146
1147 def Do(self):
02b800ce 1148 return self._projectDoc.RemoveFiles(files=self._files)
1f780e48
RD
1149
1150
1151 def Undo(self):
02b800ce
RD
1152 return self._projectDoc.AddFiles(files=self._files)
1153
1154
1f780e48
RD
1155
1156class ProjectRenameFileCommand(wx.lib.docview.Command):
1157
b792147d 1158
02b800ce 1159 def __init__(self, projectDoc, oldFilePath, newFilePath, isProject = False):
1f780e48
RD
1160 wx.lib.docview.Command.__init__(self, canUndo = True)
1161 self._projectDoc = projectDoc
02b800ce
RD
1162 self._oldFilePath = oldFilePath
1163 self._newFilePath = newFilePath
1f780e48
RD
1164 self._isProject = isProject
1165
1166
1167 def GetName(self):
02b800ce 1168 return _("Rename File %s to %s") % (os.path.basename(self._oldFilePath), os.path.basename(self._newFilePath))
1f780e48
RD
1169
1170
1171 def Do(self):
02b800ce 1172 return self._projectDoc.RenameFile(self._oldFilePath, self._newFilePath, self._isProject)
1f780e48
RD
1173
1174
1175 def Undo(self):
02b800ce 1176 return self._projectDoc.RenameFile(self._newFilePath, self._oldFilePath, self._isProject)
1f780e48
RD
1177
1178
02b800ce
RD
1179class 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)
b792147d
RD
1197
1198
02b800ce
RD
1199class 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
1228class 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
1257class 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
1285class ProjectTreeCtrl(wx.TreeCtrl):
1286
b792147d
RD
1287 #----------------------------------------------------------------------------
1288 # Overridden Methods
1289 #----------------------------------------------------------------------------
1f780e48
RD
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:
b792147d
RD
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
1f780e48
RD
1305 iconIndex = iconList.AddIcon(icon)
1306 self._iconIndexLookup.append((template, iconIndex))
02b800ce 1307
1f780e48 1308 icon = getBlankIcon()
b792147d
RD
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"
1f780e48 1314 self._blankIconIndex = iconList.AddIcon(icon)
02b800ce
RD
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
1f780e48
RD
1332 self.AssignImageList(iconList)
1333
1334
1335 def OnCompareItems(self, item1, item2):
02b800ce
RD
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
1f780e48 1345
02b800ce
RD
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
1f780e48 1352
02b800ce
RD
1353
1354 def AppendItem(self, parent, filename, file):
1355 item = wx.TreeCtrl.AppendItem(self, parent, filename)
1f780e48
RD
1356
1357 found = False
02b800ce 1358 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
1f780e48
RD
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)
02b800ce 1364## self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Selected)
1f780e48
RD
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)
02b800ce 1371## self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Selected)
1f780e48 1372
02b800ce
RD
1373 self.SetPyData(item, file)
1374
1f780e48
RD
1375 return item
1376
1377
02b800ce
RD
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)
b792147d 1403
02b800ce
RD
1404 return folderItems
1405
b792147d 1406
02b800ce
RD
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
b792147d
RD
1424
1425
02b800ce
RD
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
b792147d
RD
1450
1451
02b800ce
RD
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
1f780e48 1462
02b800ce
RD
1463class ProjectView(wx.lib.docview.View):
1464 LOGICAL_MODE = "logical"
1465 PHYSICAL_MODE = "physical"
1f780e48
RD
1466
1467 #----------------------------------------------------------------------------
1468 # Overridden methods
1469 #----------------------------------------------------------------------------
1470
1471 def __init__(self, service = None):
1472 wx.lib.docview.View.__init__(self)
02b800ce
RD
1473 # self._service = service # not used, but kept to match other Services
1474 self._projectChoice = None
1475 self._logicalBtn = None
1476 self._physicalBtn = None
1f780e48
RD
1477 self._treeCtrl = None
1478 self._editingSoDontKillFocus = False
1479 self._checkEditMenu = True
02b800ce
RD
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()
1f780e48
RD
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):
02b800ce 1495 if not self._projectChoice:
1f780e48 1496 return None
1f780e48 1497
02b800ce
RD
1498 selItem = self._projectChoice.GetSelection()
1499 if selItem == wx.NOT_FOUND:
1500 return None
1f780e48 1501
02b800ce
RD
1502 document = self._projectChoice.GetClientData(selItem)
1503 return document
1f780e48
RD
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
02b800ce 1523 wx.EVT_SIZE(frame, self.OnSize)
1f780e48
RD
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
02b800ce
RD
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)
1f780e48 1569 self._treeCtrl.AddRoot(_("Projects"))
1f780e48 1570 if self._embeddedWindow:
02b800ce
RD
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():
aca310e5 1579 sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 3) # wxBug: without bottom margin, can't resize embedded window
1f780e48 1580 else:
02b800ce
RD
1581 sizer.Add(panel, 1, wx.EXPAND)
1582
1f780e48
RD
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)
02b800ce
RD
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)
aca310e5
RD
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)
1f780e48 1604
02b800ce
RD
1605 # drag-and-drop support
1606 dt = ProjectFileDropTarget(self)
1607 self._treeCtrl.SetDropTarget(dt)
aca310e5 1608
02b800ce
RD
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
1f780e48 1628
02b800ce
RD
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)
1f780e48
RD
1641
1642
b792147d 1643 def OnBeginDrag(self, event):
02b800ce
RD
1644 if self.GetMode() == ProjectView.PHYSICAL_MODE:
1645 return
1646
b792147d
RD
1647 item = event.GetItem()
1648 if item.IsOk():
02b800ce
RD
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
b792147d
RD
1656
1657 def OnEndDrag(self, event):
1658 item = event.GetItem()
02b800ce
RD
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)
b792147d 1665
02b800ce
RD
1666 folderPath = self._GetItemFolderPath(item)
1667
1668 self.GetDocument().GetCommandProcessor().Submit(ProjectMoveFilesCommand(self.GetDocument(), files, folderPath))
b792147d
RD
1669
1670
1f780e48
RD
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 = []
02b800ce
RD
1684 curProject = None
1685
1686 if self._projectChoice:
1687 for i in range(self._projectChoice.GetCount()):
1688 project = self._projectChoice.GetClientData(i)
1f780e48
RD
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()))
1f780e48 1693 config.Write("ProjectSavedDocs", projectFileNames.__repr__())
02b800ce
RD
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")
1f780e48
RD
1704
1705
1706 def OnClose(self, deleteWindow = True):
1707 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
1708 self.WriteProjectConfig()
02b800ce 1709
1f780e48
RD
1710 project = self.GetDocument()
1711 if not project:
1712 return True
02b800ce 1713 if not project.Close():
1f780e48 1714 return True
02b800ce
RD
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()
aca310e5
RD
1721 try:
1722 rootItem = self._treeCtrl.GetRootItem()
1723 self._treeCtrl.DeleteChildren(rootItem)
1724 finally:
1725 self._treeCtrl.Thaw()
02b800ce 1726
1f780e48
RD
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):
aca310e5
RD
1736 if wx.lib.docview.View.OnUpdate(self, sender, hint):
1737 return
02b800ce 1738
1f780e48
RD
1739 if hint:
1740 if hint[0] == "add":
02b800ce
RD
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
aca310e5
RD
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()
02b800ce 1751
aca310e5
RD
1752 mode = self.GetMode()
1753
1754 project = projectDoc.GetModel()
1755 projectDir = project.homeDir
1756 rootItem = self._treeCtrl.GetRootItem()
02b800ce 1757
aca310e5
RD
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)
02b800ce 1794
aca310e5
RD
1795 finally:
1796 self._treeCtrl.Thaw()
02b800ce
RD
1797 return
1798
1f780e48 1799 elif hint[0] == "remove":
02b800ce
RD
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
aca310e5
RD
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
02b800ce 1816
aca310e5
RD
1817 finally:
1818 self._treeCtrl.Thaw()
02b800ce
RD
1819 return
1820
1f780e48 1821 elif hint[0] == "rename":
02b800ce
RD
1822 projectDoc = hint[1]
1823 if self.GetDocument() != projectDoc: # project being updated isn't currently viewed project
1824 return
1825
1826 self._treeCtrl.Freeze()
aca310e5
RD
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()
02b800ce
RD
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()
aca310e5
RD
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()
02b800ce
RD
1851 return
1852
1f780e48 1853
02b800ce
RD
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
1f780e48
RD
1879
1880 def ProcessEvent(self, event):
1881 id = event.GetId()
02b800ce 1882 if id == ProjectService.CLOSE_PROJECT_ID:
aca310e5
RD
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):
02b800ce
RD
1900 self.RemoveCurrentDocumentUpdate()
1901 return True
1902 elif id == ProjectService.ADD_FILES_TO_PROJECT_ID:
1f780e48
RD
1903 self.OnAddFileToProject(event)
1904 return True
02b800ce 1905 elif id == ProjectService.ADD_DIR_FILES_TO_PROJECT_ID:
b792147d
RD
1906 self.OnAddDirToProject(event)
1907 return True
1f780e48
RD
1908 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1909 return False # Implement this one in the service
02b800ce
RD
1910 elif id == ProjectService.ADD_FOLDER_ID:
1911 self.OnAddFolder(event)
1912 return True
1f780e48
RD
1913 elif id == ProjectService.RENAME_ID:
1914 self.OnRename(event)
1915 return True
b792147d
RD
1916 elif id == ProjectService.DELETE_FILE_ID:
1917 self.OnDeleteFile(event)
02b800ce
RD
1918 return True
1919 elif id == ProjectService.DELETE_PROJECT_ID:
1920 self.OnDeleteProject(event)
1921 return True
1f780e48
RD
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
b792147d
RD
1931 elif (id == wx.ID_CLEAR
1932 or id == ProjectService.REMOVE_FROM_PROJECT):
1f780e48
RD
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
02b800ce
RD
1944 elif id == ProjectService.PROJECT_PROPERTIES_ID:
1945 self.OnProjectProperties(event)
1946 return True
1f780e48
RD
1947 else:
1948 return False
1949
b792147d 1950
1f780e48
RD
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
02b800ce 1958
1f780e48 1959 id = event.GetId()
02b800ce
RD
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)
26ee3a06 1972 return True
1f780e48 1973 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
02b800ce
RD
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):
aca310e5
RD
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
02b800ce 2015 event.Enable(self._HasFilesSelected() or (self.GetDocument() != None and self.GetMode() == ProjectView.LOGICAL_MODE and self._HasFoldersSelected()))
1f780e48 2016 return True
1f780e48
RD
2017 elif id == wx.ID_PASTE:
2018 event.Enable(self.CanPaste())
2019 return True
1f780e48
RD
2020 elif id == wx.ID_SELECTALL:
2021 event.Enable(self._HasFiles())
2022 return True
26ee3a06
RD
2023 elif (id == wx.ID_PREVIEW
2024 or id == wx.ID_PRINT):
2025 event.Enable(False)
02b800ce 2026 return True
1f780e48
RD
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
02b800ce
RD
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
1f780e48
RD
2065
2066 def GetSelectedFile(self):
2067 for item in self._treeCtrl.GetSelections():
02b800ce
RD
2068 filePath = self._GetItemFilePath(item)
2069 if filePath:
2070 return filePath
2071 return None
2072
6f1a3f9c 2073
6f1a3f9c 2074 def GetSelectedFiles(self):
02b800ce 2075 filePaths = []
6f1a3f9c 2076 for item in self._treeCtrl.GetSelections():
02b800ce
RD
2077 filePath = self._GetItemFilePath(item)
2078 if filePath and filePath not in filePaths:
2079 filePaths.append(filePath)
2080 return filePaths
6f1a3f9c 2081
bbf7159c 2082
02b800ce
RD
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
26ee3a06
RD
2101
2102
1f780e48 2103 def AddProjectToView(self, document):
02b800ce
RD
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
aca310e5
RD
2113 try:
2114 rootItem = self._treeCtrl.GetRootItem()
2115 self._treeCtrl.DeleteChildren(rootItem)
02b800ce 2116
aca310e5
RD
2117 if document:
2118 mode = self.GetMode()
2119 docFilePath = document.GetFilename()
02b800ce 2120
02b800ce 2121 if mode == ProjectView.LOGICAL_MODE:
aca310e5 2122 folders = document.GetModel().logicalFolders
02b800ce 2123 else:
aca310e5
RD
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)
02b800ce 2152 break
aca310e5
RD
2153 else:
2154 item = rootItem
2155
2156 fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
02b800ce 2157
aca310e5
RD
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)
02b800ce 2170
aca310e5
RD
2171 if self._embeddedWindow:
2172 document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
1f780e48 2173
aca310e5
RD
2174 finally:
2175 self._treeCtrl.Thaw()
2176 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
1f780e48 2177
02b800ce
RD
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 """
6f1a3f9c
RD
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
aca310e5
RD
2196 def ClearFolderState(self):
2197 config = wx.ConfigBase_Get()
2198 config.DeleteGroup(getProjectKeyName(self.GetDocument().GetFilename()))
2199
2200
02b800ce
RD
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):
aca310e5 2218 """ Load the open/close state of folders. """
02b800ce 2219 self._loading = True
aca310e5 2220
02b800ce 2221 config = wx.ConfigBase_Get()
aca310e5 2222 openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()), "")
02b800ce
RD
2223 if openFolderData:
2224 folderList = eval(openFolderData)
2225
2226 folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
2227 for item in folderItemList:
aca310e5
RD
2228 folderPath = self._GetItemFolderPath(item)
2229 if folderPath in folderList:
02b800ce 2230 self._treeCtrl.Expand(item)
aca310e5
RD
2231 else:
2232 self._treeCtrl.Collapse(item)
2233
2234 else:
2235 projectService = wx.GetApp().GetService(ProjectService)
2236
02b800ce
RD
2237 folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
2238 for item in folderItemList:
aca310e5
RD
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)
02b800ce
RD
2244
2245 self._loading = False
2246
2247
1f780e48
RD
2248 #----------------------------------------------------------------------------
2249 # Control events
2250 #----------------------------------------------------------------------------
2251
2252 def OnProperties(self, event):
02b800ce
RD
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
1f780e48
RD
2335
2336
2337 def OnAddFileToProject(self, event):
2338 if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
1f780e48
RD
2339 descr = ''
2340 for temp in self.GetDocumentManager()._templates:
2341 if temp.IsVisible():
2342 if len(descr) > 0:
2343 descr = descr + _('|')
1f780e48 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
aca310e5 2345 descr = _("All|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
1f780e48
RD
2346 else:
2347 descr = _("*.*")
02b800ce
RD
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:
1f780e48 2352 dialog.Destroy()
02b800ce
RD
2353 return
2354 paths = dialog.GetPaths()
2355 dialog.Destroy()
1f780e48 2356 if len(paths):
02b800ce
RD
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
1f780e48
RD
2368
2369
b792147d 2370 def OnAddDirToProject(self, event):
02b800ce 2371 frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Add Directory Files to Project"), size= (320,200))
b792147d 2372 contentSizer = wx.BoxSizer(wx.VERTICAL)
02b800ce 2373
bbb28897
RD
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)
b792147d 2376 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
bbb28897 2377 dirCtrl = wx.TextCtrl(frame, -1, os.path.dirname(self.GetDocument().GetFilename()), size=(250,-1))
b792147d 2378 dirCtrl.SetToolTipString(dirCtrl.GetValue())
bbb28897 2379 lineSizer.Add(dirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
6f1a3f9c 2380 findDirButton = wx.Button(frame, -1, _("Browse..."))
bbb28897
RD
2381 lineSizer.Add(findDirButton, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, HALF_SPACE)
2382 flexGridSizer.Add(lineSizer, 1, wx.EXPAND)
02b800ce 2383
b792147d
RD
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)
02b800ce 2389 dlg.CenterOnParent()
b792147d
RD
2390 if dlg.ShowModal() == wx.ID_OK:
2391 dirCtrl.SetValue(dlg.GetPath())
2392 dirCtrl.SetToolTipString(dirCtrl.GetValue())
2393 dirCtrl.SetInsertionPointEnd()
b792147d
RD
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)
02b800ce 2401
b792147d 2402 choices = []
b792147d
RD
2403 descr = ''
2404 for template in visibleTemplates:
2405 if len(descr) > 0:
2406 descr = descr + _('|')
b792147d
RD
2407 descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
2408 choices.append(descr)
aca310e5 2409 choices.insert(0, _("All")) # first item
bbb28897 2410 filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
b792147d
RD
2411 filterChoice.SetSelection(0)
2412 filterChoice.SetToolTipString(_("Select file type filter."))
bbb28897
RD
2413 flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL)
2414 flexGridSizer.Add(filterChoice, 1, wx.EXPAND)
02b800ce 2415
bbb28897 2416 contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE)
02b800ce 2417
b792147d
RD
2418 subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories"))
2419 subfolderCtrl.SetValue(True)
bbb28897 2420 contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE)
b792147d 2421
bbb28897 2422 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
b792147d
RD
2423 findBtn = wx.Button(frame, wx.ID_OK, _("Add"))
2424 findBtn.SetDefault()
bbb28897 2425 buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE)
b792147d 2426 buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
bbb28897 2427 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
b792147d 2428
bbb28897 2429 frame.SetSizer(contentSizer)
b792147d
RD
2430 frame.Fit()
2431
02b800ce 2432 frame.CenterOnParent()
b792147d
RD
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 )
02b800ce 2443 dlg.CenterOnParent()
b792147d
RD
2444 dlg.ShowModal()
2445 dlg.Destroy()
2446
2447 status = frame.ShowModal()
2448 else:
2449 passedCheck = True
b792147d 2450
02b800ce
RD
2451 frame.Destroy()
2452
2453 if status == wx.ID_OK:
b792147d 2454 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
02b800ce 2455
aca310e5
RD
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
b792147d
RD
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):
02b800ce 2483 paths.append(filename)
aca310e5
RD
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)
1f780e48 2498
aca310e5
RD
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))
1f780e48 2504
1f780e48 2505
02b800ce
RD
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))
1f780e48
RD
2509
2510
2511 def OnFocus(self, event):
02b800ce 2512 self.GetDocumentManager().ActivateView(self)
1f780e48
RD
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
02b800ce
RD
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):
aca310e5
RD
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__':
02b800ce 2539 item, flags = self._treeCtrl.HitTest(event.GetPosition())
aca310e5
RD
2540 if item.IsOk() and self._treeCtrl.GetChildrenCount(item, False):
2541 event.Skip()
2542 else:
2543 event.Skip()
1f780e48
RD
2544
2545 def OnRightClick(self, event):
02b800ce
RD
2546 self.Activate()
2547 if not self.GetSelectedProject():
1f780e48 2548 return
1f780e48
RD
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)
02b800ce
RD
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
1f780e48
RD
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
02b800ce 2574 itemIDs = []
1f780e48 2575 menuBar = self._GetParentFrame().GetMenuBar()
02b800ce 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]
26ee3a06 2577 svnIDs = [SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
6f1a3f9c
RD
2578 if SVN_INSTALLED:
2579 itemIDs = itemIDs + [None, SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
26ee3a06 2580 globalIDs = [wx.ID_UNDO, wx.ID_REDO, wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS]
6f1a3f9c 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]
1f780e48
RD
2582 for itemID in itemIDs:
2583 if not itemID:
2584 menu.AppendSeparator()
2585 else:
aca310e5
RD
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
1f780e48
RD
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:
aca310e5
RD
2616 if SVN_INSTALLED:
2617 svnService = wx.GetApp().GetService(SVNService.SVNService)
2618
26ee3a06
RD
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)
1f780e48
RD
2626 menu.Append(itemID, item.GetLabel())
2627 self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY()))
2628 menu.Destroy()
2629
b792147d 2630
aca310e5 2631 def ProjectServiceProcessEvent(self, event):
1f780e48 2632 projectService = wx.GetApp().GetService(ProjectService)
b792147d 2633 if projectService:
aca310e5 2634 projectService.ProcessEvent(event)
b792147d 2635
1f780e48 2636
02b800ce
RD
2637 def OnRename(self, event=None):
2638 items = self._treeCtrl.GetSelections()
aca310e5
RD
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)
1f780e48
RD
2651
2652
2653 def OnBeginLabelEdit(self, event):
2654 self._editingSoDontKillFocus = True
2655 item = event.GetItem()
aca310e5
RD
2656 if self._IsItemFile(item):
2657 file = self._GetItemFile(item)
2658 if file.type == 'xform':
2659 event.Veto()
02b800ce 2660 if (self.GetMode() == ProjectView.PHYSICAL_MODE) and not self._IsItemFile(item):
1f780e48
RD
2661 event.Veto()
2662
2663
2664 def OnEndLabelEdit(self, event):
2665 self._editingSoDontKillFocus = False
2666 item = event.GetItem()
2667 newName = event.GetLabel()
aca310e5 2668 if not self.ChangeLabel(item, newName):
1f780e48 2669 event.Veto()
aca310e5
RD
2670
2671
2672 def ChangeLabel(self, item, newName):
2673 if not newName:
2674 return False
1f780e48 2675 if self._IsItemFile(item):
02b800ce
RD
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)):
aca310e5 2680 return False
02b800ce
RD
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())
aca310e5 2693 return False
02b800ce
RD
2694 doc = self.GetDocument()
2695 if not doc.GetCommandProcessor().Submit(ProjectRenameFolderCommand(doc, oldFolderPath, newFolderPath)):
aca310e5 2696 return False
02b800ce 2697 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
1f780e48 2698
aca310e5
RD
2699 return True
2700
1f780e48
RD
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)
aca310e5 2706 hasFilesInClipboard = False
1f780e48
RD
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()
1f780e48
RD
2712 return hasFilesInClipboard
2713
2714
2715 def OnCut(self, event):
b792147d
RD
2716 self.OnCopy(event)
2717 self.OnClear(event)
1f780e48
RD
2718
2719
2720 def OnCopy(self, event):
2721 fileDataObject = wx.FileDataObject()
2722 items = self._treeCtrl.GetSelections()
2723 for item in items:
02b800ce
RD
2724 filePath = self._GetItemFilePath(item)
2725 if filePath:
2726 fileDataObject.AddFile(filePath)
1f780e48
RD
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):
02b800ce
RD
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))
1f780e48
RD
2744 wx.TheClipboard.Close()
2745
2746
2747 def OnClear(self, event):
02b800ce
RD
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))
1f780e48
RD
2769
2770
b792147d
RD
2771 def OnDeleteFile(self, event):
2772 yesNoMsg = wx.MessageDialog(self.GetFrame(),
02b800ce 2773 _("Delete cannot be reversed.\n\nRemove the selected files from the\nproject and file system permanently?"),
b792147d
RD
2774 _("Delete File"),
2775 wx.YES_NO|wx.ICON_QUESTION)
02b800ce
RD
2776 yesNoMsg.CenterOnParent()
2777 status = yesNoMsg.ShowModal()
2778 yesNoMsg.Destroy()
2779 if status == wx.ID_NO:
b792147d 2780 return
02b800ce 2781
b792147d 2782 items = self._treeCtrl.GetSelections()
b792147d
RD
2783 delFiles = []
2784 for item in items:
02b800ce
RD
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
b792147d 2792 # remove selected files from file system
02b800ce
RD
2793 for filePath in delFiles:
2794 if os.path.exists(filePath):
b792147d 2795 try:
02b800ce 2796 os.remove(filePath)
b792147d 2797 except:
02b800ce 2798 wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filePath), sys.exc_value),
b792147d 2799 _("Delete File"),
02b800ce
RD
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()
aca310e5
RD
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.
02b800ce
RD
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
aca310e5
RD
2890 self.ClearFolderState() # remove from registry folder settings
2891
02b800ce 2892 # close project
aca310e5 2893 if doc:
02b800ce
RD
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
b792147d 2929
1f780e48
RD
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:
02b800ce
RD
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):
1f780e48 2948 self._treeCtrl.SelectItem(child)
02b800ce
RD
2949 else:
2950 self.DoSelectAll(child)
2951 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
1f780e48
RD
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:
02b800ce 2962 items = self._treeCtrl.GetSelections()[:]
1f780e48 2963 for item in items:
02b800ce
RD
2964 filepath = self._GetItemFilePath(item)
2965 if filepath:
1f780e48
RD
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,
b792147d 2973 wx.YES_NO|wx.ICON_QUESTION
1f780e48 2974 )
02b800ce
RD
2975 yesNoMsg.CenterOnParent()
2976 status = yesNoMsg.ShowModal()
2977 yesNoMsg.Destroy()
2978 if status == wx.ID_NO:
1f780e48 2979 continue
02b800ce 2980 findFileDlg = wx.FileDialog(self.GetFrame(),
1f780e48 2981 _("Choose a file"),
02b800ce
RD
2982 defaultFile=wx.lib.docview.FileNameFromPath(filepath),
2983 style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR
1f780e48 2984 )
02b800ce
RD
2985 # findFileDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
2986 if findFileDlg.ShowModal() == wx.ID_OK:
2987 newpath = findFileDlg.GetPath()
1f780e48
RD
2988 else:
2989 newpath = None
02b800ce 2990 findFileDlg.Destroy()
1f780e48
RD
2991 if newpath:
2992 # update Project Model with new location
02b800ce 2993 self.GetDocument().UpdateFilePath(filepath, newpath)
1f780e48
RD
2994 filepath = newpath
2995
02b800ce
RD
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:
aca310e5 3000 AddProjectMapping(doc)
02b800ce 3001
1f780e48
RD
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
02b800ce 3023 def _HasFilesSelected(self):
1f780e48
RD
3024 if not self._treeCtrl:
3025 return False
3026 items = self._treeCtrl.GetSelections()
02b800ce
RD
3027 if not items:
3028 return False
3029 for item in items:
3030 if self._IsItemFile(item):
3031 return True
1f780e48
RD
3032 return False
3033
3034
02b800ce 3035 def _HasFoldersSelected(self):
1f780e48
RD
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:
02b800ce 3042 if self._IsItemFile(item):
1f780e48
RD
3043 return False
3044 return True
3045
3046
3047 def _MakeProjectName(self, project):
3048 return project.GetPrintableName()
3049
3050
02b800ce
RD
3051 def _GetItemFilePath(self, item):
3052 file = self._GetItemFile(item)
3053 if file:
3054 return file.filePath
1f780e48
RD
3055 else:
3056 return None
3057
3058
02b800ce 3059 def _GetItemFolderPath(self, item):
b792147d 3060 rootItem = self._treeCtrl.GetRootItem()
02b800ce
RD
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
1f780e48 3076
02b800ce
RD
3077
3078 def _GetItemFile(self, item):
3079 return self._treeCtrl.GetPyData(item)
1f780e48
RD
3080
3081
3082 def _IsItemFile(self, item):
02b800ce 3083 return self._GetItemFile(item) != None
1f780e48
RD
3084
3085
3086 def _IsItemProcessModelFile(self, item):
3087 if ACTIVEGRID_BASE_IDE:
3088 return False
02b800ce 3089
b792147d 3090 if self._IsItemFile(item):
02b800ce 3091 filepath = self._GetItemFilePath(item)
1f780e48
RD
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
02b800ce 3100 if filepath.endswith(ext):
1f780e48
RD
3101 return True
3102
3103 return False
3104
3105
1f780e48
RD
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
02b800ce
RD
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
1f780e48
RD
3124
3125class ProjectFileDropTarget(wx.FileDropTarget):
3126
3127 def __init__(self, view):
3128 wx.FileDropTarget.__init__(self)
3129 self._view = view
3130
3131
02b800ce
RD
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)
1f780e48
RD
3141 return True
3142 return False
3143
3144
3145 def OnDragOver(self, x, y, default):
02b800ce
RD
3146 """ Feedback to show copy cursor if copy is allowed """
3147 if self._view.GetDocument(): # only allow drop if project exists
1f780e48
RD
3148 return wx.DragCopy
3149 return wx.DragNone
3150
3151
3152class ProjectPropertiesDialog(wx.Dialog):
02b800ce 3153 RELATIVE_TO_PROJECT_FILE = _("relative to project file")
1f780e48 3154
02b800ce 3155 def __init__(self, parent, document):
1f780e48
RD
3156 wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330))
3157
1f780e48
RD
3158 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
3159
3160 notebook = wx.Notebook(self, -1)
02b800ce 3161
1f780e48 3162 tab = wx.Panel(notebook, -1)
02b800ce
RD
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()
1f780e48 3167 if os.path.isfile(filename):
02b800ce 3168 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]))
1f780e48 3169
02b800ce
RD
3170 gridSizer.Add(wx.StaticText(tab, -1, _("Location:")))
3171 gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.dirname(filename), length=50)))
1f780e48 3172
02b800ce
RD
3173 gridSizer.Add(wx.StaticText(tab, -1, _("Size:")))
3174 gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")))
1f780e48
RD
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)
02b800ce 3178 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP)
1f780e48 3179
02b800ce
RD
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)
1f780e48 3183
02b800ce
RD
3184 gridSizer.Add(wx.StaticText(tab, -1, _("Created:")))
3185 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))))
1f780e48 3186
02b800ce
RD
3187 gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")))
3188 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))))
1f780e48 3189
02b800ce
RD
3190 gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")))
3191 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))))
1f780e48 3192 else:
02b800ce
RD
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);
1f780e48
RD
3196 tab.SetSizer(spacerGrid)
3197 notebook.AddPage(tab, _("General"))
02b800ce
RD
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
aca310e5
RD
3239 if wx.Platform == "__WXMSW__":
3240 notebook.SetPageSize((310,300))
3241
02b800ce
RD
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)
aca310e5 3248 sizer.Add(self._appInfoCtrl, 1, wx.EXPAND|wx.ALL, PropertyService.LEAVE_MARGIN)
02b800ce
RD
3249 tab.SetSizer(sizer)
3250 notebook.AddPage(tab, _("App Info"))
aca310e5 3251 self._appInfoCtrl._grid.AutoSizeColumns()
02b800ce 3252
1f780e48
RD
3253
3254 sizer = wx.BoxSizer(wx.VERTICAL)
3255 sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
02b800ce 3256 sizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
1f780e48 3257
1f780e48 3258 self.SetSizer(sizer)
6f1a3f9c 3259 sizer.Fit(self)
1f780e48
RD
3260 self.Layout()
3261
3262
3263class ProjectOptionsPanel(wx.Panel):
3264
3265
3266 def __init__(self, parent, id):
3267 wx.Panel.__init__(self, parent, id)
3268 self._useSashMessageShown = False
1f780e48
RD
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)
bbf7159c
RD
3275 if not ACTIVEGRID_BASE_IDE:
3276 self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
aca310e5 3277 self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog2", True))
02b800ce
RD
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)
aca310e5
RD
3282 self._langCtrl = wx.Choice(self, -1, choices=projectmodel.LANGUAGE_LIST)
3283 self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT))
02b800ce
RD
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
1f780e48
RD
3288 projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE)
3289 self.SetSizer(projectBorderSizer)
3290 self.Layout()
3291 parent.AddPage(self, _("Project"))
3292
02b800ce 3293
1f780e48
RD
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())
bbf7159c 3309 if not ACTIVEGRID_BASE_IDE:
aca310e5 3310 config.WriteInt("RunWelcomeDialog2", self._projShowWelcomeCheckBox.GetValue())
02b800ce
RD
3311 config.Write(APP_LAST_LANGUAGE, self._langCtrl.GetStringSelection())
3312
3313
3314 def GetIcon(self):
3315 return getProjectIcon()
1f780e48
RD
3316
3317
3318class 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
1f780e48 3324 RUN_SELECTED_PM_ID = wx.NewId()
aca310e5
RD
3325 RUN_SELECTED_PM_INTERNAL_WINDOW_ID = wx.NewId()
3326 RUN_SELECTED_PM_EXTERNAL_BROWSER_ID = wx.NewId()
1f780e48 3327 RUN_CURRENT_PM_ID = wx.NewId()
aca310e5
RD
3328 RUN_CURRENT_PM_INTERNAL_WINDOW_ID = wx.NewId()
3329 RUN_CURRENT_PM_EXTERNAL_BROWSER_ID = wx.NewId()
1f780e48
RD
3330 RENAME_ID = wx.NewId()
3331 OPEN_SELECTION_ID = wx.NewId()
3332 REMOVE_FROM_PROJECT = wx.NewId()
b792147d 3333 DELETE_FILE_ID = wx.NewId()
02b800ce
RD
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()
b792147d 3341
1f780e48
RD
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
02b800ce 3351 self._logicalViewDefaults = []
aca310e5 3352 self._logicalViewOpenDefaults = []
02b800ce
RD
3353 self._fileTypeDefaults = []
3354 self._nameDefaults = []
3355 self._mapToProject = dict()
1f780e48
RD
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
1f780e48
RD
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):
b792147d 3408 projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project"))
1f780e48
RD
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)
02b800ce
RD
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)
1f780e48
RD
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)
02b800ce 3419 if not menuBar.FindItemById(ProjectService.ADD_FOLDER_ID):
aca310e5 3420 projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("New Folder"), _("Creates a new folder"))
02b800ce
RD
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"))
1f780e48
RD
3441 editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
3442 if not menuBar.FindItemById(ProjectService.RENAME_ID):
1f780e48
RD
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)
b792147d
RD
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)
1f780e48
RD
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
02b800ce
RD
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
aca310e5
RD
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
02b800ce
RD
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
aca310e5
RD
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
02b800ce
RD
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
1f780e48
RD
3567 #----------------------------------------------------------------------------
3568 # Event Processing Methods
3569 #----------------------------------------------------------------------------
3570
3571 def ProcessEventBeforeWindows(self, event):
3572 id = event.GetId()
02b800ce 3573
1f780e48
RD
3574 if id == wx.ID_CLOSE_ALL:
3575 self.OnFileCloseAll(event)
3576 return True
26ee3a06
RD
3577 return False
3578
3579
3580 def ProcessUpdateUIEventBeforeWindows(self, event):
3581 id = event.GetId()
02b800ce 3582
26ee3a06
RD
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
02b800ce 3588
26ee3a06
RD
3589 event.Enable(False)
3590 return True
02b800ce 3591
26ee3a06 3592 elif id == wx.ID_CLOSE:
02b800ce
RD
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".
26ee3a06
RD
3596 document = self.GetDocumentManager().GetCurrentDocument()
3597 if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
02b800ce
RD
3598 event.Enable(False)
3599 return True
3600 if self.GetView().ProcessUpdateUIEvent(event):
26ee3a06
RD
3601 return True
3602
1f780e48
RD
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
aca310e5
RD
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
1f780e48
RD
3620 elif id == ProjectService.RUN_CURRENT_PM_ID:
3621 self.OnRunProcessModel(event, runCurrentFile=True)
3622 return True
aca310e5
RD
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
1f780e48
RD
3629 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
3630 self.OnAddCurrentFileToProject(event)
3631 return True
02b800ce
RD
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):
1f780e48
RD
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()
aca310e5
RD
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]:
02b800ce 3656 event.Enable(True)
1f780e48 3657 return True
1f780e48
RD
3658 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
3659 event.Enable(self._CanAddCurrentFileToProject())
3660 return True
aca310e5
RD
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]:
1f780e48
RD
3666 event.Enable(False)
3667 return True
02b800ce
RD
3668 elif id == ProjectService.PROJECT_PROPERTIES_ID:
3669 event.Enable(self._HasOpenedProjects())
3670 return True
aca310e5
RD
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]:
1f780e48
RD
3675 if self.GetView():
3676 return self.GetView().ProcessUpdateUIEvent(event)
3677 else:
3678 return False
3679 else:
3680 return False
3681
3682
aca310e5 3683 def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False, newWindow=False, forceExternal=False, forceInternal=False):
02b800ce
RD
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())
1f780e48
RD
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()
1f780e48
RD
3726
3727 filesModified = False
3728 for doc in docs:
3729 if doc.IsModified():
3730 filesModified = True
3731 break
3732 if filesModified:
02b800ce 3733 frame = self.GetView().GetFrame()
1f780e48
RD
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"),
b792147d 3737 wx.YES_NO|wx.ICON_QUESTION
1f780e48 3738 )
02b800ce
RD
3739 yesNoMsg.CenterOnParent()
3740 status = yesNoMsg.ShowModal()
3741 yesNoMsg.Destroy()
3742 if status == wx.ID_YES:
1f780e48 3743 wx.GetTopLevelParent(frame).OnFileSaveAll(None)
02b800ce 3744
1f780e48
RD
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,
02b800ce 3755 self.GetView()._GetParentFrame())
1f780e48
RD
3756 if res == -1:
3757 return
3758 fileToRun = files[res]
3759 else:
3760 fileToRun = files[0]
aca310e5
RD
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)
1f780e48 3769
02b800ce 3770
aca310e5 3771 def RunProcessModel(self, fileToRun, language, deployFilePath, newWindow=True, forceExternal=False, forceInternal=False):
1f780e48 3772 for runHandler in self.GetRunHandlers():
aca310e5 3773 if runHandler.RunProjectFile(fileToRun, language, deployFilePath, newWindow, forceExternal, forceInternal):
1f780e48
RD
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
1f780e48
RD
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
02b800ce
RD
3815 if self.GetView().GetDocument(): # a project is open
3816 return True
1f780e48
RD
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
02b800ce
RD
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
1f780e48
RD
3843
3844 def FindProjectByFile(self, filename):
02b800ce 3845 retval = []
1f780e48
RD
3846 for document in self.GetDocumentManager().GetDocuments():
3847 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
3848 if document.GetFilename() == filename:
02b800ce 3849 retval.append(document)
1f780e48 3850 elif document.IsFileInProject(filename):
02b800ce 3851 retval.append(document)
aca310e5
RD
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
02b800ce 3859 return retval
1f780e48 3860
b792147d 3861
1f780e48 3862 def OnAddCurrentFileToProject(self, event):
aca310e5
RD
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
02b800ce 3870 self.GetView().Activate() # after add, should put focus on project editor
26ee3a06
RD
3871
3872
1f780e48
RD
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
02b800ce 3888 docList = eval(docString)
aca310e5
RD
3889 self.GetView()._treeCtrl.Freeze()
3890
02b800ce 3891 for fileName in docList:
1f780e48
RD
3892 if isinstance(fileName, types.StringTypes):
3893 if os.path.exists(fileName):
02b800ce 3894 doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
aca310e5 3895 self.GetView()._treeCtrl.Thaw()
1f780e48
RD
3896
3897 if doc:
3898 openedDocs = True
b792147d 3899
02b800ce
RD
3900 currProject = config.Read("ProjectCurrent")
3901 if currProject in docList:
3902 self.GetView().SetProject(currProject)
b792147d 3903
02b800ce 3904 return openedDocs
b792147d
RD
3905
3906
aca310e5
RD
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
1f780e48
RD
3939#----------------------------------------------------------------------------
3940# Icon Bitmaps - generated by encode_bitmaps.py
3941#----------------------------------------------------------------------------
3942from wx import ImageFromStream, BitmapFromImage
1f780e48
RD
3943import cStringIO
3944
3945
3946def 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\
02b800ce
RD
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\
3955n\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'
1f780e48
RD
3966
3967def getProjectBitmap():
3968 return BitmapFromImage(getProjectImage())
3969
3970def getProjectImage():
3971 stream = cStringIO.StringIO(getProjectData())
3972 return ImageFromStream(stream)
3973
3974def getProjectIcon():
bbf7159c 3975 return wx.IconFromBitmap(getProjectBitmap())
02b800ce 3976
1f780e48
RD
3977
3978#----------------------------------------------------------------------------
3979
3980def getBlankData():
3981 return \
02b800ce 3982"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
bbf7159c 3983\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
02b800ce
RD
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"
1f780e48
RD
3996
3997
3998def getBlankBitmap():
3999 return BitmapFromImage(getBlankImage())
4000
4001def getBlankImage():
4002 stream = cStringIO.StringIO(getBlankData())
4003 return ImageFromStream(stream)
4004
4005def getBlankIcon():
bbf7159c 4006 return wx.IconFromBitmap(getBlankBitmap())
02b800ce
RD
4007
4008
4009#----------------------------------------------------------------------
4010def 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
4026def getFolderClosedBitmap():
4027 return BitmapFromImage(getFolderClosedImage())
4028
4029def getFolderClosedImage():
4030 stream = cStringIO.StringIO(getFolderClosedData())
4031 return ImageFromStream(stream)
4032
4033def getFolderClosedIcon():
4034 return wx.IconFromBitmap(getFolderClosedBitmap())
4035
4036
4037#----------------------------------------------------------------------
4038def 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$\
4043Z\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\
4047R\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
4057def getFolderOpenBitmap():
4058 return BitmapFromImage(getFolderOpenImage())
4059
4060def getFolderOpenImage():
4061 stream = cStringIO.StringIO(getFolderOpenData())
4062 return ImageFromStream(stream)
4063
4064def getFolderOpenIcon():
4065 return wx.IconFromBitmap(getFolderOpenBitmap())
1f780e48 4066
02b800ce
RD
4067
4068#----------------------------------------------------------------------
4069def 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\
4085h\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
4090def getLogicalModeOnBitmap():
4091 return BitmapFromImage(getLogicalModeOnImage())
4092
4093def getLogicalModeOnImage():
4094 stream = cStringIO.StringIO(getLogicalModeOnData())
4095 return ImageFromStream(stream)
4096
4097#----------------------------------------------------------------------
4098def 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\
4114h\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
4119def getLogicalModeOffBitmap():
4120 return BitmapFromImage(getLogicalModeOffImage())
4121
4122def getLogicalModeOffImage():
4123 stream = cStringIO.StringIO(getLogicalModeOffData())
4124 return ImageFromStream(stream)
4125
4126#----------------------------------------------------------------------
4127def 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\
4137vt\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\
4139I"\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
4150def getPhysicalModeOnBitmap():
4151 return BitmapFromImage(getPhysicalModeOnImage())
4152
4153def getPhysicalModeOnImage():
4154 stream = cStringIO.StringIO(getPhysicalModeOnData())
4155 return ImageFromStream(stream)
4156
4157#----------------------------------------------------------------------
4158def 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\
4168vt\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\
4170I"\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
4181def getPhysicalModeOffBitmap():
4182 return BitmapFromImage(getPhysicalModeOffImage())
4183
4184def getPhysicalModeOffImage():
4185 stream = cStringIO.StringIO(getPhysicalModeOffData())
4186 return ImageFromStream(stream)
4187