]>
Commit | Line | Data |
---|---|---|
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 | 13 | import wx |
1f780e48 RD |
14 | import wx.lib.docview |
15 | import wx.lib.pydocview | |
02b800ce | 16 | import wx.lib.buttons |
1f780e48 | 17 | import Service |
02b800ce RD |
18 | import copy |
19 | import os | |
20 | import os.path | |
21 | import sets | |
1f780e48 | 22 | import sys |
02b800ce RD |
23 | import time |
24 | import types | |
25 | import activegrid.util.appdirs as appdirs | |
26 | import activegrid.util.fileutils as fileutils | |
aca310e5 | 27 | import activegrid.util.aglogging as aglogging |
1f780e48 | 28 | import UICommon |
b792147d | 29 | import Wizard |
6f1a3f9c | 30 | import SVNService |
02b800ce RD |
31 | import project as projectlib |
32 | import ExtensionService | |
33 | ||
1f780e48 RD |
34 | from IDE import ACTIVEGRID_BASE_IDE |
35 | if 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 | 53 | from SVNService import SVN_INSTALLED |
1f780e48 RD |
54 | |
55 | _ = wx.GetTranslation | |
56 | ||
57 | if wx.Platform == '__WXMSW__': | |
58 | _WINDOWS = True | |
59 | else: | |
60 | _WINDOWS = False | |
02b800ce | 61 | |
b792147d RD |
62 | #---------------------------------------------------------------------------- |
63 | # Constants | |
64 | #---------------------------------------------------------------------------- | |
65 | SPACE = 10 | |
66 | HALF_SPACE = 5 | |
02b800ce RD |
67 | PROJECT_EXTENSION = ".agp" |
68 | ||
aca310e5 RD |
69 | if not ACTIVEGRID_BASE_IDE: |
70 | PRE_17_TMP_DPL_NAME = "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION | |
71 | _17_TMP_DPL_NAME = ".tmp" + deploymentlib.DEPLOYMENT_EXTENSION | |
72 | ||
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. | |
76 | MAC_RIGHT_BORDER = 0 | |
77 | if wx.Platform == "__WXMAC__": | |
78 | MAC_RIGHT_BORDER = 5 | |
79 | ||
80 | ||
81 | PROJECT_KEY = "/AG_Projects" | |
82 | PROJECT_DIRECTORY_KEY = "NewProjectDirectory" | |
b792147d | 83 | |
aca310e5 | 84 | NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.getSystemDir() |
b792147d | 85 | |
1f780e48 | 86 | #---------------------------------------------------------------------------- |
02b800ce | 87 | # Methods |
1f780e48 | 88 | #---------------------------------------------------------------------------- |
b792147d | 89 | |
aca310e5 RD |
90 | def AddProjectMapping(doc, projectDoc=None, hint=None): |
91 | projectService = wx.GetApp().GetService(ProjectService) | |
92 | if projectService: | |
93 | if not projectDoc: | |
94 | if not hint: | |
95 | hint = doc.GetFilename() | |
96 | projectDocs = projectService.FindProjectByFile(hint) | |
97 | if projectDocs: | |
98 | projectDoc = projectDocs[0] | |
99 | ||
100 | projectService.AddProjectMapping(doc, projectDoc) | |
101 | if hasattr(doc, "GetModel"): | |
102 | projectService.AddProjectMapping(doc.GetModel(), projectDoc) | |
103 | ||
104 | ||
105 | def getProjectKeyName(projectName, mode=None): | |
106 | if mode: | |
107 | return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode) | |
108 | else: | |
109 | return "%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|')) | |
1f780e48 | 110 | |
02b800ce RD |
111 | |
112 | def GetDocCallback(filepath): | |
113 | """ Get the Document used by the IDE and the in-memory document model used by runtime engine """ | |
114 | docMgr = wx.GetApp().GetDocumentManager() | |
115 | ||
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 | ||
172 | def findDocumentMgr(root): | |
173 | projectService = wx.GetApp().GetService(ProjectService) | |
174 | if projectService: | |
175 | projectDoc = projectService.FindProjectFromMapping(root) | |
176 | if projectDoc: | |
177 | return projectDoc.GetModel() | |
178 | ||
179 | projectDoc = projectService.GetCurrentProject() | |
180 | if not projectDoc: | |
181 | return None | |
182 | ||
183 | if isinstance(root, wx.lib.docview.Document): | |
184 | filepath = root.GetFilename() | |
185 | elif hasattr(root, "fileName") and root.fileName: | |
186 | filepath = root.fileName | |
187 | else: | |
188 | filepath = None | |
189 | ||
190 | if filepath: | |
191 | if projectDoc.IsFileInProject(filepath): | |
192 | return projectDoc.GetModel() | |
193 | ||
194 | projects = [] | |
195 | openDocs = wx.GetApp().GetDocumentManager().GetDocuments() | |
196 | for openDoc in openDocs: | |
197 | if openDoc == projectDoc: | |
198 | continue | |
199 | if(isinstance(openDoc, ProjectDocument)): | |
200 | if openDoc.IsFileInProject(filepath): | |
201 | projects.append(openDoc) | |
202 | ||
203 | if projects: | |
204 | if len(projects) == 1: | |
205 | return projects[0].GetModel() | |
206 | else: | |
207 | choices = [os.path.basename(project.GetFilename()) for project in projects] | |
208 | dlg = wx.SingleChoiceDialog(wx.GetApp().GetTopWindow(), _("'%s' found in more than one project.\nWhich project should be used for this operation?") % os.path.basename(filepath), _("Select Project"), choices, wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE) | |
209 | dlg.CenterOnParent() | |
210 | projectDoc = None | |
211 | if dlg.ShowModal() == wx.ID_OK: | |
212 | i = dlg.GetSelection() | |
213 | projectDoc = projects[i] | |
214 | dlg.Destroy() | |
215 | return projectDoc.GetModel() | |
216 | return projectDoc.GetModel() | |
217 | ||
218 | return None | |
219 | ||
220 | ||
221 | if not ACTIVEGRID_BASE_IDE: | |
222 | basemodel.findGlobalDocumentMgr = findDocumentMgr | |
1f780e48 RD |
223 | |
224 | ||
225 | #---------------------------------------------------------------------------- | |
226 | # Classes | |
227 | #---------------------------------------------------------------------------- | |
228 | ||
aca310e5 RD |
229 | if not ACTIVEGRID_BASE_IDE: |
230 | class IDEResourceFactory(DeploymentGeneration.DeploymentResourceFactory): | |
231 | ||
232 | def __init__(self, openDocs, dataSourceService, projectDir, | |
233 | preview=False, deployFilepath=None): | |
234 | ||
235 | self.openDocs = openDocs | |
236 | self.dataSourceService = dataSourceService | |
237 | self.projectDir = projectDir | |
238 | self.preview = preview | |
239 | self.deployFilepath = deployFilepath | |
240 | ||
241 | self.defaultFlagsNoView = ( | |
242 | wx.GetApp().GetDocumentManager().GetFlags()| | |
243 | wx.lib.docview.DOC_SILENT| | |
244 | wx.lib.docview.DOC_OPEN_ONCE| | |
245 | wx.lib.docview.DOC_NO_VIEW) | |
246 | ||
247 | def getModel(self, projectFile): | |
248 | doc = wx.GetApp().GetDocumentManager().CreateDocument( | |
249 | projectFile.filePath, flags=self.defaultFlagsNoView) | |
250 | if (doc == None): # already open | |
251 | doc = self._findOpenDoc(projectFile.filePath) | |
252 | else: | |
253 | AddProjectMapping(doc) | |
254 | if (doc != None): | |
255 | return doc.GetModel() | |
256 | ||
257 | def getDataSource(self, dataSourceName): | |
258 | # in preview mode, runtime needs the generated Deployment | |
259 | # to contain the requried data source. But runtime doesn't | |
260 | # actually need to communicate to db. So here is the logic to | |
261 | # make preview works if the required data soruce has not | |
262 | # yet been defined. | |
263 | dataSource = self.dataSourceService.getDataSource(dataSourceName) | |
264 | if (dataSource != None): | |
265 | return dataSource | |
266 | elif not self.preview: | |
267 | raise DataServiceExistenceException(dataSourceName) | |
268 | else: | |
269 | # first to see if an existing dpl file is there, if so, | |
270 | # use the data source in dpl file | |
271 | if (self.deployFilepath != None): | |
272 | tempDply = None | |
273 | try: | |
274 | tempDply = xmlutils.load(deployFilepath) | |
275 | except: | |
276 | pass | |
277 | if (tempDply != None): | |
278 | for tempDataSource in tempDply.dataSources: | |
279 | if (tempDataSource.name == dataSourceName): | |
280 | return tempDataSource | |
281 | ||
282 | # if unable to use dpl file, then create a dummy data source | |
283 | import activegrid.data.dataservice as dataservice | |
284 | return dataservice.DataSource( | |
285 | name=dataSourceName, dbtype=dataservice.DB_TYPE_SQLITE) | |
286 | ||
287 | def initDocumentRef(self, projectFile, documentRef, dpl): | |
288 | doc = self._findOpenDoc(projectFile.filePath) | |
289 | if (doc and hasattr(doc, 'GetModel')): | |
290 | documentRef.document = doc.GetModel() | |
291 | if isinstance(documentRef, deploymentlib.XFormRef): | |
292 | doc.GetModel().linkDeployment(dpl, dpl.loader) | |
293 | ||
294 | def _findOpenDoc(self, filePath): | |
295 | for openDoc in self.openDocs: | |
296 | if openDoc.GetFilename() == filePath: | |
297 | return openDoc | |
298 | return None | |
299 | ||
300 | def getProjectDir(self): | |
301 | return self.projectDir | |
302 | ||
1f780e48 | 303 | |
aca310e5 | 304 | class 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 |
1001 | class 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 |
1076 | class 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 |
1092 | class 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 | ||
1131 | class 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 | |
1156 | class 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 |
1179 | class ProjectRenameFolderCommand(wx.lib.docview.Command): |
1180 | def __init__(self, doc, oldFolderPath, newFolderPath): | |
1181 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
1182 | self._doc = doc | |
1183 | self._oldFolderPath = oldFolderPath | |
1184 | self._newFolderPath = newFolderPath | |
1185 | ||
1186 | ||
1187 | def GetName(self): | |
1188 | return _("Rename Folder %s to %s") % (os.path.basename(self._oldFolderPath), os.path.basename(self._newFolderPath)) | |
1189 | ||
1190 | ||
1191 | def Do(self): | |
1192 | return self._doc.RenameFolder(self._oldFolderPath, self._newFolderPath) | |
1193 | ||
1194 | ||
1195 | def Undo(self): | |
1196 | return self._doc.RenameFolder(self._newFolderPath, self._oldFolderPath) | |
b792147d RD |
1197 | |
1198 | ||
02b800ce RD |
1199 | class ProjectAddFolderCommand(wx.lib.docview.Command): |
1200 | def __init__(self, view, doc, folderpath): | |
1201 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
1202 | self._doc = doc | |
1203 | self._view = view | |
1204 | self._folderpath = folderpath | |
1205 | ||
1206 | ||
1207 | def GetName(self): | |
1208 | return _("Add Folder %s") % (os.path.basename(self._folderpath)) | |
1209 | ||
1210 | ||
1211 | def Do(self): | |
1212 | if self._view.GetDocument() != self._doc: | |
1213 | return True | |
1214 | status = self._view.AddFolder(self._folderpath) | |
1215 | if status: | |
1216 | self._view._treeCtrl.UnselectAll() | |
1217 | item = self._view._treeCtrl.FindFolder(self._folderpath) | |
1218 | self._view._treeCtrl.SelectItem(item) | |
1219 | return status | |
1220 | ||
1221 | ||
1222 | def Undo(self): | |
1223 | if self._view.GetDocument() != self._doc: | |
1224 | return True | |
1225 | return self._view.DeleteFolder(self._folderpath) | |
1226 | ||
1227 | ||
1228 | class ProjectRemoveFolderCommand(wx.lib.docview.Command): | |
1229 | def __init__(self, view, doc, folderpath): | |
1230 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
1231 | self._doc = doc | |
1232 | self._view = view | |
1233 | self._folderpath = folderpath | |
1234 | ||
1235 | ||
1236 | def GetName(self): | |
1237 | return _("Remove Folder %s") % (os.path.basename(self._folderpath)) | |
1238 | ||
1239 | ||
1240 | def Do(self): | |
1241 | if self._view.GetDocument() != self._doc: | |
1242 | return True | |
1243 | return self._view.DeleteFolder(self._folderpath) | |
1244 | ||
1245 | ||
1246 | def Undo(self): | |
1247 | if self._view.GetDocument() != self._doc: | |
1248 | return True | |
1249 | status = self._view.AddFolder(self._folderpath) | |
1250 | if status: | |
1251 | self._view._treeCtrl.UnselectAll() | |
1252 | item = self._view._treeCtrl.FindFolder(self._folderpath) | |
1253 | self._view._treeCtrl.SelectItem(item) | |
1254 | return status | |
1255 | ||
1256 | ||
1257 | class ProjectMoveFilesCommand(wx.lib.docview.Command): | |
1258 | ||
1259 | def __init__(self, doc, files, folderPath): | |
1260 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
1261 | self._doc = doc | |
1262 | self._files = files | |
1263 | self._newFolderPath = folderPath | |
1264 | ||
1265 | self._oldFolderPaths = [] | |
1266 | for file in self._files: | |
1267 | self._oldFolderPaths.append(file.logicalFolder) | |
1268 | ||
1269 | ||
1270 | def GetName(self): | |
1271 | if len(self._files) == 1: | |
1272 | return _("Move File %s") % os.path.basename(self._files[0].filePath) | |
1273 | else: | |
1274 | return _("Move Files") | |
1275 | ||
1276 | ||
1277 | def Do(self): | |
1278 | return self._doc.MoveFiles(self._files, self._newFolderPath) | |
1279 | ||
1280 | ||
1281 | def Undo(self): | |
1282 | return self._doc.MoveFiles(self._files, self._oldFolderPaths) | |
1283 | ||
1284 | ||
1285 | class ProjectTreeCtrl(wx.TreeCtrl): | |
1286 | ||
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 |
1463 | class 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 | |
3125 | class 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 | ||
3152 | class 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 | ||
3263 | class 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 | ||
3318 | class ProjectService(Service.Service): | |
3319 | ||
3320 | #---------------------------------------------------------------------------- | |
3321 | # Constants | |
3322 | #---------------------------------------------------------------------------- | |
3323 | SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service | |
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 | #---------------------------------------------------------------------------- | |
3942 | from wx import ImageFromStream, BitmapFromImage | |
1f780e48 RD |
3943 | import cStringIO |
3944 | ||
3945 | ||
3946 | def getProjectData(): | |
3947 | return \ | |
3948 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
3949 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
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\ | |
3955 | n\xe9\xd8h\xde\xe6\x1d\xe9\xfe\xa9E\xc7\xfb\x91\xf9\xfd\x01D\xfa\xc9\xd8\xf7\ | |
3956 | \xcdPI\'\x01X\xd8>@p\xf7\x00($W\x8c\x8f&R\xa7\xa7\xa2u\xebL.\xef\xd9\x00\x97\ | |
3957 | \xa7\x87D\\er\x15\x95\xb9\xf5\x12\xa3\x81Y\x9bG\xfax0\xb3Z\x8d*\x95t\x92z\ | |
3958 | \xb5\x80yjhC\x83\x16\x96\x15\xdc\xc3AZ\x8d\xea{XN#g.,\xa6\xe0l\x9c\xde}\x89\ | |
3959 | \xb6\xc3\x9aR\xff\xe5\x01\x801}\x1c\x80\x9b\xcc\x05\xde\xb0\x9f\xd0t\x04oX\ | |
3960 | \xa6\xad4\xc9U\n\xc0&\x1e\xfd\xd6\x0e\x18\xd4Se\x00\xbca?m\xa5\xc9\x1d\xd0V\ | |
3961 | \x9a\x03\xa3\xd6\xadc\xa8\x8fv\xc0S\xa3H\xc8\x13\x01\xa2\x00\xc4V\x13\x94\ | |
3962 | \xb3)\xae\xae\x14\x8b\xd1\x17\x90laK\x03\xb3b\xab\t&\x02\xf7(\xf94\xf2k\x8c\ | |
3963 | \x8d\x8dy\xc7\xf0\xb7\x00\x80`t\x92`t\x87%\xa0\x9cM\xd1\xa8}\xce\xcc\xbf\xd1\ | |
3964 | \x11P\xce\xa6,\xe7\xaf\xdf\xd7,Ap\x89\x14\x92+\xc6_\x03\x8e\x80\xff\xc8\xf5\ | |
3965 | \xaf4\xf0\x06=\xf3\x8fJr]C\xd9\x00\x00\x00\x00IEND\xaeB`\x82' | |
1f780e48 RD |
3966 | |
3967 | def getProjectBitmap(): | |
3968 | return BitmapFromImage(getProjectImage()) | |
3969 | ||
3970 | def getProjectImage(): | |
3971 | stream = cStringIO.StringIO(getProjectData()) | |
3972 | return ImageFromStream(stream) | |
3973 | ||
3974 | def getProjectIcon(): | |
bbf7159c | 3975 | return wx.IconFromBitmap(getProjectBitmap()) |
02b800ce | 3976 | |
1f780e48 RD |
3977 | |
3978 | #---------------------------------------------------------------------------- | |
3979 | ||
3980 | def 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 | ||
3998 | def getBlankBitmap(): | |
3999 | return BitmapFromImage(getBlankImage()) | |
4000 | ||
4001 | def getBlankImage(): | |
4002 | stream = cStringIO.StringIO(getBlankData()) | |
4003 | return ImageFromStream(stream) | |
4004 | ||
4005 | def getBlankIcon(): | |
bbf7159c | 4006 | return wx.IconFromBitmap(getBlankBitmap()) |
02b800ce RD |
4007 | |
4008 | ||
4009 | #---------------------------------------------------------------------- | |
4010 | def getFolderClosedData(): | |
4011 | return \ | |
4012 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4013 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4014 | \x00\x00\xffIDAT8\x8d\xa5\x93?N\x02A\x14\x87\xbf\x19&\x10B\xb4A\x0b*iL0!$\ | |
4015 | \x06\x0f\xe0\x05lH\x88G\xe1\x0c\xdbx\x11ZN`,\xa5\x01\x8aM\xa4\x80\x84\xc4Fc\ | |
4016 | \xd0\xe8\xb0\xae\xbbc\x01\x0b,\x19\x16X~\xd5\x9b\xf7\xe7\x9by3o\x84\x90\x19\ | |
4017 | \x8e\x91\x8a\x0c\xed:\x06\xc0\xf7g\x00x\xde\x14\x80\xf3\x9b\x07\xb1\x13\xa0]\ | |
4018 | \xc7d\xcbw\x00d\x17\x81\x82\xff\x01\xc0\xb0\xd3\x9f\x83\x7f\xf5\xb2\xe8\xaa\ | |
4019 | \xf1\xb4\x84\n!3h\xd71\xef\xaf=\xeb\x0e\xc5R\xcd\xea\xcfWZ"\xd6\xc2\xb6\xc4\ | |
4020 | \xdc\xe5\xad\xd5?h\xd7M\xb5\xd9\x15\n\xe6}{\xde\x94\xe2\xf5\xbd59I\x12V\x17\ | |
4021 | \x96F\n \xfc\xfbD\xaaS\xc2\x9fI:@\x041\xdf\xa3\x8d\xb0Y\xb3\xed\xaf\xa9\x00\ | |
4022 | \xbe\xde\xc6\x9c\x9c]\x10\xea\xc3O #\xc3\xd7:)/\x19\xb0>$\x87J\x01\x04\xc1n\ | |
4023 | \xc0\xcb\xf3cl]mv\xe3\x83\xb4o\xc1\xa6D\xf4\x1b\x07\xed\xba\xd9\xa7`+ \xad\ | |
4024 | \xfe\x01\xd1\x03SV!\xfbHa\x00\x00\x00\x00IEND\xaeB`\x82' | |
4025 | ||
4026 | def getFolderClosedBitmap(): | |
4027 | return BitmapFromImage(getFolderClosedImage()) | |
4028 | ||
4029 | def getFolderClosedImage(): | |
4030 | stream = cStringIO.StringIO(getFolderClosedData()) | |
4031 | return ImageFromStream(stream) | |
4032 | ||
4033 | def getFolderClosedIcon(): | |
4034 | return wx.IconFromBitmap(getFolderClosedBitmap()) | |
4035 | ||
4036 | ||
4037 | #---------------------------------------------------------------------- | |
4038 | def getFolderOpenData(): | |
4039 | return \ | |
4040 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4041 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4042 | \x00\x01>IDAT8\x8d\xa5\x93\xbdJ\x03A\x14\x85\xbfY\x03i\xac\x14\x92\xc2F\xad$\ | |
4043 | Z\xa4\x10\x11|\x01\xc1J\xdbt\xbe\x86\x9d\x85\x0f\xa0\xe0\x1b\x04,l\xc4J\x0b\ | |
4044 | \x0bA;a\x11\x13\xb1H\xc2\xc2\xca\x84@\x88n\xb2\xd9?\xcd\xd8d6.\x9b\x104\xa7\ | |
4045 | \xbas\xef=g\xce\x9d\xe1\na\xcc1\x0b\x8c\x99\xd8@F\x07_\xd6\xb9\n\xdd\x8f\xb8\ | |
4046 | \xd0s\x9a\x00\xe4\xb6O\xc5T\x81~\xf5D\x89\xdc\x0e\xd9_\x85,\xa0\xa2\x06\xefw\ | |
4047 | R\x01\x04\x9e\x03\xc0\xea\xde\x8dH\th\xa8\xa81:\xf8\x1e\x00\xf9\x8d\x03\x00\ | |
4048 | \xa4U\x07\xc0,\xdb\xaaX\xaa\xc4"\x99\x04\xd9\xf7\xe0\xfbs$\x12\x0e\x90\xad\ | |
4049 | \x0e\x00]\xeb*N\x9b\xe5u\x05P,UD\xc2\x81&K\xbb\r@\xd4\xba\x1f\x9a\xe9\xb0\ | |
4050 | \xb6\x7f\x96h}\xbe8\x1c9\xe89M\x16\xfc\x15\xa4\xdd\xc6\xe8\x9a\x18\xc3\x99\ | |
4051 | \x97w\x8f\x99\x86\xd8\x81\xb4\xea\x18]\x93\xfcf).\x0e\\9\x96\xf4r}\x84~\x87\ | |
4052 | \xc4\x08\x81\xe7\xa0\xfa\xb5\xa9\xb7\xa6\x1c\xf4\xdao\xcc/B\x04\x0c<\xfb\xef\ | |
4053 | \x02Zd\xa9P\x98\xd8\xf8\xfax\x1b\xc7\xa9o\xf4\xbdN\x8aP{z \x0c\xdc\xb1\xa4\ | |
4054 | \xdf\x10z\x99\xaa\x97[J\'\xc3\xc0\x9dH\x98(\xf0_\xcc\xbc\x8d?\xf2)\x7f\x8e|f\ | |
4055 | \xe54\x00\x00\x00\x00IEND\xaeB`\x82' | |
4056 | ||
4057 | def getFolderOpenBitmap(): | |
4058 | return BitmapFromImage(getFolderOpenImage()) | |
4059 | ||
4060 | def getFolderOpenImage(): | |
4061 | stream = cStringIO.StringIO(getFolderOpenData()) | |
4062 | return ImageFromStream(stream) | |
4063 | ||
4064 | def getFolderOpenIcon(): | |
4065 | return wx.IconFromBitmap(getFolderOpenBitmap()) | |
1f780e48 | 4066 | |
02b800ce RD |
4067 | |
4068 | #---------------------------------------------------------------------- | |
4069 | def getLogicalModeOnData(): | |
4070 | return \ | |
4071 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4072 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4073 | \x00\x01\x83IDAT8\x8d\xa5\x93\xcbJ\xc3@\x14\x86\xbfI\x83buS\xabE+TE\x04\x17\ | |
4074 | \xde\xf0\x02\x82\xa0k\x17\n.\xdc\xf9\x1e.\xf4\x05\\\t\xfa\x18\x057\xe2\x0b\ | |
4075 | \x08ue@\xa4`\xb0\x84J\xd0(M\xa3"\xb65\x8d5.jcbS\x14\xfdW3\xe7\xfc\xe7\x9b9\ | |
4076 | \xc3\x19!\xa4\x08\xff\x91\xdcXT\x8d=\xb7\xf6\\\xa5\xe2\xd8\xf5\xfd\xab\t@\ | |
4077 | \xdf\xfc\x81\xf8\x11PQw\xddHl\x99H\x0c\xda\xbe\x19\xce\x0f\r\x17@\xae]{\xb1\ | |
4078 | \xf1\r\xc5\x83\n!E\xa8\xa8\xbb\xaeuw\x11zB\xbc\x7f24\xde1\xb6%\x02-\xb42\xbe\ | |
4079 | \xc5\x06\xd12i\x00&V\xb6\x11m\x0e\x00\xd9\xf4\xac;\xbe\xa1\x88z\x0b\x8eM\xf5\ | |
4080 | \xd5$1\xb3\xd9\x048\xde\xdf!%\xe5P4\x9b\x91\xc5+:{\x86\x03y\x19\xbe\x1e\xcc\ | |
4081 | \xafR1\x8f\x96Ic\xe6\xb34g\xbf\x01\xfcE\x00%=\x83~z\xd4dv\nW\x94\xc2\x00o/\ | |
4082 | \x0f\xc8]\xdd\xb4\xd7\xee\x00\xb8<="\x9a\x8c\xd37\x90"\x9a\xd4Qo\xba1\xf3Y\ | |
4083 | \x00\xcf\x13z\x03\xd7\xd6\x01\x88&\xe3\x00\xdc\xdf\xea\x94\r\x8b\x94da~\xb6\ | |
4084 | \xea\xda\x8f\x01\x80\x04\xf0TT\x91\x9d\x1b/8:\xb7D\xd9\xb0(\x1b\x16\x8af\xa3\ | |
4085 | h\xf5\xe1\x8a\xf5\x04\xcek\xbe\x81_Sk\xeb\x98\xd7\x05\xf4\xf7\x02\x00\x0b\ | |
4086 | \xd3\x89P_K\x00@\xefP\x82\xd5\xa1za\xee\xec\x84\xa7\xa2\xea\xe5\x1a\xd3\xd8\ | |
4087 | \x12\x90;;\t\xec\xfd\xe3\xeb\x97h\xfc\xc6lz\xd6\xfdMAK\xc0_\xf5\x01\xf4\x01\ | |
4088 | \x91\xdc\xfe\x86\x9e^\x00\x00\x00\x00IEND\xaeB`\x82' | |
4089 | ||
4090 | def getLogicalModeOnBitmap(): | |
4091 | return BitmapFromImage(getLogicalModeOnImage()) | |
4092 | ||
4093 | def getLogicalModeOnImage(): | |
4094 | stream = cStringIO.StringIO(getLogicalModeOnData()) | |
4095 | return ImageFromStream(stream) | |
4096 | ||
4097 | #---------------------------------------------------------------------- | |
4098 | def getLogicalModeOffData(): | |
4099 | return \ | |
4100 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4101 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4102 | \x00\x01\x83IDAT8\x8d\xa5\x93\xcbJ\xc3@\x14\x86\xbfI\x83buS\xabE+TE\x04\x17\ | |
4103 | \xde\xf0\x02\x82\xa0k\x17\n.\xdc\xf9\x1e.\xf4\x05\\\t\xfa\x18\x057\xe2\x0b\ | |
4104 | \x08ue@\xa4`\xb0\x84J\xd0(M\xa3"\xb65\x8d5.jcbS\x14\xfdW3\xe7\xfc\xe7\x9b9\ | |
4105 | \xc3\x19!\xa4\x08\xff\x91\xdcXT\x8d=\xb7\xf6\\\xa5\xe2\xd8\xf5\xfd\xab\t@\ | |
4106 | \xdf\xfc\x81\xf8\x11PQw\xddHl\x99H\x0c\xda\xbe\x19\xce\x0f\r\x17@\xae]{\xb1\ | |
4107 | \xf1\r\xc5\x83\n!E\xa8\xa8\xbb\xaeuw\x11zB\xbc\x7f24\xde1\xb6%\x02-\xb42\xbe\ | |
4108 | \xc5\x06\xd12i\x00&V\xb6\x11m\x0e\x00\xd9\xf4\xac;\xbe\xa1\x88z\x0b\x8eM\xf5\ | |
4109 | \xd5$1\xb3\xd9\x048\xde\xdf!%\xe5P4\x9b\x91\xc5+:{\x86\x03y\x19\xbe\x1e\xcc\ | |
4110 | \xafR1\x8f\x96Ic\xe6\xb34g\xbf\x01\xfcE\x00%=\x83~z\xd4dv\nW\x94\xc2\x00o/\ | |
4111 | \x0f\xc8]\xdd\xb4\xd7\xee\x00\xb8<="\x9a\x8c\xd37\x90"\x9a\xd4Qo\xba1\xf3Y\ | |
4112 | \x00\xcf\x13z\x03\xd7\xd6\x01\x88&\xe3\x00\xdc\xdf\xea\x94\r\x8b\x94da~\xb6\ | |
4113 | \xea\xda\x8f\x01\x80\x04\xf0TT\x91\x9d\x1b/8:\xb7D\xd9\xb0(\x1b\x16\x8af\xa3\ | |
4114 | h\xf5\xe1\x8a\xf5\x04\xcek\xbe\x81_Sk\xeb\x98\xd7\x05\xf4\xf7\x02\x00\x0b\ | |
4115 | \xd3\x89P_K\x00@\xefP\x82\xd5\xa1za\xee\xec\x84\xa7\xa2\xea\xe5\x1a\xd3\xd8\ | |
4116 | \x12\x90;;\t\xec\xfd\xe3\xeb\x97h\xfc\xc6lz\xd6\xfdMAK\xc0_\xf5\x01\xf4\x01\ | |
4117 | \x91\xdc\xfe\x86\x9e^\x00\x00\x00\x00IEND\xaeB`\x82' | |
4118 | ||
4119 | def getLogicalModeOffBitmap(): | |
4120 | return BitmapFromImage(getLogicalModeOffImage()) | |
4121 | ||
4122 | def getLogicalModeOffImage(): | |
4123 | stream = cStringIO.StringIO(getLogicalModeOffData()) | |
4124 | return ImageFromStream(stream) | |
4125 | ||
4126 | #---------------------------------------------------------------------- | |
4127 | def getPhysicalModeOnData(): | |
4128 | return \ | |
4129 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4130 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4131 | \x00\x01\xabIDAT8\x8d}\x931k\xdb@\x18\x86\x9f\xb3=\x98R\xb0\x06\xc7X\x01\x1d\ | |
4132 | \x14\x1c\xeaA4?\xa0\xa1\x8b\x9d\x04C\xe6N\xed\xd8\xad\xbf\xc0\xbf!c\xb6@\x9d\ | |
4133 | \xa1\xf4\'\xd4m\xd2l\x9dJ(\xb8R\x87\x90\x84\x80\xaeD\x8e\xad\xc1\xeePBIQ\x87\ | |
4134 | \x8b.:+\xc9\x0b\x82\xef\xee\xd3\xf3\xde{\x1f\x9c\x10\xa52\xf7)\x99N\xd2q\x1c\ | |
4135 | [{\xfe\xb3U\x91_\x8bE\x83E\xa8\xe9\xba\xa6\x1e\xc71*Rx\xd2\xa3\xe9\xba\xd4\ | |
4136 | \x97\x1a\xa2\x92L\'i\xd6\xbc\x0bZ\xecy\xd2CE\n\x15)\x00*Y\xf3!hQ\x9e\xf4\xf8\ | |
4137 | vt\xa4\r\xf2\xf0}\x90L|\xae\x93\xdb\xf5E;4uEE\xca\x184]\xd72\x91\x89\x0f\xc0\ | |
4138 | \xe3\xf6\xaee\xf8\xe7\x83\xcf\x06\x00e\xc4`o/\r\x83\x80\x96\xf4x\xf9\xea\xb5\ | |
4139 | I"\x13\xbf\x00ZJF\\\xec\xef >}\x1c\xa6\x00\x07\x87_hI\x8f\x17\x9d.*R<\x7f\ | |
4140 | \xd43\xffZF7\xa0\xb9\xc2\xf9\xc91OV\x9e\xb2\xde\xe9Z\x07\\\'\xe0\xacip\xf6\ | |
4141 | \xf5\xcdm\xfc\x08\x967\xde\xeaY\xec\xef\xe8!\x9e\x9f\x1c\x03\xf0[\xfe\x85\ | |
4142 | \xa8\x98\xd6Y\xdb\x85d\xa4\xeb60>\x03\xe0\xe7!\x94N#E\xb5\xe6P\xad9\x06\x88\ | |
4143 | \'\x97\x85\xfb\xea\xe1\x9c\x198Si\xbd\xd3%\x0c\x02\xae\xe63\x1a\xf3\x86\x15\ | |
4144 | \xd5\x82\xf3\x9a^\xea\x0f(\xf5\xb6\xb6D\xbf\xdf\xa7Zs\x08\x83\x00\x80\xab\ | |
4145 | \xf9\xac\x08g\'O\xedt\x15\x80\xfaRC\x00\x84?F\xe9\xbb\xc1\x80\x96\xf4t\xb7\ | |
4146 | \xbezw\x82\x9c\n\x8f)\xaf_\xdb\xffR\xb8\x99z.\xc1\xc1\xfb\xef\x00l\x0e\xcb\ | |
4147 | \xe2A\x83L\x9f{\xda(\xd3\xe6\xb0l\x9e\xf4\x7f\x85\x1d\xb2s\xbf\x8c\xaeh\x00\ | |
4148 | \x00\x00\x00IEND\xaeB`\x82' | |
4149 | ||
4150 | def getPhysicalModeOnBitmap(): | |
4151 | return BitmapFromImage(getPhysicalModeOnImage()) | |
4152 | ||
4153 | def getPhysicalModeOnImage(): | |
4154 | stream = cStringIO.StringIO(getPhysicalModeOnData()) | |
4155 | return ImageFromStream(stream) | |
4156 | ||
4157 | #---------------------------------------------------------------------- | |
4158 | def getPhysicalModeOffData(): | |
4159 | return \ | |
4160 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
4161 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
4162 | \x00\x01\xabIDAT8\x8d}\x931k\xdb@\x18\x86\x9f\xb3=\x98R\xb0\x06\xc7X\x01\x1d\ | |
4163 | \x14\x1c\xeaA4?\xa0\xa1\x8b\x9d\x04C\xe6N\xed\xd8\xad\xbf\xc0\xbf!c\xb6@\x9d\ | |
4164 | \xa1\xf4\'\xd4m\xd2l\x9dJ(\xb8R\x87\x90\x84\x80\xaeD\x8e\xad\xc1\xeePBIQ\x87\ | |
4165 | \x8b.:+\xc9\x0b\x82\xef\xee\xd3\xf3\xde{\x1f\x9c\x10\xa52\xf7)\x99N\xd2q\x1c\ | |
4166 | [{\xfe\xb3U\x91_\x8bE\x83E\xa8\xe9\xba\xa6\x1e\xc71*Rx\xd2\xa3\xe9\xba\xd4\ | |
4167 | \x97\x1a\xa2\x92L\'i\xd6\xbc\x0bZ\xecy\xd2CE\n\x15)\x00*Y\xf3!hQ\x9e\xf4\xf8\ | |
4168 | vt\xa4\r\xf2\xf0}\x90L|\xae\x93\xdb\xf5E;4uEE\xca\x184]\xd72\x91\x89\x0f\xc0\ | |
4169 | \xe3\xf6\xaee\xf8\xe7\x83\xcf\x06\x00e\xc4`o/\r\x83\x80\x96\xf4x\xf9\xea\xb5\ | |
4170 | I"\x13\xbf\x00ZJF\\\xec\xef >}\x1c\xa6\x00\x07\x87_hI\x8f\x17\x9d.*R<\x7f\ | |
4171 | \xd43\xffZF7\xa0\xb9\xc2\xf9\xc91OV\x9e\xb2\xde\xe9Z\x07\\\'\xe0\xacip\xf6\ | |
4172 | \xf5\xcdm\xfc\x08\x967\xde\xeaY\xec\xef\xe8!\x9e\x9f\x1c\x03\xf0[\xfe\x85\ | |
4173 | \xa8\x98\xd6Y\xdb\x85d\xa4\xeb60>\x03\xe0\xe7!\x94N#E\xb5\xe6P\xad9\x06\x88\ | |
4174 | \'\x97\x85\xfb\xea\xe1\x9c\x198Si\xbd\xd3%\x0c\x02\xae\xe63\x1a\xf3\x86\x15\ | |
4175 | \xd5\x82\xf3\x9a^\xea\x0f(\xf5\xb6\xb6D\xbf\xdf\xa7Zs\x08\x83\x00\x80\xab\ | |
4176 | \xf9\xac\x08g\'O\xedt\x15\x80\xfaRC\x00\x84?F\xe9\xbb\xc1\x80\x96\xf4t\xb7\ | |
4177 | \xbezw\x82\x9c\n\x8f)\xaf_\xdb\xffR\xb8\x99z.\xc1\xc1\xfb\xef\x00l\x0e\xcb\ | |
4178 | \xe2A\x83L\x9f{\xda(\xd3\xe6\xb0l\x9e\xf4\x7f\x85\x1d\xb2s\xbf\x8c\xaeh\x00\ | |
4179 | \x00\x00\x00IEND\xaeB`\x82' | |
4180 | ||
4181 | def getPhysicalModeOffBitmap(): | |
4182 | return BitmapFromImage(getPhysicalModeOffImage()) | |
4183 | ||
4184 | def getPhysicalModeOffImage(): | |
4185 | stream = cStringIO.StringIO(getPhysicalModeOffData()) | |
4186 | return ImageFromStream(stream) | |
4187 |